@onekeyfe/hd-transport-react-native 1.2.3 → 1.2.4-alpha.0

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
@@ -165,6 +165,13 @@ declare class ReactNativeBleTransport {
165
165
  private protocolReprobeFailures;
166
166
  /** Endpoints whose last detection got no answer; 'woken' once their Initialize wake is spent. */
167
167
  private silentDetections;
168
+ /**
169
+ * iOS can report a reset Pro2 as a generic peripheral disconnect instead of
170
+ * CBErrorPeerRemovedPairingInformation. Keep that first probe failure across
171
+ * the automatic cold reconnect and require a second failed probe before
172
+ * classifying the OS bond as invalid.
173
+ */
174
+ private iosProbeDisconnectEvidence;
168
175
  /** Android endpoints whose cached GATT table is suspect; the next connect refreshes it. */
169
176
  private androidGattCacheRefreshes;
170
177
  /**
@@ -279,6 +286,9 @@ declare class ReactNativeBleTransport {
279
286
  private resetPlxManager;
280
287
  private createProtocolMismatchError;
281
288
  private createProtocolDetectionError;
289
+ private rememberIosProbeDisconnect;
290
+ private getIosProbeDisconnectEvidence;
291
+ private confirmIosProbeDisconnect;
282
292
  private clearProbeProtocol;
283
293
  /** Protocol to route a call with: confirmed if known, otherwise the one being probed. */
284
294
  private getActiveProtocol;
@@ -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
@@ -661,6 +661,33 @@ const isAndroidLinkLossError = (error) => {
661
661
  code === hdShared.HardwareErrorCode.BleCharacteristicNotifyError ||
662
662
  code === hdShared.HardwareErrorCode.BleConnectedError);
663
663
  };
664
+ const logIosBleBondDiagnostic = (phase, error, outcome) => {
665
+ if (reactNative.Platform.OS !== 'ios')
666
+ return;
667
+ const nativeError = (error !== null && error !== void 0 ? error : {});
668
+ const scalar = (value) => typeof value === 'string' || typeof value === 'number' ? value : undefined;
669
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport][BleBondDiagnostic] probe error', {
670
+ phase,
671
+ outcome,
672
+ errorName: scalar(nativeError.name),
673
+ errorCode: scalar(nativeError.errorCode),
674
+ attErrorCode: scalar(nativeError.attErrorCode),
675
+ iosErrorCode: scalar(nativeError.iosErrorCode),
676
+ androidErrorCode: scalar(nativeError.androidErrorCode),
677
+ nativeStaleBond: isNativeBleStaleBondError(error),
678
+ hardwareStaleBond: hdShared.isBleStaleBondHardwareError(error),
679
+ nativeDisconnect: isNativeBleDisconnectError(error),
680
+ });
681
+ };
682
+ const IOS_PROBE_DISCONNECT_EVIDENCE_TTL_MS = 5 * 60000;
683
+ const isIosProbeDisconnectError = (error) => {
684
+ if (reactNative.Platform.OS !== 'ios')
685
+ return false;
686
+ const code = error === null || error === void 0 ? void 0 : error.errorCode;
687
+ return (isNativeBleDisconnectError(error) ||
688
+ code === hdShared.HardwareErrorCode.BleDeviceNotBonded ||
689
+ code === hdShared.HardwareErrorCode.BleDeviceDisconnected);
690
+ };
664
691
  const BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
665
692
  const DEVICE_SCAN_TIMEOUT_MS = 3000;
666
693
  const IOS_NOTIFY_READY_DELAY_MS = 150;
@@ -856,6 +883,7 @@ class ReactNativeBleTransport {
856
883
  this.confirmedProtocolV2 = new Set();
857
884
  this.protocolReprobeFailures = new Map();
858
885
  this.silentDetections = new Map();
886
+ this.iosProbeDisconnectEvidence = new Map();
859
887
  this.androidGattCacheRefreshes = new Set();
860
888
  this.staleBondErrors = new Map();
861
889
  this.androidLinkStartedAt = new Map();
@@ -2090,6 +2118,7 @@ class ReactNativeBleTransport {
2090
2118
  this.androidLinkStartedAt.clear();
2091
2119
  stopBleKeyMissingTracking();
2092
2120
  stopBleEncryptionTracking();
2121
+ this.iosProbeDisconnectEvidence.clear();
2093
2122
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
2094
2123
  (_a = this.runPromise) === null || _a === void 0 ? void 0 : _a.reject(error);
2095
2124
  this.runPromise = null;
@@ -2575,6 +2604,50 @@ class ReactNativeBleTransport {
2575
2604
  createProtocolDetectionError() {
2576
2605
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1 GetFeatures or Protocol V2 Ping');
2577
2606
  }
2607
+ rememberIosProbeDisconnect(uuid, phase, error) {
2608
+ if (!isIosProbeDisconnectError(error))
2609
+ return undefined;
2610
+ const previous = this.getIosProbeDisconnectEvidence(uuid);
2611
+ const errorCode = error === null || error === void 0 ? void 0 : error.errorCode;
2612
+ const evidence = Object.assign(Object.assign({ phase }, (typeof errorCode === 'string' || typeof errorCode === 'number' ? { errorCode } : {})), { recordedAt: Date.now() });
2613
+ this.iosProbeDisconnectEvidence.set(uuid, evidence);
2614
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport][BleBondDiagnostic] probe disconnect recorded', {
2615
+ phase,
2616
+ errorCode: evidence.errorCode,
2617
+ repeated: Boolean(previous),
2618
+ });
2619
+ if (!previous)
2620
+ return undefined;
2621
+ return this.confirmIosProbeDisconnect(uuid, 'repeated-probe-disconnect');
2622
+ }
2623
+ getIosProbeDisconnectEvidence(uuid) {
2624
+ const evidence = this.iosProbeDisconnectEvidence.get(uuid);
2625
+ if (!evidence)
2626
+ return undefined;
2627
+ const ageMs = Date.now() - evidence.recordedAt;
2628
+ if (ageMs <= IOS_PROBE_DISCONNECT_EVIDENCE_TTL_MS)
2629
+ return evidence;
2630
+ this.iosProbeDisconnectEvidence.delete(uuid);
2631
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport][BleBondDiagnostic] probe disconnect expired', {
2632
+ triggerPhase: evidence.phase,
2633
+ triggerErrorCode: evidence.errorCode,
2634
+ ageMs,
2635
+ });
2636
+ return undefined;
2637
+ }
2638
+ confirmIosProbeDisconnect(uuid, confirmation) {
2639
+ const evidence = this.getIosProbeDisconnectEvidence(uuid);
2640
+ if (!evidence)
2641
+ return undefined;
2642
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport][BleBondDiagnostic] stale bond confirmed', {
2643
+ confirmation,
2644
+ triggerPhase: evidence.phase,
2645
+ triggerErrorCode: evidence.errorCode,
2646
+ evidenceAgeMs: Date.now() - evidence.recordedAt,
2647
+ finalErrorCode: hdShared.HardwareErrorCode.BleBondInvalid,
2648
+ });
2649
+ return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleBondInvalid);
2650
+ }
2578
2651
  clearProbeProtocol(uuid, protocol) {
2579
2652
  if (this.probingProtocols.get(uuid) === protocol) {
2580
2653
  this.probingProtocols.delete(uuid);
@@ -2588,9 +2661,10 @@ class ReactNativeBleTransport {
2588
2661
  return (_a = this.deviceProtocol.get(uuid)) !== null && _a !== void 0 ? _a : this.probingProtocols.get(uuid);
2589
2662
  }
2590
2663
  detectProtocol(uuid, expectedProtocol, protocolHint, rebuildTransport) {
2591
- var _a;
2664
+ var _a, _b;
2592
2665
  return __awaiter(this, void 0, void 0, function* () {
2593
2666
  if (expectedProtocol === 'V1') {
2667
+ this.iosProbeDisconnectEvidence.delete(uuid);
2594
2668
  this.deviceProtocol.set(uuid, 'V1');
2595
2669
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol selected', {
2596
2670
  deviceId: uuid,
@@ -2604,6 +2678,7 @@ class ReactNativeBleTransport {
2604
2678
  }
2605
2679
  if (expectedProtocol === 'V2') {
2606
2680
  if (yield this.probeProtocolV2(uuid)) {
2681
+ this.iosProbeDisconnectEvidence.delete(uuid);
2607
2682
  this.deviceProtocol.set(uuid, 'V2');
2608
2683
  this.sessionProtocols.set(uuid, 'V2');
2609
2684
  this.confirmedProtocolV2.add(uuid);
@@ -2614,6 +2689,19 @@ class ReactNativeBleTransport {
2614
2689
  });
2615
2690
  return 'V2';
2616
2691
  }
2692
+ const staleBondError = this.confirmIosProbeDisconnect(uuid, 'follow-up-no-response');
2693
+ if (reactNative.Platform.OS === 'ios') {
2694
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport][BleBondDiagnostic] protocol detection ended', {
2695
+ outcome: 'expected-v2-no-response',
2696
+ expectedProtocol,
2697
+ protocolHint: protocolHint !== null && protocolHint !== void 0 ? protocolHint : 'none',
2698
+ finalErrorCode: staleBondError
2699
+ ? hdShared.HardwareErrorCode.BleBondInvalid
2700
+ : hdShared.HardwareErrorCode.RuntimeError,
2701
+ });
2702
+ }
2703
+ if (staleBondError)
2704
+ throw staleBondError;
2617
2705
  throw this.createProtocolMismatchError(expectedProtocol);
2618
2706
  }
2619
2707
  const sessionProtocol = this.sessionProtocols.get(uuid);
@@ -2637,6 +2725,7 @@ class ReactNativeBleTransport {
2637
2725
  }
2638
2726
  const detected = protocol === 'V1' ? yield this.probeProtocolV1(uuid) : yield this.probeProtocolV2(uuid);
2639
2727
  if (detected) {
2728
+ this.iosProbeDisconnectEvidence.delete(uuid);
2640
2729
  this.deviceProtocol.set(uuid, protocol);
2641
2730
  this.sessionProtocols.set(uuid, protocol);
2642
2731
  if (protocol === 'V2') {
@@ -2662,6 +2751,26 @@ class ReactNativeBleTransport {
2662
2751
  }
2663
2752
  this.deviceProtocol.delete(uuid);
2664
2753
  this.probingProtocols.delete(uuid);
2754
+ const staleBondError = this.confirmIosProbeDisconnect(uuid, 'follow-up-no-response');
2755
+ if (reactNative.Platform.OS === 'ios') {
2756
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport][BleBondDiagnostic] protocol detection ended', {
2757
+ outcome: 'all-probes-no-response',
2758
+ expectedProtocol: expectedProtocol !== null && expectedProtocol !== void 0 ? expectedProtocol : 'none',
2759
+ protocolHint: protocolHint !== null && protocolHint !== void 0 ? protocolHint : 'none',
2760
+ sessionProtocol: sessionProtocol !== null && sessionProtocol !== void 0 ? sessionProtocol : 'none',
2761
+ probeOrder: probeOrder.join(','),
2762
+ trustSessionProtocol,
2763
+ reprobeFailures,
2764
+ transportCached: Boolean(transportCache[uuid]),
2765
+ notifyActive: Boolean((_b = transportCache[uuid]) === null || _b === void 0 ? void 0 : _b.notifySubscription),
2766
+ staleBondRecorded: this.iosProbeDisconnectEvidence.has(uuid),
2767
+ finalErrorCode: staleBondError
2768
+ ? hdShared.HardwareErrorCode.BleBondInvalid
2769
+ : hdShared.HardwareErrorCode.BleTimeoutError,
2770
+ });
2771
+ }
2772
+ if (staleBondError)
2773
+ throw staleBondError;
2665
2774
  throw this.createProtocolDetectionError();
2666
2775
  });
2667
2776
  }
@@ -2745,7 +2854,12 @@ class ReactNativeBleTransport {
2745
2854
  catch (error) {
2746
2855
  this.clearProbeProtocol(uuid, 'V1');
2747
2856
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe failed:', error);
2748
- if (shouldRethrowProtocolProbeError(error)) {
2857
+ const shouldRethrow = shouldRethrowProtocolProbeError(error);
2858
+ const staleBondError = this.rememberIosProbeDisconnect(uuid, 'protocol-v1-get-features', error);
2859
+ logIosBleBondDiagnostic('protocol-v1-get-features', error, shouldRethrow ? 'rethrow' : 'fallback');
2860
+ if (staleBondError)
2861
+ throw staleBondError;
2862
+ if (shouldRethrow) {
2749
2863
  throw error;
2750
2864
  }
2751
2865
  return false;
@@ -2761,18 +2875,28 @@ class ReactNativeBleTransport {
2761
2875
  this.probingProtocols.set(uuid, 'V2');
2762
2876
  (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
2763
2877
  this.throwIfStaleBondError(uuid);
2764
- const detected = yield transport.probeProtocolV2({
2765
- call: (name, data, options) => this.callProtocolV2(uuid, name, data, options),
2766
- timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT_MS,
2767
- logger: Log,
2768
- logPrefix: 'ProtocolV2 RN-BLE',
2769
- onProbeFailed: () => {
2770
- var _a;
2771
- (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
2772
- this.resetProtocolV2Frames(uuid);
2773
- },
2774
- shouldRethrow: shouldRethrowProtocolProbeError,
2775
- });
2878
+ let detected;
2879
+ try {
2880
+ detected = yield transport.probeProtocolV2({
2881
+ call: (name, data, options) => this.callProtocolV2(uuid, name, data, options),
2882
+ timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT_MS,
2883
+ logger: Log,
2884
+ logPrefix: 'ProtocolV2 RN-BLE',
2885
+ onProbeFailed: () => {
2886
+ var _a;
2887
+ (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
2888
+ this.resetProtocolV2Frames(uuid);
2889
+ },
2890
+ shouldRethrow: shouldRethrowProtocolProbeError,
2891
+ });
2892
+ }
2893
+ catch (error) {
2894
+ const staleBondError = this.rememberIosProbeDisconnect(uuid, 'protocol-v2-ping', error);
2895
+ logIosBleBondDiagnostic('protocol-v2-ping', error, 'rethrow');
2896
+ if (staleBondError)
2897
+ throw staleBondError;
2898
+ throw error;
2899
+ }
2776
2900
  if (!detected) {
2777
2901
  this.clearProbeProtocol(uuid, 'V2');
2778
2902
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-react-native",
3
- "version": "1.2.3",
3
+ "version": "1.2.4-alpha.0",
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.3",
24
- "@onekeyfe/hd-shared": "1.2.3",
25
- "@onekeyfe/hd-transport": "1.2.3",
23
+ "@onekeyfe/hd-core": "1.2.4-alpha.0",
24
+ "@onekeyfe/hd-shared": "1.2.4-alpha.0",
25
+ "@onekeyfe/hd-transport": "1.2.4-alpha.0",
26
26
  "@onekeyfe/react-native-ble-utils": "0.1.9",
27
27
  "react-native-ble-plx": "3.5.1"
28
28
  },
29
- "gitHead": "de8e0beb9d993464fbcac6231d7efe501d789c7d"
29
+ "gitHead": "4996c67dbb5a3ac85c2559b164d9a931274890a7"
30
30
  }
@@ -1,3 +1,6 @@
1
+ import { Platform } from 'react-native';
2
+ import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
3
+
1
4
  import ReactNativeBleTransport, { PROTOCOL_REPROBE_FALLBACK_ATTEMPTS } from '../index';
2
5
  import protocolV1Schema from './protocolV1SchemaFixture';
3
6
 
@@ -89,7 +92,9 @@ describe('protocol re-probe after a known device goes away', () => {
89
92
  test('an unknown device still probes both protocols', async () => {
90
93
  const { transport, probeV1, probeV2 } = createHarness({ v1: false, v2: false });
91
94
 
92
- await expect(detect(transport)).rejects.toBeDefined();
95
+ await expect(detect(transport)).rejects.toMatchObject({
96
+ errorCode: HardwareErrorCode.BleTimeoutError,
97
+ });
93
98
 
94
99
  expect(probeV1).toHaveBeenCalledTimes(1);
95
100
  expect(probeV2).toHaveBeenCalledTimes(1);
@@ -154,3 +159,113 @@ describe('protocol re-probe after a known device goes away', () => {
154
159
  expect((known.transport as any).protocolReprobeFailures.get(UUID)).toBeUndefined();
155
160
  });
156
161
  });
162
+
163
+ describe('iOS reset-device stale bond confirmation', () => {
164
+ afterEach(() => {
165
+ Object.assign(Platform, { OS: 'ios' });
166
+ });
167
+
168
+ test('promotes a probe disconnect followed by a silent reconnect to BleBondInvalid', async () => {
169
+ const transport = new ReactNativeBleTransport({});
170
+ transport.configure(protocolV1Schema);
171
+ (transport as any).callProtocolV1 = jest
172
+ .fn()
173
+ .mockRejectedValue(ERRORS.TypedError(HardwareErrorCode.BleDeviceNotBonded));
174
+
175
+ await expect(detect(transport)).rejects.toMatchObject({
176
+ errorCode: HardwareErrorCode.BleDeviceNotBonded,
177
+ });
178
+
179
+ (transport as any).probeProtocolV1 = jest.fn(() => Promise.resolve(false));
180
+ (transport as any).probeProtocolV2 = jest.fn(() => Promise.resolve(false));
181
+ (transport as any).resetProbeStateAfterProtocolProbe = jest.fn(() => Promise.resolve());
182
+
183
+ await expect(detect(transport)).rejects.toMatchObject({
184
+ errorCode: HardwareErrorCode.BleBondInvalid,
185
+ });
186
+ await expect(detect(transport)).rejects.toMatchObject({
187
+ errorCode: HardwareErrorCode.BleBondInvalid,
188
+ });
189
+ expect((transport as any).iosProbeDisconnectEvidence.has(UUID)).toBe(true);
190
+ });
191
+
192
+ test('promotes a repeated iOS probe disconnect without waiting for probe timeouts', () => {
193
+ const { transport } = createHarness({ v1: false, v2: false });
194
+ const firstError = Object.assign(new Error('The specified device has disconnected from us'), {
195
+ errorCode: 201,
196
+ iosErrorCode: 7,
197
+ });
198
+ const secondError = ERRORS.TypedError(HardwareErrorCode.BleDeviceDisconnected);
199
+
200
+ expect(
201
+ (transport as any).rememberIosProbeDisconnect(UUID, 'protocol-v1-get-features', firstError)
202
+ ).toBeUndefined();
203
+ expect(
204
+ (transport as any).rememberIosProbeDisconnect(UUID, 'protocol-v2-ping', secondError)
205
+ ).toMatchObject({ errorCode: HardwareErrorCode.BleBondInvalid });
206
+ expect((transport as any).iosProbeDisconnectEvidence.has(UUID)).toBe(true);
207
+ });
208
+
209
+ test('clears the disconnect evidence after a valid protocol response', async () => {
210
+ const { transport } = createHarness({ v1: true, v2: false });
211
+ (transport as any).rememberIosProbeDisconnect(
212
+ UUID,
213
+ 'protocol-v1-get-features',
214
+ ERRORS.TypedError(HardwareErrorCode.BleDeviceNotBonded)
215
+ );
216
+
217
+ await expect(detect(transport)).resolves.toBe('V1');
218
+ expect((transport as any).iosProbeDisconnectEvidence.has(UUID)).toBe(false);
219
+ });
220
+
221
+ test('preserves evidence across disconnect and stalled setup cleanup', async () => {
222
+ const { transport } = createHarness({ v1: false, v2: false });
223
+ (transport as any).rememberIosProbeDisconnect(
224
+ UUID,
225
+ 'protocol-v1-get-features',
226
+ ERRORS.TypedError(HardwareErrorCode.BleDeviceNotBonded)
227
+ );
228
+
229
+ await (transport as any).disconnectUnlocked(UUID);
230
+ expect((transport as any).iosProbeDisconnectEvidence.has(UUID)).toBe(true);
231
+
232
+ (transport as any).abandonStalledConnection(UUID, 'connect-native');
233
+ expect((transport as any).iosProbeDisconnectEvidence.has(UUID)).toBe(true);
234
+
235
+ await expect(detect(transport)).rejects.toMatchObject({
236
+ errorCode: HardwareErrorCode.BleBondInvalid,
237
+ });
238
+ });
239
+
240
+ test('expires old disconnect evidence before classifying a later timeout', async () => {
241
+ const { transport } = createHarness({ v1: false, v2: false });
242
+ (transport as any).rememberIosProbeDisconnect(
243
+ UUID,
244
+ 'protocol-v1-get-features',
245
+ ERRORS.TypedError(HardwareErrorCode.BleDeviceNotBonded)
246
+ );
247
+ const evidence = (transport as any).iosProbeDisconnectEvidence.get(UUID);
248
+ evidence.recordedAt = Date.now() - 5 * 60_000 - 1;
249
+
250
+ await expect(detect(transport)).rejects.toMatchObject({
251
+ errorCode: HardwareErrorCode.BleTimeoutError,
252
+ });
253
+ expect((transport as any).iosProbeDisconnectEvidence.has(UUID)).toBe(false);
254
+ });
255
+
256
+ test('does not classify the same sequence as a stale bond on Android', async () => {
257
+ Object.assign(Platform, { OS: 'android' });
258
+ const { transport } = createHarness({ v1: false, v2: false });
259
+
260
+ expect(
261
+ (transport as any).rememberIosProbeDisconnect(
262
+ UUID,
263
+ 'protocol-v1-get-features',
264
+ ERRORS.TypedError(HardwareErrorCode.BleDeviceNotBonded)
265
+ )
266
+ ).toBeUndefined();
267
+ await expect(detect(transport)).rejects.toMatchObject({
268
+ errorCode: HardwareErrorCode.BleTimeoutError,
269
+ });
270
+ });
271
+ });
package/src/index.ts CHANGED
@@ -233,6 +233,55 @@ const isAndroidLinkLossError = (error: unknown): boolean => {
233
233
  code === HardwareErrorCode.BleConnectedError
234
234
  );
235
235
  };
236
+
237
+ const logIosBleBondDiagnostic = (
238
+ phase: string,
239
+ error: unknown,
240
+ outcome: 'fallback' | 'rethrow'
241
+ ) => {
242
+ if (Platform.OS !== 'ios') return;
243
+
244
+ const nativeError = (error ?? {}) as {
245
+ name?: unknown;
246
+ errorCode?: unknown;
247
+ attErrorCode?: unknown;
248
+ iosErrorCode?: unknown;
249
+ androidErrorCode?: unknown;
250
+ };
251
+ const scalar = (value: unknown) =>
252
+ typeof value === 'string' || typeof value === 'number' ? value : undefined;
253
+
254
+ Log?.debug('[ReactNativeBleTransport][BleBondDiagnostic] probe error', {
255
+ phase,
256
+ outcome,
257
+ errorName: scalar(nativeError.name),
258
+ errorCode: scalar(nativeError.errorCode),
259
+ attErrorCode: scalar(nativeError.attErrorCode),
260
+ iosErrorCode: scalar(nativeError.iosErrorCode),
261
+ androidErrorCode: scalar(nativeError.androidErrorCode),
262
+ nativeStaleBond: isNativeBleStaleBondError(error),
263
+ hardwareStaleBond: isBleStaleBondHardwareError(error),
264
+ nativeDisconnect: isNativeBleDisconnectError(error),
265
+ });
266
+ };
267
+
268
+ type IosProbeDisconnectEvidence = {
269
+ phase: 'protocol-v1-get-features' | 'protocol-v2-ping';
270
+ errorCode?: string | number;
271
+ recordedAt: number;
272
+ };
273
+
274
+ const IOS_PROBE_DISCONNECT_EVIDENCE_TTL_MS = 5 * 60_000;
275
+
276
+ const isIosProbeDisconnectError = (error: unknown): boolean => {
277
+ if (Platform.OS !== 'ios') return false;
278
+ const code = (error as { errorCode?: unknown })?.errorCode;
279
+ return (
280
+ isNativeBleDisconnectError(error) ||
281
+ code === HardwareErrorCode.BleDeviceNotBonded ||
282
+ code === HardwareErrorCode.BleDeviceDisconnected
283
+ );
284
+ };
236
285
  /** Consecutive wedged writes on one device before the BLE manager itself is recreated. */
237
286
  export const BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
238
287
  const DEVICE_SCAN_TIMEOUT_MS = 3000;
@@ -575,6 +624,14 @@ export default class ReactNativeBleTransport {
575
624
  /** Endpoints whose last detection got no answer; 'woken' once their Initialize wake is spent. */
576
625
  private silentDetections = new Map<string, 'silent' | 'woken'>();
577
626
 
627
+ /**
628
+ * iOS can report a reset Pro2 as a generic peripheral disconnect instead of
629
+ * CBErrorPeerRemovedPairingInformation. Keep that first probe failure across
630
+ * the automatic cold reconnect and require a second failed probe before
631
+ * classifying the OS bond as invalid.
632
+ */
633
+ private iosProbeDisconnectEvidence = new Map<string, IosProbeDisconnectEvidence>();
634
+
578
635
  /** Android endpoints whose cached GATT table is suspect; the next connect refreshes it. */
579
636
  private androidGattCacheRefreshes = new Set<string>();
580
637
 
@@ -2111,6 +2168,7 @@ export default class ReactNativeBleTransport {
2111
2168
  this.androidLinkStartedAt.clear();
2112
2169
  stopBleKeyMissingTracking();
2113
2170
  stopBleEncryptionTracking();
2171
+ this.iosProbeDisconnectEvidence.clear();
2114
2172
  const error = ERRORS.TypedError(HardwareErrorCode.BleDeviceDisconnected);
2115
2173
  this.runPromise?.reject(error);
2116
2174
  this.runPromise = null;
@@ -2730,6 +2788,64 @@ export default class ReactNativeBleTransport {
2730
2788
  );
2731
2789
  }
2732
2790
 
2791
+ private rememberIosProbeDisconnect(
2792
+ uuid: string,
2793
+ phase: IosProbeDisconnectEvidence['phase'],
2794
+ error: unknown
2795
+ ): Error | undefined {
2796
+ if (!isIosProbeDisconnectError(error)) return undefined;
2797
+
2798
+ const previous = this.getIosProbeDisconnectEvidence(uuid);
2799
+ const errorCode = (error as { errorCode?: unknown })?.errorCode;
2800
+ const evidence: IosProbeDisconnectEvidence = {
2801
+ phase,
2802
+ ...(typeof errorCode === 'string' || typeof errorCode === 'number' ? { errorCode } : {}),
2803
+ recordedAt: Date.now(),
2804
+ };
2805
+ this.iosProbeDisconnectEvidence.set(uuid, evidence);
2806
+ Log?.debug('[ReactNativeBleTransport][BleBondDiagnostic] probe disconnect recorded', {
2807
+ phase,
2808
+ errorCode: evidence.errorCode,
2809
+ repeated: Boolean(previous),
2810
+ });
2811
+
2812
+ if (!previous) return undefined;
2813
+ return this.confirmIosProbeDisconnect(uuid, 'repeated-probe-disconnect');
2814
+ }
2815
+
2816
+ private getIosProbeDisconnectEvidence(uuid: string): IosProbeDisconnectEvidence | undefined {
2817
+ const evidence = this.iosProbeDisconnectEvidence.get(uuid);
2818
+ if (!evidence) return undefined;
2819
+
2820
+ const ageMs = Date.now() - evidence.recordedAt;
2821
+ if (ageMs <= IOS_PROBE_DISCONNECT_EVIDENCE_TTL_MS) return evidence;
2822
+
2823
+ this.iosProbeDisconnectEvidence.delete(uuid);
2824
+ Log?.debug('[ReactNativeBleTransport][BleBondDiagnostic] probe disconnect expired', {
2825
+ triggerPhase: evidence.phase,
2826
+ triggerErrorCode: evidence.errorCode,
2827
+ ageMs,
2828
+ });
2829
+ return undefined;
2830
+ }
2831
+
2832
+ private confirmIosProbeDisconnect(
2833
+ uuid: string,
2834
+ confirmation: 'repeated-probe-disconnect' | 'follow-up-no-response'
2835
+ ): Error | undefined {
2836
+ const evidence = this.getIosProbeDisconnectEvidence(uuid);
2837
+ if (!evidence) return undefined;
2838
+
2839
+ Log?.debug('[ReactNativeBleTransport][BleBondDiagnostic] stale bond confirmed', {
2840
+ confirmation,
2841
+ triggerPhase: evidence.phase,
2842
+ triggerErrorCode: evidence.errorCode,
2843
+ evidenceAgeMs: Date.now() - evidence.recordedAt,
2844
+ finalErrorCode: HardwareErrorCode.BleBondInvalid,
2845
+ });
2846
+ return ERRORS.TypedError(HardwareErrorCode.BleBondInvalid);
2847
+ }
2848
+
2733
2849
  private clearProbeProtocol(uuid: string, protocol: ProtocolType) {
2734
2850
  if (this.probingProtocols.get(uuid) === protocol) {
2735
2851
  this.probingProtocols.delete(uuid);
@@ -2761,6 +2877,7 @@ export default class ReactNativeBleTransport {
2761
2877
  // received no response. skipProtocolProbe does not need it either — its only
2762
2878
  // caller is the V2 firmware-install reconnect.
2763
2879
  if (expectedProtocol === 'V1') {
2880
+ this.iosProbeDisconnectEvidence.delete(uuid);
2764
2881
  this.deviceProtocol.set(uuid, 'V1');
2765
2882
  Log?.debug('[ReactNativeBleTransport] protocol selected', {
2766
2883
  deviceId: uuid,
@@ -2776,6 +2893,7 @@ export default class ReactNativeBleTransport {
2776
2893
 
2777
2894
  if (expectedProtocol === 'V2') {
2778
2895
  if (await this.probeProtocolV2(uuid)) {
2896
+ this.iosProbeDisconnectEvidence.delete(uuid);
2779
2897
  this.deviceProtocol.set(uuid, 'V2');
2780
2898
  this.sessionProtocols.set(uuid, 'V2');
2781
2899
  this.confirmedProtocolV2.add(uuid);
@@ -2786,6 +2904,18 @@ export default class ReactNativeBleTransport {
2786
2904
  });
2787
2905
  return 'V2';
2788
2906
  }
2907
+ const staleBondError = this.confirmIosProbeDisconnect(uuid, 'follow-up-no-response');
2908
+ if (Platform.OS === 'ios') {
2909
+ Log?.debug('[ReactNativeBleTransport][BleBondDiagnostic] protocol detection ended', {
2910
+ outcome: 'expected-v2-no-response',
2911
+ expectedProtocol,
2912
+ protocolHint: protocolHint ?? 'none',
2913
+ finalErrorCode: staleBondError
2914
+ ? HardwareErrorCode.BleBondInvalid
2915
+ : HardwareErrorCode.RuntimeError,
2916
+ });
2917
+ }
2918
+ if (staleBondError) throw staleBondError;
2789
2919
  throw this.createProtocolMismatchError(expectedProtocol);
2790
2920
  }
2791
2921
 
@@ -2821,6 +2951,7 @@ export default class ReactNativeBleTransport {
2821
2951
  const detected =
2822
2952
  protocol === 'V1' ? await this.probeProtocolV1(uuid) : await this.probeProtocolV2(uuid);
2823
2953
  if (detected) {
2954
+ this.iosProbeDisconnectEvidence.delete(uuid);
2824
2955
  this.deviceProtocol.set(uuid, protocol);
2825
2956
  this.sessionProtocols.set(uuid, protocol);
2826
2957
  if (protocol === 'V2') {
@@ -2850,6 +2981,25 @@ export default class ReactNativeBleTransport {
2850
2981
 
2851
2982
  this.deviceProtocol.delete(uuid);
2852
2983
  this.probingProtocols.delete(uuid);
2984
+ const staleBondError = this.confirmIosProbeDisconnect(uuid, 'follow-up-no-response');
2985
+ if (Platform.OS === 'ios') {
2986
+ Log?.debug('[ReactNativeBleTransport][BleBondDiagnostic] protocol detection ended', {
2987
+ outcome: 'all-probes-no-response',
2988
+ expectedProtocol: expectedProtocol ?? 'none',
2989
+ protocolHint: protocolHint ?? 'none',
2990
+ sessionProtocol: sessionProtocol ?? 'none',
2991
+ probeOrder: probeOrder.join(','),
2992
+ trustSessionProtocol,
2993
+ reprobeFailures,
2994
+ transportCached: Boolean(transportCache[uuid]),
2995
+ notifyActive: Boolean(transportCache[uuid]?.notifySubscription),
2996
+ staleBondRecorded: this.iosProbeDisconnectEvidence.has(uuid),
2997
+ finalErrorCode: staleBondError
2998
+ ? HardwareErrorCode.BleBondInvalid
2999
+ : HardwareErrorCode.BleTimeoutError,
3000
+ });
3001
+ }
3002
+ if (staleBondError) throw staleBondError;
2853
3003
  throw this.createProtocolDetectionError();
2854
3004
  }
2855
3005
 
@@ -2948,7 +3098,19 @@ export default class ReactNativeBleTransport {
2948
3098
  } catch (error) {
2949
3099
  this.clearProbeProtocol(uuid, 'V1');
2950
3100
  Log?.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe failed:', error);
2951
- if (shouldRethrowProtocolProbeError(error)) {
3101
+ const shouldRethrow = shouldRethrowProtocolProbeError(error);
3102
+ const staleBondError = this.rememberIosProbeDisconnect(
3103
+ uuid,
3104
+ 'protocol-v1-get-features',
3105
+ error
3106
+ );
3107
+ logIosBleBondDiagnostic(
3108
+ 'protocol-v1-get-features',
3109
+ error,
3110
+ shouldRethrow ? 'rethrow' : 'fallback'
3111
+ );
3112
+ if (staleBondError) throw staleBondError;
3113
+ if (shouldRethrow) {
2952
3114
  throw error;
2953
3115
  }
2954
3116
  return false;
@@ -2963,18 +3125,26 @@ export default class ReactNativeBleTransport {
2963
3125
  this.probingProtocols.set(uuid, 'V2');
2964
3126
  this.protocolV2Assemblers.get(uuid)?.reset();
2965
3127
  this.throwIfStaleBondError(uuid);
2966
- const detected = await probeProtocolV2Helper({
2967
- call: (name: string, data: Record<string, unknown>, options?: TransportCallOptions) =>
2968
- this.callProtocolV2(uuid, name, data, options),
2969
- timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT_MS,
2970
- logger: Log,
2971
- logPrefix: 'ProtocolV2 RN-BLE',
2972
- onProbeFailed: () => {
2973
- this.protocolV2Assemblers.get(uuid)?.reset();
2974
- this.resetProtocolV2Frames(uuid);
2975
- },
2976
- shouldRethrow: shouldRethrowProtocolProbeError,
2977
- });
3128
+ let detected: boolean;
3129
+ try {
3130
+ detected = await probeProtocolV2Helper({
3131
+ call: (name: string, data: Record<string, unknown>, options?: TransportCallOptions) =>
3132
+ this.callProtocolV2(uuid, name, data, options),
3133
+ timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT_MS,
3134
+ logger: Log,
3135
+ logPrefix: 'ProtocolV2 RN-BLE',
3136
+ onProbeFailed: () => {
3137
+ this.protocolV2Assemblers.get(uuid)?.reset();
3138
+ this.resetProtocolV2Frames(uuid);
3139
+ },
3140
+ shouldRethrow: shouldRethrowProtocolProbeError,
3141
+ });
3142
+ } catch (error) {
3143
+ const staleBondError = this.rememberIosProbeDisconnect(uuid, 'protocol-v2-ping', error);
3144
+ logIosBleBondDiagnostic('protocol-v2-ping', error, 'rethrow');
3145
+ if (staleBondError) throw staleBondError;
3146
+ throw error;
3147
+ }
2978
3148
  if (!detected) {
2979
3149
  this.clearProbeProtocol(uuid, 'V2');
2980
3150
  } else {