@onekeyfe/hd-transport-react-native 1.2.3-alpha.10 → 1.2.3-alpha.11

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-react-native",
3
- "version": "1.2.3-alpha.10",
3
+ "version": "1.2.3-alpha.11",
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-alpha.10",
24
- "@onekeyfe/hd-shared": "1.2.3-alpha.10",
25
- "@onekeyfe/hd-transport": "1.2.3-alpha.10",
26
- "@onekeyfe/react-native-ble-utils": "0.1.9",
23
+ "@onekeyfe/hd-core": "1.2.3-alpha.11",
24
+ "@onekeyfe/hd-shared": "1.2.3-alpha.11",
25
+ "@onekeyfe/hd-transport": "1.2.3-alpha.11",
26
+ "@onekeyfe/react-native-ble-utils": "0.1.6",
27
27
  "react-native-ble-plx": "3.5.1"
28
28
  },
29
- "gitHead": "2295285a846281941c21955684281446f587d993"
29
+ "gitHead": "b4254228fd77d7263e059e5f2a2fe0e1f8ea8060"
30
30
  }
package/src/BleManager.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import BleUtils from '@onekeyfe/react-native-ble-utils';
2
2
  import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
3
3
 
4
- import { onBleKeyMissing, startBleKeyMissingTracking } from './bleKeyMissing';
5
4
  import { bleLogger } from './logger';
6
5
 
7
6
  import type { Peripheral } from '@onekeyfe/react-native-ble-utils';
@@ -33,71 +32,20 @@ export const getBondedDevices = () => BleUtils.getBondedPeripherals();
33
32
 
34
33
  export const pairDevice = (macAddress: string) => BleUtils.pairDevice(macAddress);
35
34
 
36
- /**
37
- * Android replaces a bond as BONDING -> NONE, then NONE -> BONDING within milliseconds.
38
- * The window covers slow broadcast delivery before the first half counts as a failure.
39
- */
40
- export const SYSTEM_BONDING_RESTART_WINDOW_MS = 1500;
41
-
42
- export type DeviceBondStateOptions = {
43
- /**
44
- * The system, not this transport, started the bonding in progress. Android 16+ re-pairs
45
- * by itself after it detects a lost bond, and a successful re-pair replaces the old bond
46
- * instead of going straight to BONDED.
47
- */
48
- systemInitiated?: boolean;
49
- };
50
-
51
- const createBondFailureError = (bondState: Peripheral['bondState']) => {
52
- const nativeReason =
53
- 'reason' in bondState && typeof bondState.reason === 'number' ? bondState.reason : undefined;
54
- const reason =
55
- nativeReason === undefined ? 'unknown' : bondFailureReasons[nativeReason] ?? 'unknown';
56
- const params = {
57
- phase: 'bond',
58
- reason,
59
- ...(nativeReason === undefined ? {} : { nativeReason }),
60
- };
61
- if (reason === 'timeout') {
62
- // Connection timeouts are retried by Core; pairing requires a new user attempt.
63
- return ERRORS.TypedError(
64
- HardwareErrorCode.BleDeviceNotBonded,
65
- 'Bluetooth pairing timed out',
66
- params
67
- );
68
- }
69
- if (reason === 'canceled') {
70
- return ERRORS.TypedError(
71
- HardwareErrorCode.BleDeviceBondedCanceled,
72
- 'Bluetooth pairing canceled',
73
- params
74
- );
75
- }
76
- return ERRORS.TypedError(
77
- HardwareErrorCode.BleDeviceNotBonded,
78
- 'Bluetooth pairing failed',
79
- params
80
- );
81
- };
82
-
83
35
  export const onDeviceBondState = (
84
36
  bleMacAddress: string,
85
- signal?: AbortSignal,
86
- options?: DeviceBondStateOptions
37
+ signal?: AbortSignal
87
38
  ): Promise<Peripheral | undefined> =>
88
39
  new Promise((resolve, reject) => {
89
40
  if (signal?.aborted) {
90
41
  reject(ERRORS.TypedError(HardwareErrorCode.BleDeviceDisconnected));
91
42
  return;
92
43
  }
93
- let pendingFailure: ReturnType<typeof setTimeout> | undefined;
94
44
  const cleanup = () => {
95
45
  if (timeout) {
96
46
  clearTimeout(timeout);
97
47
  }
98
- if (pendingFailure) clearTimeout(pendingFailure);
99
48
  if (cleanupListener) cleanupListener();
100
- cleanupKeyMissing?.();
101
49
  signal?.removeEventListener('abort', onAbort);
102
50
  };
103
51
  const onAbort = () => {
@@ -117,20 +65,6 @@ export const onDeviceBondState = (
117
65
  );
118
66
  }, 60 * 1000);
119
67
 
120
- // A failed system re-pair restores the old bond, so it also ends in BONDED. Key
121
- // missing is what tells it apart from a bond that now works.
122
- const cleanupKeyMissing = startBleKeyMissingTracking()
123
- ? onBleKeyMissing(bleMacAddress, () => {
124
- cleanup();
125
- reject(
126
- ERRORS.TypedError(HardwareErrorCode.BleBondInvalid, undefined, {
127
- phase: 'bond',
128
- reason: 'key_missing',
129
- })
130
- );
131
- })
132
- : undefined;
133
-
134
68
  const cleanupListener = BleUtils.onDeviceBondState(peripheral => {
135
69
  if (peripheral.id?.toLowerCase() !== bleMacAddress.toLowerCase()) {
136
70
  return;
@@ -139,23 +73,49 @@ export const onDeviceBondState = (
139
73
 
140
74
  const hasBonded = bondState.preState === 'BOND_BONDING' && bondState.state === 'BOND_BONDED';
141
75
  const hasFailed = bondState.preState === 'BOND_BONDING' && bondState.state === 'BOND_NONE';
142
- const hasRestarted = bondState.preState === 'BOND_NONE' && bondState.state === 'BOND_BONDING';
143
76
  Logger.debug('onDeviceBondState bondState:', bondState);
144
77
  if (hasBonded) {
145
78
  cleanup();
146
79
  resolve(peripheral);
147
- } else if (hasRestarted && pendingFailure) {
148
- clearTimeout(pendingFailure);
149
- pendingFailure = undefined;
150
- } else if (hasFailed && options?.systemInitiated) {
151
- if (pendingFailure) clearTimeout(pendingFailure);
152
- pendingFailure = setTimeout(() => {
153
- cleanup();
154
- reject(createBondFailureError(bondState));
155
- }, SYSTEM_BONDING_RESTART_WINDOW_MS);
156
80
  } else if (hasFailed) {
157
81
  cleanup();
158
- reject(createBondFailureError(bondState));
82
+ const nativeReason =
83
+ 'reason' in bondState && typeof bondState.reason === 'number'
84
+ ? bondState.reason
85
+ : undefined;
86
+ const reason =
87
+ nativeReason === undefined ? 'unknown' : bondFailureReasons[nativeReason] ?? 'unknown';
88
+ const params = {
89
+ phase: 'bond',
90
+ reason,
91
+ ...(nativeReason === undefined ? {} : { nativeReason }),
92
+ };
93
+ if (reason === 'timeout') {
94
+ // Connection timeouts are retried by Core; pairing requires a new user attempt.
95
+ reject(
96
+ ERRORS.TypedError(
97
+ HardwareErrorCode.BleDeviceNotBonded,
98
+ 'Bluetooth pairing timed out',
99
+ params
100
+ )
101
+ );
102
+ } else if (reason === 'canceled') {
103
+ reject(
104
+ ERRORS.TypedError(
105
+ HardwareErrorCode.BleDeviceBondedCanceled,
106
+ 'Bluetooth pairing canceled',
107
+ params
108
+ )
109
+ );
110
+ } else {
111
+ reject(
112
+ ERRORS.TypedError(
113
+ HardwareErrorCode.BleDeviceNotBonded,
114
+ 'Bluetooth pairing failed',
115
+ params
116
+ )
117
+ );
118
+ }
159
119
  }
160
120
  });
161
121
  });
@@ -25,21 +25,6 @@ jest.mock('../BleManager', () => ({
25
25
  pairDevice: jest.fn(),
26
26
  }));
27
27
 
28
- // The native facade is replaced wholesale above; key missing and link encryption are covered
29
- // by their own suites.
30
- jest.mock('../bleKeyMissing', () => ({
31
- isBleKeyMissingSupported: jest.fn(() => false),
32
- startBleKeyMissingTracking: jest.fn(() => false),
33
- stopBleKeyMissingTracking: jest.fn(),
34
- waitForBleKeyMissing: jest.fn(() => Promise.resolve(false)),
35
- }));
36
- jest.mock('../bleEncryption', () => ({
37
- markBleLinkEncrypted: jest.fn(),
38
- startBleEncryptionTracking: jest.fn(() => false),
39
- stopBleEncryptionTracking: jest.fn(),
40
- waitForAndroidLinkEncryption: jest.fn(() => Promise.resolve('unresolved')),
41
- }));
42
-
43
28
  jest.mock('../subscribeBleOn', () => ({
44
29
  subscribeBleOn: jest.fn(() => Promise.resolve()),
45
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,90 @@ 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
+ });
187
+
188
+ test('promotes a repeated iOS probe disconnect without waiting for probe timeouts', () => {
189
+ const { transport } = createHarness({ v1: false, v2: false });
190
+ const firstError = Object.assign(new Error('The specified device has disconnected from us'), {
191
+ errorCode: 201,
192
+ iosErrorCode: 7,
193
+ });
194
+ const secondError = ERRORS.TypedError(HardwareErrorCode.BleDeviceDisconnected);
195
+
196
+ expect(
197
+ (transport as any).rememberIosProbeDisconnect(UUID, 'protocol-v1-get-features', firstError)
198
+ ).toBeUndefined();
199
+ expect(
200
+ (transport as any).rememberIosProbeDisconnect(UUID, 'protocol-v2-ping', secondError)
201
+ ).toMatchObject({ errorCode: HardwareErrorCode.BleBondInvalid });
202
+ });
203
+
204
+ test('clears the disconnect evidence after a valid protocol response', async () => {
205
+ const { transport } = createHarness({ v1: true, v2: false });
206
+ (transport as any).rememberIosProbeDisconnect(
207
+ UUID,
208
+ 'protocol-v1-get-features',
209
+ ERRORS.TypedError(HardwareErrorCode.BleDeviceNotBonded)
210
+ );
211
+
212
+ await expect(detect(transport)).resolves.toBe('V1');
213
+ expect((transport as any).iosProbeDisconnectEvidence.has(UUID)).toBe(false);
214
+ });
215
+
216
+ test('preserves evidence for acquire retry cleanup and clears it on an explicit disconnect', async () => {
217
+ const { transport } = createHarness({ v1: false, v2: false });
218
+ (transport as any).rememberIosProbeDisconnect(
219
+ UUID,
220
+ 'protocol-v1-get-features',
221
+ ERRORS.TypedError(HardwareErrorCode.BleDeviceNotBonded)
222
+ );
223
+
224
+ await (transport as any).disconnectUnlocked(UUID, {
225
+ preserveIosProbeDisconnect: true,
226
+ });
227
+ expect((transport as any).iosProbeDisconnectEvidence.has(UUID)).toBe(true);
228
+
229
+ await (transport as any).disconnectUnlocked(UUID);
230
+ expect((transport as any).iosProbeDisconnectEvidence.has(UUID)).toBe(false);
231
+ });
232
+
233
+ test('does not classify the same sequence as a stale bond on Android', async () => {
234
+ Object.assign(Platform, { OS: 'android' });
235
+ const { transport } = createHarness({ v1: false, v2: false });
236
+
237
+ expect(
238
+ (transport as any).rememberIosProbeDisconnect(
239
+ UUID,
240
+ 'protocol-v1-get-features',
241
+ ERRORS.TypedError(HardwareErrorCode.BleDeviceNotBonded)
242
+ )
243
+ ).toBeUndefined();
244
+ await expect(detect(transport)).rejects.toMatchObject({
245
+ errorCode: HardwareErrorCode.BleTimeoutError,
246
+ });
247
+ });
248
+ });
@@ -45,21 +45,6 @@ jest.mock('../BleManager', () => ({
45
45
  pairDevice: jest.fn(() => Promise.resolve({ bonded: true, bonding: false })),
46
46
  }));
47
47
 
48
- // The native facade is replaced wholesale above; key missing and link encryption are covered
49
- // by their own suites.
50
- jest.mock('../bleKeyMissing', () => ({
51
- isBleKeyMissingSupported: jest.fn(() => false),
52
- startBleKeyMissingTracking: jest.fn(() => false),
53
- stopBleKeyMissingTracking: jest.fn(),
54
- waitForBleKeyMissing: jest.fn(() => Promise.resolve(false)),
55
- }));
56
- jest.mock('../bleEncryption', () => ({
57
- markBleLinkEncrypted: jest.fn(),
58
- startBleEncryptionTracking: jest.fn(() => false),
59
- stopBleEncryptionTracking: jest.fn(),
60
- waitForAndroidLinkEncryption: jest.fn(() => Promise.resolve('unresolved')),
61
- }));
62
-
63
48
  jest.mock('../subscribeBleOn', () => ({
64
49
  subscribeBleOn: jest.fn(() => Promise.resolve()),
65
50
  }));