@onekeyfe/hd-transport-react-native 1.2.3-alpha.1 → 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/README.md +1 -1
- 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/bleNativeDisconnect.d.ts +3 -0
- package/dist/bleNativeDisconnect.d.ts.map +1 -0
- package/dist/index.d.ts +69 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +951 -139
- package/package.json +6 -6
- package/src/BleManager.ts +113 -5
- package/src/__tests__/androidEncryption.test.ts +482 -0
- package/src/__tests__/androidKeyMissing.test.ts +307 -0
- package/src/__tests__/bleNativeDisconnect.test.ts +33 -0
- package/src/__tests__/connectTimeout.test.ts +427 -3
- package/src/__tests__/enumerate.test.ts +15 -0
- package/src/__tests__/protocolV2Link.test.ts +991 -62
- package/src/__tests__/staleCallTimeout.test.ts +34 -5
- package/src/bleEncryption.ts +267 -0
- package/src/bleKeyMissing.ts +106 -0
- package/src/bleNativeDisconnect.ts +40 -0
- package/src/index.ts +785 -156
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;
|
|
@@ -57,20 +127,71 @@ const bleLogger = {
|
|
|
57
127
|
};
|
|
58
128
|
|
|
59
129
|
const Logger = bleLogger;
|
|
130
|
+
const bondFailureReasons = {
|
|
131
|
+
1: 'authentication_failed',
|
|
132
|
+
2: 'rejected',
|
|
133
|
+
3: 'canceled',
|
|
134
|
+
4: 'device_unreachable',
|
|
135
|
+
5: 'discovery_in_progress',
|
|
136
|
+
6: 'timeout',
|
|
137
|
+
7: 'repeated_attempts',
|
|
138
|
+
8: 'remote_canceled',
|
|
139
|
+
9: 'removed',
|
|
140
|
+
};
|
|
60
141
|
const getConnectedDeviceIds = (serviceUuids) => BleUtils__default["default"].getConnectedPeripherals(serviceUuids);
|
|
61
142
|
const pairDevice = (macAddress) => BleUtils__default["default"].pairDevice(macAddress);
|
|
62
|
-
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) => {
|
|
158
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
159
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected));
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
let pendingFailure;
|
|
63
163
|
const cleanup = () => {
|
|
64
164
|
if (timeout) {
|
|
65
165
|
clearTimeout(timeout);
|
|
66
166
|
}
|
|
167
|
+
if (pendingFailure)
|
|
168
|
+
clearTimeout(pendingFailure);
|
|
67
169
|
if (cleanupListener)
|
|
68
170
|
cleanupListener();
|
|
171
|
+
cleanupKeyMissing === null || cleanupKeyMissing === void 0 ? void 0 : cleanupKeyMissing();
|
|
172
|
+
signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', onAbort);
|
|
69
173
|
};
|
|
174
|
+
const onAbort = () => {
|
|
175
|
+
cleanup();
|
|
176
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected));
|
|
177
|
+
};
|
|
178
|
+
signal === null || signal === void 0 ? void 0 : signal.addEventListener('abort', onAbort, { once: true });
|
|
70
179
|
const timeout = setTimeout(() => {
|
|
71
180
|
cleanup();
|
|
72
|
-
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, '
|
|
181
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'Bluetooth pairing timed out', {
|
|
182
|
+
phase: 'bond',
|
|
183
|
+
reason: 'timeout',
|
|
184
|
+
}));
|
|
73
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;
|
|
74
195
|
const cleanupListener = BleUtils__default["default"].onDeviceBondState(peripheral => {
|
|
75
196
|
var _a;
|
|
76
197
|
if (((_a = peripheral.id) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== bleMacAddress.toLowerCase()) {
|
|
@@ -78,15 +199,28 @@ const onDeviceBondState = (bleMacAddress) => new Promise((resolve, reject) => {
|
|
|
78
199
|
}
|
|
79
200
|
const { bondState } = peripheral;
|
|
80
201
|
const hasBonded = bondState.preState === 'BOND_BONDING' && bondState.state === 'BOND_BONDED';
|
|
81
|
-
const
|
|
202
|
+
const hasFailed = bondState.preState === 'BOND_BONDING' && bondState.state === 'BOND_NONE';
|
|
203
|
+
const hasRestarted = bondState.preState === 'BOND_NONE' && bondState.state === 'BOND_BONDING';
|
|
82
204
|
Logger.debug('onDeviceBondState bondState:', bondState);
|
|
83
205
|
if (hasBonded) {
|
|
84
206
|
cleanup();
|
|
85
207
|
resolve(peripheral);
|
|
86
208
|
}
|
|
87
|
-
else if (
|
|
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
|
+
}
|
|
221
|
+
else if (hasFailed) {
|
|
88
222
|
cleanup();
|
|
89
|
-
reject(
|
|
223
|
+
reject(createBondFailureError(bondState));
|
|
90
224
|
}
|
|
91
225
|
});
|
|
92
226
|
});
|
|
@@ -194,6 +328,193 @@ const subscribeBleOn = (bleManager, ms = 1000) => new Promise((resolve, reject)
|
|
|
194
328
|
}, ms);
|
|
195
329
|
});
|
|
196
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
|
+
|
|
497
|
+
const BLE_PLX_DEVICE_DISCONNECTED = 201;
|
|
498
|
+
const IOS_PERIPHERAL_DISCONNECTED = 7;
|
|
499
|
+
const nativeErrorText$1 = (error) => [error.reason, error.message]
|
|
500
|
+
.filter((value) => typeof value === 'string')
|
|
501
|
+
.join(' ');
|
|
502
|
+
const isNativeBleDisconnectError = (error) => {
|
|
503
|
+
if (!error || typeof error !== 'object') {
|
|
504
|
+
return false;
|
|
505
|
+
}
|
|
506
|
+
const nativeError = error;
|
|
507
|
+
return (nativeError.errorCode === BLE_PLX_DEVICE_DISCONNECTED ||
|
|
508
|
+
nativeError.iosErrorCode === IOS_PERIPHERAL_DISCONNECTED);
|
|
509
|
+
};
|
|
510
|
+
const toBleDisconnectHardwareError = (error) => {
|
|
511
|
+
if ((error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleDeviceDisconnected) {
|
|
512
|
+
return error;
|
|
513
|
+
}
|
|
514
|
+
const nativeError = (error !== null && error !== void 0 ? error : {});
|
|
515
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected, nativeErrorText$1(nativeError) || undefined);
|
|
516
|
+
};
|
|
517
|
+
|
|
197
518
|
const ATT_INSUFFICIENT_AUTHENTICATION = 5;
|
|
198
519
|
const ATT_INSUFFICIENT_ENCRYPTION = 15;
|
|
199
520
|
const IOS_PEER_REMOVED_PAIRING_INFORMATION = 14;
|
|
@@ -258,6 +579,7 @@ class BleTransport {
|
|
|
258
579
|
const { check, ProtocolV1, parseConfigure } = transport__default["default"];
|
|
259
580
|
const Log = bleLogger;
|
|
260
581
|
const transportCache = {};
|
|
582
|
+
let bleManagerResetPromise;
|
|
261
583
|
const FIRMWARE_UPLOAD_WRITE_BURST_SIZE = reactNative.Platform.OS === 'ios' ? 4 : 5;
|
|
262
584
|
const FIRMWARE_UPLOAD_WRITE_PAUSE_MS = reactNative.Platform.OS === 'ios' ? 8 : 10;
|
|
263
585
|
const FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS = reactNative.Platform.OS === 'ios' ? 24 : 30;
|
|
@@ -316,6 +638,29 @@ const WEDGED_WRITE_MESSAGE = 'BLE write timeout after';
|
|
|
316
638
|
const isWedgedWriteError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleWriteCharacteristicError &&
|
|
317
639
|
typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' &&
|
|
318
640
|
error.message.startsWith(WEDGED_WRITE_MESSAGE);
|
|
641
|
+
const shouldRethrowProtocolProbeError = (error) => {
|
|
642
|
+
const code = error === null || error === void 0 ? void 0 : error.errorCode;
|
|
643
|
+
return (hdShared.isBleStaleBondHardwareError(error) ||
|
|
644
|
+
isNativeBleDisconnectError(error) ||
|
|
645
|
+
code === hdShared.HardwareErrorCode.BleDeviceNotBonded ||
|
|
646
|
+
code === hdShared.HardwareErrorCode.BleDeviceBondedCanceled ||
|
|
647
|
+
code === hdShared.HardwareErrorCode.BleDeviceDisconnected ||
|
|
648
|
+
code === hdShared.HardwareErrorCode.BleCharacteristicNotifyError ||
|
|
649
|
+
code === hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure ||
|
|
650
|
+
code === hdShared.HardwareErrorCode.BleWriteCharacteristicError);
|
|
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
|
+
};
|
|
319
664
|
const BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
|
|
320
665
|
const DEVICE_SCAN_TIMEOUT_MS = 3000;
|
|
321
666
|
const IOS_NOTIFY_READY_DELAY_MS = 150;
|
|
@@ -350,10 +695,10 @@ function getDeviceDisplayName(device) {
|
|
|
350
695
|
}
|
|
351
696
|
const IOS_REQUEST_MTU = 247;
|
|
352
697
|
const ANDROID_REQUEST_MTU = 517;
|
|
353
|
-
const BLE_MTU_REFRESH_RETRY_DELAY_MS = 200;
|
|
354
698
|
const ANDROID_HIGH_PRIORITY_IDLE_MS = 1000;
|
|
355
699
|
const getRequestedBleMtu = () => reactNative.Platform.OS === 'android' ? ANDROID_REQUEST_MTU : IOS_REQUEST_MTU;
|
|
356
700
|
const BLE_NATIVE_CONNECT_TIMEOUT_MS = 3000;
|
|
701
|
+
const BLE_MTU_REQUEST_TIMEOUT_MS = BLE_NATIVE_CONNECT_TIMEOUT_MS;
|
|
357
702
|
const connectOptions = {
|
|
358
703
|
requestMTU: getRequestedBleMtu(),
|
|
359
704
|
timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
|
|
@@ -362,6 +707,29 @@ const connectOptions = {
|
|
|
362
707
|
const fallbackConnectOptions = {
|
|
363
708
|
timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
|
|
364
709
|
};
|
|
710
|
+
const androidRefreshGattConnectOptions = {
|
|
711
|
+
timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
|
|
712
|
+
refreshGatt: 'OnConnected',
|
|
713
|
+
};
|
|
714
|
+
const ANDROID_MTU_EXCHANGE_TIMEOUT_MS = 12000;
|
|
715
|
+
const ANDROID_LINK_DROP_QUIET_MS = 5000;
|
|
716
|
+
const ANDROID_LINK_DROP_POLL_MS = 250;
|
|
717
|
+
const isKnownDefaultMtu = (mtu) => typeof mtu === 'number' && Number.isFinite(mtu) && mtu <= 23;
|
|
718
|
+
const isMissingGattShapeError = (error) => {
|
|
719
|
+
const code = error === null || error === void 0 ? void 0 : error.errorCode;
|
|
720
|
+
const message = error === null || error === void 0 ? void 0 : error.message;
|
|
721
|
+
return (code === hdShared.HardwareErrorCode.BleServiceNotFound ||
|
|
722
|
+
code === hdShared.HardwareErrorCode.BleCharacteristicNotFound ||
|
|
723
|
+
(typeof message === 'string' &&
|
|
724
|
+
(message.includes('BLECharacteristicNotFound') ||
|
|
725
|
+
message.includes('BLECharacteristicNotWritable') ||
|
|
726
|
+
message.includes('BLECharacteristicNotNotifiable'))));
|
|
727
|
+
};
|
|
728
|
+
const isStaleGattTableNotifyReason = (reason) => !!reason &&
|
|
729
|
+
(reason.includes('Cannot write client characteristic config descriptor') ||
|
|
730
|
+
reason.includes('Cannot find client characteristic config descriptor') ||
|
|
731
|
+
reason.includes('The handle is invalid') ||
|
|
732
|
+
reason.includes('Writing is not permitted'));
|
|
365
733
|
const BLE_CONNECT_TIMEOUT_MS = BLE_NATIVE_CONNECT_TIMEOUT_MS * 2 + 2000;
|
|
366
734
|
const BLE_GATT_SETUP_TIMEOUT_MS = 10000;
|
|
367
735
|
const PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = 3;
|
|
@@ -376,6 +744,11 @@ const isWedgedBleSetupError = (error) => (error === null || error === void 0 ? v
|
|
|
376
744
|
error.message.startsWith(BLE_SETUP_WEDGED_MESSAGE);
|
|
377
745
|
const shouldRethrowBleSetupError = (error) => isConnectTimeoutError(error) || isWedgedBleSetupError(error);
|
|
378
746
|
const isNativeOperationTimeoutError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === reactNativeBlePlx.BleErrorCode.OperationTimedOut;
|
|
747
|
+
const isMtuOrCancelledConnectError = (error) => {
|
|
748
|
+
const errorCode = error === null || error === void 0 ? void 0 : error.errorCode;
|
|
749
|
+
return (errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
|
|
750
|
+
errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled);
|
|
751
|
+
};
|
|
379
752
|
const tryToGetConfiguration = (device) => {
|
|
380
753
|
if (!device || !device.serviceUUIDs)
|
|
381
754
|
return null;
|
|
@@ -387,25 +760,65 @@ const tryToGetConfiguration = (device) => {
|
|
|
387
760
|
return null;
|
|
388
761
|
return infos;
|
|
389
762
|
};
|
|
390
|
-
const requestNegotiatedMtu = (device, stage, attempt) => __awaiter(void 0, void 0, void 0, function* () {
|
|
763
|
+
const requestNegotiatedMtu = (device, stage, attempt, cancelTransaction) => __awaiter(void 0, void 0, void 0, function* () {
|
|
391
764
|
if (reactNative.Platform.OS !== 'ios' && reactNative.Platform.OS !== 'android')
|
|
392
|
-
return device;
|
|
765
|
+
return { device, timedOut: false };
|
|
766
|
+
const transactionId = `${device.id}:mtu:${stage}:${attempt}:${Date.now()}`;
|
|
767
|
+
let timeoutId;
|
|
768
|
+
let timedOut = false;
|
|
393
769
|
try {
|
|
394
|
-
const
|
|
395
|
-
|
|
770
|
+
const request = device.requestMTU(getRequestedBleMtu(), transactionId);
|
|
771
|
+
request.catch(() => undefined);
|
|
772
|
+
const mtuDevice = yield Promise.race([
|
|
773
|
+
request,
|
|
774
|
+
new Promise((_, reject) => {
|
|
775
|
+
timeoutId = setTimeout(() => {
|
|
776
|
+
timedOut = true;
|
|
777
|
+
reject(new Error(`BLE MTU request timeout after ${BLE_MTU_REQUEST_TIMEOUT_MS}ms`));
|
|
778
|
+
}, BLE_MTU_REQUEST_TIMEOUT_MS);
|
|
779
|
+
}),
|
|
780
|
+
]);
|
|
781
|
+
return { device: mtuDevice, timedOut: false };
|
|
396
782
|
}
|
|
397
783
|
catch (error) {
|
|
784
|
+
if (timedOut && cancelTransaction) {
|
|
785
|
+
try {
|
|
786
|
+
Promise.resolve(cancelTransaction(transactionId)).catch(cancelError => {
|
|
787
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] MTU cancellation failed', {
|
|
788
|
+
platform: reactNative.Platform.OS,
|
|
789
|
+
stage,
|
|
790
|
+
attempt,
|
|
791
|
+
error: cancelError instanceof Error ? cancelError.message : String(cancelError),
|
|
792
|
+
});
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
catch (cancelError) {
|
|
796
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] MTU cancellation failed', {
|
|
797
|
+
platform: reactNative.Platform.OS,
|
|
798
|
+
stage,
|
|
799
|
+
attempt,
|
|
800
|
+
error: cancelError instanceof Error ? cancelError.message : String(cancelError),
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
}
|
|
398
804
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] MTU refresh failed, continuing with current value', {
|
|
399
805
|
platform: reactNative.Platform.OS,
|
|
400
806
|
stage,
|
|
401
807
|
attempt,
|
|
402
808
|
actual: device.mtu,
|
|
809
|
+
timedOut,
|
|
403
810
|
error: error instanceof Error ? error.message : String(error),
|
|
404
811
|
});
|
|
405
|
-
return device;
|
|
812
|
+
return { device, timedOut };
|
|
813
|
+
}
|
|
814
|
+
finally {
|
|
815
|
+
if (timeoutId)
|
|
816
|
+
clearTimeout(timeoutId);
|
|
406
817
|
}
|
|
407
818
|
});
|
|
408
|
-
const resolveNegotiatedMtu = (device) =>
|
|
819
|
+
const resolveNegotiatedMtu = (device, cancelTransaction) => shouldRefreshNegotiatedMtu(device.mtu)
|
|
820
|
+
? requestNegotiatedMtu(device, 'connected', 0, cancelTransaction)
|
|
821
|
+
: Promise.resolve({ device, timedOut: false });
|
|
409
822
|
function remapError(error) {
|
|
410
823
|
var _a;
|
|
411
824
|
if (error instanceof reactNativeBlePlx.BleError) {
|
|
@@ -429,6 +842,7 @@ class ReactNativeBleTransport {
|
|
|
429
842
|
this.name = 'ReactNativeBleTransport';
|
|
430
843
|
this.configured = false;
|
|
431
844
|
this.stopped = false;
|
|
845
|
+
this.bondAbortController = new AbortController();
|
|
432
846
|
this.scanTimeout = DEVICE_SCAN_TIMEOUT_MS;
|
|
433
847
|
this.runPromise = null;
|
|
434
848
|
this.runPromiseDeviceId = null;
|
|
@@ -441,7 +855,10 @@ class ReactNativeBleTransport {
|
|
|
441
855
|
this.sessionProtocols = new Map();
|
|
442
856
|
this.confirmedProtocolV2 = new Set();
|
|
443
857
|
this.protocolReprobeFailures = new Map();
|
|
858
|
+
this.silentDetections = new Map();
|
|
859
|
+
this.androidGattCacheRefreshes = new Set();
|
|
444
860
|
this.staleBondErrors = new Map();
|
|
861
|
+
this.androidLinkStartedAt = new Map();
|
|
445
862
|
this.acquiringProtocolV2 = new Set();
|
|
446
863
|
this.protocolV2Assemblers = new Map();
|
|
447
864
|
this.protocolV2FrameQueues = new Map();
|
|
@@ -458,11 +875,20 @@ class ReactNativeBleTransport {
|
|
|
458
875
|
},
|
|
459
876
|
classifyError: () => 'link-fatal',
|
|
460
877
|
onLinkInvalidated: (uuid, reason) => __awaiter(this, void 0, void 0, function* () {
|
|
461
|
-
var _b;
|
|
878
|
+
var _b, _c;
|
|
462
879
|
(_b = this.protocolV2Assemblers.get(uuid)) === null || _b === void 0 ? void 0 : _b.reset();
|
|
463
880
|
this.rejectProtocolV2Frames(uuid, new Error(reason));
|
|
464
881
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 link invalidated:', uuid, reason);
|
|
465
882
|
if (reason.startsWith('Protocol V2 link-fatal error:')) {
|
|
883
|
+
if (this.probingProtocols.get(uuid) !== 'V2') {
|
|
884
|
+
const transport = transportCache[uuid];
|
|
885
|
+
try {
|
|
886
|
+
this.emitDeviceDisconnect(uuid, (_c = transport === null || transport === void 0 ? void 0 : transport.device) === null || _c === void 0 ? void 0 : _c.name, transport === null || transport === void 0 ? void 0 : transport.monitorToken);
|
|
887
|
+
}
|
|
888
|
+
catch (_d) {
|
|
889
|
+
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] Protocol V2 disconnect listener failed');
|
|
890
|
+
}
|
|
891
|
+
}
|
|
466
892
|
yield this.releaseNative(uuid, true);
|
|
467
893
|
}
|
|
468
894
|
}),
|
|
@@ -474,11 +900,16 @@ class ReactNativeBleTransport {
|
|
|
474
900
|
this.androidPriorityResetTimers = new Map();
|
|
475
901
|
this.nextMonitorToken = 1;
|
|
476
902
|
this.lifecycleOperations = new Map();
|
|
903
|
+
this.scanCleanups = new Set();
|
|
477
904
|
this.scanTimeout = (_a = options.scanTimeout) !== null && _a !== void 0 ? _a : DEVICE_SCAN_TIMEOUT_MS;
|
|
478
905
|
}
|
|
479
906
|
init(logger, emitter) {
|
|
480
907
|
setBleLogger(logger);
|
|
481
908
|
this.emitter = emitter;
|
|
909
|
+
if (reactNative.Platform.OS === 'android') {
|
|
910
|
+
startBleKeyMissingTracking();
|
|
911
|
+
startBleEncryptionTracking();
|
|
912
|
+
}
|
|
482
913
|
}
|
|
483
914
|
configure(signedData) {
|
|
484
915
|
const messages = parseConfigure(signedData);
|
|
@@ -502,10 +933,38 @@ class ReactNativeBleTransport {
|
|
|
502
933
|
listen() {
|
|
503
934
|
}
|
|
504
935
|
getPlxManager() {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
936
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
937
|
+
while (bleManagerResetPromise) {
|
|
938
|
+
yield this.waitForManagerReset();
|
|
939
|
+
}
|
|
940
|
+
if (this.stopped)
|
|
941
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
942
|
+
if (!this.blePlxManager)
|
|
943
|
+
this.blePlxManager = new reactNativeBlePlx.BleManager();
|
|
944
|
+
return this.blePlxManager;
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
waitForManagerReset() {
|
|
948
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
949
|
+
if (!bleManagerResetPromise)
|
|
950
|
+
return;
|
|
951
|
+
let timeout;
|
|
952
|
+
try {
|
|
953
|
+
yield Promise.race([
|
|
954
|
+
bleManagerResetPromise,
|
|
955
|
+
new Promise((_, reject) => {
|
|
956
|
+
timeout = setTimeout(() => reject(this.createWedgedBleSetupError()), BLE_CONNECT_TIMEOUT_MS);
|
|
957
|
+
}),
|
|
958
|
+
]);
|
|
959
|
+
}
|
|
960
|
+
catch (_a) {
|
|
961
|
+
throw this.createWedgedBleSetupError();
|
|
962
|
+
}
|
|
963
|
+
finally {
|
|
964
|
+
if (timeout)
|
|
965
|
+
clearTimeout(timeout);
|
|
966
|
+
}
|
|
967
|
+
});
|
|
509
968
|
}
|
|
510
969
|
resolveCharacteristics(device) {
|
|
511
970
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -670,30 +1129,55 @@ class ReactNativeBleTransport {
|
|
|
670
1129
|
}
|
|
671
1130
|
enumerate() {
|
|
672
1131
|
return __awaiter(this, void 0, void 0, function* () {
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
if (reactNative.
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
reactNative.PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT,
|
|
688
|
-
reactNative.PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN,
|
|
689
|
-
]);
|
|
690
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('requesting permissions, result: ', resultConnect);
|
|
691
|
-
if (resultConnect[reactNative.PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT] !== 'granted' ||
|
|
692
|
-
resultConnect[reactNative.PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN] !== 'granted') {
|
|
693
|
-
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePermissionError));
|
|
694
|
-
return;
|
|
695
|
-
}
|
|
1132
|
+
const scanStartedAt = Date.now();
|
|
1133
|
+
let firstDeviceMs;
|
|
1134
|
+
const blePlxManager = yield this.getPlxManager();
|
|
1135
|
+
yield subscribeBleOn(blePlxManager);
|
|
1136
|
+
if (reactNative.Platform.OS === 'android' && reactNative.Platform.Version >= 31) {
|
|
1137
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('requesting permissions, please wait...');
|
|
1138
|
+
const resultConnect = yield reactNative.PermissionsAndroid.requestMultiple([
|
|
1139
|
+
reactNative.PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT,
|
|
1140
|
+
reactNative.PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN,
|
|
1141
|
+
]);
|
|
1142
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('requesting permissions, result: ', resultConnect);
|
|
1143
|
+
if (resultConnect[reactNative.PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT] !== 'granted' ||
|
|
1144
|
+
resultConnect[reactNative.PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN] !== 'granted') {
|
|
1145
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePermissionError);
|
|
696
1146
|
}
|
|
1147
|
+
}
|
|
1148
|
+
if (this.stopped)
|
|
1149
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1150
|
+
return new Promise((resolve, reject) => {
|
|
1151
|
+
const deviceList = [];
|
|
1152
|
+
let finished = false;
|
|
1153
|
+
let scanCleanup;
|
|
1154
|
+
const finishScan = (error) => {
|
|
1155
|
+
if (scanCleanup)
|
|
1156
|
+
return scanCleanup;
|
|
1157
|
+
finished = true;
|
|
1158
|
+
clearScanTimer();
|
|
1159
|
+
scanCleanup = this.runNativeTeardown('scan', blePlxManager, () => __awaiter(this, void 0, void 0, function* () {
|
|
1160
|
+
yield blePlxManager.stopDeviceScan();
|
|
1161
|
+
})).then(() => {
|
|
1162
|
+
this.scanCleanups.delete(cancelScan);
|
|
1163
|
+
if (error)
|
|
1164
|
+
reject(error);
|
|
1165
|
+
else
|
|
1166
|
+
resolve(deviceList);
|
|
1167
|
+
});
|
|
1168
|
+
return scanCleanup;
|
|
1169
|
+
};
|
|
1170
|
+
const cancelScan = () => finishScan(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected));
|
|
1171
|
+
this.scanCleanups.add(cancelScan);
|
|
1172
|
+
const clearScanTimer = timer.timeout(() => {
|
|
1173
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] scan completed', {
|
|
1174
|
+
elapsedMs: Date.now() - scanStartedAt,
|
|
1175
|
+
firstDeviceMs,
|
|
1176
|
+
deviceCount: deviceList.length,
|
|
1177
|
+
scanWindowMs: this.scanTimeout,
|
|
1178
|
+
});
|
|
1179
|
+
finishScan();
|
|
1180
|
+
}, this.scanTimeout);
|
|
697
1181
|
blePlxManager.startDeviceScan(getBluetoothServiceUuids(), {
|
|
698
1182
|
allowDuplicates: true,
|
|
699
1183
|
scanMode: reactNativeBlePlx.ScanMode.LowLatency,
|
|
@@ -702,19 +1186,17 @@ class ReactNativeBleTransport {
|
|
|
702
1186
|
if (error) {
|
|
703
1187
|
Log === null || Log === void 0 ? void 0 : Log.debug('ble scan error: ', error);
|
|
704
1188
|
if ([reactNativeBlePlx.BleErrorCode.BluetoothPoweredOff, reactNativeBlePlx.BleErrorCode.BluetoothInUnknownState].includes(error.errorCode)) {
|
|
705
|
-
|
|
1189
|
+
finishScan(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePermissionError));
|
|
706
1190
|
}
|
|
707
1191
|
else if (error.errorCode === reactNativeBlePlx.BleErrorCode.BluetoothUnauthorized) {
|
|
708
|
-
|
|
1192
|
+
finishScan(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleLocationError));
|
|
709
1193
|
}
|
|
710
1194
|
else if (error.errorCode === reactNativeBlePlx.BleErrorCode.LocationServicesDisabled) {
|
|
711
|
-
|
|
712
|
-
}
|
|
713
|
-
else if (error.errorCode === reactNativeBlePlx.BleErrorCode.ScanStartFailed) {
|
|
714
|
-
timer.timeout(() => { }, this.scanTimeout);
|
|
1195
|
+
finishScan(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleLocationServicesDisabled));
|
|
715
1196
|
}
|
|
1197
|
+
else if (error.errorCode === reactNativeBlePlx.BleErrorCode.ScanStartFailed) ;
|
|
716
1198
|
else {
|
|
717
|
-
|
|
1199
|
+
finishScan(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleScanError, (_a = error.reason) !== null && _a !== void 0 ? _a : ''));
|
|
718
1200
|
}
|
|
719
1201
|
return;
|
|
720
1202
|
}
|
|
@@ -739,6 +1221,8 @@ class ReactNativeBleTransport {
|
|
|
739
1221
|
});
|
|
740
1222
|
}
|
|
741
1223
|
});
|
|
1224
|
+
if (finished)
|
|
1225
|
+
return;
|
|
742
1226
|
getConnectedDeviceIds(reactNative.Platform.OS === 'ios' ? getBluetoothServiceUuids() : []).then(devices => {
|
|
743
1227
|
for (const device of devices) {
|
|
744
1228
|
const localName = 'localName' in device && typeof device.localName === 'string'
|
|
@@ -754,10 +1238,11 @@ class ReactNativeBleTransport {
|
|
|
754
1238
|
addDevice(device);
|
|
755
1239
|
}
|
|
756
1240
|
}
|
|
757
|
-
});
|
|
1241
|
+
}, error => Log === null || Log === void 0 ? void 0 : Log.debug('search connected peripheral failed:', error));
|
|
758
1242
|
const addDevice = (device) => {
|
|
759
1243
|
var _a;
|
|
760
|
-
if (deviceList.every(d => d.id !== device.id)) {
|
|
1244
|
+
if (!finished && deviceList.every(d => d.id !== device.id)) {
|
|
1245
|
+
firstDeviceMs !== null && firstDeviceMs !== void 0 ? firstDeviceMs : (firstDeviceMs = Date.now() - scanStartedAt);
|
|
761
1246
|
const displayName = (_a = getDeviceDisplayName(device)) !== null && _a !== void 0 ? _a : 'Unknown BLE Device';
|
|
762
1247
|
deviceList.push(Object.assign(Object.assign({}, device), { name: displayName, commType: 'ble' }));
|
|
763
1248
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] OneKey BLE device discovered', {
|
|
@@ -767,16 +1252,14 @@ class ReactNativeBleTransport {
|
|
|
767
1252
|
});
|
|
768
1253
|
}
|
|
769
1254
|
};
|
|
770
|
-
|
|
771
|
-
blePlxManager.stopDeviceScan();
|
|
772
|
-
resolve(deviceList);
|
|
773
|
-
}, this.scanTimeout);
|
|
774
|
-
}));
|
|
1255
|
+
});
|
|
775
1256
|
});
|
|
776
1257
|
}
|
|
777
1258
|
installTransportForAcquire(uuid, device, characteristics) {
|
|
778
1259
|
return __awaiter(this, void 0, void 0, function* () {
|
|
779
1260
|
const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristicsWithTimeout(uuid, device));
|
|
1261
|
+
if (this.stopped)
|
|
1262
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
780
1263
|
const transport$1 = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
|
|
781
1264
|
transport$1.mtuSize = typeof device.mtu === 'number' ? device.mtu : undefined;
|
|
782
1265
|
const monitorToken = this.nextMonitorToken;
|
|
@@ -797,30 +1280,12 @@ class ReactNativeBleTransport {
|
|
|
797
1280
|
else if (reactNative.Platform.OS === 'android') {
|
|
798
1281
|
yield delay(ANDROID_NOTIFY_READY_DELAY_MS);
|
|
799
1282
|
}
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
if ((reactNative.Platform.OS === 'ios' || reactNative.Platform.OS === 'android') &&
|
|
803
|
-
shouldRefreshNegotiatedMtu(transport$1.mtuSize)) {
|
|
804
|
-
refreshAttempts += 1;
|
|
805
|
-
let refreshedDevice = yield requestNegotiatedMtu(transport$1.device, 'servicesAndNotifyReady', 1);
|
|
806
|
-
transport$1.device = refreshedDevice;
|
|
807
|
-
transport$1.mtuSize =
|
|
808
|
-
typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport$1.mtuSize;
|
|
809
|
-
if (shouldRefreshNegotiatedMtu(transport$1.mtuSize)) {
|
|
810
|
-
yield delay(BLE_MTU_REFRESH_RETRY_DELAY_MS);
|
|
811
|
-
refreshAttempts += 1;
|
|
812
|
-
refreshedDevice = yield requestNegotiatedMtu(transport$1.device, 'servicesAndNotifyReady', 2);
|
|
813
|
-
transport$1.device = refreshedDevice;
|
|
814
|
-
transport$1.mtuSize =
|
|
815
|
-
typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport$1.mtuSize;
|
|
816
|
-
}
|
|
817
|
-
}
|
|
1283
|
+
if (this.stopped)
|
|
1284
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
818
1285
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE MTU ready', {
|
|
819
1286
|
platform: reactNative.Platform.OS,
|
|
820
1287
|
requested: getRequestedBleMtu(),
|
|
821
|
-
initial: initialMtu,
|
|
822
1288
|
actual: transport$1.mtuSize,
|
|
823
|
-
refreshAttempts,
|
|
824
1289
|
});
|
|
825
1290
|
return transport$1;
|
|
826
1291
|
});
|
|
@@ -831,12 +1296,21 @@ class ReactNativeBleTransport {
|
|
|
831
1296
|
if (!uuid) {
|
|
832
1297
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleRequiredUUID);
|
|
833
1298
|
}
|
|
834
|
-
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
|
+
}));
|
|
835
1307
|
});
|
|
836
1308
|
}
|
|
837
1309
|
acquireUnlocked(input) {
|
|
838
1310
|
var _a;
|
|
839
1311
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1312
|
+
if (this.stopped)
|
|
1313
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
840
1314
|
const { uuid, forceCleanRunPromise, expectedProtocol, skipProtocolProbe } = input;
|
|
841
1315
|
const shouldMapProtocolV2StaleBond = expectedProtocol
|
|
842
1316
|
? expectedProtocol === 'V2'
|
|
@@ -868,9 +1342,13 @@ class ReactNativeBleTransport {
|
|
|
868
1342
|
const isCachedDeviceConnected = yield cachedTransport.device
|
|
869
1343
|
.isConnected()
|
|
870
1344
|
.catch(() => false);
|
|
1345
|
+
const isCachedAndroidLinkUsable = reactNative.Platform.OS !== 'android' || !this.androidGattCacheRefreshes.has(uuid);
|
|
871
1346
|
if (isCachedDeviceConnected &&
|
|
1347
|
+
isCachedAndroidLinkUsable &&
|
|
872
1348
|
cachedProtocol &&
|
|
873
1349
|
(!expectedProtocol || cachedProtocol === expectedProtocol)) {
|
|
1350
|
+
if (this.stopped)
|
|
1351
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
874
1352
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] reuse cached BLE transport:', uuid, cachedProtocol);
|
|
875
1353
|
return { uuid, protocolType: cachedProtocol };
|
|
876
1354
|
}
|
|
@@ -879,6 +1357,16 @@ class ReactNativeBleTransport {
|
|
|
879
1357
|
}
|
|
880
1358
|
}
|
|
881
1359
|
let device = null;
|
|
1360
|
+
const isAndroid = reactNative.Platform.OS === 'android';
|
|
1361
|
+
let androidBondedBeforeConnect = false;
|
|
1362
|
+
const refreshAndroidGattCache = isAndroid && (!!skipProtocolProbe || this.androidGattCacheRefreshes.has(uuid));
|
|
1363
|
+
let nativeConnectOptions = connectOptions;
|
|
1364
|
+
if (isAndroid) {
|
|
1365
|
+
nativeConnectOptions = refreshAndroidGattCache
|
|
1366
|
+
? androidRefreshGattConnectOptions
|
|
1367
|
+
: fallbackConnectOptions;
|
|
1368
|
+
}
|
|
1369
|
+
let androidRefreshConnectRan = false;
|
|
882
1370
|
if (forceCleanRunPromise && this.runPromise) {
|
|
883
1371
|
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
|
|
884
1372
|
this.runPromise.reject(error);
|
|
@@ -887,6 +1375,7 @@ class ReactNativeBleTransport {
|
|
|
887
1375
|
Log === null || Log === void 0 ? void 0 : Log.debug('Force clean Bluetooth run promise, forceCleanRunPromise: ', forceCleanRunPromise);
|
|
888
1376
|
}
|
|
889
1377
|
const blePlxManager = yield this.getPlxManager();
|
|
1378
|
+
let skipPostConnectMtu = false;
|
|
890
1379
|
try {
|
|
891
1380
|
yield subscribeBleOn(blePlxManager);
|
|
892
1381
|
}
|
|
@@ -894,6 +1383,34 @@ class ReactNativeBleTransport {
|
|
|
894
1383
|
Log === null || Log === void 0 ? void 0 : Log.debug('subscribeBleOn error: ', error);
|
|
895
1384
|
throw error;
|
|
896
1385
|
}
|
|
1386
|
+
if (this.stopped)
|
|
1387
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1388
|
+
if (reactNative.Platform.OS === 'android') {
|
|
1389
|
+
startBleKeyMissingTracking();
|
|
1390
|
+
startBleEncryptionTracking();
|
|
1391
|
+
this.androidLinkStartedAt.delete(uuid);
|
|
1392
|
+
try {
|
|
1393
|
+
const bondState = yield pairDevice(uuid);
|
|
1394
|
+
androidBondedBeforeConnect = !bondState.bonding && bondState.bonded;
|
|
1395
|
+
if (bondState.bonding) {
|
|
1396
|
+
yield onDeviceBondState(uuid, this.bondAbortController.signal, {
|
|
1397
|
+
systemInitiated: bondState.initiated === false,
|
|
1398
|
+
});
|
|
1399
|
+
}
|
|
1400
|
+
else if (!bondState.bonded) {
|
|
1401
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'device is not bonded');
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
catch (error) {
|
|
1405
|
+
yield this.runNativeTeardown(uuid, blePlxManager, () => __awaiter(this, void 0, void 0, function* () {
|
|
1406
|
+
yield this.runBestEffortNativeOperation('bond failure: cancel manager connection', () => blePlxManager.cancelDeviceConnection(uuid));
|
|
1407
|
+
}));
|
|
1408
|
+
throw error;
|
|
1409
|
+
}
|
|
1410
|
+
this.androidLinkStartedAt.set(uuid, Date.now());
|
|
1411
|
+
}
|
|
1412
|
+
if (this.stopped)
|
|
1413
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
897
1414
|
if (!device) {
|
|
898
1415
|
const devices = yield blePlxManager.devices([uuid]);
|
|
899
1416
|
[device] = devices;
|
|
@@ -907,15 +1424,16 @@ class ReactNativeBleTransport {
|
|
|
907
1424
|
if (!device) {
|
|
908
1425
|
Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device: ', uuid);
|
|
909
1426
|
try {
|
|
910
|
-
device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid,
|
|
1427
|
+
device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, nativeConnectOptions));
|
|
1428
|
+
androidRefreshConnectRan = refreshAndroidGattCache;
|
|
911
1429
|
}
|
|
912
1430
|
catch (e) {
|
|
913
1431
|
Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device has error: ', e);
|
|
914
1432
|
if (shouldRethrowBleSetupError(e)) {
|
|
915
1433
|
throw e;
|
|
916
1434
|
}
|
|
917
|
-
if (e
|
|
918
|
-
|
|
1435
|
+
if (isMtuOrCancelledConnectError(e)) {
|
|
1436
|
+
skipPostConnectMtu = true;
|
|
919
1437
|
Log === null || Log === void 0 ? void 0 : Log.debug('first try to reconnect without params');
|
|
920
1438
|
device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, fallbackConnectOptions));
|
|
921
1439
|
}
|
|
@@ -931,19 +1449,27 @@ class ReactNativeBleTransport {
|
|
|
931
1449
|
if (!device) {
|
|
932
1450
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, 'unable to connect to device');
|
|
933
1451
|
}
|
|
1452
|
+
if (refreshAndroidGattCache &&
|
|
1453
|
+
!androidRefreshConnectRan &&
|
|
1454
|
+
(yield device.isConnected().catch(() => false))) {
|
|
1455
|
+
yield this.dropAndroidLink(uuid, blePlxManager, device, 'gatt cache refresh');
|
|
1456
|
+
if (this.stopped)
|
|
1457
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1458
|
+
}
|
|
934
1459
|
if (!(yield device.isConnected())) {
|
|
935
1460
|
Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device: ', uuid);
|
|
936
1461
|
const disconnectedDevice = device;
|
|
937
1462
|
try {
|
|
938
|
-
device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(
|
|
1463
|
+
device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(nativeConnectOptions));
|
|
1464
|
+
androidRefreshConnectRan = refreshAndroidGattCache;
|
|
939
1465
|
}
|
|
940
1466
|
catch (e) {
|
|
941
1467
|
Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device has error: ', e);
|
|
942
1468
|
if (shouldRethrowBleSetupError(e)) {
|
|
943
1469
|
throw e;
|
|
944
1470
|
}
|
|
945
|
-
if (e
|
|
946
|
-
|
|
1471
|
+
if (isMtuOrCancelledConnectError(e)) {
|
|
1472
|
+
skipPostConnectMtu = true;
|
|
947
1473
|
Log === null || Log === void 0 ? void 0 : Log.debug('second try to reconnect without params');
|
|
948
1474
|
try {
|
|
949
1475
|
device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
|
|
@@ -965,33 +1491,70 @@ class ReactNativeBleTransport {
|
|
|
965
1491
|
}
|
|
966
1492
|
}
|
|
967
1493
|
}
|
|
968
|
-
if (
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
1494
|
+
if (this.stopped)
|
|
1495
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1496
|
+
if (reactNative.Platform.OS === 'android' && !(yield device.isConnected().catch(() => false))) {
|
|
1497
|
+
const disconnectedDevice = device;
|
|
1498
|
+
yield this.runNativeTeardown(uuid, blePlxManager, () => __awaiter(this, void 0, void 0, function* () {
|
|
1499
|
+
yield Promise.all([
|
|
1500
|
+
this.runBestEffortNativeOperation('connect failure: cancel manager connection', () => blePlxManager.cancelDeviceConnection(uuid)),
|
|
1501
|
+
this.runBestEffortNativeOperation('connect failure: cancel device connection', () => disconnectedDevice.cancelConnection()),
|
|
1502
|
+
]);
|
|
1503
|
+
}));
|
|
1504
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, 'device is not connected');
|
|
1505
|
+
}
|
|
1506
|
+
if (this.stopped)
|
|
1507
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1508
|
+
let characteristics;
|
|
1509
|
+
if (isAndroid) {
|
|
1510
|
+
if (refreshAndroidGattCache) {
|
|
1511
|
+
characteristics = yield this.resolveCharacteristicsWithTimeout(uuid, device);
|
|
1512
|
+
if (androidRefreshConnectRan)
|
|
1513
|
+
this.androidGattCacheRefreshes.delete(uuid);
|
|
1514
|
+
if (this.stopped)
|
|
1515
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1516
|
+
}
|
|
1517
|
+
device = yield this.negotiateAndroidMtu(uuid, blePlxManager, device);
|
|
1518
|
+
}
|
|
1519
|
+
else if (!skipPostConnectMtu) {
|
|
1520
|
+
const mtuResult = yield resolveNegotiatedMtu(device, transactionId => blePlxManager.cancelTransaction(transactionId));
|
|
1521
|
+
device = mtuResult.device;
|
|
1522
|
+
if (mtuResult.timedOut) {
|
|
1523
|
+
if (this.stopped)
|
|
1524
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1525
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] post-connect MTU timed out, reconnecting without requesting MTU');
|
|
1526
|
+
const timedOutDevice = device;
|
|
1527
|
+
let mtuTeardownSettled = false;
|
|
1528
|
+
yield this.runNativeTeardown(uuid, blePlxManager, () => __awaiter(this, void 0, void 0, function* () {
|
|
1529
|
+
yield this.runBestEffortNativeOperation('mtu timeout: cancel device connection', () => timedOutDevice.cancelConnection());
|
|
1530
|
+
mtuTeardownSettled = true;
|
|
1531
|
+
}));
|
|
1532
|
+
if (this.stopped)
|
|
1533
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1534
|
+
if (!mtuTeardownSettled || this.blePlxManager !== blePlxManager) {
|
|
1535
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'BLE MTU cleanup timed out');
|
|
973
1536
|
}
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
yield onDeviceBondState(uuid);
|
|
1537
|
+
try {
|
|
1538
|
+
device = yield this.connectWithTimeout(uuid, () => timedOutDevice.connect(fallbackConnectOptions));
|
|
977
1539
|
}
|
|
978
|
-
|
|
979
|
-
|
|
1540
|
+
catch (error) {
|
|
1541
|
+
if (shouldRethrowBleSetupError(error))
|
|
1542
|
+
throw error;
|
|
1543
|
+
if ((error === null || error === void 0 ? void 0 : error.errorCode) === reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
|
|
1544
|
+
device = timedOutDevice;
|
|
1545
|
+
}
|
|
1546
|
+
else {
|
|
1547
|
+
remapError(error);
|
|
1548
|
+
}
|
|
980
1549
|
}
|
|
981
1550
|
}
|
|
982
|
-
catch (error) {
|
|
983
|
-
yield this.runNativeTeardown(uuid, blePlxManager, () => __awaiter(this, void 0, void 0, function* () {
|
|
984
|
-
yield Promise.all([
|
|
985
|
-
this.runBestEffortNativeOperation('bond failure: cancel manager connection', () => blePlxManager.cancelDeviceConnection(uuid)),
|
|
986
|
-
this.runBestEffortNativeOperation('bond failure: cancel device connection', () => connectedDevice.cancelConnection()),
|
|
987
|
-
]);
|
|
988
|
-
}));
|
|
989
|
-
throw error;
|
|
990
|
-
}
|
|
991
1551
|
}
|
|
992
|
-
|
|
1552
|
+
if (this.stopped)
|
|
1553
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
993
1554
|
const acquiredDevice = device;
|
|
994
|
-
const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, acquiredDevice);
|
|
1555
|
+
const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristicsWithTimeout(uuid, acquiredDevice));
|
|
1556
|
+
if (this.stopped)
|
|
1557
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
995
1558
|
const protocolHint = expectedProtocol
|
|
996
1559
|
? undefined
|
|
997
1560
|
: (_a = input.protocolHint) !== null && _a !== void 0 ? _a : this.deviceProtocolHints.get(uuid);
|
|
@@ -1002,7 +1565,13 @@ class ReactNativeBleTransport {
|
|
|
1002
1565
|
if (shouldMapProtocolV2StaleBond) {
|
|
1003
1566
|
this.acquiringProtocolV2.add(uuid);
|
|
1004
1567
|
}
|
|
1568
|
+
let linkEncryption;
|
|
1005
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
|
+
}
|
|
1006
1575
|
yield this.installTransportForAcquire(uuid, acquiredDevice, {
|
|
1007
1576
|
writeCharacteristic,
|
|
1008
1577
|
notifyCharacteristic,
|
|
@@ -1034,20 +1603,20 @@ class ReactNativeBleTransport {
|
|
|
1034
1603
|
const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint, () => __awaiter(this, void 0, void 0, function* () {
|
|
1035
1604
|
yield this.installTransportForAcquire(uuid, acquiredDevice);
|
|
1036
1605
|
}));
|
|
1606
|
+
if (this.stopped)
|
|
1607
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1037
1608
|
const currentTransport = transportCache[uuid];
|
|
1038
1609
|
if (!currentTransport) {
|
|
1039
1610
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
|
|
1040
1611
|
}
|
|
1041
1612
|
this.attachDisconnectSubscription(currentTransport, currentTransport.device, uuid);
|
|
1613
|
+
if (linkEncryption === 'unresolved') {
|
|
1614
|
+
markBleLinkEncrypted(uuid);
|
|
1615
|
+
}
|
|
1042
1616
|
return { uuid, protocolType };
|
|
1043
1617
|
}
|
|
1044
1618
|
catch (error) {
|
|
1045
|
-
|
|
1046
|
-
yield this.disconnectUnlocked(uuid);
|
|
1047
|
-
}
|
|
1048
|
-
else {
|
|
1049
|
-
yield this.releaseUnlocked(uuid, true);
|
|
1050
|
-
}
|
|
1619
|
+
yield this.disconnectUnlocked(uuid);
|
|
1051
1620
|
throw error;
|
|
1052
1621
|
}
|
|
1053
1622
|
finally {
|
|
@@ -1059,7 +1628,7 @@ class ReactNativeBleTransport {
|
|
|
1059
1628
|
let bufferLength = 0;
|
|
1060
1629
|
let buffer$1 = [];
|
|
1061
1630
|
const subscription = characteristic.monitor((error, c) => {
|
|
1062
|
-
var _a, _b, _c, _d, _e, _f
|
|
1631
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1063
1632
|
const isCurrentMonitor = this.monitorTokens.get(uuid) === monitorToken;
|
|
1064
1633
|
if (error) {
|
|
1065
1634
|
Log === null || Log === void 0 ? void 0 : Log.debug(`error monitor ${characteristic.uuid}, deviceId: ${characteristic.deviceID}: ${error}`);
|
|
@@ -1076,16 +1645,16 @@ class ReactNativeBleTransport {
|
|
|
1076
1645
|
this.rememberStaleBondError(uuid, toBleStaleBondHardwareError(error));
|
|
1077
1646
|
return;
|
|
1078
1647
|
}
|
|
1648
|
+
if (reactNative.Platform.OS === 'android' && isStaleGattTableNotifyReason(error.reason)) {
|
|
1649
|
+
this.androidGattCacheRefreshes.add(uuid);
|
|
1650
|
+
}
|
|
1079
1651
|
if (this.getActiveProtocol(uuid) === 'V2') {
|
|
1080
1652
|
let errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
|
|
1081
1653
|
if ((_a = error.reason) === null || _a === void 0 ? void 0 : _a.includes('The connection has timed out unexpectedly')) {
|
|
1082
1654
|
errorCode = hdShared.HardwareErrorCode.BleTimeoutError;
|
|
1083
1655
|
}
|
|
1084
|
-
else if ((
|
|
1085
|
-
((
|
|
1086
|
-
((_d = error.reason) === null || _d === void 0 ? void 0 : _d.includes('The handle is invalid')) ||
|
|
1087
|
-
((_e = error.reason) === null || _e === void 0 ? void 0 : _e.includes('Writing is not permitted')) ||
|
|
1088
|
-
((_f = error.reason) === null || _f === void 0 ? void 0 : _f.includes('notify change failed for device'))) {
|
|
1656
|
+
else if (isStaleGattTableNotifyReason(error.reason) ||
|
|
1657
|
+
((_b = error.reason) === null || _b === void 0 ? void 0 : _b.includes('notify change failed for device'))) {
|
|
1089
1658
|
errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure;
|
|
1090
1659
|
}
|
|
1091
1660
|
this.rejectProtocolV2Frames(uuid, hdShared.ERRORS.TypedError(errorCode));
|
|
@@ -1093,14 +1662,11 @@ class ReactNativeBleTransport {
|
|
|
1093
1662
|
}
|
|
1094
1663
|
if (this.runPromise && this.runPromiseDeviceId === uuid) {
|
|
1095
1664
|
let ERROR = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
|
|
1096
|
-
if ((
|
|
1665
|
+
if ((_c = error.reason) === null || _c === void 0 ? void 0 : _c.includes('The connection has timed out unexpectedly')) {
|
|
1097
1666
|
ERROR = hdShared.HardwareErrorCode.BleTimeoutError;
|
|
1098
1667
|
}
|
|
1099
|
-
if ((
|
|
1100
|
-
((
|
|
1101
|
-
((_k = error.reason) === null || _k === void 0 ? void 0 : _k.includes('The handle is invalid')) ||
|
|
1102
|
-
((_l = error.reason) === null || _l === void 0 ? void 0 : _l.includes('Writing is not permitted')) ||
|
|
1103
|
-
((_m = error.reason) === null || _m === void 0 ? void 0 : _m.includes('notify change failed for device'))) {
|
|
1668
|
+
if (isStaleGattTableNotifyReason(error.reason) ||
|
|
1669
|
+
((_d = error.reason) === null || _d === void 0 ? void 0 : _d.includes('notify change failed for device'))) {
|
|
1104
1670
|
const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure);
|
|
1105
1671
|
this.runPromise.reject(notifyError);
|
|
1106
1672
|
Log === null || Log === void 0 ? void 0 : Log.debug(`${hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure} ${error.message} ${error.reason}`);
|
|
@@ -1142,7 +1708,7 @@ class ReactNativeBleTransport {
|
|
|
1142
1708
|
bufferLength = 0;
|
|
1143
1709
|
buffer$1 = [];
|
|
1144
1710
|
if (this.runPromiseDeviceId === uuid) {
|
|
1145
|
-
(
|
|
1711
|
+
(_e = this.runPromise) === null || _e === void 0 ? void 0 : _e.resolve(value.toString('hex'));
|
|
1146
1712
|
}
|
|
1147
1713
|
}
|
|
1148
1714
|
}
|
|
@@ -1153,7 +1719,7 @@ class ReactNativeBleTransport {
|
|
|
1153
1719
|
this.rejectProtocolV2Frames(uuid, notifyError);
|
|
1154
1720
|
}
|
|
1155
1721
|
else if (this.runPromiseDeviceId === uuid) {
|
|
1156
|
-
(
|
|
1722
|
+
(_f = this.runPromise) === null || _f === void 0 ? void 0 : _f.reject(notifyError);
|
|
1157
1723
|
}
|
|
1158
1724
|
}
|
|
1159
1725
|
}, notifyTransactionId);
|
|
@@ -1248,14 +1814,64 @@ class ReactNativeBleTransport {
|
|
|
1248
1814
|
if (!protocol) {
|
|
1249
1815
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${uuid}`);
|
|
1250
1816
|
}
|
|
1251
|
-
if (protocol === 'V2') {
|
|
1252
|
-
return this.callProtocolV2(uuid, name, data, options);
|
|
1253
|
-
}
|
|
1254
1817
|
const forceRun = name === 'Initialize' || name === 'Cancel';
|
|
1255
|
-
if (this.runPromise && !forceRun) {
|
|
1818
|
+
if (protocol !== 'V2' && this.runPromise && !forceRun) {
|
|
1256
1819
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportCallInProgress);
|
|
1257
1820
|
}
|
|
1258
|
-
|
|
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;
|
|
1259
1875
|
});
|
|
1260
1876
|
}
|
|
1261
1877
|
callProtocolV1(uuid, name, data, options) {
|
|
@@ -1429,13 +2045,14 @@ class ReactNativeBleTransport {
|
|
|
1429
2045
|
return check.call(jsonData);
|
|
1430
2046
|
}
|
|
1431
2047
|
catch (e) {
|
|
1432
|
-
|
|
1433
|
-
|
|
2048
|
+
const isProbeTimeout = (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS &&
|
|
2049
|
+
(name === 'GetFeatures' || name === 'Initialize');
|
|
2050
|
+
if (isProbeTimeout) {
|
|
2051
|
+
Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] Protocol V1 ${name} probe call failed:`, e);
|
|
1434
2052
|
}
|
|
1435
2053
|
else {
|
|
1436
2054
|
Log === null || Log === void 0 ? void 0 : Log.error('call error: ', e);
|
|
1437
2055
|
}
|
|
1438
|
-
const isProbeTimeout = name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS;
|
|
1439
2056
|
const isStaleCall = this.runPromise !== runPromise;
|
|
1440
2057
|
if (!isProbeTimeout &&
|
|
1441
2058
|
!isStaleCall &&
|
|
@@ -1455,7 +2072,46 @@ class ReactNativeBleTransport {
|
|
|
1455
2072
|
});
|
|
1456
2073
|
}
|
|
1457
2074
|
stop() {
|
|
2075
|
+
var _a;
|
|
2076
|
+
if (this.stopPromise)
|
|
2077
|
+
return this.stopPromise;
|
|
1458
2078
|
this.stopped = true;
|
|
2079
|
+
this.bondAbortController.abort();
|
|
2080
|
+
const deviceIds = new Set([
|
|
2081
|
+
...this.monitorTokens.keys(),
|
|
2082
|
+
...this.sessionProtocols.keys(),
|
|
2083
|
+
...this.lifecycleOperations.keys(),
|
|
2084
|
+
...(this.runPromiseDeviceId ? [this.runPromiseDeviceId] : []),
|
|
2085
|
+
]);
|
|
2086
|
+
const scans = Array.from(this.scanCleanups, cleanup => cleanup());
|
|
2087
|
+
this.androidPriorityResetTimers.forEach(timeout => clearTimeout(timeout));
|
|
2088
|
+
this.androidPriorityResetTimers.clear();
|
|
2089
|
+
this.androidHighPriorityDevices.clear();
|
|
2090
|
+
this.androidLinkStartedAt.clear();
|
|
2091
|
+
stopBleKeyMissingTracking();
|
|
2092
|
+
stopBleEncryptionTracking();
|
|
2093
|
+
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
2094
|
+
(_a = this.runPromise) === null || _a === void 0 ? void 0 : _a.reject(error);
|
|
2095
|
+
this.runPromise = null;
|
|
2096
|
+
this.runPromiseDeviceId = null;
|
|
2097
|
+
deviceIds.forEach(uuid => this.rejectProtocolV2Frames(uuid, error));
|
|
2098
|
+
const manager = this.blePlxManager;
|
|
2099
|
+
const pendingConnections = manager
|
|
2100
|
+
? Array.from(this.lifecycleOperations.keys(), uuid => this.runNativeTeardown(uuid, manager, () => __awaiter(this, void 0, void 0, function* () {
|
|
2101
|
+
yield this.runBestEffortNativeOperation('stop: cancel pending device connection', () => manager.cancelDeviceConnection(uuid));
|
|
2102
|
+
})))
|
|
2103
|
+
: [];
|
|
2104
|
+
this.stopPromise = Promise.all([
|
|
2105
|
+
...scans,
|
|
2106
|
+
...pendingConnections,
|
|
2107
|
+
...Array.from(deviceIds, uuid => this.disconnect(uuid)),
|
|
2108
|
+
]).then(() => __awaiter(this, void 0, void 0, function* () {
|
|
2109
|
+
yield this.protocolV2Links.invalidateAllLinks('React Native BLE transport stopped');
|
|
2110
|
+
yield this.waitForManagerReset();
|
|
2111
|
+
this.blePlxManager = undefined;
|
|
2112
|
+
this.emitter = undefined;
|
|
2113
|
+
}));
|
|
2114
|
+
return this.stopPromise;
|
|
1459
2115
|
}
|
|
1460
2116
|
disconnect(session) {
|
|
1461
2117
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -1585,13 +2241,93 @@ class ReactNativeBleTransport {
|
|
|
1585
2241
|
});
|
|
1586
2242
|
}
|
|
1587
2243
|
cancel() {
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
2244
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2245
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native transport cancel');
|
|
2246
|
+
const pending = this.runPromise;
|
|
2247
|
+
const deviceId = this.runPromiseDeviceId;
|
|
2248
|
+
if (pending) {
|
|
2249
|
+
pending.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallQueueActionCancelled));
|
|
2250
|
+
if (this.runPromise === pending) {
|
|
2251
|
+
this.runPromise = null;
|
|
2252
|
+
this.runPromiseDeviceId = null;
|
|
2253
|
+
}
|
|
2254
|
+
if (deviceId)
|
|
2255
|
+
yield this.disconnect(deviceId);
|
|
2256
|
+
}
|
|
2257
|
+
});
|
|
2258
|
+
}
|
|
2259
|
+
negotiateAndroidMtu(uuid, manager, device) {
|
|
2260
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2261
|
+
if (!shouldRefreshNegotiatedMtu(device.mtu))
|
|
2262
|
+
return device;
|
|
2263
|
+
const startedAt = Date.now();
|
|
2264
|
+
let timer;
|
|
2265
|
+
let timedOut = false;
|
|
2266
|
+
let negotiated = device;
|
|
2267
|
+
let failure;
|
|
2268
|
+
try {
|
|
2269
|
+
negotiated = yield Promise.race([
|
|
2270
|
+
device.requestMTU(ANDROID_REQUEST_MTU, `${device.id}:mtu:connected:0:${startedAt}`),
|
|
2271
|
+
new Promise((_, reject) => {
|
|
2272
|
+
timer = setTimeout(() => {
|
|
2273
|
+
timedOut = true;
|
|
2274
|
+
reject(new Error(`BLE MTU exchange timeout after ${ANDROID_MTU_EXCHANGE_TIMEOUT_MS}ms`));
|
|
2275
|
+
}, ANDROID_MTU_EXCHANGE_TIMEOUT_MS);
|
|
2276
|
+
}),
|
|
2277
|
+
]);
|
|
2278
|
+
}
|
|
2279
|
+
catch (error) {
|
|
2280
|
+
failure = error instanceof Error ? error.message : String(error);
|
|
2281
|
+
}
|
|
2282
|
+
finally {
|
|
2283
|
+
if (timer)
|
|
2284
|
+
clearTimeout(timer);
|
|
2285
|
+
}
|
|
2286
|
+
Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] BLE MTU exchange ${failure ? 'failed' : 'completed'}`, {
|
|
2287
|
+
connectIdSuffix: uuid.slice(-8),
|
|
2288
|
+
elapsedMs: Date.now() - startedAt,
|
|
2289
|
+
timedOut,
|
|
2290
|
+
actual: negotiated.mtu,
|
|
2291
|
+
error: failure,
|
|
2292
|
+
});
|
|
2293
|
+
if (this.stopped)
|
|
2294
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
2295
|
+
if (!timedOut && !isKnownDefaultMtu(negotiated.mtu))
|
|
2296
|
+
return negotiated;
|
|
2297
|
+
const resetManager = this.abandonStalledConnection(uuid, timedOut ? 'mtu-backstop' : 'mtu-default');
|
|
2298
|
+
yield this.dropAndroidLink(uuid, manager, negotiated, timedOut ? 'mtu exchange timeout' : 'default mtu');
|
|
2299
|
+
if (resetManager)
|
|
2300
|
+
throw this.createWedgedBleSetupError();
|
|
2301
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, timedOut
|
|
2302
|
+
? 'BLE MTU exchange did not complete, reconnecting on a fresh link'
|
|
2303
|
+
: `BLE link stayed at the default MTU ${negotiated.mtu}, reconnecting on a fresh link`);
|
|
2304
|
+
});
|
|
2305
|
+
}
|
|
2306
|
+
dropAndroidLink(uuid, manager, device, reason) {
|
|
2307
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2308
|
+
yield this.runNativeTeardown(uuid, manager, () => __awaiter(this, void 0, void 0, function* () {
|
|
2309
|
+
yield Promise.all([
|
|
2310
|
+
this.runBestEffortNativeOperation(`${reason}: cancel manager connection`, () => manager.cancelDeviceConnection(uuid)),
|
|
2311
|
+
this.runBestEffortNativeOperation(`${reason}: cancel device connection`, () => device.cancelConnection()),
|
|
2312
|
+
]);
|
|
2313
|
+
}));
|
|
2314
|
+
const startedAt = Date.now();
|
|
2315
|
+
while (!this.stopped && Date.now() - startedAt < ANDROID_LINK_DROP_QUIET_MS) {
|
|
2316
|
+
yield delay(ANDROID_LINK_DROP_POLL_MS);
|
|
2317
|
+
}
|
|
2318
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE link drop', {
|
|
2319
|
+
connectIdSuffix: uuid.slice(-8),
|
|
2320
|
+
reason,
|
|
2321
|
+
stopped: this.stopped,
|
|
2322
|
+
});
|
|
2323
|
+
});
|
|
1592
2324
|
}
|
|
1593
2325
|
connectWithTimeout(uuid, connect) {
|
|
1594
2326
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2327
|
+
if (this.stopped)
|
|
2328
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
2329
|
+
const startedAt = Date.now();
|
|
2330
|
+
let succeeded = false;
|
|
1595
2331
|
let timer;
|
|
1596
2332
|
let timedOut = false;
|
|
1597
2333
|
const pending = connect();
|
|
@@ -1606,6 +2342,7 @@ class ReactNativeBleTransport {
|
|
|
1606
2342
|
}, BLE_CONNECT_TIMEOUT_MS);
|
|
1607
2343
|
}),
|
|
1608
2344
|
]);
|
|
2345
|
+
succeeded = true;
|
|
1609
2346
|
return result;
|
|
1610
2347
|
}
|
|
1611
2348
|
catch (error) {
|
|
@@ -1620,11 +2357,19 @@ class ReactNativeBleTransport {
|
|
|
1620
2357
|
finally {
|
|
1621
2358
|
if (timer)
|
|
1622
2359
|
clearTimeout(timer);
|
|
2360
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] connect completed', {
|
|
2361
|
+
connectIdSuffix: uuid.slice(-8),
|
|
2362
|
+
elapsedMs: Date.now() - startedAt,
|
|
2363
|
+
succeeded,
|
|
2364
|
+
backstopExpired: timedOut,
|
|
2365
|
+
});
|
|
1623
2366
|
}
|
|
1624
2367
|
});
|
|
1625
2368
|
}
|
|
1626
2369
|
resolveCharacteristicsWithTimeout(uuid, device) {
|
|
1627
2370
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2371
|
+
const startedAt = Date.now();
|
|
2372
|
+
let succeeded = false;
|
|
1628
2373
|
let timer;
|
|
1629
2374
|
let timedOut = false;
|
|
1630
2375
|
const pending = this.resolveCharacteristics(device);
|
|
@@ -1640,6 +2385,7 @@ class ReactNativeBleTransport {
|
|
|
1640
2385
|
}),
|
|
1641
2386
|
]);
|
|
1642
2387
|
this.connectionSetupTimeoutCounts.delete(uuid);
|
|
2388
|
+
succeeded = true;
|
|
1643
2389
|
return result;
|
|
1644
2390
|
}
|
|
1645
2391
|
catch (error) {
|
|
@@ -1649,11 +2395,20 @@ class ReactNativeBleTransport {
|
|
|
1649
2395
|
throw this.createWedgedBleSetupError();
|
|
1650
2396
|
}
|
|
1651
2397
|
}
|
|
2398
|
+
if (reactNative.Platform.OS === 'android' && isMissingGattShapeError(error)) {
|
|
2399
|
+
this.androidGattCacheRefreshes.add(uuid);
|
|
2400
|
+
}
|
|
1652
2401
|
throw error;
|
|
1653
2402
|
}
|
|
1654
2403
|
finally {
|
|
1655
2404
|
if (timer)
|
|
1656
2405
|
clearTimeout(timer);
|
|
2406
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] GATT setup completed', {
|
|
2407
|
+
connectIdSuffix: uuid.slice(-8),
|
|
2408
|
+
elapsedMs: Date.now() - startedAt,
|
|
2409
|
+
succeeded,
|
|
2410
|
+
backstopExpired: timedOut,
|
|
2411
|
+
});
|
|
1657
2412
|
}
|
|
1658
2413
|
});
|
|
1659
2414
|
}
|
|
@@ -1755,6 +2510,8 @@ class ReactNativeBleTransport {
|
|
|
1755
2510
|
}
|
|
1756
2511
|
}
|
|
1757
2512
|
resetPlxManager() {
|
|
2513
|
+
if (bleManagerResetPromise)
|
|
2514
|
+
return;
|
|
1758
2515
|
const manager = this.blePlxManager;
|
|
1759
2516
|
this.blePlxManager = undefined;
|
|
1760
2517
|
const reason = 'React Native BLE manager reset';
|
|
@@ -1792,16 +2549,25 @@ class ReactNativeBleTransport {
|
|
|
1792
2549
|
this.acquiringProtocolV2.clear();
|
|
1793
2550
|
this.sessionProtocols.clear();
|
|
1794
2551
|
this.protocolReprobeFailures.clear();
|
|
2552
|
+
this.silentDetections.clear();
|
|
1795
2553
|
this.writeTimeoutCounts.clear();
|
|
1796
2554
|
this.connectionSetupTimeoutCounts.clear();
|
|
1797
2555
|
this.monitorTokens.clear();
|
|
1798
2556
|
this.protocolV2Assemblers.clear();
|
|
2557
|
+
let reset;
|
|
1799
2558
|
try {
|
|
1800
|
-
manager === null || manager === void 0 ? void 0 : manager.destroy();
|
|
2559
|
+
reset = Promise.resolve(manager === null || manager === void 0 ? void 0 : manager.destroy());
|
|
1801
2560
|
}
|
|
1802
2561
|
catch (error) {
|
|
1803
|
-
|
|
2562
|
+
reset = Promise.reject(error);
|
|
1804
2563
|
}
|
|
2564
|
+
bleManagerResetPromise = reset;
|
|
2565
|
+
reset.then(() => {
|
|
2566
|
+
if (bleManagerResetPromise === reset)
|
|
2567
|
+
bleManagerResetPromise = undefined;
|
|
2568
|
+
}, error => {
|
|
2569
|
+
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE manager destroy failed:', error);
|
|
2570
|
+
});
|
|
1805
2571
|
}
|
|
1806
2572
|
createProtocolMismatchError(expected) {
|
|
1807
2573
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
|
|
@@ -1857,6 +2623,7 @@ class ReactNativeBleTransport {
|
|
|
1857
2623
|
!protocolHint &&
|
|
1858
2624
|
reprobeFailures < PROTOCOL_REPROBE_FALLBACK_ATTEMPTS;
|
|
1859
2625
|
const probeOrder = trustSessionProtocol ? [sessionProtocol] : fullProbeOrder;
|
|
2626
|
+
yield this.wakeSilentProtocolV1Device(uuid, probeOrder);
|
|
1860
2627
|
for (let i = 0; i < probeOrder.length; i += 1) {
|
|
1861
2628
|
const protocol = probeOrder[i];
|
|
1862
2629
|
if (i > 0) {
|
|
@@ -1876,6 +2643,7 @@ class ReactNativeBleTransport {
|
|
|
1876
2643
|
this.confirmedProtocolV2.add(uuid);
|
|
1877
2644
|
}
|
|
1878
2645
|
this.protocolReprobeFailures.delete(uuid);
|
|
2646
|
+
this.silentDetections.delete(uuid);
|
|
1879
2647
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
|
|
1880
2648
|
deviceId: uuid,
|
|
1881
2649
|
protocol,
|
|
@@ -1884,6 +2652,8 @@ class ReactNativeBleTransport {
|
|
|
1884
2652
|
return protocol;
|
|
1885
2653
|
}
|
|
1886
2654
|
}
|
|
2655
|
+
if (!this.silentDetections.has(uuid))
|
|
2656
|
+
this.silentDetections.set(uuid, 'silent');
|
|
1887
2657
|
if (trustSessionProtocol) {
|
|
1888
2658
|
this.protocolReprobeFailures.set(uuid, reprobeFailures + 1);
|
|
1889
2659
|
}
|
|
@@ -1895,6 +2665,30 @@ class ReactNativeBleTransport {
|
|
|
1895
2665
|
throw this.createProtocolDetectionError();
|
|
1896
2666
|
});
|
|
1897
2667
|
}
|
|
2668
|
+
wakeSilentProtocolV1Device(uuid, probeOrder) {
|
|
2669
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2670
|
+
if (reactNative.Platform.OS !== 'android' ||
|
|
2671
|
+
probeOrder[0] !== 'V1' ||
|
|
2672
|
+
this.silentDetections.get(uuid) !== 'silent') {
|
|
2673
|
+
return;
|
|
2674
|
+
}
|
|
2675
|
+
this.silentDetections.set(uuid, 'woken');
|
|
2676
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] sending Protocol V1 Initialize wake', {
|
|
2677
|
+
connectIdSuffix: uuid.slice(-8),
|
|
2678
|
+
});
|
|
2679
|
+
try {
|
|
2680
|
+
this.probingProtocols.set(uuid, 'V1');
|
|
2681
|
+
yield this.callProtocolV1(uuid, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
|
|
2682
|
+
}
|
|
2683
|
+
catch (error) {
|
|
2684
|
+
if (shouldRethrowProtocolProbeError(error))
|
|
2685
|
+
throw error;
|
|
2686
|
+
}
|
|
2687
|
+
finally {
|
|
2688
|
+
this.clearProbeProtocol(uuid, 'V1');
|
|
2689
|
+
}
|
|
2690
|
+
});
|
|
2691
|
+
}
|
|
1898
2692
|
resetProbeStateAfterProtocolProbe(uuid, protocol) {
|
|
1899
2693
|
var _a, _b, _c;
|
|
1900
2694
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -1951,7 +2745,7 @@ class ReactNativeBleTransport {
|
|
|
1951
2745
|
catch (error) {
|
|
1952
2746
|
this.clearProbeProtocol(uuid, 'V1');
|
|
1953
2747
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe failed:', error);
|
|
1954
|
-
if (
|
|
2748
|
+
if (shouldRethrowProtocolProbeError(error)) {
|
|
1955
2749
|
throw error;
|
|
1956
2750
|
}
|
|
1957
2751
|
return false;
|
|
@@ -1977,7 +2771,7 @@ class ReactNativeBleTransport {
|
|
|
1977
2771
|
(_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1978
2772
|
this.resetProtocolV2Frames(uuid);
|
|
1979
2773
|
},
|
|
1980
|
-
shouldRethrow:
|
|
2774
|
+
shouldRethrow: shouldRethrowProtocolProbeError,
|
|
1981
2775
|
});
|
|
1982
2776
|
if (!detected) {
|
|
1983
2777
|
this.clearProbeProtocol(uuid, 'V2');
|
|
@@ -2104,6 +2898,9 @@ class ReactNativeBleTransport {
|
|
|
2104
2898
|
this.rememberStaleBondError(uuid, bondError);
|
|
2105
2899
|
throw bondError;
|
|
2106
2900
|
}
|
|
2901
|
+
if (isNativeBleDisconnectError(error)) {
|
|
2902
|
+
throw toBleDisconnectHardwareError(error);
|
|
2903
|
+
}
|
|
2107
2904
|
if (getFirmwareUploadWriteRetryType(error) !== 'congested' ||
|
|
2108
2905
|
attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
|
|
2109
2906
|
throw error;
|
|
@@ -2146,6 +2943,7 @@ class ReactNativeBleTransport {
|
|
|
2146
2943
|
if (!this._messages || !this._messagesV2) {
|
|
2147
2944
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
2148
2945
|
}
|
|
2946
|
+
const isProtocolProbe = this.probingProtocols.get(uuid) === 'V2';
|
|
2149
2947
|
const callOptions = options;
|
|
2150
2948
|
const highThroughputWrite = transport.isProtocolV2HighThroughputCall(name);
|
|
2151
2949
|
if (highThroughputWrite) {
|
|
@@ -2186,6 +2984,14 @@ class ReactNativeBleTransport {
|
|
|
2186
2984
|
}
|
|
2187
2985
|
catch (e) {
|
|
2188
2986
|
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] Protocol V2 call error:', e);
|
|
2987
|
+
if (!isProtocolProbe &&
|
|
2988
|
+
(e === null || e === void 0 ? void 0 : e.errorCode) === hdShared.HardwareErrorCode.BleTimeoutError &&
|
|
2989
|
+
!this.monitorTokens.has(uuid)) {
|
|
2990
|
+
yield this.runLifecycleOperation(uuid, () => __awaiter(this, void 0, void 0, function* () {
|
|
2991
|
+
if (!this.monitorTokens.has(uuid))
|
|
2992
|
+
yield this.disconnectUnlocked(uuid);
|
|
2993
|
+
}));
|
|
2994
|
+
}
|
|
2189
2995
|
throw e;
|
|
2190
2996
|
}
|
|
2191
2997
|
finally {
|
|
@@ -2200,7 +3006,7 @@ class ReactNativeBleTransport {
|
|
|
2200
3006
|
const transport = this.getCachedTransport(uuid);
|
|
2201
3007
|
if (!shouldRefreshNegotiatedMtu(transport.mtuSize))
|
|
2202
3008
|
return;
|
|
2203
|
-
const refreshedDevice = yield requestNegotiatedMtu(transport.device, 'highThroughput', 1);
|
|
3009
|
+
const { device: refreshedDevice } = yield requestNegotiatedMtu(transport.device, 'highThroughput', 1, transactionId => { var _a; return (_a = this.blePlxManager) === null || _a === void 0 ? void 0 : _a.cancelTransaction(transactionId); });
|
|
2204
3010
|
transport.device = refreshedDevice;
|
|
2205
3011
|
transport.mtuSize =
|
|
2206
3012
|
typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport.mtuSize;
|
|
@@ -2317,6 +3123,12 @@ class ReactNativeBleTransport {
|
|
|
2317
3123
|
}
|
|
2318
3124
|
}
|
|
2319
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;
|
|
3129
|
+
exports.ANDROID_LINK_DROP_QUIET_MS = ANDROID_LINK_DROP_QUIET_MS;
|
|
3130
|
+
exports.ANDROID_MTU_EXCHANGE_TIMEOUT_MS = ANDROID_MTU_EXCHANGE_TIMEOUT_MS;
|
|
3131
|
+
exports.ANDROID_SYSTEM_REPAIR_TIMEOUT_MS = ANDROID_SYSTEM_REPAIR_TIMEOUT_MS;
|
|
2320
3132
|
exports.BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD;
|
|
2321
3133
|
exports.BLE_CONNECT_TIMEOUT_MS = BLE_CONNECT_TIMEOUT_MS;
|
|
2322
3134
|
exports.BLE_GATT_SETUP_TIMEOUT_MS = BLE_GATT_SETUP_TIMEOUT_MS;
|