@onekeyfe/hd-transport-react-native 1.2.2-alpha.114 → 1.2.2-alpha.116

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
@@ -384,7 +384,8 @@ const shouldRethrowProtocolProbeError = (error) => {
384
384
  code === hdShared.HardwareErrorCode.BleDeviceDisconnected ||
385
385
  code === hdShared.HardwareErrorCode.BleCharacteristicNotifyError ||
386
386
  code === hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure ||
387
- code === hdShared.HardwareErrorCode.BleWriteCharacteristicError);
387
+ code === hdShared.HardwareErrorCode.BleWriteCharacteristicError ||
388
+ isProtocolV2DefaultMtuError(error));
388
389
  };
389
390
  const BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
390
391
  const DEVICE_SCAN_TIMEOUT_MS = 3000;
@@ -432,6 +433,40 @@ const connectOptions = {
432
433
  const fallbackConnectOptions = {
433
434
  timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
434
435
  };
436
+ const androidConnectOptions = {
437
+ timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
438
+ };
439
+ const androidRefreshGattConnectOptions = {
440
+ timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
441
+ refreshGatt: 'OnConnected',
442
+ };
443
+ const ANDROID_MTU_EXCHANGE_TIMEOUT_MS = 12000;
444
+ const ANDROID_LINK_DROP_QUIET_MS = 5000;
445
+ const ANDROID_LINK_DROP_TIMEOUT_MS = 8000;
446
+ const ANDROID_LINK_DROP_POLL_MS = 250;
447
+ const isKnownDefaultMtu = (mtu) => typeof mtu === 'number' && Number.isFinite(mtu) && mtu <= 23;
448
+ const DEFAULT_MTU_LINK_MESSAGE = 'BLE link stayed at the default MTU';
449
+ const createDefaultMtuLinkError = (mtu) => hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `${DEFAULT_MTU_LINK_MESSAGE} ${String(mtu)}, reconnecting on a fresh link`);
450
+ const PROTOCOL_V2_DEFAULT_MTU_MESSAGE = 'Protocol V2 needs a negotiated BLE MTU';
451
+ const isProtocolV2DefaultMtuError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleConnectedError &&
452
+ typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' &&
453
+ error.message.startsWith(PROTOCOL_V2_DEFAULT_MTU_MESSAGE);
454
+ const createProtocolV2DefaultMtuError = (mtu) => hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `${PROTOCOL_V2_DEFAULT_MTU_MESSAGE}, current MTU ${String(mtu)}`);
455
+ const isMissingGattShapeError = (error) => {
456
+ const code = error === null || error === void 0 ? void 0 : error.errorCode;
457
+ const message = error === null || error === void 0 ? void 0 : error.message;
458
+ return (code === hdShared.HardwareErrorCode.BleServiceNotFound ||
459
+ code === hdShared.HardwareErrorCode.BleCharacteristicNotFound ||
460
+ (typeof message === 'string' &&
461
+ (message.includes('BLECharacteristicNotFound') ||
462
+ message.includes('BLECharacteristicNotWritable') ||
463
+ message.includes('BLECharacteristicNotNotifiable'))));
464
+ };
465
+ const isStaleGattTableNotifyReason = (reason) => !!reason &&
466
+ (reason.includes('Cannot write client characteristic config descriptor') ||
467
+ reason.includes('Cannot find client characteristic config descriptor') ||
468
+ reason.includes('The handle is invalid') ||
469
+ reason.includes('Writing is not permitted'));
435
470
  const BLE_CONNECT_TIMEOUT_MS = BLE_NATIVE_CONNECT_TIMEOUT_MS * 2 + 2000;
436
471
  const BLE_GATT_SETUP_TIMEOUT_MS = 10000;
437
472
  const PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = 3;
@@ -446,10 +481,9 @@ const isWedgedBleSetupError = (error) => (error === null || error === void 0 ? v
446
481
  error.message.startsWith(BLE_SETUP_WEDGED_MESSAGE);
447
482
  const shouldRethrowBleSetupError = (error) => isConnectTimeoutError(error) || isWedgedBleSetupError(error);
448
483
  const isNativeOperationTimeoutError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === reactNativeBlePlx.BleErrorCode.OperationTimedOut;
449
- const isMtuOrCancelledConnectError = (error) => {
484
+ const isAbandonedNativeOperationError = (error) => {
450
485
  const errorCode = error === null || error === void 0 ? void 0 : error.errorCode;
451
- return (errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
452
- errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled);
486
+ return (errorCode === reactNativeBlePlx.BleErrorCode.OperationTimedOut || errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled);
453
487
  };
454
488
  const tryToGetConfiguration = (device) => {
455
489
  if (!device || !device.serviceUUIDs)
@@ -464,7 +498,7 @@ const tryToGetConfiguration = (device) => {
464
498
  };
465
499
  const requestNegotiatedMtu = (device, stage, attempt, cancelTransaction) => __awaiter(void 0, void 0, void 0, function* () {
466
500
  if (reactNative.Platform.OS !== 'ios' && reactNative.Platform.OS !== 'android')
467
- return { device, timedOut: false };
501
+ return { device, abandoned: false };
468
502
  const transactionId = `${device.id}:mtu:${stage}:${attempt}:${Date.now()}`;
469
503
  let timeoutId;
470
504
  let timedOut = false;
@@ -480,7 +514,7 @@ const requestNegotiatedMtu = (device, stage, attempt, cancelTransaction) => __aw
480
514
  }, BLE_MTU_REQUEST_TIMEOUT_MS);
481
515
  }),
482
516
  ]);
483
- return { device: mtuDevice, timedOut: false };
517
+ return { device: mtuDevice, abandoned: false };
484
518
  }
485
519
  catch (error) {
486
520
  if (timedOut && cancelTransaction) {
@@ -508,10 +542,9 @@ const requestNegotiatedMtu = (device, stage, attempt, cancelTransaction) => __aw
508
542
  stage,
509
543
  attempt,
510
544
  actual: device.mtu,
511
- timedOut,
512
545
  error: error instanceof Error ? error.message : String(error),
513
546
  });
514
- return { device, timedOut };
547
+ return { device, abandoned: timedOut };
515
548
  }
516
549
  finally {
517
550
  if (timeoutId)
@@ -520,7 +553,7 @@ const requestNegotiatedMtu = (device, stage, attempt, cancelTransaction) => __aw
520
553
  });
521
554
  const resolveNegotiatedMtu = (device, cancelTransaction) => shouldRefreshNegotiatedMtu(device.mtu)
522
555
  ? requestNegotiatedMtu(device, 'connected', 0, cancelTransaction)
523
- : Promise.resolve({ device, timedOut: false });
556
+ : Promise.resolve({ device, abandoned: false });
524
557
  function remapError(error) {
525
558
  var _a;
526
559
  if (error instanceof reactNativeBlePlx.BleError) {
@@ -557,6 +590,12 @@ class ReactNativeBleTransport {
557
590
  this.sessionProtocols = new Map();
558
591
  this.confirmedProtocolV2 = new Set();
559
592
  this.protocolReprobeFailures = new Map();
593
+ this.silentDetections = new Set();
594
+ this.androidGattCacheRefreshes = new Set();
595
+ this.androidMtuExchangeTimeoutMs = ANDROID_MTU_EXCHANGE_TIMEOUT_MS;
596
+ this.androidLinkDropQuietMs = ANDROID_LINK_DROP_QUIET_MS;
597
+ this.androidLinkDropTimeoutMs = ANDROID_LINK_DROP_TIMEOUT_MS;
598
+ this.protocolWakeAttempts = new Set();
560
599
  this.staleBondErrors = new Map();
561
600
  this.acquiringProtocolV2 = new Set();
562
601
  this.protocolV2Assemblers = new Map();
@@ -790,9 +829,10 @@ class ReactNativeBleTransport {
790
829
  transport.notifySubscription = undefined;
791
830
  let { device } = transport;
792
831
  const isConnected = yield device.isConnected().catch(() => false);
832
+ const reconnectOptions = reactNative.Platform.OS === 'android' ? androidRefreshGattConnectOptions : connectOptions;
793
833
  if (!isConnected) {
794
834
  try {
795
- device = yield this.connectWithTimeout(uuid, () => device.connect(connectOptions));
835
+ device = yield this.connectWithTimeout(uuid, () => device.connect(reconnectOptions));
796
836
  }
797
837
  catch (e) {
798
838
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
@@ -805,7 +845,16 @@ class ReactNativeBleTransport {
805
845
  }
806
846
  }
807
847
  const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, device);
848
+ if (reactNative.Platform.OS === 'android') {
849
+ const manager = yield this.getPlxManager();
850
+ device = yield this.negotiateAndroidMtu(uuid, manager, device);
851
+ if (isKnownDefaultMtu(device.mtu)) {
852
+ yield this.dropAndroidLink(uuid, manager, device, 'firmware reconnect default mtu');
853
+ throw createDefaultMtuLinkError(device.mtu);
854
+ }
855
+ }
808
856
  transport.device = device;
857
+ transport.mtuSize = typeof device.mtu === 'number' ? device.mtu : transport.mtuSize;
809
858
  transport.writeCharacteristic = writeCharacteristic;
810
859
  transport.notifyCharacteristic = notifyCharacteristic;
811
860
  const monitorToken = this.nextMonitorToken;
@@ -952,7 +1001,7 @@ class ReactNativeBleTransport {
952
1001
  }
953
1002
  installTransportForAcquire(uuid, device, characteristics) {
954
1003
  return __awaiter(this, void 0, void 0, function* () {
955
- const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristicsWithTimeout(uuid, device));
1004
+ const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristicsForAcquire(uuid, device));
956
1005
  if (this.stopped)
957
1006
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
958
1007
  const transport$1 = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
@@ -1030,7 +1079,11 @@ class ReactNativeBleTransport {
1030
1079
  const isCachedDeviceConnected = yield cachedTransport.device
1031
1080
  .isConnected()
1032
1081
  .catch(() => false);
1082
+ const isCachedAndroidLinkUsable = reactNative.Platform.OS !== 'android' ||
1083
+ (!isKnownDefaultMtu(cachedTransport.mtuSize) &&
1084
+ !this.androidGattCacheRefreshes.has(uuid));
1033
1085
  if (isCachedDeviceConnected &&
1086
+ isCachedAndroidLinkUsable &&
1034
1087
  cachedProtocol &&
1035
1088
  (!expectedProtocol || cachedProtocol === expectedProtocol)) {
1036
1089
  if (this.stopped)
@@ -1043,6 +1096,15 @@ class ReactNativeBleTransport {
1043
1096
  }
1044
1097
  }
1045
1098
  let device = null;
1099
+ const isAndroid = reactNative.Platform.OS === 'android';
1100
+ const refreshAndroidGattCache = isAndroid && (!!skipProtocolProbe || this.androidGattCacheRefreshes.has(uuid));
1101
+ let nativeConnectOptions = connectOptions;
1102
+ if (isAndroid) {
1103
+ nativeConnectOptions = refreshAndroidGattCache
1104
+ ? androidRefreshGattConnectOptions
1105
+ : androidConnectOptions;
1106
+ }
1107
+ let androidRefreshConnectRan = false;
1046
1108
  if (forceCleanRunPromise && this.runPromise) {
1047
1109
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
1048
1110
  this.runPromise.reject(error);
@@ -1051,7 +1113,6 @@ class ReactNativeBleTransport {
1051
1113
  Log === null || Log === void 0 ? void 0 : Log.debug('Force clean Bluetooth run promise, forceCleanRunPromise: ', forceCleanRunPromise);
1052
1114
  }
1053
1115
  const blePlxManager = yield this.getPlxManager();
1054
- let skipPostConnectMtu = false;
1055
1116
  try {
1056
1117
  yield subscribeBleOn(blePlxManager);
1057
1118
  }
@@ -1093,16 +1154,18 @@ class ReactNativeBleTransport {
1093
1154
  if (!device) {
1094
1155
  Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device: ', uuid);
1095
1156
  try {
1096
- device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, connectOptions));
1157
+ device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, nativeConnectOptions));
1158
+ androidRefreshConnectRan = refreshAndroidGattCache;
1097
1159
  }
1098
1160
  catch (e) {
1099
1161
  Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device has error: ', e);
1100
1162
  if (shouldRethrowBleSetupError(e)) {
1101
1163
  throw e;
1102
1164
  }
1103
- if (isMtuOrCancelledConnectError(e)) {
1104
- skipPostConnectMtu = true;
1165
+ if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
1166
+ e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
1105
1167
  Log === null || Log === void 0 ? void 0 : Log.debug('first try to reconnect without params');
1168
+ yield this.runBestEffortNativeOperation('connect cancelled: cancel manager connection', () => blePlxManager.cancelDeviceConnection(uuid));
1106
1169
  device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, fallbackConnectOptions));
1107
1170
  }
1108
1171
  else if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
@@ -1117,20 +1180,27 @@ class ReactNativeBleTransport {
1117
1180
  if (!device) {
1118
1181
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, 'unable to connect to device');
1119
1182
  }
1183
+ if (isAndroid && refreshAndroidGattCache && (yield device.isConnected().catch(() => false))) {
1184
+ yield this.dropAndroidLink(uuid, blePlxManager, device, 'gatt cache refresh');
1185
+ if (this.stopped)
1186
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
1187
+ }
1120
1188
  if (!(yield device.isConnected())) {
1121
1189
  Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device: ', uuid);
1122
1190
  const disconnectedDevice = device;
1123
1191
  try {
1124
- device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(connectOptions));
1192
+ device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(nativeConnectOptions));
1193
+ androidRefreshConnectRan = refreshAndroidGattCache;
1125
1194
  }
1126
1195
  catch (e) {
1127
1196
  Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device has error: ', e);
1128
1197
  if (shouldRethrowBleSetupError(e)) {
1129
1198
  throw e;
1130
1199
  }
1131
- if (isMtuOrCancelledConnectError(e)) {
1132
- skipPostConnectMtu = true;
1200
+ if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
1201
+ e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
1133
1202
  Log === null || Log === void 0 ? void 0 : Log.debug('second try to reconnect without params');
1203
+ yield this.runBestEffortNativeOperation('connect cancelled: cancel device connection', () => disconnectedDevice.cancelConnection());
1134
1204
  try {
1135
1205
  device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
1136
1206
  }
@@ -1165,24 +1235,38 @@ class ReactNativeBleTransport {
1165
1235
  }
1166
1236
  if (this.stopped)
1167
1237
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
1168
- if (!skipPostConnectMtu) {
1169
- const mtuResult = yield resolveNegotiatedMtu(device, transactionId => blePlxManager.cancelTransaction(transactionId));
1170
- device = mtuResult.device;
1171
- if (mtuResult.timedOut) {
1172
- if (this.stopped)
1173
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
1174
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] post-connect MTU timed out, reconnecting without requesting MTU');
1175
- const timedOutDevice = device;
1176
- yield this.runBestEffortNativeOperation('mtu timeout: cancel device connection', () => timedOutDevice.cancelConnection());
1238
+ let characteristics;
1239
+ if (isAndroid) {
1240
+ if (refreshAndroidGattCache) {
1241
+ characteristics = yield this.resolveCharacteristicsForAcquire(uuid, device);
1242
+ if (androidRefreshConnectRan)
1243
+ this.androidGattCacheRefreshes.delete(uuid);
1177
1244
  if (this.stopped)
1178
1245
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
1179
- device = yield this.connectWithTimeout(uuid, () => timedOutDevice.connect(fallbackConnectOptions));
1246
+ }
1247
+ device = yield this.negotiateAndroidMtu(uuid, blePlxManager, device);
1248
+ if (isKnownDefaultMtu(device.mtu)) {
1249
+ const resetManager = this.abandonStalledConnection(uuid, 'mtu-default');
1250
+ yield this.dropAndroidLink(uuid, blePlxManager, device, 'default mtu');
1251
+ if (resetManager)
1252
+ throw this.createWedgedBleSetupError();
1253
+ throw createDefaultMtuLinkError(device.mtu);
1254
+ }
1255
+ }
1256
+ else {
1257
+ const negotiatedMtu = yield resolveNegotiatedMtu(device, transactionId => blePlxManager.cancelTransaction(transactionId));
1258
+ device = negotiatedMtu.device;
1259
+ if (negotiatedMtu.abandoned) {
1260
+ yield this.runNativeTeardown(uuid, blePlxManager, () => __awaiter(this, void 0, void 0, function* () {
1261
+ yield this.runBestEffortNativeOperation('mtu abandoned: cancel manager connection', () => blePlxManager.cancelDeviceConnection(uuid));
1262
+ }));
1263
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, 'BLE MTU negotiation abandoned, reconnecting');
1180
1264
  }
1181
1265
  }
1182
1266
  if (this.stopped)
1183
1267
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
1184
1268
  const acquiredDevice = device;
1185
- const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, acquiredDevice);
1269
+ const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristicsForAcquire(uuid, acquiredDevice));
1186
1270
  if (this.stopped)
1187
1271
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
1188
1272
  const protocolHint = expectedProtocol
@@ -1278,6 +1362,9 @@ class ReactNativeBleTransport {
1278
1362
  ((_f = error.reason) === null || _f === void 0 ? void 0 : _f.includes('notify change failed for device'))) {
1279
1363
  errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure;
1280
1364
  }
1365
+ if (reactNative.Platform.OS === 'android' && isStaleGattTableNotifyReason(error.reason)) {
1366
+ this.androidGattCacheRefreshes.add(uuid);
1367
+ }
1281
1368
  this.rejectProtocolV2Frames(uuid, hdShared.ERRORS.TypedError(errorCode));
1282
1369
  return;
1283
1370
  }
@@ -1292,6 +1379,9 @@ class ReactNativeBleTransport {
1292
1379
  ((_l = error.reason) === null || _l === void 0 ? void 0 : _l.includes('Writing is not permitted')) ||
1293
1380
  ((_m = error.reason) === null || _m === void 0 ? void 0 : _m.includes('notify change failed for device'))) {
1294
1381
  const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure);
1382
+ if (reactNative.Platform.OS === 'android' && isStaleGattTableNotifyReason(error.reason)) {
1383
+ this.androidGattCacheRefreshes.add(uuid);
1384
+ }
1295
1385
  this.runPromise.reject(notifyError);
1296
1386
  Log === null || Log === void 0 ? void 0 : Log.debug(`${hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure} ${error.message} ${error.reason}`);
1297
1387
  return;
@@ -1826,6 +1916,111 @@ class ReactNativeBleTransport {
1826
1916
  }
1827
1917
  });
1828
1918
  }
1919
+ negotiateAndroidMtu(uuid, manager, device) {
1920
+ return __awaiter(this, void 0, void 0, function* () {
1921
+ if (!shouldRefreshNegotiatedMtu(device.mtu))
1922
+ return device;
1923
+ const startedAt = Date.now();
1924
+ const transactionId = `${device.id}:mtu:connected:0:${startedAt}`;
1925
+ let timer;
1926
+ let timedOut = false;
1927
+ const request = device.requestMTU(ANDROID_REQUEST_MTU, transactionId);
1928
+ request.catch(() => undefined);
1929
+ try {
1930
+ const negotiated = yield Promise.race([
1931
+ request,
1932
+ new Promise((_, reject) => {
1933
+ timer = setTimeout(() => {
1934
+ timedOut = true;
1935
+ reject(new Error(`BLE MTU exchange timeout after ${this.androidMtuExchangeTimeoutMs}ms`));
1936
+ }, this.androidMtuExchangeTimeoutMs);
1937
+ }),
1938
+ ]);
1939
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE MTU exchange completed', {
1940
+ connectIdSuffix: uuid.slice(-8),
1941
+ elapsedMs: Date.now() - startedAt,
1942
+ actual: negotiated.mtu,
1943
+ });
1944
+ return negotiated;
1945
+ }
1946
+ catch (error) {
1947
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE MTU exchange failed', {
1948
+ connectIdSuffix: uuid.slice(-8),
1949
+ elapsedMs: Date.now() - startedAt,
1950
+ timedOut,
1951
+ actual: device.mtu,
1952
+ error: error instanceof Error ? error.message : String(error),
1953
+ });
1954
+ if (this.stopped)
1955
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
1956
+ if (!timedOut)
1957
+ return device;
1958
+ yield Promise.resolve(manager.cancelTransaction(transactionId)).catch(() => undefined);
1959
+ const resetManager = this.abandonStalledConnection(uuid, 'mtu-backstop');
1960
+ yield this.dropAndroidLink(uuid, manager, device, 'mtu exchange timeout');
1961
+ if (resetManager)
1962
+ throw this.createWedgedBleSetupError();
1963
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, 'BLE MTU exchange did not complete, reconnecting on a fresh link');
1964
+ }
1965
+ finally {
1966
+ if (timer)
1967
+ clearTimeout(timer);
1968
+ }
1969
+ });
1970
+ }
1971
+ dropAndroidLink(uuid, manager, device, reason) {
1972
+ return __awaiter(this, void 0, void 0, function* () {
1973
+ yield this.runNativeTeardown(uuid, manager, () => __awaiter(this, void 0, void 0, function* () {
1974
+ yield Promise.all([
1975
+ this.runBestEffortNativeOperation(`${reason}: cancel manager connection`, () => manager.cancelDeviceConnection(uuid)),
1976
+ this.runBestEffortNativeOperation(`${reason}: cancel device connection`, () => device.cancelConnection()),
1977
+ ]);
1978
+ }));
1979
+ const startedAt = Date.now();
1980
+ const target = uuid.toUpperCase();
1981
+ let registryClear = false;
1982
+ let quietPeriodElapsed = false;
1983
+ while (!this.stopped) {
1984
+ const elapsed = Date.now() - startedAt;
1985
+ quietPeriodElapsed = elapsed >= this.androidLinkDropQuietMs;
1986
+ if (!registryClear) {
1987
+ const connected = yield getConnectedDeviceIds([]).catch(() => undefined);
1988
+ registryClear =
1989
+ !!connected &&
1990
+ !connected.some(peripheral => { var _a; return String((_a = peripheral === null || peripheral === void 0 ? void 0 : peripheral.id) !== null && _a !== void 0 ? _a : '').toUpperCase() === target; });
1991
+ }
1992
+ if ((registryClear && quietPeriodElapsed) || elapsed >= this.androidLinkDropTimeoutMs) {
1993
+ break;
1994
+ }
1995
+ yield delay(ANDROID_LINK_DROP_POLL_MS);
1996
+ }
1997
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE link drop', {
1998
+ connectIdSuffix: uuid.slice(-8),
1999
+ reason,
2000
+ registryClear,
2001
+ quietPeriodElapsed,
2002
+ stopped: this.stopped,
2003
+ elapsedMs: Date.now() - startedAt,
2004
+ });
2005
+ });
2006
+ }
2007
+ resolveCharacteristicsForAcquire(uuid, device) {
2008
+ return __awaiter(this, void 0, void 0, function* () {
2009
+ try {
2010
+ return yield this.resolveCharacteristicsWithTimeout(uuid, device);
2011
+ }
2012
+ catch (error) {
2013
+ if (reactNative.Platform.OS === 'android' && isMissingGattShapeError(error)) {
2014
+ this.androidGattCacheRefreshes.add(uuid);
2015
+ const manager = this.blePlxManager;
2016
+ if (manager) {
2017
+ yield this.dropAndroidLink(uuid, manager, device, 'stale gatt table');
2018
+ }
2019
+ }
2020
+ throw error;
2021
+ }
2022
+ });
2023
+ }
1829
2024
  connectWithTimeout(uuid, connect) {
1830
2025
  return __awaiter(this, void 0, void 0, function* () {
1831
2026
  if (this.stopped)
@@ -1893,7 +2088,7 @@ class ReactNativeBleTransport {
1893
2088
  return result;
1894
2089
  }
1895
2090
  catch (error) {
1896
- if (timedOut || isNativeOperationTimeoutError(error)) {
2091
+ if (timedOut || isAbandonedNativeOperationError(error)) {
1897
2092
  const resetManager = this.abandonStalledConnection(uuid, timedOut ? 'gatt-backstop' : 'gatt-native');
1898
2093
  if (resetManager) {
1899
2094
  throw this.createWedgedBleSetupError();
@@ -2050,6 +2245,8 @@ class ReactNativeBleTransport {
2050
2245
  this.acquiringProtocolV2.clear();
2051
2246
  this.sessionProtocols.clear();
2052
2247
  this.protocolReprobeFailures.clear();
2248
+ this.silentDetections.clear();
2249
+ this.protocolWakeAttempts.clear();
2053
2250
  this.writeTimeoutCounts.clear();
2054
2251
  this.connectionSetupTimeoutCounts.clear();
2055
2252
  this.monitorTokens.clear();
@@ -2123,6 +2320,7 @@ class ReactNativeBleTransport {
2123
2320
  !protocolHint &&
2124
2321
  reprobeFailures < PROTOCOL_REPROBE_FALLBACK_ATTEMPTS;
2125
2322
  const probeOrder = trustSessionProtocol ? [sessionProtocol] : fullProbeOrder;
2323
+ yield this.wakeSilentProtocolV1Device(uuid, probeOrder);
2126
2324
  for (let i = 0; i < probeOrder.length; i += 1) {
2127
2325
  const protocol = probeOrder[i];
2128
2326
  if (i > 0) {
@@ -2142,6 +2340,8 @@ class ReactNativeBleTransport {
2142
2340
  this.confirmedProtocolV2.add(uuid);
2143
2341
  }
2144
2342
  this.protocolReprobeFailures.delete(uuid);
2343
+ this.silentDetections.delete(uuid);
2344
+ this.protocolWakeAttempts.delete(uuid);
2145
2345
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
2146
2346
  deviceId: uuid,
2147
2347
  protocol,
@@ -2150,6 +2350,7 @@ class ReactNativeBleTransport {
2150
2350
  return protocol;
2151
2351
  }
2152
2352
  }
2353
+ this.silentDetections.add(uuid);
2153
2354
  if (trustSessionProtocol) {
2154
2355
  this.protocolReprobeFailures.set(uuid, reprobeFailures + 1);
2155
2356
  }
@@ -2161,6 +2362,38 @@ class ReactNativeBleTransport {
2161
2362
  throw this.createProtocolDetectionError();
2162
2363
  });
2163
2364
  }
2365
+ wakeSilentProtocolV1Device(uuid, probeOrder) {
2366
+ return __awaiter(this, void 0, void 0, function* () {
2367
+ if (!this._messages)
2368
+ return;
2369
+ if (!probeOrder.includes('V1'))
2370
+ return;
2371
+ if (!this.silentDetections.has(uuid))
2372
+ return;
2373
+ if (this.protocolWakeAttempts.has(uuid))
2374
+ return;
2375
+ if (!transportCache[uuid])
2376
+ return;
2377
+ this.protocolWakeAttempts.add(uuid);
2378
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] sending Protocol V1 Initialize wake', {
2379
+ connectIdSuffix: uuid.slice(-8),
2380
+ });
2381
+ try {
2382
+ this.probingProtocols.set(uuid, 'V1');
2383
+ yield this.callProtocolV1(uuid, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
2384
+ }
2385
+ catch (error) {
2386
+ if (shouldRethrowProtocolProbeError(error)) {
2387
+ this.clearProbeProtocol(uuid, 'V1');
2388
+ throw error;
2389
+ }
2390
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 Initialize wake did not answer:', error);
2391
+ }
2392
+ finally {
2393
+ this.clearProbeProtocol(uuid, 'V1');
2394
+ }
2395
+ });
2396
+ }
2164
2397
  resetProbeStateAfterProtocolProbe(uuid, protocol) {
2165
2398
  var _a, _b, _c;
2166
2399
  return __awaiter(this, void 0, void 0, function* () {
@@ -2415,6 +2648,12 @@ class ReactNativeBleTransport {
2415
2648
  if (!this._messages || !this._messagesV2) {
2416
2649
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
2417
2650
  }
2651
+ if (reactNative.Platform.OS === 'android') {
2652
+ const activeTransport = transportCache[uuid];
2653
+ if (activeTransport && isKnownDefaultMtu(activeTransport.mtuSize)) {
2654
+ throw createProtocolV2DefaultMtuError(activeTransport.mtuSize);
2655
+ }
2656
+ }
2418
2657
  const isProtocolProbe = this.probingProtocols.get(uuid) === 'V2';
2419
2658
  const callOptions = options;
2420
2659
  const highThroughputWrite = transport.isProtocolV2HighThroughputCall(name);
@@ -2478,11 +2717,11 @@ class ReactNativeBleTransport {
2478
2717
  const transport = this.getCachedTransport(uuid);
2479
2718
  if (!shouldRefreshNegotiatedMtu(transport.mtuSize))
2480
2719
  return;
2481
- 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); });
2720
+ const { device: refreshedDevice, abandoned } = yield requestNegotiatedMtu(transport.device, 'highThroughput', 1, transactionId => { var _a; return (_a = this.blePlxManager) === null || _a === void 0 ? void 0 : _a.cancelTransaction(transactionId); });
2482
2721
  transport.device = refreshedDevice;
2483
2722
  transport.mtuSize =
2484
2723
  typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport.mtuSize;
2485
- if (shouldRefreshNegotiatedMtu(transport.mtuSize)) {
2724
+ if (abandoned || shouldRefreshNegotiatedMtu(transport.mtuSize)) {
2486
2725
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `Protocol V2 high-throughput BLE MTU unavailable: ${String(transport.mtuSize)}`);
2487
2726
  }
2488
2727
  });
@@ -2595,6 +2834,9 @@ class ReactNativeBleTransport {
2595
2834
  }
2596
2835
  }
2597
2836
 
2837
+ exports.ANDROID_LINK_DROP_QUIET_MS = ANDROID_LINK_DROP_QUIET_MS;
2838
+ exports.ANDROID_LINK_DROP_TIMEOUT_MS = ANDROID_LINK_DROP_TIMEOUT_MS;
2839
+ exports.ANDROID_MTU_EXCHANGE_TIMEOUT_MS = ANDROID_MTU_EXCHANGE_TIMEOUT_MS;
2598
2840
  exports.BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD;
2599
2841
  exports.BLE_CONNECT_TIMEOUT_MS = BLE_CONNECT_TIMEOUT_MS;
2600
2842
  exports.BLE_GATT_SETUP_TIMEOUT_MS = BLE_GATT_SETUP_TIMEOUT_MS;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-react-native",
3
- "version": "1.2.2-alpha.114",
3
+ "version": "1.2.2-alpha.116",
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.114",
24
- "@onekeyfe/hd-shared": "1.2.2-alpha.114",
25
- "@onekeyfe/hd-transport": "1.2.2-alpha.114",
23
+ "@onekeyfe/hd-core": "1.2.2-alpha.116",
24
+ "@onekeyfe/hd-shared": "1.2.2-alpha.116",
25
+ "@onekeyfe/hd-transport": "1.2.2-alpha.116",
26
26
  "@onekeyfe/react-native-ble-utils": "0.1.6",
27
27
  "react-native-ble-plx": "3.5.1"
28
28
  },
29
- "gitHead": "6490382eeeb6bda264b71c7e9bf96143840ee1cb"
29
+ "gitHead": "222deb57d08349d95cfcaa8fac4b705e3aeaff8a"
30
30
  }