@onekeyfe/hd-transport-react-native 1.2.2-alpha.115 → 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.d.ts CHANGED
@@ -74,6 +74,28 @@ declare function getProtocolV2BleTuning(): {
74
74
  iosPacketLength: number;
75
75
  androidPacketLength: number;
76
76
  };
77
+ /**
78
+ * Bound for the Android MTU exchange. A healthy exchange with a Pro 2 completes in ~50ms.
79
+ * When the phone has no cached GATT table (first connect after bonding, or a connect after
80
+ * an aborted discovery) the stack runs its own discovery first and only executes the MTU
81
+ * request after it; single passes on a Pro 2 at MTU 23 measured 1.7-4.8s and a refresh
82
+ * plus restart 7.7s, so the bound has to sit well above those. An exchange the stack holds
83
+ * as already in progress never completes, so a longer bound costs nothing on that link.
84
+ */
85
+ declare const ANDROID_MTU_EXCHANGE_TIMEOUT_MS = 12000;
86
+ /**
87
+ * Android keeps an LE link up for its GATT link idle timer (4s) after the last client
88
+ * closes, and per-link ATT state such as a pending MTU exchange lives as long as the link.
89
+ * A reconnect inside that window attaches to the same link. The only connectivity signal
90
+ * reachable from here, BluetoothManager.getConnectedDevices(GATT), is built from the
91
+ * per-app GATT client registry rather than the LE link: it empties the moment this client
92
+ * closes on phones with no resident GATT server, and stays populated for the life of the
93
+ * link on phones that have one. So an unusable link is held for at least the quiet period
94
+ * (past the idle timer), and additionally until that registry no longer lists the device,
95
+ * within the overall bound.
96
+ */
97
+ declare const ANDROID_LINK_DROP_QUIET_MS = 5000;
98
+ declare const ANDROID_LINK_DROP_TIMEOUT_MS = 8000;
77
99
  /**
78
100
  * JS backstop for connect. The native adapter applies its own 3s budget, but it
79
101
  * schedules that timeout on its serial queue, so a busy queue (e.g. right after a
@@ -137,6 +159,16 @@ declare class ReactNativeBleTransport {
137
159
  private protocolReprobeFailures;
138
160
  /** Endpoints whose last detection got no answer on any protocol. */
139
161
  private silentDetections;
162
+ /**
163
+ * Android endpoints whose cached GATT table is suspect and must be rediscovered on the
164
+ * next connect. The cache is no longer refreshed on every connect, so a missing OneKey
165
+ * service or characteristic marks the endpoint instead.
166
+ */
167
+ private androidGattCacheRefreshes;
168
+ /** Instance copies of the Android timing bounds so tests can shorten real-time waits. */
169
+ androidMtuExchangeTimeoutMs: number;
170
+ androidLinkDropQuietMs: number;
171
+ androidLinkDropTimeoutMs: number;
140
172
  /**
141
173
  * Endpoints already sent a Protocol V1 wake. The wake starts a fresh wallet session
142
174
  * on the device, and a failed detection tears the link down and reconnects on every
@@ -203,6 +235,21 @@ declare class ReactNativeBleTransport {
203
235
  private runBestEffortNativeOperation;
204
236
  private runLifecycleOperation;
205
237
  cancel(): Promise<void>;
238
+ /**
239
+ * Negotiate the ATT MTU as the first request on an Android GATT client, with nothing queued
240
+ * ahead of it, and never close the client while it is outstanding: an exchange abandoned
241
+ * mid-flight stays marked in progress for the whole LE link. A link that already negotiated
242
+ * an MTU answers at once with the recorded value.
243
+ */
244
+ private negotiateAndroidMtu;
245
+ /**
246
+ * Abandon an Android LE link whose per-link GATT state is unusable. Closing the GATT
247
+ * client alone keeps the link up for the stack's idle timer, and Core's retry arrives
248
+ * inside that window, so this holds for at least ANDROID_LINK_DROP_QUIET_MS and until the
249
+ * GATT registry no longer lists the device, capped at ANDROID_LINK_DROP_TIMEOUT_MS.
250
+ */
251
+ private dropAndroidLink;
252
+ private resolveCharacteristicsForAcquire;
206
253
  /** Run a native connect under the JS backstop budget. */
207
254
  private connectWithTimeout;
208
255
  /** Resolve the complete GATT shape under a budget so acquire() always settles. */
@@ -277,4 +324,4 @@ declare class ReactNativeBleTransport {
277
324
  getProtocolType(path: string): ProtocolType | undefined;
278
325
  }
279
326
 
280
- export { BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD, BLE_CONNECT_TIMEOUT_MS, BLE_GATT_SETUP_TIMEOUT_MS, BLE_NATIVE_TEARDOWN_TIMEOUT_MS, BLE_SETUP_WEDGED_MESSAGE, BLE_WRITE_PACKET_TIMEOUT_MS, BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD, IOneKeyDevice, PROTOCOL_REPROBE_FALLBACK_ATTEMPTS, ProtocolV2BleTuning, configureProtocolV2BleTuning, ReactNativeBleTransport as default, getFirmwareUploadWriteRetryType, getProtocolV2BleTuning, resetProtocolV2BleTuning };
327
+ export { ANDROID_LINK_DROP_QUIET_MS, ANDROID_LINK_DROP_TIMEOUT_MS, ANDROID_MTU_EXCHANGE_TIMEOUT_MS, BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD, BLE_CONNECT_TIMEOUT_MS, BLE_GATT_SETUP_TIMEOUT_MS, BLE_NATIVE_TEARDOWN_TIMEOUT_MS, BLE_SETUP_WEDGED_MESSAGE, BLE_WRITE_PACKET_TIMEOUT_MS, BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD, IOneKeyDevice, PROTOCOL_REPROBE_FALLBACK_ATTEMPTS, ProtocolV2BleTuning, configureProtocolV2BleTuning, ReactNativeBleTransport as default, getFirmwareUploadWriteRetryType, getProtocolV2BleTuning, resetProtocolV2BleTuning };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
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;
@@ -556,6 +591,10 @@ class ReactNativeBleTransport {
556
591
  this.confirmedProtocolV2 = new Set();
557
592
  this.protocolReprobeFailures = new Map();
558
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;
559
598
  this.protocolWakeAttempts = new Set();
560
599
  this.staleBondErrors = new Map();
561
600
  this.acquiringProtocolV2 = new Set();
@@ -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,7 +1096,15 @@ class ReactNativeBleTransport {
1043
1096
  }
1044
1097
  }
1045
1098
  let device = null;
1046
- let mtuHandshakeRefused = false;
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;
1047
1108
  if (forceCleanRunPromise && this.runPromise) {
1048
1109
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
1049
1110
  this.runPromise.reject(error);
@@ -1093,7 +1154,8 @@ 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);
@@ -1103,7 +1165,6 @@ class ReactNativeBleTransport {
1103
1165
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
1104
1166
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
1105
1167
  Log === null || Log === void 0 ? void 0 : Log.debug('first try to reconnect without params');
1106
- mtuHandshakeRefused = true;
1107
1168
  yield this.runBestEffortNativeOperation('connect cancelled: cancel manager connection', () => blePlxManager.cancelDeviceConnection(uuid));
1108
1169
  device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, fallbackConnectOptions));
1109
1170
  }
@@ -1119,11 +1180,17 @@ class ReactNativeBleTransport {
1119
1180
  if (!device) {
1120
1181
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, 'unable to connect to device');
1121
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
+ }
1122
1188
  if (!(yield device.isConnected())) {
1123
1189
  Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device: ', uuid);
1124
1190
  const disconnectedDevice = device;
1125
1191
  try {
1126
- device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(connectOptions));
1192
+ device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(nativeConnectOptions));
1193
+ androidRefreshConnectRan = refreshAndroidGattCache;
1127
1194
  }
1128
1195
  catch (e) {
1129
1196
  Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device has error: ', e);
@@ -1133,7 +1200,6 @@ class ReactNativeBleTransport {
1133
1200
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
1134
1201
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
1135
1202
  Log === null || Log === void 0 ? void 0 : Log.debug('second try to reconnect without params');
1136
- mtuHandshakeRefused = true;
1137
1203
  yield this.runBestEffortNativeOperation('connect cancelled: cancel device connection', () => disconnectedDevice.cancelConnection());
1138
1204
  try {
1139
1205
  device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
@@ -1169,11 +1235,23 @@ class ReactNativeBleTransport {
1169
1235
  }
1170
1236
  if (this.stopped)
1171
1237
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
1172
- if (mtuHandshakeRefused) {
1173
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] skipping MTU refresh after cancelled MTU connect', {
1174
- connectIdSuffix: uuid.slice(-8),
1175
- actual: device.mtu,
1176
- });
1238
+ let characteristics;
1239
+ if (isAndroid) {
1240
+ if (refreshAndroidGattCache) {
1241
+ characteristics = yield this.resolveCharacteristicsForAcquire(uuid, device);
1242
+ if (androidRefreshConnectRan)
1243
+ this.androidGattCacheRefreshes.delete(uuid);
1244
+ if (this.stopped)
1245
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
1246
+ }
1247
+ device = yield this.negotiateAndroidMtu(uuid, blePlxManager, device);
1248
+ 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
+ }
1177
1255
  }
1178
1256
  else {
1179
1257
  const negotiatedMtu = yield resolveNegotiatedMtu(device, transactionId => blePlxManager.cancelTransaction(transactionId));
@@ -1188,7 +1266,7 @@ class ReactNativeBleTransport {
1188
1266
  if (this.stopped)
1189
1267
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
1190
1268
  const acquiredDevice = device;
1191
- const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, acquiredDevice);
1269
+ const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristicsForAcquire(uuid, acquiredDevice));
1192
1270
  if (this.stopped)
1193
1271
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
1194
1272
  const protocolHint = expectedProtocol
@@ -1284,6 +1362,9 @@ class ReactNativeBleTransport {
1284
1362
  ((_f = error.reason) === null || _f === void 0 ? void 0 : _f.includes('notify change failed for device'))) {
1285
1363
  errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure;
1286
1364
  }
1365
+ if (reactNative.Platform.OS === 'android' && isStaleGattTableNotifyReason(error.reason)) {
1366
+ this.androidGattCacheRefreshes.add(uuid);
1367
+ }
1287
1368
  this.rejectProtocolV2Frames(uuid, hdShared.ERRORS.TypedError(errorCode));
1288
1369
  return;
1289
1370
  }
@@ -1298,6 +1379,9 @@ class ReactNativeBleTransport {
1298
1379
  ((_l = error.reason) === null || _l === void 0 ? void 0 : _l.includes('Writing is not permitted')) ||
1299
1380
  ((_m = error.reason) === null || _m === void 0 ? void 0 : _m.includes('notify change failed for device'))) {
1300
1381
  const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure);
1382
+ if (reactNative.Platform.OS === 'android' && isStaleGattTableNotifyReason(error.reason)) {
1383
+ this.androidGattCacheRefreshes.add(uuid);
1384
+ }
1301
1385
  this.runPromise.reject(notifyError);
1302
1386
  Log === null || Log === void 0 ? void 0 : Log.debug(`${hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure} ${error.message} ${error.reason}`);
1303
1387
  return;
@@ -1832,6 +1916,111 @@ class ReactNativeBleTransport {
1832
1916
  }
1833
1917
  });
1834
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
+ }
1835
2024
  connectWithTimeout(uuid, connect) {
1836
2025
  return __awaiter(this, void 0, void 0, function* () {
1837
2026
  if (this.stopped)
@@ -2459,6 +2648,12 @@ class ReactNativeBleTransport {
2459
2648
  if (!this._messages || !this._messagesV2) {
2460
2649
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
2461
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
+ }
2462
2657
  const isProtocolProbe = this.probingProtocols.get(uuid) === 'V2';
2463
2658
  const callOptions = options;
2464
2659
  const highThroughputWrite = transport.isProtocolV2HighThroughputCall(name);
@@ -2639,6 +2834,9 @@ class ReactNativeBleTransport {
2639
2834
  }
2640
2835
  }
2641
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;
2642
2840
  exports.BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD;
2643
2841
  exports.BLE_CONNECT_TIMEOUT_MS = BLE_CONNECT_TIMEOUT_MS;
2644
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.115",
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.115",
24
- "@onekeyfe/hd-shared": "1.2.2-alpha.115",
25
- "@onekeyfe/hd-transport": "1.2.2-alpha.115",
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": "a115ca6998586c05c22996f278091778fd5471f1"
29
+ "gitHead": "222deb57d08349d95cfcaa8fac4b705e3aeaff8a"
30
30
  }
@@ -6,6 +6,9 @@ import { ERRORS, HardwareErrorCode, createDeferred } from '@onekeyfe/hd-shared';
6
6
 
7
7
  import { onDeviceBondState } from '../BleManager';
8
8
  import ReactNativeBleTransport, {
9
+ ANDROID_LINK_DROP_QUIET_MS,
10
+ ANDROID_LINK_DROP_TIMEOUT_MS,
11
+ ANDROID_MTU_EXCHANGE_TIMEOUT_MS,
9
12
  BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD,
10
13
  BLE_CONNECT_TIMEOUT_MS,
11
14
  BLE_GATT_SETUP_TIMEOUT_MS,
@@ -641,3 +644,104 @@ describe('BLE connect timeout', () => {
641
644
  expect((transport as any).connectionSetupTimeoutCounts.has(UUID)).toBe(false);
642
645
  });
643
646
  });
647
+
648
+ describe('Android MTU and link-drop bounds', () => {
649
+ beforeAll(() => {
650
+ jest.useFakeTimers({ doNotFake: ['setImmediate', 'performance'] });
651
+ });
652
+
653
+ afterAll(() => {
654
+ jest.useRealTimers();
655
+ });
656
+
657
+ afterEach(() => {
658
+ jest.clearAllTimers();
659
+ jest.restoreAllMocks();
660
+ Object.assign(Platform, { OS: 'ios' });
661
+ });
662
+
663
+ test('the bounds sit above a cold-cache discovery and the 4s GATT link idle timer', () => {
664
+ // Single discovery passes on a Pro 2 at MTU 23 measured up to 4.8s, a refresh plus
665
+ // restart 7.7s; a stuck exchange never completes, so the bound only has to clear those.
666
+ expect(ANDROID_MTU_EXCHANGE_TIMEOUT_MS).toBeGreaterThanOrEqual(10_000);
667
+ expect(ANDROID_MTU_EXCHANGE_TIMEOUT_MS).toBeLessThanOrEqual(20_000);
668
+ // Android keeps the LE link for 4s after the last GATT client closes.
669
+ expect(ANDROID_LINK_DROP_QUIET_MS).toBeGreaterThan(4000);
670
+ expect(ANDROID_LINK_DROP_QUIET_MS).toBeLessThanOrEqual(ANDROID_LINK_DROP_TIMEOUT_MS);
671
+ expect(ANDROID_LINK_DROP_TIMEOUT_MS).toBeLessThanOrEqual(10_000);
672
+ });
673
+
674
+ test('holds an unusable link past the idle timer even when the GATT registry is already empty', async () => {
675
+ Object.assign(Platform, { OS: 'android' });
676
+ const { transport, device, bleManager } = createHarness(() => Promise.resolve(device));
677
+ device.isConnected.mockResolvedValue(true);
678
+ Object.assign(device, { mtu: 23, requestMTU: jest.fn(() => new Promise(() => {})) });
679
+ let settled: Error | undefined;
680
+ const acquire = transport.acquire({ uuid: UUID, expectedProtocol: 'V1' }).catch(error => {
681
+ settled = error;
682
+ });
683
+ await flush();
684
+ await flush();
685
+ expect(device.requestMTU).toHaveBeenCalledTimes(1);
686
+
687
+ jest.advanceTimersByTime(ANDROID_MTU_EXCHANGE_TIMEOUT_MS);
688
+ await flush();
689
+ await flush();
690
+ expect(bleManager.cancelDeviceConnection).toHaveBeenCalledWith(UUID);
691
+
692
+ // BluetoothManager.getConnectedDevices(GATT) reports this app's GATT client, which is
693
+ // already closed; the LE link itself stays up for the idle timer, so the wait must not
694
+ // release on that signal alone.
695
+ expect(
696
+ (BleUtils as unknown as { getConnectedPeripherals: jest.Mock }).getConnectedPeripherals
697
+ ).toHaveBeenCalled();
698
+ jest.advanceTimersByTime(4000);
699
+ await flush();
700
+ await flush();
701
+ expect(settled).toBeUndefined();
702
+
703
+ await advanceUntil(() => !!settled, ANDROID_LINK_DROP_TIMEOUT_MS);
704
+ await acquire;
705
+ expect(settled).toMatchObject({ errorCode: HardwareErrorCode.BleConnectedError });
706
+ });
707
+
708
+ test('a slow but successful MTU exchange completes inside the bound', async () => {
709
+ Object.assign(Platform, { OS: 'android' });
710
+ const { transport, device } = createHarness(() => Promise.resolve(device));
711
+ device.isConnected.mockResolvedValue(true);
712
+ const negotiated = { ...device, mtu: 247 };
713
+ Object.assign(device, {
714
+ mtu: 23,
715
+ // A cold cache makes the stack discover first; the exchange then completes late.
716
+ requestMTU: jest.fn(
717
+ () =>
718
+ new Promise(resolve => {
719
+ setTimeout(() => resolve(negotiated), 8000);
720
+ })
721
+ ),
722
+ });
723
+ const [, notifyCharacteristic] = await device.characteristicsForService();
724
+ Object.assign(notifyCharacteristic, { monitor: jest.fn(() => ({ remove: jest.fn() })) });
725
+ let result: unknown;
726
+ let failure: Error | undefined;
727
+ const acquire = transport.acquire({ uuid: UUID, expectedProtocol: 'V1' }).then(
728
+ value => {
729
+ result = value;
730
+ },
731
+ error => {
732
+ failure = error;
733
+ }
734
+ );
735
+ await flush();
736
+ await flush();
737
+ expect(device.discoverAllServicesAndCharacteristics).not.toHaveBeenCalled();
738
+
739
+ jest.advanceTimersByTime(8000);
740
+ await advanceUntil(() => result !== undefined || failure !== undefined, 5000);
741
+ await acquire;
742
+ expect(failure).toBeUndefined();
743
+ expect(result).toEqual({ uuid: UUID, protocolType: 'V1' });
744
+ expect(device.discoverAllServicesAndCharacteristics).toHaveBeenCalledTimes(1);
745
+ await transport.release(UUID, true);
746
+ });
747
+ });