@onekeyfe/hd-transport-react-native 1.2.0-alpha.159 → 1.2.0-alpha.160

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.
@@ -0,0 +1,5 @@
1
+ import { isBleStaleBondHardwareError } from '@onekeyfe/hd-shared';
2
+ export { isBleStaleBondHardwareError };
3
+ export declare const isNativeBleStaleBondError: (error: unknown) => boolean;
4
+ export declare const toBleStaleBondHardwareError: (error: unknown) => Error;
5
+ //# sourceMappingURL=bleStaleBond.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bleStaleBond.d.ts","sourceRoot":"","sources":["../src/bleStaleBond.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,2BAA2B,EAC5B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,2BAA2B,EAAE,CAAC;AAmBvC,eAAO,MAAM,yBAAyB,UAAW,OAAO,KAAG,OAgB1D,CAAC;AAEF,eAAO,MAAM,2BAA2B,UAAW,OAAO,UAkBzD,CAAC"}
package/dist/index.d.ts CHANGED
@@ -126,6 +126,12 @@ declare class ReactNativeBleTransport {
126
126
  private confirmedProtocolV2;
127
127
  /** Consecutive detections that failed while trusting sessionProtocols. */
128
128
  private protocolReprobeFailures;
129
+ /**
130
+ * Native encryption/pairing failures seen before Protocol V2 probe starts.
131
+ * Pro2/Neo GATT connect can succeed on a stale iOS bond; the CCCD write then
132
+ * fails with ATT 14/15. Remember it so detectProtocol fails immediately.
133
+ */
134
+ private staleBondErrors;
129
135
  private protocolV2Assemblers;
130
136
  private protocolV2FrameQueues;
131
137
  private protocolV2FramePromises;
@@ -216,6 +222,8 @@ declare class ReactNativeBleTransport {
216
222
  private resolveProtocolV2Frame;
217
223
  private resetProtocolV2Frames;
218
224
  private rejectProtocolV2Frames;
225
+ private rememberStaleBondError;
226
+ private throwIfStaleBondError;
219
227
  private readProtocolV2Frame;
220
228
  private writeProtocolV2Packet;
221
229
  private writeProtocolV2Frame;
@@ -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
@@ -194,6 +194,40 @@ const subscribeBleOn = (bleManager, ms = 1000) => new Promise((resolve, reject)
194
194
  }, ms);
195
195
  });
196
196
 
197
+ const ATT_INSUFFICIENT_AUTHENTICATION = 5;
198
+ const ATT_UNLIKELY_ERROR = 14;
199
+ const ATT_INSUFFICIENT_ENCRYPTION = 15;
200
+ const IOS_PEER_REMOVED_PAIRING_INFORMATION = 14;
201
+ const nativeErrorText = (error) => [error.reason, error.message]
202
+ .filter((value) => typeof value === 'string')
203
+ .join(' ');
204
+ const isNativeBleStaleBondError = (error) => {
205
+ if (!error || typeof error !== 'object') {
206
+ return typeof error === 'string' ? hdShared.isBleStaleBondErrorText(error) : false;
207
+ }
208
+ const nativeError = error;
209
+ if (nativeError.attErrorCode === ATT_INSUFFICIENT_AUTHENTICATION ||
210
+ nativeError.attErrorCode === ATT_UNLIKELY_ERROR ||
211
+ nativeError.attErrorCode === ATT_INSUFFICIENT_ENCRYPTION ||
212
+ nativeError.iosErrorCode === IOS_PEER_REMOVED_PAIRING_INFORMATION) {
213
+ return true;
214
+ }
215
+ return hdShared.isBleStaleBondErrorText(nativeErrorText(nativeError));
216
+ };
217
+ const toBleStaleBondHardwareError = (error) => {
218
+ if (hdShared.isBleStaleBondHardwareError(error)) {
219
+ return error;
220
+ }
221
+ const nativeError = (error !== null && error !== void 0 ? error : {});
222
+ const text = nativeErrorText(nativeError);
223
+ const peerRemoved = nativeError.iosErrorCode === IOS_PEER_REMOVED_PAIRING_INFORMATION ||
224
+ nativeError.attErrorCode === ATT_UNLIKELY_ERROR ||
225
+ text.includes('Peer removed pairing information');
226
+ return hdShared.ERRORS.TypedError(peerRemoved
227
+ ? hdShared.HardwareErrorCode.BlePeerRemovedPairingInformation
228
+ : hdShared.HardwareErrorCode.BleDeviceBondError, text || undefined);
229
+ };
230
+
197
231
  const isHeaderChunk = (chunk) => {
198
232
  if (chunk.length < 9)
199
233
  return false;
@@ -375,9 +409,8 @@ const resolveNegotiatedMtu = (device) => requestNegotiatedMtu(device, 'connected
375
409
  function remapError(error) {
376
410
  var _a;
377
411
  if (error instanceof reactNativeBlePlx.BleError) {
378
- if (error.iosErrorCode === reactNativeBlePlx.BleATTErrorCode.UnlikelyError ||
379
- error.reason === 'Peer removed pairing information') {
380
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePeerRemovedPairingInformation);
412
+ if (isNativeBleStaleBondError(error)) {
413
+ throw toBleStaleBondHardwareError(error);
381
414
  }
382
415
  if ((error === null || error === void 0 ? void 0 : error.attErrorCode) === 22) {
383
416
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceBondError);
@@ -408,6 +441,7 @@ class ReactNativeBleTransport {
408
441
  this.sessionProtocols = new Map();
409
442
  this.confirmedProtocolV2 = new Set();
410
443
  this.protocolReprobeFailures = new Map();
444
+ this.staleBondErrors = new Map();
411
445
  this.protocolV2Assemblers = new Map();
412
446
  this.protocolV2FrameQueues = new Map();
413
447
  this.protocolV2FramePromises = new Map();
@@ -935,7 +969,7 @@ class ReactNativeBleTransport {
935
969
  return { uuid, protocolType };
936
970
  }
937
971
  catch (error) {
938
- if ((error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleDeviceBondError) {
972
+ if (hdShared.isBleStaleBondHardwareError(error)) {
939
973
  yield this.disconnectUnlocked(uuid);
940
974
  }
941
975
  else {
@@ -949,7 +983,7 @@ class ReactNativeBleTransport {
949
983
  let bufferLength = 0;
950
984
  let buffer$1 = [];
951
985
  const subscription = characteristic.monitor((error, c) => {
952
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
986
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
953
987
  const isCurrentMonitor = this.monitorTokens.get(uuid) === monitorToken;
954
988
  if (error) {
955
989
  Log === null || Log === void 0 ? void 0 : Log.debug(`error monitor ${characteristic.uuid}, deviceId: ${characteristic.deviceID}: ${error}`);
@@ -961,19 +995,20 @@ class ReactNativeBleTransport {
961
995
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
962
996
  return;
963
997
  }
998
+ if (isNativeBleStaleBondError(error)) {
999
+ this.rememberStaleBondError(uuid, toBleStaleBondHardwareError(error));
1000
+ return;
1001
+ }
964
1002
  if (this.getActiveProtocol(uuid) === 'V2') {
965
1003
  let errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
966
1004
  if ((_a = error.reason) === null || _a === void 0 ? void 0 : _a.includes('The connection has timed out unexpectedly')) {
967
1005
  errorCode = hdShared.HardwareErrorCode.BleTimeoutError;
968
1006
  }
969
- else if ((_b = error.reason) === null || _b === void 0 ? void 0 : _b.includes('Encryption is insufficient')) {
970
- errorCode = hdShared.HardwareErrorCode.BleDeviceBondError;
971
- }
972
- else if (((_c = error.reason) === null || _c === void 0 ? void 0 : _c.includes('Cannot write client characteristic config descriptor')) ||
973
- ((_d = error.reason) === null || _d === void 0 ? void 0 : _d.includes('Cannot find client characteristic config descriptor')) ||
974
- ((_e = error.reason) === null || _e === void 0 ? void 0 : _e.includes('The handle is invalid')) ||
975
- ((_f = error.reason) === null || _f === void 0 ? void 0 : _f.includes('Writing is not permitted')) ||
976
- ((_g = error.reason) === null || _g === void 0 ? void 0 : _g.includes('notify change failed for device'))) {
1007
+ else if (((_b = error.reason) === null || _b === void 0 ? void 0 : _b.includes('Cannot write client characteristic config descriptor')) ||
1008
+ ((_c = error.reason) === null || _c === void 0 ? void 0 : _c.includes('Cannot find client characteristic config descriptor')) ||
1009
+ ((_d = error.reason) === null || _d === void 0 ? void 0 : _d.includes('The handle is invalid')) ||
1010
+ ((_e = error.reason) === null || _e === void 0 ? void 0 : _e.includes('Writing is not permitted')) ||
1011
+ ((_f = error.reason) === null || _f === void 0 ? void 0 : _f.includes('notify change failed for device'))) {
977
1012
  errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure;
978
1013
  }
979
1014
  this.rejectProtocolV2Frames(uuid, hdShared.ERRORS.TypedError(errorCode));
@@ -981,17 +1016,14 @@ class ReactNativeBleTransport {
981
1016
  }
982
1017
  if (this.runPromise && this.runPromiseDeviceId === uuid) {
983
1018
  let ERROR = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
984
- if ((_h = error.reason) === null || _h === void 0 ? void 0 : _h.includes('The connection has timed out unexpectedly')) {
1019
+ if ((_g = error.reason) === null || _g === void 0 ? void 0 : _g.includes('The connection has timed out unexpectedly')) {
985
1020
  ERROR = hdShared.HardwareErrorCode.BleTimeoutError;
986
1021
  }
987
- if ((_j = error.reason) === null || _j === void 0 ? void 0 : _j.includes('Encryption is insufficient')) {
988
- ERROR = hdShared.HardwareErrorCode.BleDeviceBondError;
989
- }
990
- if (((_k = error.reason) === null || _k === void 0 ? void 0 : _k.includes('Cannot write client characteristic config descriptor')) ||
991
- ((_l = error.reason) === null || _l === void 0 ? void 0 : _l.includes('Cannot find client characteristic config descriptor')) ||
992
- ((_m = error.reason) === null || _m === void 0 ? void 0 : _m.includes('The handle is invalid')) ||
993
- ((_o = error.reason) === null || _o === void 0 ? void 0 : _o.includes('Writing is not permitted')) ||
994
- ((_p = error.reason) === null || _p === void 0 ? void 0 : _p.includes('notify change failed for device'))) {
1022
+ if (((_h = error.reason) === null || _h === void 0 ? void 0 : _h.includes('Cannot write client characteristic config descriptor')) ||
1023
+ ((_j = error.reason) === null || _j === void 0 ? void 0 : _j.includes('Cannot find client characteristic config descriptor')) ||
1024
+ ((_k = error.reason) === null || _k === void 0 ? void 0 : _k.includes('The handle is invalid')) ||
1025
+ ((_l = error.reason) === null || _l === void 0 ? void 0 : _l.includes('Writing is not permitted')) ||
1026
+ ((_m = error.reason) === null || _m === void 0 ? void 0 : _m.includes('notify change failed for device'))) {
995
1027
  const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure);
996
1028
  this.runPromise.reject(notifyError);
997
1029
  Log === null || Log === void 0 ? void 0 : Log.debug(`${hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure} ${error.message} ${error.reason}`);
@@ -1033,7 +1065,7 @@ class ReactNativeBleTransport {
1033
1065
  bufferLength = 0;
1034
1066
  buffer$1 = [];
1035
1067
  if (this.runPromiseDeviceId === uuid) {
1036
- (_q = this.runPromise) === null || _q === void 0 ? void 0 : _q.resolve(value.toString('hex'));
1068
+ (_o = this.runPromise) === null || _o === void 0 ? void 0 : _o.resolve(value.toString('hex'));
1037
1069
  }
1038
1070
  }
1039
1071
  }
@@ -1044,7 +1076,7 @@ class ReactNativeBleTransport {
1044
1076
  this.rejectProtocolV2Frames(uuid, notifyError);
1045
1077
  }
1046
1078
  else if (this.runPromiseDeviceId === uuid) {
1047
- (_r = this.runPromise) === null || _r === void 0 ? void 0 : _r.reject(notifyError);
1079
+ (_p = this.runPromise) === null || _p === void 0 ? void 0 : _p.reject(notifyError);
1048
1080
  }
1049
1081
  }
1050
1082
  }, notifyTransactionId);
@@ -1098,6 +1130,7 @@ class ReactNativeBleTransport {
1098
1130
  this.protocolV2HighVolumeLogSignatures.delete(uuid);
1099
1131
  this.deviceProtocol.delete(uuid);
1100
1132
  this.probingProtocols.delete(uuid);
1133
+ this.staleBondErrors.delete(uuid);
1101
1134
  (_e = this.protocolV2Assemblers.get(uuid)) === null || _e === void 0 ? void 0 : _e.reset();
1102
1135
  this.protocolV2Assemblers.delete(uuid);
1103
1136
  this.resetProtocolV2Frames(uuid);
@@ -1191,6 +1224,9 @@ class ReactNativeBleTransport {
1191
1224
  if (isWedgedWriteError(e)) {
1192
1225
  throw e;
1193
1226
  }
1227
+ if (isNativeBleStaleBondError(e) || hdShared.isBleStaleBondHardwareError(e)) {
1228
+ throw toBleStaleBondHardwareError(e);
1229
+ }
1194
1230
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1195
1231
  }
1196
1232
  }
@@ -1221,6 +1257,9 @@ class ReactNativeBleTransport {
1221
1257
  if (isWedgedWriteError(e)) {
1222
1258
  throw e;
1223
1259
  }
1260
+ if (isNativeBleStaleBondError(e) || hdShared.isBleStaleBondHardwareError(e)) {
1261
+ throw toBleStaleBondHardwareError(e);
1262
+ }
1224
1263
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1225
1264
  }
1226
1265
  }
@@ -1286,6 +1325,11 @@ class ReactNativeBleTransport {
1286
1325
  if (isWedgedWriteError(e)) {
1287
1326
  throw e;
1288
1327
  }
1328
+ if (isNativeBleStaleBondError(e) || hdShared.isBleStaleBondHardwareError(e)) {
1329
+ const bondError = toBleStaleBondHardwareError(e);
1330
+ this.rememberStaleBondError(uuid, bondError);
1331
+ throw bondError;
1332
+ }
1289
1333
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected) {
1290
1334
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded);
1291
1335
  }
@@ -1383,6 +1427,7 @@ class ReactNativeBleTransport {
1383
1427
  }
1384
1428
  this.deviceProtocol.delete(session);
1385
1429
  this.probingProtocols.delete(session);
1430
+ this.staleBondErrors.delete(session);
1386
1431
  this.deviceProtocolHints.delete(session);
1387
1432
  this.sessionProtocols.delete(session);
1388
1433
  this.protocolReprobeFailures.delete(session);
@@ -1561,6 +1606,7 @@ class ReactNativeBleTransport {
1561
1606
  }
1562
1607
  this.deviceProtocol.delete(uuid);
1563
1608
  this.probingProtocols.delete(uuid);
1609
+ this.staleBondErrors.delete(uuid);
1564
1610
  this.protocolV2Assemblers.delete(uuid);
1565
1611
  this.resetProtocolV2Frames(uuid);
1566
1612
  if (timeouts >= BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD) {
@@ -1630,6 +1676,7 @@ class ReactNativeBleTransport {
1630
1676
  }
1631
1677
  this.deviceProtocol.delete(uuid);
1632
1678
  this.probingProtocols.delete(uuid);
1679
+ this.staleBondErrors.delete(uuid);
1633
1680
  this.protocolV2Assemblers.delete(uuid);
1634
1681
  this.resetProtocolV2Frames(uuid);
1635
1682
  if (timeouts >= BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD) {
@@ -1672,6 +1719,7 @@ class ReactNativeBleTransport {
1672
1719
  });
1673
1720
  this.deviceProtocol.clear();
1674
1721
  this.probingProtocols.clear();
1722
+ this.staleBondErrors.clear();
1675
1723
  this.sessionProtocols.clear();
1676
1724
  this.confirmedProtocolV2.clear();
1677
1725
  this.protocolReprobeFailures.clear();
@@ -1717,6 +1765,7 @@ class ReactNativeBleTransport {
1717
1765
  });
1718
1766
  return expectedProtocol;
1719
1767
  }
1768
+ this.throwIfStaleBondError(uuid);
1720
1769
  if (expectedProtocol === 'V1') {
1721
1770
  if (yield this.probeProtocolV1(uuid)) {
1722
1771
  this.deviceProtocol.set(uuid, 'V1');
@@ -1845,7 +1894,7 @@ class ReactNativeBleTransport {
1845
1894
  catch (error) {
1846
1895
  this.clearProbeProtocol(uuid, 'V1');
1847
1896
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe failed:', error);
1848
- if (isWedgedWriteError(error)) {
1897
+ if (isWedgedWriteError(error) || hdShared.isBleStaleBondHardwareError(error)) {
1849
1898
  throw error;
1850
1899
  }
1851
1900
  return false;
@@ -1860,6 +1909,7 @@ class ReactNativeBleTransport {
1860
1909
  }
1861
1910
  this.probingProtocols.set(uuid, 'V2');
1862
1911
  (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1912
+ this.throwIfStaleBondError(uuid);
1863
1913
  const detected = yield transport.probeProtocolV2({
1864
1914
  call: (name, data, options) => this.callProtocolV2(uuid, name, data, options),
1865
1915
  timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT_MS,
@@ -1870,6 +1920,7 @@ class ReactNativeBleTransport {
1870
1920
  (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1871
1921
  this.resetProtocolV2Frames(uuid);
1872
1922
  },
1923
+ shouldRethrow: hdShared.isBleStaleBondHardwareError,
1873
1924
  });
1874
1925
  if (!detected) {
1875
1926
  this.clearProbeProtocol(uuid, 'V2');
@@ -1930,6 +1981,19 @@ class ReactNativeBleTransport {
1930
1981
  framePromise.reject(error);
1931
1982
  }
1932
1983
  }
1984
+ rememberStaleBondError(uuid, error) {
1985
+ this.staleBondErrors.set(uuid, error);
1986
+ this.rejectProtocolV2Frames(uuid, error);
1987
+ if (this.runPromise && this.runPromiseDeviceId === uuid) {
1988
+ this.runPromise.reject(error);
1989
+ }
1990
+ }
1991
+ throwIfStaleBondError(uuid) {
1992
+ const error = this.staleBondErrors.get(uuid);
1993
+ if (error) {
1994
+ throw error;
1995
+ }
1996
+ }
1933
1997
  readProtocolV2Frame(uuid) {
1934
1998
  return __awaiter(this, void 0, void 0, function* () {
1935
1999
  const queuedFrame = this.getProtocolV2FrameQueue(uuid).shift();
@@ -1978,6 +2042,11 @@ class ReactNativeBleTransport {
1978
2042
  return;
1979
2043
  }
1980
2044
  catch (error) {
2045
+ if (isNativeBleStaleBondError(error) || hdShared.isBleStaleBondHardwareError(error)) {
2046
+ const bondError = toBleStaleBondHardwareError(error);
2047
+ this.rememberStaleBondError(uuid, bondError);
2048
+ throw bondError;
2049
+ }
1981
2050
  if (getFirmwareUploadWriteRetryType(error) !== 'congested' ||
1982
2051
  attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
1983
2052
  throw error;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-react-native",
3
- "version": "1.2.0-alpha.159",
3
+ "version": "1.2.0-alpha.160",
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.159",
24
- "@onekeyfe/hd-shared": "1.2.0-alpha.159",
25
- "@onekeyfe/hd-transport": "1.2.0-alpha.159",
23
+ "@onekeyfe/hd-core": "1.2.0-alpha.160",
24
+ "@onekeyfe/hd-shared": "1.2.0-alpha.160",
25
+ "@onekeyfe/hd-transport": "1.2.0-alpha.160",
26
26
  "@onekeyfe/react-native-ble-utils": "^0.1.6",
27
27
  "react-native-ble-plx": "3.5.1"
28
28
  },
29
- "gitHead": "302f9ff916b3769cdb0cf79d58d09a87eeac68c2"
29
+ "gitHead": "df1736352e44ac76af4185eb7a6112a081b0d68c"
30
30
  }
@@ -0,0 +1,31 @@
1
+ import { HardwareErrorCode } from '@onekeyfe/hd-shared';
2
+
3
+ import { isNativeBleStaleBondError, toBleStaleBondHardwareError } from '../bleStaleBond';
4
+
5
+ describe('native BLE stale bond mapping', () => {
6
+ test.each([
7
+ [
8
+ { attErrorCode: 15, reason: 'Encryption is insufficient' },
9
+ HardwareErrorCode.BleDeviceBondError,
10
+ ],
11
+ [
12
+ { attErrorCode: 5, reason: 'GATT_INSUF_AUTHENTICATION' },
13
+ HardwareErrorCode.BleDeviceBondError,
14
+ ],
15
+ [
16
+ { iosErrorCode: 14, reason: 'Peer removed pairing information' },
17
+ HardwareErrorCode.BlePeerRemovedPairingInformation,
18
+ ],
19
+ [
20
+ { attErrorCode: 14, reason: 'Unlikely error' },
21
+ HardwareErrorCode.BlePeerRemovedPairingInformation,
22
+ ],
23
+ ])('maps %j to %s', (nativeError, errorCode) => {
24
+ expect(isNativeBleStaleBondError(nativeError)).toBe(true);
25
+ expect(toBleStaleBondHardwareError(nativeError)).toMatchObject({ errorCode });
26
+ });
27
+
28
+ test('does not treat a generic disconnect as a stale bond', () => {
29
+ expect(isNativeBleStaleBondError({ reason: 'Device disconnected' })).toBe(false);
30
+ });
31
+ });
@@ -109,9 +109,11 @@ const schemas = {
109
109
  const createHarness = ({
110
110
  deviceName = 'OneKey Pro 2',
111
111
  isWritableWithResponse = true,
112
+ monitorError,
112
113
  }: {
113
114
  deviceName?: string;
114
115
  isWritableWithResponse?: boolean;
116
+ monitorError?: (Error & { reason?: string; attErrorCode?: number; iosErrorCode?: number }) | null;
115
117
  } = {}) => {
116
118
  const uuid = 'rn-pro2-id';
117
119
  const sentSeqs: number[] = [];
@@ -130,6 +132,9 @@ const createHarness = ({
130
132
  isNotifiable: true,
131
133
  monitor: jest.fn(callback => {
132
134
  notifyCallback = callback;
135
+ if (monitorError) {
136
+ queueMicrotask(() => callback(monitorError, null));
137
+ }
133
138
  return { remove: jest.fn() };
134
139
  }),
135
140
  };
@@ -459,6 +464,35 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
459
464
  }
460
465
  );
461
466
 
467
+ test.each([
468
+ [
469
+ 'Encryption is insufficient',
470
+ { reason: 'Encryption is insufficient', attErrorCode: 15 },
471
+ HardwareErrorCode.BleDeviceBondError,
472
+ ],
473
+ [
474
+ 'Peer removed pairing information',
475
+ { reason: 'Peer removed pairing information', iosErrorCode: 14 },
476
+ HardwareErrorCode.BlePeerRemovedPairingInformation,
477
+ ],
478
+ ] as const)(
479
+ 'fails Protocol V2 acquire immediately on %s instead of waiting for Ping',
480
+ async (_label, nativeError, errorCode) => {
481
+ const { transport, uuid, device } = createHarness({
482
+ monitorError: Object.assign(new Error(nativeError.reason), nativeError),
483
+ });
484
+ const probe = jest.spyOn(transport as any, 'probeProtocolV2');
485
+
486
+ await expect(transport.acquire({ uuid, expectedProtocol: 'V2' })).rejects.toMatchObject({
487
+ errorCode,
488
+ });
489
+
490
+ expect(probe).not.toHaveBeenCalled();
491
+ expect(device.cancelConnection).toHaveBeenCalled();
492
+ expect(transport.getProtocolType(uuid)).toBeUndefined();
493
+ }
494
+ );
495
+
462
496
  test.each(['ios', 'android'] as const)(
463
497
  'reports a stale bond on %s when a previously confirmed Protocol V2 device stops responding',
464
498
  async platform => {
@@ -0,0 +1,63 @@
1
+ import {
2
+ ERRORS,
3
+ HardwareErrorCode,
4
+ isBleStaleBondErrorText,
5
+ isBleStaleBondHardwareError,
6
+ } from '@onekeyfe/hd-shared';
7
+
8
+ export { isBleStaleBondHardwareError };
9
+
10
+ const ATT_INSUFFICIENT_AUTHENTICATION = 5;
11
+ const ATT_UNLIKELY_ERROR = 14;
12
+ const ATT_INSUFFICIENT_ENCRYPTION = 15;
13
+ const IOS_PEER_REMOVED_PAIRING_INFORMATION = 14;
14
+
15
+ type NativeBleErrorFields = {
16
+ attErrorCode?: unknown;
17
+ iosErrorCode?: unknown;
18
+ reason?: unknown;
19
+ message?: unknown;
20
+ };
21
+
22
+ const nativeErrorText = (error: NativeBleErrorFields) =>
23
+ [error.reason, error.message]
24
+ .filter((value): value is string => typeof value === 'string')
25
+ .join(' ');
26
+
27
+ export const isNativeBleStaleBondError = (error: unknown): boolean => {
28
+ if (!error || typeof error !== 'object') {
29
+ return typeof error === 'string' ? isBleStaleBondErrorText(error) : false;
30
+ }
31
+
32
+ const nativeError = error as NativeBleErrorFields;
33
+ if (
34
+ nativeError.attErrorCode === ATT_INSUFFICIENT_AUTHENTICATION ||
35
+ nativeError.attErrorCode === ATT_UNLIKELY_ERROR ||
36
+ nativeError.attErrorCode === ATT_INSUFFICIENT_ENCRYPTION ||
37
+ nativeError.iosErrorCode === IOS_PEER_REMOVED_PAIRING_INFORMATION
38
+ ) {
39
+ return true;
40
+ }
41
+
42
+ return isBleStaleBondErrorText(nativeErrorText(nativeError));
43
+ };
44
+
45
+ export const toBleStaleBondHardwareError = (error: unknown) => {
46
+ if (isBleStaleBondHardwareError(error)) {
47
+ return error as Error;
48
+ }
49
+
50
+ const nativeError = (error ?? {}) as NativeBleErrorFields;
51
+ const text = nativeErrorText(nativeError);
52
+ const peerRemoved =
53
+ nativeError.iosErrorCode === IOS_PEER_REMOVED_PAIRING_INFORMATION ||
54
+ nativeError.attErrorCode === ATT_UNLIKELY_ERROR ||
55
+ text.includes('Peer removed pairing information');
56
+
57
+ return ERRORS.TypedError(
58
+ peerRemoved
59
+ ? HardwareErrorCode.BlePeerRemovedPairingInformation
60
+ : HardwareErrorCode.BleDeviceBondError,
61
+ text || undefined
62
+ );
63
+ };
package/src/index.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { PermissionsAndroid, Platform } from 'react-native';
2
2
  import { Buffer } from 'buffer';
3
3
  import {
4
- BleATTErrorCode,
5
4
  BleError,
6
5
  BleErrorCode,
7
6
  BleManager as BlePlxManager,
@@ -48,6 +47,11 @@ import {
48
47
  getInfosForServiceUuid,
49
48
  isSameBleUuid,
50
49
  } from './constants';
50
+ import {
51
+ isBleStaleBondHardwareError,
52
+ isNativeBleStaleBondError,
53
+ toBleStaleBondHardwareError,
54
+ } from './bleStaleBond';
51
55
  import { isHeaderChunk } from './utils/validateNotify';
52
56
  import BleTransport from './BleTransport';
53
57
  import timer from './utils/timer';
@@ -309,13 +313,8 @@ type IOBleErrorRemap = Error | BleError | null | undefined;
309
313
 
310
314
  function remapError(error: IOBleErrorRemap) {
311
315
  if (error instanceof BleError) {
312
- if (
313
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
314
- // @ts-expect-error
315
- error.iosErrorCode === BleATTErrorCode.UnlikelyError ||
316
- error.reason === 'Peer removed pairing information'
317
- ) {
318
- throw ERRORS.TypedError(HardwareErrorCode.BlePeerRemovedPairingInformation);
316
+ if (isNativeBleStaleBondError(error)) {
317
+ throw toBleStaleBondHardwareError(error);
319
318
  }
320
319
 
321
320
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -390,6 +389,13 @@ export default class ReactNativeBleTransport {
390
389
  /** Consecutive detections that failed while trusting sessionProtocols. */
391
390
  private protocolReprobeFailures: Map<string, number> = new Map();
392
391
 
392
+ /**
393
+ * Native encryption/pairing failures seen before Protocol V2 probe starts.
394
+ * Pro2/Neo GATT connect can succeed on a stale iOS bond; the CCCD write then
395
+ * fails with ATT 14/15. Remember it so detectProtocol fails immediately.
396
+ */
397
+ private staleBondErrors: Map<string, Error> = new Map();
398
+
393
399
  private protocolV2Assemblers: Map<string, ProtocolV2FrameAssembler> = new Map();
394
400
 
395
401
  private protocolV2FrameQueues: Map<string, Uint8Array[]> = new Map();
@@ -1046,7 +1052,7 @@ export default class ReactNativeBleTransport {
1046
1052
  this.attachDisconnectSubscription(currentTransport, currentTransport.device, uuid);
1047
1053
  return { uuid, protocolType };
1048
1054
  } catch (error) {
1049
- if ((error as { errorCode?: unknown })?.errorCode === HardwareErrorCode.BleDeviceBondError) {
1055
+ if (isBleStaleBondHardwareError(error)) {
1050
1056
  await this.disconnectUnlocked(uuid);
1051
1057
  } else {
1052
1058
  await this.releaseUnlocked(uuid, true);
@@ -1079,17 +1085,18 @@ export default class ReactNativeBleTransport {
1079
1085
  Log?.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
1080
1086
  return;
1081
1087
  }
1088
+ if (isNativeBleStaleBondError(error)) {
1089
+ this.rememberStaleBondError(uuid, toBleStaleBondHardwareError(error));
1090
+ return;
1091
+ }
1082
1092
  if (this.getActiveProtocol(uuid) === 'V2') {
1083
1093
  let errorCode:
1084
- | typeof HardwareErrorCode.BleDeviceBondError
1085
1094
  | typeof HardwareErrorCode.BleCharacteristicNotifyError
1086
1095
  | typeof HardwareErrorCode.BleCharacteristicNotifyChangeFailure
1087
1096
  | typeof HardwareErrorCode.BleTimeoutError =
1088
1097
  HardwareErrorCode.BleCharacteristicNotifyError;
1089
1098
  if (error.reason?.includes('The connection has timed out unexpectedly')) {
1090
1099
  errorCode = HardwareErrorCode.BleTimeoutError;
1091
- } else if (error.reason?.includes('Encryption is insufficient')) {
1092
- errorCode = HardwareErrorCode.BleDeviceBondError;
1093
1100
  } else if (
1094
1101
  error.reason?.includes('Cannot write client characteristic config descriptor') ||
1095
1102
  error.reason?.includes('Cannot find client characteristic config descriptor') ||
@@ -1104,16 +1111,12 @@ export default class ReactNativeBleTransport {
1104
1111
  }
1105
1112
  if (this.runPromise && this.runPromiseDeviceId === uuid) {
1106
1113
  let ERROR:
1107
- | typeof HardwareErrorCode.BleDeviceBondError
1108
1114
  | typeof HardwareErrorCode.BleCharacteristicNotifyError
1109
1115
  | typeof HardwareErrorCode.BleTimeoutError =
1110
1116
  HardwareErrorCode.BleCharacteristicNotifyError;
1111
1117
  if (error.reason?.includes('The connection has timed out unexpectedly')) {
1112
1118
  ERROR = HardwareErrorCode.BleTimeoutError;
1113
1119
  }
1114
- if (error.reason?.includes('Encryption is insufficient')) {
1115
- ERROR = HardwareErrorCode.BleDeviceBondError;
1116
- }
1117
1120
  if (
1118
1121
  error.reason?.includes('Cannot write client characteristic config descriptor') ||
1119
1122
  error.reason?.includes('Cannot find client characteristic config descriptor') || // pro firmware 2.3.0 upgrade
@@ -1248,6 +1251,7 @@ export default class ReactNativeBleTransport {
1248
1251
 
1249
1252
  this.deviceProtocol.delete(uuid);
1250
1253
  this.probingProtocols.delete(uuid);
1254
+ this.staleBondErrors.delete(uuid);
1251
1255
  // Confirmed protocol and caller hints stay in deviceProtocol / protocolHint.
1252
1256
  this.protocolV2Assemblers.get(uuid)?.reset();
1253
1257
  this.protocolV2Assemblers.delete(uuid);
@@ -1385,6 +1389,9 @@ export default class ReactNativeBleTransport {
1385
1389
  if (isWedgedWriteError(e)) {
1386
1390
  throw e;
1387
1391
  }
1392
+ if (isNativeBleStaleBondError(e) || isBleStaleBondHardwareError(e)) {
1393
+ throw toBleStaleBondHardwareError(e);
1394
+ }
1388
1395
  throw ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError);
1389
1396
  }
1390
1397
  }
@@ -1419,6 +1426,9 @@ export default class ReactNativeBleTransport {
1419
1426
  if (isWedgedWriteError(e)) {
1420
1427
  throw e;
1421
1428
  }
1429
+ if (isNativeBleStaleBondError(e) || isBleStaleBondHardwareError(e)) {
1430
+ throw toBleStaleBondHardwareError(e);
1431
+ }
1422
1432
  throw ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError);
1423
1433
  }
1424
1434
  }
@@ -1511,6 +1521,11 @@ export default class ReactNativeBleTransport {
1511
1521
  if (isWedgedWriteError(e)) {
1512
1522
  throw e;
1513
1523
  }
1524
+ if (isNativeBleStaleBondError(e) || isBleStaleBondHardwareError(e)) {
1525
+ const bondError = toBleStaleBondHardwareError(e);
1526
+ this.rememberStaleBondError(uuid, bondError);
1527
+ throw bondError;
1528
+ }
1514
1529
  if (e.errorCode === BleErrorCode.DeviceDisconnected) {
1515
1530
  throw ERRORS.TypedError(HardwareErrorCode.BleDeviceNotBonded);
1516
1531
  } else if (e.errorCode === BleErrorCode.OperationStartFailed) {
@@ -1620,6 +1635,7 @@ export default class ReactNativeBleTransport {
1620
1635
  }
1621
1636
  this.deviceProtocol.delete(session);
1622
1637
  this.probingProtocols.delete(session);
1638
+ this.staleBondErrors.delete(session);
1623
1639
  this.deviceProtocolHints.delete(session);
1624
1640
  this.sessionProtocols.delete(session);
1625
1641
  this.protocolReprobeFailures.delete(session);
@@ -1843,6 +1859,7 @@ export default class ReactNativeBleTransport {
1843
1859
  }
1844
1860
  this.deviceProtocol.delete(uuid);
1845
1861
  this.probingProtocols.delete(uuid);
1862
+ this.staleBondErrors.delete(uuid);
1846
1863
  this.protocolV2Assemblers.delete(uuid);
1847
1864
  this.resetProtocolV2Frames(uuid);
1848
1865
 
@@ -1939,6 +1956,7 @@ export default class ReactNativeBleTransport {
1939
1956
  }
1940
1957
  this.deviceProtocol.delete(uuid);
1941
1958
  this.probingProtocols.delete(uuid);
1959
+ this.staleBondErrors.delete(uuid);
1942
1960
  this.protocolV2Assemblers.delete(uuid);
1943
1961
  this.resetProtocolV2Frames(uuid);
1944
1962
 
@@ -1989,6 +2007,7 @@ export default class ReactNativeBleTransport {
1989
2007
  });
1990
2008
  this.deviceProtocol.clear();
1991
2009
  this.probingProtocols.clear();
2010
+ this.staleBondErrors.clear();
1992
2011
  this.sessionProtocols.clear();
1993
2012
  this.confirmedProtocolV2.clear();
1994
2013
  this.protocolReprobeFailures.clear();
@@ -2053,6 +2072,10 @@ export default class ReactNativeBleTransport {
2053
2072
  return expectedProtocol;
2054
2073
  }
2055
2074
 
2075
+ // iOS Classic/Touch/Pro skip probing above. Pro2/Neo still probe, and a stale
2076
+ // OS bond must not wait for Ping/GetFeatures timeouts after ATT 14/15.
2077
+ this.throwIfStaleBondError(uuid);
2078
+
2056
2079
  if (expectedProtocol === 'V1') {
2057
2080
  if (await this.probeProtocolV1(uuid)) {
2058
2081
  this.deviceProtocol.set(uuid, 'V1');
@@ -2209,7 +2232,7 @@ export default class ReactNativeBleTransport {
2209
2232
  Log?.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe failed:', error);
2210
2233
  // A wedged write already dropped the link, so probing another protocol on it
2211
2234
  // would only fail against a torn-down transport: surface the real cause.
2212
- if (isWedgedWriteError(error)) {
2235
+ if (isWedgedWriteError(error) || isBleStaleBondHardwareError(error)) {
2213
2236
  throw error;
2214
2237
  }
2215
2238
  return false;
@@ -2223,6 +2246,7 @@ export default class ReactNativeBleTransport {
2223
2246
 
2224
2247
  this.probingProtocols.set(uuid, 'V2');
2225
2248
  this.protocolV2Assemblers.get(uuid)?.reset();
2249
+ this.throwIfStaleBondError(uuid);
2226
2250
  const detected = await probeProtocolV2Helper({
2227
2251
  call: (name: string, data: Record<string, unknown>, options?: TransportCallOptions) =>
2228
2252
  this.callProtocolV2(uuid, name, data, options),
@@ -2233,6 +2257,7 @@ export default class ReactNativeBleTransport {
2233
2257
  this.protocolV2Assemblers.get(uuid)?.reset();
2234
2258
  this.resetProtocolV2Frames(uuid);
2235
2259
  },
2260
+ shouldRethrow: isBleStaleBondHardwareError,
2236
2261
  });
2237
2262
  if (!detected) {
2238
2263
  this.clearProbeProtocol(uuid, 'V2');
@@ -2301,6 +2326,21 @@ export default class ReactNativeBleTransport {
2301
2326
  }
2302
2327
  }
2303
2328
 
2329
+ private rememberStaleBondError(uuid: string, error: Error) {
2330
+ this.staleBondErrors.set(uuid, error);
2331
+ this.rejectProtocolV2Frames(uuid, error);
2332
+ if (this.runPromise && this.runPromiseDeviceId === uuid) {
2333
+ this.runPromise.reject(error);
2334
+ }
2335
+ }
2336
+
2337
+ private throwIfStaleBondError(uuid: string) {
2338
+ const error = this.staleBondErrors.get(uuid);
2339
+ if (error) {
2340
+ throw error;
2341
+ }
2342
+ }
2343
+
2304
2344
  private async readProtocolV2Frame(uuid: string) {
2305
2345
  const queuedFrame = this.getProtocolV2FrameQueue(uuid).shift();
2306
2346
  if (queuedFrame) {
@@ -2359,6 +2399,11 @@ export default class ReactNativeBleTransport {
2359
2399
  assertCurrentGeneration();
2360
2400
  return;
2361
2401
  } catch (error) {
2402
+ if (isNativeBleStaleBondError(error) || isBleStaleBondHardwareError(error)) {
2403
+ const bondError = toBleStaleBondHardwareError(error);
2404
+ this.rememberStaleBondError(uuid, bondError);
2405
+ throw bondError;
2406
+ }
2362
2407
  if (
2363
2408
  getFirmwareUploadWriteRetryType(error) !== 'congested' ||
2364
2409
  attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES