@onekeyfe/hd-transport-react-native 1.2.0-alpha.182 → 1.2.0-alpha.184
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 +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +103 -31
- package/package.json +5 -5
- package/src/__tests__/bleStaleBond.test.ts +31 -0
- package/src/__tests__/protocolV2Link.test.ts +50 -0
- package/src/bleStaleBond.ts +60 -0
- package/src/index.ts +76 -23
|
@@ -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;AAkBvC,eAAO,MAAM,yBAAyB,UAAW,OAAO,KAAG,OAe1D,CAAC;AAEF,eAAO,MAAM,2BAA2B,UAAW,OAAO,UAiBzD,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -134,6 +134,14 @@ declare class ReactNativeBleTransport {
|
|
|
134
134
|
private confirmedProtocolV2;
|
|
135
135
|
/** Consecutive detections that failed while trusting sessionProtocols. */
|
|
136
136
|
private protocolReprobeFailures;
|
|
137
|
+
/**
|
|
138
|
+
* Native encryption/pairing failures seen before Protocol V2 probe starts.
|
|
139
|
+
* Pro2/Neo GATT connect can succeed on a stale iOS bond; the CCCD write then
|
|
140
|
+
* fails with ATT 5/15. Remember it so detectProtocol fails immediately.
|
|
141
|
+
*/
|
|
142
|
+
private staleBondErrors;
|
|
143
|
+
/** Strict or previously confirmed V2 target while acquire installs notifications. */
|
|
144
|
+
private acquiringProtocolV2;
|
|
137
145
|
private protocolV2Assemblers;
|
|
138
146
|
private protocolV2FrameQueues;
|
|
139
147
|
private protocolV2FramePromises;
|
|
@@ -224,6 +232,8 @@ declare class ReactNativeBleTransport {
|
|
|
224
232
|
private resolveProtocolV2Frame;
|
|
225
233
|
private resetProtocolV2Frames;
|
|
226
234
|
private rejectProtocolV2Frames;
|
|
235
|
+
private rememberStaleBondError;
|
|
236
|
+
private throwIfStaleBondError;
|
|
227
237
|
private readProtocolV2Frame;
|
|
228
238
|
private writeProtocolV2Packet;
|
|
229
239
|
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,37 @@ 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_INSUFFICIENT_ENCRYPTION = 15;
|
|
199
|
+
const IOS_PEER_REMOVED_PAIRING_INFORMATION = 14;
|
|
200
|
+
const nativeErrorText = (error) => [error.reason, error.message]
|
|
201
|
+
.filter((value) => typeof value === 'string')
|
|
202
|
+
.join(' ');
|
|
203
|
+
const isNativeBleStaleBondError = (error) => {
|
|
204
|
+
if (!error || typeof error !== 'object') {
|
|
205
|
+
return typeof error === 'string' ? hdShared.isBleStaleBondErrorText(error) : false;
|
|
206
|
+
}
|
|
207
|
+
const nativeError = error;
|
|
208
|
+
if (nativeError.attErrorCode === ATT_INSUFFICIENT_AUTHENTICATION ||
|
|
209
|
+
nativeError.attErrorCode === ATT_INSUFFICIENT_ENCRYPTION ||
|
|
210
|
+
nativeError.iosErrorCode === IOS_PEER_REMOVED_PAIRING_INFORMATION) {
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
return hdShared.isBleStaleBondErrorText(nativeErrorText(nativeError));
|
|
214
|
+
};
|
|
215
|
+
const toBleStaleBondHardwareError = (error) => {
|
|
216
|
+
if (hdShared.isBleStaleBondHardwareError(error)) {
|
|
217
|
+
return error;
|
|
218
|
+
}
|
|
219
|
+
const nativeError = (error !== null && error !== void 0 ? error : {});
|
|
220
|
+
const text = nativeErrorText(nativeError);
|
|
221
|
+
const peerRemoved = nativeError.iosErrorCode === IOS_PEER_REMOVED_PAIRING_INFORMATION ||
|
|
222
|
+
text.includes('Peer removed pairing information');
|
|
223
|
+
return hdShared.ERRORS.TypedError(peerRemoved
|
|
224
|
+
? hdShared.HardwareErrorCode.BlePeerRemovedPairingInformation
|
|
225
|
+
: hdShared.HardwareErrorCode.BleDeviceBondError, text || undefined);
|
|
226
|
+
};
|
|
227
|
+
|
|
197
228
|
const isHeaderChunk = (chunk) => {
|
|
198
229
|
if (chunk.length < 9)
|
|
199
230
|
return false;
|
|
@@ -372,12 +403,11 @@ const requestNegotiatedMtu = (device, stage, attempt) => __awaiter(void 0, void
|
|
|
372
403
|
}
|
|
373
404
|
});
|
|
374
405
|
const resolveNegotiatedMtu = (device) => requestNegotiatedMtu(device, 'connected', 0);
|
|
375
|
-
function remapError(error) {
|
|
406
|
+
function remapError(error, mapProtocolV2StaleBond) {
|
|
376
407
|
var _a;
|
|
377
408
|
if (error instanceof reactNativeBlePlx.BleError) {
|
|
378
|
-
if (
|
|
379
|
-
error
|
|
380
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePeerRemovedPairingInformation);
|
|
409
|
+
if (mapProtocolV2StaleBond && isNativeBleStaleBondError(error)) {
|
|
410
|
+
throw toBleStaleBondHardwareError(error);
|
|
381
411
|
}
|
|
382
412
|
if ((error === null || error === void 0 ? void 0 : error.attErrorCode) === 22) {
|
|
383
413
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceBondError);
|
|
@@ -408,6 +438,8 @@ class ReactNativeBleTransport {
|
|
|
408
438
|
this.sessionProtocols = new Map();
|
|
409
439
|
this.confirmedProtocolV2 = new Set();
|
|
410
440
|
this.protocolReprobeFailures = new Map();
|
|
441
|
+
this.staleBondErrors = new Map();
|
|
442
|
+
this.acquiringProtocolV2 = new Set();
|
|
411
443
|
this.protocolV2Assemblers = new Map();
|
|
412
444
|
this.protocolV2FrameQueues = new Map();
|
|
413
445
|
this.protocolV2FramePromises = new Map();
|
|
@@ -803,6 +835,9 @@ class ReactNativeBleTransport {
|
|
|
803
835
|
var _a;
|
|
804
836
|
return __awaiter(this, void 0, void 0, function* () {
|
|
805
837
|
const { uuid, forceCleanRunPromise, expectedProtocol, skipProtocolProbe } = input;
|
|
838
|
+
const shouldMapProtocolV2StaleBond = expectedProtocol
|
|
839
|
+
? expectedProtocol === 'V2'
|
|
840
|
+
: this.confirmedProtocolV2.has(uuid);
|
|
806
841
|
const cachedTransport = transportCache[uuid];
|
|
807
842
|
if (skipProtocolProbe && !cachedTransport && this.blePlxManager) {
|
|
808
843
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] refresh uncached BLE connection for firmware install:', uuid);
|
|
@@ -895,7 +930,7 @@ class ReactNativeBleTransport {
|
|
|
895
930
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleAlreadyConnected);
|
|
896
931
|
}
|
|
897
932
|
else {
|
|
898
|
-
remapError(e);
|
|
933
|
+
remapError(e, shouldMapProtocolV2StaleBond);
|
|
899
934
|
}
|
|
900
935
|
}
|
|
901
936
|
}
|
|
@@ -929,7 +964,7 @@ class ReactNativeBleTransport {
|
|
|
929
964
|
}
|
|
930
965
|
}
|
|
931
966
|
else {
|
|
932
|
-
remapError(e);
|
|
967
|
+
remapError(e, shouldMapProtocolV2StaleBond);
|
|
933
968
|
}
|
|
934
969
|
}
|
|
935
970
|
}
|
|
@@ -943,11 +978,14 @@ class ReactNativeBleTransport {
|
|
|
943
978
|
if (protocolHint) {
|
|
944
979
|
this.deviceProtocolHints.set(uuid, protocolHint);
|
|
945
980
|
}
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
});
|
|
981
|
+
if (shouldMapProtocolV2StaleBond) {
|
|
982
|
+
this.acquiringProtocolV2.add(uuid);
|
|
983
|
+
}
|
|
950
984
|
try {
|
|
985
|
+
yield this.installTransportForAcquire(uuid, acquiredDevice, {
|
|
986
|
+
writeCharacteristic,
|
|
987
|
+
notifyCharacteristic,
|
|
988
|
+
});
|
|
951
989
|
if (skipProtocolProbe) {
|
|
952
990
|
if (!expectedProtocol) {
|
|
953
991
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'skipProtocolProbe requires an expected BLE protocol');
|
|
@@ -981,7 +1019,7 @@ class ReactNativeBleTransport {
|
|
|
981
1019
|
return { uuid, protocolType };
|
|
982
1020
|
}
|
|
983
1021
|
catch (error) {
|
|
984
|
-
if ((error
|
|
1022
|
+
if (hdShared.isBleStaleBondHardwareError(error)) {
|
|
985
1023
|
yield this.disconnectUnlocked(uuid);
|
|
986
1024
|
}
|
|
987
1025
|
else {
|
|
@@ -989,13 +1027,16 @@ class ReactNativeBleTransport {
|
|
|
989
1027
|
}
|
|
990
1028
|
throw error;
|
|
991
1029
|
}
|
|
1030
|
+
finally {
|
|
1031
|
+
this.acquiringProtocolV2.delete(uuid);
|
|
1032
|
+
}
|
|
992
1033
|
});
|
|
993
1034
|
}
|
|
994
1035
|
_monitorCharacteristic(characteristic, uuid, monitorToken, notifyTransactionId) {
|
|
995
1036
|
let bufferLength = 0;
|
|
996
1037
|
let buffer$1 = [];
|
|
997
1038
|
const subscription = characteristic.monitor((error, c) => {
|
|
998
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p
|
|
1039
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
999
1040
|
const isCurrentMonitor = this.monitorTokens.get(uuid) === monitorToken;
|
|
1000
1041
|
if (error) {
|
|
1001
1042
|
Log === null || Log === void 0 ? void 0 : Log.debug(`error monitor ${characteristic.uuid}, deviceId: ${characteristic.deviceID}: ${error}`);
|
|
@@ -1007,19 +1048,21 @@ class ReactNativeBleTransport {
|
|
|
1007
1048
|
Log === null || Log === void 0 ? void 0 : Log.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
|
|
1008
1049
|
return;
|
|
1009
1050
|
}
|
|
1051
|
+
if ((this.getActiveProtocol(uuid) === 'V2' || this.acquiringProtocolV2.has(uuid)) &&
|
|
1052
|
+
isNativeBleStaleBondError(error)) {
|
|
1053
|
+
this.rememberStaleBondError(uuid, toBleStaleBondHardwareError(error));
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1010
1056
|
if (this.getActiveProtocol(uuid) === 'V2') {
|
|
1011
1057
|
let errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
|
|
1012
1058
|
if ((_a = error.reason) === null || _a === void 0 ? void 0 : _a.includes('The connection has timed out unexpectedly')) {
|
|
1013
1059
|
errorCode = hdShared.HardwareErrorCode.BleTimeoutError;
|
|
1014
1060
|
}
|
|
1015
|
-
else if ((_b = error.reason) === null || _b === void 0 ? void 0 : _b.includes('
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
((
|
|
1020
|
-
((_e = error.reason) === null || _e === void 0 ? void 0 : _e.includes('The handle is invalid')) ||
|
|
1021
|
-
((_f = error.reason) === null || _f === void 0 ? void 0 : _f.includes('Writing is not permitted')) ||
|
|
1022
|
-
((_g = error.reason) === null || _g === void 0 ? void 0 : _g.includes('notify change failed for device'))) {
|
|
1061
|
+
else if (((_b = error.reason) === null || _b === void 0 ? void 0 : _b.includes('Cannot write client characteristic config descriptor')) ||
|
|
1062
|
+
((_c = error.reason) === null || _c === void 0 ? void 0 : _c.includes('Cannot find client characteristic config descriptor')) ||
|
|
1063
|
+
((_d = error.reason) === null || _d === void 0 ? void 0 : _d.includes('The handle is invalid')) ||
|
|
1064
|
+
((_e = error.reason) === null || _e === void 0 ? void 0 : _e.includes('Writing is not permitted')) ||
|
|
1065
|
+
((_f = error.reason) === null || _f === void 0 ? void 0 : _f.includes('notify change failed for device'))) {
|
|
1023
1066
|
errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure;
|
|
1024
1067
|
}
|
|
1025
1068
|
this.rejectProtocolV2Frames(uuid, hdShared.ERRORS.TypedError(errorCode));
|
|
@@ -1027,17 +1070,14 @@ class ReactNativeBleTransport {
|
|
|
1027
1070
|
}
|
|
1028
1071
|
if (this.runPromise && this.runPromiseDeviceId === uuid) {
|
|
1029
1072
|
let ERROR = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
|
|
1030
|
-
if ((
|
|
1073
|
+
if ((_g = error.reason) === null || _g === void 0 ? void 0 : _g.includes('The connection has timed out unexpectedly')) {
|
|
1031
1074
|
ERROR = hdShared.HardwareErrorCode.BleTimeoutError;
|
|
1032
1075
|
}
|
|
1033
|
-
if ((
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
((
|
|
1038
|
-
((_m = error.reason) === null || _m === void 0 ? void 0 : _m.includes('The handle is invalid')) ||
|
|
1039
|
-
((_o = error.reason) === null || _o === void 0 ? void 0 : _o.includes('Writing is not permitted')) ||
|
|
1040
|
-
((_p = error.reason) === null || _p === void 0 ? void 0 : _p.includes('notify change failed for device'))) {
|
|
1076
|
+
if (((_h = error.reason) === null || _h === void 0 ? void 0 : _h.includes('Cannot write client characteristic config descriptor')) ||
|
|
1077
|
+
((_j = error.reason) === null || _j === void 0 ? void 0 : _j.includes('Cannot find client characteristic config descriptor')) ||
|
|
1078
|
+
((_k = error.reason) === null || _k === void 0 ? void 0 : _k.includes('The handle is invalid')) ||
|
|
1079
|
+
((_l = error.reason) === null || _l === void 0 ? void 0 : _l.includes('Writing is not permitted')) ||
|
|
1080
|
+
((_m = error.reason) === null || _m === void 0 ? void 0 : _m.includes('notify change failed for device'))) {
|
|
1041
1081
|
const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure);
|
|
1042
1082
|
this.runPromise.reject(notifyError);
|
|
1043
1083
|
Log === null || Log === void 0 ? void 0 : Log.debug(`${hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure} ${error.message} ${error.reason}`);
|
|
@@ -1079,7 +1119,7 @@ class ReactNativeBleTransport {
|
|
|
1079
1119
|
bufferLength = 0;
|
|
1080
1120
|
buffer$1 = [];
|
|
1081
1121
|
if (this.runPromiseDeviceId === uuid) {
|
|
1082
|
-
(
|
|
1122
|
+
(_o = this.runPromise) === null || _o === void 0 ? void 0 : _o.resolve(value.toString('hex'));
|
|
1083
1123
|
}
|
|
1084
1124
|
}
|
|
1085
1125
|
}
|
|
@@ -1090,7 +1130,7 @@ class ReactNativeBleTransport {
|
|
|
1090
1130
|
this.rejectProtocolV2Frames(uuid, notifyError);
|
|
1091
1131
|
}
|
|
1092
1132
|
else if (this.runPromiseDeviceId === uuid) {
|
|
1093
|
-
(
|
|
1133
|
+
(_p = this.runPromise) === null || _p === void 0 ? void 0 : _p.reject(notifyError);
|
|
1094
1134
|
}
|
|
1095
1135
|
}
|
|
1096
1136
|
}, notifyTransactionId);
|
|
@@ -1144,6 +1184,8 @@ class ReactNativeBleTransport {
|
|
|
1144
1184
|
this.protocolV2HighVolumeLogSignatures.delete(uuid);
|
|
1145
1185
|
this.deviceProtocol.delete(uuid);
|
|
1146
1186
|
this.probingProtocols.delete(uuid);
|
|
1187
|
+
this.staleBondErrors.delete(uuid);
|
|
1188
|
+
this.acquiringProtocolV2.delete(uuid);
|
|
1147
1189
|
(_e = this.protocolV2Assemblers.get(uuid)) === null || _e === void 0 ? void 0 : _e.reset();
|
|
1148
1190
|
this.protocolV2Assemblers.delete(uuid);
|
|
1149
1191
|
this.resetProtocolV2Frames(uuid);
|
|
@@ -1429,6 +1471,7 @@ class ReactNativeBleTransport {
|
|
|
1429
1471
|
}
|
|
1430
1472
|
this.deviceProtocol.delete(session);
|
|
1431
1473
|
this.probingProtocols.delete(session);
|
|
1474
|
+
this.staleBondErrors.delete(session);
|
|
1432
1475
|
this.deviceProtocolHints.delete(session);
|
|
1433
1476
|
this.sessionProtocols.delete(session);
|
|
1434
1477
|
this.protocolReprobeFailures.delete(session);
|
|
@@ -1607,6 +1650,8 @@ class ReactNativeBleTransport {
|
|
|
1607
1650
|
}
|
|
1608
1651
|
this.deviceProtocol.delete(uuid);
|
|
1609
1652
|
this.probingProtocols.delete(uuid);
|
|
1653
|
+
this.staleBondErrors.delete(uuid);
|
|
1654
|
+
this.acquiringProtocolV2.delete(uuid);
|
|
1610
1655
|
this.protocolV2Assemblers.delete(uuid);
|
|
1611
1656
|
this.resetProtocolV2Frames(uuid);
|
|
1612
1657
|
if (timeouts >= BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD) {
|
|
@@ -1676,6 +1721,8 @@ class ReactNativeBleTransport {
|
|
|
1676
1721
|
}
|
|
1677
1722
|
this.deviceProtocol.delete(uuid);
|
|
1678
1723
|
this.probingProtocols.delete(uuid);
|
|
1724
|
+
this.staleBondErrors.delete(uuid);
|
|
1725
|
+
this.acquiringProtocolV2.delete(uuid);
|
|
1679
1726
|
this.protocolV2Assemblers.delete(uuid);
|
|
1680
1727
|
this.resetProtocolV2Frames(uuid);
|
|
1681
1728
|
if (timeouts >= BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD) {
|
|
@@ -1718,6 +1765,8 @@ class ReactNativeBleTransport {
|
|
|
1718
1765
|
});
|
|
1719
1766
|
this.deviceProtocol.clear();
|
|
1720
1767
|
this.probingProtocols.clear();
|
|
1768
|
+
this.staleBondErrors.clear();
|
|
1769
|
+
this.acquiringProtocolV2.clear();
|
|
1721
1770
|
this.sessionProtocols.clear();
|
|
1722
1771
|
this.confirmedProtocolV2.clear();
|
|
1723
1772
|
this.protocolReprobeFailures.clear();
|
|
@@ -1763,6 +1812,9 @@ class ReactNativeBleTransport {
|
|
|
1763
1812
|
});
|
|
1764
1813
|
return 'V1';
|
|
1765
1814
|
}
|
|
1815
|
+
if (expectedProtocol === 'V2' || this.acquiringProtocolV2.has(uuid)) {
|
|
1816
|
+
this.throwIfStaleBondError(uuid);
|
|
1817
|
+
}
|
|
1766
1818
|
if (expectedProtocol === 'V2') {
|
|
1767
1819
|
if (yield this.probeProtocolV2(uuid)) {
|
|
1768
1820
|
this.deviceProtocol.set(uuid, 'V2');
|
|
@@ -1893,6 +1945,7 @@ class ReactNativeBleTransport {
|
|
|
1893
1945
|
}
|
|
1894
1946
|
this.probingProtocols.set(uuid, 'V2');
|
|
1895
1947
|
(_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1948
|
+
this.throwIfStaleBondError(uuid);
|
|
1896
1949
|
const detected = yield transport.probeProtocolV2({
|
|
1897
1950
|
call: (name, data, options) => this.callProtocolV2(uuid, name, data, options),
|
|
1898
1951
|
timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT_MS,
|
|
@@ -1903,6 +1956,7 @@ class ReactNativeBleTransport {
|
|
|
1903
1956
|
(_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1904
1957
|
this.resetProtocolV2Frames(uuid);
|
|
1905
1958
|
},
|
|
1959
|
+
shouldRethrow: hdShared.isBleStaleBondHardwareError,
|
|
1906
1960
|
});
|
|
1907
1961
|
if (!detected) {
|
|
1908
1962
|
this.clearProbeProtocol(uuid, 'V2');
|
|
@@ -1963,6 +2017,19 @@ class ReactNativeBleTransport {
|
|
|
1963
2017
|
framePromise.reject(error);
|
|
1964
2018
|
}
|
|
1965
2019
|
}
|
|
2020
|
+
rememberStaleBondError(uuid, error) {
|
|
2021
|
+
this.staleBondErrors.set(uuid, error);
|
|
2022
|
+
this.rejectProtocolV2Frames(uuid, error);
|
|
2023
|
+
if (this.runPromise && this.runPromiseDeviceId === uuid) {
|
|
2024
|
+
this.runPromise.reject(error);
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
throwIfStaleBondError(uuid) {
|
|
2028
|
+
const error = this.staleBondErrors.get(uuid);
|
|
2029
|
+
if (error) {
|
|
2030
|
+
throw error;
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
1966
2033
|
readProtocolV2Frame(uuid) {
|
|
1967
2034
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1968
2035
|
const queuedFrame = this.getProtocolV2FrameQueue(uuid).shift();
|
|
@@ -2011,6 +2078,11 @@ class ReactNativeBleTransport {
|
|
|
2011
2078
|
return;
|
|
2012
2079
|
}
|
|
2013
2080
|
catch (error) {
|
|
2081
|
+
if (isNativeBleStaleBondError(error) || hdShared.isBleStaleBondHardwareError(error)) {
|
|
2082
|
+
const bondError = toBleStaleBondHardwareError(error);
|
|
2083
|
+
this.rememberStaleBondError(uuid, bondError);
|
|
2084
|
+
throw bondError;
|
|
2085
|
+
}
|
|
2014
2086
|
if (getFirmwareUploadWriteRetryType(error) !== 'congested' ||
|
|
2015
2087
|
attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
|
|
2016
2088
|
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.184",
|
|
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.184",
|
|
24
|
+
"@onekeyfe/hd-shared": "1.2.0-alpha.184",
|
|
25
|
+
"@onekeyfe/hd-transport": "1.2.0-alpha.184",
|
|
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": "833ce30a921a49ca0ed60cfc491062c26f5b90ae"
|
|
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
|
+
])('maps %j to %s', (nativeError, errorCode) => {
|
|
20
|
+
expect(isNativeBleStaleBondError(nativeError)).toBe(true);
|
|
21
|
+
expect(toBleStaleBondHardwareError(nativeError)).toMatchObject({ errorCode });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('does not treat the generic ATT unlikely error as a stale bond', () => {
|
|
25
|
+
expect(isNativeBleStaleBondError({ attErrorCode: 14, reason: 'Unlikely error' })).toBe(false);
|
|
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
|
};
|
|
@@ -405,6 +410,22 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
405
410
|
await transport.release(uuid, true);
|
|
406
411
|
});
|
|
407
412
|
|
|
413
|
+
test('keeps native stale-bond write mapping out of Protocol V1 calls', async () => {
|
|
414
|
+
const { transport, uuid, writeCharacteristic } = createV1Harness();
|
|
415
|
+
const nativeError = Object.assign(new Error('Encryption is insufficient'), {
|
|
416
|
+
attErrorCode: 15,
|
|
417
|
+
reason: 'Encryption is insufficient',
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
await transport.acquire({ uuid, expectedProtocol: 'V1' });
|
|
421
|
+
writeCharacteristic.writeWithResponse.mockRejectedValueOnce(nativeError);
|
|
422
|
+
|
|
423
|
+
await expect(transport.call(uuid, 'Initialize', {}, { timeoutMs: 50 })).rejects.toMatchObject({
|
|
424
|
+
errorCode: HardwareErrorCode.BleWriteCharacteristicError,
|
|
425
|
+
});
|
|
426
|
+
await transport.release(uuid, true);
|
|
427
|
+
});
|
|
428
|
+
|
|
408
429
|
test('detects Protocol V2 on iOS without using the BLE name as a protocol hint', async () => {
|
|
409
430
|
const { transport, uuid, device, sentSeqs, writeCharacteristic } = createHarness({
|
|
410
431
|
deviceName: 'Pro2 6E9E',
|
|
@@ -519,6 +540,35 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
519
540
|
}
|
|
520
541
|
);
|
|
521
542
|
|
|
543
|
+
test.each([
|
|
544
|
+
[
|
|
545
|
+
'Encryption is insufficient',
|
|
546
|
+
{ reason: 'Encryption is insufficient', attErrorCode: 15 },
|
|
547
|
+
HardwareErrorCode.BleDeviceBondError,
|
|
548
|
+
],
|
|
549
|
+
[
|
|
550
|
+
'Peer removed pairing information',
|
|
551
|
+
{ reason: 'Peer removed pairing information', iosErrorCode: 14 },
|
|
552
|
+
HardwareErrorCode.BlePeerRemovedPairingInformation,
|
|
553
|
+
],
|
|
554
|
+
] as const)(
|
|
555
|
+
'fails Protocol V2 acquire immediately on %s instead of waiting for Ping',
|
|
556
|
+
async (_label, nativeError, errorCode) => {
|
|
557
|
+
const { transport, uuid, device } = createHarness({
|
|
558
|
+
monitorError: Object.assign(new Error(nativeError.reason), nativeError),
|
|
559
|
+
});
|
|
560
|
+
const probe = jest.spyOn(transport as any, 'probeProtocolV2');
|
|
561
|
+
|
|
562
|
+
await expect(transport.acquire({ uuid, expectedProtocol: 'V2' })).rejects.toMatchObject({
|
|
563
|
+
errorCode,
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
expect(probe).not.toHaveBeenCalled();
|
|
567
|
+
expect(device.cancelConnection).toHaveBeenCalled();
|
|
568
|
+
expect(transport.getProtocolType(uuid)).toBeUndefined();
|
|
569
|
+
}
|
|
570
|
+
);
|
|
571
|
+
|
|
522
572
|
test.each(['ios', 'android'] as const)(
|
|
523
573
|
'reports a stale bond on %s when a previously confirmed Protocol V2 device stops responding',
|
|
524
574
|
async platform => {
|
|
@@ -0,0 +1,60 @@
|
|
|
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_INSUFFICIENT_ENCRYPTION = 15;
|
|
12
|
+
const IOS_PEER_REMOVED_PAIRING_INFORMATION = 14;
|
|
13
|
+
|
|
14
|
+
type NativeBleErrorFields = {
|
|
15
|
+
attErrorCode?: unknown;
|
|
16
|
+
iosErrorCode?: unknown;
|
|
17
|
+
reason?: unknown;
|
|
18
|
+
message?: unknown;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const nativeErrorText = (error: NativeBleErrorFields) =>
|
|
22
|
+
[error.reason, error.message]
|
|
23
|
+
.filter((value): value is string => typeof value === 'string')
|
|
24
|
+
.join(' ');
|
|
25
|
+
|
|
26
|
+
export const isNativeBleStaleBondError = (error: unknown): boolean => {
|
|
27
|
+
if (!error || typeof error !== 'object') {
|
|
28
|
+
return typeof error === 'string' ? isBleStaleBondErrorText(error) : false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const nativeError = error as NativeBleErrorFields;
|
|
32
|
+
if (
|
|
33
|
+
nativeError.attErrorCode === ATT_INSUFFICIENT_AUTHENTICATION ||
|
|
34
|
+
nativeError.attErrorCode === ATT_INSUFFICIENT_ENCRYPTION ||
|
|
35
|
+
nativeError.iosErrorCode === IOS_PEER_REMOVED_PAIRING_INFORMATION
|
|
36
|
+
) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return isBleStaleBondErrorText(nativeErrorText(nativeError));
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const toBleStaleBondHardwareError = (error: unknown) => {
|
|
44
|
+
if (isBleStaleBondHardwareError(error)) {
|
|
45
|
+
return error as Error;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const nativeError = (error ?? {}) as NativeBleErrorFields;
|
|
49
|
+
const text = nativeErrorText(nativeError);
|
|
50
|
+
const peerRemoved =
|
|
51
|
+
nativeError.iosErrorCode === IOS_PEER_REMOVED_PAIRING_INFORMATION ||
|
|
52
|
+
text.includes('Peer removed pairing information');
|
|
53
|
+
|
|
54
|
+
return ERRORS.TypedError(
|
|
55
|
+
peerRemoved
|
|
56
|
+
? HardwareErrorCode.BlePeerRemovedPairingInformation
|
|
57
|
+
: HardwareErrorCode.BleDeviceBondError,
|
|
58
|
+
text || undefined
|
|
59
|
+
);
|
|
60
|
+
};
|
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';
|
|
@@ -316,15 +320,10 @@ const resolveNegotiatedMtu = (device: Device) => requestNegotiatedMtu(device, 'c
|
|
|
316
320
|
|
|
317
321
|
type IOBleErrorRemap = Error | BleError | null | undefined;
|
|
318
322
|
|
|
319
|
-
function remapError(error: IOBleErrorRemap) {
|
|
323
|
+
function remapError(error: IOBleErrorRemap, mapProtocolV2StaleBond: boolean) {
|
|
320
324
|
if (error instanceof BleError) {
|
|
321
|
-
if (
|
|
322
|
-
|
|
323
|
-
// @ts-expect-error
|
|
324
|
-
error.iosErrorCode === BleATTErrorCode.UnlikelyError ||
|
|
325
|
-
error.reason === 'Peer removed pairing information'
|
|
326
|
-
) {
|
|
327
|
-
throw ERRORS.TypedError(HardwareErrorCode.BlePeerRemovedPairingInformation);
|
|
325
|
+
if (mapProtocolV2StaleBond && isNativeBleStaleBondError(error)) {
|
|
326
|
+
throw toBleStaleBondHardwareError(error);
|
|
328
327
|
}
|
|
329
328
|
|
|
330
329
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
@@ -399,6 +398,16 @@ export default class ReactNativeBleTransport {
|
|
|
399
398
|
/** Consecutive detections that failed while trusting sessionProtocols. */
|
|
400
399
|
private protocolReprobeFailures: Map<string, number> = new Map();
|
|
401
400
|
|
|
401
|
+
/**
|
|
402
|
+
* Native encryption/pairing failures seen before Protocol V2 probe starts.
|
|
403
|
+
* Pro2/Neo GATT connect can succeed on a stale iOS bond; the CCCD write then
|
|
404
|
+
* fails with ATT 5/15. Remember it so detectProtocol fails immediately.
|
|
405
|
+
*/
|
|
406
|
+
private staleBondErrors: Map<string, Error> = new Map();
|
|
407
|
+
|
|
408
|
+
/** Strict or previously confirmed V2 target while acquire installs notifications. */
|
|
409
|
+
private acquiringProtocolV2 = new Set<string>();
|
|
410
|
+
|
|
402
411
|
private protocolV2Assemblers: Map<string, ProtocolV2FrameAssembler> = new Map();
|
|
403
412
|
|
|
404
413
|
private protocolV2FrameQueues: Map<string, Uint8Array[]> = new Map();
|
|
@@ -886,6 +895,9 @@ export default class ReactNativeBleTransport {
|
|
|
886
895
|
|
|
887
896
|
private async acquireUnlocked(input: FirmwareInstallBleAcquireInput) {
|
|
888
897
|
const { uuid, forceCleanRunPromise, expectedProtocol, skipProtocolProbe } = input;
|
|
898
|
+
const shouldMapProtocolV2StaleBond = expectedProtocol
|
|
899
|
+
? expectedProtocol === 'V2'
|
|
900
|
+
: this.confirmedProtocolV2.has(uuid);
|
|
889
901
|
|
|
890
902
|
const cachedTransport = transportCache[uuid];
|
|
891
903
|
if (skipProtocolProbe && !cachedTransport && this.blePlxManager) {
|
|
@@ -1012,7 +1024,7 @@ export default class ReactNativeBleTransport {
|
|
|
1012
1024
|
Log?.debug('device already connected');
|
|
1013
1025
|
throw ERRORS.TypedError(HardwareErrorCode.BleAlreadyConnected);
|
|
1014
1026
|
} else {
|
|
1015
|
-
remapError(e);
|
|
1027
|
+
remapError(e, shouldMapProtocolV2StaleBond);
|
|
1016
1028
|
}
|
|
1017
1029
|
}
|
|
1018
1030
|
}
|
|
@@ -1056,7 +1068,7 @@ export default class ReactNativeBleTransport {
|
|
|
1056
1068
|
}
|
|
1057
1069
|
}
|
|
1058
1070
|
} else {
|
|
1059
|
-
remapError(e);
|
|
1071
|
+
remapError(e, shouldMapProtocolV2StaleBond);
|
|
1060
1072
|
}
|
|
1061
1073
|
}
|
|
1062
1074
|
}
|
|
@@ -1076,12 +1088,16 @@ export default class ReactNativeBleTransport {
|
|
|
1076
1088
|
this.deviceProtocolHints.set(uuid, protocolHint);
|
|
1077
1089
|
}
|
|
1078
1090
|
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
});
|
|
1091
|
+
if (shouldMapProtocolV2StaleBond) {
|
|
1092
|
+
this.acquiringProtocolV2.add(uuid);
|
|
1093
|
+
}
|
|
1083
1094
|
|
|
1084
1095
|
try {
|
|
1096
|
+
await this.installTransportForAcquire(uuid, acquiredDevice, {
|
|
1097
|
+
writeCharacteristic,
|
|
1098
|
+
notifyCharacteristic,
|
|
1099
|
+
});
|
|
1100
|
+
|
|
1085
1101
|
if (skipProtocolProbe) {
|
|
1086
1102
|
if (!expectedProtocol) {
|
|
1087
1103
|
throw ERRORS.TypedError(
|
|
@@ -1126,12 +1142,14 @@ export default class ReactNativeBleTransport {
|
|
|
1126
1142
|
this.attachDisconnectSubscription(currentTransport, currentTransport.device, uuid);
|
|
1127
1143
|
return { uuid, protocolType };
|
|
1128
1144
|
} catch (error) {
|
|
1129
|
-
if ((error
|
|
1145
|
+
if (isBleStaleBondHardwareError(error)) {
|
|
1130
1146
|
await this.disconnectUnlocked(uuid);
|
|
1131
1147
|
} else {
|
|
1132
1148
|
await this.releaseUnlocked(uuid, true);
|
|
1133
1149
|
}
|
|
1134
1150
|
throw error;
|
|
1151
|
+
} finally {
|
|
1152
|
+
this.acquiringProtocolV2.delete(uuid);
|
|
1135
1153
|
}
|
|
1136
1154
|
}
|
|
1137
1155
|
|
|
@@ -1159,17 +1177,21 @@ export default class ReactNativeBleTransport {
|
|
|
1159
1177
|
Log?.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
|
|
1160
1178
|
return;
|
|
1161
1179
|
}
|
|
1180
|
+
if (
|
|
1181
|
+
(this.getActiveProtocol(uuid) === 'V2' || this.acquiringProtocolV2.has(uuid)) &&
|
|
1182
|
+
isNativeBleStaleBondError(error)
|
|
1183
|
+
) {
|
|
1184
|
+
this.rememberStaleBondError(uuid, toBleStaleBondHardwareError(error));
|
|
1185
|
+
return;
|
|
1186
|
+
}
|
|
1162
1187
|
if (this.getActiveProtocol(uuid) === 'V2') {
|
|
1163
1188
|
let errorCode:
|
|
1164
|
-
| typeof HardwareErrorCode.BleDeviceBondError
|
|
1165
1189
|
| typeof HardwareErrorCode.BleCharacteristicNotifyError
|
|
1166
1190
|
| typeof HardwareErrorCode.BleCharacteristicNotifyChangeFailure
|
|
1167
1191
|
| typeof HardwareErrorCode.BleTimeoutError =
|
|
1168
1192
|
HardwareErrorCode.BleCharacteristicNotifyError;
|
|
1169
1193
|
if (error.reason?.includes('The connection has timed out unexpectedly')) {
|
|
1170
1194
|
errorCode = HardwareErrorCode.BleTimeoutError;
|
|
1171
|
-
} else if (error.reason?.includes('Encryption is insufficient')) {
|
|
1172
|
-
errorCode = HardwareErrorCode.BleDeviceBondError;
|
|
1173
1195
|
} else if (
|
|
1174
1196
|
error.reason?.includes('Cannot write client characteristic config descriptor') ||
|
|
1175
1197
|
error.reason?.includes('Cannot find client characteristic config descriptor') ||
|
|
@@ -1184,16 +1206,12 @@ export default class ReactNativeBleTransport {
|
|
|
1184
1206
|
}
|
|
1185
1207
|
if (this.runPromise && this.runPromiseDeviceId === uuid) {
|
|
1186
1208
|
let ERROR:
|
|
1187
|
-
| typeof HardwareErrorCode.BleDeviceBondError
|
|
1188
1209
|
| typeof HardwareErrorCode.BleCharacteristicNotifyError
|
|
1189
1210
|
| typeof HardwareErrorCode.BleTimeoutError =
|
|
1190
1211
|
HardwareErrorCode.BleCharacteristicNotifyError;
|
|
1191
1212
|
if (error.reason?.includes('The connection has timed out unexpectedly')) {
|
|
1192
1213
|
ERROR = HardwareErrorCode.BleTimeoutError;
|
|
1193
1214
|
}
|
|
1194
|
-
if (error.reason?.includes('Encryption is insufficient')) {
|
|
1195
|
-
ERROR = HardwareErrorCode.BleDeviceBondError;
|
|
1196
|
-
}
|
|
1197
1215
|
if (
|
|
1198
1216
|
error.reason?.includes('Cannot write client characteristic config descriptor') ||
|
|
1199
1217
|
error.reason?.includes('Cannot find client characteristic config descriptor') || // pro firmware 2.3.0 upgrade
|
|
@@ -1328,6 +1346,8 @@ export default class ReactNativeBleTransport {
|
|
|
1328
1346
|
|
|
1329
1347
|
this.deviceProtocol.delete(uuid);
|
|
1330
1348
|
this.probingProtocols.delete(uuid);
|
|
1349
|
+
this.staleBondErrors.delete(uuid);
|
|
1350
|
+
this.acquiringProtocolV2.delete(uuid);
|
|
1331
1351
|
// Confirmed protocol and caller hints stay in deviceProtocol / protocolHint.
|
|
1332
1352
|
this.protocolV2Assemblers.get(uuid)?.reset();
|
|
1333
1353
|
this.protocolV2Assemblers.delete(uuid);
|
|
@@ -1700,6 +1720,7 @@ export default class ReactNativeBleTransport {
|
|
|
1700
1720
|
}
|
|
1701
1721
|
this.deviceProtocol.delete(session);
|
|
1702
1722
|
this.probingProtocols.delete(session);
|
|
1723
|
+
this.staleBondErrors.delete(session);
|
|
1703
1724
|
this.deviceProtocolHints.delete(session);
|
|
1704
1725
|
this.sessionProtocols.delete(session);
|
|
1705
1726
|
this.protocolReprobeFailures.delete(session);
|
|
@@ -1923,6 +1944,8 @@ export default class ReactNativeBleTransport {
|
|
|
1923
1944
|
}
|
|
1924
1945
|
this.deviceProtocol.delete(uuid);
|
|
1925
1946
|
this.probingProtocols.delete(uuid);
|
|
1947
|
+
this.staleBondErrors.delete(uuid);
|
|
1948
|
+
this.acquiringProtocolV2.delete(uuid);
|
|
1926
1949
|
this.protocolV2Assemblers.delete(uuid);
|
|
1927
1950
|
this.resetProtocolV2Frames(uuid);
|
|
1928
1951
|
|
|
@@ -2019,6 +2042,8 @@ export default class ReactNativeBleTransport {
|
|
|
2019
2042
|
}
|
|
2020
2043
|
this.deviceProtocol.delete(uuid);
|
|
2021
2044
|
this.probingProtocols.delete(uuid);
|
|
2045
|
+
this.staleBondErrors.delete(uuid);
|
|
2046
|
+
this.acquiringProtocolV2.delete(uuid);
|
|
2022
2047
|
this.protocolV2Assemblers.delete(uuid);
|
|
2023
2048
|
this.resetProtocolV2Frames(uuid);
|
|
2024
2049
|
|
|
@@ -2069,6 +2094,8 @@ export default class ReactNativeBleTransport {
|
|
|
2069
2094
|
});
|
|
2070
2095
|
this.deviceProtocol.clear();
|
|
2071
2096
|
this.probingProtocols.clear();
|
|
2097
|
+
this.staleBondErrors.clear();
|
|
2098
|
+
this.acquiringProtocolV2.clear();
|
|
2072
2099
|
this.sessionProtocols.clear();
|
|
2073
2100
|
this.confirmedProtocolV2.clear();
|
|
2074
2101
|
this.protocolReprobeFailures.clear();
|
|
@@ -2140,6 +2167,10 @@ export default class ReactNativeBleTransport {
|
|
|
2140
2167
|
return 'V1';
|
|
2141
2168
|
}
|
|
2142
2169
|
|
|
2170
|
+
if (expectedProtocol === 'V2' || this.acquiringProtocolV2.has(uuid)) {
|
|
2171
|
+
this.throwIfStaleBondError(uuid);
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2143
2174
|
if (expectedProtocol === 'V2') {
|
|
2144
2175
|
if (await this.probeProtocolV2(uuid)) {
|
|
2145
2176
|
this.deviceProtocol.set(uuid, 'V2');
|
|
@@ -2296,6 +2327,7 @@ export default class ReactNativeBleTransport {
|
|
|
2296
2327
|
|
|
2297
2328
|
this.probingProtocols.set(uuid, 'V2');
|
|
2298
2329
|
this.protocolV2Assemblers.get(uuid)?.reset();
|
|
2330
|
+
this.throwIfStaleBondError(uuid);
|
|
2299
2331
|
const detected = await probeProtocolV2Helper({
|
|
2300
2332
|
call: (name: string, data: Record<string, unknown>, options?: TransportCallOptions) =>
|
|
2301
2333
|
this.callProtocolV2(uuid, name, data, options),
|
|
@@ -2306,6 +2338,7 @@ export default class ReactNativeBleTransport {
|
|
|
2306
2338
|
this.protocolV2Assemblers.get(uuid)?.reset();
|
|
2307
2339
|
this.resetProtocolV2Frames(uuid);
|
|
2308
2340
|
},
|
|
2341
|
+
shouldRethrow: isBleStaleBondHardwareError,
|
|
2309
2342
|
});
|
|
2310
2343
|
if (!detected) {
|
|
2311
2344
|
this.clearProbeProtocol(uuid, 'V2');
|
|
@@ -2374,6 +2407,21 @@ export default class ReactNativeBleTransport {
|
|
|
2374
2407
|
}
|
|
2375
2408
|
}
|
|
2376
2409
|
|
|
2410
|
+
private rememberStaleBondError(uuid: string, error: Error) {
|
|
2411
|
+
this.staleBondErrors.set(uuid, error);
|
|
2412
|
+
this.rejectProtocolV2Frames(uuid, error);
|
|
2413
|
+
if (this.runPromise && this.runPromiseDeviceId === uuid) {
|
|
2414
|
+
this.runPromise.reject(error);
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
private throwIfStaleBondError(uuid: string) {
|
|
2419
|
+
const error = this.staleBondErrors.get(uuid);
|
|
2420
|
+
if (error) {
|
|
2421
|
+
throw error;
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
|
|
2377
2425
|
private async readProtocolV2Frame(uuid: string) {
|
|
2378
2426
|
const queuedFrame = this.getProtocolV2FrameQueue(uuid).shift();
|
|
2379
2427
|
if (queuedFrame) {
|
|
@@ -2432,6 +2480,11 @@ export default class ReactNativeBleTransport {
|
|
|
2432
2480
|
assertCurrentGeneration();
|
|
2433
2481
|
return;
|
|
2434
2482
|
} catch (error) {
|
|
2483
|
+
if (isNativeBleStaleBondError(error) || isBleStaleBondHardwareError(error)) {
|
|
2484
|
+
const bondError = toBleStaleBondHardwareError(error);
|
|
2485
|
+
this.rememberStaleBondError(uuid, bondError);
|
|
2486
|
+
throw bondError;
|
|
2487
|
+
}
|
|
2435
2488
|
if (
|
|
2436
2489
|
getFirmwareUploadWriteRetryType(error) !== 'congested' ||
|
|
2437
2490
|
attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES
|