@onekeyfe/hd-transport-react-native 1.2.2 → 1.2.3-alpha.10
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/dist/BleManager.d.ts +6 -5
- package/dist/BleManager.d.ts.map +1 -1
- package/dist/bleEncryption.d.ts +17 -0
- package/dist/bleEncryption.d.ts.map +1 -0
- package/dist/bleKeyMissing.d.ts +7 -0
- package/dist/bleKeyMissing.d.ts.map +1 -0
- package/dist/index.d.ts +35 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +384 -23
- package/package.json +6 -6
- package/src/BleManager.ts +78 -38
- package/src/__tests__/androidEncryption.test.ts +482 -0
- package/src/__tests__/androidKeyMissing.test.ts +307 -0
- package/src/__tests__/enumerate.test.ts +15 -0
- package/src/__tests__/protocolV2Link.test.ts +15 -0
- package/src/bleEncryption.ts +267 -0
- package/src/bleKeyMissing.ts +106 -0
- package/src/index.ts +153 -8
package/dist/index.js
CHANGED
|
@@ -46,6 +46,76 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
46
46
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
+
const lastKeyMissingAt = new Map();
|
|
50
|
+
const listeners$1 = new Set();
|
|
51
|
+
let unsubscribeNative$1;
|
|
52
|
+
const normalizeDeviceId$1 = (deviceId) => deviceId.toLowerCase();
|
|
53
|
+
const getCapableBleUtils$1 = () => {
|
|
54
|
+
if (reactNative.Platform.OS !== 'android')
|
|
55
|
+
return undefined;
|
|
56
|
+
const bleUtils = BleUtils__default["default"];
|
|
57
|
+
if (typeof bleUtils.supportsDeviceKeyMissing !== 'function' ||
|
|
58
|
+
typeof bleUtils.onDeviceKeyMissing !== 'function') {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
return bleUtils.supportsDeviceKeyMissing() ? bleUtils : undefined;
|
|
62
|
+
};
|
|
63
|
+
const isBleKeyMissingSupported = () => getCapableBleUtils$1() !== undefined;
|
|
64
|
+
const startBleKeyMissingTracking = () => {
|
|
65
|
+
if (unsubscribeNative$1)
|
|
66
|
+
return true;
|
|
67
|
+
const bleUtils = getCapableBleUtils$1();
|
|
68
|
+
if (!(bleUtils === null || bleUtils === void 0 ? void 0 : bleUtils.onDeviceKeyMissing))
|
|
69
|
+
return false;
|
|
70
|
+
unsubscribeNative$1 = bleUtils.onDeviceKeyMissing(event => {
|
|
71
|
+
if (typeof (event === null || event === void 0 ? void 0 : event.id) !== 'string')
|
|
72
|
+
return;
|
|
73
|
+
const deviceId = normalizeDeviceId$1(event.id);
|
|
74
|
+
lastKeyMissingAt.set(deviceId, Date.now());
|
|
75
|
+
listeners$1.forEach(listener => listener(deviceId));
|
|
76
|
+
});
|
|
77
|
+
return true;
|
|
78
|
+
};
|
|
79
|
+
const stopBleKeyMissingTracking = () => {
|
|
80
|
+
unsubscribeNative$1 === null || unsubscribeNative$1 === void 0 ? void 0 : unsubscribeNative$1();
|
|
81
|
+
unsubscribeNative$1 = undefined;
|
|
82
|
+
lastKeyMissingAt.clear();
|
|
83
|
+
listeners$1.clear();
|
|
84
|
+
};
|
|
85
|
+
const hasBleKeyMissingSince = (deviceId, sinceMs) => {
|
|
86
|
+
const at = lastKeyMissingAt.get(normalizeDeviceId$1(deviceId));
|
|
87
|
+
return at !== undefined && at >= sinceMs;
|
|
88
|
+
};
|
|
89
|
+
const onBleKeyMissing = (deviceId, callback) => {
|
|
90
|
+
const target = normalizeDeviceId$1(deviceId);
|
|
91
|
+
const listener = id => {
|
|
92
|
+
if (id === target)
|
|
93
|
+
callback();
|
|
94
|
+
};
|
|
95
|
+
listeners$1.add(listener);
|
|
96
|
+
return () => {
|
|
97
|
+
listeners$1.delete(listener);
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
const waitForBleKeyMissing = (deviceId, sinceMs, timeoutMs) => {
|
|
101
|
+
if (!startBleKeyMissingTracking())
|
|
102
|
+
return Promise.resolve(false);
|
|
103
|
+
if (hasBleKeyMissingSince(deviceId, sinceMs))
|
|
104
|
+
return Promise.resolve(true);
|
|
105
|
+
return new Promise(resolve => {
|
|
106
|
+
const settle = (found) => {
|
|
107
|
+
clearTimeout(timer);
|
|
108
|
+
cleanup();
|
|
109
|
+
resolve(found);
|
|
110
|
+
};
|
|
111
|
+
const cleanup = onBleKeyMissing(deviceId, () => {
|
|
112
|
+
if (hasBleKeyMissingSince(deviceId, sinceMs))
|
|
113
|
+
settle(true);
|
|
114
|
+
});
|
|
115
|
+
const timer = setTimeout(() => settle(false), timeoutMs);
|
|
116
|
+
});
|
|
117
|
+
};
|
|
118
|
+
|
|
49
119
|
let activeLogger;
|
|
50
120
|
const setBleLogger = (logger) => {
|
|
51
121
|
activeLogger = logger;
|
|
@@ -70,17 +140,35 @@ const bondFailureReasons = {
|
|
|
70
140
|
};
|
|
71
141
|
const getConnectedDeviceIds = (serviceUuids) => BleUtils__default["default"].getConnectedPeripherals(serviceUuids);
|
|
72
142
|
const pairDevice = (macAddress) => BleUtils__default["default"].pairDevice(macAddress);
|
|
73
|
-
const
|
|
143
|
+
const SYSTEM_BONDING_RESTART_WINDOW_MS = 1500;
|
|
144
|
+
const createBondFailureError = (bondState) => {
|
|
145
|
+
var _a;
|
|
146
|
+
const nativeReason = 'reason' in bondState && typeof bondState.reason === 'number' ? bondState.reason : undefined;
|
|
147
|
+
const reason = nativeReason === undefined ? 'unknown' : (_a = bondFailureReasons[nativeReason]) !== null && _a !== void 0 ? _a : 'unknown';
|
|
148
|
+
const params = Object.assign({ phase: 'bond', reason }, (nativeReason === undefined ? {} : { nativeReason }));
|
|
149
|
+
if (reason === 'timeout') {
|
|
150
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'Bluetooth pairing timed out', params);
|
|
151
|
+
}
|
|
152
|
+
if (reason === 'canceled') {
|
|
153
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceBondedCanceled, 'Bluetooth pairing canceled', params);
|
|
154
|
+
}
|
|
155
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'Bluetooth pairing failed', params);
|
|
156
|
+
};
|
|
157
|
+
const onDeviceBondState = (bleMacAddress, signal, options) => new Promise((resolve, reject) => {
|
|
74
158
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
75
159
|
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected));
|
|
76
160
|
return;
|
|
77
161
|
}
|
|
162
|
+
let pendingFailure;
|
|
78
163
|
const cleanup = () => {
|
|
79
164
|
if (timeout) {
|
|
80
165
|
clearTimeout(timeout);
|
|
81
166
|
}
|
|
167
|
+
if (pendingFailure)
|
|
168
|
+
clearTimeout(pendingFailure);
|
|
82
169
|
if (cleanupListener)
|
|
83
170
|
cleanupListener();
|
|
171
|
+
cleanupKeyMissing === null || cleanupKeyMissing === void 0 ? void 0 : cleanupKeyMissing();
|
|
84
172
|
signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', onAbort);
|
|
85
173
|
};
|
|
86
174
|
const onAbort = () => {
|
|
@@ -95,35 +183,44 @@ const onDeviceBondState = (bleMacAddress, signal) => new Promise((resolve, rejec
|
|
|
95
183
|
reason: 'timeout',
|
|
96
184
|
}));
|
|
97
185
|
}, 60 * 1000);
|
|
186
|
+
const cleanupKeyMissing = startBleKeyMissingTracking()
|
|
187
|
+
? onBleKeyMissing(bleMacAddress, () => {
|
|
188
|
+
cleanup();
|
|
189
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleBondInvalid, undefined, {
|
|
190
|
+
phase: 'bond',
|
|
191
|
+
reason: 'key_missing',
|
|
192
|
+
}));
|
|
193
|
+
})
|
|
194
|
+
: undefined;
|
|
98
195
|
const cleanupListener = BleUtils__default["default"].onDeviceBondState(peripheral => {
|
|
99
|
-
var _a
|
|
196
|
+
var _a;
|
|
100
197
|
if (((_a = peripheral.id) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== bleMacAddress.toLowerCase()) {
|
|
101
198
|
return;
|
|
102
199
|
}
|
|
103
200
|
const { bondState } = peripheral;
|
|
104
201
|
const hasBonded = bondState.preState === 'BOND_BONDING' && bondState.state === 'BOND_BONDED';
|
|
105
202
|
const hasFailed = bondState.preState === 'BOND_BONDING' && bondState.state === 'BOND_NONE';
|
|
203
|
+
const hasRestarted = bondState.preState === 'BOND_NONE' && bondState.state === 'BOND_BONDING';
|
|
106
204
|
Logger.debug('onDeviceBondState bondState:', bondState);
|
|
107
205
|
if (hasBonded) {
|
|
108
206
|
cleanup();
|
|
109
207
|
resolve(peripheral);
|
|
110
208
|
}
|
|
209
|
+
else if (hasRestarted && pendingFailure) {
|
|
210
|
+
clearTimeout(pendingFailure);
|
|
211
|
+
pendingFailure = undefined;
|
|
212
|
+
}
|
|
213
|
+
else if (hasFailed && (options === null || options === void 0 ? void 0 : options.systemInitiated)) {
|
|
214
|
+
if (pendingFailure)
|
|
215
|
+
clearTimeout(pendingFailure);
|
|
216
|
+
pendingFailure = setTimeout(() => {
|
|
217
|
+
cleanup();
|
|
218
|
+
reject(createBondFailureError(bondState));
|
|
219
|
+
}, SYSTEM_BONDING_RESTART_WINDOW_MS);
|
|
220
|
+
}
|
|
111
221
|
else if (hasFailed) {
|
|
112
222
|
cleanup();
|
|
113
|
-
|
|
114
|
-
? bondState.reason
|
|
115
|
-
: undefined;
|
|
116
|
-
const reason = nativeReason === undefined ? 'unknown' : (_b = bondFailureReasons[nativeReason]) !== null && _b !== void 0 ? _b : 'unknown';
|
|
117
|
-
const params = Object.assign({ phase: 'bond', reason }, (nativeReason === undefined ? {} : { nativeReason }));
|
|
118
|
-
if (reason === 'timeout') {
|
|
119
|
-
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'Bluetooth pairing timed out', params));
|
|
120
|
-
}
|
|
121
|
-
else if (reason === 'canceled') {
|
|
122
|
-
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceBondedCanceled, 'Bluetooth pairing canceled', params));
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'Bluetooth pairing failed', params));
|
|
126
|
-
}
|
|
223
|
+
reject(createBondFailureError(bondState));
|
|
127
224
|
}
|
|
128
225
|
});
|
|
129
226
|
});
|
|
@@ -231,6 +328,172 @@ const subscribeBleOn = (bleManager, ms = 1000) => new Promise((resolve, reject)
|
|
|
231
328
|
}, ms);
|
|
232
329
|
});
|
|
233
330
|
|
|
331
|
+
const HCI_PIN_OR_KEY_MISSING = 0x06;
|
|
332
|
+
const lastEncryption = new Map();
|
|
333
|
+
const lastAclDisconnectAt = new Map();
|
|
334
|
+
const listeners = new Set();
|
|
335
|
+
let unsubscribeNative;
|
|
336
|
+
const normalizeDeviceId = (deviceId) => deviceId.toLowerCase();
|
|
337
|
+
const isEncrypted = (record) => record.status === 0 && record.enabled;
|
|
338
|
+
const getCapableBleUtils = () => {
|
|
339
|
+
if (reactNative.Platform.OS !== 'android')
|
|
340
|
+
return undefined;
|
|
341
|
+
const bleUtils = BleUtils__default["default"];
|
|
342
|
+
if (typeof bleUtils.supportsDeviceEncryptionChange !== 'function' ||
|
|
343
|
+
typeof bleUtils.onDeviceEncryptionChange !== 'function' ||
|
|
344
|
+
typeof bleUtils.onDeviceAclDisconnected !== 'function') {
|
|
345
|
+
return undefined;
|
|
346
|
+
}
|
|
347
|
+
return bleUtils.supportsDeviceEncryptionChange() ? bleUtils : undefined;
|
|
348
|
+
};
|
|
349
|
+
const notify = (deviceId) => {
|
|
350
|
+
listeners.forEach(listener => listener(deviceId));
|
|
351
|
+
};
|
|
352
|
+
const startBleEncryptionTracking = () => {
|
|
353
|
+
if (unsubscribeNative)
|
|
354
|
+
return true;
|
|
355
|
+
const bleUtils = getCapableBleUtils();
|
|
356
|
+
if (!(bleUtils === null || bleUtils === void 0 ? void 0 : bleUtils.onDeviceEncryptionChange) || !bleUtils.onDeviceAclDisconnected)
|
|
357
|
+
return false;
|
|
358
|
+
unsubscribeNative = [
|
|
359
|
+
bleUtils.onDeviceEncryptionChange(event => {
|
|
360
|
+
if (typeof (event === null || event === void 0 ? void 0 : event.id) !== 'string')
|
|
361
|
+
return;
|
|
362
|
+
const deviceId = normalizeDeviceId(event.id);
|
|
363
|
+
lastEncryption.set(deviceId, {
|
|
364
|
+
at: Date.now(),
|
|
365
|
+
status: Number(event.status),
|
|
366
|
+
enabled: event.enabled === true,
|
|
367
|
+
});
|
|
368
|
+
notify(deviceId);
|
|
369
|
+
}),
|
|
370
|
+
bleUtils.onDeviceAclDisconnected(event => {
|
|
371
|
+
if (typeof (event === null || event === void 0 ? void 0 : event.id) !== 'string')
|
|
372
|
+
return;
|
|
373
|
+
const deviceId = normalizeDeviceId(event.id);
|
|
374
|
+
lastAclDisconnectAt.set(deviceId, Date.now());
|
|
375
|
+
notify(deviceId);
|
|
376
|
+
}),
|
|
377
|
+
];
|
|
378
|
+
return true;
|
|
379
|
+
};
|
|
380
|
+
const stopBleEncryptionTracking = () => {
|
|
381
|
+
unsubscribeNative === null || unsubscribeNative === void 0 ? void 0 : unsubscribeNative.forEach(unsubscribe => unsubscribe());
|
|
382
|
+
unsubscribeNative = undefined;
|
|
383
|
+
lastEncryption.clear();
|
|
384
|
+
lastAclDisconnectAt.clear();
|
|
385
|
+
listeners.clear();
|
|
386
|
+
};
|
|
387
|
+
const markBleLinkEncrypted = (deviceId) => {
|
|
388
|
+
if (!unsubscribeNative)
|
|
389
|
+
return;
|
|
390
|
+
lastEncryption.set(normalizeDeviceId(deviceId), { at: Date.now(), status: 0, enabled: true });
|
|
391
|
+
};
|
|
392
|
+
const waitForAndroidLinkEncryption = ({ deviceId, linkStartedAt, resultTimeoutMs, repairTimeoutMs, keyMissingGraceMs, signal, onRepairStarted, }) => {
|
|
393
|
+
if (!startBleEncryptionTracking())
|
|
394
|
+
return Promise.resolve('unresolved');
|
|
395
|
+
startBleKeyMissingTracking();
|
|
396
|
+
const target = normalizeDeviceId(deviceId);
|
|
397
|
+
const previous = lastEncryption.get(target);
|
|
398
|
+
const previousDropAt = lastAclDisconnectAt.get(target);
|
|
399
|
+
if (previous &&
|
|
400
|
+
previous.at < linkStartedAt &&
|
|
401
|
+
isEncrypted(previous) &&
|
|
402
|
+
(previousDropAt === undefined || previousDropAt < previous.at)) {
|
|
403
|
+
return Promise.resolve('already-encrypted');
|
|
404
|
+
}
|
|
405
|
+
return new Promise((resolve, reject) => {
|
|
406
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
407
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected));
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
let repairStartedAt;
|
|
411
|
+
let droppedDuringRepair = false;
|
|
412
|
+
let timer;
|
|
413
|
+
let settled = false;
|
|
414
|
+
const cleanup = () => {
|
|
415
|
+
settled = true;
|
|
416
|
+
if (timer)
|
|
417
|
+
clearTimeout(timer);
|
|
418
|
+
listeners.delete(onLinkEvent);
|
|
419
|
+
cleanupKeyMissing();
|
|
420
|
+
signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', onAbort);
|
|
421
|
+
};
|
|
422
|
+
const finish = (result) => {
|
|
423
|
+
cleanup();
|
|
424
|
+
resolve(result);
|
|
425
|
+
};
|
|
426
|
+
const fail = (error) => {
|
|
427
|
+
cleanup();
|
|
428
|
+
reject(error);
|
|
429
|
+
};
|
|
430
|
+
const arm = (ms, onExpire) => {
|
|
431
|
+
if (timer)
|
|
432
|
+
clearTimeout(timer);
|
|
433
|
+
timer = setTimeout(onExpire, ms);
|
|
434
|
+
};
|
|
435
|
+
const bondInvalid = () => hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleBondInvalid, undefined, {
|
|
436
|
+
phase: 'connect',
|
|
437
|
+
reason: 'key_missing',
|
|
438
|
+
});
|
|
439
|
+
const evaluate = () => {
|
|
440
|
+
if (settled)
|
|
441
|
+
return;
|
|
442
|
+
const record = lastEncryption.get(target);
|
|
443
|
+
const dropAt = lastAclDisconnectAt.get(target);
|
|
444
|
+
if (repairStartedAt === undefined) {
|
|
445
|
+
if (hasBleKeyMissingSince(target, linkStartedAt)) {
|
|
446
|
+
fail(bondInvalid());
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
if (dropAt !== undefined && dropAt >= linkStartedAt) {
|
|
450
|
+
finish('unresolved');
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
if (!record || record.at < linkStartedAt)
|
|
454
|
+
return;
|
|
455
|
+
if (isEncrypted(record)) {
|
|
456
|
+
finish('encrypted');
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
if (record.status !== HCI_PIN_OR_KEY_MISSING) {
|
|
460
|
+
finish('unresolved');
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
repairStartedAt = record.at;
|
|
464
|
+
onRepairStarted === null || onRepairStarted === void 0 ? void 0 : onRepairStarted();
|
|
465
|
+
arm(repairTimeoutMs, () => fail(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'Bluetooth pairing timed out', {
|
|
466
|
+
phase: 'bond',
|
|
467
|
+
reason: 'timeout',
|
|
468
|
+
})));
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
if (hasBleKeyMissingSince(target, repairStartedAt)) {
|
|
472
|
+
fail(bondInvalid());
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
if (record && record.at > repairStartedAt && isEncrypted(record)) {
|
|
476
|
+
finish('re-paired');
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
if (!droppedDuringRepair && dropAt !== undefined && dropAt >= repairStartedAt) {
|
|
480
|
+
droppedDuringRepair = true;
|
|
481
|
+
arm(keyMissingGraceMs, () => fail(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected)));
|
|
482
|
+
}
|
|
483
|
+
};
|
|
484
|
+
const onLinkEvent = id => {
|
|
485
|
+
if (id === target)
|
|
486
|
+
evaluate();
|
|
487
|
+
};
|
|
488
|
+
const onAbort = () => fail(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected));
|
|
489
|
+
listeners.add(onLinkEvent);
|
|
490
|
+
const cleanupKeyMissing = onBleKeyMissing(target, evaluate);
|
|
491
|
+
signal === null || signal === void 0 ? void 0 : signal.addEventListener('abort', onAbort, { once: true });
|
|
492
|
+
arm(resultTimeoutMs, () => finish('unresolved'));
|
|
493
|
+
evaluate();
|
|
494
|
+
});
|
|
495
|
+
};
|
|
496
|
+
|
|
234
497
|
const BLE_PLX_DEVICE_DISCONNECTED = 201;
|
|
235
498
|
const IOS_PERIPHERAL_DISCONNECTED = 7;
|
|
236
499
|
const nativeErrorText$1 = (error) => [error.reason, error.message]
|
|
@@ -386,6 +649,18 @@ const shouldRethrowProtocolProbeError = (error) => {
|
|
|
386
649
|
code === hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure ||
|
|
387
650
|
code === hdShared.HardwareErrorCode.BleWriteCharacteristicError);
|
|
388
651
|
};
|
|
652
|
+
const ANDROID_KEY_MISSING_LINK_WINDOW_MS = 10000;
|
|
653
|
+
const ANDROID_KEY_MISSING_GRACE_MS = 500;
|
|
654
|
+
const ANDROID_ENCRYPTION_RESULT_TIMEOUT_MS = 1500;
|
|
655
|
+
const ANDROID_SYSTEM_REPAIR_TIMEOUT_MS = 35000;
|
|
656
|
+
const isAndroidLinkLossError = (error) => {
|
|
657
|
+
const code = error === null || error === void 0 ? void 0 : error.errorCode;
|
|
658
|
+
return (isNativeBleDisconnectError(error) ||
|
|
659
|
+
code === hdShared.HardwareErrorCode.BleDeviceNotBonded ||
|
|
660
|
+
code === hdShared.HardwareErrorCode.BleDeviceDisconnected ||
|
|
661
|
+
code === hdShared.HardwareErrorCode.BleCharacteristicNotifyError ||
|
|
662
|
+
code === hdShared.HardwareErrorCode.BleConnectedError);
|
|
663
|
+
};
|
|
389
664
|
const BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
|
|
390
665
|
const DEVICE_SCAN_TIMEOUT_MS = 3000;
|
|
391
666
|
const IOS_NOTIFY_READY_DELAY_MS = 150;
|
|
@@ -583,6 +858,7 @@ class ReactNativeBleTransport {
|
|
|
583
858
|
this.silentDetections = new Map();
|
|
584
859
|
this.androidGattCacheRefreshes = new Set();
|
|
585
860
|
this.staleBondErrors = new Map();
|
|
861
|
+
this.androidLinkStartedAt = new Map();
|
|
586
862
|
this.acquiringProtocolV2 = new Set();
|
|
587
863
|
this.protocolV2Assemblers = new Map();
|
|
588
864
|
this.protocolV2FrameQueues = new Map();
|
|
@@ -630,6 +906,10 @@ class ReactNativeBleTransport {
|
|
|
630
906
|
init(logger, emitter) {
|
|
631
907
|
setBleLogger(logger);
|
|
632
908
|
this.emitter = emitter;
|
|
909
|
+
if (reactNative.Platform.OS === 'android') {
|
|
910
|
+
startBleKeyMissingTracking();
|
|
911
|
+
startBleEncryptionTracking();
|
|
912
|
+
}
|
|
633
913
|
}
|
|
634
914
|
configure(signedData) {
|
|
635
915
|
const messages = parseConfigure(signedData);
|
|
@@ -1016,7 +1296,14 @@ class ReactNativeBleTransport {
|
|
|
1016
1296
|
if (!uuid) {
|
|
1017
1297
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleRequiredUUID);
|
|
1018
1298
|
}
|
|
1019
|
-
return this.runLifecycleOperation(uuid, () => this
|
|
1299
|
+
return this.runLifecycleOperation(uuid, () => __awaiter(this, void 0, void 0, function* () {
|
|
1300
|
+
try {
|
|
1301
|
+
return yield this.acquireUnlocked(input);
|
|
1302
|
+
}
|
|
1303
|
+
catch (error) {
|
|
1304
|
+
throw yield this.resolveAndroidBondInvalid(uuid, error);
|
|
1305
|
+
}
|
|
1306
|
+
}));
|
|
1020
1307
|
});
|
|
1021
1308
|
}
|
|
1022
1309
|
acquireUnlocked(input) {
|
|
@@ -1071,6 +1358,7 @@ class ReactNativeBleTransport {
|
|
|
1071
1358
|
}
|
|
1072
1359
|
let device = null;
|
|
1073
1360
|
const isAndroid = reactNative.Platform.OS === 'android';
|
|
1361
|
+
let androidBondedBeforeConnect = false;
|
|
1074
1362
|
const refreshAndroidGattCache = isAndroid && (!!skipProtocolProbe || this.androidGattCacheRefreshes.has(uuid));
|
|
1075
1363
|
let nativeConnectOptions = connectOptions;
|
|
1076
1364
|
if (isAndroid) {
|
|
@@ -1098,10 +1386,16 @@ class ReactNativeBleTransport {
|
|
|
1098
1386
|
if (this.stopped)
|
|
1099
1387
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1100
1388
|
if (reactNative.Platform.OS === 'android') {
|
|
1389
|
+
startBleKeyMissingTracking();
|
|
1390
|
+
startBleEncryptionTracking();
|
|
1391
|
+
this.androidLinkStartedAt.delete(uuid);
|
|
1101
1392
|
try {
|
|
1102
1393
|
const bondState = yield pairDevice(uuid);
|
|
1394
|
+
androidBondedBeforeConnect = !bondState.bonding && bondState.bonded;
|
|
1103
1395
|
if (bondState.bonding) {
|
|
1104
|
-
yield onDeviceBondState(uuid, this.bondAbortController.signal
|
|
1396
|
+
yield onDeviceBondState(uuid, this.bondAbortController.signal, {
|
|
1397
|
+
systemInitiated: bondState.initiated === false,
|
|
1398
|
+
});
|
|
1105
1399
|
}
|
|
1106
1400
|
else if (!bondState.bonded) {
|
|
1107
1401
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'device is not bonded');
|
|
@@ -1113,6 +1407,7 @@ class ReactNativeBleTransport {
|
|
|
1113
1407
|
}));
|
|
1114
1408
|
throw error;
|
|
1115
1409
|
}
|
|
1410
|
+
this.androidLinkStartedAt.set(uuid, Date.now());
|
|
1116
1411
|
}
|
|
1117
1412
|
if (this.stopped)
|
|
1118
1413
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
@@ -1270,7 +1565,13 @@ class ReactNativeBleTransport {
|
|
|
1270
1565
|
if (shouldMapProtocolV2StaleBond) {
|
|
1271
1566
|
this.acquiringProtocolV2.add(uuid);
|
|
1272
1567
|
}
|
|
1568
|
+
let linkEncryption;
|
|
1273
1569
|
try {
|
|
1570
|
+
if (androidBondedBeforeConnect && !skipProtocolProbe) {
|
|
1571
|
+
linkEncryption = yield this.waitForAndroidLinkSecurity(uuid);
|
|
1572
|
+
if (this.stopped)
|
|
1573
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1574
|
+
}
|
|
1274
1575
|
yield this.installTransportForAcquire(uuid, acquiredDevice, {
|
|
1275
1576
|
writeCharacteristic,
|
|
1276
1577
|
notifyCharacteristic,
|
|
@@ -1309,6 +1610,9 @@ class ReactNativeBleTransport {
|
|
|
1309
1610
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
|
|
1310
1611
|
}
|
|
1311
1612
|
this.attachDisconnectSubscription(currentTransport, currentTransport.device, uuid);
|
|
1613
|
+
if (linkEncryption === 'unresolved') {
|
|
1614
|
+
markBleLinkEncrypted(uuid);
|
|
1615
|
+
}
|
|
1312
1616
|
return { uuid, protocolType };
|
|
1313
1617
|
}
|
|
1314
1618
|
catch (error) {
|
|
@@ -1510,14 +1814,64 @@ class ReactNativeBleTransport {
|
|
|
1510
1814
|
if (!protocol) {
|
|
1511
1815
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${uuid}`);
|
|
1512
1816
|
}
|
|
1513
|
-
if (protocol === 'V2') {
|
|
1514
|
-
return this.callProtocolV2(uuid, name, data, options);
|
|
1515
|
-
}
|
|
1516
1817
|
const forceRun = name === 'Initialize' || name === 'Cancel';
|
|
1517
|
-
if (this.runPromise && !forceRun) {
|
|
1818
|
+
if (protocol !== 'V2' && this.runPromise && !forceRun) {
|
|
1518
1819
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportCallInProgress);
|
|
1519
1820
|
}
|
|
1520
|
-
|
|
1821
|
+
try {
|
|
1822
|
+
return protocol === 'V2'
|
|
1823
|
+
? yield this.callProtocolV2(uuid, name, data, options)
|
|
1824
|
+
: yield this.callProtocolV1(uuid, name, data, options);
|
|
1825
|
+
}
|
|
1826
|
+
catch (error) {
|
|
1827
|
+
throw yield this.resolveAndroidBondInvalid(uuid, error);
|
|
1828
|
+
}
|
|
1829
|
+
});
|
|
1830
|
+
}
|
|
1831
|
+
resolveAndroidBondInvalid(uuid, error) {
|
|
1832
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1833
|
+
if (reactNative.Platform.OS !== 'android' || !isBleKeyMissingSupported())
|
|
1834
|
+
return error;
|
|
1835
|
+
if (hdShared.isBleStaleBondHardwareError(error) || !isAndroidLinkLossError(error))
|
|
1836
|
+
return error;
|
|
1837
|
+
const linkStartedAt = this.androidLinkStartedAt.get(uuid);
|
|
1838
|
+
if (linkStartedAt === undefined ||
|
|
1839
|
+
Date.now() - linkStartedAt > ANDROID_KEY_MISSING_LINK_WINDOW_MS) {
|
|
1840
|
+
return error;
|
|
1841
|
+
}
|
|
1842
|
+
if (!(yield waitForBleKeyMissing(uuid, linkStartedAt, ANDROID_KEY_MISSING_GRACE_MS))) {
|
|
1843
|
+
return error;
|
|
1844
|
+
}
|
|
1845
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android key missing, bond is invalid:', uuid);
|
|
1846
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleBondInvalid, undefined, {
|
|
1847
|
+
phase: 'connect',
|
|
1848
|
+
reason: 'key_missing',
|
|
1849
|
+
});
|
|
1850
|
+
});
|
|
1851
|
+
}
|
|
1852
|
+
waitForAndroidLinkSecurity(uuid) {
|
|
1853
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1854
|
+
const linkStartedAt = this.androidLinkStartedAt.get(uuid);
|
|
1855
|
+
if (linkStartedAt === undefined)
|
|
1856
|
+
return 'unresolved';
|
|
1857
|
+
const waitStartedAt = Date.now();
|
|
1858
|
+
const result = yield waitForAndroidLinkEncryption({
|
|
1859
|
+
deviceId: uuid,
|
|
1860
|
+
linkStartedAt,
|
|
1861
|
+
resultTimeoutMs: ANDROID_ENCRYPTION_RESULT_TIMEOUT_MS,
|
|
1862
|
+
repairTimeoutMs: ANDROID_SYSTEM_REPAIR_TIMEOUT_MS,
|
|
1863
|
+
keyMissingGraceMs: ANDROID_KEY_MISSING_GRACE_MS,
|
|
1864
|
+
signal: this.bondAbortController.signal,
|
|
1865
|
+
onRepairStarted: () => {
|
|
1866
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android bond lost, waiting for system re-pairing:', uuid);
|
|
1867
|
+
},
|
|
1868
|
+
});
|
|
1869
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android link encryption', {
|
|
1870
|
+
connectIdSuffix: uuid.slice(-8),
|
|
1871
|
+
result,
|
|
1872
|
+
waitedMs: Date.now() - waitStartedAt,
|
|
1873
|
+
});
|
|
1874
|
+
return result;
|
|
1521
1875
|
});
|
|
1522
1876
|
}
|
|
1523
1877
|
callProtocolV1(uuid, name, data, options) {
|
|
@@ -1733,6 +2087,9 @@ class ReactNativeBleTransport {
|
|
|
1733
2087
|
this.androidPriorityResetTimers.forEach(timeout => clearTimeout(timeout));
|
|
1734
2088
|
this.androidPriorityResetTimers.clear();
|
|
1735
2089
|
this.androidHighPriorityDevices.clear();
|
|
2090
|
+
this.androidLinkStartedAt.clear();
|
|
2091
|
+
stopBleKeyMissingTracking();
|
|
2092
|
+
stopBleEncryptionTracking();
|
|
1736
2093
|
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1737
2094
|
(_a = this.runPromise) === null || _a === void 0 ? void 0 : _a.reject(error);
|
|
1738
2095
|
this.runPromise = null;
|
|
@@ -2766,8 +3123,12 @@ class ReactNativeBleTransport {
|
|
|
2766
3123
|
}
|
|
2767
3124
|
}
|
|
2768
3125
|
|
|
3126
|
+
exports.ANDROID_ENCRYPTION_RESULT_TIMEOUT_MS = ANDROID_ENCRYPTION_RESULT_TIMEOUT_MS;
|
|
3127
|
+
exports.ANDROID_KEY_MISSING_GRACE_MS = ANDROID_KEY_MISSING_GRACE_MS;
|
|
3128
|
+
exports.ANDROID_KEY_MISSING_LINK_WINDOW_MS = ANDROID_KEY_MISSING_LINK_WINDOW_MS;
|
|
2769
3129
|
exports.ANDROID_LINK_DROP_QUIET_MS = ANDROID_LINK_DROP_QUIET_MS;
|
|
2770
3130
|
exports.ANDROID_MTU_EXCHANGE_TIMEOUT_MS = ANDROID_MTU_EXCHANGE_TIMEOUT_MS;
|
|
3131
|
+
exports.ANDROID_SYSTEM_REPAIR_TIMEOUT_MS = ANDROID_SYSTEM_REPAIR_TIMEOUT_MS;
|
|
2771
3132
|
exports.BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD;
|
|
2772
3133
|
exports.BLE_CONNECT_TIMEOUT_MS = BLE_CONNECT_TIMEOUT_MS;
|
|
2773
3134
|
exports.BLE_GATT_SETUP_TIMEOUT_MS = BLE_GATT_SETUP_TIMEOUT_MS;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-transport-react-native",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3-alpha.10",
|
|
4
4
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -20,11 +20,11 @@
|
|
|
20
20
|
"lint:fix": "eslint . --fix"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@onekeyfe/hd-core": "1.2.
|
|
24
|
-
"@onekeyfe/hd-shared": "1.2.
|
|
25
|
-
"@onekeyfe/hd-transport": "1.2.
|
|
26
|
-
"@onekeyfe/react-native-ble-utils": "0.1.
|
|
23
|
+
"@onekeyfe/hd-core": "1.2.3-alpha.10",
|
|
24
|
+
"@onekeyfe/hd-shared": "1.2.3-alpha.10",
|
|
25
|
+
"@onekeyfe/hd-transport": "1.2.3-alpha.10",
|
|
26
|
+
"@onekeyfe/react-native-ble-utils": "0.1.9",
|
|
27
27
|
"react-native-ble-plx": "3.5.1"
|
|
28
28
|
},
|
|
29
|
-
"gitHead": "
|
|
29
|
+
"gitHead": "2295285a846281941c21955684281446f587d993"
|
|
30
30
|
}
|