@onekeyfe/hd-transport-react-native 1.2.3-alpha.9 → 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/bleEncryption.d.ts +17 -0
- package/dist/bleEncryption.d.ts.map +1 -0
- package/dist/index.d.ts +19 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +229 -17
- package/package.json +6 -6
- package/src/__tests__/androidEncryption.test.ts +482 -0
- package/src/__tests__/enumerate.test.ts +8 -1
- package/src/__tests__/protocolV2Link.test.ts +8 -1
- package/src/bleEncryption.ts +267 -0
- package/src/index.ts +71 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { Platform } from 'react-native';
|
|
2
|
+
import BleUtils from '@onekeyfe/react-native-ble-utils';
|
|
3
|
+
import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
hasBleKeyMissingSince,
|
|
7
|
+
onBleKeyMissing,
|
|
8
|
+
startBleKeyMissingTracking,
|
|
9
|
+
} from './bleKeyMissing';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Android 16+ starts encrypting a bonded LE link on its own right after connecting, with the
|
|
13
|
+
* stored key. A GATT request that needs encryption and is sent before that attempt finishes is
|
|
14
|
+
* rejected by the peer, and the framework retries it by encrypting again with the same key. When
|
|
15
|
+
* the peer has lost the bond, that second failure makes firmware that tolerates a single one per
|
|
16
|
+
* link drop the connection, before the system's own re-pairing can finish.
|
|
17
|
+
*
|
|
18
|
+
* Tracking the link's encryption result lets the transport hold GATT until then, so a lost bond
|
|
19
|
+
* is re-paired in place (the system shows a pairing request) instead of failing.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/** HCI "PIN or Key Missing": the peer no longer has the keys of the stored bond. */
|
|
23
|
+
export const HCI_PIN_OR_KEY_MISSING = 0x06;
|
|
24
|
+
|
|
25
|
+
export type AndroidLinkEncryption =
|
|
26
|
+
/** This link was encrypted with the stored bond. */
|
|
27
|
+
| 'encrypted'
|
|
28
|
+
/** The link was reused and had already been encrypted. */
|
|
29
|
+
| 'already-encrypted'
|
|
30
|
+
/** The stored bond was lost and the system re-paired on this link. */
|
|
31
|
+
| 're-paired'
|
|
32
|
+
/** No result arrived in time; callers proceed as if nothing was known. */
|
|
33
|
+
| 'unresolved';
|
|
34
|
+
|
|
35
|
+
type EncryptionRecord = { at: number; status: number; enabled: boolean };
|
|
36
|
+
|
|
37
|
+
// An app can still resolve an older react-native-ble-utils, and the JS bundle can run on a native
|
|
38
|
+
// build that predates the events, so none of these members is assumed to exist.
|
|
39
|
+
type EncryptionCapableBleUtils = Partial<
|
|
40
|
+
Pick<
|
|
41
|
+
typeof BleUtils,
|
|
42
|
+
'supportsDeviceEncryptionChange' | 'onDeviceEncryptionChange' | 'onDeviceAclDisconnected'
|
|
43
|
+
>
|
|
44
|
+
>;
|
|
45
|
+
|
|
46
|
+
type LinkListener = (deviceId: string) => void;
|
|
47
|
+
|
|
48
|
+
const lastEncryption = new Map<string, EncryptionRecord>();
|
|
49
|
+
const lastAclDisconnectAt = new Map<string, number>();
|
|
50
|
+
const listeners = new Set<LinkListener>();
|
|
51
|
+
let unsubscribeNative: Array<() => void> | undefined;
|
|
52
|
+
|
|
53
|
+
const normalizeDeviceId = (deviceId: string) => deviceId.toLowerCase();
|
|
54
|
+
|
|
55
|
+
const isEncrypted = (record: EncryptionRecord) => record.status === 0 && record.enabled;
|
|
56
|
+
|
|
57
|
+
const getCapableBleUtils = (): EncryptionCapableBleUtils | undefined => {
|
|
58
|
+
if (Platform.OS !== 'android') return undefined;
|
|
59
|
+
const bleUtils: EncryptionCapableBleUtils = BleUtils;
|
|
60
|
+
if (
|
|
61
|
+
typeof bleUtils.supportsDeviceEncryptionChange !== 'function' ||
|
|
62
|
+
typeof bleUtils.onDeviceEncryptionChange !== 'function' ||
|
|
63
|
+
typeof bleUtils.onDeviceAclDisconnected !== 'function'
|
|
64
|
+
) {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
return bleUtils.supportsDeviceEncryptionChange() ? bleUtils : undefined;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const notify = (deviceId: string) => {
|
|
71
|
+
listeners.forEach(listener => listener(deviceId));
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/** False means the encryption result can never be observed here, so nothing should wait for it. */
|
|
75
|
+
export const isBleEncryptionTrackingSupported = () => getCapableBleUtils() !== undefined;
|
|
76
|
+
|
|
77
|
+
/** Idempotent. Returns whether encryption results are observable on this OS and native build. */
|
|
78
|
+
export const startBleEncryptionTracking = (): boolean => {
|
|
79
|
+
if (unsubscribeNative) return true;
|
|
80
|
+
const bleUtils = getCapableBleUtils();
|
|
81
|
+
if (!bleUtils?.onDeviceEncryptionChange || !bleUtils.onDeviceAclDisconnected) return false;
|
|
82
|
+
|
|
83
|
+
unsubscribeNative = [
|
|
84
|
+
bleUtils.onDeviceEncryptionChange(event => {
|
|
85
|
+
if (typeof event?.id !== 'string') return;
|
|
86
|
+
const deviceId = normalizeDeviceId(event.id);
|
|
87
|
+
lastEncryption.set(deviceId, {
|
|
88
|
+
at: Date.now(),
|
|
89
|
+
status: Number(event.status),
|
|
90
|
+
enabled: event.enabled === true,
|
|
91
|
+
});
|
|
92
|
+
notify(deviceId);
|
|
93
|
+
}),
|
|
94
|
+
bleUtils.onDeviceAclDisconnected(event => {
|
|
95
|
+
if (typeof event?.id !== 'string') return;
|
|
96
|
+
const deviceId = normalizeDeviceId(event.id);
|
|
97
|
+
lastAclDisconnectAt.set(deviceId, Date.now());
|
|
98
|
+
notify(deviceId);
|
|
99
|
+
}),
|
|
100
|
+
];
|
|
101
|
+
return true;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export const stopBleEncryptionTracking = () => {
|
|
105
|
+
unsubscribeNative?.forEach(unsubscribe => unsubscribe());
|
|
106
|
+
unsubscribeNative = undefined;
|
|
107
|
+
lastEncryption.clear();
|
|
108
|
+
lastAclDisconnectAt.clear();
|
|
109
|
+
listeners.clear();
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Records a link that carried requests needing encryption, so reusing it later does not wait for
|
|
114
|
+
* an encryption result that was reported before tracking started.
|
|
115
|
+
*/
|
|
116
|
+
export const markBleLinkEncrypted = (deviceId: string) => {
|
|
117
|
+
if (!unsubscribeNative) return;
|
|
118
|
+
lastEncryption.set(normalizeDeviceId(deviceId), { at: Date.now(), status: 0, enabled: true });
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export type WaitForAndroidLinkEncryptionOptions = {
|
|
122
|
+
deviceId: string;
|
|
123
|
+
/** When the current link attempt began; earlier results describe another link. */
|
|
124
|
+
linkStartedAt: number;
|
|
125
|
+
/** How long to wait for the first encryption result of this link. */
|
|
126
|
+
resultTimeoutMs: number;
|
|
127
|
+
/** How long a system re-pairing may take once the stored bond was reported lost. */
|
|
128
|
+
repairTimeoutMs: number;
|
|
129
|
+
/** After the link drops during re-pairing, how long to wait for the key-missing report. */
|
|
130
|
+
keyMissingGraceMs: number;
|
|
131
|
+
signal?: AbortSignal;
|
|
132
|
+
onRepairStarted?: () => void;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Resolves once the link can carry requests that need encryption. Rejects with `BleBondInvalid`
|
|
137
|
+
* when the system reports that re-pairing failed, with `BleDeviceDisconnected` when the link drops
|
|
138
|
+
* during re-pairing, and with `BleDeviceNotBonded` when re-pairing does not finish in time.
|
|
139
|
+
*/
|
|
140
|
+
export const waitForAndroidLinkEncryption = ({
|
|
141
|
+
deviceId,
|
|
142
|
+
linkStartedAt,
|
|
143
|
+
resultTimeoutMs,
|
|
144
|
+
repairTimeoutMs,
|
|
145
|
+
keyMissingGraceMs,
|
|
146
|
+
signal,
|
|
147
|
+
onRepairStarted,
|
|
148
|
+
}: WaitForAndroidLinkEncryptionOptions): Promise<AndroidLinkEncryption> => {
|
|
149
|
+
if (!startBleEncryptionTracking()) return Promise.resolve('unresolved');
|
|
150
|
+
startBleKeyMissingTracking();
|
|
151
|
+
const target = normalizeDeviceId(deviceId);
|
|
152
|
+
|
|
153
|
+
const previous = lastEncryption.get(target);
|
|
154
|
+
const previousDropAt = lastAclDisconnectAt.get(target);
|
|
155
|
+
if (
|
|
156
|
+
previous &&
|
|
157
|
+
previous.at < linkStartedAt &&
|
|
158
|
+
isEncrypted(previous) &&
|
|
159
|
+
(previousDropAt === undefined || previousDropAt < previous.at)
|
|
160
|
+
) {
|
|
161
|
+
return Promise.resolve('already-encrypted');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return new Promise((resolve, reject) => {
|
|
165
|
+
if (signal?.aborted) {
|
|
166
|
+
reject(ERRORS.TypedError(HardwareErrorCode.BleDeviceDisconnected));
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Set once this link's first encryption failed because the peer lost the bond. */
|
|
171
|
+
let repairStartedAt: number | undefined;
|
|
172
|
+
let droppedDuringRepair = false;
|
|
173
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
174
|
+
let settled = false;
|
|
175
|
+
|
|
176
|
+
const cleanup = () => {
|
|
177
|
+
settled = true;
|
|
178
|
+
if (timer) clearTimeout(timer);
|
|
179
|
+
listeners.delete(onLinkEvent);
|
|
180
|
+
cleanupKeyMissing();
|
|
181
|
+
signal?.removeEventListener('abort', onAbort);
|
|
182
|
+
};
|
|
183
|
+
const finish = (result: AndroidLinkEncryption) => {
|
|
184
|
+
cleanup();
|
|
185
|
+
resolve(result);
|
|
186
|
+
};
|
|
187
|
+
const fail = (error: Error) => {
|
|
188
|
+
cleanup();
|
|
189
|
+
reject(error);
|
|
190
|
+
};
|
|
191
|
+
const arm = (ms: number, onExpire: () => void) => {
|
|
192
|
+
if (timer) clearTimeout(timer);
|
|
193
|
+
timer = setTimeout(onExpire, ms);
|
|
194
|
+
};
|
|
195
|
+
const bondInvalid = () =>
|
|
196
|
+
ERRORS.TypedError(HardwareErrorCode.BleBondInvalid, undefined, {
|
|
197
|
+
phase: 'connect',
|
|
198
|
+
reason: 'key_missing',
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const evaluate = () => {
|
|
202
|
+
if (settled) return;
|
|
203
|
+
const record = lastEncryption.get(target);
|
|
204
|
+
const dropAt = lastAclDisconnectAt.get(target);
|
|
205
|
+
|
|
206
|
+
if (repairStartedAt === undefined) {
|
|
207
|
+
if (hasBleKeyMissingSince(target, linkStartedAt)) {
|
|
208
|
+
fail(bondInvalid());
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (dropAt !== undefined && dropAt >= linkStartedAt) {
|
|
212
|
+
// The link is gone; the caller's next request reports it with its usual error.
|
|
213
|
+
finish('unresolved');
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (!record || record.at < linkStartedAt) return;
|
|
217
|
+
if (isEncrypted(record)) {
|
|
218
|
+
finish('encrypted');
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (record.status !== HCI_PIN_OR_KEY_MISSING) {
|
|
222
|
+
finish('unresolved');
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
// The system re-pairs on its own after a lost bond. Nothing may touch GATT until then.
|
|
226
|
+
repairStartedAt = record.at;
|
|
227
|
+
onRepairStarted?.();
|
|
228
|
+
arm(repairTimeoutMs, () =>
|
|
229
|
+
fail(
|
|
230
|
+
ERRORS.TypedError(HardwareErrorCode.BleDeviceNotBonded, 'Bluetooth pairing timed out', {
|
|
231
|
+
phase: 'bond',
|
|
232
|
+
reason: 'timeout',
|
|
233
|
+
})
|
|
234
|
+
)
|
|
235
|
+
);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (hasBleKeyMissingSince(target, repairStartedAt)) {
|
|
240
|
+
fail(bondInvalid());
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
if (record && record.at > repairStartedAt && isEncrypted(record)) {
|
|
244
|
+
finish('re-paired');
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
if (!droppedDuringRepair && dropAt !== undefined && dropAt >= repairStartedAt) {
|
|
248
|
+
droppedDuringRepair = true;
|
|
249
|
+
// A failed re-pairing drops the link and reports key missing right after.
|
|
250
|
+
arm(keyMissingGraceMs, () =>
|
|
251
|
+
fail(ERRORS.TypedError(HardwareErrorCode.BleDeviceDisconnected))
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const onLinkEvent: LinkListener = id => {
|
|
257
|
+
if (id === target) evaluate();
|
|
258
|
+
};
|
|
259
|
+
const onAbort = () => fail(ERRORS.TypedError(HardwareErrorCode.BleDeviceDisconnected));
|
|
260
|
+
|
|
261
|
+
listeners.add(onLinkEvent);
|
|
262
|
+
const cleanupKeyMissing = onBleKeyMissing(target, evaluate);
|
|
263
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
264
|
+
arm(resultTimeoutMs, () => finish('unresolved'));
|
|
265
|
+
evaluate();
|
|
266
|
+
});
|
|
267
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -53,6 +53,12 @@ import {
|
|
|
53
53
|
stopBleKeyMissingTracking,
|
|
54
54
|
waitForBleKeyMissing,
|
|
55
55
|
} from './bleKeyMissing';
|
|
56
|
+
import {
|
|
57
|
+
markBleLinkEncrypted,
|
|
58
|
+
startBleEncryptionTracking,
|
|
59
|
+
stopBleEncryptionTracking,
|
|
60
|
+
waitForAndroidLinkEncryption,
|
|
61
|
+
} from './bleEncryption';
|
|
56
62
|
import { isNativeBleDisconnectError, toBleDisconnectHardwareError } from './bleNativeDisconnect';
|
|
57
63
|
import {
|
|
58
64
|
isBleStaleBondHardwareError,
|
|
@@ -68,6 +74,7 @@ import type { Deferred } from '@onekeyfe/hd-shared';
|
|
|
68
74
|
import type { Characteristic, Device, Subscription } from 'react-native-ble-plx';
|
|
69
75
|
import type EventEmitter from 'events';
|
|
70
76
|
import type { BleAcquireInput, TransportOptions } from './types';
|
|
77
|
+
import type { AndroidLinkEncryption } from './bleEncryption';
|
|
71
78
|
|
|
72
79
|
type FirmwareInstallBleAcquireInput = BleAcquireInput & {
|
|
73
80
|
/**
|
|
@@ -202,6 +209,16 @@ const shouldRethrowProtocolProbeError = (error: unknown): boolean => {
|
|
|
202
209
|
export const ANDROID_KEY_MISSING_LINK_WINDOW_MS = 10_000;
|
|
203
210
|
/** The broadcast and the GATT disconnect it explains travel separately; either can land first. */
|
|
204
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;
|
|
205
222
|
/**
|
|
206
223
|
* How a link dropped by a device that refuses a stale bond reaches JS. A wedged write is
|
|
207
224
|
* excluded: the system is still re-pairing then and has not reported key missing yet.
|
|
@@ -635,6 +652,11 @@ export default class ReactNativeBleTransport {
|
|
|
635
652
|
init(logger: any, emitter: EventEmitter) {
|
|
636
653
|
setBleLogger(logger);
|
|
637
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
|
+
}
|
|
638
660
|
}
|
|
639
661
|
|
|
640
662
|
configure(signedData: any) {
|
|
@@ -1172,6 +1194,8 @@ export default class ReactNativeBleTransport {
|
|
|
1172
1194
|
|
|
1173
1195
|
let device: Device | null = null;
|
|
1174
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;
|
|
1175
1199
|
// A firmware-install reconnect always refreshes: the new firmware may expose a different table.
|
|
1176
1200
|
const refreshAndroidGattCache =
|
|
1177
1201
|
isAndroid && (!!skipProtocolProbe || this.androidGattCacheRefreshes.has(uuid));
|
|
@@ -1205,11 +1229,13 @@ export default class ReactNativeBleTransport {
|
|
|
1205
1229
|
if (Platform.OS === 'android') {
|
|
1206
1230
|
// Subscribe before the link exists: key missing is broadcast while Android encrypts it.
|
|
1207
1231
|
startBleKeyMissingTracking();
|
|
1232
|
+
startBleEncryptionTracking();
|
|
1208
1233
|
// Failures before the new link starts must not be read against the previous link.
|
|
1209
1234
|
this.androidLinkStartedAt.delete(uuid);
|
|
1210
1235
|
// Initiate bonding locally before GATT can trigger peripheral-initiated pairing.
|
|
1211
1236
|
try {
|
|
1212
1237
|
const bondState = await pairDevice(uuid);
|
|
1238
|
+
androidBondedBeforeConnect = !bondState.bonding && bondState.bonded;
|
|
1213
1239
|
if (bondState.bonding) {
|
|
1214
1240
|
await onDeviceBondState(uuid, this.bondAbortController.signal, {
|
|
1215
1241
|
systemInitiated: bondState.initiated === false,
|
|
@@ -1413,7 +1439,13 @@ export default class ReactNativeBleTransport {
|
|
|
1413
1439
|
this.acquiringProtocolV2.add(uuid);
|
|
1414
1440
|
}
|
|
1415
1441
|
|
|
1442
|
+
let linkEncryption: AndroidLinkEncryption | undefined;
|
|
1416
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
|
+
}
|
|
1417
1449
|
await this.installTransportForAcquire(uuid, acquiredDevice, {
|
|
1418
1450
|
writeCharacteristic,
|
|
1419
1451
|
notifyCharacteristic,
|
|
@@ -1465,6 +1497,11 @@ export default class ReactNativeBleTransport {
|
|
|
1465
1497
|
throw ERRORS.TypedError(HardwareErrorCode.TransportNotFound);
|
|
1466
1498
|
}
|
|
1467
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
|
+
}
|
|
1468
1505
|
return { uuid, protocolType };
|
|
1469
1506
|
} catch (error) {
|
|
1470
1507
|
// A failed acquire must retire the physical link before Core retries. Logical
|
|
@@ -1773,6 +1810,39 @@ export default class ReactNativeBleTransport {
|
|
|
1773
1810
|
});
|
|
1774
1811
|
}
|
|
1775
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;
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1776
1846
|
private async callProtocolV1(
|
|
1777
1847
|
uuid: string,
|
|
1778
1848
|
name: string,
|
|
@@ -2040,6 +2110,7 @@ export default class ReactNativeBleTransport {
|
|
|
2040
2110
|
this.androidHighPriorityDevices.clear();
|
|
2041
2111
|
this.androidLinkStartedAt.clear();
|
|
2042
2112
|
stopBleKeyMissingTracking();
|
|
2113
|
+
stopBleEncryptionTracking();
|
|
2043
2114
|
const error = ERRORS.TypedError(HardwareErrorCode.BleDeviceDisconnected);
|
|
2044
2115
|
this.runPromise?.reject(error);
|
|
2045
2116
|
this.runPromise = null;
|