@ledgerhq/react-native-hw-transport-ble 6.28.3-nightly.0 → 6.28.4-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -82,10 +82,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
82
82
  };
83
83
  exports.__esModule = true;
84
84
  exports.setReconnectionConfig = void 0;
85
- /* eslint-disable prefer-template */
86
85
  var hw_transport_1 = __importDefault(require("@ledgerhq/hw-transport"));
87
- var react_native_ble_plx_1 = require("react-native-ble-plx");
88
- var devices_1 = require("@ledgerhq/devices");
89
86
  // ---------------------------------------------------------------------------------------------
90
87
  // Since this is a react-native library and metro bundler does not support
91
88
  // package exports yet (see: https://github.com/facebook/metro/issues/670)
@@ -96,6 +93,8 @@ var devices_1 = require("@ledgerhq/devices");
96
93
  // See: https://github.com/LedgerHQ/ledger-live/pull/879
97
94
  var sendAPDU_1 = require("@ledgerhq/devices/lib/ble/sendAPDU");
98
95
  var receiveAPDU_1 = require("@ledgerhq/devices/lib/ble/receiveAPDU");
96
+ var react_native_ble_plx_1 = require("react-native-ble-plx");
97
+ var devices_1 = require("@ledgerhq/devices");
99
98
  var logs_1 = require("@ledgerhq/logs");
100
99
  var rxjs_1 = require("rxjs");
101
100
  var operators_1 = require("rxjs/operators");
@@ -103,25 +102,18 @@ var errors_1 = require("@ledgerhq/errors");
103
102
  var monitorCharacteristic_1 = require("./monitorCharacteristic");
104
103
  var awaitsBleOn_1 = require("./awaitsBleOn");
105
104
  var remapErrors_1 = require("./remapErrors");
106
- var connectOptions = {
107
- requestMTU: 156,
108
- connectionPriority: 1
109
- };
110
- var transportsCache = {};
111
- var _bleManager = null;
112
105
  /**
113
- * Allows lazy initialization of BleManager
114
- * Useful for iOS to only ask for Bluetooth permission when needed
115
- *
116
- * Do not use _bleManager directly
117
- * Only use this instance getter inside BleTransport
106
+ * This is potentially not needed anymore, to be checked if the bug is still
107
+ * happening.
118
108
  */
119
- var bleManagerInstance = function () {
120
- if (!_bleManager) {
121
- _bleManager = new react_native_ble_plx_1.BleManager();
122
- }
123
- return _bleManager;
109
+ var reconnectionConfig = {
110
+ pairingThreshold: 1000,
111
+ delayAfterFirstPairing: 4000
112
+ };
113
+ var setReconnectionConfig = function (config) {
114
+ reconnectionConfig = config;
124
115
  };
116
+ exports.setReconnectionConfig = setReconnectionConfig;
125
117
  var retrieveInfos = function (device) {
126
118
  if (!device || !device.serviceUUIDs)
127
119
  return;
@@ -133,147 +125,168 @@ var retrieveInfos = function (device) {
133
125
  return;
134
126
  return infos;
135
127
  };
136
- var reconnectionConfig = {
137
- pairingThreshold: 1000,
138
- delayAfterFirstPairing: 4000
128
+ var delay = function (ms) {
129
+ return new Promise(function (success) { return setTimeout(success, ms); });
130
+ };
131
+ /**
132
+ * A cache of Bluetooth transport instances associated with device IDs.
133
+ * Allows efficient storage and retrieval of previously initialized transports.
134
+ * @type {Object.<string, BluetoothTransport>}
135
+ */
136
+ var transportsCache = {};
137
+ /**
138
+ * Returns the instance of the Bluetooth Low Energy Manager. It initializes it only
139
+ * when it's first needed, preventing the permission prompt happening prematurely.
140
+ * Important: Do NOT access the _bleManager variable directly.
141
+ * Use this function instead.
142
+ * @returns {BleManager} - The instance of the BleManager.
143
+ */
144
+ var _bleManager = null;
145
+ var bleManagerInstance = function () {
146
+ if (!_bleManager) {
147
+ _bleManager = new react_native_ble_plx_1.BleManager();
148
+ }
149
+ return _bleManager;
150
+ };
151
+ var clearDisconnectTimeout = function (deviceId) {
152
+ var cachedTransport = transportsCache[deviceId];
153
+ if (cachedTransport && cachedTransport.disconnectTimeout) {
154
+ (0, logs_1.log)(TAG, "Clearing queued disconnect");
155
+ clearTimeout(cachedTransport.disconnectTimeout);
156
+ }
139
157
  };
140
- function setReconnectionConfig(config) {
141
- reconnectionConfig = config;
142
- }
143
- exports.setReconnectionConfig = setReconnectionConfig;
144
- var delay = function (ms) { return new Promise(function (success) { return setTimeout(success, ms); }); };
145
158
  function open(deviceOrId, needsReconnect) {
146
159
  return __awaiter(this, void 0, void 0, function () {
147
- var device, devices, connectedDevices, connectedDevicesFiltered, e_1, e_2, res, characteristics, _a, _b, uuid, e_3, e_4_1, deviceModel, serviceUuid, writeUuid, writeCmdUuid, notifyUuid, writeC, writeCmdC, notifyC, characteristics_1, characteristics_1_1, c, notifyObservable, notif, transport, onDisconnect, disconnectedSub, beforeMTUTime, afterMTUTime;
160
+ var device, devices, connectedDevices, connectedDevicesFiltered, e_1, e_2, res, characteristics, _a, _b, uuid, e_3, e_4_1, deviceModel, serviceUuid, writeUuid, writeCmdUuid, notifyUuid, writeC, writeCmdC, notifyC, characteristics_1, characteristics_1_1, c, notifyObservable, notif, transport, disconnectedSub, onDisconnect, beforeMTUTime, afterMTUTime;
148
161
  var _c, _d, e_4, _e, e_5, _f;
149
162
  return __generator(this, function (_g) {
150
163
  switch (_g.label) {
151
164
  case 0:
152
- if (!(typeof deviceOrId === "string")) return [3 /*break*/, 13];
165
+ (0, logs_1.log)(TAG, "open with ".concat(deviceOrId));
166
+ if (!(typeof deviceOrId === "string")) return [3 /*break*/, 12];
153
167
  if (transportsCache[deviceOrId]) {
154
- (0, logs_1.log)("ble-verbose", "Transport in cache, using that.");
168
+ (0, logs_1.log)(TAG, "Transport in cache, using that.");
169
+ clearDisconnectTimeout(deviceOrId);
155
170
  return [2 /*return*/, transportsCache[deviceOrId]];
156
171
  }
157
- (0, logs_1.log)("ble-verbose", "Tries to open device: ".concat(deviceOrId));
172
+ (0, logs_1.log)(TAG, "Tries to open device: ".concat(deviceOrId));
158
173
  return [4 /*yield*/, (0, awaitsBleOn_1.awaitsBleOn)(bleManagerInstance())];
159
174
  case 1:
160
175
  _g.sent();
161
- if (!!device) return [3 /*break*/, 3];
162
176
  return [4 /*yield*/, bleManagerInstance().devices([deviceOrId])];
163
177
  case 2:
164
178
  devices = _g.sent();
165
- (0, logs_1.log)("ble-verbose", "found ".concat(devices.length, " devices"));
179
+ (0, logs_1.log)(TAG, "found ".concat(devices.length, " devices"));
166
180
  _c = __read(devices, 1), device = _c[0];
167
- _g.label = 3;
168
- case 3:
169
- if (!!device) return [3 /*break*/, 5];
181
+ if (!!device) return [3 /*break*/, 4];
170
182
  return [4 /*yield*/, bleManagerInstance().connectedDevices((0, devices_1.getBluetoothServiceUuids)())];
171
- case 4:
183
+ case 3:
172
184
  connectedDevices = _g.sent();
173
185
  connectedDevicesFiltered = connectedDevices.filter(function (d) { return d.id === deviceOrId; });
174
- (0, logs_1.log)("ble-verbose", "found ".concat(connectedDevicesFiltered.length, " connected devices"));
186
+ (0, logs_1.log)(TAG, "found ".concat(connectedDevicesFiltered.length, " connected devices"));
175
187
  _d = __read(connectedDevicesFiltered, 1), device = _d[0];
188
+ _g.label = 4;
189
+ case 4:
190
+ if (!!device) return [3 /*break*/, 11];
191
+ // We still don't have a device, so we attempt to connect to it.
192
+ (0, logs_1.log)(TAG, "connectToDevice(".concat(deviceOrId, ")"));
176
193
  _g.label = 5;
177
194
  case 5:
178
- if (!!device) return [3 /*break*/, 12];
179
- (0, logs_1.log)("ble-verbose", "connectToDevice(".concat(deviceOrId, ")"));
180
- _g.label = 6;
195
+ _g.trys.push([5, 7, , 11]);
196
+ return [4 /*yield*/, bleManagerInstance().connectToDevice(deviceOrId)];
181
197
  case 6:
182
- _g.trys.push([6, 8, , 12]);
183
- return [4 /*yield*/, bleManagerInstance().connectToDevice(deviceOrId, connectOptions)];
184
- case 7:
185
198
  device = _g.sent();
186
- return [3 /*break*/, 12];
187
- case 8:
199
+ return [3 /*break*/, 11];
200
+ case 7:
188
201
  e_1 = _g.sent();
189
- if (!(e_1.errorCode === react_native_ble_plx_1.BleErrorCode.DeviceMTUChangeFailed)) return [3 /*break*/, 10];
190
- // eslint-disable-next-line require-atomic-updates
191
- connectOptions = {};
202
+ (0, logs_1.log)(TAG, "error code ".concat(e_1.errorCode));
203
+ if (!(e_1.errorCode === react_native_ble_plx_1.BleErrorCode.DeviceMTUChangeFailed)) return [3 /*break*/, 9];
192
204
  return [4 /*yield*/, bleManagerInstance().connectToDevice(deviceOrId)];
193
- case 9:
205
+ case 8:
194
206
  device = _g.sent();
195
- return [3 /*break*/, 11];
196
- case 10: throw e_1;
197
- case 11: return [3 /*break*/, 12];
198
- case 12:
207
+ return [3 /*break*/, 10];
208
+ case 9: throw e_1;
209
+ case 10: return [3 /*break*/, 11];
210
+ case 11:
199
211
  if (!device) {
200
212
  throw new errors_1.CantOpenDevice();
201
213
  }
202
- return [3 /*break*/, 14];
203
- case 13:
214
+ return [3 /*break*/, 13];
215
+ case 12:
216
+ // It was already a Device
204
217
  device = deviceOrId;
205
- _g.label = 14;
206
- case 14: return [4 /*yield*/, device.isConnected()];
218
+ _g.label = 13;
219
+ case 13: return [4 /*yield*/, device.isConnected()];
220
+ case 14:
221
+ if (!!(_g.sent())) return [3 /*break*/, 21];
222
+ (0, logs_1.log)(TAG, "not connected. connecting...");
223
+ _g.label = 15;
207
224
  case 15:
208
- if (!!(_g.sent())) return [3 /*break*/, 22];
209
- (0, logs_1.log)("ble-verbose", "not connected. connecting...");
210
- _g.label = 16;
225
+ _g.trys.push([15, 17, , 21]);
226
+ return [4 /*yield*/, device.connect()];
211
227
  case 16:
212
- _g.trys.push([16, 18, , 22]);
213
- return [4 /*yield*/, device.connect(connectOptions)];
214
- case 17:
215
228
  _g.sent();
216
- return [3 /*break*/, 22];
217
- case 18:
229
+ return [3 /*break*/, 21];
230
+ case 17:
218
231
  e_2 = _g.sent();
219
- if (!(e_2.errorCode === react_native_ble_plx_1.BleErrorCode.DeviceMTUChangeFailed)) return [3 /*break*/, 20];
220
- // eslint-disable-next-line require-atomic-updates
221
- connectOptions = {};
232
+ if (!(e_2.errorCode === react_native_ble_plx_1.BleErrorCode.DeviceMTUChangeFailed)) return [3 /*break*/, 19];
233
+ // Retry once for this specific error.
222
234
  return [4 /*yield*/, device.connect()];
223
- case 19:
235
+ case 18:
236
+ // Retry once for this specific error.
224
237
  _g.sent();
225
- return [3 /*break*/, 21];
226
- case 20: throw e_2;
227
- case 21: return [3 /*break*/, 22];
228
- case 22: return [4 /*yield*/, device.discoverAllServicesAndCharacteristics()];
229
- case 23:
238
+ return [3 /*break*/, 20];
239
+ case 19: throw e_2;
240
+ case 20: return [3 /*break*/, 21];
241
+ case 21: return [4 /*yield*/, device.discoverAllServicesAndCharacteristics()];
242
+ case 22:
230
243
  _g.sent();
231
244
  res = retrieveInfos(device);
232
- if (!!res) return [3 /*break*/, 33];
245
+ if (!!res) return [3 /*break*/, 32];
246
+ _g.label = 23;
247
+ case 23:
248
+ _g.trys.push([23, 30, 31, 32]);
249
+ _a = __values((0, devices_1.getBluetoothServiceUuids)()), _b = _a.next();
233
250
  _g.label = 24;
234
251
  case 24:
235
- _g.trys.push([24, 31, 32, 33]);
236
- _a = __values((0, devices_1.getBluetoothServiceUuids)()), _b = _a.next();
252
+ if (!!_b.done) return [3 /*break*/, 29];
253
+ uuid = _b.value;
237
254
  _g.label = 25;
238
255
  case 25:
239
- if (!!_b.done) return [3 /*break*/, 30];
240
- uuid = _b.value;
241
- _g.label = 26;
242
- case 26:
243
- _g.trys.push([26, 28, , 29]);
256
+ _g.trys.push([25, 27, , 28]);
244
257
  return [4 /*yield*/, device.characteristicsForService(uuid)];
245
- case 27:
258
+ case 26:
246
259
  characteristics = _g.sent();
247
260
  res = (0, devices_1.getInfosForServiceUuid)(uuid);
248
- return [3 /*break*/, 30];
249
- case 28:
250
- e_3 = _g.sent();
251
261
  return [3 /*break*/, 29];
252
- case 29:
262
+ case 27:
263
+ e_3 = _g.sent();
264
+ return [3 /*break*/, 28];
265
+ case 28:
253
266
  _b = _a.next();
254
- return [3 /*break*/, 25];
255
- case 30: return [3 /*break*/, 33];
256
- case 31:
267
+ return [3 /*break*/, 24];
268
+ case 29: return [3 /*break*/, 32];
269
+ case 30:
257
270
  e_4_1 = _g.sent();
258
271
  e_4 = { error: e_4_1 };
259
- return [3 /*break*/, 33];
260
- case 32:
272
+ return [3 /*break*/, 32];
273
+ case 31:
261
274
  try {
262
275
  if (_b && !_b.done && (_e = _a["return"])) _e.call(_a);
263
276
  }
264
277
  finally { if (e_4) throw e_4.error; }
265
278
  return [7 /*endfinally*/];
266
- case 33:
279
+ case 32:
267
280
  if (!res) {
268
281
  throw new errors_1.TransportError("service not found", "BLEServiceNotFound");
269
282
  }
270
283
  deviceModel = res.deviceModel, serviceUuid = res.serviceUuid, writeUuid = res.writeUuid, writeCmdUuid = res.writeCmdUuid, notifyUuid = res.notifyUuid;
271
- if (!!characteristics) return [3 /*break*/, 35];
284
+ if (!!characteristics) return [3 /*break*/, 34];
272
285
  return [4 /*yield*/, device.characteristicsForService(serviceUuid)];
273
- case 34:
286
+ case 33:
274
287
  characteristics = _g.sent();
275
- _g.label = 35;
276
- case 35:
288
+ _g.label = 34;
289
+ case 34:
277
290
  if (!characteristics) {
278
291
  throw new errors_1.TransportError("service not found", "BLEServiceNotFound");
279
292
  }
@@ -299,23 +312,23 @@ function open(deviceOrId, needsReconnect) {
299
312
  finally { if (e_5) throw e_5.error; }
300
313
  }
301
314
  if (!writeC) {
302
- throw new errors_1.TransportError("write characteristic not found", "BLEChracteristicNotFound");
315
+ throw new errors_1.TransportError("write characteristic not found", "BLECharacteristicNotFound");
303
316
  }
304
317
  if (!notifyC) {
305
- throw new errors_1.TransportError("notify characteristic not found", "BLEChracteristicNotFound");
318
+ throw new errors_1.TransportError("notify characteristic not found", "BLECharacteristicNotFound");
306
319
  }
307
320
  if (!writeC.isWritableWithResponse) {
308
- throw new errors_1.TransportError("write characteristic not writableWithResponse", "BLEChracteristicInvalid");
321
+ throw new errors_1.TransportError("write characteristic not writableWithResponse", "BLECharacteristicInvalid");
309
322
  }
310
323
  if (!notifyC.isNotifiable) {
311
- throw new errors_1.TransportError("notify characteristic not notifiable", "BLEChracteristicInvalid");
324
+ throw new errors_1.TransportError("notify characteristic not notifiable", "BLECharacteristicInvalid");
312
325
  }
313
326
  if (writeCmdC) {
314
327
  if (!writeCmdC.isWritableWithoutResponse) {
315
- throw new errors_1.TransportError("write cmd characteristic not writableWithoutResponse", "BLEChracteristicInvalid");
328
+ throw new errors_1.TransportError("write cmd characteristic not writableWithoutResponse", "BLECharacteristicInvalid");
316
329
  }
317
330
  }
318
- (0, logs_1.log)("ble-verbose", "device.mtu=".concat(device.mtu));
331
+ (0, logs_1.log)(TAG, "device.mtu=".concat(device.mtu));
319
332
  notifyObservable = (0, monitorCharacteristic_1.monitorCharacteristic)(notifyC).pipe((0, operators_1.catchError)(function (e) {
320
333
  // LL-9033 fw 2.0.2 introduced this case, we silence the inner unhandled error.
321
334
  var msg = String(e);
@@ -328,58 +341,57 @@ function open(deviceOrId, needsReconnect) {
328
341
  (0, logs_1.log)("ble-frame", "<= " + value.toString("hex"));
329
342
  }), (0, operators_1.share)());
330
343
  notif = notifyObservable.subscribe();
331
- transport = new BluetoothTransport(device, writeC, writeCmdC, notifyObservable, deviceModel);
332
- return [4 /*yield*/, transport.requestConnectionPriority("High")];
333
- case 36:
334
- _g.sent();
344
+ transport = new BleTransport(device, writeC, writeCmdC, notifyObservable, deviceModel);
335
345
  onDisconnect = function (e) {
346
+ transport.isConnected = false;
336
347
  transport.notYetDisconnected = false;
337
348
  notif.unsubscribe();
338
- disconnectedSub.remove();
349
+ disconnectedSub === null || disconnectedSub === void 0 ? void 0 : disconnectedSub.remove();
350
+ clearDisconnectTimeout(transport.id);
339
351
  delete transportsCache[transport.id];
340
- (0, logs_1.log)("ble-verbose", "BleTransport(".concat(transport.id, ") disconnected"));
352
+ (0, logs_1.log)(TAG, "BleTransport(".concat(transport.id, ") disconnected"));
341
353
  transport.emit("disconnect", e);
342
354
  };
343
355
  // eslint-disable-next-line require-atomic-updates
344
356
  transportsCache[transport.id] = transport;
357
+ beforeMTUTime = Date.now();
345
358
  disconnectedSub = device.onDisconnected(function (e) {
346
359
  if (!transport.notYetDisconnected)
347
360
  return;
348
361
  onDisconnect(e);
349
362
  });
350
- beforeMTUTime = Date.now();
351
- _g.label = 37;
352
- case 37:
353
- _g.trys.push([37, , 39, 45]);
363
+ _g.label = 35;
364
+ case 35:
365
+ _g.trys.push([35, , 37, 43]);
354
366
  return [4 /*yield*/, transport.inferMTU()];
355
- case 38:
367
+ case 36:
356
368
  _g.sent();
357
- return [3 /*break*/, 45];
358
- case 39:
369
+ return [3 /*break*/, 43];
370
+ case 37:
359
371
  afterMTUTime = Date.now();
360
- if (!reconnectionConfig) return [3 /*break*/, 43];
372
+ if (!reconnectionConfig) return [3 /*break*/, 41];
361
373
  // workaround for #279: we need to open() again if we come the first time here,
362
374
  // to make sure we do a disconnect() after the first pairing time
363
375
  // because of a firmware bug
364
376
  if (afterMTUTime - beforeMTUTime < reconnectionConfig.pairingThreshold) {
365
377
  needsReconnect = false; // (optim) there is likely no new pairing done because mtu answer was fast.
366
378
  }
367
- if (!needsReconnect) return [3 /*break*/, 42];
379
+ if (!needsReconnect) return [3 /*break*/, 40];
368
380
  // necessary time for the bonding workaround
369
- return [4 /*yield*/, BluetoothTransport.disconnect(transport.id)["catch"](function () { })];
370
- case 40:
381
+ return [4 /*yield*/, BleTransport.disconnect(transport.id)["catch"](function () { })];
382
+ case 38:
371
383
  // necessary time for the bonding workaround
372
384
  _g.sent();
373
385
  return [4 /*yield*/, delay(reconnectionConfig.delayAfterFirstPairing)];
374
- case 41:
386
+ case 39:
375
387
  _g.sent();
388
+ _g.label = 40;
389
+ case 40: return [3 /*break*/, 42];
390
+ case 41:
391
+ needsReconnect = false;
376
392
  _g.label = 42;
377
- case 42: return [3 /*break*/, 44];
393
+ case 42: return [7 /*endfinally*/];
378
394
  case 43:
379
- needsReconnect = false;
380
- _g.label = 44;
381
- case 44: return [7 /*endfinally*/];
382
- case 45:
383
395
  if (needsReconnect) {
384
396
  return [2 /*return*/, open(device, false)];
385
397
  }
@@ -391,16 +403,22 @@ function open(deviceOrId, needsReconnect) {
391
403
  /**
392
404
  * react-native bluetooth BLE implementation
393
405
  * @example
394
- * import BluetoothTransport from "@ledgerhq/react-native-hw-transport-ble";
406
+ * import BleTransport from "@ledgerhq/react-native-hw-transport-ble";
395
407
  */
396
- var BluetoothTransport = /** @class */ (function (_super) {
397
- __extends(BluetoothTransport, _super);
398
- function BluetoothTransport(device, writeCharacteristic, writeCmdCharacteristic, notifyObservable, deviceModel) {
408
+ var TAG = "ble-verbose";
409
+ var BleTransport = /** @class */ (function (_super) {
410
+ __extends(BleTransport, _super);
411
+ function BleTransport(device, writeCharacteristic, writeCmdCharacteristic, notifyObservable, deviceModel) {
399
412
  var _this = _super.call(this) || this;
413
+ _this.disconnectTimeout = null;
414
+ _this.isConnected = true;
400
415
  _this.mtuSize = 20;
401
416
  _this.notYetDisconnected = true;
402
417
  /**
403
- * communicate with a BLE transport
418
+ * Send data to the device using a low level API.
419
+ * It's recommended to use the "send" method for a higher level API.
420
+ * @param {Buffer} apdu - The data to send.
421
+ * @returns {Promise<Buffer>} A promise that resolves with the response data from the device.
404
422
  */
405
423
  _this.exchange = function (apdu) {
406
424
  return _this.exchangeAtomicImpl(function () { return __awaiter(_this, void 0, void 0, function () {
@@ -411,9 +429,7 @@ var BluetoothTransport = /** @class */ (function (_super) {
411
429
  _b.trys.push([0, 2, , 5]);
412
430
  msgIn = apdu.toString("hex");
413
431
  (0, logs_1.log)("apdu", "=> ".concat(msgIn));
414
- return [4 /*yield*/, (0, rxjs_1.merge)(
415
- // $FlowFixMe
416
- this.notifyObservable.pipe(receiveAPDU_1.receiveAPDU), (0, sendAPDU_1.sendAPDU)(this.write, apdu, this.mtuSize)).toPromise()];
432
+ return [4 /*yield*/, (0, rxjs_1.merge)(this.notifyObservable.pipe(receiveAPDU_1.receiveAPDU), (0, sendAPDU_1.sendAPDU)(this.write, apdu, this.mtuSize)).toPromise()];
417
433
  case 1:
418
434
  data = _b.sent();
419
435
  msgOut = data.toString("hex");
@@ -436,6 +452,12 @@ var BluetoothTransport = /** @class */ (function (_super) {
436
452
  });
437
453
  }); });
438
454
  };
455
+ /**
456
+ * Do not call this directly unless you know what you're doing. Communication
457
+ * with a Ledger device should be through the {@link exchange} method.
458
+ * @param buffer
459
+ * @param txid
460
+ */
439
461
  _this.write = function (buffer, txid) { return __awaiter(_this, void 0, void 0, function () {
440
462
  var e_7, e_8;
441
463
  return __generator(this, function (_b) {
@@ -473,15 +495,17 @@ var BluetoothTransport = /** @class */ (function (_super) {
473
495
  _this.writeCmdCharacteristic = writeCmdCharacteristic;
474
496
  _this.notifyObservable = notifyObservable;
475
497
  _this.deviceModel = deviceModel;
476
- (0, logs_1.log)("ble-verbose", "BleTransport(".concat(String(_this.id), ") new instance"));
498
+ (0, logs_1.log)(TAG, "BleTransport(".concat(String(_this.id), ") new instance"));
499
+ clearDisconnectTimeout(_this.id);
477
500
  return _this;
478
501
  }
479
502
  /**
480
- * TODO could add this concept in all transports
481
- * observe event with { available: bool, string } // available is generic, type is specific
482
- * an event is emit once and then listened
503
+ * Listen to state changes on the bleManagerInstance and notify the
504
+ * specified observer.
505
+ * @param observer
506
+ * @returns TransportSubscription
483
507
  */
484
- BluetoothTransport.observeState = function (observer) {
508
+ BleTransport.observeState = function (observer) {
485
509
  var emitFromState = function (type) {
486
510
  observer.next({
487
511
  type: type,
@@ -495,48 +519,52 @@ var BluetoothTransport = /** @class */ (function (_super) {
495
519
  };
496
520
  /**
497
521
  * Scan for bluetooth Ledger devices
522
+ * @param observer Device is partial in order to avoid the live-common/this dep
523
+ * @returns TransportSubscription
498
524
  */
499
- BluetoothTransport.listen = function (observer) {
525
+ BleTransport.listen = function (observer) {
500
526
  var _this = this;
501
- (0, logs_1.log)("ble-verbose", "listen...");
527
+ (0, logs_1.log)(TAG, "listening for devices");
502
528
  var unsubscribed;
503
529
  var stateSub = bleManagerInstance().onStateChange(function (state) { return __awaiter(_this, void 0, void 0, function () {
504
530
  var devices;
505
531
  return __generator(this, function (_b) {
506
532
  switch (_b.label) {
507
533
  case 0:
508
- if (!(state === "PoweredOn")) return [3 /*break*/, 3];
534
+ if (!(state === "PoweredOn")) return [3 /*break*/, 4];
509
535
  stateSub.remove();
510
536
  return [4 /*yield*/, bleManagerInstance().connectedDevices((0, devices_1.getBluetoothServiceUuids)())];
511
537
  case 1:
512
538
  devices = _b.sent();
513
539
  if (unsubscribed)
514
540
  return [2 /*return*/];
515
- return [4 /*yield*/, Promise.all(devices.map(function (d) {
516
- return BluetoothTransport.disconnect(d.id)["catch"](function () { });
517
- }))];
541
+ if (!devices.length) return [3 /*break*/, 3];
542
+ (0, logs_1.log)(TAG, "disconnecting from devices");
543
+ return [4 /*yield*/, Promise.all(devices.map(function (d) { return BleTransport.disconnect(d.id)["catch"](function () { }); }))];
518
544
  case 2:
519
545
  _b.sent();
546
+ _b.label = 3;
547
+ case 3:
520
548
  if (unsubscribed)
521
549
  return [2 /*return*/];
522
- bleManagerInstance().startDeviceScan((0, devices_1.getBluetoothServiceUuids)(), null, function (bleError, device) {
550
+ bleManagerInstance().startDeviceScan((0, devices_1.getBluetoothServiceUuids)(), null, function (bleError, scannedDevice) {
523
551
  if (bleError) {
524
- observer.error(mapBleErrorToHwTransportError(bleError));
552
+ observer.error((0, remapErrors_1.mapBleErrorToHwTransportError)(bleError));
525
553
  unsubscribe();
526
554
  return;
527
555
  }
528
- var res = retrieveInfos(device);
556
+ var res = retrieveInfos(scannedDevice);
529
557
  var deviceModel = res && res.deviceModel;
530
- if (device) {
558
+ if (scannedDevice) {
531
559
  observer.next({
532
560
  type: "add",
533
- descriptor: device,
561
+ descriptor: scannedDevice,
534
562
  deviceModel: deviceModel
535
563
  });
536
564
  }
537
565
  });
538
- _b.label = 3;
539
- case 3: return [2 /*return*/];
566
+ _b.label = 4;
567
+ case 4: return [2 /*return*/];
540
568
  }
541
569
  });
542
570
  }); }, true);
@@ -544,7 +572,7 @@ var BluetoothTransport = /** @class */ (function (_super) {
544
572
  unsubscribed = true;
545
573
  bleManagerInstance().stopDeviceScan();
546
574
  stateSub.remove();
547
- (0, logs_1.log)("ble-verbose", "done listening.");
575
+ (0, logs_1.log)(TAG, "done listening.");
548
576
  };
549
577
  return {
550
578
  unsubscribe: unsubscribe
@@ -552,17 +580,20 @@ var BluetoothTransport = /** @class */ (function (_super) {
552
580
  };
553
581
  /**
554
582
  * Open a BLE transport
555
- * @param {*} deviceOrId
583
+ * @param {Device | string} deviceOrId
556
584
  */
557
- BluetoothTransport.open = function (deviceOrId) {
585
+ BleTransport.open = function (deviceOrId) {
558
586
  return __awaiter(this, void 0, void 0, function () {
559
587
  return __generator(this, function (_b) {
560
588
  return [2 /*return*/, open(deviceOrId, true)];
561
589
  });
562
590
  });
563
591
  };
564
- // TODO we probably will do this at end of open
565
- BluetoothTransport.prototype.inferMTU = function () {
592
+ /**
593
+ * Negotiate with the device the maximum transfer unit for the ble frames
594
+ * @returns Promise<number>
595
+ */
596
+ BleTransport.prototype.inferMTU = function () {
566
597
  return __awaiter(this, void 0, void 0, function () {
567
598
  var mtu, mtuSize;
568
599
  var _this = this;
@@ -587,7 +618,7 @@ var BluetoothTransport = /** @class */ (function (_super) {
587
618
  return [3 /*break*/, 4];
588
619
  case 2:
589
620
  e_9 = _b.sent();
590
- (0, logs_1.log)("ble-error", "inferMTU got " + String(e_9));
621
+ (0, logs_1.log)("ble-error", "inferMTU got " + JSON.stringify(e_9));
591
622
  return [4 /*yield*/, bleManagerInstance()
592
623
  .cancelDeviceConnection(this.id)["catch"](function () { })];
593
624
  case 3:
@@ -601,7 +632,7 @@ var BluetoothTransport = /** @class */ (function (_super) {
601
632
  _b.sent();
602
633
  if (mtu > 23) {
603
634
  mtuSize = mtu - 3;
604
- (0, logs_1.log)("ble-verbose", "BleTransport(".concat(String(this.id), ") mtu set to ").concat(String(mtuSize)));
635
+ (0, logs_1.log)(TAG, "BleTransport(".concat(this.id, ") mtu set to ").concat(mtuSize));
605
636
  this.mtuSize = mtuSize;
606
637
  }
607
638
  return [2 /*return*/, this.mtuSize];
@@ -609,84 +640,112 @@ var BluetoothTransport = /** @class */ (function (_super) {
609
640
  });
610
641
  });
611
642
  };
612
- BluetoothTransport.prototype.requestConnectionPriority = function (connectionPriority) {
643
+ /**
644
+ * Exposed method from the ble-plx library
645
+ * Request the connection priority for the given device.
646
+ * @param {"Balanced" | "High" | "LowPower"} connectionPriority: Connection priority.
647
+ * @returns {Promise<Device>} Connected device.
648
+ */
649
+ BleTransport.prototype.requestConnectionPriority = function (connectionPriority) {
613
650
  return __awaiter(this, void 0, void 0, function () {
614
651
  return __generator(this, function (_b) {
615
652
  switch (_b.label) {
616
653
  case 0: return [4 /*yield*/, (0, remapErrors_1.decoratePromiseErrors)(this.device.requestConnectionPriority(react_native_ble_plx_1.ConnectionPriority[connectionPriority]))];
617
- case 1:
618
- _b.sent();
619
- return [2 /*return*/];
654
+ case 1: return [2 /*return*/, _b.sent()];
620
655
  }
621
656
  });
622
657
  });
623
658
  };
624
- BluetoothTransport.prototype.setScrambleKey = function () { };
625
- BluetoothTransport.prototype.close = function () {
659
+ /**
660
+ * We intentionally do not immediately close a transport connection.
661
+ * Instead, we queue the disconnect and wait for a future connection to dismiss the event.
662
+ * This approach prevents unnecessary disconnects and reconnects. We use the isConnected
663
+ * flag to ensure that we do not trigger a disconnect if the current cached transport has
664
+ * already been disconnected.
665
+ * @returns {Promise<void>}
666
+ */
667
+ BleTransport.prototype.close = function () {
626
668
  return __awaiter(this, void 0, void 0, function () {
669
+ var resolve, disconnectPromise;
670
+ var _this = this;
627
671
  return __generator(this, function (_b) {
628
672
  switch (_b.label) {
629
673
  case 0:
630
- if (!this.exchangeBusyPromise) return [3 /*break*/, 2];
631
- return [4 /*yield*/, this.exchangeBusyPromise];
674
+ disconnectPromise = new Promise(function (innerResolve) {
675
+ resolve = innerResolve;
676
+ });
677
+ clearDisconnectTimeout(this.id);
678
+ (0, logs_1.log)(TAG, "Queuing a disconnect");
679
+ this.disconnectTimeout = setTimeout(function () {
680
+ (0, logs_1.log)(TAG, "Triggering a disconnect from ".concat(_this.id));
681
+ if (_this.isConnected) {
682
+ BleTransport.disconnect(_this.id)["catch"](function () { })["finally"](resolve);
683
+ }
684
+ else {
685
+ resolve();
686
+ }
687
+ }, BleTransport.disconnectTimeoutMs);
688
+ // The closure will occur no later than 5s, triggered either by disconnection
689
+ // or the actual response of the apdu.
690
+ return [4 /*yield*/, Promise.race([
691
+ this.exchangeBusyPromise || Promise.resolve(),
692
+ disconnectPromise,
693
+ ])];
632
694
  case 1:
695
+ // The closure will occur no later than 5s, triggered either by disconnection
696
+ // or the actual response of the apdu.
633
697
  _b.sent();
634
- _b.label = 2;
635
- case 2: return [2 /*return*/];
698
+ return [2 /*return*/];
636
699
  }
637
700
  });
638
701
  });
639
702
  };
640
703
  var _a;
641
- _a = BluetoothTransport;
704
+ _a = BleTransport;
705
+ BleTransport.disconnectTimeoutMs = 5000;
642
706
  /**
643
707
  *
644
708
  */
645
- BluetoothTransport.isSupported = function () {
709
+ BleTransport.isSupported = function () {
646
710
  return Promise.resolve(typeof react_native_ble_plx_1.BleManager === "function");
647
711
  };
648
712
  /**
649
713
  *
650
714
  */
651
- BluetoothTransport.setLogLevel = function (level) {
652
- bleManagerInstance().setLogLevel(level);
653
- };
654
- BluetoothTransport.list = function () {
715
+ BleTransport.list = function () {
655
716
  throw new Error("not implemented");
656
717
  };
657
718
  /**
658
- * Globally disconnect a BLE device by its ID
719
+ * Exposed method from the ble-plx library
720
+ * Sets new log level for native module's logging mechanism.
721
+ * @param string logLevel New log level to be set.
659
722
  */
660
- BluetoothTransport.disconnect = function (id) { return __awaiter(void 0, void 0, void 0, function () {
723
+ BleTransport.setLogLevel = function (logLevel) {
724
+ if (Object.values(react_native_ble_plx_1.LogLevel).includes(logLevel)) {
725
+ bleManagerInstance().setLogLevel(logLevel);
726
+ }
727
+ else {
728
+ throw new Error("".concat(logLevel, " is not a valid LogLevel"));
729
+ }
730
+ };
731
+ /**
732
+ * Exposed method from the ble-plx library
733
+ * Disconnects from {@link Device} if it's connected or cancels pending connection.
734
+ */
735
+ BleTransport.disconnect = function (id) { return __awaiter(void 0, void 0, void 0, function () {
661
736
  return __generator(_a, function (_b) {
662
737
  switch (_b.label) {
663
738
  case 0:
664
- (0, logs_1.log)("ble-verbose", "user disconnect(".concat(id, ")"));
739
+ (0, logs_1.log)(TAG, "user disconnect(".concat(id, ")"));
665
740
  return [4 /*yield*/, bleManagerInstance().cancelDeviceConnection(id)];
666
741
  case 1:
667
742
  _b.sent();
743
+ (0, logs_1.log)(TAG, "disconnected");
668
744
  return [2 /*return*/];
669
745
  }
670
746
  });
671
747
  }); };
672
- return BluetoothTransport;
748
+ return BleTransport;
673
749
  }(hw_transport_1["default"]));
674
- exports["default"] = BluetoothTransport;
675
- var bleErrorToHwTransportError = new Map([
676
- [react_native_ble_plx_1.BleErrorCode.ScanStartFailed, errors_1.HwTransportErrorType.BleScanStartFailed],
677
- [
678
- react_native_ble_plx_1.BleErrorCode.LocationServicesDisabled,
679
- errors_1.HwTransportErrorType.BleLocationServicesDisabled,
680
- ],
681
- [
682
- react_native_ble_plx_1.BleErrorCode.BluetoothUnauthorized,
683
- errors_1.HwTransportErrorType.BleBluetoothUnauthorized,
684
- ],
685
- ]);
686
- var mapBleErrorToHwTransportError = function (bleError) {
687
- var message = "".concat(bleError.message, ". Origin: ").concat(bleError.errorCode);
688
- var inferedType = bleErrorToHwTransportError.get(bleError.errorCode);
689
- var type = !inferedType ? errors_1.HwTransportErrorType.Unknown : inferedType;
690
- return new errors_1.HwTransportError(type, message);
691
- };
750
+ exports["default"] = BleTransport;
692
751
  //# sourceMappingURL=BleTransport.js.map