@ledgerhq/react-native-hw-transport-ble 6.25.1-alpha.3 → 6.25.2-monorepo.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +5 -0
- package/CHANGELOG.md +23 -0
- package/jest.config.ts +6 -0
- package/lib/BleTransport.d.ts +2 -2
- package/lib/BleTransport.d.ts.map +1 -1
- package/lib/BleTransport.js +13 -2
- package/lib/BleTransport.js.map +1 -1
- package/lib/awaitsBleOn.d.ts +0 -0
- package/lib/awaitsBleOn.d.ts.map +0 -0
- package/lib/awaitsBleOn.js +0 -0
- package/lib/awaitsBleOn.js.map +0 -0
- package/lib/monitorCharacteristic.d.ts +0 -0
- package/lib/monitorCharacteristic.d.ts.map +0 -0
- package/lib/monitorCharacteristic.js +0 -0
- package/lib/monitorCharacteristic.js.map +0 -0
- package/lib/remapErrors.d.ts +0 -0
- package/lib/remapErrors.d.ts.map +0 -0
- package/lib/remapErrors.js +0 -0
- package/lib/remapErrors.js.map +0 -0
- package/lib/timer.d.ts +0 -0
- package/lib/timer.d.ts.map +0 -0
- package/lib/timer.js +0 -0
- package/lib/timer.js.map +0 -0
- package/lib/types.d.ts +0 -0
- package/lib/types.d.ts.map +0 -0
- package/lib/types.js +0 -0
- package/lib/types.js.map +0 -0
- package/lib-es/BleTransport.d.ts +2 -2
- package/lib-es/BleTransport.d.ts.map +1 -1
- package/lib-es/BleTransport.js +16 -5
- package/lib-es/BleTransport.js.map +1 -1
- package/lib-es/awaitsBleOn.d.ts +0 -0
- package/lib-es/awaitsBleOn.d.ts.map +0 -0
- package/lib-es/awaitsBleOn.js +0 -0
- package/lib-es/awaitsBleOn.js.map +0 -0
- package/lib-es/monitorCharacteristic.d.ts +0 -0
- package/lib-es/monitorCharacteristic.d.ts.map +0 -0
- package/lib-es/monitorCharacteristic.js +0 -0
- package/lib-es/monitorCharacteristic.js.map +0 -0
- package/lib-es/remapErrors.d.ts +0 -0
- package/lib-es/remapErrors.d.ts.map +0 -0
- package/lib-es/remapErrors.js +0 -0
- package/lib-es/remapErrors.js.map +0 -0
- package/lib-es/timer.d.ts +0 -0
- package/lib-es/timer.d.ts.map +0 -0
- package/lib-es/timer.js +0 -0
- package/lib-es/timer.js.map +0 -0
- package/lib-es/types.d.ts +0 -0
- package/lib-es/types.d.ts.map +0 -0
- package/lib-es/types.js +0 -0
- package/lib-es/types.js.map +0 -0
- package/package.json +11 -8
- package/src/BleTransport.ts +23 -4
- package/.yalc/@ledgerhq/hw-transport/flow-typed/npm/@ledgerhq/devices_vx.x.x.js +0 -60
- package/.yalc/@ledgerhq/hw-transport/flow-typed/npm/events_vx.x.x.js +0 -186
- package/.yalc/@ledgerhq/hw-transport/flow-typed/npm/flow-bin_v0.x.x.js +0 -6
- package/.yalc/@ledgerhq/hw-transport/flow-typed/npm/flow-typed_vx.x.x.js +0 -193
- package/LICENSE +0 -202
- package/lib-es/BleTransport.js.flow +0 -483
- package/lib-es/awaitsBleOn.js.flow +0 -33
- package/lib-es/monitorCharacteristic.js.flow +0 -41
- package/lib-es/remapErrors.js.flow +0 -20
- package/lib-es/timer.js.flow +0 -27
- package/lib-es/types.js.flow +0 -4
|
@@ -1,483 +0,0 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
/* eslint-disable prefer-template */
|
|
3
|
-
|
|
4
|
-
import Transport from "@ledgerhq/hw-transport";
|
|
5
|
-
import {
|
|
6
|
-
BleManager,
|
|
7
|
-
ConnectionPriority,
|
|
8
|
-
BleErrorCode,
|
|
9
|
-
} from "react-native-ble-plx";
|
|
10
|
-
import {
|
|
11
|
-
getBluetoothServiceUuids,
|
|
12
|
-
getInfosForServiceUuid,
|
|
13
|
-
} from "@ledgerhq/devices";
|
|
14
|
-
import type { DeviceModel } from "@ledgerhq/devices";
|
|
15
|
-
|
|
16
|
-
import { sendAPDU } from "@ledgerhq/devices/lib/ble/sendAPDU";
|
|
17
|
-
import { receiveAPDU } from "@ledgerhq/devices/lib/ble/receiveAPDU";
|
|
18
|
-
import { log } from "@ledgerhq/logs";
|
|
19
|
-
import { Observable, defer, merge, from } from "rxjs";
|
|
20
|
-
import { share, ignoreElements, first, map, tap } from "rxjs/operators";
|
|
21
|
-
import {
|
|
22
|
-
CantOpenDevice,
|
|
23
|
-
TransportError,
|
|
24
|
-
DisconnectedDeviceDuringOperation,
|
|
25
|
-
} from "@ledgerhq/errors";
|
|
26
|
-
import type { Device, Characteristic } from "./types";
|
|
27
|
-
import { monitorCharacteristic } from "./monitorCharacteristic";
|
|
28
|
-
import { awaitsBleOn } from "./awaitsBleOn";
|
|
29
|
-
import { decoratePromiseErrors, remapError } from "./remapErrors";
|
|
30
|
-
|
|
31
|
-
let connectOptions = {
|
|
32
|
-
requestMTU: 156,
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const transportsCache = {};
|
|
36
|
-
const bleManager = new BleManager();
|
|
37
|
-
|
|
38
|
-
const retrieveInfos = (device) => {
|
|
39
|
-
if (!device || !device.serviceUUIDs) return;
|
|
40
|
-
const [serviceUUID] = device.serviceUUIDs;
|
|
41
|
-
if (!serviceUUID) return;
|
|
42
|
-
const infos = getInfosForServiceUuid(serviceUUID);
|
|
43
|
-
if (!infos) return;
|
|
44
|
-
return infos;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
type ReconnectionConfig = {
|
|
48
|
-
pairingThreshold: number,
|
|
49
|
-
delayAfterFirstPairing: number,
|
|
50
|
-
};
|
|
51
|
-
let reconnectionConfig: ?ReconnectionConfig = {
|
|
52
|
-
pairingThreshold: 1000,
|
|
53
|
-
delayAfterFirstPairing: 4000,
|
|
54
|
-
};
|
|
55
|
-
export function setReconnectionConfig(config: ?ReconnectionConfig) {
|
|
56
|
-
reconnectionConfig = config;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const delay = (ms) => new Promise((success) => setTimeout(success, ms));
|
|
60
|
-
|
|
61
|
-
async function open(deviceOrId: Device | string, needsReconnect: boolean) {
|
|
62
|
-
let device;
|
|
63
|
-
if (typeof deviceOrId === "string") {
|
|
64
|
-
if (transportsCache[deviceOrId]) {
|
|
65
|
-
log("ble-verbose", "Transport in cache, using that.");
|
|
66
|
-
return transportsCache[deviceOrId];
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
log("ble-verbose", `open(${deviceOrId})`);
|
|
70
|
-
|
|
71
|
-
await awaitsBleOn(bleManager);
|
|
72
|
-
|
|
73
|
-
if (!device) {
|
|
74
|
-
// works for iOS but not Android
|
|
75
|
-
const devices = await bleManager.devices([deviceOrId]);
|
|
76
|
-
log("ble-verbose", `found ${devices.length} devices`);
|
|
77
|
-
[device] = devices;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (!device) {
|
|
81
|
-
const connectedDevices = await bleManager.connectedDevices(
|
|
82
|
-
getBluetoothServiceUuids()
|
|
83
|
-
);
|
|
84
|
-
const connectedDevicesFiltered = connectedDevices.filter(
|
|
85
|
-
(d) => d.id === deviceOrId
|
|
86
|
-
);
|
|
87
|
-
log(
|
|
88
|
-
"ble-verbose",
|
|
89
|
-
`found ${connectedDevicesFiltered.length} connected devices`
|
|
90
|
-
);
|
|
91
|
-
[device] = connectedDevicesFiltered;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (!device) {
|
|
95
|
-
log("ble-verbose", `connectToDevice(${deviceOrId})`);
|
|
96
|
-
try {
|
|
97
|
-
device = await bleManager.connectToDevice(deviceOrId, connectOptions);
|
|
98
|
-
} catch (e) {
|
|
99
|
-
if (e.errorCode === BleErrorCode.DeviceMTUChangeFailed) {
|
|
100
|
-
// eslint-disable-next-line require-atomic-updates
|
|
101
|
-
connectOptions = {};
|
|
102
|
-
device = await bleManager.connectToDevice(deviceOrId);
|
|
103
|
-
} else {
|
|
104
|
-
throw e;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (!device) {
|
|
110
|
-
throw new CantOpenDevice();
|
|
111
|
-
}
|
|
112
|
-
} else {
|
|
113
|
-
device = deviceOrId;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (!(await device.isConnected())) {
|
|
117
|
-
log("ble-verbose", "not connected. connecting...");
|
|
118
|
-
try {
|
|
119
|
-
await device.connect(connectOptions);
|
|
120
|
-
} catch (e) {
|
|
121
|
-
if (e.errorCode === BleErrorCode.DeviceMTUChangeFailed) {
|
|
122
|
-
// eslint-disable-next-line require-atomic-updates
|
|
123
|
-
connectOptions = {};
|
|
124
|
-
await device.connect();
|
|
125
|
-
} else {
|
|
126
|
-
throw e;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
await device.discoverAllServicesAndCharacteristics();
|
|
132
|
-
|
|
133
|
-
let res = retrieveInfos(device);
|
|
134
|
-
let characteristics;
|
|
135
|
-
if (!res) {
|
|
136
|
-
for (const uuid of getBluetoothServiceUuids()) {
|
|
137
|
-
try {
|
|
138
|
-
characteristics = await device.characteristicsForService(uuid);
|
|
139
|
-
res = getInfosForServiceUuid(uuid);
|
|
140
|
-
break;
|
|
141
|
-
} catch (e) {
|
|
142
|
-
// we attempt to connect to service
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
if (!res) {
|
|
147
|
-
throw new TransportError("service not found", "BLEServiceNotFound");
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const { deviceModel, serviceUuid, writeUuid, notifyUuid } = res;
|
|
151
|
-
|
|
152
|
-
if (!characteristics) {
|
|
153
|
-
characteristics = await device.characteristicsForService(serviceUuid);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (!characteristics) {
|
|
157
|
-
throw new TransportError("service not found", "BLEServiceNotFound");
|
|
158
|
-
}
|
|
159
|
-
let writeC;
|
|
160
|
-
let notifyC;
|
|
161
|
-
for (const c of characteristics) {
|
|
162
|
-
if (c.uuid === writeUuid) {
|
|
163
|
-
writeC = c;
|
|
164
|
-
} else if (c.uuid === notifyUuid) {
|
|
165
|
-
notifyC = c;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
if (!writeC) {
|
|
169
|
-
throw new TransportError(
|
|
170
|
-
"write characteristic not found",
|
|
171
|
-
"BLEChracteristicNotFound"
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
if (!notifyC) {
|
|
175
|
-
throw new TransportError(
|
|
176
|
-
"notify characteristic not found",
|
|
177
|
-
"BLEChracteristicNotFound"
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
if (!writeC.isWritableWithResponse) {
|
|
181
|
-
throw new TransportError(
|
|
182
|
-
"write characteristic not writableWithResponse",
|
|
183
|
-
"BLEChracteristicInvalid"
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
if (!notifyC.isNotifiable) {
|
|
187
|
-
throw new TransportError(
|
|
188
|
-
"notify characteristic not notifiable",
|
|
189
|
-
"BLEChracteristicInvalid"
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
log("ble-verbose", `device.mtu=${device.mtu}`);
|
|
194
|
-
|
|
195
|
-
const notifyObservable = monitorCharacteristic(notifyC).pipe(
|
|
196
|
-
tap((value) => {
|
|
197
|
-
log("ble-frame", "<= " + value.toString("hex"));
|
|
198
|
-
}),
|
|
199
|
-
share()
|
|
200
|
-
);
|
|
201
|
-
|
|
202
|
-
const notif = notifyObservable.subscribe();
|
|
203
|
-
|
|
204
|
-
const transport = new BluetoothTransport(
|
|
205
|
-
device,
|
|
206
|
-
writeC,
|
|
207
|
-
notifyObservable,
|
|
208
|
-
deviceModel
|
|
209
|
-
);
|
|
210
|
-
|
|
211
|
-
const onDisconnect = (e) => {
|
|
212
|
-
transport.notYetDisconnected = false;
|
|
213
|
-
notif.unsubscribe();
|
|
214
|
-
disconnectedSub.remove();
|
|
215
|
-
delete transportsCache[transport.id];
|
|
216
|
-
log("ble-verbose", `BleTransport(${transport.id}) disconnected`);
|
|
217
|
-
transport.emit("disconnect", e);
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
// eslint-disable-next-line require-atomic-updates
|
|
221
|
-
transportsCache[transport.id] = transport;
|
|
222
|
-
const disconnectedSub = device.onDisconnected((e) => {
|
|
223
|
-
if (!transport.notYetDisconnected) return;
|
|
224
|
-
onDisconnect(e);
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
let beforeMTUTime = Date.now();
|
|
228
|
-
try {
|
|
229
|
-
await transport.inferMTU();
|
|
230
|
-
} finally {
|
|
231
|
-
let afterMTUTime = Date.now();
|
|
232
|
-
|
|
233
|
-
if (reconnectionConfig) {
|
|
234
|
-
// workaround for #279: we need to open() again if we come the first time here,
|
|
235
|
-
// to make sure we do a disconnect() after the first pairing time
|
|
236
|
-
// because of a firmware bug
|
|
237
|
-
|
|
238
|
-
if (afterMTUTime - beforeMTUTime < reconnectionConfig.pairingThreshold) {
|
|
239
|
-
needsReconnect = false; // (optim) there is likely no new pairing done because mtu answer was fast.
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
if (needsReconnect) {
|
|
243
|
-
// necessary time for the bonding workaround
|
|
244
|
-
await BluetoothTransport.disconnect(transport.id).catch(() => {});
|
|
245
|
-
await delay(reconnectionConfig.delayAfterFirstPairing);
|
|
246
|
-
}
|
|
247
|
-
} else {
|
|
248
|
-
needsReconnect = false;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
if (needsReconnect) {
|
|
253
|
-
return open(device, false);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
return transport;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* react-native bluetooth BLE implementation
|
|
261
|
-
* @example
|
|
262
|
-
* import BluetoothTransport from "@ledgerhq/react-native-hw-transport-ble";
|
|
263
|
-
*/
|
|
264
|
-
export default class BluetoothTransport extends Transport<Device | string> {
|
|
265
|
-
/**
|
|
266
|
-
*
|
|
267
|
-
*/
|
|
268
|
-
static isSupported = (): Promise<boolean> =>
|
|
269
|
-
Promise.resolve(typeof BleManager === "function");
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
*
|
|
273
|
-
*/
|
|
274
|
-
static setLogLevel = (level: *) => {
|
|
275
|
-
bleManager.setLogLevel(level);
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* TODO could add this concept in all transports
|
|
280
|
-
* observe event with { available: bool, string } // available is generic, type is specific
|
|
281
|
-
* an event is emit once and then listened
|
|
282
|
-
*/
|
|
283
|
-
static observeState(observer: *) {
|
|
284
|
-
const emitFromState = (type) => {
|
|
285
|
-
observer.next({ type, available: type === "PoweredOn" });
|
|
286
|
-
};
|
|
287
|
-
bleManager.onStateChange(emitFromState, true);
|
|
288
|
-
return {
|
|
289
|
-
unsubscribe: () => {},
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
static list = (): * => {
|
|
294
|
-
throw new Error("not implemented");
|
|
295
|
-
};
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
* Scan for bluetooth Ledger devices
|
|
299
|
-
*/
|
|
300
|
-
static listen(observer: *) {
|
|
301
|
-
log("ble-verbose", "listen...");
|
|
302
|
-
let unsubscribed;
|
|
303
|
-
|
|
304
|
-
// $FlowFixMe
|
|
305
|
-
const stateSub = bleManager.onStateChange(async (state) => {
|
|
306
|
-
if (state === "PoweredOn") {
|
|
307
|
-
stateSub.remove();
|
|
308
|
-
|
|
309
|
-
const devices = await bleManager.connectedDevices(
|
|
310
|
-
getBluetoothServiceUuids()
|
|
311
|
-
);
|
|
312
|
-
if (unsubscribed) return;
|
|
313
|
-
|
|
314
|
-
await Promise.all(
|
|
315
|
-
devices.map((d) =>
|
|
316
|
-
BluetoothTransport.disconnect(d.id).catch(() => {})
|
|
317
|
-
)
|
|
318
|
-
);
|
|
319
|
-
if (unsubscribed) return;
|
|
320
|
-
|
|
321
|
-
bleManager.startDeviceScan(
|
|
322
|
-
getBluetoothServiceUuids(),
|
|
323
|
-
null,
|
|
324
|
-
(bleError, device) => {
|
|
325
|
-
if (bleError) {
|
|
326
|
-
observer.error(bleError);
|
|
327
|
-
unsubscribe();
|
|
328
|
-
return;
|
|
329
|
-
}
|
|
330
|
-
const res = retrieveInfos(device);
|
|
331
|
-
const deviceModel = res && res.deviceModel;
|
|
332
|
-
observer.next({ type: "add", descriptor: device, deviceModel });
|
|
333
|
-
}
|
|
334
|
-
);
|
|
335
|
-
}
|
|
336
|
-
}, true);
|
|
337
|
-
const unsubscribe = () => {
|
|
338
|
-
unsubscribed = true;
|
|
339
|
-
bleManager.stopDeviceScan();
|
|
340
|
-
stateSub.remove();
|
|
341
|
-
log("ble-verbose", "done listening.");
|
|
342
|
-
};
|
|
343
|
-
return { unsubscribe };
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
/**
|
|
347
|
-
* Open a BLE transport
|
|
348
|
-
* @param {*} deviceOrId
|
|
349
|
-
*/
|
|
350
|
-
static async open(deviceOrId: Device | string) {
|
|
351
|
-
return open(deviceOrId, true);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
/**
|
|
355
|
-
* Globally disconnect a BLE device by its ID
|
|
356
|
-
*/
|
|
357
|
-
static disconnect = async (id: *) => {
|
|
358
|
-
log("ble-verbose", `user disconnect(${id})`);
|
|
359
|
-
await bleManager.cancelDeviceConnection(id);
|
|
360
|
-
};
|
|
361
|
-
|
|
362
|
-
id: string;
|
|
363
|
-
|
|
364
|
-
device: Device;
|
|
365
|
-
|
|
366
|
-
mtuSize: number = 20;
|
|
367
|
-
|
|
368
|
-
writeCharacteristic: Characteristic;
|
|
369
|
-
|
|
370
|
-
notifyObservable: Observable<Buffer>;
|
|
371
|
-
|
|
372
|
-
deviceModel: DeviceModel;
|
|
373
|
-
|
|
374
|
-
notYetDisconnected = true;
|
|
375
|
-
|
|
376
|
-
constructor(
|
|
377
|
-
device: Device,
|
|
378
|
-
writeCharacteristic: Characteristic,
|
|
379
|
-
notifyObservable: Observable<Buffer>,
|
|
380
|
-
deviceModel: DeviceModel
|
|
381
|
-
) {
|
|
382
|
-
super();
|
|
383
|
-
this.id = device.id;
|
|
384
|
-
this.device = device;
|
|
385
|
-
this.writeCharacteristic = writeCharacteristic;
|
|
386
|
-
this.notifyObservable = notifyObservable;
|
|
387
|
-
this.deviceModel = deviceModel;
|
|
388
|
-
log("ble-verbose", `BleTransport(${String(this.id)}) new instance`);
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
/**
|
|
392
|
-
* communicate with a BLE transport
|
|
393
|
-
*/
|
|
394
|
-
exchange = (apdu: Buffer): Promise<Buffer> =>
|
|
395
|
-
this.exchangeAtomicImpl(async () => {
|
|
396
|
-
try {
|
|
397
|
-
const msgIn = apdu.toString("hex");
|
|
398
|
-
log("apdu", `=> ${msgIn}`);
|
|
399
|
-
|
|
400
|
-
const data = await merge(
|
|
401
|
-
// $FlowFixMe
|
|
402
|
-
this.notifyObservable.pipe(receiveAPDU),
|
|
403
|
-
sendAPDU(this.write, apdu, this.mtuSize)
|
|
404
|
-
).toPromise();
|
|
405
|
-
|
|
406
|
-
const msgOut = data.toString("hex");
|
|
407
|
-
log("apdu", `<= ${msgOut}`);
|
|
408
|
-
|
|
409
|
-
return data;
|
|
410
|
-
} catch (e) {
|
|
411
|
-
log("ble-error", "exchange got " + String(e));
|
|
412
|
-
if (this.notYetDisconnected) {
|
|
413
|
-
// in such case we will always disconnect because something is bad.
|
|
414
|
-
await bleManager.cancelDeviceConnection(this.id).catch(() => {}); // but we ignore if disconnect worked.
|
|
415
|
-
}
|
|
416
|
-
throw remapError(e);
|
|
417
|
-
}
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
// TODO we probably will do this at end of open
|
|
421
|
-
async inferMTU() {
|
|
422
|
-
let { mtu } = this.device;
|
|
423
|
-
await this.exchangeAtomicImpl(async () => {
|
|
424
|
-
try {
|
|
425
|
-
mtu =
|
|
426
|
-
(await merge(
|
|
427
|
-
this.notifyObservable.pipe(
|
|
428
|
-
first((buffer) => buffer.readUInt8(0) === 0x08),
|
|
429
|
-
map((buffer) => buffer.readUInt8(5))
|
|
430
|
-
),
|
|
431
|
-
defer(() => from(this.write(Buffer.from([0x08, 0, 0, 0, 0])))).pipe(
|
|
432
|
-
ignoreElements()
|
|
433
|
-
)
|
|
434
|
-
).toPromise()) + 3;
|
|
435
|
-
} catch (e) {
|
|
436
|
-
log("ble-error", "inferMTU got " + String(e));
|
|
437
|
-
await bleManager.cancelDeviceConnection(this.id).catch(() => {}); // but we ignore if disconnect worked.
|
|
438
|
-
throw remapError(e);
|
|
439
|
-
}
|
|
440
|
-
});
|
|
441
|
-
|
|
442
|
-
if (mtu > 23) {
|
|
443
|
-
const mtuSize = mtu - 3;
|
|
444
|
-
log(
|
|
445
|
-
"ble-verbose",
|
|
446
|
-
`BleTransport(${String(this.id)}) mtu set to ${String(mtuSize)}`
|
|
447
|
-
);
|
|
448
|
-
this.mtuSize = mtuSize;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
return this.mtuSize;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
async requestConnectionPriority(
|
|
455
|
-
connectionPriority: "Balanced" | "High" | "LowPower"
|
|
456
|
-
) {
|
|
457
|
-
await decoratePromiseErrors(
|
|
458
|
-
this.device.requestConnectionPriority(
|
|
459
|
-
ConnectionPriority[connectionPriority]
|
|
460
|
-
)
|
|
461
|
-
);
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
setScrambleKey() {}
|
|
465
|
-
|
|
466
|
-
write = async (buffer: Buffer, txid?: ?string) => {
|
|
467
|
-
log("ble-frame", "=> " + buffer.toString("hex"));
|
|
468
|
-
try {
|
|
469
|
-
await this.writeCharacteristic.writeWithResponse(
|
|
470
|
-
buffer.toString("base64"),
|
|
471
|
-
txid
|
|
472
|
-
);
|
|
473
|
-
} catch (e) {
|
|
474
|
-
throw new DisconnectedDeviceDuringOperation(e.message);
|
|
475
|
-
}
|
|
476
|
-
};
|
|
477
|
-
|
|
478
|
-
async close() {
|
|
479
|
-
if (this.exchangeBusyPromise) {
|
|
480
|
-
await this.exchangeBusyPromise;
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
|
|
3
|
-
import { BluetoothRequired } from "@ledgerhq/errors";
|
|
4
|
-
import { log } from "@ledgerhq/logs";
|
|
5
|
-
import timer from "./timer";
|
|
6
|
-
import type { BleManager } from "./types";
|
|
7
|
-
|
|
8
|
-
export const awaitsBleOn = (
|
|
9
|
-
bleManager: BleManager,
|
|
10
|
-
ms: number = 3000
|
|
11
|
-
): Promise<void> =>
|
|
12
|
-
new Promise((resolve, reject) => {
|
|
13
|
-
let done = false;
|
|
14
|
-
let lastState = "Unknown";
|
|
15
|
-
|
|
16
|
-
const stateSub = bleManager.onStateChange((state) => {
|
|
17
|
-
lastState = state;
|
|
18
|
-
log("ble-verbose", `ble state -> ${state}`);
|
|
19
|
-
if (state === "PoweredOn") {
|
|
20
|
-
if (done) return;
|
|
21
|
-
removeTimeout();
|
|
22
|
-
done = true;
|
|
23
|
-
stateSub.remove();
|
|
24
|
-
resolve();
|
|
25
|
-
}
|
|
26
|
-
}, true);
|
|
27
|
-
|
|
28
|
-
const removeTimeout = timer.timeout(() => {
|
|
29
|
-
if (done) return;
|
|
30
|
-
stateSub.remove();
|
|
31
|
-
reject(new BluetoothRequired("", { state: lastState }));
|
|
32
|
-
}, ms);
|
|
33
|
-
});
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
|
|
3
|
-
import { Observable } from "rxjs";
|
|
4
|
-
import { TransportError } from "@ledgerhq/errors";
|
|
5
|
-
import type { Characteristic } from "./types";
|
|
6
|
-
import { log } from "@ledgerhq/logs";
|
|
7
|
-
|
|
8
|
-
export const monitorCharacteristic = (
|
|
9
|
-
characteristic: Characteristic
|
|
10
|
-
): Observable<Buffer> =>
|
|
11
|
-
Observable.create((o) => {
|
|
12
|
-
log("ble-verbose", "start monitor " + characteristic.uuid);
|
|
13
|
-
const subscription = characteristic.monitor((error, c) => {
|
|
14
|
-
if (error) {
|
|
15
|
-
log(
|
|
16
|
-
"ble-verbose",
|
|
17
|
-
"error monitor " + characteristic.uuid + ": " + error
|
|
18
|
-
);
|
|
19
|
-
o.error(error);
|
|
20
|
-
} else if (!c) {
|
|
21
|
-
o.error(
|
|
22
|
-
new TransportError(
|
|
23
|
-
"characteristic monitor null value",
|
|
24
|
-
"CharacteristicMonitorNull"
|
|
25
|
-
)
|
|
26
|
-
);
|
|
27
|
-
} else {
|
|
28
|
-
try {
|
|
29
|
-
const value = Buffer.from(c.value, "base64");
|
|
30
|
-
o.next(value);
|
|
31
|
-
} catch (error) {
|
|
32
|
-
o.error(error);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
return () => {
|
|
38
|
-
log("ble-verbose", "end monitor " + characteristic.uuid);
|
|
39
|
-
subscription.remove();
|
|
40
|
-
};
|
|
41
|
-
});
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
import { DisconnectedDevice } from "@ledgerhq/errors";
|
|
3
|
-
|
|
4
|
-
export const remapError = (error: ?Error) => {
|
|
5
|
-
if (!error || !error.message) return error;
|
|
6
|
-
if (
|
|
7
|
-
error.message.includes("was disconnected") ||
|
|
8
|
-
error.message.includes("not found")
|
|
9
|
-
) {
|
|
10
|
-
return new DisconnectedDevice();
|
|
11
|
-
}
|
|
12
|
-
return error;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export const rethrowError = (e: ?Error) => {
|
|
16
|
-
throw remapError(e);
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export const decoratePromiseErrors = <A>(promise: Promise<A>): Promise<A> =>
|
|
20
|
-
promise.catch(rethrowError);
|
package/lib-es/timer.js.flow
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
|
|
3
|
-
const timer =
|
|
4
|
-
process.env.NODE_ENV === "development"
|
|
5
|
-
? {
|
|
6
|
-
timeout: (fn: Function, ms: number) => {
|
|
7
|
-
// hack for a bug in RN https://github.com/facebook/react-native/issues/9030
|
|
8
|
-
const startTime = Date.now();
|
|
9
|
-
const interval = setInterval(() => {
|
|
10
|
-
if (Date.now() - startTime >= ms) {
|
|
11
|
-
clearInterval(interval);
|
|
12
|
-
fn();
|
|
13
|
-
}
|
|
14
|
-
}, 100);
|
|
15
|
-
return () => {
|
|
16
|
-
clearInterval(interval);
|
|
17
|
-
};
|
|
18
|
-
},
|
|
19
|
-
}
|
|
20
|
-
: {
|
|
21
|
-
timeout: (fn: Function, ms: number) => {
|
|
22
|
-
const timeout = setTimeout(fn, ms);
|
|
23
|
-
return () => clearTimeout(timeout);
|
|
24
|
-
},
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export default timer;
|
package/lib-es/types.js.flow
DELETED