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