@onekeyfe/hd-transport-react-native 1.2.2-alpha.105 → 1.2.2-alpha.107

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -57,6 +57,17 @@ 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
73
  const onDeviceBondState = (bleMacAddress) => new Promise((resolve, reject) => {
@@ -69,24 +80,40 @@ const onDeviceBondState = (bleMacAddress) => new Promise((resolve, reject) => {
69
80
  };
70
81
  const timeout = setTimeout(() => {
71
82
  cleanup();
72
- reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'device is not bonded'));
83
+ reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'Bluetooth pairing timed out', {
84
+ phase: 'bond',
85
+ reason: 'timeout',
86
+ }));
73
87
  }, 60 * 1000);
74
88
  const cleanupListener = BleUtils__default["default"].onDeviceBondState(peripheral => {
75
- var _a;
89
+ var _a, _b;
76
90
  if (((_a = peripheral.id) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== bleMacAddress.toLowerCase()) {
77
91
  return;
78
92
  }
79
93
  const { bondState } = peripheral;
80
94
  const hasBonded = bondState.preState === 'BOND_BONDING' && bondState.state === 'BOND_BONDED';
81
- const hasCanceled = bondState.preState === 'BOND_BONDING' && bondState.state === 'BOND_NONE';
95
+ const hasFailed = bondState.preState === 'BOND_BONDING' && bondState.state === 'BOND_NONE';
82
96
  Logger.debug('onDeviceBondState bondState:', bondState);
83
97
  if (hasBonded) {
84
98
  cleanup();
85
99
  resolve(peripheral);
86
100
  }
87
- else if (hasCanceled) {
101
+ else if (hasFailed) {
88
102
  cleanup();
89
- reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceBondedCanceled, 'bonding canceled'));
103
+ const nativeReason = 'reason' in bondState && typeof bondState.reason === 'number'
104
+ ? bondState.reason
105
+ : undefined;
106
+ const reason = nativeReason === undefined ? 'unknown' : (_b = bondFailureReasons[nativeReason]) !== null && _b !== void 0 ? _b : 'unknown';
107
+ const params = Object.assign({ phase: 'bond', reason }, (nativeReason === undefined ? {} : { nativeReason }));
108
+ if (reason === 'timeout') {
109
+ reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'Bluetooth pairing timed out', params));
110
+ }
111
+ else if (reason === 'canceled') {
112
+ reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceBondedCanceled, 'Bluetooth pairing canceled', params));
113
+ }
114
+ else {
115
+ reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'Bluetooth pairing failed', params));
116
+ }
90
117
  }
91
118
  });
92
119
  });
@@ -194,6 +221,27 @@ const subscribeBleOn = (bleManager, ms = 1000) => new Promise((resolve, reject)
194
221
  }, ms);
195
222
  });
196
223
 
224
+ const BLE_PLX_DEVICE_DISCONNECTED = 201;
225
+ const IOS_PERIPHERAL_DISCONNECTED = 7;
226
+ const nativeErrorText$1 = (error) => [error.reason, error.message]
227
+ .filter((value) => typeof value === 'string')
228
+ .join(' ');
229
+ const isNativeBleDisconnectError = (error) => {
230
+ if (!error || typeof error !== 'object') {
231
+ return false;
232
+ }
233
+ const nativeError = error;
234
+ return (nativeError.errorCode === BLE_PLX_DEVICE_DISCONNECTED ||
235
+ nativeError.iosErrorCode === IOS_PERIPHERAL_DISCONNECTED);
236
+ };
237
+ const toBleDisconnectHardwareError = (error) => {
238
+ if ((error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleDeviceDisconnected) {
239
+ return error;
240
+ }
241
+ const nativeError = (error !== null && error !== void 0 ? error : {});
242
+ return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected, nativeErrorText$1(nativeError) || undefined);
243
+ };
244
+
197
245
  const ATT_INSUFFICIENT_AUTHENTICATION = 5;
198
246
  const ATT_INSUFFICIENT_ENCRYPTION = 15;
199
247
  const IOS_PEER_REMOVED_PAIRING_INFORMATION = 14;
@@ -258,6 +306,7 @@ class BleTransport {
258
306
  const { check, ProtocolV1, parseConfigure } = transport__default["default"];
259
307
  const Log = bleLogger;
260
308
  const transportCache = {};
309
+ let bleManagerResetPromise;
261
310
  const FIRMWARE_UPLOAD_WRITE_BURST_SIZE = reactNative.Platform.OS === 'ios' ? 4 : 5;
262
311
  const FIRMWARE_UPLOAD_WRITE_PAUSE_MS = reactNative.Platform.OS === 'ios' ? 8 : 10;
263
312
  const FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS = reactNative.Platform.OS === 'ios' ? 24 : 30;
@@ -316,6 +365,17 @@ const WEDGED_WRITE_MESSAGE = 'BLE write timeout after';
316
365
  const isWedgedWriteError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleWriteCharacteristicError &&
317
366
  typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' &&
318
367
  error.message.startsWith(WEDGED_WRITE_MESSAGE);
368
+ const shouldRethrowProtocolProbeError = (error) => {
369
+ const code = error === null || error === void 0 ? void 0 : error.errorCode;
370
+ return (hdShared.isBleStaleBondHardwareError(error) ||
371
+ isNativeBleDisconnectError(error) ||
372
+ code === hdShared.HardwareErrorCode.BleDeviceNotBonded ||
373
+ code === hdShared.HardwareErrorCode.BleDeviceBondedCanceled ||
374
+ code === hdShared.HardwareErrorCode.BleDeviceDisconnected ||
375
+ code === hdShared.HardwareErrorCode.BleCharacteristicNotifyError ||
376
+ code === hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure ||
377
+ code === hdShared.HardwareErrorCode.BleWriteCharacteristicError);
378
+ };
319
379
  const BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
320
380
  const DEVICE_SCAN_TIMEOUT_MS = 3000;
321
381
  const IOS_NOTIFY_READY_DELAY_MS = 150;
@@ -474,6 +534,7 @@ class ReactNativeBleTransport {
474
534
  this.androidPriorityResetTimers = new Map();
475
535
  this.nextMonitorToken = 1;
476
536
  this.lifecycleOperations = new Map();
537
+ this.scanCleanups = new Set();
477
538
  this.scanTimeout = (_a = options.scanTimeout) !== null && _a !== void 0 ? _a : DEVICE_SCAN_TIMEOUT_MS;
478
539
  }
479
540
  init(logger, emitter) {
@@ -502,10 +563,38 @@ class ReactNativeBleTransport {
502
563
  listen() {
503
564
  }
504
565
  getPlxManager() {
505
- if (this.blePlxManager)
506
- return Promise.resolve(this.blePlxManager);
507
- this.blePlxManager = new reactNativeBlePlx.BleManager();
508
- return Promise.resolve(this.blePlxManager);
566
+ return __awaiter(this, void 0, void 0, function* () {
567
+ while (bleManagerResetPromise) {
568
+ yield this.waitForManagerReset();
569
+ }
570
+ if (this.stopped)
571
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
572
+ if (!this.blePlxManager)
573
+ this.blePlxManager = new reactNativeBlePlx.BleManager();
574
+ return this.blePlxManager;
575
+ });
576
+ }
577
+ waitForManagerReset() {
578
+ return __awaiter(this, void 0, void 0, function* () {
579
+ if (!bleManagerResetPromise)
580
+ return;
581
+ let timeout;
582
+ try {
583
+ yield Promise.race([
584
+ bleManagerResetPromise,
585
+ new Promise((_, reject) => {
586
+ timeout = setTimeout(() => reject(this.createWedgedBleSetupError()), BLE_CONNECT_TIMEOUT_MS);
587
+ }),
588
+ ]);
589
+ }
590
+ catch (_a) {
591
+ throw this.createWedgedBleSetupError();
592
+ }
593
+ finally {
594
+ if (timeout)
595
+ clearTimeout(timeout);
596
+ }
597
+ });
509
598
  }
510
599
  resolveCharacteristics(device) {
511
600
  return __awaiter(this, void 0, void 0, function* () {
@@ -670,30 +759,55 @@ class ReactNativeBleTransport {
670
759
  }
671
760
  enumerate() {
672
761
  return __awaiter(this, void 0, void 0, function* () {
673
- return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
674
- const deviceList = [];
675
- const blePlxManager = yield this.getPlxManager();
676
- try {
677
- yield subscribeBleOn(blePlxManager);
678
- }
679
- catch (error) {
680
- Log === null || Log === void 0 ? void 0 : Log.debug('subscribeBleOn error: ', error);
681
- reject(error);
682
- return;
683
- }
684
- if (reactNative.Platform.OS === 'android' && reactNative.Platform.Version >= 31) {
685
- Log === null || Log === void 0 ? void 0 : Log.debug('requesting permissions, please wait...');
686
- const resultConnect = yield reactNative.PermissionsAndroid.requestMultiple([
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
- }
762
+ const scanStartedAt = Date.now();
763
+ let firstDeviceMs;
764
+ const blePlxManager = yield this.getPlxManager();
765
+ yield subscribeBleOn(blePlxManager);
766
+ if (reactNative.Platform.OS === 'android' && reactNative.Platform.Version >= 31) {
767
+ Log === null || Log === void 0 ? void 0 : Log.debug('requesting permissions, please wait...');
768
+ const resultConnect = yield reactNative.PermissionsAndroid.requestMultiple([
769
+ reactNative.PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT,
770
+ reactNative.PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN,
771
+ ]);
772
+ Log === null || Log === void 0 ? void 0 : Log.debug('requesting permissions, result: ', resultConnect);
773
+ if (resultConnect[reactNative.PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT] !== 'granted' ||
774
+ resultConnect[reactNative.PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN] !== 'granted') {
775
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePermissionError);
696
776
  }
777
+ }
778
+ if (this.stopped)
779
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
780
+ return new Promise((resolve, reject) => {
781
+ const deviceList = [];
782
+ let finished = false;
783
+ let scanCleanup;
784
+ const finishScan = (error) => {
785
+ if (scanCleanup)
786
+ return scanCleanup;
787
+ finished = true;
788
+ clearScanTimer();
789
+ scanCleanup = this.runNativeTeardown('scan', blePlxManager, () => __awaiter(this, void 0, void 0, function* () {
790
+ yield blePlxManager.stopDeviceScan();
791
+ })).then(() => {
792
+ this.scanCleanups.delete(cancelScan);
793
+ if (error)
794
+ reject(error);
795
+ else
796
+ resolve(deviceList);
797
+ });
798
+ return scanCleanup;
799
+ };
800
+ const cancelScan = () => finishScan(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected));
801
+ this.scanCleanups.add(cancelScan);
802
+ const clearScanTimer = timer.timeout(() => {
803
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] scan completed', {
804
+ elapsedMs: Date.now() - scanStartedAt,
805
+ firstDeviceMs,
806
+ deviceCount: deviceList.length,
807
+ scanWindowMs: this.scanTimeout,
808
+ });
809
+ finishScan();
810
+ }, this.scanTimeout);
697
811
  blePlxManager.startDeviceScan(getBluetoothServiceUuids(), {
698
812
  allowDuplicates: true,
699
813
  scanMode: reactNativeBlePlx.ScanMode.LowLatency,
@@ -702,19 +816,17 @@ class ReactNativeBleTransport {
702
816
  if (error) {
703
817
  Log === null || Log === void 0 ? void 0 : Log.debug('ble scan error: ', error);
704
818
  if ([reactNativeBlePlx.BleErrorCode.BluetoothPoweredOff, reactNativeBlePlx.BleErrorCode.BluetoothInUnknownState].includes(error.errorCode)) {
705
- reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePermissionError));
819
+ finishScan(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePermissionError));
706
820
  }
707
821
  else if (error.errorCode === reactNativeBlePlx.BleErrorCode.BluetoothUnauthorized) {
708
- reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleLocationError));
822
+ finishScan(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleLocationError));
709
823
  }
710
824
  else if (error.errorCode === reactNativeBlePlx.BleErrorCode.LocationServicesDisabled) {
711
- reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleLocationServicesDisabled));
712
- }
713
- else if (error.errorCode === reactNativeBlePlx.BleErrorCode.ScanStartFailed) {
714
- timer.timeout(() => { }, this.scanTimeout);
825
+ finishScan(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleLocationServicesDisabled));
715
826
  }
827
+ else if (error.errorCode === reactNativeBlePlx.BleErrorCode.ScanStartFailed) ;
716
828
  else {
717
- reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleScanError, (_a = error.reason) !== null && _a !== void 0 ? _a : ''));
829
+ finishScan(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleScanError, (_a = error.reason) !== null && _a !== void 0 ? _a : ''));
718
830
  }
719
831
  return;
720
832
  }
@@ -739,6 +851,8 @@ class ReactNativeBleTransport {
739
851
  });
740
852
  }
741
853
  });
854
+ if (finished)
855
+ return;
742
856
  getConnectedDeviceIds(reactNative.Platform.OS === 'ios' ? getBluetoothServiceUuids() : []).then(devices => {
743
857
  for (const device of devices) {
744
858
  const localName = 'localName' in device && typeof device.localName === 'string'
@@ -754,10 +868,11 @@ class ReactNativeBleTransport {
754
868
  addDevice(device);
755
869
  }
756
870
  }
757
- });
871
+ }, error => Log === null || Log === void 0 ? void 0 : Log.debug('search connected peripheral failed:', error));
758
872
  const addDevice = (device) => {
759
873
  var _a;
760
- if (deviceList.every(d => d.id !== device.id)) {
874
+ if (!finished && deviceList.every(d => d.id !== device.id)) {
875
+ firstDeviceMs !== null && firstDeviceMs !== void 0 ? firstDeviceMs : (firstDeviceMs = Date.now() - scanStartedAt);
761
876
  const displayName = (_a = getDeviceDisplayName(device)) !== null && _a !== void 0 ? _a : 'Unknown BLE Device';
762
877
  deviceList.push(Object.assign(Object.assign({}, device), { name: displayName, commType: 'ble' }));
763
878
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] OneKey BLE device discovered', {
@@ -767,16 +882,14 @@ class ReactNativeBleTransport {
767
882
  });
768
883
  }
769
884
  };
770
- timer.timeout(() => {
771
- blePlxManager.stopDeviceScan();
772
- resolve(deviceList);
773
- }, this.scanTimeout);
774
- }));
885
+ });
775
886
  });
776
887
  }
777
888
  installTransportForAcquire(uuid, device, characteristics) {
778
889
  return __awaiter(this, void 0, void 0, function* () {
779
890
  const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristicsWithTimeout(uuid, device));
891
+ if (this.stopped)
892
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
780
893
  const transport$1 = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
781
894
  transport$1.mtuSize = typeof device.mtu === 'number' ? device.mtu : undefined;
782
895
  const monitorToken = this.nextMonitorToken;
@@ -797,17 +910,23 @@ class ReactNativeBleTransport {
797
910
  else if (reactNative.Platform.OS === 'android') {
798
911
  yield delay(ANDROID_NOTIFY_READY_DELAY_MS);
799
912
  }
913
+ if (this.stopped)
914
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
800
915
  const initialMtu = transport$1.mtuSize;
801
916
  let refreshAttempts = 0;
802
917
  if ((reactNative.Platform.OS === 'ios' || reactNative.Platform.OS === 'android') &&
803
918
  shouldRefreshNegotiatedMtu(transport$1.mtuSize)) {
804
919
  refreshAttempts += 1;
805
920
  let refreshedDevice = yield requestNegotiatedMtu(transport$1.device, 'servicesAndNotifyReady', 1);
921
+ if (this.stopped)
922
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
806
923
  transport$1.device = refreshedDevice;
807
924
  transport$1.mtuSize =
808
925
  typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport$1.mtuSize;
809
926
  if (shouldRefreshNegotiatedMtu(transport$1.mtuSize)) {
810
927
  yield delay(BLE_MTU_REFRESH_RETRY_DELAY_MS);
928
+ if (this.stopped)
929
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
811
930
  refreshAttempts += 1;
812
931
  refreshedDevice = yield requestNegotiatedMtu(transport$1.device, 'servicesAndNotifyReady', 2);
813
932
  transport$1.device = refreshedDevice;
@@ -815,6 +934,8 @@ class ReactNativeBleTransport {
815
934
  typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport$1.mtuSize;
816
935
  }
817
936
  }
937
+ if (this.stopped)
938
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
818
939
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE MTU ready', {
819
940
  platform: reactNative.Platform.OS,
820
941
  requested: getRequestedBleMtu(),
@@ -837,6 +958,8 @@ class ReactNativeBleTransport {
837
958
  acquireUnlocked(input) {
838
959
  var _a;
839
960
  return __awaiter(this, void 0, void 0, function* () {
961
+ if (this.stopped)
962
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
840
963
  const { uuid, forceCleanRunPromise, expectedProtocol, skipProtocolProbe } = input;
841
964
  const shouldMapProtocolV2StaleBond = expectedProtocol
842
965
  ? expectedProtocol === 'V2'
@@ -871,6 +994,8 @@ class ReactNativeBleTransport {
871
994
  if (isCachedDeviceConnected &&
872
995
  cachedProtocol &&
873
996
  (!expectedProtocol || cachedProtocol === expectedProtocol)) {
997
+ if (this.stopped)
998
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
874
999
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] reuse cached BLE transport:', uuid, cachedProtocol);
875
1000
  return { uuid, protocolType: cachedProtocol };
876
1001
  }
@@ -894,6 +1019,27 @@ class ReactNativeBleTransport {
894
1019
  Log === null || Log === void 0 ? void 0 : Log.debug('subscribeBleOn error: ', error);
895
1020
  throw error;
896
1021
  }
1022
+ if (this.stopped)
1023
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
1024
+ if (reactNative.Platform.OS === 'android') {
1025
+ try {
1026
+ const bondState = yield pairDevice(uuid);
1027
+ if (bondState.bonding) {
1028
+ yield onDeviceBondState(uuid);
1029
+ }
1030
+ else if (!bondState.bonded) {
1031
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'device is not bonded');
1032
+ }
1033
+ }
1034
+ catch (error) {
1035
+ yield this.runNativeTeardown(uuid, blePlxManager, () => __awaiter(this, void 0, void 0, function* () {
1036
+ yield this.runBestEffortNativeOperation('bond failure: cancel manager connection', () => blePlxManager.cancelDeviceConnection(uuid));
1037
+ }));
1038
+ throw error;
1039
+ }
1040
+ }
1041
+ if (this.stopped)
1042
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
897
1043
  if (!device) {
898
1044
  const devices = yield blePlxManager.devices([uuid]);
899
1045
  [device] = devices;
@@ -965,33 +1111,27 @@ class ReactNativeBleTransport {
965
1111
  }
966
1112
  }
967
1113
  }
968
- if (reactNative.Platform.OS === 'android') {
969
- const connectedDevice = device;
970
- try {
971
- if (!(yield connectedDevice.isConnected().catch(() => false))) {
972
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `Device ${uuid} is not connected before bonding`);
973
- }
974
- const bondState = yield pairDevice(uuid);
975
- if (bondState.bonding) {
976
- yield onDeviceBondState(uuid);
977
- }
978
- else if (!bondState.bonded) {
979
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'device is not bonded');
980
- }
981
- }
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
- }
1114
+ if (this.stopped)
1115
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
1116
+ if (reactNative.Platform.OS === 'android' && !(yield device.isConnected().catch(() => false))) {
1117
+ const disconnectedDevice = device;
1118
+ yield this.runNativeTeardown(uuid, blePlxManager, () => __awaiter(this, void 0, void 0, function* () {
1119
+ yield Promise.all([
1120
+ this.runBestEffortNativeOperation('connect failure: cancel manager connection', () => blePlxManager.cancelDeviceConnection(uuid)),
1121
+ this.runBestEffortNativeOperation('connect failure: cancel device connection', () => disconnectedDevice.cancelConnection()),
1122
+ ]);
1123
+ }));
1124
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, 'device is not connected');
991
1125
  }
1126
+ if (this.stopped)
1127
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
992
1128
  device = yield resolveNegotiatedMtu(device);
1129
+ if (this.stopped)
1130
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
993
1131
  const acquiredDevice = device;
994
1132
  const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, acquiredDevice);
1133
+ if (this.stopped)
1134
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
995
1135
  const protocolHint = expectedProtocol
996
1136
  ? undefined
997
1137
  : (_a = input.protocolHint) !== null && _a !== void 0 ? _a : this.deviceProtocolHints.get(uuid);
@@ -1034,6 +1174,8 @@ class ReactNativeBleTransport {
1034
1174
  const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint, () => __awaiter(this, void 0, void 0, function* () {
1035
1175
  yield this.installTransportForAcquire(uuid, acquiredDevice);
1036
1176
  }));
1177
+ if (this.stopped)
1178
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
1037
1179
  const currentTransport = transportCache[uuid];
1038
1180
  if (!currentTransport) {
1039
1181
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
@@ -1042,7 +1184,7 @@ class ReactNativeBleTransport {
1042
1184
  return { uuid, protocolType };
1043
1185
  }
1044
1186
  catch (error) {
1045
- if (hdShared.isBleStaleBondHardwareError(error)) {
1187
+ if (hdShared.isBleStaleBondHardwareError(error) || shouldRethrowProtocolProbeError(error)) {
1046
1188
  yield this.disconnectUnlocked(uuid);
1047
1189
  }
1048
1190
  else {
@@ -1455,7 +1597,42 @@ class ReactNativeBleTransport {
1455
1597
  });
1456
1598
  }
1457
1599
  stop() {
1600
+ var _a;
1601
+ if (this.stopPromise)
1602
+ return this.stopPromise;
1458
1603
  this.stopped = true;
1604
+ const deviceIds = new Set([
1605
+ ...this.monitorTokens.keys(),
1606
+ ...this.sessionProtocols.keys(),
1607
+ ...this.lifecycleOperations.keys(),
1608
+ ...(this.runPromiseDeviceId ? [this.runPromiseDeviceId] : []),
1609
+ ]);
1610
+ const scans = Array.from(this.scanCleanups, cleanup => cleanup());
1611
+ this.androidPriorityResetTimers.forEach(timeout => clearTimeout(timeout));
1612
+ this.androidPriorityResetTimers.clear();
1613
+ this.androidHighPriorityDevices.clear();
1614
+ const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
1615
+ (_a = this.runPromise) === null || _a === void 0 ? void 0 : _a.reject(error);
1616
+ this.runPromise = null;
1617
+ this.runPromiseDeviceId = null;
1618
+ deviceIds.forEach(uuid => this.rejectProtocolV2Frames(uuid, error));
1619
+ const manager = this.blePlxManager;
1620
+ const pendingConnections = manager
1621
+ ? Array.from(this.lifecycleOperations.keys(), uuid => this.runNativeTeardown(uuid, manager, () => __awaiter(this, void 0, void 0, function* () {
1622
+ yield this.runBestEffortNativeOperation('stop: cancel pending device connection', () => manager.cancelDeviceConnection(uuid));
1623
+ })))
1624
+ : [];
1625
+ this.stopPromise = Promise.all([
1626
+ ...scans,
1627
+ ...pendingConnections,
1628
+ ...Array.from(deviceIds, uuid => this.disconnect(uuid)),
1629
+ ]).then(() => __awaiter(this, void 0, void 0, function* () {
1630
+ yield this.protocolV2Links.invalidateAllLinks('React Native BLE transport stopped');
1631
+ yield this.waitForManagerReset();
1632
+ this.blePlxManager = undefined;
1633
+ this.emitter = undefined;
1634
+ }));
1635
+ return this.stopPromise;
1459
1636
  }
1460
1637
  disconnect(session) {
1461
1638
  return __awaiter(this, void 0, void 0, function* () {
@@ -1585,13 +1762,27 @@ class ReactNativeBleTransport {
1585
1762
  });
1586
1763
  }
1587
1764
  cancel() {
1588
- Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native transport cancel');
1589
- if (this.runPromise) ;
1590
- this.runPromise = null;
1591
- this.runPromiseDeviceId = null;
1765
+ return __awaiter(this, void 0, void 0, function* () {
1766
+ Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native transport cancel');
1767
+ const pending = this.runPromise;
1768
+ const deviceId = this.runPromiseDeviceId;
1769
+ if (pending) {
1770
+ pending.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallQueueActionCancelled));
1771
+ if (this.runPromise === pending) {
1772
+ this.runPromise = null;
1773
+ this.runPromiseDeviceId = null;
1774
+ }
1775
+ if (deviceId)
1776
+ yield this.disconnect(deviceId);
1777
+ }
1778
+ });
1592
1779
  }
1593
1780
  connectWithTimeout(uuid, connect) {
1594
1781
  return __awaiter(this, void 0, void 0, function* () {
1782
+ if (this.stopped)
1783
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
1784
+ const startedAt = Date.now();
1785
+ let succeeded = false;
1595
1786
  let timer;
1596
1787
  let timedOut = false;
1597
1788
  const pending = connect();
@@ -1606,6 +1797,7 @@ class ReactNativeBleTransport {
1606
1797
  }, BLE_CONNECT_TIMEOUT_MS);
1607
1798
  }),
1608
1799
  ]);
1800
+ succeeded = true;
1609
1801
  return result;
1610
1802
  }
1611
1803
  catch (error) {
@@ -1620,11 +1812,19 @@ class ReactNativeBleTransport {
1620
1812
  finally {
1621
1813
  if (timer)
1622
1814
  clearTimeout(timer);
1815
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] connect completed', {
1816
+ connectIdSuffix: uuid.slice(-8),
1817
+ elapsedMs: Date.now() - startedAt,
1818
+ succeeded,
1819
+ backstopExpired: timedOut,
1820
+ });
1623
1821
  }
1624
1822
  });
1625
1823
  }
1626
1824
  resolveCharacteristicsWithTimeout(uuid, device) {
1627
1825
  return __awaiter(this, void 0, void 0, function* () {
1826
+ const startedAt = Date.now();
1827
+ let succeeded = false;
1628
1828
  let timer;
1629
1829
  let timedOut = false;
1630
1830
  const pending = this.resolveCharacteristics(device);
@@ -1640,6 +1840,7 @@ class ReactNativeBleTransport {
1640
1840
  }),
1641
1841
  ]);
1642
1842
  this.connectionSetupTimeoutCounts.delete(uuid);
1843
+ succeeded = true;
1643
1844
  return result;
1644
1845
  }
1645
1846
  catch (error) {
@@ -1654,6 +1855,12 @@ class ReactNativeBleTransport {
1654
1855
  finally {
1655
1856
  if (timer)
1656
1857
  clearTimeout(timer);
1858
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] GATT setup completed', {
1859
+ connectIdSuffix: uuid.slice(-8),
1860
+ elapsedMs: Date.now() - startedAt,
1861
+ succeeded,
1862
+ backstopExpired: timedOut,
1863
+ });
1657
1864
  }
1658
1865
  });
1659
1866
  }
@@ -1755,6 +1962,8 @@ class ReactNativeBleTransport {
1755
1962
  }
1756
1963
  }
1757
1964
  resetPlxManager() {
1965
+ if (bleManagerResetPromise)
1966
+ return;
1758
1967
  const manager = this.blePlxManager;
1759
1968
  this.blePlxManager = undefined;
1760
1969
  const reason = 'React Native BLE manager reset';
@@ -1796,12 +2005,20 @@ class ReactNativeBleTransport {
1796
2005
  this.connectionSetupTimeoutCounts.clear();
1797
2006
  this.monitorTokens.clear();
1798
2007
  this.protocolV2Assemblers.clear();
2008
+ let reset;
1799
2009
  try {
1800
- manager === null || manager === void 0 ? void 0 : manager.destroy();
2010
+ reset = Promise.resolve(manager === null || manager === void 0 ? void 0 : manager.destroy());
1801
2011
  }
1802
2012
  catch (error) {
1803
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE manager destroy failed (ignored):', error);
2013
+ reset = Promise.reject(error);
1804
2014
  }
2015
+ bleManagerResetPromise = reset;
2016
+ reset.then(() => {
2017
+ if (bleManagerResetPromise === reset)
2018
+ bleManagerResetPromise = undefined;
2019
+ }, error => {
2020
+ Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE manager destroy failed:', error);
2021
+ });
1805
2022
  }
1806
2023
  createProtocolMismatchError(expected) {
1807
2024
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
@@ -1951,7 +2168,7 @@ class ReactNativeBleTransport {
1951
2168
  catch (error) {
1952
2169
  this.clearProbeProtocol(uuid, 'V1');
1953
2170
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe failed:', error);
1954
- if (isWedgedWriteError(error)) {
2171
+ if (shouldRethrowProtocolProbeError(error)) {
1955
2172
  throw error;
1956
2173
  }
1957
2174
  return false;
@@ -1977,7 +2194,7 @@ class ReactNativeBleTransport {
1977
2194
  (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1978
2195
  this.resetProtocolV2Frames(uuid);
1979
2196
  },
1980
- shouldRethrow: hdShared.isBleStaleBondHardwareError,
2197
+ shouldRethrow: shouldRethrowProtocolProbeError,
1981
2198
  });
1982
2199
  if (!detected) {
1983
2200
  this.clearProbeProtocol(uuid, 'V2');
@@ -2104,6 +2321,9 @@ class ReactNativeBleTransport {
2104
2321
  this.rememberStaleBondError(uuid, bondError);
2105
2322
  throw bondError;
2106
2323
  }
2324
+ if (isNativeBleDisconnectError(error)) {
2325
+ throw toBleDisconnectHardwareError(error);
2326
+ }
2107
2327
  if (getFirmwareUploadWriteRetryType(error) !== 'congested' ||
2108
2328
  attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
2109
2329
  throw error;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-react-native",
3
- "version": "1.2.2-alpha.105",
3
+ "version": "1.2.2-alpha.107",
4
4
  "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -20,11 +20,11 @@
20
20
  "lint:fix": "eslint . --fix"
21
21
  },
22
22
  "dependencies": {
23
- "@onekeyfe/hd-core": "1.2.2-alpha.105",
24
- "@onekeyfe/hd-shared": "1.2.2-alpha.105",
25
- "@onekeyfe/hd-transport": "1.2.2-alpha.105",
26
- "@onekeyfe/react-native-ble-utils": "^0.1.6",
23
+ "@onekeyfe/hd-core": "1.2.2-alpha.107",
24
+ "@onekeyfe/hd-shared": "1.2.2-alpha.107",
25
+ "@onekeyfe/hd-transport": "1.2.2-alpha.107",
26
+ "@onekeyfe/react-native-ble-utils": "^0.1.7",
27
27
  "react-native-ble-plx": "3.5.1"
28
28
  },
29
- "gitHead": "16dcd1b1a3d6687019947f22235522feb1d79095"
29
+ "gitHead": "0139b6e93599824f13f1e7121a70a59ec424f466"
30
30
  }