@onekeyfe/hd-transport-react-native 1.2.3-alpha.9 → 1.2.4-alpha.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/dist/bleEncryption.d.ts +17 -0
- package/dist/bleEncryption.d.ts.map +1 -0
- package/dist/index.d.ts +29 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +367 -31
- package/package.json +6 -6
- package/src/__tests__/androidEncryption.test.ts +482 -0
- package/src/__tests__/enumerate.test.ts +8 -1
- package/src/__tests__/protocolReprobe.test.ts +116 -1
- package/src/__tests__/protocolV2Link.test.ts +8 -1
- package/src/bleEncryption.ts +267 -0
- package/src/index.ts +254 -13
package/dist/index.js
CHANGED
|
@@ -47,10 +47,10 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
47
47
|
};
|
|
48
48
|
|
|
49
49
|
const lastKeyMissingAt = new Map();
|
|
50
|
-
const listeners = new Set();
|
|
51
|
-
let unsubscribeNative;
|
|
52
|
-
const normalizeDeviceId = (deviceId) => deviceId.toLowerCase();
|
|
53
|
-
const getCapableBleUtils = () => {
|
|
50
|
+
const listeners$1 = new Set();
|
|
51
|
+
let unsubscribeNative$1;
|
|
52
|
+
const normalizeDeviceId$1 = (deviceId) => deviceId.toLowerCase();
|
|
53
|
+
const getCapableBleUtils$1 = () => {
|
|
54
54
|
if (reactNative.Platform.OS !== 'android')
|
|
55
55
|
return undefined;
|
|
56
56
|
const bleUtils = BleUtils__default["default"];
|
|
@@ -60,41 +60,41 @@ const getCapableBleUtils = () => {
|
|
|
60
60
|
}
|
|
61
61
|
return bleUtils.supportsDeviceKeyMissing() ? bleUtils : undefined;
|
|
62
62
|
};
|
|
63
|
-
const isBleKeyMissingSupported = () => getCapableBleUtils() !== undefined;
|
|
63
|
+
const isBleKeyMissingSupported = () => getCapableBleUtils$1() !== undefined;
|
|
64
64
|
const startBleKeyMissingTracking = () => {
|
|
65
|
-
if (unsubscribeNative)
|
|
65
|
+
if (unsubscribeNative$1)
|
|
66
66
|
return true;
|
|
67
|
-
const bleUtils = getCapableBleUtils();
|
|
67
|
+
const bleUtils = getCapableBleUtils$1();
|
|
68
68
|
if (!(bleUtils === null || bleUtils === void 0 ? void 0 : bleUtils.onDeviceKeyMissing))
|
|
69
69
|
return false;
|
|
70
|
-
unsubscribeNative = bleUtils.onDeviceKeyMissing(event => {
|
|
70
|
+
unsubscribeNative$1 = bleUtils.onDeviceKeyMissing(event => {
|
|
71
71
|
if (typeof (event === null || event === void 0 ? void 0 : event.id) !== 'string')
|
|
72
72
|
return;
|
|
73
|
-
const deviceId = normalizeDeviceId(event.id);
|
|
73
|
+
const deviceId = normalizeDeviceId$1(event.id);
|
|
74
74
|
lastKeyMissingAt.set(deviceId, Date.now());
|
|
75
|
-
listeners.forEach(listener => listener(deviceId));
|
|
75
|
+
listeners$1.forEach(listener => listener(deviceId));
|
|
76
76
|
});
|
|
77
77
|
return true;
|
|
78
78
|
};
|
|
79
79
|
const stopBleKeyMissingTracking = () => {
|
|
80
|
-
unsubscribeNative === null || unsubscribeNative === void 0 ? void 0 : unsubscribeNative();
|
|
81
|
-
unsubscribeNative = undefined;
|
|
80
|
+
unsubscribeNative$1 === null || unsubscribeNative$1 === void 0 ? void 0 : unsubscribeNative$1();
|
|
81
|
+
unsubscribeNative$1 = undefined;
|
|
82
82
|
lastKeyMissingAt.clear();
|
|
83
|
-
listeners.clear();
|
|
83
|
+
listeners$1.clear();
|
|
84
84
|
};
|
|
85
85
|
const hasBleKeyMissingSince = (deviceId, sinceMs) => {
|
|
86
|
-
const at = lastKeyMissingAt.get(normalizeDeviceId(deviceId));
|
|
86
|
+
const at = lastKeyMissingAt.get(normalizeDeviceId$1(deviceId));
|
|
87
87
|
return at !== undefined && at >= sinceMs;
|
|
88
88
|
};
|
|
89
89
|
const onBleKeyMissing = (deviceId, callback) => {
|
|
90
|
-
const target = normalizeDeviceId(deviceId);
|
|
90
|
+
const target = normalizeDeviceId$1(deviceId);
|
|
91
91
|
const listener = id => {
|
|
92
92
|
if (id === target)
|
|
93
93
|
callback();
|
|
94
94
|
};
|
|
95
|
-
listeners.add(listener);
|
|
95
|
+
listeners$1.add(listener);
|
|
96
96
|
return () => {
|
|
97
|
-
listeners.delete(listener);
|
|
97
|
+
listeners$1.delete(listener);
|
|
98
98
|
};
|
|
99
99
|
};
|
|
100
100
|
const waitForBleKeyMissing = (deviceId, sinceMs, timeoutMs) => {
|
|
@@ -328,6 +328,172 @@ const subscribeBleOn = (bleManager, ms = 1000) => new Promise((resolve, reject)
|
|
|
328
328
|
}, ms);
|
|
329
329
|
});
|
|
330
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
|
+
|
|
331
497
|
const BLE_PLX_DEVICE_DISCONNECTED = 201;
|
|
332
498
|
const IOS_PERIPHERAL_DISCONNECTED = 7;
|
|
333
499
|
const nativeErrorText$1 = (error) => [error.reason, error.message]
|
|
@@ -485,6 +651,8 @@ const shouldRethrowProtocolProbeError = (error) => {
|
|
|
485
651
|
};
|
|
486
652
|
const ANDROID_KEY_MISSING_LINK_WINDOW_MS = 10000;
|
|
487
653
|
const ANDROID_KEY_MISSING_GRACE_MS = 500;
|
|
654
|
+
const ANDROID_ENCRYPTION_RESULT_TIMEOUT_MS = 1500;
|
|
655
|
+
const ANDROID_SYSTEM_REPAIR_TIMEOUT_MS = 35000;
|
|
488
656
|
const isAndroidLinkLossError = (error) => {
|
|
489
657
|
const code = error === null || error === void 0 ? void 0 : error.errorCode;
|
|
490
658
|
return (isNativeBleDisconnectError(error) ||
|
|
@@ -493,6 +661,33 @@ const isAndroidLinkLossError = (error) => {
|
|
|
493
661
|
code === hdShared.HardwareErrorCode.BleCharacteristicNotifyError ||
|
|
494
662
|
code === hdShared.HardwareErrorCode.BleConnectedError);
|
|
495
663
|
};
|
|
664
|
+
const logIosBleBondDiagnostic = (phase, error, outcome) => {
|
|
665
|
+
if (reactNative.Platform.OS !== 'ios')
|
|
666
|
+
return;
|
|
667
|
+
const nativeError = (error !== null && error !== void 0 ? error : {});
|
|
668
|
+
const scalar = (value) => typeof value === 'string' || typeof value === 'number' ? value : undefined;
|
|
669
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport][BleBondDiagnostic] probe error', {
|
|
670
|
+
phase,
|
|
671
|
+
outcome,
|
|
672
|
+
errorName: scalar(nativeError.name),
|
|
673
|
+
errorCode: scalar(nativeError.errorCode),
|
|
674
|
+
attErrorCode: scalar(nativeError.attErrorCode),
|
|
675
|
+
iosErrorCode: scalar(nativeError.iosErrorCode),
|
|
676
|
+
androidErrorCode: scalar(nativeError.androidErrorCode),
|
|
677
|
+
nativeStaleBond: isNativeBleStaleBondError(error),
|
|
678
|
+
hardwareStaleBond: hdShared.isBleStaleBondHardwareError(error),
|
|
679
|
+
nativeDisconnect: isNativeBleDisconnectError(error),
|
|
680
|
+
});
|
|
681
|
+
};
|
|
682
|
+
const IOS_PROBE_DISCONNECT_EVIDENCE_TTL_MS = 5 * 60000;
|
|
683
|
+
const isIosProbeDisconnectError = (error) => {
|
|
684
|
+
if (reactNative.Platform.OS !== 'ios')
|
|
685
|
+
return false;
|
|
686
|
+
const code = error === null || error === void 0 ? void 0 : error.errorCode;
|
|
687
|
+
return (isNativeBleDisconnectError(error) ||
|
|
688
|
+
code === hdShared.HardwareErrorCode.BleDeviceNotBonded ||
|
|
689
|
+
code === hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
690
|
+
};
|
|
496
691
|
const BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
|
|
497
692
|
const DEVICE_SCAN_TIMEOUT_MS = 3000;
|
|
498
693
|
const IOS_NOTIFY_READY_DELAY_MS = 150;
|
|
@@ -688,6 +883,7 @@ class ReactNativeBleTransport {
|
|
|
688
883
|
this.confirmedProtocolV2 = new Set();
|
|
689
884
|
this.protocolReprobeFailures = new Map();
|
|
690
885
|
this.silentDetections = new Map();
|
|
886
|
+
this.iosProbeDisconnectEvidence = new Map();
|
|
691
887
|
this.androidGattCacheRefreshes = new Set();
|
|
692
888
|
this.staleBondErrors = new Map();
|
|
693
889
|
this.androidLinkStartedAt = new Map();
|
|
@@ -738,6 +934,10 @@ class ReactNativeBleTransport {
|
|
|
738
934
|
init(logger, emitter) {
|
|
739
935
|
setBleLogger(logger);
|
|
740
936
|
this.emitter = emitter;
|
|
937
|
+
if (reactNative.Platform.OS === 'android') {
|
|
938
|
+
startBleKeyMissingTracking();
|
|
939
|
+
startBleEncryptionTracking();
|
|
940
|
+
}
|
|
741
941
|
}
|
|
742
942
|
configure(signedData) {
|
|
743
943
|
const messages = parseConfigure(signedData);
|
|
@@ -1186,6 +1386,7 @@ class ReactNativeBleTransport {
|
|
|
1186
1386
|
}
|
|
1187
1387
|
let device = null;
|
|
1188
1388
|
const isAndroid = reactNative.Platform.OS === 'android';
|
|
1389
|
+
let androidBondedBeforeConnect = false;
|
|
1189
1390
|
const refreshAndroidGattCache = isAndroid && (!!skipProtocolProbe || this.androidGattCacheRefreshes.has(uuid));
|
|
1190
1391
|
let nativeConnectOptions = connectOptions;
|
|
1191
1392
|
if (isAndroid) {
|
|
@@ -1214,9 +1415,11 @@ class ReactNativeBleTransport {
|
|
|
1214
1415
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1215
1416
|
if (reactNative.Platform.OS === 'android') {
|
|
1216
1417
|
startBleKeyMissingTracking();
|
|
1418
|
+
startBleEncryptionTracking();
|
|
1217
1419
|
this.androidLinkStartedAt.delete(uuid);
|
|
1218
1420
|
try {
|
|
1219
1421
|
const bondState = yield pairDevice(uuid);
|
|
1422
|
+
androidBondedBeforeConnect = !bondState.bonding && bondState.bonded;
|
|
1220
1423
|
if (bondState.bonding) {
|
|
1221
1424
|
yield onDeviceBondState(uuid, this.bondAbortController.signal, {
|
|
1222
1425
|
systemInitiated: bondState.initiated === false,
|
|
@@ -1390,7 +1593,13 @@ class ReactNativeBleTransport {
|
|
|
1390
1593
|
if (shouldMapProtocolV2StaleBond) {
|
|
1391
1594
|
this.acquiringProtocolV2.add(uuid);
|
|
1392
1595
|
}
|
|
1596
|
+
let linkEncryption;
|
|
1393
1597
|
try {
|
|
1598
|
+
if (androidBondedBeforeConnect && !skipProtocolProbe) {
|
|
1599
|
+
linkEncryption = yield this.waitForAndroidLinkSecurity(uuid);
|
|
1600
|
+
if (this.stopped)
|
|
1601
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1602
|
+
}
|
|
1394
1603
|
yield this.installTransportForAcquire(uuid, acquiredDevice, {
|
|
1395
1604
|
writeCharacteristic,
|
|
1396
1605
|
notifyCharacteristic,
|
|
@@ -1429,6 +1638,9 @@ class ReactNativeBleTransport {
|
|
|
1429
1638
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
|
|
1430
1639
|
}
|
|
1431
1640
|
this.attachDisconnectSubscription(currentTransport, currentTransport.device, uuid);
|
|
1641
|
+
if (linkEncryption === 'unresolved') {
|
|
1642
|
+
markBleLinkEncrypted(uuid);
|
|
1643
|
+
}
|
|
1432
1644
|
return { uuid, protocolType };
|
|
1433
1645
|
}
|
|
1434
1646
|
catch (error) {
|
|
@@ -1665,6 +1877,31 @@ class ReactNativeBleTransport {
|
|
|
1665
1877
|
});
|
|
1666
1878
|
});
|
|
1667
1879
|
}
|
|
1880
|
+
waitForAndroidLinkSecurity(uuid) {
|
|
1881
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1882
|
+
const linkStartedAt = this.androidLinkStartedAt.get(uuid);
|
|
1883
|
+
if (linkStartedAt === undefined)
|
|
1884
|
+
return 'unresolved';
|
|
1885
|
+
const waitStartedAt = Date.now();
|
|
1886
|
+
const result = yield waitForAndroidLinkEncryption({
|
|
1887
|
+
deviceId: uuid,
|
|
1888
|
+
linkStartedAt,
|
|
1889
|
+
resultTimeoutMs: ANDROID_ENCRYPTION_RESULT_TIMEOUT_MS,
|
|
1890
|
+
repairTimeoutMs: ANDROID_SYSTEM_REPAIR_TIMEOUT_MS,
|
|
1891
|
+
keyMissingGraceMs: ANDROID_KEY_MISSING_GRACE_MS,
|
|
1892
|
+
signal: this.bondAbortController.signal,
|
|
1893
|
+
onRepairStarted: () => {
|
|
1894
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android bond lost, waiting for system re-pairing:', uuid);
|
|
1895
|
+
},
|
|
1896
|
+
});
|
|
1897
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android link encryption', {
|
|
1898
|
+
connectIdSuffix: uuid.slice(-8),
|
|
1899
|
+
result,
|
|
1900
|
+
waitedMs: Date.now() - waitStartedAt,
|
|
1901
|
+
});
|
|
1902
|
+
return result;
|
|
1903
|
+
});
|
|
1904
|
+
}
|
|
1668
1905
|
callProtocolV1(uuid, name, data, options) {
|
|
1669
1906
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1670
1907
|
if (!this._messages) {
|
|
@@ -1880,6 +2117,8 @@ class ReactNativeBleTransport {
|
|
|
1880
2117
|
this.androidHighPriorityDevices.clear();
|
|
1881
2118
|
this.androidLinkStartedAt.clear();
|
|
1882
2119
|
stopBleKeyMissingTracking();
|
|
2120
|
+
stopBleEncryptionTracking();
|
|
2121
|
+
this.iosProbeDisconnectEvidence.clear();
|
|
1883
2122
|
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1884
2123
|
(_a = this.runPromise) === null || _a === void 0 ? void 0 : _a.reject(error);
|
|
1885
2124
|
this.runPromise = null;
|
|
@@ -2365,6 +2604,50 @@ class ReactNativeBleTransport {
|
|
|
2365
2604
|
createProtocolDetectionError() {
|
|
2366
2605
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1 GetFeatures or Protocol V2 Ping');
|
|
2367
2606
|
}
|
|
2607
|
+
rememberIosProbeDisconnect(uuid, phase, error) {
|
|
2608
|
+
if (!isIosProbeDisconnectError(error))
|
|
2609
|
+
return undefined;
|
|
2610
|
+
const previous = this.getIosProbeDisconnectEvidence(uuid);
|
|
2611
|
+
const errorCode = error === null || error === void 0 ? void 0 : error.errorCode;
|
|
2612
|
+
const evidence = Object.assign(Object.assign({ phase }, (typeof errorCode === 'string' || typeof errorCode === 'number' ? { errorCode } : {})), { recordedAt: Date.now() });
|
|
2613
|
+
this.iosProbeDisconnectEvidence.set(uuid, evidence);
|
|
2614
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport][BleBondDiagnostic] probe disconnect recorded', {
|
|
2615
|
+
phase,
|
|
2616
|
+
errorCode: evidence.errorCode,
|
|
2617
|
+
repeated: Boolean(previous),
|
|
2618
|
+
});
|
|
2619
|
+
if (!previous)
|
|
2620
|
+
return undefined;
|
|
2621
|
+
return this.confirmIosProbeDisconnect(uuid, 'repeated-probe-disconnect');
|
|
2622
|
+
}
|
|
2623
|
+
getIosProbeDisconnectEvidence(uuid) {
|
|
2624
|
+
const evidence = this.iosProbeDisconnectEvidence.get(uuid);
|
|
2625
|
+
if (!evidence)
|
|
2626
|
+
return undefined;
|
|
2627
|
+
const ageMs = Date.now() - evidence.recordedAt;
|
|
2628
|
+
if (ageMs <= IOS_PROBE_DISCONNECT_EVIDENCE_TTL_MS)
|
|
2629
|
+
return evidence;
|
|
2630
|
+
this.iosProbeDisconnectEvidence.delete(uuid);
|
|
2631
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport][BleBondDiagnostic] probe disconnect expired', {
|
|
2632
|
+
triggerPhase: evidence.phase,
|
|
2633
|
+
triggerErrorCode: evidence.errorCode,
|
|
2634
|
+
ageMs,
|
|
2635
|
+
});
|
|
2636
|
+
return undefined;
|
|
2637
|
+
}
|
|
2638
|
+
confirmIosProbeDisconnect(uuid, confirmation) {
|
|
2639
|
+
const evidence = this.getIosProbeDisconnectEvidence(uuid);
|
|
2640
|
+
if (!evidence)
|
|
2641
|
+
return undefined;
|
|
2642
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport][BleBondDiagnostic] stale bond confirmed', {
|
|
2643
|
+
confirmation,
|
|
2644
|
+
triggerPhase: evidence.phase,
|
|
2645
|
+
triggerErrorCode: evidence.errorCode,
|
|
2646
|
+
evidenceAgeMs: Date.now() - evidence.recordedAt,
|
|
2647
|
+
finalErrorCode: hdShared.HardwareErrorCode.BleBondInvalid,
|
|
2648
|
+
});
|
|
2649
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleBondInvalid);
|
|
2650
|
+
}
|
|
2368
2651
|
clearProbeProtocol(uuid, protocol) {
|
|
2369
2652
|
if (this.probingProtocols.get(uuid) === protocol) {
|
|
2370
2653
|
this.probingProtocols.delete(uuid);
|
|
@@ -2378,9 +2661,10 @@ class ReactNativeBleTransport {
|
|
|
2378
2661
|
return (_a = this.deviceProtocol.get(uuid)) !== null && _a !== void 0 ? _a : this.probingProtocols.get(uuid);
|
|
2379
2662
|
}
|
|
2380
2663
|
detectProtocol(uuid, expectedProtocol, protocolHint, rebuildTransport) {
|
|
2381
|
-
var _a;
|
|
2664
|
+
var _a, _b;
|
|
2382
2665
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2383
2666
|
if (expectedProtocol === 'V1') {
|
|
2667
|
+
this.iosProbeDisconnectEvidence.delete(uuid);
|
|
2384
2668
|
this.deviceProtocol.set(uuid, 'V1');
|
|
2385
2669
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol selected', {
|
|
2386
2670
|
deviceId: uuid,
|
|
@@ -2394,6 +2678,7 @@ class ReactNativeBleTransport {
|
|
|
2394
2678
|
}
|
|
2395
2679
|
if (expectedProtocol === 'V2') {
|
|
2396
2680
|
if (yield this.probeProtocolV2(uuid)) {
|
|
2681
|
+
this.iosProbeDisconnectEvidence.delete(uuid);
|
|
2397
2682
|
this.deviceProtocol.set(uuid, 'V2');
|
|
2398
2683
|
this.sessionProtocols.set(uuid, 'V2');
|
|
2399
2684
|
this.confirmedProtocolV2.add(uuid);
|
|
@@ -2404,6 +2689,19 @@ class ReactNativeBleTransport {
|
|
|
2404
2689
|
});
|
|
2405
2690
|
return 'V2';
|
|
2406
2691
|
}
|
|
2692
|
+
const staleBondError = this.confirmIosProbeDisconnect(uuid, 'follow-up-no-response');
|
|
2693
|
+
if (reactNative.Platform.OS === 'ios') {
|
|
2694
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport][BleBondDiagnostic] protocol detection ended', {
|
|
2695
|
+
outcome: 'expected-v2-no-response',
|
|
2696
|
+
expectedProtocol,
|
|
2697
|
+
protocolHint: protocolHint !== null && protocolHint !== void 0 ? protocolHint : 'none',
|
|
2698
|
+
finalErrorCode: staleBondError
|
|
2699
|
+
? hdShared.HardwareErrorCode.BleBondInvalid
|
|
2700
|
+
: hdShared.HardwareErrorCode.RuntimeError,
|
|
2701
|
+
});
|
|
2702
|
+
}
|
|
2703
|
+
if (staleBondError)
|
|
2704
|
+
throw staleBondError;
|
|
2407
2705
|
throw this.createProtocolMismatchError(expectedProtocol);
|
|
2408
2706
|
}
|
|
2409
2707
|
const sessionProtocol = this.sessionProtocols.get(uuid);
|
|
@@ -2427,6 +2725,7 @@ class ReactNativeBleTransport {
|
|
|
2427
2725
|
}
|
|
2428
2726
|
const detected = protocol === 'V1' ? yield this.probeProtocolV1(uuid) : yield this.probeProtocolV2(uuid);
|
|
2429
2727
|
if (detected) {
|
|
2728
|
+
this.iosProbeDisconnectEvidence.delete(uuid);
|
|
2430
2729
|
this.deviceProtocol.set(uuid, protocol);
|
|
2431
2730
|
this.sessionProtocols.set(uuid, protocol);
|
|
2432
2731
|
if (protocol === 'V2') {
|
|
@@ -2452,6 +2751,26 @@ class ReactNativeBleTransport {
|
|
|
2452
2751
|
}
|
|
2453
2752
|
this.deviceProtocol.delete(uuid);
|
|
2454
2753
|
this.probingProtocols.delete(uuid);
|
|
2754
|
+
const staleBondError = this.confirmIosProbeDisconnect(uuid, 'follow-up-no-response');
|
|
2755
|
+
if (reactNative.Platform.OS === 'ios') {
|
|
2756
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport][BleBondDiagnostic] protocol detection ended', {
|
|
2757
|
+
outcome: 'all-probes-no-response',
|
|
2758
|
+
expectedProtocol: expectedProtocol !== null && expectedProtocol !== void 0 ? expectedProtocol : 'none',
|
|
2759
|
+
protocolHint: protocolHint !== null && protocolHint !== void 0 ? protocolHint : 'none',
|
|
2760
|
+
sessionProtocol: sessionProtocol !== null && sessionProtocol !== void 0 ? sessionProtocol : 'none',
|
|
2761
|
+
probeOrder: probeOrder.join(','),
|
|
2762
|
+
trustSessionProtocol,
|
|
2763
|
+
reprobeFailures,
|
|
2764
|
+
transportCached: Boolean(transportCache[uuid]),
|
|
2765
|
+
notifyActive: Boolean((_b = transportCache[uuid]) === null || _b === void 0 ? void 0 : _b.notifySubscription),
|
|
2766
|
+
staleBondRecorded: this.iosProbeDisconnectEvidence.has(uuid),
|
|
2767
|
+
finalErrorCode: staleBondError
|
|
2768
|
+
? hdShared.HardwareErrorCode.BleBondInvalid
|
|
2769
|
+
: hdShared.HardwareErrorCode.BleTimeoutError,
|
|
2770
|
+
});
|
|
2771
|
+
}
|
|
2772
|
+
if (staleBondError)
|
|
2773
|
+
throw staleBondError;
|
|
2455
2774
|
throw this.createProtocolDetectionError();
|
|
2456
2775
|
});
|
|
2457
2776
|
}
|
|
@@ -2535,7 +2854,12 @@ class ReactNativeBleTransport {
|
|
|
2535
2854
|
catch (error) {
|
|
2536
2855
|
this.clearProbeProtocol(uuid, 'V1');
|
|
2537
2856
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe failed:', error);
|
|
2538
|
-
|
|
2857
|
+
const shouldRethrow = shouldRethrowProtocolProbeError(error);
|
|
2858
|
+
const staleBondError = this.rememberIosProbeDisconnect(uuid, 'protocol-v1-get-features', error);
|
|
2859
|
+
logIosBleBondDiagnostic('protocol-v1-get-features', error, shouldRethrow ? 'rethrow' : 'fallback');
|
|
2860
|
+
if (staleBondError)
|
|
2861
|
+
throw staleBondError;
|
|
2862
|
+
if (shouldRethrow) {
|
|
2539
2863
|
throw error;
|
|
2540
2864
|
}
|
|
2541
2865
|
return false;
|
|
@@ -2551,18 +2875,28 @@ class ReactNativeBleTransport {
|
|
|
2551
2875
|
this.probingProtocols.set(uuid, 'V2');
|
|
2552
2876
|
(_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
2553
2877
|
this.throwIfStaleBondError(uuid);
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2878
|
+
let detected;
|
|
2879
|
+
try {
|
|
2880
|
+
detected = yield transport.probeProtocolV2({
|
|
2881
|
+
call: (name, data, options) => this.callProtocolV2(uuid, name, data, options),
|
|
2882
|
+
timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT_MS,
|
|
2883
|
+
logger: Log,
|
|
2884
|
+
logPrefix: 'ProtocolV2 RN-BLE',
|
|
2885
|
+
onProbeFailed: () => {
|
|
2886
|
+
var _a;
|
|
2887
|
+
(_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
2888
|
+
this.resetProtocolV2Frames(uuid);
|
|
2889
|
+
},
|
|
2890
|
+
shouldRethrow: shouldRethrowProtocolProbeError,
|
|
2891
|
+
});
|
|
2892
|
+
}
|
|
2893
|
+
catch (error) {
|
|
2894
|
+
const staleBondError = this.rememberIosProbeDisconnect(uuid, 'protocol-v2-ping', error);
|
|
2895
|
+
logIosBleBondDiagnostic('protocol-v2-ping', error, 'rethrow');
|
|
2896
|
+
if (staleBondError)
|
|
2897
|
+
throw staleBondError;
|
|
2898
|
+
throw error;
|
|
2899
|
+
}
|
|
2566
2900
|
if (!detected) {
|
|
2567
2901
|
this.clearProbeProtocol(uuid, 'V2');
|
|
2568
2902
|
}
|
|
@@ -2913,10 +3247,12 @@ class ReactNativeBleTransport {
|
|
|
2913
3247
|
}
|
|
2914
3248
|
}
|
|
2915
3249
|
|
|
3250
|
+
exports.ANDROID_ENCRYPTION_RESULT_TIMEOUT_MS = ANDROID_ENCRYPTION_RESULT_TIMEOUT_MS;
|
|
2916
3251
|
exports.ANDROID_KEY_MISSING_GRACE_MS = ANDROID_KEY_MISSING_GRACE_MS;
|
|
2917
3252
|
exports.ANDROID_KEY_MISSING_LINK_WINDOW_MS = ANDROID_KEY_MISSING_LINK_WINDOW_MS;
|
|
2918
3253
|
exports.ANDROID_LINK_DROP_QUIET_MS = ANDROID_LINK_DROP_QUIET_MS;
|
|
2919
3254
|
exports.ANDROID_MTU_EXCHANGE_TIMEOUT_MS = ANDROID_MTU_EXCHANGE_TIMEOUT_MS;
|
|
3255
|
+
exports.ANDROID_SYSTEM_REPAIR_TIMEOUT_MS = ANDROID_SYSTEM_REPAIR_TIMEOUT_MS;
|
|
2920
3256
|
exports.BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD;
|
|
2921
3257
|
exports.BLE_CONNECT_TIMEOUT_MS = BLE_CONNECT_TIMEOUT_MS;
|
|
2922
3258
|
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.4-alpha.0",
|
|
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.4-alpha.0",
|
|
24
|
+
"@onekeyfe/hd-shared": "1.2.4-alpha.0",
|
|
25
|
+
"@onekeyfe/hd-transport": "1.2.4-alpha.0",
|
|
26
|
+
"@onekeyfe/react-native-ble-utils": "0.1.9",
|
|
27
27
|
"react-native-ble-plx": "3.5.1"
|
|
28
28
|
},
|
|
29
|
-
"gitHead": "
|
|
29
|
+
"gitHead": "4996c67dbb5a3ac85c2559b164d9a931274890a7"
|
|
30
30
|
}
|