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

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