@ledgerhq/react-native-hw-transport-ble 6.29.5 → 6.29.6-nightly.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.
- package/CHANGELOG.md +9 -0
- package/lib/BleTransport.d.ts +19 -6
- package/lib/BleTransport.d.ts.map +1 -1
- package/lib/BleTransport.js +138 -87
- package/lib/BleTransport.js.map +1 -1
- package/lib/monitorCharacteristic.d.ts +2 -1
- package/lib/monitorCharacteristic.d.ts.map +1 -1
- package/lib/monitorCharacteristic.js +9 -5
- package/lib/monitorCharacteristic.js.map +1 -1
- package/lib-es/BleTransport.d.ts +19 -6
- package/lib-es/BleTransport.d.ts.map +1 -1
- package/lib-es/BleTransport.js +140 -89
- package/lib-es/BleTransport.js.map +1 -1
- package/lib-es/monitorCharacteristic.d.ts +2 -1
- package/lib-es/monitorCharacteristic.d.ts.map +1 -1
- package/lib-es/monitorCharacteristic.js +10 -6
- package/lib-es/monitorCharacteristic.js.map +1 -1
- package/package.json +5 -5
- package/src/BleTransport.ts +166 -86
- package/src/monitorCharacteristic.ts +17 -6
|
@@ -4,15 +4,20 @@ exports.monitorCharacteristic = void 0;
|
|
|
4
4
|
const rxjs_1 = require("rxjs");
|
|
5
5
|
const errors_1 = require("@ledgerhq/errors");
|
|
6
6
|
const logs_1 = require("@ledgerhq/logs");
|
|
7
|
-
const
|
|
8
|
-
|
|
7
|
+
const LOG_TYPE = "ble-verbose";
|
|
8
|
+
const monitorCharacteristic = (characteristic, context) => new rxjs_1.Observable(o => {
|
|
9
|
+
const tracer = new logs_1.LocalTracer(LOG_TYPE, context);
|
|
10
|
+
tracer.trace(`Start monitoring BLE characteristics`, {
|
|
11
|
+
characteristicUuid: characteristic.uuid,
|
|
12
|
+
});
|
|
9
13
|
const subscription = characteristic.monitor((error, c) => {
|
|
10
14
|
if (error) {
|
|
11
|
-
|
|
15
|
+
tracer.trace("Error while monitoring characteristics", { error });
|
|
12
16
|
o.error(error);
|
|
13
17
|
}
|
|
14
18
|
else if (!c) {
|
|
15
|
-
|
|
19
|
+
tracer.trace("BLE monitored characteristic null value");
|
|
20
|
+
o.error(new errors_1.TransportError("Characteristic monitor null value", "CharacteristicMonitorNull"));
|
|
16
21
|
}
|
|
17
22
|
else {
|
|
18
23
|
try {
|
|
@@ -25,7 +30,6 @@ const monitorCharacteristic = (characteristic) => new rxjs_1.Observable(o => {
|
|
|
25
30
|
}
|
|
26
31
|
});
|
|
27
32
|
return () => {
|
|
28
|
-
(0, logs_1.log)("ble-verbose", "end monitor " + characteristic.uuid);
|
|
29
33
|
subscription.remove();
|
|
30
34
|
};
|
|
31
35
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"monitorCharacteristic.js","sourceRoot":"","sources":["../src/monitorCharacteristic.ts"],"names":[],"mappings":";;;AAAA,+BAAkC;AAClC,6CAAkD;AAElD,
|
|
1
|
+
{"version":3,"file":"monitorCharacteristic.js","sourceRoot":"","sources":["../src/monitorCharacteristic.ts"],"names":[],"mappings":";;;AAAA,+BAAkC;AAClC,6CAAkD;AAElD,yCAA2D;AAE3D,MAAM,QAAQ,GAAG,aAAa,CAAC;AAExB,MAAM,qBAAqB,GAAG,CACnC,cAA8B,EAC9B,OAAsB,EACF,EAAE,CACtB,IAAI,iBAAU,CAAC,CAAC,CAAC,EAAE;IACjB,MAAM,MAAM,GAAG,IAAI,kBAAW,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAClD,MAAM,CAAC,KAAK,CAAC,sCAAsC,EAAE;QACnD,kBAAkB,EAAE,cAAc,CAAC,IAAI;KACxC,CAAC,CAAC;IAEH,MAAM,YAAY,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QACvD,IAAI,KAAK,EAAE;YACT,MAAM,CAAC,KAAK,CAAC,wCAAwC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YAClE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;SAChB;aAAM,IAAI,CAAC,CAAC,EAAE;YACb,MAAM,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAC;YACxD,CAAC,CAAC,KAAK,CACL,IAAI,uBAAc,CAAC,mCAAmC,EAAE,2BAA2B,CAAC,CACrF,CAAC;SACH;aAAM;YACL,IAAI;gBACF,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;gBAC7C,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;aACf;YAAC,OAAO,KAAK,EAAE;gBACd,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;aAChB;SACF;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,GAAG,EAAE;QACV,YAAY,CAAC,MAAM,EAAE,CAAC;IACxB,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAhCQ,QAAA,qBAAqB,yBAgC7B"}
|
package/lib-es/BleTransport.d.ts
CHANGED
|
@@ -3,10 +3,16 @@ import Transport from "@ledgerhq/hw-transport";
|
|
|
3
3
|
import type { Subscription as TransportSubscription, Observer as TransportObserver } from "@ledgerhq/hw-transport";
|
|
4
4
|
import { DeviceId, Device, Characteristic } from "react-native-ble-plx";
|
|
5
5
|
import type { DeviceModel } from "@ledgerhq/devices";
|
|
6
|
+
import { TraceContext } from "@ledgerhq/logs";
|
|
6
7
|
import { Observable, Observer } from "rxjs";
|
|
7
8
|
import { HwTransportError } from "@ledgerhq/errors";
|
|
8
9
|
import { ReconnectionConfig } from "./types";
|
|
9
10
|
export declare const setReconnectionConfig: (config: ReconnectionConfig | null | undefined) => void;
|
|
11
|
+
/**
|
|
12
|
+
* react-native bluetooth BLE implementation
|
|
13
|
+
* @example
|
|
14
|
+
* import BleTransport from "@ledgerhq/react-native-hw-transport-ble";
|
|
15
|
+
*/
|
|
10
16
|
export default class BleTransport extends Transport {
|
|
11
17
|
static disconnectTimeoutMs: number;
|
|
12
18
|
/**
|
|
@@ -38,17 +44,21 @@ export default class BleTransport extends Transport {
|
|
|
38
44
|
* @param observer Device is partial in order to avoid the live-common/this dep
|
|
39
45
|
* @returns TransportSubscription
|
|
40
46
|
*/
|
|
41
|
-
static listen(observer: TransportObserver<any, HwTransportError
|
|
47
|
+
static listen(observer: TransportObserver<any, HwTransportError>, context?: TraceContext): TransportSubscription;
|
|
42
48
|
/**
|
|
43
|
-
*
|
|
49
|
+
* Opens a BLE transport
|
|
50
|
+
*
|
|
44
51
|
* @param {Device | string} deviceOrId
|
|
52
|
+
* @param timeoutMs TODO: to keep, used in a separate PR
|
|
53
|
+
* @param context An optional context object for log/tracing strategy
|
|
45
54
|
*/
|
|
46
|
-
static open(deviceOrId: Device | string): Promise<BleTransport>;
|
|
55
|
+
static open(deviceOrId: Device | string, timeoutMs?: number, context?: TraceContext): Promise<BleTransport>;
|
|
47
56
|
/**
|
|
48
|
-
*
|
|
57
|
+
* Exposes method from the ble-plx library to disconnect a device
|
|
58
|
+
*
|
|
49
59
|
* Disconnects from {@link Device} if it's connected or cancels pending connection.
|
|
50
60
|
*/
|
|
51
|
-
static disconnect: (id: DeviceId) => Promise<void>;
|
|
61
|
+
static disconnect: (id: DeviceId, context?: TraceContext) => Promise<void>;
|
|
52
62
|
device: Device;
|
|
53
63
|
deviceModel: DeviceModel;
|
|
54
64
|
disconnectTimeout: null | ReturnType<typeof setTimeout>;
|
|
@@ -59,10 +69,13 @@ export default class BleTransport extends Transport {
|
|
|
59
69
|
notYetDisconnected: boolean;
|
|
60
70
|
writeCharacteristic: Characteristic;
|
|
61
71
|
writeCmdCharacteristic: Characteristic | undefined;
|
|
62
|
-
constructor(device: Device, writeCharacteristic: Characteristic, writeCmdCharacteristic: Characteristic | undefined, notifyObservable: Observable<any>, deviceModel: DeviceModel
|
|
72
|
+
constructor(device: Device, writeCharacteristic: Characteristic, writeCmdCharacteristic: Characteristic | undefined, notifyObservable: Observable<any>, deviceModel: DeviceModel, { context }?: {
|
|
73
|
+
context?: TraceContext;
|
|
74
|
+
});
|
|
63
75
|
/**
|
|
64
76
|
* Send data to the device using a low level API.
|
|
65
77
|
* It's recommended to use the "send" method for a higher level API.
|
|
78
|
+
*
|
|
66
79
|
* @param {Buffer} apdu - The data to send.
|
|
67
80
|
* @returns {Promise<Buffer>} A promise that resolves with the response data from the device.
|
|
68
81
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BleTransport.d.ts","sourceRoot":"","sources":["../src/BleTransport.ts"],"names":[],"mappings":";AAAA,OAAO,SAAS,MAAM,wBAAwB,CAAC;AAC/C,OAAO,KAAK,EACV,YAAY,IAAI,qBAAqB,EACrC,QAAQ,IAAI,iBAAiB,EAC9B,MAAM,wBAAwB,CAAC;AAYhC,OAAO,EAKL,QAAQ,EACR,MAAM,EACN,cAAc,EAGf,MAAM,sBAAsB,CAAC;AAO9B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"BleTransport.d.ts","sourceRoot":"","sources":["../src/BleTransport.ts"],"names":[],"mappings":"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"}
|
package/lib-es/BleTransport.js
CHANGED
|
@@ -21,13 +21,14 @@ import { sendAPDU } from "@ledgerhq/devices/lib/ble/sendAPDU";
|
|
|
21
21
|
import { receiveAPDU } from "@ledgerhq/devices/lib/ble/receiveAPDU";
|
|
22
22
|
import { BleManager, ConnectionPriority, BleErrorCode, LogLevel, } from "react-native-ble-plx";
|
|
23
23
|
import { DeviceModelId, getBluetoothServiceUuids, getInfosForServiceUuid, } from "@ledgerhq/devices";
|
|
24
|
-
import {
|
|
25
|
-
import { defer, merge, from, of, throwError } from "rxjs";
|
|
24
|
+
import { trace, LocalTracer } from "@ledgerhq/logs";
|
|
25
|
+
import { defer, merge, from, of, throwError, firstValueFrom } from "rxjs";
|
|
26
26
|
import { share, ignoreElements, first, map, tap, catchError } from "rxjs/operators";
|
|
27
27
|
import { CantOpenDevice, TransportError, DisconnectedDeviceDuringOperation, PairingFailed, PeerRemovedPairing, } from "@ledgerhq/errors";
|
|
28
28
|
import { monitorCharacteristic } from "./monitorCharacteristic";
|
|
29
29
|
import { awaitsBleOn } from "./awaitsBleOn";
|
|
30
30
|
import { decoratePromiseErrors, remapError, mapBleErrorToHwTransportError } from "./remapErrors";
|
|
31
|
+
const LOG_TYPE = "ble-verbose";
|
|
31
32
|
/**
|
|
32
33
|
* This is potentially not needed anymore, to be checked if the bug is still
|
|
33
34
|
* happening.
|
|
@@ -82,28 +83,40 @@ const bleManagerInstance = () => {
|
|
|
82
83
|
}
|
|
83
84
|
return _bleManager;
|
|
84
85
|
};
|
|
85
|
-
const clearDisconnectTimeout = (deviceId) => {
|
|
86
|
+
const clearDisconnectTimeout = (deviceId, context) => {
|
|
86
87
|
const cachedTransport = transportsCache[deviceId];
|
|
87
88
|
if (cachedTransport && cachedTransport.disconnectTimeout) {
|
|
88
|
-
|
|
89
|
+
trace({ type: LOG_TYPE, message: "Clearing queued disconnect", context });
|
|
89
90
|
clearTimeout(cachedTransport.disconnectTimeout);
|
|
90
91
|
}
|
|
91
92
|
};
|
|
92
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Opens a BLE connection with a given device. Returns a Transport instance.
|
|
95
|
+
*
|
|
96
|
+
* @param deviceOrId
|
|
97
|
+
* @param needsReconnect
|
|
98
|
+
* @param timeoutMs TODO: to keep, used in a separate PR
|
|
99
|
+
* @param context Optional tracing/log context
|
|
100
|
+
* @returns A BleTransport instance
|
|
101
|
+
*/
|
|
102
|
+
function open(deviceOrId, needsReconnect, timeoutMs, context) {
|
|
93
103
|
return __awaiter(this, void 0, void 0, function* () {
|
|
104
|
+
const tracer = new LocalTracer(LOG_TYPE, context);
|
|
94
105
|
let device;
|
|
95
|
-
|
|
106
|
+
tracer.trace(`Opening ${deviceOrId}`, { needsReconnect });
|
|
96
107
|
if (typeof deviceOrId === "string") {
|
|
97
108
|
if (transportsCache[deviceOrId]) {
|
|
98
|
-
|
|
109
|
+
tracer.trace(`Transport in cache, using it`);
|
|
99
110
|
clearDisconnectTimeout(deviceOrId);
|
|
111
|
+
// The cached transport probably has an older trace/log context
|
|
112
|
+
transportsCache[deviceOrId].setTraceContext(context);
|
|
100
113
|
return transportsCache[deviceOrId];
|
|
101
114
|
}
|
|
102
|
-
|
|
115
|
+
tracer.trace(`Trying to open device: ${deviceOrId}`);
|
|
103
116
|
yield awaitsBleOn(bleManagerInstance());
|
|
104
117
|
// Returns a list of known devices by their identifiers
|
|
105
118
|
const devices = yield bleManagerInstance().devices([deviceOrId]);
|
|
106
|
-
|
|
119
|
+
tracer.trace(`Found ${devices.length} already known device(s) with given id`, { deviceOrId });
|
|
107
120
|
[device] = devices;
|
|
108
121
|
if (!device) {
|
|
109
122
|
// Returns a list of the peripherals currently connected to the system
|
|
@@ -111,18 +124,21 @@ function open(deviceOrId, needsReconnect) {
|
|
|
111
124
|
// connected to our app, we check that below.
|
|
112
125
|
const connectedDevices = yield bleManagerInstance().connectedDevices(getBluetoothServiceUuids());
|
|
113
126
|
const connectedDevicesFiltered = connectedDevices.filter(d => d.id === deviceOrId);
|
|
114
|
-
|
|
127
|
+
tracer.trace(`No known device with given id. Found ${connectedDevicesFiltered.length} devices from already connected devices`, { deviceOrId });
|
|
115
128
|
[device] = connectedDevicesFiltered;
|
|
116
129
|
}
|
|
117
130
|
if (!device) {
|
|
118
131
|
// We still don't have a device, so we attempt to connect to it.
|
|
119
|
-
|
|
132
|
+
tracer.trace(`No known nor connected devices with given id. Trying to connect to device`, {
|
|
133
|
+
deviceOrId,
|
|
134
|
+
timeoutMs,
|
|
135
|
+
});
|
|
120
136
|
// Nb ConnectionOptions dropped since it's not used internally by ble-plx.
|
|
121
137
|
try {
|
|
122
138
|
device = yield bleManagerInstance().connectToDevice(deviceOrId, connectOptions);
|
|
123
139
|
}
|
|
124
140
|
catch (e) {
|
|
125
|
-
|
|
141
|
+
tracer.trace(`Error code: ${e.errorCode}`);
|
|
126
142
|
if (e.errorCode === BleErrorCode.DeviceMTUChangeFailed) {
|
|
127
143
|
// If the MTU update did not work, we try to connect without requesting for a specific MTU
|
|
128
144
|
connectOptions = {};
|
|
@@ -142,21 +158,22 @@ function open(deviceOrId, needsReconnect) {
|
|
|
142
158
|
device = deviceOrId;
|
|
143
159
|
}
|
|
144
160
|
if (!(yield device.isConnected())) {
|
|
145
|
-
|
|
161
|
+
tracer.trace(`Device found but not connected. connecting...`, { timeoutMs, connectOptions });
|
|
146
162
|
try {
|
|
147
|
-
yield device.connect(connectOptions);
|
|
163
|
+
yield device.connect(Object.assign({}, connectOptions));
|
|
148
164
|
}
|
|
149
|
-
catch (
|
|
150
|
-
|
|
151
|
-
if (
|
|
152
|
-
|
|
165
|
+
catch (error) {
|
|
166
|
+
tracer.trace(`Connect error`, { error });
|
|
167
|
+
if (error.errorCode === BleErrorCode.DeviceMTUChangeFailed) {
|
|
168
|
+
tracer.trace(`Device mtu=${device.mtu}, reconnecting`);
|
|
153
169
|
connectOptions = {};
|
|
154
170
|
yield device.connect();
|
|
155
171
|
}
|
|
156
|
-
else if (
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
172
|
+
else if (error.iosErrorCode === 14 || error.reason === "Peer removed pairing information") {
|
|
173
|
+
tracer.trace(`iOS broken pairing`, {
|
|
174
|
+
device,
|
|
175
|
+
bluetoothInfoCache: bluetoothInfoCache[device.id],
|
|
176
|
+
});
|
|
160
177
|
const { deviceModel } = bluetoothInfoCache[device.id] || {};
|
|
161
178
|
const { productName } = deviceModel || {};
|
|
162
179
|
throw new PeerRemovedPairing(undefined, {
|
|
@@ -165,10 +182,11 @@ function open(deviceOrId, needsReconnect) {
|
|
|
165
182
|
});
|
|
166
183
|
}
|
|
167
184
|
else {
|
|
168
|
-
throw remapError(
|
|
185
|
+
throw remapError(error);
|
|
169
186
|
}
|
|
170
187
|
}
|
|
171
188
|
}
|
|
189
|
+
tracer.trace(`Device is connected now, getting services and characteristics`);
|
|
172
190
|
yield device.discoverAllServicesAndCharacteristics();
|
|
173
191
|
let res = retrieveInfos(device);
|
|
174
192
|
let characteristics;
|
|
@@ -185,6 +203,7 @@ function open(deviceOrId, needsReconnect) {
|
|
|
185
203
|
}
|
|
186
204
|
}
|
|
187
205
|
if (!res) {
|
|
206
|
+
tracer.trace(`Service not found`);
|
|
188
207
|
throw new TransportError("service not found", "BLEServiceNotFound");
|
|
189
208
|
}
|
|
190
209
|
const { deviceModel, serviceUuid, writeUuid, writeCmdUuid, notifyUuid } = res;
|
|
@@ -192,6 +211,7 @@ function open(deviceOrId, needsReconnect) {
|
|
|
192
211
|
characteristics = yield device.characteristicsForService(serviceUuid);
|
|
193
212
|
}
|
|
194
213
|
if (!characteristics) {
|
|
214
|
+
tracer.trace(`Characteristics not found`);
|
|
195
215
|
throw new TransportError("service not found", "BLEServiceNotFound");
|
|
196
216
|
}
|
|
197
217
|
let writeC;
|
|
@@ -225,31 +245,36 @@ function open(deviceOrId, needsReconnect) {
|
|
|
225
245
|
throw new TransportError("write cmd characteristic not writableWithoutResponse", "BLECharacteristicInvalid");
|
|
226
246
|
}
|
|
227
247
|
}
|
|
228
|
-
|
|
229
|
-
const notifyObservable = monitorCharacteristic(notifyC).pipe(catchError(e => {
|
|
248
|
+
tracer.trace(`device.mtu=${device.mtu}`);
|
|
249
|
+
const notifyObservable = monitorCharacteristic(notifyC, context).pipe(catchError(e => {
|
|
230
250
|
// LL-9033 fw 2.0.2 introduced this case, we silence the inner unhandled error.
|
|
231
251
|
const msg = String(e);
|
|
232
|
-
return msg.includes("notify change failed")
|
|
252
|
+
return msg.includes("notify change failed")
|
|
253
|
+
? of(new PairingFailed(msg))
|
|
254
|
+
: throwError(() => e);
|
|
233
255
|
}), tap(value => {
|
|
234
256
|
if (value instanceof PairingFailed)
|
|
235
257
|
return;
|
|
236
|
-
|
|
258
|
+
trace({ type: "ble-frame", message: `<= ${value.toString("hex")}`, context });
|
|
237
259
|
}), share());
|
|
238
260
|
const notif = notifyObservable.subscribe();
|
|
239
|
-
const transport = new BleTransport(device, writeC, writeCmdC, notifyObservable, deviceModel
|
|
261
|
+
const transport = new BleTransport(device, writeC, writeCmdC, notifyObservable, deviceModel, {
|
|
262
|
+
context,
|
|
263
|
+
});
|
|
264
|
+
tracer.trace(`New BleTransport created`);
|
|
240
265
|
// Keeping it as a comment for now but if no new bluetooth issues occur, we will be able to remove it
|
|
241
266
|
// await transport.requestConnectionPriority("High");
|
|
242
267
|
// eslint-disable-next-line prefer-const
|
|
243
268
|
let disconnectedSub;
|
|
244
|
-
const onDisconnect = (
|
|
269
|
+
const onDisconnect = (error) => {
|
|
245
270
|
transport.isConnected = false;
|
|
246
271
|
transport.notYetDisconnected = false;
|
|
247
272
|
notif.unsubscribe();
|
|
248
273
|
disconnectedSub === null || disconnectedSub === void 0 ? void 0 : disconnectedSub.remove();
|
|
249
274
|
clearDisconnectTimeout(transport.id);
|
|
250
275
|
delete transportsCache[transport.id];
|
|
251
|
-
|
|
252
|
-
transport.emit("disconnect",
|
|
276
|
+
tracer.trace(`On device disconnected callback: cleared cached transport for ${transport.id}, emitting Transport event "disconnect"`, { reason: error });
|
|
277
|
+
transport.emit("disconnect", error);
|
|
253
278
|
};
|
|
254
279
|
// eslint-disable-next-line require-atomic-updates
|
|
255
280
|
transportsCache[transport.id] = transport;
|
|
@@ -273,7 +298,7 @@ function open(deviceOrId, needsReconnect) {
|
|
|
273
298
|
needsReconnect = false;
|
|
274
299
|
}
|
|
275
300
|
else if (deviceModel.id === DeviceModelId.stax) {
|
|
276
|
-
|
|
301
|
+
tracer.trace(`Skipping "needsReconnect" strategy for Stax`);
|
|
277
302
|
needsReconnect = false;
|
|
278
303
|
}
|
|
279
304
|
if (needsReconnect) {
|
|
@@ -286,8 +311,8 @@ function open(deviceOrId, needsReconnect) {
|
|
|
286
311
|
}
|
|
287
312
|
}
|
|
288
313
|
if (needsReconnect) {
|
|
289
|
-
|
|
290
|
-
return open(device, false);
|
|
314
|
+
tracer.trace(`Reconnecting`);
|
|
315
|
+
return open(device, false, timeoutMs, context);
|
|
291
316
|
}
|
|
292
317
|
return transport;
|
|
293
318
|
});
|
|
@@ -297,7 +322,6 @@ function open(deviceOrId, needsReconnect) {
|
|
|
297
322
|
* @example
|
|
298
323
|
* import BleTransport from "@ledgerhq/react-native-hw-transport-ble";
|
|
299
324
|
*/
|
|
300
|
-
const TAG = "ble-verbose";
|
|
301
325
|
class BleTransport extends Transport {
|
|
302
326
|
/**
|
|
303
327
|
* Listen to state changes on the bleManagerInstance and notify the
|
|
@@ -322,8 +346,9 @@ class BleTransport extends Transport {
|
|
|
322
346
|
* @param observer Device is partial in order to avoid the live-common/this dep
|
|
323
347
|
* @returns TransportSubscription
|
|
324
348
|
*/
|
|
325
|
-
static listen(observer) {
|
|
326
|
-
|
|
349
|
+
static listen(observer, context) {
|
|
350
|
+
const tracer = new LocalTracer(LOG_TYPE, context);
|
|
351
|
+
tracer.trace("Listening for devices ...");
|
|
327
352
|
let unsubscribed;
|
|
328
353
|
const stateSub = bleManagerInstance().onStateChange((state) => __awaiter(this, void 0, void 0, function* () {
|
|
329
354
|
if (state === "PoweredOn") {
|
|
@@ -332,7 +357,7 @@ class BleTransport extends Transport {
|
|
|
332
357
|
if (unsubscribed)
|
|
333
358
|
return;
|
|
334
359
|
if (devices.length) {
|
|
335
|
-
|
|
360
|
+
tracer.trace("Disconnecting from all devices", { deviceCount: devices.length });
|
|
336
361
|
yield Promise.all(devices.map(d => BleTransport.disconnect(d.id).catch(() => { })));
|
|
337
362
|
}
|
|
338
363
|
if (unsubscribed)
|
|
@@ -359,23 +384,26 @@ class BleTransport extends Transport {
|
|
|
359
384
|
unsubscribed = true;
|
|
360
385
|
bleManagerInstance().stopDeviceScan();
|
|
361
386
|
stateSub.remove();
|
|
362
|
-
|
|
387
|
+
tracer.trace("Done listening");
|
|
363
388
|
};
|
|
364
389
|
return {
|
|
365
390
|
unsubscribe,
|
|
366
391
|
};
|
|
367
392
|
}
|
|
368
393
|
/**
|
|
369
|
-
*
|
|
394
|
+
* Opens a BLE transport
|
|
395
|
+
*
|
|
370
396
|
* @param {Device | string} deviceOrId
|
|
397
|
+
* @param timeoutMs TODO: to keep, used in a separate PR
|
|
398
|
+
* @param context An optional context object for log/tracing strategy
|
|
371
399
|
*/
|
|
372
|
-
static open(deviceOrId) {
|
|
400
|
+
static open(deviceOrId, timeoutMs, context) {
|
|
373
401
|
return __awaiter(this, void 0, void 0, function* () {
|
|
374
|
-
return open(deviceOrId, true);
|
|
402
|
+
return open(deviceOrId, true, timeoutMs, context);
|
|
375
403
|
});
|
|
376
404
|
}
|
|
377
|
-
constructor(device, writeCharacteristic, writeCmdCharacteristic, notifyObservable, deviceModel) {
|
|
378
|
-
super();
|
|
405
|
+
constructor(device, writeCharacteristic, writeCmdCharacteristic, notifyObservable, deviceModel, { context } = {}) {
|
|
406
|
+
super({ context, logType: LOG_TYPE });
|
|
379
407
|
this.disconnectTimeout = null;
|
|
380
408
|
this.isConnected = true;
|
|
381
409
|
this.mtuSize = 20;
|
|
@@ -383,29 +411,40 @@ class BleTransport extends Transport {
|
|
|
383
411
|
/**
|
|
384
412
|
* Send data to the device using a low level API.
|
|
385
413
|
* It's recommended to use the "send" method for a higher level API.
|
|
414
|
+
*
|
|
386
415
|
* @param {Buffer} apdu - The data to send.
|
|
387
416
|
* @returns {Promise<Buffer>} A promise that resolves with the response data from the device.
|
|
388
417
|
*/
|
|
389
|
-
this.exchange = (apdu) =>
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
yield bleManagerInstance()
|
|
403
|
-
.cancelDeviceConnection(this.id)
|
|
404
|
-
.catch(() => { }); // but we ignore if disconnect worked.
|
|
418
|
+
this.exchange = (apdu) => {
|
|
419
|
+
const tracer = this.tracer.withUpdatedContext({
|
|
420
|
+
function: "exchange",
|
|
421
|
+
});
|
|
422
|
+
tracer.trace("Exchanging APDU ...");
|
|
423
|
+
return this.exchangeAtomicImpl(() => __awaiter(this, void 0, void 0, function* () {
|
|
424
|
+
try {
|
|
425
|
+
const msgIn = apdu.toString("hex");
|
|
426
|
+
tracer.withType("apdu").trace(`=> ${msgIn}`);
|
|
427
|
+
const data = yield firstValueFrom(merge(this.notifyObservable.pipe(receiveAPDU), sendAPDU(this.write, apdu, this.mtuSize)));
|
|
428
|
+
const msgOut = data.toString("hex");
|
|
429
|
+
tracer.withType("apdu").trace(`<= ${msgOut}`);
|
|
430
|
+
return data;
|
|
405
431
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
432
|
+
catch (error) {
|
|
433
|
+
tracer.withType("ble-error").trace(`Error while exchanging APDU`, { error });
|
|
434
|
+
if (this.notYetDisconnected) {
|
|
435
|
+
// in such case we will always disconnect because something is bad.
|
|
436
|
+
yield bleManagerInstance()
|
|
437
|
+
.cancelDeviceConnection(this.id)
|
|
438
|
+
.catch(() => { }); // but we ignore if disconnect worked.
|
|
439
|
+
}
|
|
440
|
+
const mappedError = remapError(error);
|
|
441
|
+
tracer.trace("Error while exchanging APDU, mapped and throws following error", {
|
|
442
|
+
mappedError,
|
|
443
|
+
});
|
|
444
|
+
throw mappedError;
|
|
445
|
+
}
|
|
446
|
+
}));
|
|
447
|
+
};
|
|
409
448
|
/**
|
|
410
449
|
* Do not call this directly unless you know what you're doing. Communication
|
|
411
450
|
* with a Ledger device should be through the {@link exchange} method.
|
|
@@ -413,22 +452,18 @@ class BleTransport extends Transport {
|
|
|
413
452
|
* @param txid
|
|
414
453
|
*/
|
|
415
454
|
this.write = (buffer, txid) => __awaiter(this, void 0, void 0, function* () {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
try {
|
|
455
|
+
try {
|
|
456
|
+
if (!this.writeCmdCharacteristic) {
|
|
419
457
|
yield this.writeCharacteristic.writeWithResponse(buffer.toString("base64"), txid);
|
|
420
458
|
}
|
|
421
|
-
|
|
422
|
-
throw new DisconnectedDeviceDuringOperation(e.message);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
else {
|
|
426
|
-
try {
|
|
459
|
+
else {
|
|
427
460
|
yield this.writeCmdCharacteristic.writeWithoutResponse(buffer.toString("base64"), txid);
|
|
428
461
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
462
|
+
this.tracer.withType("ble-frame").trace("=> " + buffer.toString("hex"));
|
|
463
|
+
}
|
|
464
|
+
catch (error) {
|
|
465
|
+
this.tracer.trace("Error while writing APDU", { error });
|
|
466
|
+
throw new DisconnectedDeviceDuringOperation(error instanceof Error ? error.message : `${error}`);
|
|
432
467
|
}
|
|
433
468
|
});
|
|
434
469
|
this.id = device.id;
|
|
@@ -437,8 +472,8 @@ class BleTransport extends Transport {
|
|
|
437
472
|
this.writeCmdCharacteristic = writeCmdCharacteristic;
|
|
438
473
|
this.notifyObservable = notifyObservable;
|
|
439
474
|
this.deviceModel = deviceModel;
|
|
440
|
-
log(TAG, `BleTransport(${String(this.id)}) new instance`);
|
|
441
475
|
clearDisconnectTimeout(this.id);
|
|
476
|
+
this.tracer.trace(`New instance of BleTransport for device ${this.id}`);
|
|
442
477
|
}
|
|
443
478
|
/**
|
|
444
479
|
* Negotiate with the device the maximum transfer unit for the ble frames
|
|
@@ -447,24 +482,32 @@ class BleTransport extends Transport {
|
|
|
447
482
|
inferMTU() {
|
|
448
483
|
return __awaiter(this, void 0, void 0, function* () {
|
|
449
484
|
let { mtu } = this.device;
|
|
485
|
+
this.tracer.trace(`Inferring MTU ...`, { currentDeviceMtu: mtu });
|
|
450
486
|
yield this.exchangeAtomicImpl(() => __awaiter(this, void 0, void 0, function* () {
|
|
451
487
|
try {
|
|
452
|
-
mtu = yield merge(this.notifyObservable.pipe(tap(maybeError => {
|
|
488
|
+
mtu = yield firstValueFrom(merge(this.notifyObservable.pipe(tap(maybeError => {
|
|
453
489
|
if (maybeError instanceof Error)
|
|
454
490
|
throw maybeError;
|
|
455
|
-
}), first(buffer => buffer.readUInt8(0) === 0x08), map(buffer => buffer.readUInt8(5))), defer(() => from(this.write(Buffer.from([0x08, 0, 0, 0, 0])))).pipe(ignoreElements()))
|
|
491
|
+
}), first(buffer => buffer.readUInt8(0) === 0x08), map(buffer => buffer.readUInt8(5))), defer(() => from(this.write(Buffer.from([0x08, 0, 0, 0, 0])))).pipe(ignoreElements())));
|
|
456
492
|
}
|
|
457
|
-
catch (
|
|
458
|
-
|
|
493
|
+
catch (error) {
|
|
494
|
+
this.tracer.withType("ble-error").trace("Error while inferring MTU", { mtu });
|
|
459
495
|
yield bleManagerInstance()
|
|
460
496
|
.cancelDeviceConnection(this.id)
|
|
461
497
|
.catch(() => { }); // but we ignore if disconnect worked.
|
|
462
|
-
|
|
498
|
+
const mappedError = remapError(error);
|
|
499
|
+
this.tracer.trace("Error while inferring APDU, mapped and throws following error", {
|
|
500
|
+
mappedError,
|
|
501
|
+
});
|
|
502
|
+
throw mappedError;
|
|
463
503
|
}
|
|
464
504
|
}));
|
|
505
|
+
this.tracer.trace(`Successfully negotiated MTU with device`, {
|
|
506
|
+
mtu,
|
|
507
|
+
mtuSize: this.mtuSize,
|
|
508
|
+
});
|
|
465
509
|
if (mtu > 20) {
|
|
466
510
|
this.mtuSize = mtu;
|
|
467
|
-
log(TAG, `BleTransport(${this.id}) mtu set to ${this.mtuSize}`);
|
|
468
511
|
}
|
|
469
512
|
return this.mtuSize;
|
|
470
513
|
});
|
|
@@ -490,16 +533,15 @@ class BleTransport extends Transport {
|
|
|
490
533
|
*/
|
|
491
534
|
close() {
|
|
492
535
|
return __awaiter(this, void 0, void 0, function* () {
|
|
536
|
+
this.tracer.trace("Closing, queuing a disconnect ...");
|
|
493
537
|
let resolve;
|
|
494
538
|
const disconnectPromise = new Promise(innerResolve => {
|
|
495
539
|
resolve = innerResolve;
|
|
496
540
|
});
|
|
497
541
|
clearDisconnectTimeout(this.id);
|
|
498
|
-
log(TAG, "Queuing a disconnect");
|
|
499
542
|
this.disconnectTimeout = setTimeout(() => {
|
|
500
|
-
log(TAG, `Triggering a disconnect from ${this.id}`);
|
|
501
543
|
if (this.isConnected) {
|
|
502
|
-
BleTransport.disconnect(this.id)
|
|
544
|
+
BleTransport.disconnect(this.id, this.tracer.getContext())
|
|
503
545
|
.catch(() => { })
|
|
504
546
|
.finally(resolve);
|
|
505
547
|
}
|
|
@@ -540,13 +582,22 @@ BleTransport.setLogLevel = (logLevel) => {
|
|
|
540
582
|
}
|
|
541
583
|
};
|
|
542
584
|
/**
|
|
543
|
-
*
|
|
585
|
+
* Exposes method from the ble-plx library to disconnect a device
|
|
586
|
+
*
|
|
544
587
|
* Disconnects from {@link Device} if it's connected or cancels pending connection.
|
|
545
588
|
*/
|
|
546
|
-
BleTransport.disconnect = (id) => __awaiter(void 0, void 0, void 0, function* () {
|
|
547
|
-
|
|
589
|
+
BleTransport.disconnect = (id, context) => __awaiter(void 0, void 0, void 0, function* () {
|
|
590
|
+
trace({
|
|
591
|
+
type: LOG_TYPE,
|
|
592
|
+
message: `Trying to disconnect device ${id})`,
|
|
593
|
+
context,
|
|
594
|
+
});
|
|
548
595
|
yield bleManagerInstance().cancelDeviceConnection(id);
|
|
549
|
-
|
|
596
|
+
trace({
|
|
597
|
+
type: LOG_TYPE,
|
|
598
|
+
message: `Device ${id} disconnected`,
|
|
599
|
+
context,
|
|
600
|
+
});
|
|
550
601
|
});
|
|
551
602
|
export default BleTransport;
|
|
552
603
|
//# sourceMappingURL=BleTransport.js.map
|