@onekeyfe/hd-transport-react-native 1.2.3-alpha.8 → 1.2.3
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
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Platform } from 'react-native';
|
|
2
|
+
import BleUtils from '@onekeyfe/react-native-ble-utils';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Android 16+ keeps its side of a bond the device no longer has keys for, and reports it
|
|
6
|
+
* with ACTION_KEY_MISSING instead of a GATT status. The device then drops the link, so
|
|
7
|
+
* without this signal a wiped device is indistinguishable from any other disconnect.
|
|
8
|
+
*
|
|
9
|
+
* The signal only describes the link that was being encrypted when it fired. Callers must
|
|
10
|
+
* bound it to their own connection attempt with `sinceMs`; reusing an older one would turn
|
|
11
|
+
* an ordinary disconnect (a firmware-update reboot, for example) into a terminal bond error.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// An app can still resolve a react-native-ble-utils older than 0.1.8, and the JS bundle can
|
|
15
|
+
// run on a native build that predates the event, so neither method is assumed to exist.
|
|
16
|
+
type KeyMissingCapableBleUtils = Partial<
|
|
17
|
+
Pick<typeof BleUtils, 'supportsDeviceKeyMissing' | 'onDeviceKeyMissing'>
|
|
18
|
+
>;
|
|
19
|
+
|
|
20
|
+
type KeyMissingListener = (deviceId: string) => void;
|
|
21
|
+
|
|
22
|
+
const lastKeyMissingAt = new Map<string, number>();
|
|
23
|
+
const listeners = new Set<KeyMissingListener>();
|
|
24
|
+
let unsubscribeNative: (() => void) | undefined;
|
|
25
|
+
|
|
26
|
+
const normalizeDeviceId = (deviceId: string) => deviceId.toLowerCase();
|
|
27
|
+
|
|
28
|
+
const getCapableBleUtils = (): KeyMissingCapableBleUtils | undefined => {
|
|
29
|
+
if (Platform.OS !== 'android') return undefined;
|
|
30
|
+
const bleUtils: KeyMissingCapableBleUtils = BleUtils;
|
|
31
|
+
if (
|
|
32
|
+
typeof bleUtils.supportsDeviceKeyMissing !== 'function' ||
|
|
33
|
+
typeof bleUtils.onDeviceKeyMissing !== 'function'
|
|
34
|
+
) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
return bleUtils.supportsDeviceKeyMissing() ? bleUtils : undefined;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/** False means the event can never fire here, so nothing should wait for it. */
|
|
41
|
+
export const isBleKeyMissingSupported = () => getCapableBleUtils() !== undefined;
|
|
42
|
+
|
|
43
|
+
/** Idempotent. Returns whether the signal is available on this OS and native build. */
|
|
44
|
+
export const startBleKeyMissingTracking = (): boolean => {
|
|
45
|
+
if (unsubscribeNative) return true;
|
|
46
|
+
const bleUtils = getCapableBleUtils();
|
|
47
|
+
if (!bleUtils?.onDeviceKeyMissing) return false;
|
|
48
|
+
|
|
49
|
+
unsubscribeNative = bleUtils.onDeviceKeyMissing(event => {
|
|
50
|
+
if (typeof event?.id !== 'string') return;
|
|
51
|
+
const deviceId = normalizeDeviceId(event.id);
|
|
52
|
+
lastKeyMissingAt.set(deviceId, Date.now());
|
|
53
|
+
listeners.forEach(listener => listener(deviceId));
|
|
54
|
+
});
|
|
55
|
+
return true;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const stopBleKeyMissingTracking = () => {
|
|
59
|
+
unsubscribeNative?.();
|
|
60
|
+
unsubscribeNative = undefined;
|
|
61
|
+
lastKeyMissingAt.clear();
|
|
62
|
+
listeners.clear();
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const hasBleKeyMissingSince = (deviceId: string, sinceMs: number): boolean => {
|
|
66
|
+
const at = lastKeyMissingAt.get(normalizeDeviceId(deviceId));
|
|
67
|
+
return at !== undefined && at >= sinceMs;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/** Calls back for every later signal on this device until the returned cleanup runs. */
|
|
71
|
+
export const onBleKeyMissing = (deviceId: string, callback: () => void): (() => void) => {
|
|
72
|
+
const target = normalizeDeviceId(deviceId);
|
|
73
|
+
const listener: KeyMissingListener = id => {
|
|
74
|
+
if (id === target) callback();
|
|
75
|
+
};
|
|
76
|
+
listeners.add(listener);
|
|
77
|
+
return () => {
|
|
78
|
+
listeners.delete(listener);
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Resolves true once a signal at or after `sinceMs` exists for the device, false when
|
|
84
|
+
* none arrives within `timeoutMs`. The broadcast and the GATT disconnect it explains are
|
|
85
|
+
* delivered on separate paths, so the disconnect can reach JS first.
|
|
86
|
+
*/
|
|
87
|
+
export const waitForBleKeyMissing = (
|
|
88
|
+
deviceId: string,
|
|
89
|
+
sinceMs: number,
|
|
90
|
+
timeoutMs: number
|
|
91
|
+
): Promise<boolean> => {
|
|
92
|
+
if (!startBleKeyMissingTracking()) return Promise.resolve(false);
|
|
93
|
+
if (hasBleKeyMissingSince(deviceId, sinceMs)) return Promise.resolve(true);
|
|
94
|
+
|
|
95
|
+
return new Promise(resolve => {
|
|
96
|
+
const settle = (found: boolean) => {
|
|
97
|
+
clearTimeout(timer);
|
|
98
|
+
cleanup();
|
|
99
|
+
resolve(found);
|
|
100
|
+
};
|
|
101
|
+
const cleanup = onBleKeyMissing(deviceId, () => {
|
|
102
|
+
if (hasBleKeyMissingSince(deviceId, sinceMs)) settle(true);
|
|
103
|
+
});
|
|
104
|
+
const timer = setTimeout(() => settle(false), timeoutMs);
|
|
105
|
+
});
|
|
106
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -47,6 +47,18 @@ import {
|
|
|
47
47
|
getInfosForServiceUuid,
|
|
48
48
|
isSameBleUuid,
|
|
49
49
|
} from './constants';
|
|
50
|
+
import {
|
|
51
|
+
isBleKeyMissingSupported,
|
|
52
|
+
startBleKeyMissingTracking,
|
|
53
|
+
stopBleKeyMissingTracking,
|
|
54
|
+
waitForBleKeyMissing,
|
|
55
|
+
} from './bleKeyMissing';
|
|
56
|
+
import {
|
|
57
|
+
markBleLinkEncrypted,
|
|
58
|
+
startBleEncryptionTracking,
|
|
59
|
+
stopBleEncryptionTracking,
|
|
60
|
+
waitForAndroidLinkEncryption,
|
|
61
|
+
} from './bleEncryption';
|
|
50
62
|
import { isNativeBleDisconnectError, toBleDisconnectHardwareError } from './bleNativeDisconnect';
|
|
51
63
|
import {
|
|
52
64
|
isBleStaleBondHardwareError,
|
|
@@ -62,6 +74,7 @@ import type { Deferred } from '@onekeyfe/hd-shared';
|
|
|
62
74
|
import type { Characteristic, Device, Subscription } from 'react-native-ble-plx';
|
|
63
75
|
import type EventEmitter from 'events';
|
|
64
76
|
import type { BleAcquireInput, TransportOptions } from './types';
|
|
77
|
+
import type { AndroidLinkEncryption } from './bleEncryption';
|
|
65
78
|
|
|
66
79
|
type FirmwareInstallBleAcquireInput = BleAcquireInput & {
|
|
67
80
|
/**
|
|
@@ -188,6 +201,38 @@ const shouldRethrowProtocolProbeError = (error: unknown): boolean => {
|
|
|
188
201
|
code === HardwareErrorCode.BleWriteCharacteristicError
|
|
189
202
|
);
|
|
190
203
|
};
|
|
204
|
+
/**
|
|
205
|
+
* Android reports a lost bond (ACTION_KEY_MISSING) while it encrypts a new link, so only
|
|
206
|
+
* a failure this soon after the link started can be explained by it. Later disconnects,
|
|
207
|
+
* such as a firmware-update reboot, must keep their own meaning.
|
|
208
|
+
*/
|
|
209
|
+
export const ANDROID_KEY_MISSING_LINK_WINDOW_MS = 10_000;
|
|
210
|
+
/** The broadcast and the GATT disconnect it explains travel separately; either can land first. */
|
|
211
|
+
export const ANDROID_KEY_MISSING_GRACE_MS = 500;
|
|
212
|
+
/**
|
|
213
|
+
* Android reports a bonded link's first encryption result within about half a second of
|
|
214
|
+
* connecting. Past this, the result is treated as unknown and the link is used as before.
|
|
215
|
+
*/
|
|
216
|
+
export const ANDROID_ENCRYPTION_RESULT_TIMEOUT_MS = 1500;
|
|
217
|
+
/**
|
|
218
|
+
* A system re-pairing needs the user to accept a pairing request and confirm the code on the
|
|
219
|
+
* device. The Bluetooth stack gives up after 30s and then reports key missing.
|
|
220
|
+
*/
|
|
221
|
+
export const ANDROID_SYSTEM_REPAIR_TIMEOUT_MS = 35_000;
|
|
222
|
+
/**
|
|
223
|
+
* How a link dropped by a device that refuses a stale bond reaches JS. A wedged write is
|
|
224
|
+
* excluded: the system is still re-pairing then and has not reported key missing yet.
|
|
225
|
+
*/
|
|
226
|
+
const isAndroidLinkLossError = (error: unknown): boolean => {
|
|
227
|
+
const code = (error as { errorCode?: unknown })?.errorCode;
|
|
228
|
+
return (
|
|
229
|
+
isNativeBleDisconnectError(error) ||
|
|
230
|
+
code === HardwareErrorCode.BleDeviceNotBonded ||
|
|
231
|
+
code === HardwareErrorCode.BleDeviceDisconnected ||
|
|
232
|
+
code === HardwareErrorCode.BleCharacteristicNotifyError ||
|
|
233
|
+
code === HardwareErrorCode.BleConnectedError
|
|
234
|
+
);
|
|
235
|
+
};
|
|
191
236
|
/** Consecutive wedged writes on one device before the BLE manager itself is recreated. */
|
|
192
237
|
export const BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
|
|
193
238
|
const DEVICE_SCAN_TIMEOUT_MS = 3000;
|
|
@@ -540,6 +585,9 @@ export default class ReactNativeBleTransport {
|
|
|
540
585
|
*/
|
|
541
586
|
private staleBondErrors: Map<string, Error> = new Map();
|
|
542
587
|
|
|
588
|
+
/** When the current Android link attempt began; bounds which key-missing signals apply. */
|
|
589
|
+
private androidLinkStartedAt: Map<string, number> = new Map();
|
|
590
|
+
|
|
543
591
|
/** Strict or previously confirmed V2 target while acquire installs notifications. */
|
|
544
592
|
private acquiringProtocolV2 = new Set<string>();
|
|
545
593
|
|
|
@@ -604,6 +652,11 @@ export default class ReactNativeBleTransport {
|
|
|
604
652
|
init(logger: any, emitter: EventEmitter) {
|
|
605
653
|
setBleLogger(logger);
|
|
606
654
|
this.emitter = emitter;
|
|
655
|
+
if (Platform.OS === 'android') {
|
|
656
|
+
// Link security events are only meaningful if they were observed since the link came up.
|
|
657
|
+
startBleKeyMissingTracking();
|
|
658
|
+
startBleEncryptionTracking();
|
|
659
|
+
}
|
|
607
660
|
}
|
|
608
661
|
|
|
609
662
|
configure(signedData: any) {
|
|
@@ -1055,7 +1108,13 @@ export default class ReactNativeBleTransport {
|
|
|
1055
1108
|
throw ERRORS.TypedError(HardwareErrorCode.BleRequiredUUID);
|
|
1056
1109
|
}
|
|
1057
1110
|
|
|
1058
|
-
return this.runLifecycleOperation(uuid, () =>
|
|
1111
|
+
return this.runLifecycleOperation(uuid, async () => {
|
|
1112
|
+
try {
|
|
1113
|
+
return await this.acquireUnlocked(input);
|
|
1114
|
+
} catch (error) {
|
|
1115
|
+
throw await this.resolveAndroidBondInvalid(uuid, error);
|
|
1116
|
+
}
|
|
1117
|
+
});
|
|
1059
1118
|
}
|
|
1060
1119
|
|
|
1061
1120
|
private async acquireUnlocked(input: FirmwareInstallBleAcquireInput) {
|
|
@@ -1135,6 +1194,8 @@ export default class ReactNativeBleTransport {
|
|
|
1135
1194
|
|
|
1136
1195
|
let device: Device | null = null;
|
|
1137
1196
|
const isAndroid = Platform.OS === 'android';
|
|
1197
|
+
// Only a bond that existed before this acquire can have been lost by the device.
|
|
1198
|
+
let androidBondedBeforeConnect = false;
|
|
1138
1199
|
// A firmware-install reconnect always refreshes: the new firmware may expose a different table.
|
|
1139
1200
|
const refreshAndroidGattCache =
|
|
1140
1201
|
isAndroid && (!!skipProtocolProbe || this.androidGattCacheRefreshes.has(uuid));
|
|
@@ -1166,11 +1227,19 @@ export default class ReactNativeBleTransport {
|
|
|
1166
1227
|
|
|
1167
1228
|
if (this.stopped) throw ERRORS.TypedError(HardwareErrorCode.BleDeviceDisconnected);
|
|
1168
1229
|
if (Platform.OS === 'android') {
|
|
1230
|
+
// Subscribe before the link exists: key missing is broadcast while Android encrypts it.
|
|
1231
|
+
startBleKeyMissingTracking();
|
|
1232
|
+
startBleEncryptionTracking();
|
|
1233
|
+
// Failures before the new link starts must not be read against the previous link.
|
|
1234
|
+
this.androidLinkStartedAt.delete(uuid);
|
|
1169
1235
|
// Initiate bonding locally before GATT can trigger peripheral-initiated pairing.
|
|
1170
1236
|
try {
|
|
1171
1237
|
const bondState = await pairDevice(uuid);
|
|
1238
|
+
androidBondedBeforeConnect = !bondState.bonding && bondState.bonded;
|
|
1172
1239
|
if (bondState.bonding) {
|
|
1173
|
-
await onDeviceBondState(uuid, this.bondAbortController.signal
|
|
1240
|
+
await onDeviceBondState(uuid, this.bondAbortController.signal, {
|
|
1241
|
+
systemInitiated: bondState.initiated === false,
|
|
1242
|
+
});
|
|
1174
1243
|
} else if (!bondState.bonded) {
|
|
1175
1244
|
throw ERRORS.TypedError(HardwareErrorCode.BleDeviceNotBonded, 'device is not bonded');
|
|
1176
1245
|
}
|
|
@@ -1182,6 +1251,7 @@ export default class ReactNativeBleTransport {
|
|
|
1182
1251
|
});
|
|
1183
1252
|
throw error;
|
|
1184
1253
|
}
|
|
1254
|
+
this.androidLinkStartedAt.set(uuid, Date.now());
|
|
1185
1255
|
}
|
|
1186
1256
|
if (this.stopped) throw ERRORS.TypedError(HardwareErrorCode.BleDeviceDisconnected);
|
|
1187
1257
|
|
|
@@ -1369,7 +1439,13 @@ export default class ReactNativeBleTransport {
|
|
|
1369
1439
|
this.acquiringProtocolV2.add(uuid);
|
|
1370
1440
|
}
|
|
1371
1441
|
|
|
1442
|
+
let linkEncryption: AndroidLinkEncryption | undefined;
|
|
1372
1443
|
try {
|
|
1444
|
+
// A firmware-install reconnect keeps its existing sequence.
|
|
1445
|
+
if (androidBondedBeforeConnect && !skipProtocolProbe) {
|
|
1446
|
+
linkEncryption = await this.waitForAndroidLinkSecurity(uuid);
|
|
1447
|
+
if (this.stopped) throw ERRORS.TypedError(HardwareErrorCode.BleDeviceDisconnected);
|
|
1448
|
+
}
|
|
1373
1449
|
await this.installTransportForAcquire(uuid, acquiredDevice, {
|
|
1374
1450
|
writeCharacteristic,
|
|
1375
1451
|
notifyCharacteristic,
|
|
@@ -1421,6 +1497,11 @@ export default class ReactNativeBleTransport {
|
|
|
1421
1497
|
throw ERRORS.TypedError(HardwareErrorCode.TransportNotFound);
|
|
1422
1498
|
}
|
|
1423
1499
|
this.attachDisconnectSubscription(currentTransport, currentTransport.device, uuid);
|
|
1500
|
+
if (linkEncryption === 'unresolved') {
|
|
1501
|
+
// Notifications and the probe only work on an encrypted link, so the next reuse of this
|
|
1502
|
+
// link needs no result that was reported before tracking started.
|
|
1503
|
+
markBleLinkEncrypted(uuid);
|
|
1504
|
+
}
|
|
1424
1505
|
return { uuid, protocolType };
|
|
1425
1506
|
} catch (error) {
|
|
1426
1507
|
// A failed acquire must retire the physical link before Core retries. Logical
|
|
@@ -1689,16 +1770,77 @@ export default class ReactNativeBleTransport {
|
|
|
1689
1770
|
`Device protocol has not been detected for ${uuid}`
|
|
1690
1771
|
);
|
|
1691
1772
|
}
|
|
1692
|
-
if (protocol === 'V2') {
|
|
1693
|
-
return this.callProtocolV2(uuid, name, data, options);
|
|
1694
|
-
}
|
|
1695
|
-
|
|
1696
1773
|
const forceRun = name === 'Initialize' || name === 'Cancel';
|
|
1697
|
-
if (this.runPromise && !forceRun) {
|
|
1774
|
+
if (protocol !== 'V2' && this.runPromise && !forceRun) {
|
|
1698
1775
|
throw ERRORS.TypedError(HardwareErrorCode.TransportCallInProgress);
|
|
1699
1776
|
}
|
|
1700
1777
|
|
|
1701
|
-
|
|
1778
|
+
try {
|
|
1779
|
+
return protocol === 'V2'
|
|
1780
|
+
? await this.callProtocolV2(uuid, name, data, options)
|
|
1781
|
+
: await this.callProtocolV1(uuid, name, data, options);
|
|
1782
|
+
} catch (error) {
|
|
1783
|
+
// An expected-V1 acquire skips the probe, so the first call is what meets the dropped link.
|
|
1784
|
+
throw await this.resolveAndroidBondInvalid(uuid, error);
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
/**
|
|
1789
|
+
* Android 16+ keeps a bond the device no longer holds keys for. The device drops the link
|
|
1790
|
+
* and the only structured evidence is ACTION_KEY_MISSING, so a link loss right after
|
|
1791
|
+
* connect is re-read as an invalid bond when that signal belongs to the same attempt.
|
|
1792
|
+
*/
|
|
1793
|
+
private async resolveAndroidBondInvalid(uuid: string, error: unknown): Promise<unknown> {
|
|
1794
|
+
if (Platform.OS !== 'android' || !isBleKeyMissingSupported()) return error;
|
|
1795
|
+
if (isBleStaleBondHardwareError(error) || !isAndroidLinkLossError(error)) return error;
|
|
1796
|
+
const linkStartedAt = this.androidLinkStartedAt.get(uuid);
|
|
1797
|
+
if (
|
|
1798
|
+
linkStartedAt === undefined ||
|
|
1799
|
+
Date.now() - linkStartedAt > ANDROID_KEY_MISSING_LINK_WINDOW_MS
|
|
1800
|
+
) {
|
|
1801
|
+
return error;
|
|
1802
|
+
}
|
|
1803
|
+
if (!(await waitForBleKeyMissing(uuid, linkStartedAt, ANDROID_KEY_MISSING_GRACE_MS))) {
|
|
1804
|
+
return error;
|
|
1805
|
+
}
|
|
1806
|
+
Log?.debug('[ReactNativeBleTransport] Android key missing, bond is invalid:', uuid);
|
|
1807
|
+
return ERRORS.TypedError(HardwareErrorCode.BleBondInvalid, undefined, {
|
|
1808
|
+
phase: 'connect',
|
|
1809
|
+
reason: 'key_missing',
|
|
1810
|
+
});
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
/**
|
|
1814
|
+
* Android 16+ encrypts a bonded link on its own right after connecting. Subscribing to
|
|
1815
|
+
* notifications before that finishes gets the CCCD write rejected, and the framework's retry
|
|
1816
|
+
* encrypts again with the same stale key; firmware that allows one failure per link then drops
|
|
1817
|
+
* it before the system can re-pair. Holding GATT until the result lets a lost bond be re-paired
|
|
1818
|
+
* in place. Without the platform signal this resolves immediately.
|
|
1819
|
+
*/
|
|
1820
|
+
private async waitForAndroidLinkSecurity(uuid: string): Promise<AndroidLinkEncryption> {
|
|
1821
|
+
const linkStartedAt = this.androidLinkStartedAt.get(uuid);
|
|
1822
|
+
if (linkStartedAt === undefined) return 'unresolved';
|
|
1823
|
+
const waitStartedAt = Date.now();
|
|
1824
|
+
const result = await waitForAndroidLinkEncryption({
|
|
1825
|
+
deviceId: uuid,
|
|
1826
|
+
linkStartedAt,
|
|
1827
|
+
resultTimeoutMs: ANDROID_ENCRYPTION_RESULT_TIMEOUT_MS,
|
|
1828
|
+
repairTimeoutMs: ANDROID_SYSTEM_REPAIR_TIMEOUT_MS,
|
|
1829
|
+
keyMissingGraceMs: ANDROID_KEY_MISSING_GRACE_MS,
|
|
1830
|
+
signal: this.bondAbortController.signal,
|
|
1831
|
+
onRepairStarted: () => {
|
|
1832
|
+
Log?.debug(
|
|
1833
|
+
'[ReactNativeBleTransport] Android bond lost, waiting for system re-pairing:',
|
|
1834
|
+
uuid
|
|
1835
|
+
);
|
|
1836
|
+
},
|
|
1837
|
+
});
|
|
1838
|
+
Log?.debug('[ReactNativeBleTransport] Android link encryption', {
|
|
1839
|
+
connectIdSuffix: uuid.slice(-8),
|
|
1840
|
+
result,
|
|
1841
|
+
waitedMs: Date.now() - waitStartedAt,
|
|
1842
|
+
});
|
|
1843
|
+
return result;
|
|
1702
1844
|
}
|
|
1703
1845
|
|
|
1704
1846
|
private async callProtocolV1(
|
|
@@ -1966,6 +2108,9 @@ export default class ReactNativeBleTransport {
|
|
|
1966
2108
|
this.androidPriorityResetTimers.forEach(timeout => clearTimeout(timeout));
|
|
1967
2109
|
this.androidPriorityResetTimers.clear();
|
|
1968
2110
|
this.androidHighPriorityDevices.clear();
|
|
2111
|
+
this.androidLinkStartedAt.clear();
|
|
2112
|
+
stopBleKeyMissingTracking();
|
|
2113
|
+
stopBleEncryptionTracking();
|
|
1969
2114
|
const error = ERRORS.TypedError(HardwareErrorCode.BleDeviceDisconnected);
|
|
1970
2115
|
this.runPromise?.reject(error);
|
|
1971
2116
|
this.runPromise = null;
|