@ledgerhq/react-native-hw-transport-ble 6.34.0 → 6.34.1-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.
@@ -1,17 +1,7 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
12
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
4
  };
14
- var _a;
15
5
  Object.defineProperty(exports, "__esModule", { value: true });
16
6
  exports.setReconnectionConfig = void 0;
17
7
  const uuid_1 = require("uuid");
@@ -95,239 +85,240 @@ const clearDisconnectTimeout = (deviceId, context) => {
95
85
  * - rxjsScheduler: dependency injected RxJS scheduler to control time. Default AsyncScheduler.
96
86
  * @returns A BleTransport instance
97
87
  */
98
- function open(deviceOrId_1, needsReconnect_1, timeoutMs_1, context_1) {
99
- return __awaiter(this, arguments, void 0, function* (deviceOrId, needsReconnect, timeoutMs, context, { rxjsScheduler } = {}) {
100
- const tracer = new logs_1.LocalTracer(LOG_TYPE, context);
101
- let device;
102
- tracer.trace(`Opening ${deviceOrId}`, { needsReconnect });
103
- if (typeof deviceOrId === "string") {
104
- if (transportsCache[deviceOrId]) {
105
- tracer.trace(`Transport in cache, using it`);
106
- clearDisconnectTimeout(deviceOrId);
107
- // The cached transport probably has an older trace/log context
108
- transportsCache[deviceOrId].setTraceContext(context);
109
- return transportsCache[deviceOrId];
110
- }
111
- tracer.trace(`Trying to open device: ${deviceOrId}`);
112
- yield BlePlxManager_1.BlePlxManager.waitOn();
113
- // Returns a list of known devices by their identifiers
114
- device = yield BlePlxManager_1.BlePlxManager.getKnownDevice(deviceOrId);
115
- if (!device) {
116
- tracer.trace(`Found already known device with given id`, { deviceOrId });
117
- // Returns a list of the peripherals currently connected to the system
118
- // which have discovered services, connected to system doesn't mean
119
- // connected to our app, we check that below.
120
- const connectedDevices = yield BlePlxManager_1.BlePlxManager.getConnectedDevices();
121
- const connectedDevicesFiltered = connectedDevices.filter(d => d.id === deviceOrId);
122
- tracer.trace(`No known device with given id. Found ${connectedDevicesFiltered.length} devices from already connected devices`, { deviceOrId });
123
- [device] = connectedDevicesFiltered;
124
- }
125
- if (!device) {
126
- // We still don't have a device, so we attempt to connect to it.
127
- tracer.trace(`No known nor connected devices with given id. Trying to connect to device`, {
128
- deviceOrId,
129
- timeoutMs,
130
- });
131
- // Nb ConnectionOptions dropped since it's not used internally by ble-plx.
132
- try {
133
- device = yield BlePlxManager_1.BlePlxManager.connect(deviceOrId, Object.assign(Object.assign({}, connectOptions), { timeout: timeoutMs }));
134
- }
135
- catch (e) {
136
- tracer.trace(`Error code: ${e.errorCode}`);
137
- if (e.errorCode === react_native_ble_plx_1.BleErrorCode.DeviceMTUChangeFailed) {
138
- // If the MTU update did not work, we try to connect without requesting for a specific MTU
139
- connectOptions = {};
140
- device = yield BlePlxManager_1.BlePlxManager.connect(deviceOrId, { timeout: timeoutMs });
141
- }
142
- else {
143
- throw e;
144
- }
145
- }
146
- }
147
- if (!device) {
148
- throw new errors_1.CantOpenDevice();
149
- }
88
+ async function open(deviceOrId, needsReconnect, timeoutMs, context, { rxjsScheduler } = {}) {
89
+ const tracer = new logs_1.LocalTracer(LOG_TYPE, context);
90
+ let device;
91
+ tracer.trace(`Opening ${deviceOrId}`, { needsReconnect });
92
+ if (typeof deviceOrId === "string") {
93
+ if (transportsCache[deviceOrId]) {
94
+ tracer.trace(`Transport in cache, using it`);
95
+ clearDisconnectTimeout(deviceOrId);
96
+ // The cached transport probably has an older trace/log context
97
+ transportsCache[deviceOrId].setTraceContext(context);
98
+ return transportsCache[deviceOrId];
150
99
  }
151
- else {
152
- // It was already a Device
153
- device = deviceOrId;
100
+ tracer.trace(`Trying to open device: ${deviceOrId}`);
101
+ await BlePlxManager_1.BlePlxManager.waitOn();
102
+ // Returns a list of known devices by their identifiers
103
+ device = await BlePlxManager_1.BlePlxManager.getKnownDevice(deviceOrId);
104
+ if (!device) {
105
+ tracer.trace(`Found already known device with given id`, { deviceOrId });
106
+ // Returns a list of the peripherals currently connected to the system
107
+ // which have discovered services, connected to system doesn't mean
108
+ // connected to our app, we check that below.
109
+ const connectedDevices = await BlePlxManager_1.BlePlxManager.getConnectedDevices();
110
+ const connectedDevicesFiltered = connectedDevices.filter(d => d.id === deviceOrId);
111
+ tracer.trace(`No known device with given id. Found ${connectedDevicesFiltered.length} devices from already connected devices`, { deviceOrId });
112
+ [device] = connectedDevicesFiltered;
154
113
  }
155
- if (!(yield device.isConnected())) {
156
- tracer.trace(`Device found but not connected. connecting...`, { timeoutMs, connectOptions });
114
+ if (!device) {
115
+ // We still don't have a device, so we attempt to connect to it.
116
+ tracer.trace(`No known nor connected devices with given id. Trying to connect to device`, {
117
+ deviceOrId,
118
+ timeoutMs,
119
+ });
120
+ // Nb ConnectionOptions dropped since it's not used internally by ble-plx.
157
121
  try {
158
- yield device.connect(Object.assign(Object.assign({}, connectOptions), { timeout: timeoutMs }));
122
+ device = await BlePlxManager_1.BlePlxManager.connect(deviceOrId, {
123
+ ...connectOptions,
124
+ timeout: timeoutMs,
125
+ });
159
126
  }
160
- catch (error) {
161
- tracer.trace(`Connect error`, { error });
162
- if (error.errorCode === react_native_ble_plx_1.BleErrorCode.DeviceMTUChangeFailed) {
163
- tracer.trace(`Device mtu=${device.mtu}, reconnecting`);
127
+ catch (e) {
128
+ tracer.trace(`Error code: ${e.errorCode}`);
129
+ if (e.errorCode === react_native_ble_plx_1.BleErrorCode.DeviceMTUChangeFailed) {
130
+ // If the MTU update did not work, we try to connect without requesting for a specific MTU
164
131
  connectOptions = {};
165
- yield device.connect();
166
- }
167
- else if (error.iosErrorCode === 14 || error.reason === "Peer removed pairing information") {
168
- tracer.trace(`iOS broken pairing`, {
169
- device,
170
- bluetoothInfoCache: bluetoothInfoCache[device.id],
171
- });
172
- const { deviceModel } = bluetoothInfoCache[device.id] || {};
173
- const { productName } = deviceModel || {};
174
- throw new errors_1.PeerRemovedPairing(undefined, {
175
- deviceName: device.name,
176
- productName,
177
- });
178
- }
179
- // This case should not happen, but recording logs to be warned if we see it in production
180
- else if (error.errorCode === react_native_ble_plx_1.BleErrorCode.DeviceAlreadyConnected) {
181
- tracer.trace(`Device already connected, while it was not supposed to`, {
182
- error,
183
- });
184
- throw (0, remapErrors_1.remapError)(error);
132
+ device = await BlePlxManager_1.BlePlxManager.connect(deviceOrId, { timeout: timeoutMs });
185
133
  }
186
134
  else {
187
- throw (0, remapErrors_1.remapError)(error);
135
+ throw e;
188
136
  }
189
137
  }
190
138
  }
191
- tracer.trace(`Device is connected now, getting services and characteristics`);
192
- yield device.discoverAllServicesAndCharacteristics();
193
- let res = retrieveInfos(device);
194
- let characteristics;
195
- if (!res) {
196
- for (const uuid of (0, devices_1.getBluetoothServiceUuids)()) {
197
- try {
198
- characteristics = yield device.characteristicsForService(uuid);
199
- res = (0, devices_1.getInfosForServiceUuid)(uuid);
200
- break;
201
- }
202
- catch (e) {
203
- // we attempt to connect to service
204
- }
205
- }
139
+ if (!device) {
140
+ throw new errors_1.CantOpenDevice();
206
141
  }
207
- if (!res) {
208
- tracer.trace(`Service not found`);
209
- throw new errors_1.TransportError("service not found", "BLEServiceNotFound");
210
- }
211
- const { deviceModel, serviceUuid, writeUuid, writeCmdUuid, notifyUuid } = res;
212
- if (!characteristics) {
213
- characteristics = yield device.characteristicsForService(serviceUuid);
214
- }
215
- if (!characteristics) {
216
- tracer.trace(`Characteristics not found`);
217
- throw new errors_1.TransportError("service not found", "BLEServiceNotFound");
142
+ }
143
+ else {
144
+ // It was already a Device
145
+ device = deviceOrId;
146
+ }
147
+ if (!(await device.isConnected())) {
148
+ tracer.trace(`Device found but not connected. connecting...`, { timeoutMs, connectOptions });
149
+ try {
150
+ await device.connect({ ...connectOptions, timeout: timeoutMs });
218
151
  }
219
- let writableWithResponseCharacteristic;
220
- let writableWithoutResponseCharacteristic;
221
- // A characteristic that can monitor value changes
222
- let notifiableCharacteristic;
223
- for (const c of characteristics) {
224
- if (c.uuid === writeUuid) {
225
- writableWithResponseCharacteristic = c;
152
+ catch (error) {
153
+ tracer.trace(`Connect error`, { error });
154
+ if (error.errorCode === react_native_ble_plx_1.BleErrorCode.DeviceMTUChangeFailed) {
155
+ tracer.trace(`Device mtu=${device.mtu}, reconnecting`);
156
+ connectOptions = {};
157
+ await device.connect();
158
+ }
159
+ else if (error.iosErrorCode === 14 || error.reason === "Peer removed pairing information") {
160
+ tracer.trace(`iOS broken pairing`, {
161
+ device,
162
+ bluetoothInfoCache: bluetoothInfoCache[device.id],
163
+ });
164
+ const { deviceModel } = bluetoothInfoCache[device.id] || {};
165
+ const { productName } = deviceModel || {};
166
+ throw new errors_1.PeerRemovedPairing(undefined, {
167
+ deviceName: device.name,
168
+ productName,
169
+ });
226
170
  }
227
- else if (c.uuid === writeCmdUuid) {
228
- writableWithoutResponseCharacteristic = c;
171
+ // This case should not happen, but recording logs to be warned if we see it in production
172
+ else if (error.errorCode === react_native_ble_plx_1.BleErrorCode.DeviceAlreadyConnected) {
173
+ tracer.trace(`Device already connected, while it was not supposed to`, {
174
+ error,
175
+ });
176
+ throw (0, remapErrors_1.remapError)(error);
229
177
  }
230
- else if (c.uuid === notifyUuid) {
231
- notifiableCharacteristic = c;
178
+ else {
179
+ throw (0, remapErrors_1.remapError)(error);
232
180
  }
233
181
  }
234
- if (!writableWithResponseCharacteristic) {
235
- throw new errors_1.TransportError("write characteristic not found", "BLECharacteristicNotFound");
236
- }
237
- if (!notifiableCharacteristic) {
238
- throw new errors_1.TransportError("notify characteristic not found", "BLECharacteristicNotFound");
182
+ }
183
+ tracer.trace(`Device is connected now, getting services and characteristics`);
184
+ await device.discoverAllServicesAndCharacteristics();
185
+ let res = retrieveInfos(device);
186
+ let characteristics;
187
+ if (!res) {
188
+ for (const uuid of (0, devices_1.getBluetoothServiceUuids)()) {
189
+ try {
190
+ characteristics = await device.characteristicsForService(uuid);
191
+ res = (0, devices_1.getInfosForServiceUuid)(uuid);
192
+ break;
193
+ }
194
+ catch (e) {
195
+ // we attempt to connect to service
196
+ }
239
197
  }
240
- if (!writableWithResponseCharacteristic.isWritableWithResponse) {
241
- throw new errors_1.TransportError("The writable-with-response characteristic is not writable with response", "BLECharacteristicInvalid");
198
+ }
199
+ if (!res) {
200
+ tracer.trace(`Service not found`);
201
+ throw new errors_1.TransportError("service not found", "BLEServiceNotFound");
202
+ }
203
+ const { deviceModel, serviceUuid, writeUuid, writeCmdUuid, notifyUuid } = res;
204
+ if (!characteristics) {
205
+ characteristics = await device.characteristicsForService(serviceUuid);
206
+ }
207
+ if (!characteristics) {
208
+ tracer.trace(`Characteristics not found`);
209
+ throw new errors_1.TransportError("service not found", "BLEServiceNotFound");
210
+ }
211
+ let writableWithResponseCharacteristic;
212
+ let writableWithoutResponseCharacteristic;
213
+ // A characteristic that can monitor value changes
214
+ let notifiableCharacteristic;
215
+ for (const c of characteristics) {
216
+ if (c.uuid === writeUuid) {
217
+ writableWithResponseCharacteristic = c;
242
218
  }
243
- if (!notifiableCharacteristic.isNotifiable) {
244
- throw new errors_1.TransportError("notify characteristic not notifiable", "BLECharacteristicInvalid");
219
+ else if (c.uuid === writeCmdUuid) {
220
+ writableWithoutResponseCharacteristic = c;
245
221
  }
246
- if (writableWithoutResponseCharacteristic) {
247
- if (!writableWithoutResponseCharacteristic.isWritableWithoutResponse) {
248
- throw new errors_1.TransportError("The writable-without-response characteristic is not writable without response", "BLECharacteristicInvalid");
249
- }
222
+ else if (c.uuid === notifyUuid) {
223
+ notifiableCharacteristic = c;
250
224
  }
251
- tracer.trace(`device.mtu=${device.mtu}`);
252
- // Inits the observable that will emit received data from the device via BLE
253
- const notifyObservable = (0, monitorCharacteristic_1.monitorCharacteristic)(notifiableCharacteristic, context).pipe((0, operators_1.catchError)(e => {
254
- // LL-9033 fw 2.0.2 introduced this case, we silence the inner unhandled error.
255
- // It will be handled when negotiating the MTU in `inferMTU` but will be ignored in other cases.
256
- const msg = String(e);
257
- return msg.includes("notify change failed")
258
- ? (0, rxjs_1.of)(new errors_1.PairingFailed(msg))
259
- : (0, rxjs_1.throwError)(() => e);
260
- }), (0, operators_1.tap)(value => {
261
- if (value instanceof errors_1.PairingFailed)
262
- return;
263
- (0, logs_1.trace)({ type: "ble-frame", message: `<= ${value.toString("hex")}`, context });
264
- }),
265
- // Returns a new Observable that multicasts (shares) the original Observable.
266
- // As long as there is at least one Subscriber this Observable will be subscribed and emitting data.
267
- (0, operators_1.share)());
268
- // Keeps the input from the device observable alive (multicast observable)
269
- const notif = notifyObservable.subscribe();
270
- const transport = new BleTransport(device, writableWithResponseCharacteristic, writableWithoutResponseCharacteristic, notifyObservable, deviceModel, {
271
- context,
272
- rxjsScheduler,
273
- });
274
- tracer.trace(`New BleTransport created`);
275
- // Keeping it as a comment for now but if no new bluetooth issues occur, we will be able to remove it
276
- // await transport.requestConnectionPriority("High");
277
- // eslint-disable-next-line prefer-const
278
- let disconnectedSub;
279
- // Callbacks on `react-native-ble-plx` notifying the device has been disconnected
280
- const onDisconnect = (error) => {
281
- transport.isConnected = false;
282
- transport.notYetDisconnected = false;
283
- notif.unsubscribe();
284
- disconnectedSub === null || disconnectedSub === void 0 ? void 0 : disconnectedSub.remove();
285
- clearDisconnectTimeout(transport.id);
286
- delete transportsCache[transport.id];
287
- tracer.trace(`On device disconnected callback: cleared cached transport for ${transport.id}, emitting Transport event "disconnect. Error: ${error}"`, { reason: error });
288
- transport.emit("disconnect", error);
289
- };
290
- // eslint-disable-next-line require-atomic-updates
291
- transportsCache[transport.id] = transport;
292
- const beforeMTUTime = Date.now();
293
- disconnectedSub = device.onDisconnected(e => {
294
- if (!transport.notYetDisconnected)
295
- return;
296
- onDisconnect(e);
297
- });
298
- try {
299
- yield transport.inferMTU();
225
+ }
226
+ if (!writableWithResponseCharacteristic) {
227
+ throw new errors_1.TransportError("write characteristic not found", "BLECharacteristicNotFound");
228
+ }
229
+ if (!notifiableCharacteristic) {
230
+ throw new errors_1.TransportError("notify characteristic not found", "BLECharacteristicNotFound");
231
+ }
232
+ if (!writableWithResponseCharacteristic.isWritableWithResponse) {
233
+ throw new errors_1.TransportError("The writable-with-response characteristic is not writable with response", "BLECharacteristicInvalid");
234
+ }
235
+ if (!notifiableCharacteristic.isNotifiable) {
236
+ throw new errors_1.TransportError("notify characteristic not notifiable", "BLECharacteristicInvalid");
237
+ }
238
+ if (writableWithoutResponseCharacteristic) {
239
+ if (!writableWithoutResponseCharacteristic.isWritableWithoutResponse) {
240
+ throw new errors_1.TransportError("The writable-without-response characteristic is not writable without response", "BLECharacteristicInvalid");
300
241
  }
301
- finally {
302
- const afterMTUTime = Date.now();
303
- if (reconnectionConfig) {
304
- // Refer to ledgerjs archived repo issue #279
305
- // All HW .v1 LNX have a bug that prevents us from communicating with the device right after pairing.
306
- // When we connect for the first time we issue a disconnect and reconnect, this guarantees that we are
307
- // in a good state. This is avoidable in some key scenarios ↓
308
- if (afterMTUTime - beforeMTUTime < reconnectionConfig.pairingThreshold) {
309
- needsReconnect = false;
310
- }
311
- else if (deviceModel.id === devices_1.DeviceModelId.stax) {
312
- tracer.trace(`Skipping "needsReconnect" strategy for Stax`);
313
- needsReconnect = false;
314
- }
315
- if (needsReconnect) {
316
- tracer.trace(`Device needs reconnection. Triggering a disconnect`);
317
- yield BleTransport.disconnectDevice(transport.id);
318
- yield delay(reconnectionConfig.delayAfterFirstPairing);
319
- }
242
+ }
243
+ tracer.trace(`device.mtu=${device.mtu}`);
244
+ // Inits the observable that will emit received data from the device via BLE
245
+ const notifyObservable = (0, monitorCharacteristic_1.monitorCharacteristic)(notifiableCharacteristic, context).pipe((0, operators_1.catchError)(e => {
246
+ // LL-9033 fw 2.0.2 introduced this case, we silence the inner unhandled error.
247
+ // It will be handled when negotiating the MTU in `inferMTU` but will be ignored in other cases.
248
+ const msg = String(e);
249
+ return msg.includes("notify change failed")
250
+ ? (0, rxjs_1.of)(new errors_1.PairingFailed(msg))
251
+ : (0, rxjs_1.throwError)(() => e);
252
+ }), (0, operators_1.tap)(value => {
253
+ if (value instanceof errors_1.PairingFailed)
254
+ return;
255
+ (0, logs_1.trace)({ type: "ble-frame", message: `<= ${value.toString("hex")}`, context });
256
+ }),
257
+ // Returns a new Observable that multicasts (shares) the original Observable.
258
+ // As long as there is at least one Subscriber this Observable will be subscribed and emitting data.
259
+ (0, operators_1.share)());
260
+ // Keeps the input from the device observable alive (multicast observable)
261
+ const notif = notifyObservable.subscribe();
262
+ const transport = new BleTransport(device, writableWithResponseCharacteristic, writableWithoutResponseCharacteristic, notifyObservable, deviceModel, {
263
+ context,
264
+ rxjsScheduler,
265
+ });
266
+ tracer.trace(`New BleTransport created`);
267
+ // Keeping it as a comment for now but if no new bluetooth issues occur, we will be able to remove it
268
+ // await transport.requestConnectionPriority("High");
269
+ // eslint-disable-next-line prefer-const
270
+ let disconnectedSub;
271
+ // Callbacks on `react-native-ble-plx` notifying the device has been disconnected
272
+ const onDisconnect = (error) => {
273
+ transport.isConnected = false;
274
+ transport.notYetDisconnected = false;
275
+ notif.unsubscribe();
276
+ disconnectedSub?.remove();
277
+ clearDisconnectTimeout(transport.id);
278
+ delete transportsCache[transport.id];
279
+ tracer.trace(`On device disconnected callback: cleared cached transport for ${transport.id}, emitting Transport event "disconnect. Error: ${error}"`, { reason: error });
280
+ transport.emit("disconnect", error);
281
+ };
282
+ // eslint-disable-next-line require-atomic-updates
283
+ transportsCache[transport.id] = transport;
284
+ const beforeMTUTime = Date.now();
285
+ disconnectedSub = device.onDisconnected(e => {
286
+ if (!transport.notYetDisconnected)
287
+ return;
288
+ onDisconnect(e);
289
+ });
290
+ try {
291
+ await transport.inferMTU();
292
+ }
293
+ finally {
294
+ const afterMTUTime = Date.now();
295
+ if (reconnectionConfig) {
296
+ // Refer to ledgerjs archived repo issue #279
297
+ // All HW .v1 LNX have a bug that prevents us from communicating with the device right after pairing.
298
+ // When we connect for the first time we issue a disconnect and reconnect, this guarantees that we are
299
+ // in a good state. This is avoidable in some key scenarios ↓
300
+ if (afterMTUTime - beforeMTUTime < reconnectionConfig.pairingThreshold) {
301
+ needsReconnect = false;
320
302
  }
321
- else {
303
+ else if (deviceModel.id === devices_1.DeviceModelId.stax) {
304
+ tracer.trace(`Skipping "needsReconnect" strategy for Stax`);
322
305
  needsReconnect = false;
323
306
  }
307
+ if (needsReconnect) {
308
+ tracer.trace(`Device needs reconnection. Triggering a disconnect`);
309
+ await BleTransport.disconnectDevice(transport.id);
310
+ await delay(reconnectionConfig.delayAfterFirstPairing);
311
+ }
324
312
  }
325
- if (needsReconnect) {
326
- tracer.trace(`Reconnecting`);
327
- return open(device, false, timeoutMs, context);
313
+ else {
314
+ needsReconnect = false;
328
315
  }
329
- return transport;
330
- });
316
+ }
317
+ if (needsReconnect) {
318
+ tracer.trace(`Reconnecting`);
319
+ return open(device, false, timeoutMs, context);
320
+ }
321
+ return transport;
331
322
  }
332
323
  /**
333
324
  * react-native bluetooth BLE implementation
@@ -335,6 +326,23 @@ function open(deviceOrId_1, needsReconnect_1, timeoutMs_1, context_1) {
335
326
  * import BleTransport from "@ledgerhq/react-native-hw-transport-ble";
336
327
  */
337
328
  class BleTransport extends hw_transport_1.default {
329
+ static disconnectTimeoutMs = 5000;
330
+ /**
331
+ *
332
+ */
333
+ static isSupported = () => Promise.resolve(typeof BlePlxManager_1.BlePlxManager === "function");
334
+ /**
335
+ *
336
+ */
337
+ static list = () => {
338
+ throw new Error("not implemented");
339
+ };
340
+ // /**
341
+ // * Exposed method from the ble-plx library
342
+ // * Sets new log level for native module's logging mechanism.
343
+ // * @param string logLevel New log level to be set.
344
+ // */
345
+ static setLogLevel = BlePlxManager_1.BlePlxManager.setLogLevel;
338
346
  /**
339
347
  * Listen to state changes on the BlePlxManagerInstance and notify the
340
348
  * specified observer.
@@ -353,6 +361,14 @@ class BleTransport extends hw_transport_1.default {
353
361
  unsubscribe: () => { },
354
362
  };
355
363
  }
364
+ static safeRemove = (sub, tracer) => {
365
+ try {
366
+ sub.remove();
367
+ }
368
+ catch (error) {
369
+ tracer.trace("Error removing state subscription", { error });
370
+ }
371
+ };
356
372
  /**
357
373
  * Scan for bluetooth Ledger devices
358
374
  * @param observer Device is partial in order to avoid the live-common/this dep
@@ -363,10 +379,10 @@ class BleTransport extends hw_transport_1.default {
363
379
  const tracer = new logs_1.LocalTracer(LOG_TYPE, context);
364
380
  tracer.trace("Listening for devices ...");
365
381
  let unsubscribed;
366
- const stateSub = BlePlxManager_1.BlePlxManager.onStateChange((state) => __awaiter(this, void 0, void 0, function* () {
382
+ const stateSub = BlePlxManager_1.BlePlxManager.onStateChange(async (state) => {
367
383
  if (state === "PoweredOn") {
368
- _a.safeRemove(stateSub, tracer);
369
- const devices = yield BlePlxManager_1.BlePlxManager.getConnectedDevices().catch(err => {
384
+ BleTransport.safeRemove(stateSub, tracer);
385
+ const devices = await BlePlxManager_1.BlePlxManager.getConnectedDevices().catch(err => {
370
386
  // Handle possible connection errors
371
387
  tracer.trace("Error while fetching connected devices", { err });
372
388
  return [];
@@ -376,7 +392,7 @@ class BleTransport extends hw_transport_1.default {
376
392
  if (devices.length) {
377
393
  tracer.trace("Disconnecting from all devices", { deviceCount: devices.length });
378
394
  try {
379
- yield Promise.all(devices.map(d => _a.disconnectDevice(d.id)));
395
+ await Promise.all(devices.map(d => BleTransport.disconnectDevice(d.id)));
380
396
  }
381
397
  catch (error) {
382
398
  tracer.trace("Error disconnecting some devices", { error });
@@ -384,7 +400,7 @@ class BleTransport extends hw_transport_1.default {
384
400
  }
385
401
  if (unsubscribed)
386
402
  return;
387
- yield BlePlxManager_1.BlePlxManager.startScan((bleError, scannedDevice) => {
403
+ await BlePlxManager_1.BlePlxManager.startScan((bleError, scannedDevice) => {
388
404
  if (bleError) {
389
405
  tracer.trace("Listening startDeviceScan error", { scannedDevice, bleError });
390
406
  observer.error((0, remapErrors_1.mapBleErrorToHwTransportError)(bleError));
@@ -402,13 +418,13 @@ class BleTransport extends hw_transport_1.default {
402
418
  }
403
419
  });
404
420
  }
405
- }), true);
421
+ }, true);
406
422
  const unsubscribe = () => {
407
423
  if (unsubscribed)
408
424
  return;
409
425
  unsubscribed = true;
410
426
  BlePlxManager_1.BlePlxManager.stopScan().then(() => {
411
- _a.safeRemove(stateSub, tracer);
427
+ BleTransport.safeRemove(stateSub, tracer);
412
428
  tracer.trace("Done listening");
413
429
  });
414
430
  };
@@ -425,11 +441,41 @@ class BleTransport extends hw_transport_1.default {
425
441
  * @param injectedDependencies Contains optional injected dependencies used by the transport implementation
426
442
  * - rxjsScheduler: dependency injected RxJS scheduler to control time. Default AsyncScheduler.
427
443
  */
428
- static open(deviceOrId_1, timeoutMs_1, context_1) {
429
- return __awaiter(this, arguments, void 0, function* (deviceOrId, timeoutMs, context, { rxjsScheduler } = {}) {
430
- return open(deviceOrId, true, timeoutMs, context, { rxjsScheduler });
431
- });
444
+ static async open(deviceOrId, timeoutMs, context, { rxjsScheduler } = {}) {
445
+ return open(deviceOrId, true, timeoutMs, context, { rxjsScheduler });
432
446
  }
447
+ /**
448
+ * Exposes method from the ble-plx library to disconnect a device
449
+ *
450
+ * Disconnects from {@link Device} if it's connected or cancels pending connection.
451
+ * A "disconnect" event will normally be emitted by the ble-plx lib once the device is disconnected.
452
+ * Errors are logged but silenced.
453
+ */
454
+ static disconnectDevice = async (id, context) => {
455
+ const tracer = new logs_1.LocalTracer(LOG_TYPE, context);
456
+ tracer.trace(`Trying to disconnect device ${id}`);
457
+ await BlePlxManager_1.BlePlxManager.disconnectDevice(id).catch(error => {
458
+ // Only log, ignore if disconnect did not work
459
+ tracer
460
+ .withType("ble-error")
461
+ .trace(`Error while trying to cancel device connection`, { error });
462
+ });
463
+ tracer.trace(`Device ${id} disconnected`);
464
+ };
465
+ device;
466
+ deviceModel;
467
+ disconnectTimeout = null;
468
+ id;
469
+ isConnected = true;
470
+ mtuSize = 20;
471
+ // Observable emitting data received from the device via BLE
472
+ notifyObservable;
473
+ notYetDisconnected = true;
474
+ writableWithResponseCharacteristic;
475
+ writableWithoutResponseCharacteristic;
476
+ rxjsScheduler;
477
+ // Transaction ids of communication operations that are currently pending
478
+ currentTransactionIds;
433
479
  /**
434
480
  * The static `open` function is used to handle BleTransport instantiation
435
481
  *
@@ -447,102 +493,6 @@ class BleTransport extends hw_transport_1.default {
447
493
  */
448
494
  constructor(device, writableWithResponseCharacteristic, writableWithoutResponseCharacteristic, notifyObservable, deviceModel, { context, rxjsScheduler } = {}) {
449
495
  super({ context, logType: LOG_TYPE });
450
- this.disconnectTimeout = null;
451
- this.isConnected = true;
452
- this.mtuSize = 20;
453
- this.notYetDisconnected = true;
454
- /**
455
- * A message exchange (APDU request <-> response) with the device that can be aborted
456
- *
457
- * The message will be BLE-encoded/framed before being sent, and the response will be BLE-decoded.
458
- *
459
- * @param message A buffer (u8 array) of a none BLE-encoded message (an APDU for ex) to be sent to the device
460
- * as a request
461
- * @param options Contains optional options for the exchange function
462
- * - abortTimeoutMs: stop the exchange after a given timeout. Another timeout exists
463
- * to detect unresponsive device (see `unresponsiveTimeout`). This timeout aborts the exchange.
464
- * @returns A promise that resolves with the response data from the device.
465
- */
466
- this.exchange = (message, { abortTimeoutMs } = {}) => {
467
- const tracer = this.tracer.withUpdatedContext({
468
- function: "exchange",
469
- });
470
- tracer.trace("Exchanging APDU ...", { abortTimeoutMs });
471
- tracer.withType("apdu").trace(`=> ${message.toString("hex")}`);
472
- return this.exchangeAtomicImpl(() => {
473
- return (0, rxjs_1.firstValueFrom)(
474
- // `sendApdu` will only emit if an error occurred, otherwise it will complete,
475
- // while `receiveAPDU` will emit the full response.
476
- // Consequently it monitors the response while being able to reject on an error from the send.
477
- (0, rxjs_1.merge)(this.notifyObservable.pipe(data => (0, receiveAPDU_1.receiveAPDU)(data, { context: tracer.getContext() })), (0, sendAPDU_1.sendAPDU)(this.write, message, this.mtuSize, {
478
- context: tracer.getContext(),
479
- })).pipe(abortTimeoutMs ? (0, operators_1.timeout)(abortTimeoutMs, this.rxjsScheduler) : (0, operators_1.tap)(), (0, operators_1.tap)(data => {
480
- tracer.withType("apdu").trace(`<= ${data.toString("hex")}`);
481
- }), (0, operators_1.catchError)((error) => __awaiter(this, void 0, void 0, function* () {
482
- // Currently only 1 reason the exchange has been explicitly aborted (other than job and transport errors): a timeout
483
- if (error instanceof rxjs_1.TimeoutError) {
484
- tracer.trace("Aborting due to timeout and trying to cancel all communication write of the current exchange", {
485
- abortTimeoutMs,
486
- transactionIds: this.currentTransactionIds,
487
- });
488
- // No concurrent exchange should happen at the same time, so all pending operations are part of the same exchange
489
- yield this.cancelPendingOperations();
490
- throw new errors_1.TransportExchangeTimeoutError("Exchange aborted due to timeout");
491
- }
492
- tracer.withType("ble-error").trace(`Error while exchanging APDU`, { error });
493
- if (this.notYetDisconnected) {
494
- // In such case we will always disconnect because something is bad.
495
- // This sends a "disconnect" event
496
- yield _a.disconnectDevice(this.id);
497
- }
498
- const mappedError = (0, remapErrors_1.remapError)(error);
499
- tracer.trace("Error while exchanging APDU, mapped and throws following error", {
500
- mappedError,
501
- });
502
- throw mappedError;
503
- })), (0, operators_1.finalize)(() => {
504
- tracer.trace("Clearing current transaction ids", {
505
- currentTransactionIds: this.currentTransactionIds,
506
- });
507
- this.clearCurrentTransactionIds();
508
- })));
509
- });
510
- };
511
- /**
512
- * Do not call this directly unless you know what you're doing. Communication
513
- * with a Ledger device should be through the {@link exchange} method.
514
- *
515
- * For each call a transaction id is added to the current stack of transaction ids.
516
- * With this transaction id, a pending BLE communication operations can be cancelled.
517
- * Note: each frame/packet of a longer BLE-encoded message to be sent should have their unique transaction id.
518
- *
519
- * @param buffer BLE-encoded packet to send to the device
520
- * @param frameId Frame id to make `write` aware of a bigger message that this frame/packet is part of.
521
- * Helps creating related a collection of transaction ids
522
- */
523
- this.write = (buffer) => __awaiter(this, void 0, void 0, function* () {
524
- const transactionId = (0, uuid_1.v4)();
525
- this.currentTransactionIds.push(transactionId);
526
- const tracer = this.tracer.withUpdatedContext({ transactionId });
527
- tracer.trace("Writing to device", {
528
- willMessageBeAcked: !this.writableWithoutResponseCharacteristic,
529
- });
530
- try {
531
- if (!this.writableWithoutResponseCharacteristic) {
532
- // The message will be acked in response by the device
533
- yield this.writableWithResponseCharacteristic.writeWithResponse(buffer.toString("base64"), transactionId);
534
- }
535
- else {
536
- // The message won't be acked in response by the device
537
- yield this.writableWithoutResponseCharacteristic.writeWithoutResponse(buffer.toString("base64"), transactionId);
538
- }
539
- tracer.withType("ble-frame").trace("=> " + buffer.toString("hex"));
540
- }
541
- catch (error) {
542
- tracer.trace("Error while writing APDU", { error });
543
- throw new errors_1.DisconnectedDeviceDuringOperation(error instanceof Error ? error.message : `${error}`);
544
- }
545
- });
546
496
  this.id = device.id;
547
497
  this.device = device;
548
498
  this.writableWithResponseCharacteristic = writableWithResponseCharacteristic;
@@ -554,6 +504,63 @@ class BleTransport extends hw_transport_1.default {
554
504
  clearDisconnectTimeout(this.id);
555
505
  this.tracer.trace(`New instance of BleTransport for device ${this.id}`);
556
506
  }
507
+ /**
508
+ * A message exchange (APDU request <-> response) with the device that can be aborted
509
+ *
510
+ * The message will be BLE-encoded/framed before being sent, and the response will be BLE-decoded.
511
+ *
512
+ * @param message A buffer (u8 array) of a none BLE-encoded message (an APDU for ex) to be sent to the device
513
+ * as a request
514
+ * @param options Contains optional options for the exchange function
515
+ * - abortTimeoutMs: stop the exchange after a given timeout. Another timeout exists
516
+ * to detect unresponsive device (see `unresponsiveTimeout`). This timeout aborts the exchange.
517
+ * @returns A promise that resolves with the response data from the device.
518
+ */
519
+ exchange = (message, { abortTimeoutMs } = {}) => {
520
+ const tracer = this.tracer.withUpdatedContext({
521
+ function: "exchange",
522
+ });
523
+ tracer.trace("Exchanging APDU ...", { abortTimeoutMs });
524
+ tracer.withType("apdu").trace(`=> ${message.toString("hex")}`);
525
+ return this.exchangeAtomicImpl(() => {
526
+ return (0, rxjs_1.firstValueFrom)(
527
+ // `sendApdu` will only emit if an error occurred, otherwise it will complete,
528
+ // while `receiveAPDU` will emit the full response.
529
+ // Consequently it monitors the response while being able to reject on an error from the send.
530
+ (0, rxjs_1.merge)(this.notifyObservable.pipe(data => (0, receiveAPDU_1.receiveAPDU)(data, { context: tracer.getContext() })), (0, sendAPDU_1.sendAPDU)(this.write, message, this.mtuSize, {
531
+ context: tracer.getContext(),
532
+ })).pipe(abortTimeoutMs ? (0, operators_1.timeout)(abortTimeoutMs, this.rxjsScheduler) : (0, operators_1.tap)(), (0, operators_1.tap)(data => {
533
+ tracer.withType("apdu").trace(`<= ${data.toString("hex")}`);
534
+ }), (0, operators_1.catchError)(async (error) => {
535
+ // Currently only 1 reason the exchange has been explicitly aborted (other than job and transport errors): a timeout
536
+ if (error instanceof rxjs_1.TimeoutError) {
537
+ tracer.trace("Aborting due to timeout and trying to cancel all communication write of the current exchange", {
538
+ abortTimeoutMs,
539
+ transactionIds: this.currentTransactionIds,
540
+ });
541
+ // No concurrent exchange should happen at the same time, so all pending operations are part of the same exchange
542
+ await this.cancelPendingOperations();
543
+ throw new errors_1.TransportExchangeTimeoutError("Exchange aborted due to timeout");
544
+ }
545
+ tracer.withType("ble-error").trace(`Error while exchanging APDU`, { error });
546
+ if (this.notYetDisconnected) {
547
+ // In such case we will always disconnect because something is bad.
548
+ // This sends a "disconnect" event
549
+ await BleTransport.disconnectDevice(this.id);
550
+ }
551
+ const mappedError = (0, remapErrors_1.remapError)(error);
552
+ tracer.trace("Error while exchanging APDU, mapped and throws following error", {
553
+ mappedError,
554
+ });
555
+ throw mappedError;
556
+ }), (0, operators_1.finalize)(() => {
557
+ tracer.trace("Clearing current transaction ids", {
558
+ currentTransactionIds: this.currentTransactionIds,
559
+ });
560
+ this.clearCurrentTransactionIds();
561
+ })));
562
+ });
563
+ };
557
564
  /**
558
565
  * Tries to cancel all operations that have a recorded transaction and are pending
559
566
  *
@@ -563,18 +570,16 @@ class BleTransport extends hw_transport_1.default {
563
570
  * but this error should be ignored. (In `exchange` our observable is unsubscribed before `cancelPendingOperations`
564
571
  * is called so the error is ignored)
565
572
  */
566
- cancelPendingOperations() {
567
- return __awaiter(this, void 0, void 0, function* () {
568
- for (const transactionId of this.currentTransactionIds) {
569
- try {
570
- this.tracer.trace("Cancelling operation", { transactionId });
571
- yield BlePlxManager_1.BlePlxManager.cancelTransaction(transactionId);
572
- }
573
- catch (error) {
574
- this.tracer.trace("Error while cancelling operation", { transactionId, error });
575
- }
573
+ async cancelPendingOperations() {
574
+ for (const transactionId of this.currentTransactionIds) {
575
+ try {
576
+ this.tracer.trace("Cancelling operation", { transactionId });
577
+ await BlePlxManager_1.BlePlxManager.cancelTransaction(transactionId);
576
578
  }
577
- });
579
+ catch (error) {
580
+ this.tracer.trace("Error while cancelling operation", { transactionId, error });
581
+ }
582
+ }
578
583
  }
579
584
  /**
580
585
  * Sets the collection of current transaction ids to an empty array
@@ -586,43 +591,41 @@ class BleTransport extends hw_transport_1.default {
586
591
  * Negotiate with the device the maximum transfer unit for the ble frames
587
592
  * @returns Promise<number>
588
593
  */
589
- inferMTU() {
590
- return __awaiter(this, void 0, void 0, function* () {
591
- let { mtu } = this.device;
592
- this.tracer.trace(`Inferring MTU ...`, { currentDeviceMtu: mtu });
593
- yield this.exchangeAtomicImpl(() => __awaiter(this, void 0, void 0, function* () {
594
- try {
595
- mtu = yield (0, rxjs_1.firstValueFrom)((0, rxjs_1.merge)(this.notifyObservable.pipe((0, operators_1.map)(maybeError => {
596
- // Catches the PairingFailed Error that has only been emitted
597
- if (maybeError instanceof Error) {
598
- throw maybeError;
599
- }
600
- return maybeError;
601
- }), (0, operators_1.first)(buffer => buffer.readUInt8(0) === 0x08), (0, operators_1.map)(buffer => buffer.readUInt8(5))), (0, rxjs_1.defer)(() => (0, rxjs_1.from)(this.write(Buffer.from([0x08, 0, 0, 0, 0])))).pipe((0, operators_1.ignoreElements)())));
602
- }
603
- catch (error) {
604
- this.tracer.withType("ble-error").trace("Error while inferring MTU", { mtu });
605
- yield _a.disconnectDevice(this.id);
606
- const mappedError = (0, remapErrors_1.remapError)(error);
607
- this.tracer.trace("Error while inferring APDU, mapped and throws following error", {
608
- mappedError,
609
- });
610
- throw mappedError;
611
- }
612
- finally {
613
- // When negotiating the MTU, a message is sent/written to the device, and a transaction id was associated to this write
614
- this.clearCurrentTransactionIds();
615
- }
616
- }));
617
- this.tracer.trace(`Successfully negotiated MTU with device`, {
618
- mtu,
619
- mtuSize: this.mtuSize,
620
- });
621
- if (mtu > 20) {
622
- this.mtuSize = mtu;
594
+ async inferMTU() {
595
+ let { mtu } = this.device;
596
+ this.tracer.trace(`Inferring MTU ...`, { currentDeviceMtu: mtu });
597
+ await this.exchangeAtomicImpl(async () => {
598
+ try {
599
+ mtu = await (0, rxjs_1.firstValueFrom)((0, rxjs_1.merge)(this.notifyObservable.pipe((0, operators_1.map)(maybeError => {
600
+ // Catches the PairingFailed Error that has only been emitted
601
+ if (maybeError instanceof Error) {
602
+ throw maybeError;
603
+ }
604
+ return maybeError;
605
+ }), (0, operators_1.first)(buffer => buffer.readUInt8(0) === 0x08), (0, operators_1.map)(buffer => buffer.readUInt8(5))), (0, rxjs_1.defer)(() => (0, rxjs_1.from)(this.write(Buffer.from([0x08, 0, 0, 0, 0])))).pipe((0, operators_1.ignoreElements)())));
623
606
  }
624
- return this.mtuSize;
607
+ catch (error) {
608
+ this.tracer.withType("ble-error").trace("Error while inferring MTU", { mtu });
609
+ await BleTransport.disconnectDevice(this.id);
610
+ const mappedError = (0, remapErrors_1.remapError)(error);
611
+ this.tracer.trace("Error while inferring APDU, mapped and throws following error", {
612
+ mappedError,
613
+ });
614
+ throw mappedError;
615
+ }
616
+ finally {
617
+ // When negotiating the MTU, a message is sent/written to the device, and a transaction id was associated to this write
618
+ this.clearCurrentTransactionIds();
619
+ }
620
+ });
621
+ this.tracer.trace(`Successfully negotiated MTU with device`, {
622
+ mtu,
623
+ mtuSize: this.mtuSize,
625
624
  });
625
+ if (mtu > 20) {
626
+ this.mtuSize = mtu;
627
+ }
628
+ return this.mtuSize;
626
629
  }
627
630
  /**
628
631
  * Exposed method from the ble-plx library
@@ -630,11 +633,44 @@ class BleTransport extends hw_transport_1.default {
630
633
  * @param {"Balanced" | "High" | "LowPower"} connectionPriority: Connection priority.
631
634
  * @returns {Promise<Device>} Connected device.
632
635
  */
633
- requestConnectionPriority(connectionPriority) {
634
- return __awaiter(this, void 0, void 0, function* () {
635
- return yield (0, remapErrors_1.decoratePromiseErrors)(this.device.requestConnectionPriority(react_native_ble_plx_1.ConnectionPriority[connectionPriority]));
636
- });
636
+ async requestConnectionPriority(connectionPriority) {
637
+ return await (0, remapErrors_1.decoratePromiseErrors)(this.device.requestConnectionPriority(react_native_ble_plx_1.ConnectionPriority[connectionPriority]));
637
638
  }
639
+ /**
640
+ * Do not call this directly unless you know what you're doing. Communication
641
+ * with a Ledger device should be through the {@link exchange} method.
642
+ *
643
+ * For each call a transaction id is added to the current stack of transaction ids.
644
+ * With this transaction id, a pending BLE communication operations can be cancelled.
645
+ * Note: each frame/packet of a longer BLE-encoded message to be sent should have their unique transaction id.
646
+ *
647
+ * @param buffer BLE-encoded packet to send to the device
648
+ * @param frameId Frame id to make `write` aware of a bigger message that this frame/packet is part of.
649
+ * Helps creating related a collection of transaction ids
650
+ */
651
+ write = async (buffer) => {
652
+ const transactionId = (0, uuid_1.v4)();
653
+ this.currentTransactionIds.push(transactionId);
654
+ const tracer = this.tracer.withUpdatedContext({ transactionId });
655
+ tracer.trace("Writing to device", {
656
+ willMessageBeAcked: !this.writableWithoutResponseCharacteristic,
657
+ });
658
+ try {
659
+ if (!this.writableWithoutResponseCharacteristic) {
660
+ // The message will be acked in response by the device
661
+ await this.writableWithResponseCharacteristic.writeWithResponse(buffer.toString("base64"), transactionId);
662
+ }
663
+ else {
664
+ // The message won't be acked in response by the device
665
+ await this.writableWithoutResponseCharacteristic.writeWithoutResponse(buffer.toString("base64"), transactionId);
666
+ }
667
+ tracer.withType("ble-frame").trace("=> " + buffer.toString("hex"));
668
+ }
669
+ catch (error) {
670
+ tracer.trace("Error while writing APDU", { error });
671
+ throw new errors_1.DisconnectedDeviceDuringOperation(error instanceof Error ? error.message : `${error}`);
672
+ }
673
+ };
638
674
  /**
639
675
  * We intentionally do not immediately close a transport connection.
640
676
  * Instead, we queue the disconnect and wait for a future connection to dismiss the event.
@@ -643,76 +679,30 @@ class BleTransport extends hw_transport_1.default {
643
679
  * already been disconnected.
644
680
  * @returns {Promise<void>}
645
681
  */
646
- close() {
647
- return __awaiter(this, void 0, void 0, function* () {
648
- const tracer = this.tracer.withUpdatedContext({ function: "close" });
649
- tracer.trace("Closing, queuing a disconnect with a timeout ...");
650
- let resolve;
651
- const disconnectPromise = new Promise(innerResolve => {
652
- resolve = innerResolve;
653
- });
654
- clearDisconnectTimeout(this.id);
655
- this.disconnectTimeout = setTimeout(() => {
656
- tracer.trace("Disconnect timeout has been reached ...");
657
- if (this.isConnected) {
658
- _a.disconnectDevice(this.id, tracer.getContext())
659
- .catch(() => { })
660
- .finally(resolve);
661
- }
662
- else {
663
- resolve();
664
- }
665
- }, _a.disconnectTimeoutMs);
666
- // The closure will occur no later than 5s, triggered either by disconnection
667
- // or the actual response of the apdu.
668
- yield Promise.race([this.exchangeBusyPromise || Promise.resolve(), disconnectPromise]);
669
- return;
682
+ async close() {
683
+ const tracer = this.tracer.withUpdatedContext({ function: "close" });
684
+ tracer.trace("Closing, queuing a disconnect with a timeout ...");
685
+ let resolve;
686
+ const disconnectPromise = new Promise(innerResolve => {
687
+ resolve = innerResolve;
670
688
  });
689
+ clearDisconnectTimeout(this.id);
690
+ this.disconnectTimeout = setTimeout(() => {
691
+ tracer.trace("Disconnect timeout has been reached ...");
692
+ if (this.isConnected) {
693
+ BleTransport.disconnectDevice(this.id, tracer.getContext())
694
+ .catch(() => { })
695
+ .finally(resolve);
696
+ }
697
+ else {
698
+ resolve();
699
+ }
700
+ }, BleTransport.disconnectTimeoutMs);
701
+ // The closure will occur no later than 5s, triggered either by disconnection
702
+ // or the actual response of the apdu.
703
+ await Promise.race([this.exchangeBusyPromise || Promise.resolve(), disconnectPromise]);
704
+ return;
671
705
  }
672
706
  }
673
- _a = BleTransport;
674
- BleTransport.disconnectTimeoutMs = 5000;
675
- /**
676
- *
677
- */
678
- BleTransport.isSupported = () => Promise.resolve(typeof BlePlxManager_1.BlePlxManager === "function");
679
- /**
680
- *
681
- */
682
- BleTransport.list = () => {
683
- throw new Error("not implemented");
684
- };
685
- // /**
686
- // * Exposed method from the ble-plx library
687
- // * Sets new log level for native module's logging mechanism.
688
- // * @param string logLevel New log level to be set.
689
- // */
690
- BleTransport.setLogLevel = BlePlxManager_1.BlePlxManager.setLogLevel;
691
- BleTransport.safeRemove = (sub, tracer) => {
692
- try {
693
- sub.remove();
694
- }
695
- catch (error) {
696
- tracer.trace("Error removing state subscription", { error });
697
- }
698
- };
699
- /**
700
- * Exposes method from the ble-plx library to disconnect a device
701
- *
702
- * Disconnects from {@link Device} if it's connected or cancels pending connection.
703
- * A "disconnect" event will normally be emitted by the ble-plx lib once the device is disconnected.
704
- * Errors are logged but silenced.
705
- */
706
- BleTransport.disconnectDevice = (id, context) => __awaiter(void 0, void 0, void 0, function* () {
707
- const tracer = new logs_1.LocalTracer(LOG_TYPE, context);
708
- tracer.trace(`Trying to disconnect device ${id}`);
709
- yield BlePlxManager_1.BlePlxManager.disconnectDevice(id).catch(error => {
710
- // Only log, ignore if disconnect did not work
711
- tracer
712
- .withType("ble-error")
713
- .trace(`Error while trying to cancel device connection`, { error });
714
- });
715
- tracer.trace(`Device ${id} disconnected`);
716
- });
717
707
  exports.default = BleTransport;
718
708
  //# sourceMappingURL=BleTransport.js.map