@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.
- package/dist/bleStaleBond.d.ts +5 -0
- package/dist/bleStaleBond.d.ts.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +94 -25
- package/package.json +5 -5
- package/src/__tests__/bleStaleBond.test.ts +31 -0
- package/src/__tests__/protocolV2Link.test.ts +34 -0
- package/src/bleStaleBond.ts +63 -0
- package/src/index.ts +62 -17
|
@@ -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;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,
|
|
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
|
|
379
|
-
error
|
|
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
|
|
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
|
|
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('
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
((
|
|
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 ((
|
|
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 ((
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
((
|
|
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
|
-
(
|
|
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
|
-
(
|
|
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.
|
|
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.
|
|
24
|
-
"@onekeyfe/hd-shared": "1.2.0-alpha.
|
|
25
|
-
"@onekeyfe/hd-transport": "1.2.0-alpha.
|
|
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": "
|
|
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
|
-
|
|
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
|
|
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
|