@onekeyfe/hd-transport-react-native 1.2.3-alpha.3 → 1.2.3-alpha.4
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 +585 -134
- package/package.json +6 -6
- package/src/BleManager.ts +73 -5
- package/src/__tests__/bleNativeDisconnect.test.ts +33 -0
- package/src/__tests__/connectTimeout.test.ts +427 -3
- package/src/__tests__/protocolV2Link.test.ts +976 -62
- package/src/__tests__/staleCallTimeout.test.ts +34 -5
- package/src/bleNativeDisconnect.ts +40 -0
- package/src/index.ts +633 -149
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
|
});
|
|
@@ -194,6 +231,27 @@ const subscribeBleOn = (bleManager, ms = 1000) => new Promise((resolve, reject)
|
|
|
194
231
|
}, ms);
|
|
195
232
|
});
|
|
196
233
|
|
|
234
|
+
const BLE_PLX_DEVICE_DISCONNECTED = 201;
|
|
235
|
+
const IOS_PERIPHERAL_DISCONNECTED = 7;
|
|
236
|
+
const nativeErrorText$1 = (error) => [error.reason, error.message]
|
|
237
|
+
.filter((value) => typeof value === 'string')
|
|
238
|
+
.join(' ');
|
|
239
|
+
const isNativeBleDisconnectError = (error) => {
|
|
240
|
+
if (!error || typeof error !== 'object') {
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
const nativeError = error;
|
|
244
|
+
return (nativeError.errorCode === BLE_PLX_DEVICE_DISCONNECTED ||
|
|
245
|
+
nativeError.iosErrorCode === IOS_PERIPHERAL_DISCONNECTED);
|
|
246
|
+
};
|
|
247
|
+
const toBleDisconnectHardwareError = (error) => {
|
|
248
|
+
if ((error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleDeviceDisconnected) {
|
|
249
|
+
return error;
|
|
250
|
+
}
|
|
251
|
+
const nativeError = (error !== null && error !== void 0 ? error : {});
|
|
252
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected, nativeErrorText$1(nativeError) || undefined);
|
|
253
|
+
};
|
|
254
|
+
|
|
197
255
|
const ATT_INSUFFICIENT_AUTHENTICATION = 5;
|
|
198
256
|
const ATT_INSUFFICIENT_ENCRYPTION = 15;
|
|
199
257
|
const IOS_PEER_REMOVED_PAIRING_INFORMATION = 14;
|
|
@@ -258,6 +316,7 @@ class BleTransport {
|
|
|
258
316
|
const { check, ProtocolV1, parseConfigure } = transport__default["default"];
|
|
259
317
|
const Log = bleLogger;
|
|
260
318
|
const transportCache = {};
|
|
319
|
+
let bleManagerResetPromise;
|
|
261
320
|
const FIRMWARE_UPLOAD_WRITE_BURST_SIZE = reactNative.Platform.OS === 'ios' ? 4 : 5;
|
|
262
321
|
const FIRMWARE_UPLOAD_WRITE_PAUSE_MS = reactNative.Platform.OS === 'ios' ? 8 : 10;
|
|
263
322
|
const FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS = reactNative.Platform.OS === 'ios' ? 24 : 30;
|
|
@@ -316,6 +375,17 @@ const WEDGED_WRITE_MESSAGE = 'BLE write timeout after';
|
|
|
316
375
|
const isWedgedWriteError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleWriteCharacteristicError &&
|
|
317
376
|
typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' &&
|
|
318
377
|
error.message.startsWith(WEDGED_WRITE_MESSAGE);
|
|
378
|
+
const shouldRethrowProtocolProbeError = (error) => {
|
|
379
|
+
const code = error === null || error === void 0 ? void 0 : error.errorCode;
|
|
380
|
+
return (hdShared.isBleStaleBondHardwareError(error) ||
|
|
381
|
+
isNativeBleDisconnectError(error) ||
|
|
382
|
+
code === hdShared.HardwareErrorCode.BleDeviceNotBonded ||
|
|
383
|
+
code === hdShared.HardwareErrorCode.BleDeviceBondedCanceled ||
|
|
384
|
+
code === hdShared.HardwareErrorCode.BleDeviceDisconnected ||
|
|
385
|
+
code === hdShared.HardwareErrorCode.BleCharacteristicNotifyError ||
|
|
386
|
+
code === hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure ||
|
|
387
|
+
code === hdShared.HardwareErrorCode.BleWriteCharacteristicError);
|
|
388
|
+
};
|
|
319
389
|
const BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
|
|
320
390
|
const DEVICE_SCAN_TIMEOUT_MS = 3000;
|
|
321
391
|
const IOS_NOTIFY_READY_DELAY_MS = 150;
|
|
@@ -350,10 +420,10 @@ function getDeviceDisplayName(device) {
|
|
|
350
420
|
}
|
|
351
421
|
const IOS_REQUEST_MTU = 247;
|
|
352
422
|
const ANDROID_REQUEST_MTU = 517;
|
|
353
|
-
const BLE_MTU_REFRESH_RETRY_DELAY_MS = 200;
|
|
354
423
|
const ANDROID_HIGH_PRIORITY_IDLE_MS = 1000;
|
|
355
424
|
const getRequestedBleMtu = () => reactNative.Platform.OS === 'android' ? ANDROID_REQUEST_MTU : IOS_REQUEST_MTU;
|
|
356
425
|
const BLE_NATIVE_CONNECT_TIMEOUT_MS = 3000;
|
|
426
|
+
const BLE_MTU_REQUEST_TIMEOUT_MS = BLE_NATIVE_CONNECT_TIMEOUT_MS;
|
|
357
427
|
const connectOptions = {
|
|
358
428
|
requestMTU: getRequestedBleMtu(),
|
|
359
429
|
timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
|
|
@@ -362,6 +432,29 @@ const connectOptions = {
|
|
|
362
432
|
const fallbackConnectOptions = {
|
|
363
433
|
timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
|
|
364
434
|
};
|
|
435
|
+
const androidRefreshGattConnectOptions = {
|
|
436
|
+
timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
|
|
437
|
+
refreshGatt: 'OnConnected',
|
|
438
|
+
};
|
|
439
|
+
const ANDROID_MTU_EXCHANGE_TIMEOUT_MS = 12000;
|
|
440
|
+
const ANDROID_LINK_DROP_QUIET_MS = 5000;
|
|
441
|
+
const ANDROID_LINK_DROP_POLL_MS = 250;
|
|
442
|
+
const isKnownDefaultMtu = (mtu) => typeof mtu === 'number' && Number.isFinite(mtu) && mtu <= 23;
|
|
443
|
+
const isMissingGattShapeError = (error) => {
|
|
444
|
+
const code = error === null || error === void 0 ? void 0 : error.errorCode;
|
|
445
|
+
const message = error === null || error === void 0 ? void 0 : error.message;
|
|
446
|
+
return (code === hdShared.HardwareErrorCode.BleServiceNotFound ||
|
|
447
|
+
code === hdShared.HardwareErrorCode.BleCharacteristicNotFound ||
|
|
448
|
+
(typeof message === 'string' &&
|
|
449
|
+
(message.includes('BLECharacteristicNotFound') ||
|
|
450
|
+
message.includes('BLECharacteristicNotWritable') ||
|
|
451
|
+
message.includes('BLECharacteristicNotNotifiable'))));
|
|
452
|
+
};
|
|
453
|
+
const isStaleGattTableNotifyReason = (reason) => !!reason &&
|
|
454
|
+
(reason.includes('Cannot write client characteristic config descriptor') ||
|
|
455
|
+
reason.includes('Cannot find client characteristic config descriptor') ||
|
|
456
|
+
reason.includes('The handle is invalid') ||
|
|
457
|
+
reason.includes('Writing is not permitted'));
|
|
365
458
|
const BLE_CONNECT_TIMEOUT_MS = BLE_NATIVE_CONNECT_TIMEOUT_MS * 2 + 2000;
|
|
366
459
|
const BLE_GATT_SETUP_TIMEOUT_MS = 10000;
|
|
367
460
|
const PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = 3;
|
|
@@ -376,6 +469,11 @@ const isWedgedBleSetupError = (error) => (error === null || error === void 0 ? v
|
|
|
376
469
|
error.message.startsWith(BLE_SETUP_WEDGED_MESSAGE);
|
|
377
470
|
const shouldRethrowBleSetupError = (error) => isConnectTimeoutError(error) || isWedgedBleSetupError(error);
|
|
378
471
|
const isNativeOperationTimeoutError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === reactNativeBlePlx.BleErrorCode.OperationTimedOut;
|
|
472
|
+
const isMtuOrCancelledConnectError = (error) => {
|
|
473
|
+
const errorCode = error === null || error === void 0 ? void 0 : error.errorCode;
|
|
474
|
+
return (errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
|
|
475
|
+
errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled);
|
|
476
|
+
};
|
|
379
477
|
const tryToGetConfiguration = (device) => {
|
|
380
478
|
if (!device || !device.serviceUUIDs)
|
|
381
479
|
return null;
|
|
@@ -387,25 +485,65 @@ const tryToGetConfiguration = (device) => {
|
|
|
387
485
|
return null;
|
|
388
486
|
return infos;
|
|
389
487
|
};
|
|
390
|
-
const requestNegotiatedMtu = (device, stage, attempt) => __awaiter(void 0, void 0, void 0, function* () {
|
|
488
|
+
const requestNegotiatedMtu = (device, stage, attempt, cancelTransaction) => __awaiter(void 0, void 0, void 0, function* () {
|
|
391
489
|
if (reactNative.Platform.OS !== 'ios' && reactNative.Platform.OS !== 'android')
|
|
392
|
-
return device;
|
|
490
|
+
return { device, timedOut: false };
|
|
491
|
+
const transactionId = `${device.id}:mtu:${stage}:${attempt}:${Date.now()}`;
|
|
492
|
+
let timeoutId;
|
|
493
|
+
let timedOut = false;
|
|
393
494
|
try {
|
|
394
|
-
const
|
|
395
|
-
|
|
495
|
+
const request = device.requestMTU(getRequestedBleMtu(), transactionId);
|
|
496
|
+
request.catch(() => undefined);
|
|
497
|
+
const mtuDevice = yield Promise.race([
|
|
498
|
+
request,
|
|
499
|
+
new Promise((_, reject) => {
|
|
500
|
+
timeoutId = setTimeout(() => {
|
|
501
|
+
timedOut = true;
|
|
502
|
+
reject(new Error(`BLE MTU request timeout after ${BLE_MTU_REQUEST_TIMEOUT_MS}ms`));
|
|
503
|
+
}, BLE_MTU_REQUEST_TIMEOUT_MS);
|
|
504
|
+
}),
|
|
505
|
+
]);
|
|
506
|
+
return { device: mtuDevice, timedOut: false };
|
|
396
507
|
}
|
|
397
508
|
catch (error) {
|
|
509
|
+
if (timedOut && cancelTransaction) {
|
|
510
|
+
try {
|
|
511
|
+
Promise.resolve(cancelTransaction(transactionId)).catch(cancelError => {
|
|
512
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] MTU cancellation failed', {
|
|
513
|
+
platform: reactNative.Platform.OS,
|
|
514
|
+
stage,
|
|
515
|
+
attempt,
|
|
516
|
+
error: cancelError instanceof Error ? cancelError.message : String(cancelError),
|
|
517
|
+
});
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
catch (cancelError) {
|
|
521
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] MTU cancellation failed', {
|
|
522
|
+
platform: reactNative.Platform.OS,
|
|
523
|
+
stage,
|
|
524
|
+
attempt,
|
|
525
|
+
error: cancelError instanceof Error ? cancelError.message : String(cancelError),
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
}
|
|
398
529
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] MTU refresh failed, continuing with current value', {
|
|
399
530
|
platform: reactNative.Platform.OS,
|
|
400
531
|
stage,
|
|
401
532
|
attempt,
|
|
402
533
|
actual: device.mtu,
|
|
534
|
+
timedOut,
|
|
403
535
|
error: error instanceof Error ? error.message : String(error),
|
|
404
536
|
});
|
|
405
|
-
return device;
|
|
537
|
+
return { device, timedOut };
|
|
538
|
+
}
|
|
539
|
+
finally {
|
|
540
|
+
if (timeoutId)
|
|
541
|
+
clearTimeout(timeoutId);
|
|
406
542
|
}
|
|
407
543
|
});
|
|
408
|
-
const resolveNegotiatedMtu = (device) =>
|
|
544
|
+
const resolveNegotiatedMtu = (device, cancelTransaction) => shouldRefreshNegotiatedMtu(device.mtu)
|
|
545
|
+
? requestNegotiatedMtu(device, 'connected', 0, cancelTransaction)
|
|
546
|
+
: Promise.resolve({ device, timedOut: false });
|
|
409
547
|
function remapError(error) {
|
|
410
548
|
var _a;
|
|
411
549
|
if (error instanceof reactNativeBlePlx.BleError) {
|
|
@@ -429,6 +567,7 @@ class ReactNativeBleTransport {
|
|
|
429
567
|
this.name = 'ReactNativeBleTransport';
|
|
430
568
|
this.configured = false;
|
|
431
569
|
this.stopped = false;
|
|
570
|
+
this.bondAbortController = new AbortController();
|
|
432
571
|
this.scanTimeout = DEVICE_SCAN_TIMEOUT_MS;
|
|
433
572
|
this.runPromise = null;
|
|
434
573
|
this.runPromiseDeviceId = null;
|
|
@@ -441,6 +580,8 @@ class ReactNativeBleTransport {
|
|
|
441
580
|
this.sessionProtocols = new Map();
|
|
442
581
|
this.confirmedProtocolV2 = new Set();
|
|
443
582
|
this.protocolReprobeFailures = new Map();
|
|
583
|
+
this.silentDetections = new Map();
|
|
584
|
+
this.androidGattCacheRefreshes = new Set();
|
|
444
585
|
this.staleBondErrors = new Map();
|
|
445
586
|
this.acquiringProtocolV2 = new Set();
|
|
446
587
|
this.protocolV2Assemblers = new Map();
|
|
@@ -458,11 +599,20 @@ class ReactNativeBleTransport {
|
|
|
458
599
|
},
|
|
459
600
|
classifyError: () => 'link-fatal',
|
|
460
601
|
onLinkInvalidated: (uuid, reason) => __awaiter(this, void 0, void 0, function* () {
|
|
461
|
-
var _b;
|
|
602
|
+
var _b, _c;
|
|
462
603
|
(_b = this.protocolV2Assemblers.get(uuid)) === null || _b === void 0 ? void 0 : _b.reset();
|
|
463
604
|
this.rejectProtocolV2Frames(uuid, new Error(reason));
|
|
464
605
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 link invalidated:', uuid, reason);
|
|
465
606
|
if (reason.startsWith('Protocol V2 link-fatal error:')) {
|
|
607
|
+
if (this.probingProtocols.get(uuid) !== 'V2') {
|
|
608
|
+
const transport = transportCache[uuid];
|
|
609
|
+
try {
|
|
610
|
+
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);
|
|
611
|
+
}
|
|
612
|
+
catch (_d) {
|
|
613
|
+
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] Protocol V2 disconnect listener failed');
|
|
614
|
+
}
|
|
615
|
+
}
|
|
466
616
|
yield this.releaseNative(uuid, true);
|
|
467
617
|
}
|
|
468
618
|
}),
|
|
@@ -474,6 +624,7 @@ class ReactNativeBleTransport {
|
|
|
474
624
|
this.androidPriorityResetTimers = new Map();
|
|
475
625
|
this.nextMonitorToken = 1;
|
|
476
626
|
this.lifecycleOperations = new Map();
|
|
627
|
+
this.scanCleanups = new Set();
|
|
477
628
|
this.scanTimeout = (_a = options.scanTimeout) !== null && _a !== void 0 ? _a : DEVICE_SCAN_TIMEOUT_MS;
|
|
478
629
|
}
|
|
479
630
|
init(logger, emitter) {
|
|
@@ -502,10 +653,38 @@ class ReactNativeBleTransport {
|
|
|
502
653
|
listen() {
|
|
503
654
|
}
|
|
504
655
|
getPlxManager() {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
656
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
657
|
+
while (bleManagerResetPromise) {
|
|
658
|
+
yield this.waitForManagerReset();
|
|
659
|
+
}
|
|
660
|
+
if (this.stopped)
|
|
661
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
662
|
+
if (!this.blePlxManager)
|
|
663
|
+
this.blePlxManager = new reactNativeBlePlx.BleManager();
|
|
664
|
+
return this.blePlxManager;
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
waitForManagerReset() {
|
|
668
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
669
|
+
if (!bleManagerResetPromise)
|
|
670
|
+
return;
|
|
671
|
+
let timeout;
|
|
672
|
+
try {
|
|
673
|
+
yield Promise.race([
|
|
674
|
+
bleManagerResetPromise,
|
|
675
|
+
new Promise((_, reject) => {
|
|
676
|
+
timeout = setTimeout(() => reject(this.createWedgedBleSetupError()), BLE_CONNECT_TIMEOUT_MS);
|
|
677
|
+
}),
|
|
678
|
+
]);
|
|
679
|
+
}
|
|
680
|
+
catch (_a) {
|
|
681
|
+
throw this.createWedgedBleSetupError();
|
|
682
|
+
}
|
|
683
|
+
finally {
|
|
684
|
+
if (timeout)
|
|
685
|
+
clearTimeout(timeout);
|
|
686
|
+
}
|
|
687
|
+
});
|
|
509
688
|
}
|
|
510
689
|
resolveCharacteristics(device) {
|
|
511
690
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -670,30 +849,55 @@ class ReactNativeBleTransport {
|
|
|
670
849
|
}
|
|
671
850
|
enumerate() {
|
|
672
851
|
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
|
-
}
|
|
852
|
+
const scanStartedAt = Date.now();
|
|
853
|
+
let firstDeviceMs;
|
|
854
|
+
const blePlxManager = yield this.getPlxManager();
|
|
855
|
+
yield subscribeBleOn(blePlxManager);
|
|
856
|
+
if (reactNative.Platform.OS === 'android' && reactNative.Platform.Version >= 31) {
|
|
857
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('requesting permissions, please wait...');
|
|
858
|
+
const resultConnect = yield reactNative.PermissionsAndroid.requestMultiple([
|
|
859
|
+
reactNative.PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT,
|
|
860
|
+
reactNative.PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN,
|
|
861
|
+
]);
|
|
862
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('requesting permissions, result: ', resultConnect);
|
|
863
|
+
if (resultConnect[reactNative.PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT] !== 'granted' ||
|
|
864
|
+
resultConnect[reactNative.PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN] !== 'granted') {
|
|
865
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePermissionError);
|
|
696
866
|
}
|
|
867
|
+
}
|
|
868
|
+
if (this.stopped)
|
|
869
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
870
|
+
return new Promise((resolve, reject) => {
|
|
871
|
+
const deviceList = [];
|
|
872
|
+
let finished = false;
|
|
873
|
+
let scanCleanup;
|
|
874
|
+
const finishScan = (error) => {
|
|
875
|
+
if (scanCleanup)
|
|
876
|
+
return scanCleanup;
|
|
877
|
+
finished = true;
|
|
878
|
+
clearScanTimer();
|
|
879
|
+
scanCleanup = this.runNativeTeardown('scan', blePlxManager, () => __awaiter(this, void 0, void 0, function* () {
|
|
880
|
+
yield blePlxManager.stopDeviceScan();
|
|
881
|
+
})).then(() => {
|
|
882
|
+
this.scanCleanups.delete(cancelScan);
|
|
883
|
+
if (error)
|
|
884
|
+
reject(error);
|
|
885
|
+
else
|
|
886
|
+
resolve(deviceList);
|
|
887
|
+
});
|
|
888
|
+
return scanCleanup;
|
|
889
|
+
};
|
|
890
|
+
const cancelScan = () => finishScan(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected));
|
|
891
|
+
this.scanCleanups.add(cancelScan);
|
|
892
|
+
const clearScanTimer = timer.timeout(() => {
|
|
893
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] scan completed', {
|
|
894
|
+
elapsedMs: Date.now() - scanStartedAt,
|
|
895
|
+
firstDeviceMs,
|
|
896
|
+
deviceCount: deviceList.length,
|
|
897
|
+
scanWindowMs: this.scanTimeout,
|
|
898
|
+
});
|
|
899
|
+
finishScan();
|
|
900
|
+
}, this.scanTimeout);
|
|
697
901
|
blePlxManager.startDeviceScan(getBluetoothServiceUuids(), {
|
|
698
902
|
allowDuplicates: true,
|
|
699
903
|
scanMode: reactNativeBlePlx.ScanMode.LowLatency,
|
|
@@ -702,19 +906,17 @@ class ReactNativeBleTransport {
|
|
|
702
906
|
if (error) {
|
|
703
907
|
Log === null || Log === void 0 ? void 0 : Log.debug('ble scan error: ', error);
|
|
704
908
|
if ([reactNativeBlePlx.BleErrorCode.BluetoothPoweredOff, reactNativeBlePlx.BleErrorCode.BluetoothInUnknownState].includes(error.errorCode)) {
|
|
705
|
-
|
|
909
|
+
finishScan(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePermissionError));
|
|
706
910
|
}
|
|
707
911
|
else if (error.errorCode === reactNativeBlePlx.BleErrorCode.BluetoothUnauthorized) {
|
|
708
|
-
|
|
912
|
+
finishScan(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleLocationError));
|
|
709
913
|
}
|
|
710
914
|
else if (error.errorCode === reactNativeBlePlx.BleErrorCode.LocationServicesDisabled) {
|
|
711
|
-
|
|
712
|
-
}
|
|
713
|
-
else if (error.errorCode === reactNativeBlePlx.BleErrorCode.ScanStartFailed) {
|
|
714
|
-
timer.timeout(() => { }, this.scanTimeout);
|
|
915
|
+
finishScan(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleLocationServicesDisabled));
|
|
715
916
|
}
|
|
917
|
+
else if (error.errorCode === reactNativeBlePlx.BleErrorCode.ScanStartFailed) ;
|
|
716
918
|
else {
|
|
717
|
-
|
|
919
|
+
finishScan(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleScanError, (_a = error.reason) !== null && _a !== void 0 ? _a : ''));
|
|
718
920
|
}
|
|
719
921
|
return;
|
|
720
922
|
}
|
|
@@ -739,6 +941,8 @@ class ReactNativeBleTransport {
|
|
|
739
941
|
});
|
|
740
942
|
}
|
|
741
943
|
});
|
|
944
|
+
if (finished)
|
|
945
|
+
return;
|
|
742
946
|
getConnectedDeviceIds(reactNative.Platform.OS === 'ios' ? getBluetoothServiceUuids() : []).then(devices => {
|
|
743
947
|
for (const device of devices) {
|
|
744
948
|
const localName = 'localName' in device && typeof device.localName === 'string'
|
|
@@ -754,10 +958,11 @@ class ReactNativeBleTransport {
|
|
|
754
958
|
addDevice(device);
|
|
755
959
|
}
|
|
756
960
|
}
|
|
757
|
-
});
|
|
961
|
+
}, error => Log === null || Log === void 0 ? void 0 : Log.debug('search connected peripheral failed:', error));
|
|
758
962
|
const addDevice = (device) => {
|
|
759
963
|
var _a;
|
|
760
|
-
if (deviceList.every(d => d.id !== device.id)) {
|
|
964
|
+
if (!finished && deviceList.every(d => d.id !== device.id)) {
|
|
965
|
+
firstDeviceMs !== null && firstDeviceMs !== void 0 ? firstDeviceMs : (firstDeviceMs = Date.now() - scanStartedAt);
|
|
761
966
|
const displayName = (_a = getDeviceDisplayName(device)) !== null && _a !== void 0 ? _a : 'Unknown BLE Device';
|
|
762
967
|
deviceList.push(Object.assign(Object.assign({}, device), { name: displayName, commType: 'ble' }));
|
|
763
968
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] OneKey BLE device discovered', {
|
|
@@ -767,16 +972,14 @@ class ReactNativeBleTransport {
|
|
|
767
972
|
});
|
|
768
973
|
}
|
|
769
974
|
};
|
|
770
|
-
|
|
771
|
-
blePlxManager.stopDeviceScan();
|
|
772
|
-
resolve(deviceList);
|
|
773
|
-
}, this.scanTimeout);
|
|
774
|
-
}));
|
|
975
|
+
});
|
|
775
976
|
});
|
|
776
977
|
}
|
|
777
978
|
installTransportForAcquire(uuid, device, characteristics) {
|
|
778
979
|
return __awaiter(this, void 0, void 0, function* () {
|
|
779
980
|
const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristicsWithTimeout(uuid, device));
|
|
981
|
+
if (this.stopped)
|
|
982
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
780
983
|
const transport$1 = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
|
|
781
984
|
transport$1.mtuSize = typeof device.mtu === 'number' ? device.mtu : undefined;
|
|
782
985
|
const monitorToken = this.nextMonitorToken;
|
|
@@ -797,30 +1000,12 @@ class ReactNativeBleTransport {
|
|
|
797
1000
|
else if (reactNative.Platform.OS === 'android') {
|
|
798
1001
|
yield delay(ANDROID_NOTIFY_READY_DELAY_MS);
|
|
799
1002
|
}
|
|
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
|
-
}
|
|
1003
|
+
if (this.stopped)
|
|
1004
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
818
1005
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE MTU ready', {
|
|
819
1006
|
platform: reactNative.Platform.OS,
|
|
820
1007
|
requested: getRequestedBleMtu(),
|
|
821
|
-
initial: initialMtu,
|
|
822
1008
|
actual: transport$1.mtuSize,
|
|
823
|
-
refreshAttempts,
|
|
824
1009
|
});
|
|
825
1010
|
return transport$1;
|
|
826
1011
|
});
|
|
@@ -837,6 +1022,8 @@ class ReactNativeBleTransport {
|
|
|
837
1022
|
acquireUnlocked(input) {
|
|
838
1023
|
var _a;
|
|
839
1024
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1025
|
+
if (this.stopped)
|
|
1026
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
840
1027
|
const { uuid, forceCleanRunPromise, expectedProtocol, skipProtocolProbe } = input;
|
|
841
1028
|
const shouldMapProtocolV2StaleBond = expectedProtocol
|
|
842
1029
|
? expectedProtocol === 'V2'
|
|
@@ -868,9 +1055,13 @@ class ReactNativeBleTransport {
|
|
|
868
1055
|
const isCachedDeviceConnected = yield cachedTransport.device
|
|
869
1056
|
.isConnected()
|
|
870
1057
|
.catch(() => false);
|
|
1058
|
+
const isCachedAndroidLinkUsable = reactNative.Platform.OS !== 'android' || !this.androidGattCacheRefreshes.has(uuid);
|
|
871
1059
|
if (isCachedDeviceConnected &&
|
|
1060
|
+
isCachedAndroidLinkUsable &&
|
|
872
1061
|
cachedProtocol &&
|
|
873
1062
|
(!expectedProtocol || cachedProtocol === expectedProtocol)) {
|
|
1063
|
+
if (this.stopped)
|
|
1064
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
874
1065
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] reuse cached BLE transport:', uuid, cachedProtocol);
|
|
875
1066
|
return { uuid, protocolType: cachedProtocol };
|
|
876
1067
|
}
|
|
@@ -879,6 +1070,15 @@ class ReactNativeBleTransport {
|
|
|
879
1070
|
}
|
|
880
1071
|
}
|
|
881
1072
|
let device = null;
|
|
1073
|
+
const isAndroid = reactNative.Platform.OS === 'android';
|
|
1074
|
+
const refreshAndroidGattCache = isAndroid && (!!skipProtocolProbe || this.androidGattCacheRefreshes.has(uuid));
|
|
1075
|
+
let nativeConnectOptions = connectOptions;
|
|
1076
|
+
if (isAndroid) {
|
|
1077
|
+
nativeConnectOptions = refreshAndroidGattCache
|
|
1078
|
+
? androidRefreshGattConnectOptions
|
|
1079
|
+
: fallbackConnectOptions;
|
|
1080
|
+
}
|
|
1081
|
+
let androidRefreshConnectRan = false;
|
|
882
1082
|
if (forceCleanRunPromise && this.runPromise) {
|
|
883
1083
|
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
|
|
884
1084
|
this.runPromise.reject(error);
|
|
@@ -887,6 +1087,7 @@ class ReactNativeBleTransport {
|
|
|
887
1087
|
Log === null || Log === void 0 ? void 0 : Log.debug('Force clean Bluetooth run promise, forceCleanRunPromise: ', forceCleanRunPromise);
|
|
888
1088
|
}
|
|
889
1089
|
const blePlxManager = yield this.getPlxManager();
|
|
1090
|
+
let skipPostConnectMtu = false;
|
|
890
1091
|
try {
|
|
891
1092
|
yield subscribeBleOn(blePlxManager);
|
|
892
1093
|
}
|
|
@@ -894,6 +1095,27 @@ class ReactNativeBleTransport {
|
|
|
894
1095
|
Log === null || Log === void 0 ? void 0 : Log.debug('subscribeBleOn error: ', error);
|
|
895
1096
|
throw error;
|
|
896
1097
|
}
|
|
1098
|
+
if (this.stopped)
|
|
1099
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1100
|
+
if (reactNative.Platform.OS === 'android') {
|
|
1101
|
+
try {
|
|
1102
|
+
const bondState = yield pairDevice(uuid);
|
|
1103
|
+
if (bondState.bonding) {
|
|
1104
|
+
yield onDeviceBondState(uuid, this.bondAbortController.signal);
|
|
1105
|
+
}
|
|
1106
|
+
else if (!bondState.bonded) {
|
|
1107
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'device is not bonded');
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
catch (error) {
|
|
1111
|
+
yield this.runNativeTeardown(uuid, blePlxManager, () => __awaiter(this, void 0, void 0, function* () {
|
|
1112
|
+
yield this.runBestEffortNativeOperation('bond failure: cancel manager connection', () => blePlxManager.cancelDeviceConnection(uuid));
|
|
1113
|
+
}));
|
|
1114
|
+
throw error;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
if (this.stopped)
|
|
1118
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
897
1119
|
if (!device) {
|
|
898
1120
|
const devices = yield blePlxManager.devices([uuid]);
|
|
899
1121
|
[device] = devices;
|
|
@@ -907,15 +1129,16 @@ class ReactNativeBleTransport {
|
|
|
907
1129
|
if (!device) {
|
|
908
1130
|
Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device: ', uuid);
|
|
909
1131
|
try {
|
|
910
|
-
device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid,
|
|
1132
|
+
device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, nativeConnectOptions));
|
|
1133
|
+
androidRefreshConnectRan = refreshAndroidGattCache;
|
|
911
1134
|
}
|
|
912
1135
|
catch (e) {
|
|
913
1136
|
Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device has error: ', e);
|
|
914
1137
|
if (shouldRethrowBleSetupError(e)) {
|
|
915
1138
|
throw e;
|
|
916
1139
|
}
|
|
917
|
-
if (e
|
|
918
|
-
|
|
1140
|
+
if (isMtuOrCancelledConnectError(e)) {
|
|
1141
|
+
skipPostConnectMtu = true;
|
|
919
1142
|
Log === null || Log === void 0 ? void 0 : Log.debug('first try to reconnect without params');
|
|
920
1143
|
device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, fallbackConnectOptions));
|
|
921
1144
|
}
|
|
@@ -931,19 +1154,27 @@ class ReactNativeBleTransport {
|
|
|
931
1154
|
if (!device) {
|
|
932
1155
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, 'unable to connect to device');
|
|
933
1156
|
}
|
|
1157
|
+
if (refreshAndroidGattCache &&
|
|
1158
|
+
!androidRefreshConnectRan &&
|
|
1159
|
+
(yield device.isConnected().catch(() => false))) {
|
|
1160
|
+
yield this.dropAndroidLink(uuid, blePlxManager, device, 'gatt cache refresh');
|
|
1161
|
+
if (this.stopped)
|
|
1162
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1163
|
+
}
|
|
934
1164
|
if (!(yield device.isConnected())) {
|
|
935
1165
|
Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device: ', uuid);
|
|
936
1166
|
const disconnectedDevice = device;
|
|
937
1167
|
try {
|
|
938
|
-
device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(
|
|
1168
|
+
device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(nativeConnectOptions));
|
|
1169
|
+
androidRefreshConnectRan = refreshAndroidGattCache;
|
|
939
1170
|
}
|
|
940
1171
|
catch (e) {
|
|
941
1172
|
Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device has error: ', e);
|
|
942
1173
|
if (shouldRethrowBleSetupError(e)) {
|
|
943
1174
|
throw e;
|
|
944
1175
|
}
|
|
945
|
-
if (e
|
|
946
|
-
|
|
1176
|
+
if (isMtuOrCancelledConnectError(e)) {
|
|
1177
|
+
skipPostConnectMtu = true;
|
|
947
1178
|
Log === null || Log === void 0 ? void 0 : Log.debug('second try to reconnect without params');
|
|
948
1179
|
try {
|
|
949
1180
|
device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
|
|
@@ -965,33 +1196,70 @@ class ReactNativeBleTransport {
|
|
|
965
1196
|
}
|
|
966
1197
|
}
|
|
967
1198
|
}
|
|
968
|
-
if (
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
1199
|
+
if (this.stopped)
|
|
1200
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1201
|
+
if (reactNative.Platform.OS === 'android' && !(yield device.isConnected().catch(() => false))) {
|
|
1202
|
+
const disconnectedDevice = device;
|
|
1203
|
+
yield this.runNativeTeardown(uuid, blePlxManager, () => __awaiter(this, void 0, void 0, function* () {
|
|
1204
|
+
yield Promise.all([
|
|
1205
|
+
this.runBestEffortNativeOperation('connect failure: cancel manager connection', () => blePlxManager.cancelDeviceConnection(uuid)),
|
|
1206
|
+
this.runBestEffortNativeOperation('connect failure: cancel device connection', () => disconnectedDevice.cancelConnection()),
|
|
1207
|
+
]);
|
|
1208
|
+
}));
|
|
1209
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, 'device is not connected');
|
|
1210
|
+
}
|
|
1211
|
+
if (this.stopped)
|
|
1212
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1213
|
+
let characteristics;
|
|
1214
|
+
if (isAndroid) {
|
|
1215
|
+
if (refreshAndroidGattCache) {
|
|
1216
|
+
characteristics = yield this.resolveCharacteristicsWithTimeout(uuid, device);
|
|
1217
|
+
if (androidRefreshConnectRan)
|
|
1218
|
+
this.androidGattCacheRefreshes.delete(uuid);
|
|
1219
|
+
if (this.stopped)
|
|
1220
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1221
|
+
}
|
|
1222
|
+
device = yield this.negotiateAndroidMtu(uuid, blePlxManager, device);
|
|
1223
|
+
}
|
|
1224
|
+
else if (!skipPostConnectMtu) {
|
|
1225
|
+
const mtuResult = yield resolveNegotiatedMtu(device, transactionId => blePlxManager.cancelTransaction(transactionId));
|
|
1226
|
+
device = mtuResult.device;
|
|
1227
|
+
if (mtuResult.timedOut) {
|
|
1228
|
+
if (this.stopped)
|
|
1229
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1230
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] post-connect MTU timed out, reconnecting without requesting MTU');
|
|
1231
|
+
const timedOutDevice = device;
|
|
1232
|
+
let mtuTeardownSettled = false;
|
|
1233
|
+
yield this.runNativeTeardown(uuid, blePlxManager, () => __awaiter(this, void 0, void 0, function* () {
|
|
1234
|
+
yield this.runBestEffortNativeOperation('mtu timeout: cancel device connection', () => timedOutDevice.cancelConnection());
|
|
1235
|
+
mtuTeardownSettled = true;
|
|
1236
|
+
}));
|
|
1237
|
+
if (this.stopped)
|
|
1238
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1239
|
+
if (!mtuTeardownSettled || this.blePlxManager !== blePlxManager) {
|
|
1240
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'BLE MTU cleanup timed out');
|
|
973
1241
|
}
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
yield onDeviceBondState(uuid);
|
|
1242
|
+
try {
|
|
1243
|
+
device = yield this.connectWithTimeout(uuid, () => timedOutDevice.connect(fallbackConnectOptions));
|
|
977
1244
|
}
|
|
978
|
-
|
|
979
|
-
|
|
1245
|
+
catch (error) {
|
|
1246
|
+
if (shouldRethrowBleSetupError(error))
|
|
1247
|
+
throw error;
|
|
1248
|
+
if ((error === null || error === void 0 ? void 0 : error.errorCode) === reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
|
|
1249
|
+
device = timedOutDevice;
|
|
1250
|
+
}
|
|
1251
|
+
else {
|
|
1252
|
+
remapError(error);
|
|
1253
|
+
}
|
|
980
1254
|
}
|
|
981
1255
|
}
|
|
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
1256
|
}
|
|
992
|
-
|
|
1257
|
+
if (this.stopped)
|
|
1258
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
993
1259
|
const acquiredDevice = device;
|
|
994
|
-
const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, acquiredDevice);
|
|
1260
|
+
const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristicsWithTimeout(uuid, acquiredDevice));
|
|
1261
|
+
if (this.stopped)
|
|
1262
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
995
1263
|
const protocolHint = expectedProtocol
|
|
996
1264
|
? undefined
|
|
997
1265
|
: (_a = input.protocolHint) !== null && _a !== void 0 ? _a : this.deviceProtocolHints.get(uuid);
|
|
@@ -1034,6 +1302,8 @@ class ReactNativeBleTransport {
|
|
|
1034
1302
|
const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint, () => __awaiter(this, void 0, void 0, function* () {
|
|
1035
1303
|
yield this.installTransportForAcquire(uuid, acquiredDevice);
|
|
1036
1304
|
}));
|
|
1305
|
+
if (this.stopped)
|
|
1306
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1037
1307
|
const currentTransport = transportCache[uuid];
|
|
1038
1308
|
if (!currentTransport) {
|
|
1039
1309
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
|
|
@@ -1042,12 +1312,7 @@ class ReactNativeBleTransport {
|
|
|
1042
1312
|
return { uuid, protocolType };
|
|
1043
1313
|
}
|
|
1044
1314
|
catch (error) {
|
|
1045
|
-
|
|
1046
|
-
yield this.disconnectUnlocked(uuid);
|
|
1047
|
-
}
|
|
1048
|
-
else {
|
|
1049
|
-
yield this.releaseUnlocked(uuid, true);
|
|
1050
|
-
}
|
|
1315
|
+
yield this.disconnectUnlocked(uuid);
|
|
1051
1316
|
throw error;
|
|
1052
1317
|
}
|
|
1053
1318
|
finally {
|
|
@@ -1059,7 +1324,7 @@ class ReactNativeBleTransport {
|
|
|
1059
1324
|
let bufferLength = 0;
|
|
1060
1325
|
let buffer$1 = [];
|
|
1061
1326
|
const subscription = characteristic.monitor((error, c) => {
|
|
1062
|
-
var _a, _b, _c, _d, _e, _f
|
|
1327
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1063
1328
|
const isCurrentMonitor = this.monitorTokens.get(uuid) === monitorToken;
|
|
1064
1329
|
if (error) {
|
|
1065
1330
|
Log === null || Log === void 0 ? void 0 : Log.debug(`error monitor ${characteristic.uuid}, deviceId: ${characteristic.deviceID}: ${error}`);
|
|
@@ -1076,16 +1341,16 @@ class ReactNativeBleTransport {
|
|
|
1076
1341
|
this.rememberStaleBondError(uuid, toBleStaleBondHardwareError(error));
|
|
1077
1342
|
return;
|
|
1078
1343
|
}
|
|
1344
|
+
if (reactNative.Platform.OS === 'android' && isStaleGattTableNotifyReason(error.reason)) {
|
|
1345
|
+
this.androidGattCacheRefreshes.add(uuid);
|
|
1346
|
+
}
|
|
1079
1347
|
if (this.getActiveProtocol(uuid) === 'V2') {
|
|
1080
1348
|
let errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
|
|
1081
1349
|
if ((_a = error.reason) === null || _a === void 0 ? void 0 : _a.includes('The connection has timed out unexpectedly')) {
|
|
1082
1350
|
errorCode = hdShared.HardwareErrorCode.BleTimeoutError;
|
|
1083
1351
|
}
|
|
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'))) {
|
|
1352
|
+
else if (isStaleGattTableNotifyReason(error.reason) ||
|
|
1353
|
+
((_b = error.reason) === null || _b === void 0 ? void 0 : _b.includes('notify change failed for device'))) {
|
|
1089
1354
|
errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure;
|
|
1090
1355
|
}
|
|
1091
1356
|
this.rejectProtocolV2Frames(uuid, hdShared.ERRORS.TypedError(errorCode));
|
|
@@ -1093,14 +1358,11 @@ class ReactNativeBleTransport {
|
|
|
1093
1358
|
}
|
|
1094
1359
|
if (this.runPromise && this.runPromiseDeviceId === uuid) {
|
|
1095
1360
|
let ERROR = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
|
|
1096
|
-
if ((
|
|
1361
|
+
if ((_c = error.reason) === null || _c === void 0 ? void 0 : _c.includes('The connection has timed out unexpectedly')) {
|
|
1097
1362
|
ERROR = hdShared.HardwareErrorCode.BleTimeoutError;
|
|
1098
1363
|
}
|
|
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'))) {
|
|
1364
|
+
if (isStaleGattTableNotifyReason(error.reason) ||
|
|
1365
|
+
((_d = error.reason) === null || _d === void 0 ? void 0 : _d.includes('notify change failed for device'))) {
|
|
1104
1366
|
const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure);
|
|
1105
1367
|
this.runPromise.reject(notifyError);
|
|
1106
1368
|
Log === null || Log === void 0 ? void 0 : Log.debug(`${hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure} ${error.message} ${error.reason}`);
|
|
@@ -1142,7 +1404,7 @@ class ReactNativeBleTransport {
|
|
|
1142
1404
|
bufferLength = 0;
|
|
1143
1405
|
buffer$1 = [];
|
|
1144
1406
|
if (this.runPromiseDeviceId === uuid) {
|
|
1145
|
-
(
|
|
1407
|
+
(_e = this.runPromise) === null || _e === void 0 ? void 0 : _e.resolve(value.toString('hex'));
|
|
1146
1408
|
}
|
|
1147
1409
|
}
|
|
1148
1410
|
}
|
|
@@ -1153,7 +1415,7 @@ class ReactNativeBleTransport {
|
|
|
1153
1415
|
this.rejectProtocolV2Frames(uuid, notifyError);
|
|
1154
1416
|
}
|
|
1155
1417
|
else if (this.runPromiseDeviceId === uuid) {
|
|
1156
|
-
(
|
|
1418
|
+
(_f = this.runPromise) === null || _f === void 0 ? void 0 : _f.reject(notifyError);
|
|
1157
1419
|
}
|
|
1158
1420
|
}
|
|
1159
1421
|
}, notifyTransactionId);
|
|
@@ -1429,13 +1691,14 @@ class ReactNativeBleTransport {
|
|
|
1429
1691
|
return check.call(jsonData);
|
|
1430
1692
|
}
|
|
1431
1693
|
catch (e) {
|
|
1432
|
-
|
|
1433
|
-
|
|
1694
|
+
const isProbeTimeout = (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS &&
|
|
1695
|
+
(name === 'GetFeatures' || name === 'Initialize');
|
|
1696
|
+
if (isProbeTimeout) {
|
|
1697
|
+
Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] Protocol V1 ${name} probe call failed:`, e);
|
|
1434
1698
|
}
|
|
1435
1699
|
else {
|
|
1436
1700
|
Log === null || Log === void 0 ? void 0 : Log.error('call error: ', e);
|
|
1437
1701
|
}
|
|
1438
|
-
const isProbeTimeout = name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS;
|
|
1439
1702
|
const isStaleCall = this.runPromise !== runPromise;
|
|
1440
1703
|
if (!isProbeTimeout &&
|
|
1441
1704
|
!isStaleCall &&
|
|
@@ -1455,7 +1718,43 @@ class ReactNativeBleTransport {
|
|
|
1455
1718
|
});
|
|
1456
1719
|
}
|
|
1457
1720
|
stop() {
|
|
1721
|
+
var _a;
|
|
1722
|
+
if (this.stopPromise)
|
|
1723
|
+
return this.stopPromise;
|
|
1458
1724
|
this.stopped = true;
|
|
1725
|
+
this.bondAbortController.abort();
|
|
1726
|
+
const deviceIds = new Set([
|
|
1727
|
+
...this.monitorTokens.keys(),
|
|
1728
|
+
...this.sessionProtocols.keys(),
|
|
1729
|
+
...this.lifecycleOperations.keys(),
|
|
1730
|
+
...(this.runPromiseDeviceId ? [this.runPromiseDeviceId] : []),
|
|
1731
|
+
]);
|
|
1732
|
+
const scans = Array.from(this.scanCleanups, cleanup => cleanup());
|
|
1733
|
+
this.androidPriorityResetTimers.forEach(timeout => clearTimeout(timeout));
|
|
1734
|
+
this.androidPriorityResetTimers.clear();
|
|
1735
|
+
this.androidHighPriorityDevices.clear();
|
|
1736
|
+
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1737
|
+
(_a = this.runPromise) === null || _a === void 0 ? void 0 : _a.reject(error);
|
|
1738
|
+
this.runPromise = null;
|
|
1739
|
+
this.runPromiseDeviceId = null;
|
|
1740
|
+
deviceIds.forEach(uuid => this.rejectProtocolV2Frames(uuid, error));
|
|
1741
|
+
const manager = this.blePlxManager;
|
|
1742
|
+
const pendingConnections = manager
|
|
1743
|
+
? Array.from(this.lifecycleOperations.keys(), uuid => this.runNativeTeardown(uuid, manager, () => __awaiter(this, void 0, void 0, function* () {
|
|
1744
|
+
yield this.runBestEffortNativeOperation('stop: cancel pending device connection', () => manager.cancelDeviceConnection(uuid));
|
|
1745
|
+
})))
|
|
1746
|
+
: [];
|
|
1747
|
+
this.stopPromise = Promise.all([
|
|
1748
|
+
...scans,
|
|
1749
|
+
...pendingConnections,
|
|
1750
|
+
...Array.from(deviceIds, uuid => this.disconnect(uuid)),
|
|
1751
|
+
]).then(() => __awaiter(this, void 0, void 0, function* () {
|
|
1752
|
+
yield this.protocolV2Links.invalidateAllLinks('React Native BLE transport stopped');
|
|
1753
|
+
yield this.waitForManagerReset();
|
|
1754
|
+
this.blePlxManager = undefined;
|
|
1755
|
+
this.emitter = undefined;
|
|
1756
|
+
}));
|
|
1757
|
+
return this.stopPromise;
|
|
1459
1758
|
}
|
|
1460
1759
|
disconnect(session) {
|
|
1461
1760
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -1585,13 +1884,93 @@ class ReactNativeBleTransport {
|
|
|
1585
1884
|
});
|
|
1586
1885
|
}
|
|
1587
1886
|
cancel() {
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1887
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1888
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native transport cancel');
|
|
1889
|
+
const pending = this.runPromise;
|
|
1890
|
+
const deviceId = this.runPromiseDeviceId;
|
|
1891
|
+
if (pending) {
|
|
1892
|
+
pending.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallQueueActionCancelled));
|
|
1893
|
+
if (this.runPromise === pending) {
|
|
1894
|
+
this.runPromise = null;
|
|
1895
|
+
this.runPromiseDeviceId = null;
|
|
1896
|
+
}
|
|
1897
|
+
if (deviceId)
|
|
1898
|
+
yield this.disconnect(deviceId);
|
|
1899
|
+
}
|
|
1900
|
+
});
|
|
1901
|
+
}
|
|
1902
|
+
negotiateAndroidMtu(uuid, manager, device) {
|
|
1903
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1904
|
+
if (!shouldRefreshNegotiatedMtu(device.mtu))
|
|
1905
|
+
return device;
|
|
1906
|
+
const startedAt = Date.now();
|
|
1907
|
+
let timer;
|
|
1908
|
+
let timedOut = false;
|
|
1909
|
+
let negotiated = device;
|
|
1910
|
+
let failure;
|
|
1911
|
+
try {
|
|
1912
|
+
negotiated = yield Promise.race([
|
|
1913
|
+
device.requestMTU(ANDROID_REQUEST_MTU, `${device.id}:mtu:connected:0:${startedAt}`),
|
|
1914
|
+
new Promise((_, reject) => {
|
|
1915
|
+
timer = setTimeout(() => {
|
|
1916
|
+
timedOut = true;
|
|
1917
|
+
reject(new Error(`BLE MTU exchange timeout after ${ANDROID_MTU_EXCHANGE_TIMEOUT_MS}ms`));
|
|
1918
|
+
}, ANDROID_MTU_EXCHANGE_TIMEOUT_MS);
|
|
1919
|
+
}),
|
|
1920
|
+
]);
|
|
1921
|
+
}
|
|
1922
|
+
catch (error) {
|
|
1923
|
+
failure = error instanceof Error ? error.message : String(error);
|
|
1924
|
+
}
|
|
1925
|
+
finally {
|
|
1926
|
+
if (timer)
|
|
1927
|
+
clearTimeout(timer);
|
|
1928
|
+
}
|
|
1929
|
+
Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] BLE MTU exchange ${failure ? 'failed' : 'completed'}`, {
|
|
1930
|
+
connectIdSuffix: uuid.slice(-8),
|
|
1931
|
+
elapsedMs: Date.now() - startedAt,
|
|
1932
|
+
timedOut,
|
|
1933
|
+
actual: negotiated.mtu,
|
|
1934
|
+
error: failure,
|
|
1935
|
+
});
|
|
1936
|
+
if (this.stopped)
|
|
1937
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1938
|
+
if (!timedOut && !isKnownDefaultMtu(negotiated.mtu))
|
|
1939
|
+
return negotiated;
|
|
1940
|
+
const resetManager = this.abandonStalledConnection(uuid, timedOut ? 'mtu-backstop' : 'mtu-default');
|
|
1941
|
+
yield this.dropAndroidLink(uuid, manager, negotiated, timedOut ? 'mtu exchange timeout' : 'default mtu');
|
|
1942
|
+
if (resetManager)
|
|
1943
|
+
throw this.createWedgedBleSetupError();
|
|
1944
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, timedOut
|
|
1945
|
+
? 'BLE MTU exchange did not complete, reconnecting on a fresh link'
|
|
1946
|
+
: `BLE link stayed at the default MTU ${negotiated.mtu}, reconnecting on a fresh link`);
|
|
1947
|
+
});
|
|
1948
|
+
}
|
|
1949
|
+
dropAndroidLink(uuid, manager, device, reason) {
|
|
1950
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1951
|
+
yield this.runNativeTeardown(uuid, manager, () => __awaiter(this, void 0, void 0, function* () {
|
|
1952
|
+
yield Promise.all([
|
|
1953
|
+
this.runBestEffortNativeOperation(`${reason}: cancel manager connection`, () => manager.cancelDeviceConnection(uuid)),
|
|
1954
|
+
this.runBestEffortNativeOperation(`${reason}: cancel device connection`, () => device.cancelConnection()),
|
|
1955
|
+
]);
|
|
1956
|
+
}));
|
|
1957
|
+
const startedAt = Date.now();
|
|
1958
|
+
while (!this.stopped && Date.now() - startedAt < ANDROID_LINK_DROP_QUIET_MS) {
|
|
1959
|
+
yield delay(ANDROID_LINK_DROP_POLL_MS);
|
|
1960
|
+
}
|
|
1961
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE link drop', {
|
|
1962
|
+
connectIdSuffix: uuid.slice(-8),
|
|
1963
|
+
reason,
|
|
1964
|
+
stopped: this.stopped,
|
|
1965
|
+
});
|
|
1966
|
+
});
|
|
1592
1967
|
}
|
|
1593
1968
|
connectWithTimeout(uuid, connect) {
|
|
1594
1969
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1970
|
+
if (this.stopped)
|
|
1971
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1972
|
+
const startedAt = Date.now();
|
|
1973
|
+
let succeeded = false;
|
|
1595
1974
|
let timer;
|
|
1596
1975
|
let timedOut = false;
|
|
1597
1976
|
const pending = connect();
|
|
@@ -1606,6 +1985,7 @@ class ReactNativeBleTransport {
|
|
|
1606
1985
|
}, BLE_CONNECT_TIMEOUT_MS);
|
|
1607
1986
|
}),
|
|
1608
1987
|
]);
|
|
1988
|
+
succeeded = true;
|
|
1609
1989
|
return result;
|
|
1610
1990
|
}
|
|
1611
1991
|
catch (error) {
|
|
@@ -1620,11 +2000,19 @@ class ReactNativeBleTransport {
|
|
|
1620
2000
|
finally {
|
|
1621
2001
|
if (timer)
|
|
1622
2002
|
clearTimeout(timer);
|
|
2003
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] connect completed', {
|
|
2004
|
+
connectIdSuffix: uuid.slice(-8),
|
|
2005
|
+
elapsedMs: Date.now() - startedAt,
|
|
2006
|
+
succeeded,
|
|
2007
|
+
backstopExpired: timedOut,
|
|
2008
|
+
});
|
|
1623
2009
|
}
|
|
1624
2010
|
});
|
|
1625
2011
|
}
|
|
1626
2012
|
resolveCharacteristicsWithTimeout(uuid, device) {
|
|
1627
2013
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2014
|
+
const startedAt = Date.now();
|
|
2015
|
+
let succeeded = false;
|
|
1628
2016
|
let timer;
|
|
1629
2017
|
let timedOut = false;
|
|
1630
2018
|
const pending = this.resolveCharacteristics(device);
|
|
@@ -1640,6 +2028,7 @@ class ReactNativeBleTransport {
|
|
|
1640
2028
|
}),
|
|
1641
2029
|
]);
|
|
1642
2030
|
this.connectionSetupTimeoutCounts.delete(uuid);
|
|
2031
|
+
succeeded = true;
|
|
1643
2032
|
return result;
|
|
1644
2033
|
}
|
|
1645
2034
|
catch (error) {
|
|
@@ -1649,11 +2038,20 @@ class ReactNativeBleTransport {
|
|
|
1649
2038
|
throw this.createWedgedBleSetupError();
|
|
1650
2039
|
}
|
|
1651
2040
|
}
|
|
2041
|
+
if (reactNative.Platform.OS === 'android' && isMissingGattShapeError(error)) {
|
|
2042
|
+
this.androidGattCacheRefreshes.add(uuid);
|
|
2043
|
+
}
|
|
1652
2044
|
throw error;
|
|
1653
2045
|
}
|
|
1654
2046
|
finally {
|
|
1655
2047
|
if (timer)
|
|
1656
2048
|
clearTimeout(timer);
|
|
2049
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] GATT setup completed', {
|
|
2050
|
+
connectIdSuffix: uuid.slice(-8),
|
|
2051
|
+
elapsedMs: Date.now() - startedAt,
|
|
2052
|
+
succeeded,
|
|
2053
|
+
backstopExpired: timedOut,
|
|
2054
|
+
});
|
|
1657
2055
|
}
|
|
1658
2056
|
});
|
|
1659
2057
|
}
|
|
@@ -1755,6 +2153,8 @@ class ReactNativeBleTransport {
|
|
|
1755
2153
|
}
|
|
1756
2154
|
}
|
|
1757
2155
|
resetPlxManager() {
|
|
2156
|
+
if (bleManagerResetPromise)
|
|
2157
|
+
return;
|
|
1758
2158
|
const manager = this.blePlxManager;
|
|
1759
2159
|
this.blePlxManager = undefined;
|
|
1760
2160
|
const reason = 'React Native BLE manager reset';
|
|
@@ -1792,16 +2192,25 @@ class ReactNativeBleTransport {
|
|
|
1792
2192
|
this.acquiringProtocolV2.clear();
|
|
1793
2193
|
this.sessionProtocols.clear();
|
|
1794
2194
|
this.protocolReprobeFailures.clear();
|
|
2195
|
+
this.silentDetections.clear();
|
|
1795
2196
|
this.writeTimeoutCounts.clear();
|
|
1796
2197
|
this.connectionSetupTimeoutCounts.clear();
|
|
1797
2198
|
this.monitorTokens.clear();
|
|
1798
2199
|
this.protocolV2Assemblers.clear();
|
|
2200
|
+
let reset;
|
|
1799
2201
|
try {
|
|
1800
|
-
manager === null || manager === void 0 ? void 0 : manager.destroy();
|
|
2202
|
+
reset = Promise.resolve(manager === null || manager === void 0 ? void 0 : manager.destroy());
|
|
1801
2203
|
}
|
|
1802
2204
|
catch (error) {
|
|
1803
|
-
|
|
2205
|
+
reset = Promise.reject(error);
|
|
1804
2206
|
}
|
|
2207
|
+
bleManagerResetPromise = reset;
|
|
2208
|
+
reset.then(() => {
|
|
2209
|
+
if (bleManagerResetPromise === reset)
|
|
2210
|
+
bleManagerResetPromise = undefined;
|
|
2211
|
+
}, error => {
|
|
2212
|
+
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE manager destroy failed:', error);
|
|
2213
|
+
});
|
|
1805
2214
|
}
|
|
1806
2215
|
createProtocolMismatchError(expected) {
|
|
1807
2216
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
|
|
@@ -1857,6 +2266,7 @@ class ReactNativeBleTransport {
|
|
|
1857
2266
|
!protocolHint &&
|
|
1858
2267
|
reprobeFailures < PROTOCOL_REPROBE_FALLBACK_ATTEMPTS;
|
|
1859
2268
|
const probeOrder = trustSessionProtocol ? [sessionProtocol] : fullProbeOrder;
|
|
2269
|
+
yield this.wakeSilentProtocolV1Device(uuid, probeOrder);
|
|
1860
2270
|
for (let i = 0; i < probeOrder.length; i += 1) {
|
|
1861
2271
|
const protocol = probeOrder[i];
|
|
1862
2272
|
if (i > 0) {
|
|
@@ -1876,6 +2286,7 @@ class ReactNativeBleTransport {
|
|
|
1876
2286
|
this.confirmedProtocolV2.add(uuid);
|
|
1877
2287
|
}
|
|
1878
2288
|
this.protocolReprobeFailures.delete(uuid);
|
|
2289
|
+
this.silentDetections.delete(uuid);
|
|
1879
2290
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
|
|
1880
2291
|
deviceId: uuid,
|
|
1881
2292
|
protocol,
|
|
@@ -1884,6 +2295,8 @@ class ReactNativeBleTransport {
|
|
|
1884
2295
|
return protocol;
|
|
1885
2296
|
}
|
|
1886
2297
|
}
|
|
2298
|
+
if (!this.silentDetections.has(uuid))
|
|
2299
|
+
this.silentDetections.set(uuid, 'silent');
|
|
1887
2300
|
if (trustSessionProtocol) {
|
|
1888
2301
|
this.protocolReprobeFailures.set(uuid, reprobeFailures + 1);
|
|
1889
2302
|
}
|
|
@@ -1895,6 +2308,30 @@ class ReactNativeBleTransport {
|
|
|
1895
2308
|
throw this.createProtocolDetectionError();
|
|
1896
2309
|
});
|
|
1897
2310
|
}
|
|
2311
|
+
wakeSilentProtocolV1Device(uuid, probeOrder) {
|
|
2312
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2313
|
+
if (reactNative.Platform.OS !== 'android' ||
|
|
2314
|
+
probeOrder[0] !== 'V1' ||
|
|
2315
|
+
this.silentDetections.get(uuid) !== 'silent') {
|
|
2316
|
+
return;
|
|
2317
|
+
}
|
|
2318
|
+
this.silentDetections.set(uuid, 'woken');
|
|
2319
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] sending Protocol V1 Initialize wake', {
|
|
2320
|
+
connectIdSuffix: uuid.slice(-8),
|
|
2321
|
+
});
|
|
2322
|
+
try {
|
|
2323
|
+
this.probingProtocols.set(uuid, 'V1');
|
|
2324
|
+
yield this.callProtocolV1(uuid, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
|
|
2325
|
+
}
|
|
2326
|
+
catch (error) {
|
|
2327
|
+
if (shouldRethrowProtocolProbeError(error))
|
|
2328
|
+
throw error;
|
|
2329
|
+
}
|
|
2330
|
+
finally {
|
|
2331
|
+
this.clearProbeProtocol(uuid, 'V1');
|
|
2332
|
+
}
|
|
2333
|
+
});
|
|
2334
|
+
}
|
|
1898
2335
|
resetProbeStateAfterProtocolProbe(uuid, protocol) {
|
|
1899
2336
|
var _a, _b, _c;
|
|
1900
2337
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -1951,7 +2388,7 @@ class ReactNativeBleTransport {
|
|
|
1951
2388
|
catch (error) {
|
|
1952
2389
|
this.clearProbeProtocol(uuid, 'V1');
|
|
1953
2390
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe failed:', error);
|
|
1954
|
-
if (
|
|
2391
|
+
if (shouldRethrowProtocolProbeError(error)) {
|
|
1955
2392
|
throw error;
|
|
1956
2393
|
}
|
|
1957
2394
|
return false;
|
|
@@ -1977,7 +2414,7 @@ class ReactNativeBleTransport {
|
|
|
1977
2414
|
(_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1978
2415
|
this.resetProtocolV2Frames(uuid);
|
|
1979
2416
|
},
|
|
1980
|
-
shouldRethrow:
|
|
2417
|
+
shouldRethrow: shouldRethrowProtocolProbeError,
|
|
1981
2418
|
});
|
|
1982
2419
|
if (!detected) {
|
|
1983
2420
|
this.clearProbeProtocol(uuid, 'V2');
|
|
@@ -2104,6 +2541,9 @@ class ReactNativeBleTransport {
|
|
|
2104
2541
|
this.rememberStaleBondError(uuid, bondError);
|
|
2105
2542
|
throw bondError;
|
|
2106
2543
|
}
|
|
2544
|
+
if (isNativeBleDisconnectError(error)) {
|
|
2545
|
+
throw toBleDisconnectHardwareError(error);
|
|
2546
|
+
}
|
|
2107
2547
|
if (getFirmwareUploadWriteRetryType(error) !== 'congested' ||
|
|
2108
2548
|
attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
|
|
2109
2549
|
throw error;
|
|
@@ -2146,6 +2586,7 @@ class ReactNativeBleTransport {
|
|
|
2146
2586
|
if (!this._messages || !this._messagesV2) {
|
|
2147
2587
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
2148
2588
|
}
|
|
2589
|
+
const isProtocolProbe = this.probingProtocols.get(uuid) === 'V2';
|
|
2149
2590
|
const callOptions = options;
|
|
2150
2591
|
const highThroughputWrite = transport.isProtocolV2HighThroughputCall(name);
|
|
2151
2592
|
if (highThroughputWrite) {
|
|
@@ -2186,6 +2627,14 @@ class ReactNativeBleTransport {
|
|
|
2186
2627
|
}
|
|
2187
2628
|
catch (e) {
|
|
2188
2629
|
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] Protocol V2 call error:', e);
|
|
2630
|
+
if (!isProtocolProbe &&
|
|
2631
|
+
(e === null || e === void 0 ? void 0 : e.errorCode) === hdShared.HardwareErrorCode.BleTimeoutError &&
|
|
2632
|
+
!this.monitorTokens.has(uuid)) {
|
|
2633
|
+
yield this.runLifecycleOperation(uuid, () => __awaiter(this, void 0, void 0, function* () {
|
|
2634
|
+
if (!this.monitorTokens.has(uuid))
|
|
2635
|
+
yield this.disconnectUnlocked(uuid);
|
|
2636
|
+
}));
|
|
2637
|
+
}
|
|
2189
2638
|
throw e;
|
|
2190
2639
|
}
|
|
2191
2640
|
finally {
|
|
@@ -2200,7 +2649,7 @@ class ReactNativeBleTransport {
|
|
|
2200
2649
|
const transport = this.getCachedTransport(uuid);
|
|
2201
2650
|
if (!shouldRefreshNegotiatedMtu(transport.mtuSize))
|
|
2202
2651
|
return;
|
|
2203
|
-
const refreshedDevice = yield requestNegotiatedMtu(transport.device, 'highThroughput', 1);
|
|
2652
|
+
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
2653
|
transport.device = refreshedDevice;
|
|
2205
2654
|
transport.mtuSize =
|
|
2206
2655
|
typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport.mtuSize;
|
|
@@ -2317,6 +2766,8 @@ class ReactNativeBleTransport {
|
|
|
2317
2766
|
}
|
|
2318
2767
|
}
|
|
2319
2768
|
|
|
2769
|
+
exports.ANDROID_LINK_DROP_QUIET_MS = ANDROID_LINK_DROP_QUIET_MS;
|
|
2770
|
+
exports.ANDROID_MTU_EXCHANGE_TIMEOUT_MS = ANDROID_MTU_EXCHANGE_TIMEOUT_MS;
|
|
2320
2771
|
exports.BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD;
|
|
2321
2772
|
exports.BLE_CONNECT_TIMEOUT_MS = BLE_CONNECT_TIMEOUT_MS;
|
|
2322
2773
|
exports.BLE_GATT_SETUP_TIMEOUT_MS = BLE_GATT_SETUP_TIMEOUT_MS;
|