@onekeyfe/hd-transport-react-native 1.2.3-alpha.3 → 1.2.3-alpha.5
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 +1 -1
- package/dist/BleManager.d.ts.map +1 -1
- package/dist/bleNativeDisconnect.d.ts +3 -0
- package/dist/bleNativeDisconnect.d.ts.map +1 -0
- package/dist/index.d.ts +35 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +634 -152
- package/dist/subscribeBleOn.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/BleManager.ts +73 -5
- package/src/__tests__/bleNativeDisconnect.test.ts +54 -0
- package/src/__tests__/connectTimeout.test.ts +465 -3
- package/src/__tests__/enumerate.test.ts +36 -1
- package/src/__tests__/protocolV2Link.test.ts +976 -62
- package/src/__tests__/staleCallTimeout.test.ts +34 -5
- package/src/__tests__/subscribeBleOn.test.ts +111 -0
- package/src/bleNativeDisconnect.ts +41 -0
- package/src/index.ts +647 -159
- package/src/subscribeBleOn.ts +26 -10
package/dist/index.js
CHANGED
|
@@ -57,36 +57,73 @@ const bleLogger = {
|
|
|
57
57
|
};
|
|
58
58
|
|
|
59
59
|
const Logger = bleLogger;
|
|
60
|
+
const bondFailureReasons = {
|
|
61
|
+
1: 'authentication_failed',
|
|
62
|
+
2: 'rejected',
|
|
63
|
+
3: 'canceled',
|
|
64
|
+
4: 'device_unreachable',
|
|
65
|
+
5: 'discovery_in_progress',
|
|
66
|
+
6: 'timeout',
|
|
67
|
+
7: 'repeated_attempts',
|
|
68
|
+
8: 'remote_canceled',
|
|
69
|
+
9: 'removed',
|
|
70
|
+
};
|
|
60
71
|
const getConnectedDeviceIds = (serviceUuids) => BleUtils__default["default"].getConnectedPeripherals(serviceUuids);
|
|
61
72
|
const pairDevice = (macAddress) => BleUtils__default["default"].pairDevice(macAddress);
|
|
62
|
-
const onDeviceBondState = (bleMacAddress) => new Promise((resolve, reject) => {
|
|
73
|
+
const onDeviceBondState = (bleMacAddress, signal) => new Promise((resolve, reject) => {
|
|
74
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
75
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
63
78
|
const cleanup = () => {
|
|
64
79
|
if (timeout) {
|
|
65
80
|
clearTimeout(timeout);
|
|
66
81
|
}
|
|
67
82
|
if (cleanupListener)
|
|
68
83
|
cleanupListener();
|
|
84
|
+
signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', onAbort);
|
|
85
|
+
};
|
|
86
|
+
const onAbort = () => {
|
|
87
|
+
cleanup();
|
|
88
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected));
|
|
69
89
|
};
|
|
90
|
+
signal === null || signal === void 0 ? void 0 : signal.addEventListener('abort', onAbort, { once: true });
|
|
70
91
|
const timeout = setTimeout(() => {
|
|
71
92
|
cleanup();
|
|
72
|
-
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, '
|
|
93
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'Bluetooth pairing timed out', {
|
|
94
|
+
phase: 'bond',
|
|
95
|
+
reason: 'timeout',
|
|
96
|
+
}));
|
|
73
97
|
}, 60 * 1000);
|
|
74
98
|
const cleanupListener = BleUtils__default["default"].onDeviceBondState(peripheral => {
|
|
75
|
-
var _a;
|
|
99
|
+
var _a, _b;
|
|
76
100
|
if (((_a = peripheral.id) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== bleMacAddress.toLowerCase()) {
|
|
77
101
|
return;
|
|
78
102
|
}
|
|
79
103
|
const { bondState } = peripheral;
|
|
80
104
|
const hasBonded = bondState.preState === 'BOND_BONDING' && bondState.state === 'BOND_BONDED';
|
|
81
|
-
const
|
|
105
|
+
const hasFailed = bondState.preState === 'BOND_BONDING' && bondState.state === 'BOND_NONE';
|
|
82
106
|
Logger.debug('onDeviceBondState bondState:', bondState);
|
|
83
107
|
if (hasBonded) {
|
|
84
108
|
cleanup();
|
|
85
109
|
resolve(peripheral);
|
|
86
110
|
}
|
|
87
|
-
else if (
|
|
111
|
+
else if (hasFailed) {
|
|
88
112
|
cleanup();
|
|
89
|
-
|
|
113
|
+
const nativeReason = 'reason' in bondState && typeof bondState.reason === 'number'
|
|
114
|
+
? bondState.reason
|
|
115
|
+
: undefined;
|
|
116
|
+
const reason = nativeReason === undefined ? 'unknown' : (_b = bondFailureReasons[nativeReason]) !== null && _b !== void 0 ? _b : 'unknown';
|
|
117
|
+
const params = Object.assign({ phase: 'bond', reason }, (nativeReason === undefined ? {} : { nativeReason }));
|
|
118
|
+
if (reason === 'timeout') {
|
|
119
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'Bluetooth pairing timed out', params));
|
|
120
|
+
}
|
|
121
|
+
else if (reason === 'canceled') {
|
|
122
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceBondedCanceled, 'Bluetooth pairing canceled', params));
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'Bluetooth pairing failed', params));
|
|
126
|
+
}
|
|
90
127
|
}
|
|
91
128
|
});
|
|
92
129
|
});
|
|
@@ -176,24 +213,66 @@ const timer = process.env.NODE_ENV === 'development'
|
|
|
176
213
|
|
|
177
214
|
const subscribeBleOn = (bleManager, ms = 1000) => new Promise((resolve, reject) => {
|
|
178
215
|
let done = false;
|
|
216
|
+
let cancelTimeout = () => undefined;
|
|
217
|
+
let removeSubscription = () => undefined;
|
|
218
|
+
const finish = (error) => {
|
|
219
|
+
if (done)
|
|
220
|
+
return;
|
|
221
|
+
done = true;
|
|
222
|
+
cancelTimeout();
|
|
223
|
+
removeSubscription();
|
|
224
|
+
if (error)
|
|
225
|
+
reject(error);
|
|
226
|
+
else
|
|
227
|
+
resolve();
|
|
228
|
+
};
|
|
179
229
|
const subscription = bleManager.onStateChange(state => {
|
|
180
230
|
if (state === 'PoweredOn') {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
231
|
+
finish();
|
|
232
|
+
}
|
|
233
|
+
else if (state === 'PoweredOff') {
|
|
234
|
+
finish(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePoweredOff));
|
|
235
|
+
}
|
|
236
|
+
else if (state === 'Unsupported') {
|
|
237
|
+
finish(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleUnsupported));
|
|
238
|
+
}
|
|
239
|
+
else if (state === 'Unauthorized') {
|
|
240
|
+
finish(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePermissionError));
|
|
187
241
|
}
|
|
188
242
|
}, true);
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
243
|
+
removeSubscription = () => subscription.remove();
|
|
244
|
+
if (done) {
|
|
245
|
+
removeSubscription();
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
cancelTimeout = timer.timeout(() => {
|
|
249
|
+
finish(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleScanError));
|
|
250
|
+
}, ms);
|
|
251
|
+
}
|
|
195
252
|
});
|
|
196
253
|
|
|
254
|
+
const BLE_PLX_DEVICE_DISCONNECTED = 201;
|
|
255
|
+
const IOS_PERIPHERAL_DISCONNECTED = 7;
|
|
256
|
+
const nativeErrorText$1 = (error) => [error.reason, error.message]
|
|
257
|
+
.filter((value) => typeof value === 'string')
|
|
258
|
+
.join(' ');
|
|
259
|
+
const isNativeBleDisconnectError = (error) => {
|
|
260
|
+
if (!error || typeof error !== 'object') {
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
const nativeError = error;
|
|
264
|
+
return (nativeError.errorCode === BLE_PLX_DEVICE_DISCONNECTED ||
|
|
265
|
+
nativeError.iosErrorCode === IOS_PERIPHERAL_DISCONNECTED ||
|
|
266
|
+
nativeErrorText$1(nativeError).toLowerCase().includes('was disconnected'));
|
|
267
|
+
};
|
|
268
|
+
const toBleDisconnectHardwareError = (error) => {
|
|
269
|
+
if ((error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleDeviceDisconnected) {
|
|
270
|
+
return error;
|
|
271
|
+
}
|
|
272
|
+
const nativeError = (error !== null && error !== void 0 ? error : {});
|
|
273
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected, nativeErrorText$1(nativeError) || undefined);
|
|
274
|
+
};
|
|
275
|
+
|
|
197
276
|
const ATT_INSUFFICIENT_AUTHENTICATION = 5;
|
|
198
277
|
const ATT_INSUFFICIENT_ENCRYPTION = 15;
|
|
199
278
|
const IOS_PEER_REMOVED_PAIRING_INFORMATION = 14;
|
|
@@ -258,6 +337,7 @@ class BleTransport {
|
|
|
258
337
|
const { check, ProtocolV1, parseConfigure } = transport__default["default"];
|
|
259
338
|
const Log = bleLogger;
|
|
260
339
|
const transportCache = {};
|
|
340
|
+
let bleManagerResetPromise;
|
|
261
341
|
const FIRMWARE_UPLOAD_WRITE_BURST_SIZE = reactNative.Platform.OS === 'ios' ? 4 : 5;
|
|
262
342
|
const FIRMWARE_UPLOAD_WRITE_PAUSE_MS = reactNative.Platform.OS === 'ios' ? 8 : 10;
|
|
263
343
|
const FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS = reactNative.Platform.OS === 'ios' ? 24 : 30;
|
|
@@ -316,6 +396,17 @@ const WEDGED_WRITE_MESSAGE = 'BLE write timeout after';
|
|
|
316
396
|
const isWedgedWriteError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleWriteCharacteristicError &&
|
|
317
397
|
typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' &&
|
|
318
398
|
error.message.startsWith(WEDGED_WRITE_MESSAGE);
|
|
399
|
+
const shouldRethrowProtocolProbeError = (error) => {
|
|
400
|
+
const code = error === null || error === void 0 ? void 0 : error.errorCode;
|
|
401
|
+
return (hdShared.isBleStaleBondHardwareError(error) ||
|
|
402
|
+
isNativeBleDisconnectError(error) ||
|
|
403
|
+
code === hdShared.HardwareErrorCode.BleDeviceNotBonded ||
|
|
404
|
+
code === hdShared.HardwareErrorCode.BleDeviceBondedCanceled ||
|
|
405
|
+
code === hdShared.HardwareErrorCode.BleDeviceDisconnected ||
|
|
406
|
+
code === hdShared.HardwareErrorCode.BleCharacteristicNotifyError ||
|
|
407
|
+
code === hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure ||
|
|
408
|
+
code === hdShared.HardwareErrorCode.BleWriteCharacteristicError);
|
|
409
|
+
};
|
|
319
410
|
const BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
|
|
320
411
|
const DEVICE_SCAN_TIMEOUT_MS = 3000;
|
|
321
412
|
const IOS_NOTIFY_READY_DELAY_MS = 150;
|
|
@@ -350,10 +441,10 @@ function getDeviceDisplayName(device) {
|
|
|
350
441
|
}
|
|
351
442
|
const IOS_REQUEST_MTU = 247;
|
|
352
443
|
const ANDROID_REQUEST_MTU = 517;
|
|
353
|
-
const BLE_MTU_REFRESH_RETRY_DELAY_MS = 200;
|
|
354
444
|
const ANDROID_HIGH_PRIORITY_IDLE_MS = 1000;
|
|
355
445
|
const getRequestedBleMtu = () => reactNative.Platform.OS === 'android' ? ANDROID_REQUEST_MTU : IOS_REQUEST_MTU;
|
|
356
446
|
const BLE_NATIVE_CONNECT_TIMEOUT_MS = 3000;
|
|
447
|
+
const BLE_MTU_REQUEST_TIMEOUT_MS = BLE_NATIVE_CONNECT_TIMEOUT_MS;
|
|
357
448
|
const connectOptions = {
|
|
358
449
|
requestMTU: getRequestedBleMtu(),
|
|
359
450
|
timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
|
|
@@ -362,6 +453,29 @@ const connectOptions = {
|
|
|
362
453
|
const fallbackConnectOptions = {
|
|
363
454
|
timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
|
|
364
455
|
};
|
|
456
|
+
const androidRefreshGattConnectOptions = {
|
|
457
|
+
timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
|
|
458
|
+
refreshGatt: 'OnConnected',
|
|
459
|
+
};
|
|
460
|
+
const ANDROID_MTU_EXCHANGE_TIMEOUT_MS = 12000;
|
|
461
|
+
const ANDROID_LINK_DROP_QUIET_MS = 5000;
|
|
462
|
+
const ANDROID_LINK_DROP_POLL_MS = 250;
|
|
463
|
+
const isKnownDefaultMtu = (mtu) => typeof mtu === 'number' && Number.isFinite(mtu) && mtu <= 23;
|
|
464
|
+
const isMissingGattShapeError = (error) => {
|
|
465
|
+
const code = error === null || error === void 0 ? void 0 : error.errorCode;
|
|
466
|
+
const message = error === null || error === void 0 ? void 0 : error.message;
|
|
467
|
+
return (code === hdShared.HardwareErrorCode.BleServiceNotFound ||
|
|
468
|
+
code === hdShared.HardwareErrorCode.BleCharacteristicNotFound ||
|
|
469
|
+
(typeof message === 'string' &&
|
|
470
|
+
(message.includes('BLECharacteristicNotFound') ||
|
|
471
|
+
message.includes('BLECharacteristicNotWritable') ||
|
|
472
|
+
message.includes('BLECharacteristicNotNotifiable'))));
|
|
473
|
+
};
|
|
474
|
+
const isStaleGattTableNotifyReason = (reason) => !!reason &&
|
|
475
|
+
(reason.includes('Cannot write client characteristic config descriptor') ||
|
|
476
|
+
reason.includes('Cannot find client characteristic config descriptor') ||
|
|
477
|
+
reason.includes('The handle is invalid') ||
|
|
478
|
+
reason.includes('Writing is not permitted'));
|
|
365
479
|
const BLE_CONNECT_TIMEOUT_MS = BLE_NATIVE_CONNECT_TIMEOUT_MS * 2 + 2000;
|
|
366
480
|
const BLE_GATT_SETUP_TIMEOUT_MS = 10000;
|
|
367
481
|
const PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = 3;
|
|
@@ -376,6 +490,11 @@ const isWedgedBleSetupError = (error) => (error === null || error === void 0 ? v
|
|
|
376
490
|
error.message.startsWith(BLE_SETUP_WEDGED_MESSAGE);
|
|
377
491
|
const shouldRethrowBleSetupError = (error) => isConnectTimeoutError(error) || isWedgedBleSetupError(error);
|
|
378
492
|
const isNativeOperationTimeoutError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === reactNativeBlePlx.BleErrorCode.OperationTimedOut;
|
|
493
|
+
const isMtuOrCancelledConnectError = (error) => {
|
|
494
|
+
const errorCode = error === null || error === void 0 ? void 0 : error.errorCode;
|
|
495
|
+
return (errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
|
|
496
|
+
errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled);
|
|
497
|
+
};
|
|
379
498
|
const tryToGetConfiguration = (device) => {
|
|
380
499
|
if (!device || !device.serviceUUIDs)
|
|
381
500
|
return null;
|
|
@@ -387,27 +506,67 @@ const tryToGetConfiguration = (device) => {
|
|
|
387
506
|
return null;
|
|
388
507
|
return infos;
|
|
389
508
|
};
|
|
390
|
-
const requestNegotiatedMtu = (device, stage, attempt) => __awaiter(void 0, void 0, void 0, function* () {
|
|
509
|
+
const requestNegotiatedMtu = (device, stage, attempt, cancelTransaction) => __awaiter(void 0, void 0, void 0, function* () {
|
|
391
510
|
if (reactNative.Platform.OS !== 'ios' && reactNative.Platform.OS !== 'android')
|
|
392
|
-
return device;
|
|
511
|
+
return { device, timedOut: false };
|
|
512
|
+
const transactionId = `${device.id}:mtu:${stage}:${attempt}:${Date.now()}`;
|
|
513
|
+
let timeoutId;
|
|
514
|
+
let timedOut = false;
|
|
393
515
|
try {
|
|
394
|
-
const
|
|
395
|
-
|
|
516
|
+
const request = device.requestMTU(getRequestedBleMtu(), transactionId);
|
|
517
|
+
request.catch(() => undefined);
|
|
518
|
+
const mtuDevice = yield Promise.race([
|
|
519
|
+
request,
|
|
520
|
+
new Promise((_, reject) => {
|
|
521
|
+
timeoutId = setTimeout(() => {
|
|
522
|
+
timedOut = true;
|
|
523
|
+
reject(new Error(`BLE MTU request timeout after ${BLE_MTU_REQUEST_TIMEOUT_MS}ms`));
|
|
524
|
+
}, BLE_MTU_REQUEST_TIMEOUT_MS);
|
|
525
|
+
}),
|
|
526
|
+
]);
|
|
527
|
+
return { device: mtuDevice, timedOut: false };
|
|
396
528
|
}
|
|
397
529
|
catch (error) {
|
|
530
|
+
if (timedOut && cancelTransaction) {
|
|
531
|
+
try {
|
|
532
|
+
Promise.resolve(cancelTransaction(transactionId)).catch(cancelError => {
|
|
533
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] MTU cancellation failed', {
|
|
534
|
+
platform: reactNative.Platform.OS,
|
|
535
|
+
stage,
|
|
536
|
+
attempt,
|
|
537
|
+
error: cancelError instanceof Error ? cancelError.message : String(cancelError),
|
|
538
|
+
});
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
catch (cancelError) {
|
|
542
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] MTU cancellation failed', {
|
|
543
|
+
platform: reactNative.Platform.OS,
|
|
544
|
+
stage,
|
|
545
|
+
attempt,
|
|
546
|
+
error: cancelError instanceof Error ? cancelError.message : String(cancelError),
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
}
|
|
398
550
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] MTU refresh failed, continuing with current value', {
|
|
399
551
|
platform: reactNative.Platform.OS,
|
|
400
552
|
stage,
|
|
401
553
|
attempt,
|
|
402
554
|
actual: device.mtu,
|
|
555
|
+
timedOut,
|
|
403
556
|
error: error instanceof Error ? error.message : String(error),
|
|
404
557
|
});
|
|
405
|
-
return device;
|
|
558
|
+
return { device, timedOut };
|
|
559
|
+
}
|
|
560
|
+
finally {
|
|
561
|
+
if (timeoutId)
|
|
562
|
+
clearTimeout(timeoutId);
|
|
406
563
|
}
|
|
407
564
|
});
|
|
408
|
-
const resolveNegotiatedMtu = (device) =>
|
|
565
|
+
const resolveNegotiatedMtu = (device, cancelTransaction) => shouldRefreshNegotiatedMtu(device.mtu)
|
|
566
|
+
? requestNegotiatedMtu(device, 'connected', 0, cancelTransaction)
|
|
567
|
+
: Promise.resolve({ device, timedOut: false });
|
|
409
568
|
function remapError(error) {
|
|
410
|
-
var _a;
|
|
569
|
+
var _a, _b;
|
|
411
570
|
if (error instanceof reactNativeBlePlx.BleError) {
|
|
412
571
|
if (isNativeBleStaleBondError(error)) {
|
|
413
572
|
throw toBleStaleBondHardwareError(error);
|
|
@@ -416,12 +575,13 @@ function remapError(error) {
|
|
|
416
575
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceBondError);
|
|
417
576
|
}
|
|
418
577
|
}
|
|
419
|
-
if (error
|
|
420
|
-
error
|
|
421
|
-
|
|
578
|
+
if (isNativeBleDisconnectError(error)) {
|
|
579
|
+
throw toBleDisconnectHardwareError(error);
|
|
580
|
+
}
|
|
581
|
+
if (error instanceof Error && ((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes('not found'))) {
|
|
422
582
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
423
583
|
}
|
|
424
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, (
|
|
584
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, (_b = error.reason) !== null && _b !== void 0 ? _b : error);
|
|
425
585
|
}
|
|
426
586
|
class ReactNativeBleTransport {
|
|
427
587
|
constructor(options) {
|
|
@@ -429,6 +589,7 @@ class ReactNativeBleTransport {
|
|
|
429
589
|
this.name = 'ReactNativeBleTransport';
|
|
430
590
|
this.configured = false;
|
|
431
591
|
this.stopped = false;
|
|
592
|
+
this.bondAbortController = new AbortController();
|
|
432
593
|
this.scanTimeout = DEVICE_SCAN_TIMEOUT_MS;
|
|
433
594
|
this.runPromise = null;
|
|
434
595
|
this.runPromiseDeviceId = null;
|
|
@@ -441,6 +602,8 @@ class ReactNativeBleTransport {
|
|
|
441
602
|
this.sessionProtocols = new Map();
|
|
442
603
|
this.confirmedProtocolV2 = new Set();
|
|
443
604
|
this.protocolReprobeFailures = new Map();
|
|
605
|
+
this.silentDetections = new Map();
|
|
606
|
+
this.androidGattCacheRefreshes = new Set();
|
|
444
607
|
this.staleBondErrors = new Map();
|
|
445
608
|
this.acquiringProtocolV2 = new Set();
|
|
446
609
|
this.protocolV2Assemblers = new Map();
|
|
@@ -458,11 +621,20 @@ class ReactNativeBleTransport {
|
|
|
458
621
|
},
|
|
459
622
|
classifyError: () => 'link-fatal',
|
|
460
623
|
onLinkInvalidated: (uuid, reason) => __awaiter(this, void 0, void 0, function* () {
|
|
461
|
-
var _b;
|
|
624
|
+
var _b, _c;
|
|
462
625
|
(_b = this.protocolV2Assemblers.get(uuid)) === null || _b === void 0 ? void 0 : _b.reset();
|
|
463
626
|
this.rejectProtocolV2Frames(uuid, new Error(reason));
|
|
464
627
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 link invalidated:', uuid, reason);
|
|
465
628
|
if (reason.startsWith('Protocol V2 link-fatal error:')) {
|
|
629
|
+
if (this.probingProtocols.get(uuid) !== 'V2') {
|
|
630
|
+
const transport = transportCache[uuid];
|
|
631
|
+
try {
|
|
632
|
+
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);
|
|
633
|
+
}
|
|
634
|
+
catch (_d) {
|
|
635
|
+
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] Protocol V2 disconnect listener failed');
|
|
636
|
+
}
|
|
637
|
+
}
|
|
466
638
|
yield this.releaseNative(uuid, true);
|
|
467
639
|
}
|
|
468
640
|
}),
|
|
@@ -474,6 +646,7 @@ class ReactNativeBleTransport {
|
|
|
474
646
|
this.androidPriorityResetTimers = new Map();
|
|
475
647
|
this.nextMonitorToken = 1;
|
|
476
648
|
this.lifecycleOperations = new Map();
|
|
649
|
+
this.scanCleanups = new Set();
|
|
477
650
|
this.scanTimeout = (_a = options.scanTimeout) !== null && _a !== void 0 ? _a : DEVICE_SCAN_TIMEOUT_MS;
|
|
478
651
|
}
|
|
479
652
|
init(logger, emitter) {
|
|
@@ -502,10 +675,38 @@ class ReactNativeBleTransport {
|
|
|
502
675
|
listen() {
|
|
503
676
|
}
|
|
504
677
|
getPlxManager() {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
678
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
679
|
+
while (bleManagerResetPromise) {
|
|
680
|
+
yield this.waitForManagerReset();
|
|
681
|
+
}
|
|
682
|
+
if (this.stopped)
|
|
683
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
684
|
+
if (!this.blePlxManager)
|
|
685
|
+
this.blePlxManager = new reactNativeBlePlx.BleManager();
|
|
686
|
+
return this.blePlxManager;
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
waitForManagerReset() {
|
|
690
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
691
|
+
if (!bleManagerResetPromise)
|
|
692
|
+
return;
|
|
693
|
+
let timeout;
|
|
694
|
+
try {
|
|
695
|
+
yield Promise.race([
|
|
696
|
+
bleManagerResetPromise,
|
|
697
|
+
new Promise((_, reject) => {
|
|
698
|
+
timeout = setTimeout(() => reject(this.createWedgedBleSetupError()), BLE_CONNECT_TIMEOUT_MS);
|
|
699
|
+
}),
|
|
700
|
+
]);
|
|
701
|
+
}
|
|
702
|
+
catch (_a) {
|
|
703
|
+
throw this.createWedgedBleSetupError();
|
|
704
|
+
}
|
|
705
|
+
finally {
|
|
706
|
+
if (timeout)
|
|
707
|
+
clearTimeout(timeout);
|
|
708
|
+
}
|
|
709
|
+
});
|
|
509
710
|
}
|
|
510
711
|
resolveCharacteristics(device) {
|
|
511
712
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -670,30 +871,55 @@ class ReactNativeBleTransport {
|
|
|
670
871
|
}
|
|
671
872
|
enumerate() {
|
|
672
873
|
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
|
-
}
|
|
874
|
+
const scanStartedAt = Date.now();
|
|
875
|
+
let firstDeviceMs;
|
|
876
|
+
const blePlxManager = yield this.getPlxManager();
|
|
877
|
+
yield subscribeBleOn(blePlxManager);
|
|
878
|
+
if (reactNative.Platform.OS === 'android' && reactNative.Platform.Version >= 31) {
|
|
879
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('requesting permissions, please wait...');
|
|
880
|
+
const resultConnect = yield reactNative.PermissionsAndroid.requestMultiple([
|
|
881
|
+
reactNative.PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT,
|
|
882
|
+
reactNative.PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN,
|
|
883
|
+
]);
|
|
884
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('requesting permissions, result: ', resultConnect);
|
|
885
|
+
if (resultConnect[reactNative.PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT] !== 'granted' ||
|
|
886
|
+
resultConnect[reactNative.PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN] !== 'granted') {
|
|
887
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePermissionError);
|
|
696
888
|
}
|
|
889
|
+
}
|
|
890
|
+
if (this.stopped)
|
|
891
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
892
|
+
return new Promise((resolve, reject) => {
|
|
893
|
+
const deviceList = [];
|
|
894
|
+
let finished = false;
|
|
895
|
+
let scanCleanup;
|
|
896
|
+
const finishScan = (error) => {
|
|
897
|
+
if (scanCleanup)
|
|
898
|
+
return scanCleanup;
|
|
899
|
+
finished = true;
|
|
900
|
+
clearScanTimer();
|
|
901
|
+
scanCleanup = this.runNativeTeardown('scan', blePlxManager, () => __awaiter(this, void 0, void 0, function* () {
|
|
902
|
+
yield blePlxManager.stopDeviceScan();
|
|
903
|
+
})).then(() => {
|
|
904
|
+
this.scanCleanups.delete(cancelScan);
|
|
905
|
+
if (error)
|
|
906
|
+
reject(error);
|
|
907
|
+
else
|
|
908
|
+
resolve(deviceList);
|
|
909
|
+
});
|
|
910
|
+
return scanCleanup;
|
|
911
|
+
};
|
|
912
|
+
const cancelScan = () => finishScan(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected));
|
|
913
|
+
this.scanCleanups.add(cancelScan);
|
|
914
|
+
const clearScanTimer = timer.timeout(() => {
|
|
915
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] scan completed', {
|
|
916
|
+
elapsedMs: Date.now() - scanStartedAt,
|
|
917
|
+
firstDeviceMs,
|
|
918
|
+
deviceCount: deviceList.length,
|
|
919
|
+
scanWindowMs: this.scanTimeout,
|
|
920
|
+
});
|
|
921
|
+
finishScan();
|
|
922
|
+
}, this.scanTimeout);
|
|
697
923
|
blePlxManager.startDeviceScan(getBluetoothServiceUuids(), {
|
|
698
924
|
allowDuplicates: true,
|
|
699
925
|
scanMode: reactNativeBlePlx.ScanMode.LowLatency,
|
|
@@ -701,20 +927,21 @@ class ReactNativeBleTransport {
|
|
|
701
927
|
var _a, _b;
|
|
702
928
|
if (error) {
|
|
703
929
|
Log === null || Log === void 0 ? void 0 : Log.debug('ble scan error: ', error);
|
|
704
|
-
if (
|
|
705
|
-
|
|
930
|
+
if (error.errorCode === reactNativeBlePlx.BleErrorCode.BluetoothPoweredOff) {
|
|
931
|
+
finishScan(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePoweredOff));
|
|
932
|
+
}
|
|
933
|
+
else if (error.errorCode === reactNativeBlePlx.BleErrorCode.BluetoothUnsupported) {
|
|
934
|
+
finishScan(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleUnsupported));
|
|
706
935
|
}
|
|
707
936
|
else if (error.errorCode === reactNativeBlePlx.BleErrorCode.BluetoothUnauthorized) {
|
|
708
|
-
|
|
937
|
+
finishScan(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleLocationError));
|
|
709
938
|
}
|
|
710
939
|
else if (error.errorCode === reactNativeBlePlx.BleErrorCode.LocationServicesDisabled) {
|
|
711
|
-
|
|
712
|
-
}
|
|
713
|
-
else if (error.errorCode === reactNativeBlePlx.BleErrorCode.ScanStartFailed) {
|
|
714
|
-
timer.timeout(() => { }, this.scanTimeout);
|
|
940
|
+
finishScan(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleLocationServicesDisabled));
|
|
715
941
|
}
|
|
942
|
+
else if (error.errorCode === reactNativeBlePlx.BleErrorCode.ScanStartFailed) ;
|
|
716
943
|
else {
|
|
717
|
-
|
|
944
|
+
finishScan(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleScanError, (_a = error.reason) !== null && _a !== void 0 ? _a : ''));
|
|
718
945
|
}
|
|
719
946
|
return;
|
|
720
947
|
}
|
|
@@ -739,6 +966,8 @@ class ReactNativeBleTransport {
|
|
|
739
966
|
});
|
|
740
967
|
}
|
|
741
968
|
});
|
|
969
|
+
if (finished)
|
|
970
|
+
return;
|
|
742
971
|
getConnectedDeviceIds(reactNative.Platform.OS === 'ios' ? getBluetoothServiceUuids() : []).then(devices => {
|
|
743
972
|
for (const device of devices) {
|
|
744
973
|
const localName = 'localName' in device && typeof device.localName === 'string'
|
|
@@ -754,10 +983,11 @@ class ReactNativeBleTransport {
|
|
|
754
983
|
addDevice(device);
|
|
755
984
|
}
|
|
756
985
|
}
|
|
757
|
-
});
|
|
986
|
+
}, error => Log === null || Log === void 0 ? void 0 : Log.debug('search connected peripheral failed:', error));
|
|
758
987
|
const addDevice = (device) => {
|
|
759
988
|
var _a;
|
|
760
|
-
if (deviceList.every(d => d.id !== device.id)) {
|
|
989
|
+
if (!finished && deviceList.every(d => d.id !== device.id)) {
|
|
990
|
+
firstDeviceMs !== null && firstDeviceMs !== void 0 ? firstDeviceMs : (firstDeviceMs = Date.now() - scanStartedAt);
|
|
761
991
|
const displayName = (_a = getDeviceDisplayName(device)) !== null && _a !== void 0 ? _a : 'Unknown BLE Device';
|
|
762
992
|
deviceList.push(Object.assign(Object.assign({}, device), { name: displayName, commType: 'ble' }));
|
|
763
993
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] OneKey BLE device discovered', {
|
|
@@ -767,16 +997,14 @@ class ReactNativeBleTransport {
|
|
|
767
997
|
});
|
|
768
998
|
}
|
|
769
999
|
};
|
|
770
|
-
|
|
771
|
-
blePlxManager.stopDeviceScan();
|
|
772
|
-
resolve(deviceList);
|
|
773
|
-
}, this.scanTimeout);
|
|
774
|
-
}));
|
|
1000
|
+
});
|
|
775
1001
|
});
|
|
776
1002
|
}
|
|
777
1003
|
installTransportForAcquire(uuid, device, characteristics) {
|
|
778
1004
|
return __awaiter(this, void 0, void 0, function* () {
|
|
779
1005
|
const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristicsWithTimeout(uuid, device));
|
|
1006
|
+
if (this.stopped)
|
|
1007
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
780
1008
|
const transport$1 = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
|
|
781
1009
|
transport$1.mtuSize = typeof device.mtu === 'number' ? device.mtu : undefined;
|
|
782
1010
|
const monitorToken = this.nextMonitorToken;
|
|
@@ -797,30 +1025,12 @@ class ReactNativeBleTransport {
|
|
|
797
1025
|
else if (reactNative.Platform.OS === 'android') {
|
|
798
1026
|
yield delay(ANDROID_NOTIFY_READY_DELAY_MS);
|
|
799
1027
|
}
|
|
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
|
-
}
|
|
1028
|
+
if (this.stopped)
|
|
1029
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
818
1030
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE MTU ready', {
|
|
819
1031
|
platform: reactNative.Platform.OS,
|
|
820
1032
|
requested: getRequestedBleMtu(),
|
|
821
|
-
initial: initialMtu,
|
|
822
1033
|
actual: transport$1.mtuSize,
|
|
823
|
-
refreshAttempts,
|
|
824
1034
|
});
|
|
825
1035
|
return transport$1;
|
|
826
1036
|
});
|
|
@@ -837,6 +1047,8 @@ class ReactNativeBleTransport {
|
|
|
837
1047
|
acquireUnlocked(input) {
|
|
838
1048
|
var _a;
|
|
839
1049
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1050
|
+
if (this.stopped)
|
|
1051
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
840
1052
|
const { uuid, forceCleanRunPromise, expectedProtocol, skipProtocolProbe } = input;
|
|
841
1053
|
const shouldMapProtocolV2StaleBond = expectedProtocol
|
|
842
1054
|
? expectedProtocol === 'V2'
|
|
@@ -868,9 +1080,13 @@ class ReactNativeBleTransport {
|
|
|
868
1080
|
const isCachedDeviceConnected = yield cachedTransport.device
|
|
869
1081
|
.isConnected()
|
|
870
1082
|
.catch(() => false);
|
|
1083
|
+
const isCachedAndroidLinkUsable = reactNative.Platform.OS !== 'android' || !this.androidGattCacheRefreshes.has(uuid);
|
|
871
1084
|
if (isCachedDeviceConnected &&
|
|
1085
|
+
isCachedAndroidLinkUsable &&
|
|
872
1086
|
cachedProtocol &&
|
|
873
1087
|
(!expectedProtocol || cachedProtocol === expectedProtocol)) {
|
|
1088
|
+
if (this.stopped)
|
|
1089
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
874
1090
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] reuse cached BLE transport:', uuid, cachedProtocol);
|
|
875
1091
|
return { uuid, protocolType: cachedProtocol };
|
|
876
1092
|
}
|
|
@@ -879,6 +1095,15 @@ class ReactNativeBleTransport {
|
|
|
879
1095
|
}
|
|
880
1096
|
}
|
|
881
1097
|
let device = null;
|
|
1098
|
+
const isAndroid = reactNative.Platform.OS === 'android';
|
|
1099
|
+
const refreshAndroidGattCache = isAndroid && (!!skipProtocolProbe || this.androidGattCacheRefreshes.has(uuid));
|
|
1100
|
+
let nativeConnectOptions = connectOptions;
|
|
1101
|
+
if (isAndroid) {
|
|
1102
|
+
nativeConnectOptions = refreshAndroidGattCache
|
|
1103
|
+
? androidRefreshGattConnectOptions
|
|
1104
|
+
: fallbackConnectOptions;
|
|
1105
|
+
}
|
|
1106
|
+
let androidRefreshConnectRan = false;
|
|
882
1107
|
if (forceCleanRunPromise && this.runPromise) {
|
|
883
1108
|
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
|
|
884
1109
|
this.runPromise.reject(error);
|
|
@@ -887,6 +1112,7 @@ class ReactNativeBleTransport {
|
|
|
887
1112
|
Log === null || Log === void 0 ? void 0 : Log.debug('Force clean Bluetooth run promise, forceCleanRunPromise: ', forceCleanRunPromise);
|
|
888
1113
|
}
|
|
889
1114
|
const blePlxManager = yield this.getPlxManager();
|
|
1115
|
+
let skipPostConnectMtu = false;
|
|
890
1116
|
try {
|
|
891
1117
|
yield subscribeBleOn(blePlxManager);
|
|
892
1118
|
}
|
|
@@ -894,6 +1120,27 @@ class ReactNativeBleTransport {
|
|
|
894
1120
|
Log === null || Log === void 0 ? void 0 : Log.debug('subscribeBleOn error: ', error);
|
|
895
1121
|
throw error;
|
|
896
1122
|
}
|
|
1123
|
+
if (this.stopped)
|
|
1124
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1125
|
+
if (reactNative.Platform.OS === 'android') {
|
|
1126
|
+
try {
|
|
1127
|
+
const bondState = yield pairDevice(uuid);
|
|
1128
|
+
if (bondState.bonding) {
|
|
1129
|
+
yield onDeviceBondState(uuid, this.bondAbortController.signal);
|
|
1130
|
+
}
|
|
1131
|
+
else if (!bondState.bonded) {
|
|
1132
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'device is not bonded');
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
catch (error) {
|
|
1136
|
+
yield this.runNativeTeardown(uuid, blePlxManager, () => __awaiter(this, void 0, void 0, function* () {
|
|
1137
|
+
yield this.runBestEffortNativeOperation('bond failure: cancel manager connection', () => blePlxManager.cancelDeviceConnection(uuid));
|
|
1138
|
+
}));
|
|
1139
|
+
throw error;
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
if (this.stopped)
|
|
1143
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
897
1144
|
if (!device) {
|
|
898
1145
|
const devices = yield blePlxManager.devices([uuid]);
|
|
899
1146
|
[device] = devices;
|
|
@@ -907,15 +1154,16 @@ class ReactNativeBleTransport {
|
|
|
907
1154
|
if (!device) {
|
|
908
1155
|
Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device: ', uuid);
|
|
909
1156
|
try {
|
|
910
|
-
device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid,
|
|
1157
|
+
device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, nativeConnectOptions));
|
|
1158
|
+
androidRefreshConnectRan = refreshAndroidGattCache;
|
|
911
1159
|
}
|
|
912
1160
|
catch (e) {
|
|
913
1161
|
Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device has error: ', e);
|
|
914
1162
|
if (shouldRethrowBleSetupError(e)) {
|
|
915
1163
|
throw e;
|
|
916
1164
|
}
|
|
917
|
-
if (e
|
|
918
|
-
|
|
1165
|
+
if (isMtuOrCancelledConnectError(e)) {
|
|
1166
|
+
skipPostConnectMtu = true;
|
|
919
1167
|
Log === null || Log === void 0 ? void 0 : Log.debug('first try to reconnect without params');
|
|
920
1168
|
device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, fallbackConnectOptions));
|
|
921
1169
|
}
|
|
@@ -931,19 +1179,27 @@ class ReactNativeBleTransport {
|
|
|
931
1179
|
if (!device) {
|
|
932
1180
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, 'unable to connect to device');
|
|
933
1181
|
}
|
|
1182
|
+
if (refreshAndroidGattCache &&
|
|
1183
|
+
!androidRefreshConnectRan &&
|
|
1184
|
+
(yield device.isConnected().catch(() => false))) {
|
|
1185
|
+
yield this.dropAndroidLink(uuid, blePlxManager, device, 'gatt cache refresh');
|
|
1186
|
+
if (this.stopped)
|
|
1187
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1188
|
+
}
|
|
934
1189
|
if (!(yield device.isConnected())) {
|
|
935
1190
|
Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device: ', uuid);
|
|
936
1191
|
const disconnectedDevice = device;
|
|
937
1192
|
try {
|
|
938
|
-
device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(
|
|
1193
|
+
device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(nativeConnectOptions));
|
|
1194
|
+
androidRefreshConnectRan = refreshAndroidGattCache;
|
|
939
1195
|
}
|
|
940
1196
|
catch (e) {
|
|
941
1197
|
Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device has error: ', e);
|
|
942
1198
|
if (shouldRethrowBleSetupError(e)) {
|
|
943
1199
|
throw e;
|
|
944
1200
|
}
|
|
945
|
-
if (e
|
|
946
|
-
|
|
1201
|
+
if (isMtuOrCancelledConnectError(e)) {
|
|
1202
|
+
skipPostConnectMtu = true;
|
|
947
1203
|
Log === null || Log === void 0 ? void 0 : Log.debug('second try to reconnect without params');
|
|
948
1204
|
try {
|
|
949
1205
|
device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
|
|
@@ -965,33 +1221,70 @@ class ReactNativeBleTransport {
|
|
|
965
1221
|
}
|
|
966
1222
|
}
|
|
967
1223
|
}
|
|
968
|
-
if (
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
1224
|
+
if (this.stopped)
|
|
1225
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1226
|
+
if (reactNative.Platform.OS === 'android' && !(yield device.isConnected().catch(() => false))) {
|
|
1227
|
+
const disconnectedDevice = device;
|
|
1228
|
+
yield this.runNativeTeardown(uuid, blePlxManager, () => __awaiter(this, void 0, void 0, function* () {
|
|
1229
|
+
yield Promise.all([
|
|
1230
|
+
this.runBestEffortNativeOperation('connect failure: cancel manager connection', () => blePlxManager.cancelDeviceConnection(uuid)),
|
|
1231
|
+
this.runBestEffortNativeOperation('connect failure: cancel device connection', () => disconnectedDevice.cancelConnection()),
|
|
1232
|
+
]);
|
|
1233
|
+
}));
|
|
1234
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, 'device is not connected');
|
|
1235
|
+
}
|
|
1236
|
+
if (this.stopped)
|
|
1237
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1238
|
+
let characteristics;
|
|
1239
|
+
if (isAndroid) {
|
|
1240
|
+
if (refreshAndroidGattCache) {
|
|
1241
|
+
characteristics = yield this.resolveCharacteristicsWithTimeout(uuid, device);
|
|
1242
|
+
if (androidRefreshConnectRan)
|
|
1243
|
+
this.androidGattCacheRefreshes.delete(uuid);
|
|
1244
|
+
if (this.stopped)
|
|
1245
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1246
|
+
}
|
|
1247
|
+
device = yield this.negotiateAndroidMtu(uuid, blePlxManager, device);
|
|
1248
|
+
}
|
|
1249
|
+
else if (!skipPostConnectMtu) {
|
|
1250
|
+
const mtuResult = yield resolveNegotiatedMtu(device, transactionId => blePlxManager.cancelTransaction(transactionId));
|
|
1251
|
+
device = mtuResult.device;
|
|
1252
|
+
if (mtuResult.timedOut) {
|
|
1253
|
+
if (this.stopped)
|
|
1254
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1255
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] post-connect MTU timed out, reconnecting without requesting MTU');
|
|
1256
|
+
const timedOutDevice = device;
|
|
1257
|
+
let mtuTeardownSettled = false;
|
|
1258
|
+
yield this.runNativeTeardown(uuid, blePlxManager, () => __awaiter(this, void 0, void 0, function* () {
|
|
1259
|
+
yield this.runBestEffortNativeOperation('mtu timeout: cancel device connection', () => timedOutDevice.cancelConnection());
|
|
1260
|
+
mtuTeardownSettled = true;
|
|
1261
|
+
}));
|
|
1262
|
+
if (this.stopped)
|
|
1263
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1264
|
+
if (!mtuTeardownSettled || this.blePlxManager !== blePlxManager) {
|
|
1265
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'BLE MTU cleanup timed out');
|
|
973
1266
|
}
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
yield onDeviceBondState(uuid);
|
|
1267
|
+
try {
|
|
1268
|
+
device = yield this.connectWithTimeout(uuid, () => timedOutDevice.connect(fallbackConnectOptions));
|
|
977
1269
|
}
|
|
978
|
-
|
|
979
|
-
|
|
1270
|
+
catch (error) {
|
|
1271
|
+
if (shouldRethrowBleSetupError(error))
|
|
1272
|
+
throw error;
|
|
1273
|
+
if ((error === null || error === void 0 ? void 0 : error.errorCode) === reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
|
|
1274
|
+
device = timedOutDevice;
|
|
1275
|
+
}
|
|
1276
|
+
else {
|
|
1277
|
+
remapError(error);
|
|
1278
|
+
}
|
|
980
1279
|
}
|
|
981
1280
|
}
|
|
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
1281
|
}
|
|
992
|
-
|
|
1282
|
+
if (this.stopped)
|
|
1283
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
993
1284
|
const acquiredDevice = device;
|
|
994
|
-
const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, acquiredDevice);
|
|
1285
|
+
const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristicsWithTimeout(uuid, acquiredDevice));
|
|
1286
|
+
if (this.stopped)
|
|
1287
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
995
1288
|
const protocolHint = expectedProtocol
|
|
996
1289
|
? undefined
|
|
997
1290
|
: (_a = input.protocolHint) !== null && _a !== void 0 ? _a : this.deviceProtocolHints.get(uuid);
|
|
@@ -1034,6 +1327,8 @@ class ReactNativeBleTransport {
|
|
|
1034
1327
|
const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint, () => __awaiter(this, void 0, void 0, function* () {
|
|
1035
1328
|
yield this.installTransportForAcquire(uuid, acquiredDevice);
|
|
1036
1329
|
}));
|
|
1330
|
+
if (this.stopped)
|
|
1331
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1037
1332
|
const currentTransport = transportCache[uuid];
|
|
1038
1333
|
if (!currentTransport) {
|
|
1039
1334
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
|
|
@@ -1042,12 +1337,7 @@ class ReactNativeBleTransport {
|
|
|
1042
1337
|
return { uuid, protocolType };
|
|
1043
1338
|
}
|
|
1044
1339
|
catch (error) {
|
|
1045
|
-
|
|
1046
|
-
yield this.disconnectUnlocked(uuid);
|
|
1047
|
-
}
|
|
1048
|
-
else {
|
|
1049
|
-
yield this.releaseUnlocked(uuid, true);
|
|
1050
|
-
}
|
|
1340
|
+
yield this.disconnectUnlocked(uuid);
|
|
1051
1341
|
throw error;
|
|
1052
1342
|
}
|
|
1053
1343
|
finally {
|
|
@@ -1059,7 +1349,7 @@ class ReactNativeBleTransport {
|
|
|
1059
1349
|
let bufferLength = 0;
|
|
1060
1350
|
let buffer$1 = [];
|
|
1061
1351
|
const subscription = characteristic.monitor((error, c) => {
|
|
1062
|
-
var _a, _b, _c, _d, _e, _f
|
|
1352
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1063
1353
|
const isCurrentMonitor = this.monitorTokens.get(uuid) === monitorToken;
|
|
1064
1354
|
if (error) {
|
|
1065
1355
|
Log === null || Log === void 0 ? void 0 : Log.debug(`error monitor ${characteristic.uuid}, deviceId: ${characteristic.deviceID}: ${error}`);
|
|
@@ -1076,16 +1366,16 @@ class ReactNativeBleTransport {
|
|
|
1076
1366
|
this.rememberStaleBondError(uuid, toBleStaleBondHardwareError(error));
|
|
1077
1367
|
return;
|
|
1078
1368
|
}
|
|
1369
|
+
if (reactNative.Platform.OS === 'android' && isStaleGattTableNotifyReason(error.reason)) {
|
|
1370
|
+
this.androidGattCacheRefreshes.add(uuid);
|
|
1371
|
+
}
|
|
1079
1372
|
if (this.getActiveProtocol(uuid) === 'V2') {
|
|
1080
1373
|
let errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
|
|
1081
1374
|
if ((_a = error.reason) === null || _a === void 0 ? void 0 : _a.includes('The connection has timed out unexpectedly')) {
|
|
1082
1375
|
errorCode = hdShared.HardwareErrorCode.BleTimeoutError;
|
|
1083
1376
|
}
|
|
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'))) {
|
|
1377
|
+
else if (isStaleGattTableNotifyReason(error.reason) ||
|
|
1378
|
+
((_b = error.reason) === null || _b === void 0 ? void 0 : _b.includes('notify change failed for device'))) {
|
|
1089
1379
|
errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure;
|
|
1090
1380
|
}
|
|
1091
1381
|
this.rejectProtocolV2Frames(uuid, hdShared.ERRORS.TypedError(errorCode));
|
|
@@ -1093,14 +1383,11 @@ class ReactNativeBleTransport {
|
|
|
1093
1383
|
}
|
|
1094
1384
|
if (this.runPromise && this.runPromiseDeviceId === uuid) {
|
|
1095
1385
|
let ERROR = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
|
|
1096
|
-
if ((
|
|
1386
|
+
if ((_c = error.reason) === null || _c === void 0 ? void 0 : _c.includes('The connection has timed out unexpectedly')) {
|
|
1097
1387
|
ERROR = hdShared.HardwareErrorCode.BleTimeoutError;
|
|
1098
1388
|
}
|
|
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'))) {
|
|
1389
|
+
if (isStaleGattTableNotifyReason(error.reason) ||
|
|
1390
|
+
((_d = error.reason) === null || _d === void 0 ? void 0 : _d.includes('notify change failed for device'))) {
|
|
1104
1391
|
const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure);
|
|
1105
1392
|
this.runPromise.reject(notifyError);
|
|
1106
1393
|
Log === null || Log === void 0 ? void 0 : Log.debug(`${hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure} ${error.message} ${error.reason}`);
|
|
@@ -1142,7 +1429,7 @@ class ReactNativeBleTransport {
|
|
|
1142
1429
|
bufferLength = 0;
|
|
1143
1430
|
buffer$1 = [];
|
|
1144
1431
|
if (this.runPromiseDeviceId === uuid) {
|
|
1145
|
-
(
|
|
1432
|
+
(_e = this.runPromise) === null || _e === void 0 ? void 0 : _e.resolve(value.toString('hex'));
|
|
1146
1433
|
}
|
|
1147
1434
|
}
|
|
1148
1435
|
}
|
|
@@ -1153,7 +1440,7 @@ class ReactNativeBleTransport {
|
|
|
1153
1440
|
this.rejectProtocolV2Frames(uuid, notifyError);
|
|
1154
1441
|
}
|
|
1155
1442
|
else if (this.runPromiseDeviceId === uuid) {
|
|
1156
|
-
(
|
|
1443
|
+
(_f = this.runPromise) === null || _f === void 0 ? void 0 : _f.reject(notifyError);
|
|
1157
1444
|
}
|
|
1158
1445
|
}
|
|
1159
1446
|
}, notifyTransactionId);
|
|
@@ -1429,13 +1716,14 @@ class ReactNativeBleTransport {
|
|
|
1429
1716
|
return check.call(jsonData);
|
|
1430
1717
|
}
|
|
1431
1718
|
catch (e) {
|
|
1432
|
-
|
|
1433
|
-
|
|
1719
|
+
const isProbeTimeout = (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS &&
|
|
1720
|
+
(name === 'GetFeatures' || name === 'Initialize');
|
|
1721
|
+
if (isProbeTimeout) {
|
|
1722
|
+
Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] Protocol V1 ${name} probe call failed:`, e);
|
|
1434
1723
|
}
|
|
1435
1724
|
else {
|
|
1436
1725
|
Log === null || Log === void 0 ? void 0 : Log.error('call error: ', e);
|
|
1437
1726
|
}
|
|
1438
|
-
const isProbeTimeout = name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS;
|
|
1439
1727
|
const isStaleCall = this.runPromise !== runPromise;
|
|
1440
1728
|
if (!isProbeTimeout &&
|
|
1441
1729
|
!isStaleCall &&
|
|
@@ -1455,7 +1743,43 @@ class ReactNativeBleTransport {
|
|
|
1455
1743
|
});
|
|
1456
1744
|
}
|
|
1457
1745
|
stop() {
|
|
1746
|
+
var _a;
|
|
1747
|
+
if (this.stopPromise)
|
|
1748
|
+
return this.stopPromise;
|
|
1458
1749
|
this.stopped = true;
|
|
1750
|
+
this.bondAbortController.abort();
|
|
1751
|
+
const deviceIds = new Set([
|
|
1752
|
+
...this.monitorTokens.keys(),
|
|
1753
|
+
...this.sessionProtocols.keys(),
|
|
1754
|
+
...this.lifecycleOperations.keys(),
|
|
1755
|
+
...(this.runPromiseDeviceId ? [this.runPromiseDeviceId] : []),
|
|
1756
|
+
]);
|
|
1757
|
+
const scans = Array.from(this.scanCleanups, cleanup => cleanup());
|
|
1758
|
+
this.androidPriorityResetTimers.forEach(timeout => clearTimeout(timeout));
|
|
1759
|
+
this.androidPriorityResetTimers.clear();
|
|
1760
|
+
this.androidHighPriorityDevices.clear();
|
|
1761
|
+
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1762
|
+
(_a = this.runPromise) === null || _a === void 0 ? void 0 : _a.reject(error);
|
|
1763
|
+
this.runPromise = null;
|
|
1764
|
+
this.runPromiseDeviceId = null;
|
|
1765
|
+
deviceIds.forEach(uuid => this.rejectProtocolV2Frames(uuid, error));
|
|
1766
|
+
const manager = this.blePlxManager;
|
|
1767
|
+
const pendingConnections = manager
|
|
1768
|
+
? Array.from(this.lifecycleOperations.keys(), uuid => this.runNativeTeardown(uuid, manager, () => __awaiter(this, void 0, void 0, function* () {
|
|
1769
|
+
yield this.runBestEffortNativeOperation('stop: cancel pending device connection', () => manager.cancelDeviceConnection(uuid));
|
|
1770
|
+
})))
|
|
1771
|
+
: [];
|
|
1772
|
+
this.stopPromise = Promise.all([
|
|
1773
|
+
...scans,
|
|
1774
|
+
...pendingConnections,
|
|
1775
|
+
...Array.from(deviceIds, uuid => this.disconnect(uuid)),
|
|
1776
|
+
]).then(() => __awaiter(this, void 0, void 0, function* () {
|
|
1777
|
+
yield this.protocolV2Links.invalidateAllLinks('React Native BLE transport stopped');
|
|
1778
|
+
yield this.waitForManagerReset();
|
|
1779
|
+
this.blePlxManager = undefined;
|
|
1780
|
+
this.emitter = undefined;
|
|
1781
|
+
}));
|
|
1782
|
+
return this.stopPromise;
|
|
1459
1783
|
}
|
|
1460
1784
|
disconnect(session) {
|
|
1461
1785
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -1585,13 +1909,93 @@ class ReactNativeBleTransport {
|
|
|
1585
1909
|
});
|
|
1586
1910
|
}
|
|
1587
1911
|
cancel() {
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1912
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1913
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native transport cancel');
|
|
1914
|
+
const pending = this.runPromise;
|
|
1915
|
+
const deviceId = this.runPromiseDeviceId;
|
|
1916
|
+
if (pending) {
|
|
1917
|
+
pending.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallQueueActionCancelled));
|
|
1918
|
+
if (this.runPromise === pending) {
|
|
1919
|
+
this.runPromise = null;
|
|
1920
|
+
this.runPromiseDeviceId = null;
|
|
1921
|
+
}
|
|
1922
|
+
if (deviceId)
|
|
1923
|
+
yield this.disconnect(deviceId);
|
|
1924
|
+
}
|
|
1925
|
+
});
|
|
1926
|
+
}
|
|
1927
|
+
negotiateAndroidMtu(uuid, manager, device) {
|
|
1928
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1929
|
+
if (!shouldRefreshNegotiatedMtu(device.mtu))
|
|
1930
|
+
return device;
|
|
1931
|
+
const startedAt = Date.now();
|
|
1932
|
+
let timer;
|
|
1933
|
+
let timedOut = false;
|
|
1934
|
+
let negotiated = device;
|
|
1935
|
+
let failure;
|
|
1936
|
+
try {
|
|
1937
|
+
negotiated = yield Promise.race([
|
|
1938
|
+
device.requestMTU(ANDROID_REQUEST_MTU, `${device.id}:mtu:connected:0:${startedAt}`),
|
|
1939
|
+
new Promise((_, reject) => {
|
|
1940
|
+
timer = setTimeout(() => {
|
|
1941
|
+
timedOut = true;
|
|
1942
|
+
reject(new Error(`BLE MTU exchange timeout after ${ANDROID_MTU_EXCHANGE_TIMEOUT_MS}ms`));
|
|
1943
|
+
}, ANDROID_MTU_EXCHANGE_TIMEOUT_MS);
|
|
1944
|
+
}),
|
|
1945
|
+
]);
|
|
1946
|
+
}
|
|
1947
|
+
catch (error) {
|
|
1948
|
+
failure = error instanceof Error ? error.message : String(error);
|
|
1949
|
+
}
|
|
1950
|
+
finally {
|
|
1951
|
+
if (timer)
|
|
1952
|
+
clearTimeout(timer);
|
|
1953
|
+
}
|
|
1954
|
+
Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] BLE MTU exchange ${failure ? 'failed' : 'completed'}`, {
|
|
1955
|
+
connectIdSuffix: uuid.slice(-8),
|
|
1956
|
+
elapsedMs: Date.now() - startedAt,
|
|
1957
|
+
timedOut,
|
|
1958
|
+
actual: negotiated.mtu,
|
|
1959
|
+
error: failure,
|
|
1960
|
+
});
|
|
1961
|
+
if (this.stopped)
|
|
1962
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1963
|
+
if (!timedOut && !isKnownDefaultMtu(negotiated.mtu))
|
|
1964
|
+
return negotiated;
|
|
1965
|
+
const resetManager = this.abandonStalledConnection(uuid, timedOut ? 'mtu-backstop' : 'mtu-default');
|
|
1966
|
+
yield this.dropAndroidLink(uuid, manager, negotiated, timedOut ? 'mtu exchange timeout' : 'default mtu');
|
|
1967
|
+
if (resetManager)
|
|
1968
|
+
throw this.createWedgedBleSetupError();
|
|
1969
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, timedOut
|
|
1970
|
+
? 'BLE MTU exchange did not complete, reconnecting on a fresh link'
|
|
1971
|
+
: `BLE link stayed at the default MTU ${negotiated.mtu}, reconnecting on a fresh link`);
|
|
1972
|
+
});
|
|
1973
|
+
}
|
|
1974
|
+
dropAndroidLink(uuid, manager, device, reason) {
|
|
1975
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1976
|
+
yield this.runNativeTeardown(uuid, manager, () => __awaiter(this, void 0, void 0, function* () {
|
|
1977
|
+
yield Promise.all([
|
|
1978
|
+
this.runBestEffortNativeOperation(`${reason}: cancel manager connection`, () => manager.cancelDeviceConnection(uuid)),
|
|
1979
|
+
this.runBestEffortNativeOperation(`${reason}: cancel device connection`, () => device.cancelConnection()),
|
|
1980
|
+
]);
|
|
1981
|
+
}));
|
|
1982
|
+
const startedAt = Date.now();
|
|
1983
|
+
while (!this.stopped && Date.now() - startedAt < ANDROID_LINK_DROP_QUIET_MS) {
|
|
1984
|
+
yield delay(ANDROID_LINK_DROP_POLL_MS);
|
|
1985
|
+
}
|
|
1986
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE link drop', {
|
|
1987
|
+
connectIdSuffix: uuid.slice(-8),
|
|
1988
|
+
reason,
|
|
1989
|
+
stopped: this.stopped,
|
|
1990
|
+
});
|
|
1991
|
+
});
|
|
1592
1992
|
}
|
|
1593
1993
|
connectWithTimeout(uuid, connect) {
|
|
1594
1994
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1995
|
+
if (this.stopped)
|
|
1996
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1997
|
+
const startedAt = Date.now();
|
|
1998
|
+
let succeeded = false;
|
|
1595
1999
|
let timer;
|
|
1596
2000
|
let timedOut = false;
|
|
1597
2001
|
const pending = connect();
|
|
@@ -1606,6 +2010,7 @@ class ReactNativeBleTransport {
|
|
|
1606
2010
|
}, BLE_CONNECT_TIMEOUT_MS);
|
|
1607
2011
|
}),
|
|
1608
2012
|
]);
|
|
2013
|
+
succeeded = true;
|
|
1609
2014
|
return result;
|
|
1610
2015
|
}
|
|
1611
2016
|
catch (error) {
|
|
@@ -1620,11 +2025,19 @@ class ReactNativeBleTransport {
|
|
|
1620
2025
|
finally {
|
|
1621
2026
|
if (timer)
|
|
1622
2027
|
clearTimeout(timer);
|
|
2028
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] connect completed', {
|
|
2029
|
+
connectIdSuffix: uuid.slice(-8),
|
|
2030
|
+
elapsedMs: Date.now() - startedAt,
|
|
2031
|
+
succeeded,
|
|
2032
|
+
backstopExpired: timedOut,
|
|
2033
|
+
});
|
|
1623
2034
|
}
|
|
1624
2035
|
});
|
|
1625
2036
|
}
|
|
1626
2037
|
resolveCharacteristicsWithTimeout(uuid, device) {
|
|
1627
2038
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2039
|
+
const startedAt = Date.now();
|
|
2040
|
+
let succeeded = false;
|
|
1628
2041
|
let timer;
|
|
1629
2042
|
let timedOut = false;
|
|
1630
2043
|
const pending = this.resolveCharacteristics(device);
|
|
@@ -1640,6 +2053,7 @@ class ReactNativeBleTransport {
|
|
|
1640
2053
|
}),
|
|
1641
2054
|
]);
|
|
1642
2055
|
this.connectionSetupTimeoutCounts.delete(uuid);
|
|
2056
|
+
succeeded = true;
|
|
1643
2057
|
return result;
|
|
1644
2058
|
}
|
|
1645
2059
|
catch (error) {
|
|
@@ -1649,11 +2063,26 @@ class ReactNativeBleTransport {
|
|
|
1649
2063
|
throw this.createWedgedBleSetupError();
|
|
1650
2064
|
}
|
|
1651
2065
|
}
|
|
2066
|
+
if (isNativeBleStaleBondError(error) || hdShared.isBleStaleBondHardwareError(error)) {
|
|
2067
|
+
throw toBleStaleBondHardwareError(error);
|
|
2068
|
+
}
|
|
2069
|
+
if (isNativeBleDisconnectError(error)) {
|
|
2070
|
+
throw toBleDisconnectHardwareError(error);
|
|
2071
|
+
}
|
|
2072
|
+
if (reactNative.Platform.OS === 'android' && isMissingGattShapeError(error)) {
|
|
2073
|
+
this.androidGattCacheRefreshes.add(uuid);
|
|
2074
|
+
}
|
|
1652
2075
|
throw error;
|
|
1653
2076
|
}
|
|
1654
2077
|
finally {
|
|
1655
2078
|
if (timer)
|
|
1656
2079
|
clearTimeout(timer);
|
|
2080
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] GATT setup completed', {
|
|
2081
|
+
connectIdSuffix: uuid.slice(-8),
|
|
2082
|
+
elapsedMs: Date.now() - startedAt,
|
|
2083
|
+
succeeded,
|
|
2084
|
+
backstopExpired: timedOut,
|
|
2085
|
+
});
|
|
1657
2086
|
}
|
|
1658
2087
|
});
|
|
1659
2088
|
}
|
|
@@ -1755,6 +2184,8 @@ class ReactNativeBleTransport {
|
|
|
1755
2184
|
}
|
|
1756
2185
|
}
|
|
1757
2186
|
resetPlxManager() {
|
|
2187
|
+
if (bleManagerResetPromise)
|
|
2188
|
+
return;
|
|
1758
2189
|
const manager = this.blePlxManager;
|
|
1759
2190
|
this.blePlxManager = undefined;
|
|
1760
2191
|
const reason = 'React Native BLE manager reset';
|
|
@@ -1792,16 +2223,25 @@ class ReactNativeBleTransport {
|
|
|
1792
2223
|
this.acquiringProtocolV2.clear();
|
|
1793
2224
|
this.sessionProtocols.clear();
|
|
1794
2225
|
this.protocolReprobeFailures.clear();
|
|
2226
|
+
this.silentDetections.clear();
|
|
1795
2227
|
this.writeTimeoutCounts.clear();
|
|
1796
2228
|
this.connectionSetupTimeoutCounts.clear();
|
|
1797
2229
|
this.monitorTokens.clear();
|
|
1798
2230
|
this.protocolV2Assemblers.clear();
|
|
2231
|
+
let reset;
|
|
1799
2232
|
try {
|
|
1800
|
-
manager === null || manager === void 0 ? void 0 : manager.destroy();
|
|
2233
|
+
reset = Promise.resolve(manager === null || manager === void 0 ? void 0 : manager.destroy());
|
|
1801
2234
|
}
|
|
1802
2235
|
catch (error) {
|
|
1803
|
-
|
|
2236
|
+
reset = Promise.reject(error);
|
|
1804
2237
|
}
|
|
2238
|
+
bleManagerResetPromise = reset;
|
|
2239
|
+
reset.then(() => {
|
|
2240
|
+
if (bleManagerResetPromise === reset)
|
|
2241
|
+
bleManagerResetPromise = undefined;
|
|
2242
|
+
}, error => {
|
|
2243
|
+
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE manager destroy failed:', error);
|
|
2244
|
+
});
|
|
1805
2245
|
}
|
|
1806
2246
|
createProtocolMismatchError(expected) {
|
|
1807
2247
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
|
|
@@ -1857,6 +2297,7 @@ class ReactNativeBleTransport {
|
|
|
1857
2297
|
!protocolHint &&
|
|
1858
2298
|
reprobeFailures < PROTOCOL_REPROBE_FALLBACK_ATTEMPTS;
|
|
1859
2299
|
const probeOrder = trustSessionProtocol ? [sessionProtocol] : fullProbeOrder;
|
|
2300
|
+
yield this.wakeSilentProtocolV1Device(uuid, probeOrder);
|
|
1860
2301
|
for (let i = 0; i < probeOrder.length; i += 1) {
|
|
1861
2302
|
const protocol = probeOrder[i];
|
|
1862
2303
|
if (i > 0) {
|
|
@@ -1876,6 +2317,7 @@ class ReactNativeBleTransport {
|
|
|
1876
2317
|
this.confirmedProtocolV2.add(uuid);
|
|
1877
2318
|
}
|
|
1878
2319
|
this.protocolReprobeFailures.delete(uuid);
|
|
2320
|
+
this.silentDetections.delete(uuid);
|
|
1879
2321
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
|
|
1880
2322
|
deviceId: uuid,
|
|
1881
2323
|
protocol,
|
|
@@ -1884,6 +2326,8 @@ class ReactNativeBleTransport {
|
|
|
1884
2326
|
return protocol;
|
|
1885
2327
|
}
|
|
1886
2328
|
}
|
|
2329
|
+
if (!this.silentDetections.has(uuid))
|
|
2330
|
+
this.silentDetections.set(uuid, 'silent');
|
|
1887
2331
|
if (trustSessionProtocol) {
|
|
1888
2332
|
this.protocolReprobeFailures.set(uuid, reprobeFailures + 1);
|
|
1889
2333
|
}
|
|
@@ -1895,6 +2339,30 @@ class ReactNativeBleTransport {
|
|
|
1895
2339
|
throw this.createProtocolDetectionError();
|
|
1896
2340
|
});
|
|
1897
2341
|
}
|
|
2342
|
+
wakeSilentProtocolV1Device(uuid, probeOrder) {
|
|
2343
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2344
|
+
if (reactNative.Platform.OS !== 'android' ||
|
|
2345
|
+
probeOrder[0] !== 'V1' ||
|
|
2346
|
+
this.silentDetections.get(uuid) !== 'silent') {
|
|
2347
|
+
return;
|
|
2348
|
+
}
|
|
2349
|
+
this.silentDetections.set(uuid, 'woken');
|
|
2350
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] sending Protocol V1 Initialize wake', {
|
|
2351
|
+
connectIdSuffix: uuid.slice(-8),
|
|
2352
|
+
});
|
|
2353
|
+
try {
|
|
2354
|
+
this.probingProtocols.set(uuid, 'V1');
|
|
2355
|
+
yield this.callProtocolV1(uuid, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
|
|
2356
|
+
}
|
|
2357
|
+
catch (error) {
|
|
2358
|
+
if (shouldRethrowProtocolProbeError(error))
|
|
2359
|
+
throw error;
|
|
2360
|
+
}
|
|
2361
|
+
finally {
|
|
2362
|
+
this.clearProbeProtocol(uuid, 'V1');
|
|
2363
|
+
}
|
|
2364
|
+
});
|
|
2365
|
+
}
|
|
1898
2366
|
resetProbeStateAfterProtocolProbe(uuid, protocol) {
|
|
1899
2367
|
var _a, _b, _c;
|
|
1900
2368
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -1951,7 +2419,7 @@ class ReactNativeBleTransport {
|
|
|
1951
2419
|
catch (error) {
|
|
1952
2420
|
this.clearProbeProtocol(uuid, 'V1');
|
|
1953
2421
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe failed:', error);
|
|
1954
|
-
if (
|
|
2422
|
+
if (shouldRethrowProtocolProbeError(error)) {
|
|
1955
2423
|
throw error;
|
|
1956
2424
|
}
|
|
1957
2425
|
return false;
|
|
@@ -1977,7 +2445,7 @@ class ReactNativeBleTransport {
|
|
|
1977
2445
|
(_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1978
2446
|
this.resetProtocolV2Frames(uuid);
|
|
1979
2447
|
},
|
|
1980
|
-
shouldRethrow:
|
|
2448
|
+
shouldRethrow: shouldRethrowProtocolProbeError,
|
|
1981
2449
|
});
|
|
1982
2450
|
if (!detected) {
|
|
1983
2451
|
this.clearProbeProtocol(uuid, 'V2');
|
|
@@ -2104,6 +2572,9 @@ class ReactNativeBleTransport {
|
|
|
2104
2572
|
this.rememberStaleBondError(uuid, bondError);
|
|
2105
2573
|
throw bondError;
|
|
2106
2574
|
}
|
|
2575
|
+
if (isNativeBleDisconnectError(error)) {
|
|
2576
|
+
throw toBleDisconnectHardwareError(error);
|
|
2577
|
+
}
|
|
2107
2578
|
if (getFirmwareUploadWriteRetryType(error) !== 'congested' ||
|
|
2108
2579
|
attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
|
|
2109
2580
|
throw error;
|
|
@@ -2146,6 +2617,7 @@ class ReactNativeBleTransport {
|
|
|
2146
2617
|
if (!this._messages || !this._messagesV2) {
|
|
2147
2618
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
2148
2619
|
}
|
|
2620
|
+
const isProtocolProbe = this.probingProtocols.get(uuid) === 'V2';
|
|
2149
2621
|
const callOptions = options;
|
|
2150
2622
|
const highThroughputWrite = transport.isProtocolV2HighThroughputCall(name);
|
|
2151
2623
|
if (highThroughputWrite) {
|
|
@@ -2186,6 +2658,14 @@ class ReactNativeBleTransport {
|
|
|
2186
2658
|
}
|
|
2187
2659
|
catch (e) {
|
|
2188
2660
|
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] Protocol V2 call error:', e);
|
|
2661
|
+
if (!isProtocolProbe &&
|
|
2662
|
+
(e === null || e === void 0 ? void 0 : e.errorCode) === hdShared.HardwareErrorCode.BleTimeoutError &&
|
|
2663
|
+
!this.monitorTokens.has(uuid)) {
|
|
2664
|
+
yield this.runLifecycleOperation(uuid, () => __awaiter(this, void 0, void 0, function* () {
|
|
2665
|
+
if (!this.monitorTokens.has(uuid))
|
|
2666
|
+
yield this.disconnectUnlocked(uuid);
|
|
2667
|
+
}));
|
|
2668
|
+
}
|
|
2189
2669
|
throw e;
|
|
2190
2670
|
}
|
|
2191
2671
|
finally {
|
|
@@ -2200,7 +2680,7 @@ class ReactNativeBleTransport {
|
|
|
2200
2680
|
const transport = this.getCachedTransport(uuid);
|
|
2201
2681
|
if (!shouldRefreshNegotiatedMtu(transport.mtuSize))
|
|
2202
2682
|
return;
|
|
2203
|
-
const refreshedDevice = yield requestNegotiatedMtu(transport.device, 'highThroughput', 1);
|
|
2683
|
+
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
2684
|
transport.device = refreshedDevice;
|
|
2205
2685
|
transport.mtuSize =
|
|
2206
2686
|
typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport.mtuSize;
|
|
@@ -2317,6 +2797,8 @@ class ReactNativeBleTransport {
|
|
|
2317
2797
|
}
|
|
2318
2798
|
}
|
|
2319
2799
|
|
|
2800
|
+
exports.ANDROID_LINK_DROP_QUIET_MS = ANDROID_LINK_DROP_QUIET_MS;
|
|
2801
|
+
exports.ANDROID_MTU_EXCHANGE_TIMEOUT_MS = ANDROID_MTU_EXCHANGE_TIMEOUT_MS;
|
|
2320
2802
|
exports.BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD;
|
|
2321
2803
|
exports.BLE_CONNECT_TIMEOUT_MS = BLE_CONNECT_TIMEOUT_MS;
|
|
2322
2804
|
exports.BLE_GATT_SETUP_TIMEOUT_MS = BLE_GATT_SETUP_TIMEOUT_MS;
|