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