@onekeyfe/hd-transport-react-native 1.2.0-alpha.64 → 1.2.0-alpha.65

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
@@ -64,6 +64,13 @@ declare function getProtocolV2BleTuning(): {
64
64
  * inside the native budget, so this only fires when the native timeout did not.
65
65
  */
66
66
  declare const BLE_CONNECT_TIMEOUT_MS: number;
67
+ /**
68
+ * Service discovery and characteristic resolution run after connect() succeeds, but
69
+ * CoreBluetooth schedules them on the same serial queue. If that queue is wedged by a
70
+ * device reboot, these calls can remain pending forever unless they have their own
71
+ * budget.
72
+ */
73
+ declare const BLE_GATT_SETUP_TIMEOUT_MS = 10000;
67
74
  /**
68
75
  * How many times a known device may fail its own protocol before we probe the others
69
76
  * again. Reconnect polling during a device reboot repeats this every few seconds, and
@@ -72,7 +79,7 @@ declare const BLE_CONNECT_TIMEOUT_MS: number;
72
79
  * change a device's protocol, so the shortcut has to expire rather than stick.
73
80
  */
74
81
  declare const PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = 3;
75
- /** Consecutive connect timeouts on one device before the BLE manager itself is recreated. */
82
+ /** BLE setup timeouts since the last successful setup before the manager is recreated. */
76
83
  declare const BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
77
84
  type IOneKeyDevice = OneKeyDeviceInfoBase & Device;
78
85
  declare class ReactNativeBleTransport {
@@ -99,8 +106,8 @@ declare class ReactNativeBleTransport {
99
106
  private probingProtocols;
100
107
  /** Consecutive write timeouts per device; reset by any write that completes. */
101
108
  private writeTimeoutCounts;
102
- /** Consecutive connect timeouts per device; reset by any connect that settles. */
103
- private connectTimeoutCounts;
109
+ /** BLE setup timeouts per device since the last complete characteristic resolution. */
110
+ private connectionSetupTimeoutCounts;
104
111
  private deviceProtocolHints;
105
112
  /** Protocol this device actually answered on, kept across reconnects of one session. */
106
113
  private sessionProtocols;
@@ -145,13 +152,14 @@ declare class ReactNativeBleTransport {
145
152
  cancel(): void;
146
153
  /** Run a native connect under the JS backstop budget. */
147
154
  private connectWithTimeout;
155
+ /** Resolve the complete GATT shape under a budget so acquire() always settles. */
156
+ private resolveCharacteristicsWithTimeout;
148
157
  /**
149
- * Give up on a connect the native layer never settled. The abandoned attempt still
150
- * holds a native "connecting" entry that would cancel the NEXT attempt out from under
151
- * itself, so it is cleared here fire and forget, because that call talks to the very
152
- * queue that just stopped responding.
158
+ * Give up on a BLE setup operation the native layer did not settle. The abandoned
159
+ * operation still owns native connection/GATT state that can poison the next attempt,
160
+ * so it is cleared here without awaiting the same queue that stopped responding.
153
161
  */
154
- private abandonStalledConnect;
162
+ private abandonStalledConnection;
155
163
  private getCachedTransport;
156
164
  /**
157
165
  * Write one packet under a bounded budget. A write that never settles means the
@@ -190,4 +198,4 @@ declare class ReactNativeBleTransport {
190
198
  getProtocolType(path: string): ProtocolType | undefined;
191
199
  }
192
200
 
193
- export { BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD, BLE_CONNECT_TIMEOUT_MS, BLE_WRITE_PACKET_TIMEOUT_MS, BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD, IOneKeyDevice, PROTOCOL_REPROBE_FALLBACK_ATTEMPTS, ProtocolV2BleTuning, configureProtocolV2BleTuning, ReactNativeBleTransport as default, getFirmwareUploadWriteRetryType, getProtocolV2BleTuning, resetProtocolV2BleTuning };
201
+ export { BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD, BLE_CONNECT_TIMEOUT_MS, BLE_GATT_SETUP_TIMEOUT_MS, 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
@@ -295,12 +295,14 @@ const fallbackConnectOptions = {
295
295
  timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
296
296
  };
297
297
  const BLE_CONNECT_TIMEOUT_MS = BLE_NATIVE_CONNECT_TIMEOUT_MS * 2 + 2000;
298
+ const BLE_GATT_SETUP_TIMEOUT_MS = 10000;
298
299
  const PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = 3;
299
300
  const BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
300
301
  const CONNECT_TIMEOUT_MESSAGE = 'BLE connect timeout after';
301
302
  const isConnectTimeoutError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleConnectedError &&
302
303
  typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' &&
303
304
  error.message.startsWith(CONNECT_TIMEOUT_MESSAGE);
305
+ const isNativeOperationTimeoutError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === reactNativeBlePlx.BleErrorCode.OperationTimedOut;
304
306
  const tryToGetConfiguration = (device) => {
305
307
  if (!device || !device.serviceUUIDs)
306
308
  return null;
@@ -360,7 +362,7 @@ class ReactNativeBleTransport {
360
362
  this.deviceProtocol = new Map();
361
363
  this.probingProtocols = new Map();
362
364
  this.writeTimeoutCounts = new Map();
363
- this.connectTimeoutCounts = new Map();
365
+ this.connectionSetupTimeoutCounts = new Map();
364
366
  this.deviceProtocolHints = new Map();
365
367
  this.sessionProtocols = new Map();
366
368
  this.protocolReprobeFailures = new Map();
@@ -567,7 +569,7 @@ class ReactNativeBleTransport {
567
569
  }
568
570
  }
569
571
  }
570
- const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristics(device);
572
+ const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, device);
571
573
  transport.device = device;
572
574
  transport.writeCharacteristic = writeCharacteristic;
573
575
  transport.notifyCharacteristic = notifyCharacteristic;
@@ -696,7 +698,7 @@ class ReactNativeBleTransport {
696
698
  }
697
699
  installTransportForAcquire(uuid, device, characteristics) {
698
700
  return __awaiter(this, void 0, void 0, function* () {
699
- const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristics(device));
701
+ const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristicsWithTimeout(uuid, device));
700
702
  const transport$1 = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
701
703
  if (reactNative.Platform.OS === 'android') {
702
704
  transport$1.mtuSize = typeof device.mtu === 'number' ? device.mtu : transport$1.mtuSize;
@@ -836,7 +838,7 @@ class ReactNativeBleTransport {
836
838
  }
837
839
  device = yield requestAndroidMtu(device);
838
840
  const acquiredDevice = device;
839
- const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristics(acquiredDevice);
841
+ const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, acquiredDevice);
840
842
  const protocolHint = expectedProtocol
841
843
  ? undefined
842
844
  : (_b = (_a = input.protocolHint) !== null && _a !== void 0 ? _a : this.deviceProtocolHints.get(uuid)) !== null && _b !== void 0 ? _b : inferProtocolHintFromDeviceName(getDeviceDisplayName(acquiredDevice));
@@ -1345,12 +1347,42 @@ class ReactNativeBleTransport {
1345
1347
  }, BLE_CONNECT_TIMEOUT_MS);
1346
1348
  }),
1347
1349
  ]);
1348
- this.connectTimeoutCounts.delete(uuid);
1349
1350
  return result;
1350
1351
  }
1351
1352
  catch (error) {
1352
- if (timedOut) {
1353
- this.abandonStalledConnect(uuid);
1353
+ if (timedOut || isNativeOperationTimeoutError(error)) {
1354
+ this.abandonStalledConnection(uuid, timedOut ? 'connect-backstop' : 'connect-native');
1355
+ }
1356
+ throw error;
1357
+ }
1358
+ finally {
1359
+ if (timer)
1360
+ clearTimeout(timer);
1361
+ }
1362
+ });
1363
+ }
1364
+ resolveCharacteristicsWithTimeout(uuid, device) {
1365
+ return __awaiter(this, void 0, void 0, function* () {
1366
+ let timer;
1367
+ let timedOut = false;
1368
+ const pending = this.resolveCharacteristics(device);
1369
+ pending.catch(() => undefined);
1370
+ try {
1371
+ const result = yield Promise.race([
1372
+ pending,
1373
+ new Promise((_, reject) => {
1374
+ timer = setTimeout(() => {
1375
+ timedOut = true;
1376
+ reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `BLE GATT setup timeout after ${BLE_GATT_SETUP_TIMEOUT_MS}ms for ${uuid}`));
1377
+ }, BLE_GATT_SETUP_TIMEOUT_MS);
1378
+ }),
1379
+ ]);
1380
+ this.connectionSetupTimeoutCounts.delete(uuid);
1381
+ return result;
1382
+ }
1383
+ catch (error) {
1384
+ if (timedOut || isNativeOperationTimeoutError(error)) {
1385
+ this.abandonStalledConnection(uuid, timedOut ? 'gatt-backstop' : 'gatt-native');
1354
1386
  }
1355
1387
  throw error;
1356
1388
  }
@@ -1360,12 +1392,13 @@ class ReactNativeBleTransport {
1360
1392
  }
1361
1393
  });
1362
1394
  }
1363
- abandonStalledConnect(uuid) {
1395
+ abandonStalledConnection(uuid, stage) {
1364
1396
  var _a, _b;
1365
- const timeouts = ((_a = this.connectTimeoutCounts.get(uuid)) !== null && _a !== void 0 ? _a : 0) + 1;
1366
- this.connectTimeoutCounts.set(uuid, timeouts);
1367
- Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE connect timed out:', uuid, {
1368
- consecutiveConnectTimeouts: timeouts,
1397
+ const timeouts = ((_a = this.connectionSetupTimeoutCounts.get(uuid)) !== null && _a !== void 0 ? _a : 0) + 1;
1398
+ this.connectionSetupTimeoutCounts.set(uuid, timeouts);
1399
+ Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE setup timed out:', uuid, {
1400
+ stage,
1401
+ setupTimeoutsSinceSuccess: timeouts,
1369
1402
  });
1370
1403
  (_b = this.blePlxManager) === null || _b === void 0 ? void 0 : _b.cancelDeviceConnection(uuid).catch(() => {
1371
1404
  });
@@ -1378,9 +1411,9 @@ class ReactNativeBleTransport {
1378
1411
  this.protocolV2Assemblers.delete(uuid);
1379
1412
  this.resetProtocolV2Frames(uuid);
1380
1413
  if (timeouts >= BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD) {
1381
- Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE connects wedged repeatedly, resetting BLE manager');
1414
+ Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE setup wedged repeatedly, resetting BLE manager');
1382
1415
  this.resetPlxManager();
1383
- this.connectTimeoutCounts.delete(uuid);
1416
+ this.connectionSetupTimeoutCounts.delete(uuid);
1384
1417
  }
1385
1418
  }
1386
1419
  getCachedTransport(uuid) {
@@ -1847,6 +1880,7 @@ class ReactNativeBleTransport {
1847
1880
 
1848
1881
  exports.BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD;
1849
1882
  exports.BLE_CONNECT_TIMEOUT_MS = BLE_CONNECT_TIMEOUT_MS;
1883
+ exports.BLE_GATT_SETUP_TIMEOUT_MS = BLE_GATT_SETUP_TIMEOUT_MS;
1850
1884
  exports.BLE_WRITE_PACKET_TIMEOUT_MS = BLE_WRITE_PACKET_TIMEOUT_MS;
1851
1885
  exports.BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD;
1852
1886
  exports.PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = PROTOCOL_REPROBE_FALLBACK_ATTEMPTS;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-react-native",
3
- "version": "1.2.0-alpha.64",
3
+ "version": "1.2.0-alpha.65",
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.0-alpha.64",
24
- "@onekeyfe/hd-shared": "1.2.0-alpha.64",
25
- "@onekeyfe/hd-transport": "1.2.0-alpha.64",
23
+ "@onekeyfe/hd-core": "1.2.0-alpha.65",
24
+ "@onekeyfe/hd-shared": "1.2.0-alpha.65",
25
+ "@onekeyfe/hd-transport": "1.2.0-alpha.65",
26
26
  "@onekeyfe/react-native-ble-utils": "^0.1.6",
27
27
  "react-native-ble-plx": "3.5.1"
28
28
  },
29
- "gitHead": "636cd3d9874820414cee6faa7e521f90a42ce6d5"
29
+ "gitHead": "2850b5b34935d4e97a1fe8a6459eb5a96a5cf0f6"
30
30
  }
@@ -1,10 +1,12 @@
1
1
  import { EventEmitter } from 'events';
2
+ import { BleErrorCode } from 'react-native-ble-plx';
2
3
 
3
4
  import { HardwareErrorCode } from '@onekeyfe/hd-shared';
4
5
 
5
6
  import ReactNativeBleTransport, {
6
7
  BLE_CONNECT_TIMEOUT_MS,
7
8
  BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD,
9
+ BLE_GATT_SETUP_TIMEOUT_MS,
8
10
  } from '../index';
9
11
 
10
12
  import messages from '@onekeyfe/hd-transport/messages.json';
@@ -31,6 +33,7 @@ jest.mock('react-native-ble-plx', () => ({
31
33
  OperationStartFailed: 601,
32
34
  DeviceMTUChangeFailed: 401,
33
35
  OperationCancelled: 2,
36
+ OperationTimedOut: 3,
34
37
  DeviceAlreadyConnected: 203,
35
38
  },
36
39
  BleManager: jest.fn(),
@@ -66,6 +69,14 @@ async function advanceUntil(settled: () => boolean, totalMs: number, stepMs = 25
66
69
  /** A device whose native connect() never settles — the observed iOS failure mode. */
67
70
  function createHarness(connectImpl: () => Promise<unknown>) {
68
71
  const connect = jest.fn(connectImpl);
72
+ const writeCharacteristic = {
73
+ uuid: '00000002-0000-1000-8000-00805f9b34fb',
74
+ isWritableWithResponse: true,
75
+ };
76
+ const notifyCharacteristic = {
77
+ uuid: '00000003-0000-1000-8000-00805f9b34fb',
78
+ isNotifiable: true,
79
+ };
69
80
  const device = {
70
81
  id: UUID,
71
82
  name: 'OneKey Classic',
@@ -74,6 +85,11 @@ function createHarness(connectImpl: () => Promise<unknown>) {
74
85
  isConnected: jest.fn(() => Promise.resolve(false)),
75
86
  cancelConnection: jest.fn(() => Promise.resolve()),
76
87
  connect,
88
+ discoverAllServicesAndCharacteristics: jest.fn(() => Promise.resolve()),
89
+ characteristicsForService: jest.fn(() =>
90
+ Promise.resolve([writeCharacteristic, notifyCharacteristic])
91
+ ),
92
+ services: jest.fn(() => Promise.resolve([])),
77
93
  onDisconnected: jest.fn(() => ({ remove: jest.fn() })),
78
94
  };
79
95
  const transport = new ReactNativeBleTransport({ scanTimeout: 1 });
@@ -189,4 +205,78 @@ describe('BLE connect timeout', () => {
189
205
  expect(destroy).toHaveBeenCalledTimes(1);
190
206
  expect((transport as unknown as { blePlxManager?: unknown }).blePlxManager).toBeUndefined();
191
207
  });
208
+
209
+ test('native connect timeouts contribute to the same manager reset budget', async () => {
210
+ const { transport, bleManager } = createHarness(() => Promise.resolve());
211
+ const destroy = jest.fn();
212
+ (bleManager as unknown as { destroy: jest.Mock }).destroy = destroy;
213
+ const nativeTimeout = Object.assign(new Error('Operation timed out'), {
214
+ errorCode: BleErrorCode.OperationTimedOut,
215
+ });
216
+
217
+ for (let attempt = 0; attempt < BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD; attempt += 1) {
218
+ // eslint-disable-next-line no-await-in-loop
219
+ await expect(
220
+ (transport as any).connectWithTimeout(UUID, () => Promise.reject(nativeTimeout))
221
+ ).rejects.toBe(nativeTimeout);
222
+ }
223
+
224
+ expect(destroy).toHaveBeenCalledTimes(1);
225
+ });
226
+
227
+ test('GATT discovery is bounded and abandons the native connection', async () => {
228
+ const { transport, device, bleManager } = createHarness(() => Promise.resolve());
229
+ device.discoverAllServicesAndCharacteristics.mockImplementation(
230
+ () =>
231
+ new Promise(() => {
232
+ // never settles
233
+ })
234
+ );
235
+
236
+ const errors: Array<{ errorCode?: unknown }> = [];
237
+ let settled = false;
238
+ (transport as any).resolveCharacteristicsWithTimeout(UUID, device).catch((error: unknown) => {
239
+ errors.push(error as { errorCode?: unknown });
240
+ settled = true;
241
+ });
242
+ await flush();
243
+ await advanceUntil(() => settled, BLE_GATT_SETUP_TIMEOUT_MS + 5000);
244
+
245
+ expect(errors).toHaveLength(1);
246
+ expect(errors[0]?.errorCode).toBe(HardwareErrorCode.BleConnectedError);
247
+ expect(bleManager.cancelDeviceConnection).toHaveBeenCalledWith(UUID);
248
+ });
249
+
250
+ test('a successful GATT retry clears the timeout budget before an abandoned call settles', async () => {
251
+ const { transport, device, bleManager } = createHarness(() => Promise.resolve());
252
+ let resolveAbandonedDiscovery: (() => void) | undefined;
253
+ device.discoverAllServicesAndCharacteristics
254
+ .mockImplementationOnce(
255
+ () =>
256
+ new Promise<void>(resolve => {
257
+ resolveAbandonedDiscovery = resolve;
258
+ })
259
+ )
260
+ .mockResolvedValueOnce(undefined);
261
+
262
+ let firstSettled = false;
263
+ (transport as any).resolveCharacteristicsWithTimeout(UUID, device).catch(() => {
264
+ firstSettled = true;
265
+ });
266
+ await flush();
267
+ await advanceUntil(() => firstSettled, BLE_GATT_SETUP_TIMEOUT_MS + 5000);
268
+
269
+ await expect(
270
+ (transport as any).resolveCharacteristicsWithTimeout(UUID, device)
271
+ ).resolves.toMatchObject({
272
+ writeCharacteristic: expect.any(Object),
273
+ notifyCharacteristic: expect.any(Object),
274
+ });
275
+
276
+ resolveAbandonedDiscovery?.();
277
+ await flush();
278
+
279
+ expect(bleManager.cancelDeviceConnection).toHaveBeenCalledTimes(1);
280
+ expect((transport as any).connectionSetupTimeoutCounts.has(UUID)).toBe(false);
281
+ });
192
282
  });
package/src/index.ts CHANGED
@@ -199,6 +199,13 @@ const fallbackConnectOptions: Record<string, unknown> = {
199
199
  * inside the native budget, so this only fires when the native timeout did not.
200
200
  */
201
201
  export const BLE_CONNECT_TIMEOUT_MS = BLE_NATIVE_CONNECT_TIMEOUT_MS * 2 + 2000;
202
+ /**
203
+ * Service discovery and characteristic resolution run after connect() succeeds, but
204
+ * CoreBluetooth schedules them on the same serial queue. If that queue is wedged by a
205
+ * device reboot, these calls can remain pending forever unless they have their own
206
+ * budget.
207
+ */
208
+ export const BLE_GATT_SETUP_TIMEOUT_MS = 10_000;
202
209
  /**
203
210
  * How many times a known device may fail its own protocol before we probe the others
204
211
  * again. Reconnect polling during a device reboot repeats this every few seconds, and
@@ -207,13 +214,15 @@ export const BLE_CONNECT_TIMEOUT_MS = BLE_NATIVE_CONNECT_TIMEOUT_MS * 2 + 2000;
207
214
  * change a device's protocol, so the shortcut has to expire rather than stick.
208
215
  */
209
216
  export const PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = 3;
210
- /** Consecutive connect timeouts on one device before the BLE manager itself is recreated. */
217
+ /** BLE setup timeouts since the last successful setup before the manager is recreated. */
211
218
  export const BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
212
219
  const CONNECT_TIMEOUT_MESSAGE = 'BLE connect timeout after';
213
220
  const isConnectTimeoutError = (error: unknown): boolean =>
214
221
  (error as { errorCode?: unknown })?.errorCode === HardwareErrorCode.BleConnectedError &&
215
222
  typeof (error as { message?: unknown })?.message === 'string' &&
216
223
  (error as { message: string }).message.startsWith(CONNECT_TIMEOUT_MESSAGE);
224
+ const isNativeOperationTimeoutError = (error: unknown): boolean =>
225
+ (error as { errorCode?: unknown })?.errorCode === BleErrorCode.OperationTimedOut;
217
226
 
218
227
  export type IOneKeyDevice = OneKeyDeviceInfoBase & Device;
219
228
 
@@ -314,8 +323,8 @@ export default class ReactNativeBleTransport {
314
323
  /** Consecutive write timeouts per device; reset by any write that completes. */
315
324
  private writeTimeoutCounts: Map<string, number> = new Map();
316
325
 
317
- /** Consecutive connect timeouts per device; reset by any connect that settles. */
318
- private connectTimeoutCounts: Map<string, number> = new Map();
326
+ /** BLE setup timeouts per device since the last complete characteristic resolution. */
327
+ private connectionSetupTimeoutCounts: Map<string, number> = new Map();
319
328
 
320
329
  private deviceProtocolHints: Map<string, ProtocolType> = new Map();
321
330
 
@@ -555,9 +564,8 @@ export default class ReactNativeBleTransport {
555
564
  }
556
565
  }
557
566
 
558
- const { writeCharacteristic, notifyCharacteristic } = await this.resolveCharacteristics(
559
- device
560
- );
567
+ const { writeCharacteristic, notifyCharacteristic } =
568
+ await this.resolveCharacteristicsWithTimeout(uuid, device);
561
569
 
562
570
  transport.device = device;
563
571
  transport.writeCharacteristic = writeCharacteristic;
@@ -722,7 +730,7 @@ export default class ReactNativeBleTransport {
722
730
  characteristics?: ResolvedBleCharacteristics
723
731
  ) {
724
732
  const { writeCharacteristic, notifyCharacteristic } =
725
- characteristics ?? (await this.resolveCharacteristics(device));
733
+ characteristics ?? (await this.resolveCharacteristicsWithTimeout(uuid, device));
726
734
  const transport = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
727
735
  if (Platform.OS === 'android') {
728
736
  transport.mtuSize = typeof device.mtu === 'number' ? device.mtu : transport.mtuSize;
@@ -897,9 +905,8 @@ export default class ReactNativeBleTransport {
897
905
 
898
906
  device = await requestAndroidMtu(device);
899
907
  const acquiredDevice = device;
900
- const { writeCharacteristic, notifyCharacteristic } = await this.resolveCharacteristics(
901
- acquiredDevice
902
- );
908
+ const { writeCharacteristic, notifyCharacteristic } =
909
+ await this.resolveCharacteristicsWithTimeout(uuid, acquiredDevice);
903
910
 
904
911
  const protocolHint = expectedProtocol
905
912
  ? undefined
@@ -1546,11 +1553,46 @@ export default class ReactNativeBleTransport {
1546
1553
  }, BLE_CONNECT_TIMEOUT_MS);
1547
1554
  }),
1548
1555
  ]);
1549
- this.connectTimeoutCounts.delete(uuid);
1550
1556
  return result;
1551
1557
  } catch (error) {
1552
- if (timedOut) {
1553
- this.abandonStalledConnect(uuid);
1558
+ if (timedOut || isNativeOperationTimeoutError(error)) {
1559
+ this.abandonStalledConnection(uuid, timedOut ? 'connect-backstop' : 'connect-native');
1560
+ }
1561
+ throw error;
1562
+ } finally {
1563
+ if (timer) clearTimeout(timer);
1564
+ }
1565
+ }
1566
+
1567
+ /** Resolve the complete GATT shape under a budget so acquire() always settles. */
1568
+ private async resolveCharacteristicsWithTimeout(
1569
+ uuid: string,
1570
+ device: Device
1571
+ ): Promise<ResolvedBleCharacteristics> {
1572
+ let timer: ReturnType<typeof setTimeout> | undefined;
1573
+ let timedOut = false;
1574
+ const pending = this.resolveCharacteristics(device);
1575
+ pending.catch(() => undefined);
1576
+ try {
1577
+ const result = await Promise.race([
1578
+ pending,
1579
+ new Promise<never>((_, reject) => {
1580
+ timer = setTimeout(() => {
1581
+ timedOut = true;
1582
+ reject(
1583
+ ERRORS.TypedError(
1584
+ HardwareErrorCode.BleConnectedError,
1585
+ `BLE GATT setup timeout after ${BLE_GATT_SETUP_TIMEOUT_MS}ms for ${uuid}`
1586
+ )
1587
+ );
1588
+ }, BLE_GATT_SETUP_TIMEOUT_MS);
1589
+ }),
1590
+ ]);
1591
+ this.connectionSetupTimeoutCounts.delete(uuid);
1592
+ return result;
1593
+ } catch (error) {
1594
+ if (timedOut || isNativeOperationTimeoutError(error)) {
1595
+ this.abandonStalledConnection(uuid, timedOut ? 'gatt-backstop' : 'gatt-native');
1554
1596
  }
1555
1597
  throw error;
1556
1598
  } finally {
@@ -1559,16 +1601,19 @@ export default class ReactNativeBleTransport {
1559
1601
  }
1560
1602
 
1561
1603
  /**
1562
- * Give up on a connect the native layer never settled. The abandoned attempt still
1563
- * holds a native "connecting" entry that would cancel the NEXT attempt out from under
1564
- * itself, so it is cleared here fire and forget, because that call talks to the very
1565
- * queue that just stopped responding.
1604
+ * Give up on a BLE setup operation the native layer did not settle. The abandoned
1605
+ * operation still owns native connection/GATT state that can poison the next attempt,
1606
+ * so it is cleared here without awaiting the same queue that stopped responding.
1566
1607
  */
1567
- private abandonStalledConnect(uuid: string) {
1568
- const timeouts = (this.connectTimeoutCounts.get(uuid) ?? 0) + 1;
1569
- this.connectTimeoutCounts.set(uuid, timeouts);
1570
- Log?.error('[ReactNativeBleTransport] BLE connect timed out:', uuid, {
1571
- consecutiveConnectTimeouts: timeouts,
1608
+ private abandonStalledConnection(
1609
+ uuid: string,
1610
+ stage: 'connect-backstop' | 'connect-native' | 'gatt-backstop' | 'gatt-native'
1611
+ ) {
1612
+ const timeouts = (this.connectionSetupTimeoutCounts.get(uuid) ?? 0) + 1;
1613
+ this.connectionSetupTimeoutCounts.set(uuid, timeouts);
1614
+ Log?.error('[ReactNativeBleTransport] BLE setup timed out:', uuid, {
1615
+ stage,
1616
+ setupTimeoutsSinceSuccess: timeouts,
1572
1617
  });
1573
1618
 
1574
1619
  this.blePlxManager?.cancelDeviceConnection(uuid).catch(() => {
@@ -1586,9 +1631,9 @@ export default class ReactNativeBleTransport {
1586
1631
  if (timeouts >= BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD) {
1587
1632
  // BleManager.destroy() force-rejects every promise the native queue abandoned —
1588
1633
  // the only JS-reachable way to settle them — and drops all cached peripherals.
1589
- Log?.error('[ReactNativeBleTransport] BLE connects wedged repeatedly, resetting BLE manager');
1634
+ Log?.error('[ReactNativeBleTransport] BLE setup wedged repeatedly, resetting BLE manager');
1590
1635
  this.resetPlxManager();
1591
- this.connectTimeoutCounts.delete(uuid);
1636
+ this.connectionSetupTimeoutCounts.delete(uuid);
1592
1637
  }
1593
1638
  }
1594
1639