@onekeyfe/hd-transport-react-native 1.2.2-alpha.0 → 1.2.2-alpha.100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +62 -168
- package/package.json +5 -5
- package/src/__tests__/protocolV2Link.test.ts +3 -140
- package/src/index.ts +68 -197
- package/dist/bleStaleBond.d.ts +0 -5
- package/dist/bleStaleBond.d.ts.map +0 -1
- package/src/__tests__/bleStaleBond.test.ts +0 -39
- package/src/bleStaleBond.ts +0 -61
package/dist/index.d.ts
CHANGED
|
@@ -38,14 +38,6 @@ type BleAcquireInput = {
|
|
|
38
38
|
protocolHint?: ProtocolType;
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
-
type FirmwareInstallBleAcquireInput = BleAcquireInput & {
|
|
42
|
-
/**
|
|
43
|
-
* Reuse the already-verified protocol after an expected firmware-install
|
|
44
|
-
* disconnect. The install loader accepts status requests but may not answer
|
|
45
|
-
* the generic protocol probe used by a normal acquire.
|
|
46
|
-
*/
|
|
47
|
-
skipProtocolProbe?: boolean;
|
|
48
|
-
};
|
|
49
41
|
type FirmwareUploadWriteRetryType = 'congested';
|
|
50
42
|
type ResolvedBleCharacteristics = {
|
|
51
43
|
writeCharacteristic: Characteristic;
|
|
@@ -134,14 +126,6 @@ declare class ReactNativeBleTransport {
|
|
|
134
126
|
private confirmedProtocolV2;
|
|
135
127
|
/** Consecutive detections that failed while trusting sessionProtocols. */
|
|
136
128
|
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;
|
|
145
129
|
private protocolV2Assemblers;
|
|
146
130
|
private protocolV2FrameQueues;
|
|
147
131
|
private protocolV2FramePromises;
|
|
@@ -171,7 +155,7 @@ declare class ReactNativeBleTransport {
|
|
|
171
155
|
*/
|
|
172
156
|
enumerate(): Promise<IOneKeyDevice[]>;
|
|
173
157
|
private installTransportForAcquire;
|
|
174
|
-
acquire(input:
|
|
158
|
+
acquire(input: BleAcquireInput): Promise<{
|
|
175
159
|
uuid: string;
|
|
176
160
|
protocolType: ProtocolType;
|
|
177
161
|
}>;
|
|
@@ -232,8 +216,6 @@ declare class ReactNativeBleTransport {
|
|
|
232
216
|
private resolveProtocolV2Frame;
|
|
233
217
|
private resetProtocolV2Frames;
|
|
234
218
|
private rejectProtocolV2Frames;
|
|
235
|
-
private rememberStaleBondError;
|
|
236
|
-
private throwIfStaleBondError;
|
|
237
219
|
private readProtocolV2Frame;
|
|
238
220
|
private writeProtocolV2Packet;
|
|
239
221
|
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,38 +194,6 @@ 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 normalizedText = text.toLowerCase();
|
|
222
|
-
const peerRemoved = nativeError.iosErrorCode === IOS_PEER_REMOVED_PAIRING_INFORMATION ||
|
|
223
|
-
normalizedText.includes('peer removed pairing information');
|
|
224
|
-
return hdShared.ERRORS.TypedError(peerRemoved
|
|
225
|
-
? hdShared.HardwareErrorCode.BlePeerRemovedPairingInformation
|
|
226
|
-
: hdShared.HardwareErrorCode.BleDeviceBondError, text || undefined);
|
|
227
|
-
};
|
|
228
|
-
|
|
229
197
|
const isHeaderChunk = (chunk) => {
|
|
230
198
|
if (chunk.length < 9)
|
|
231
199
|
return false;
|
|
@@ -404,11 +372,12 @@ const requestNegotiatedMtu = (device, stage, attempt) => __awaiter(void 0, void
|
|
|
404
372
|
}
|
|
405
373
|
});
|
|
406
374
|
const resolveNegotiatedMtu = (device) => requestNegotiatedMtu(device, 'connected', 0);
|
|
407
|
-
function remapError(error
|
|
375
|
+
function remapError(error) {
|
|
408
376
|
var _a;
|
|
409
377
|
if (error instanceof reactNativeBlePlx.BleError) {
|
|
410
|
-
if (
|
|
411
|
-
|
|
378
|
+
if (error.iosErrorCode === reactNativeBlePlx.BleATTErrorCode.UnlikelyError ||
|
|
379
|
+
error.reason === 'Peer removed pairing information') {
|
|
380
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePeerRemovedPairingInformation);
|
|
412
381
|
}
|
|
413
382
|
if ((error === null || error === void 0 ? void 0 : error.attErrorCode) === 22) {
|
|
414
383
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceBondError);
|
|
@@ -439,8 +408,6 @@ class ReactNativeBleTransport {
|
|
|
439
408
|
this.sessionProtocols = new Map();
|
|
440
409
|
this.confirmedProtocolV2 = new Set();
|
|
441
410
|
this.protocolReprobeFailures = new Map();
|
|
442
|
-
this.staleBondErrors = new Map();
|
|
443
|
-
this.acquiringProtocolV2 = new Set();
|
|
444
411
|
this.protocolV2Assemblers = new Map();
|
|
445
412
|
this.protocolV2FrameQueues = new Map();
|
|
446
413
|
this.protocolV2FramePromises = new Map();
|
|
@@ -835,46 +802,19 @@ class ReactNativeBleTransport {
|
|
|
835
802
|
acquireUnlocked(input) {
|
|
836
803
|
var _a;
|
|
837
804
|
return __awaiter(this, void 0, void 0, function* () {
|
|
838
|
-
const { uuid, forceCleanRunPromise, expectedProtocol
|
|
839
|
-
const shouldMapProtocolV2StaleBond = expectedProtocol
|
|
840
|
-
? expectedProtocol === 'V2'
|
|
841
|
-
: this.confirmedProtocolV2.has(uuid);
|
|
805
|
+
const { uuid, forceCleanRunPromise, expectedProtocol } = input;
|
|
842
806
|
const cachedTransport = transportCache[uuid];
|
|
843
|
-
if (skipProtocolProbe && !cachedTransport && this.blePlxManager) {
|
|
844
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] refresh uncached BLE connection for firmware install:', uuid);
|
|
845
|
-
const manager = this.blePlxManager;
|
|
846
|
-
yield this.runNativeTeardown(uuid, manager, () => __awaiter(this, void 0, void 0, function* () {
|
|
847
|
-
yield this.runBestEffortNativeOperation('firmware install reconnect: cancel uncached device connection', () => manager.cancelDeviceConnection(uuid));
|
|
848
|
-
}));
|
|
849
|
-
}
|
|
850
807
|
if (cachedTransport) {
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
operations.push(this.runBestEffortNativeOperation('firmware install reconnect: cancel device connection', () => manager.cancelDeviceConnection(uuid)));
|
|
859
|
-
}
|
|
860
|
-
operations.push(this.runBestEffortNativeOperation('firmware install reconnect: device cancel connection', () => cachedTransport.device.cancelConnection()));
|
|
861
|
-
yield Promise.all(operations);
|
|
862
|
-
}));
|
|
863
|
-
}
|
|
864
|
-
else {
|
|
865
|
-
const cachedProtocol = this.deviceProtocol.get(uuid);
|
|
866
|
-
const isCachedDeviceConnected = yield cachedTransport.device
|
|
867
|
-
.isConnected()
|
|
868
|
-
.catch(() => false);
|
|
869
|
-
if (isCachedDeviceConnected &&
|
|
870
|
-
cachedProtocol &&
|
|
871
|
-
(!expectedProtocol || cachedProtocol === expectedProtocol)) {
|
|
872
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] reuse cached BLE transport:', uuid, cachedProtocol);
|
|
873
|
-
return { uuid, protocolType: cachedProtocol };
|
|
874
|
-
}
|
|
875
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('transport not reusable, will release: ', uuid);
|
|
876
|
-
yield this.releaseUnlocked(uuid, true);
|
|
808
|
+
const cachedProtocol = this.deviceProtocol.get(uuid);
|
|
809
|
+
const isCachedDeviceConnected = yield cachedTransport.device.isConnected().catch(() => false);
|
|
810
|
+
if (isCachedDeviceConnected &&
|
|
811
|
+
cachedProtocol &&
|
|
812
|
+
(!expectedProtocol || cachedProtocol === expectedProtocol)) {
|
|
813
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] reuse cached BLE transport:', uuid, cachedProtocol);
|
|
814
|
+
return { uuid, protocolType: cachedProtocol };
|
|
877
815
|
}
|
|
816
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('transport not reusable, will release: ', uuid);
|
|
817
|
+
yield this.releaseUnlocked(uuid, true);
|
|
878
818
|
}
|
|
879
819
|
let device = null;
|
|
880
820
|
if (forceCleanRunPromise && this.runPromise) {
|
|
@@ -931,7 +871,7 @@ class ReactNativeBleTransport {
|
|
|
931
871
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleAlreadyConnected);
|
|
932
872
|
}
|
|
933
873
|
else {
|
|
934
|
-
remapError(e
|
|
874
|
+
remapError(e);
|
|
935
875
|
}
|
|
936
876
|
}
|
|
937
877
|
}
|
|
@@ -965,7 +905,7 @@ class ReactNativeBleTransport {
|
|
|
965
905
|
}
|
|
966
906
|
}
|
|
967
907
|
else {
|
|
968
|
-
remapError(e
|
|
908
|
+
remapError(e);
|
|
969
909
|
}
|
|
970
910
|
}
|
|
971
911
|
}
|
|
@@ -979,38 +919,11 @@ class ReactNativeBleTransport {
|
|
|
979
919
|
if (protocolHint) {
|
|
980
920
|
this.deviceProtocolHints.set(uuid, protocolHint);
|
|
981
921
|
}
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
922
|
+
yield this.installTransportForAcquire(uuid, acquiredDevice, {
|
|
923
|
+
writeCharacteristic,
|
|
924
|
+
notifyCharacteristic,
|
|
925
|
+
});
|
|
985
926
|
try {
|
|
986
|
-
yield this.installTransportForAcquire(uuid, acquiredDevice, {
|
|
987
|
-
writeCharacteristic,
|
|
988
|
-
notifyCharacteristic,
|
|
989
|
-
});
|
|
990
|
-
if (skipProtocolProbe) {
|
|
991
|
-
if (!expectedProtocol) {
|
|
992
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'skipProtocolProbe requires an expected BLE protocol');
|
|
993
|
-
}
|
|
994
|
-
const hasConfirmedProtocol = this.sessionProtocols.get(uuid) === expectedProtocol ||
|
|
995
|
-
(expectedProtocol === 'V2' && this.confirmedProtocolV2.has(uuid));
|
|
996
|
-
if (!hasConfirmedProtocol) {
|
|
997
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'skipProtocolProbe requires a previously confirmed protocol for this BLE endpoint');
|
|
998
|
-
}
|
|
999
|
-
this.deviceProtocol.set(uuid, expectedProtocol);
|
|
1000
|
-
this.sessionProtocols.set(uuid, expectedProtocol);
|
|
1001
|
-
this.protocolReprobeFailures.delete(uuid);
|
|
1002
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol selected without probe', {
|
|
1003
|
-
deviceId: uuid,
|
|
1004
|
-
protocol: expectedProtocol,
|
|
1005
|
-
source: 'firmware-install-reconnect',
|
|
1006
|
-
});
|
|
1007
|
-
const currentTransport = transportCache[uuid];
|
|
1008
|
-
if (!currentTransport) {
|
|
1009
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
|
|
1010
|
-
}
|
|
1011
|
-
this.attachDisconnectSubscription(currentTransport, currentTransport.device, uuid);
|
|
1012
|
-
return { uuid, protocolType: expectedProtocol };
|
|
1013
|
-
}
|
|
1014
927
|
const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint, () => __awaiter(this, void 0, void 0, function* () {
|
|
1015
928
|
yield this.installTransportForAcquire(uuid, acquiredDevice);
|
|
1016
929
|
}));
|
|
@@ -1022,7 +935,7 @@ class ReactNativeBleTransport {
|
|
|
1022
935
|
return { uuid, protocolType };
|
|
1023
936
|
}
|
|
1024
937
|
catch (error) {
|
|
1025
|
-
if (
|
|
938
|
+
if ((error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleDeviceBondError) {
|
|
1026
939
|
yield this.disconnectUnlocked(uuid);
|
|
1027
940
|
}
|
|
1028
941
|
else {
|
|
@@ -1030,16 +943,13 @@ class ReactNativeBleTransport {
|
|
|
1030
943
|
}
|
|
1031
944
|
throw error;
|
|
1032
945
|
}
|
|
1033
|
-
finally {
|
|
1034
|
-
this.acquiringProtocolV2.delete(uuid);
|
|
1035
|
-
}
|
|
1036
946
|
});
|
|
1037
947
|
}
|
|
1038
948
|
_monitorCharacteristic(characteristic, uuid, monitorToken, notifyTransactionId) {
|
|
1039
949
|
let bufferLength = 0;
|
|
1040
950
|
let buffer$1 = [];
|
|
1041
951
|
const subscription = characteristic.monitor((error, c) => {
|
|
1042
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
952
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
|
|
1043
953
|
const isCurrentMonitor = this.monitorTokens.get(uuid) === monitorToken;
|
|
1044
954
|
if (error) {
|
|
1045
955
|
Log === null || Log === void 0 ? void 0 : Log.debug(`error monitor ${characteristic.uuid}, deviceId: ${characteristic.deviceID}: ${error}`);
|
|
@@ -1051,21 +961,19 @@ class ReactNativeBleTransport {
|
|
|
1051
961
|
Log === null || Log === void 0 ? void 0 : Log.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
|
|
1052
962
|
return;
|
|
1053
963
|
}
|
|
1054
|
-
if ((this.getActiveProtocol(uuid) === 'V2' || this.acquiringProtocolV2.has(uuid)) &&
|
|
1055
|
-
isNativeBleStaleBondError(error)) {
|
|
1056
|
-
this.rememberStaleBondError(uuid, toBleStaleBondHardwareError(error));
|
|
1057
|
-
return;
|
|
1058
|
-
}
|
|
1059
964
|
if (this.getActiveProtocol(uuid) === 'V2') {
|
|
1060
965
|
let errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
|
|
1061
966
|
if ((_a = error.reason) === null || _a === void 0 ? void 0 : _a.includes('The connection has timed out unexpectedly')) {
|
|
1062
967
|
errorCode = hdShared.HardwareErrorCode.BleTimeoutError;
|
|
1063
968
|
}
|
|
1064
|
-
else if ((
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
((
|
|
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'))) {
|
|
1069
977
|
errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure;
|
|
1070
978
|
}
|
|
1071
979
|
this.rejectProtocolV2Frames(uuid, hdShared.ERRORS.TypedError(errorCode));
|
|
@@ -1073,14 +981,17 @@ class ReactNativeBleTransport {
|
|
|
1073
981
|
}
|
|
1074
982
|
if (this.runPromise && this.runPromiseDeviceId === uuid) {
|
|
1075
983
|
let ERROR = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
|
|
1076
|
-
if ((
|
|
984
|
+
if ((_h = error.reason) === null || _h === void 0 ? void 0 : _h.includes('The connection has timed out unexpectedly')) {
|
|
1077
985
|
ERROR = hdShared.HardwareErrorCode.BleTimeoutError;
|
|
1078
986
|
}
|
|
1079
|
-
if ((
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
((
|
|
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'))) {
|
|
1084
995
|
const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure);
|
|
1085
996
|
this.runPromise.reject(notifyError);
|
|
1086
997
|
Log === null || Log === void 0 ? void 0 : Log.debug(`${hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure} ${error.message} ${error.reason}`);
|
|
@@ -1122,7 +1033,7 @@ class ReactNativeBleTransport {
|
|
|
1122
1033
|
bufferLength = 0;
|
|
1123
1034
|
buffer$1 = [];
|
|
1124
1035
|
if (this.runPromiseDeviceId === uuid) {
|
|
1125
|
-
(
|
|
1036
|
+
(_q = this.runPromise) === null || _q === void 0 ? void 0 : _q.resolve(value.toString('hex'));
|
|
1126
1037
|
}
|
|
1127
1038
|
}
|
|
1128
1039
|
}
|
|
@@ -1133,7 +1044,7 @@ class ReactNativeBleTransport {
|
|
|
1133
1044
|
this.rejectProtocolV2Frames(uuid, notifyError);
|
|
1134
1045
|
}
|
|
1135
1046
|
else if (this.runPromiseDeviceId === uuid) {
|
|
1136
|
-
(
|
|
1047
|
+
(_r = this.runPromise) === null || _r === void 0 ? void 0 : _r.reject(notifyError);
|
|
1137
1048
|
}
|
|
1138
1049
|
}
|
|
1139
1050
|
}, notifyTransactionId);
|
|
@@ -1187,8 +1098,6 @@ class ReactNativeBleTransport {
|
|
|
1187
1098
|
this.protocolV2HighVolumeLogSignatures.delete(uuid);
|
|
1188
1099
|
this.deviceProtocol.delete(uuid);
|
|
1189
1100
|
this.probingProtocols.delete(uuid);
|
|
1190
|
-
this.staleBondErrors.delete(uuid);
|
|
1191
|
-
this.acquiringProtocolV2.delete(uuid);
|
|
1192
1101
|
(_e = this.protocolV2Assemblers.get(uuid)) === null || _e === void 0 ? void 0 : _e.reset();
|
|
1193
1102
|
this.protocolV2Assemblers.delete(uuid);
|
|
1194
1103
|
this.resetProtocolV2Frames(uuid);
|
|
@@ -1474,7 +1383,6 @@ class ReactNativeBleTransport {
|
|
|
1474
1383
|
}
|
|
1475
1384
|
this.deviceProtocol.delete(session);
|
|
1476
1385
|
this.probingProtocols.delete(session);
|
|
1477
|
-
this.staleBondErrors.delete(session);
|
|
1478
1386
|
this.deviceProtocolHints.delete(session);
|
|
1479
1387
|
this.sessionProtocols.delete(session);
|
|
1480
1388
|
this.protocolReprobeFailures.delete(session);
|
|
@@ -1653,8 +1561,6 @@ class ReactNativeBleTransport {
|
|
|
1653
1561
|
}
|
|
1654
1562
|
this.deviceProtocol.delete(uuid);
|
|
1655
1563
|
this.probingProtocols.delete(uuid);
|
|
1656
|
-
this.staleBondErrors.delete(uuid);
|
|
1657
|
-
this.acquiringProtocolV2.delete(uuid);
|
|
1658
1564
|
this.protocolV2Assemblers.delete(uuid);
|
|
1659
1565
|
this.resetProtocolV2Frames(uuid);
|
|
1660
1566
|
if (timeouts >= BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD) {
|
|
@@ -1724,8 +1630,6 @@ class ReactNativeBleTransport {
|
|
|
1724
1630
|
}
|
|
1725
1631
|
this.deviceProtocol.delete(uuid);
|
|
1726
1632
|
this.probingProtocols.delete(uuid);
|
|
1727
|
-
this.staleBondErrors.delete(uuid);
|
|
1728
|
-
this.acquiringProtocolV2.delete(uuid);
|
|
1729
1633
|
this.protocolV2Assemblers.delete(uuid);
|
|
1730
1634
|
this.resetProtocolV2Frames(uuid);
|
|
1731
1635
|
if (timeouts >= BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD) {
|
|
@@ -1768,9 +1672,8 @@ class ReactNativeBleTransport {
|
|
|
1768
1672
|
});
|
|
1769
1673
|
this.deviceProtocol.clear();
|
|
1770
1674
|
this.probingProtocols.clear();
|
|
1771
|
-
this.staleBondErrors.clear();
|
|
1772
|
-
this.acquiringProtocolV2.clear();
|
|
1773
1675
|
this.sessionProtocols.clear();
|
|
1676
|
+
this.confirmedProtocolV2.clear();
|
|
1774
1677
|
this.protocolReprobeFailures.clear();
|
|
1775
1678
|
this.writeTimeoutCounts.clear();
|
|
1776
1679
|
this.connectionSetupTimeoutCounts.clear();
|
|
@@ -1783,8 +1686,9 @@ class ReactNativeBleTransport {
|
|
|
1783
1686
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE manager destroy failed (ignored):', error);
|
|
1784
1687
|
}
|
|
1785
1688
|
}
|
|
1786
|
-
createProtocolMismatchError(expected) {
|
|
1787
|
-
|
|
1689
|
+
createProtocolMismatchError(expected, uuid) {
|
|
1690
|
+
const isStaleV2Bond = expected === 'V2' && this.confirmedProtocolV2.has(uuid);
|
|
1691
|
+
return hdShared.ERRORS.TypedError(isStaleV2Bond ? hdShared.HardwareErrorCode.BleDeviceBondError : hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
|
|
1788
1692
|
}
|
|
1789
1693
|
createProtocolDetectionError() {
|
|
1790
1694
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1 GetFeatures or Protocol V2 Ping');
|
|
@@ -1804,17 +1708,27 @@ class ReactNativeBleTransport {
|
|
|
1804
1708
|
detectProtocol(uuid, expectedProtocol, protocolHint, rebuildTransport) {
|
|
1805
1709
|
var _a;
|
|
1806
1710
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1807
|
-
if (expectedProtocol === 'V1') {
|
|
1808
|
-
this.deviceProtocol.set(uuid,
|
|
1711
|
+
if (reactNative.Platform.OS === 'ios' && expectedProtocol === 'V1') {
|
|
1712
|
+
this.deviceProtocol.set(uuid, expectedProtocol);
|
|
1809
1713
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol selected', {
|
|
1810
1714
|
deviceId: uuid,
|
|
1811
|
-
protocol:
|
|
1715
|
+
protocol: expectedProtocol,
|
|
1812
1716
|
source: 'expected',
|
|
1813
1717
|
});
|
|
1814
|
-
return
|
|
1718
|
+
return expectedProtocol;
|
|
1815
1719
|
}
|
|
1816
|
-
if (expectedProtocol === '
|
|
1817
|
-
this.
|
|
1720
|
+
if (expectedProtocol === 'V1') {
|
|
1721
|
+
if (yield this.probeProtocolV1(uuid)) {
|
|
1722
|
+
this.deviceProtocol.set(uuid, 'V1');
|
|
1723
|
+
this.sessionProtocols.set(uuid, 'V1');
|
|
1724
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
|
|
1725
|
+
deviceId: uuid,
|
|
1726
|
+
protocol: 'V1',
|
|
1727
|
+
source: 'expected',
|
|
1728
|
+
});
|
|
1729
|
+
return 'V1';
|
|
1730
|
+
}
|
|
1731
|
+
throw this.createProtocolMismatchError(expectedProtocol, uuid);
|
|
1818
1732
|
}
|
|
1819
1733
|
if (expectedProtocol === 'V2') {
|
|
1820
1734
|
if (yield this.probeProtocolV2(uuid)) {
|
|
@@ -1828,7 +1742,7 @@ class ReactNativeBleTransport {
|
|
|
1828
1742
|
});
|
|
1829
1743
|
return 'V2';
|
|
1830
1744
|
}
|
|
1831
|
-
throw this.createProtocolMismatchError(expectedProtocol);
|
|
1745
|
+
throw this.createProtocolMismatchError(expectedProtocol, uuid);
|
|
1832
1746
|
}
|
|
1833
1747
|
const sessionProtocol = this.sessionProtocols.get(uuid);
|
|
1834
1748
|
const reprobeFailures = (_a = this.protocolReprobeFailures.get(uuid)) !== null && _a !== void 0 ? _a : 0;
|
|
@@ -1946,7 +1860,6 @@ class ReactNativeBleTransport {
|
|
|
1946
1860
|
}
|
|
1947
1861
|
this.probingProtocols.set(uuid, 'V2');
|
|
1948
1862
|
(_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1949
|
-
this.throwIfStaleBondError(uuid);
|
|
1950
1863
|
const detected = yield transport.probeProtocolV2({
|
|
1951
1864
|
call: (name, data, options) => this.callProtocolV2(uuid, name, data, options),
|
|
1952
1865
|
timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT_MS,
|
|
@@ -1957,7 +1870,6 @@ class ReactNativeBleTransport {
|
|
|
1957
1870
|
(_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1958
1871
|
this.resetProtocolV2Frames(uuid);
|
|
1959
1872
|
},
|
|
1960
|
-
shouldRethrow: hdShared.isBleStaleBondHardwareError,
|
|
1961
1873
|
});
|
|
1962
1874
|
if (!detected) {
|
|
1963
1875
|
this.clearProbeProtocol(uuid, 'V2');
|
|
@@ -2018,19 +1930,6 @@ class ReactNativeBleTransport {
|
|
|
2018
1930
|
framePromise.reject(error);
|
|
2019
1931
|
}
|
|
2020
1932
|
}
|
|
2021
|
-
rememberStaleBondError(uuid, error) {
|
|
2022
|
-
this.staleBondErrors.set(uuid, error);
|
|
2023
|
-
this.rejectProtocolV2Frames(uuid, error);
|
|
2024
|
-
if (this.runPromise && this.runPromiseDeviceId === uuid) {
|
|
2025
|
-
this.runPromise.reject(error);
|
|
2026
|
-
}
|
|
2027
|
-
}
|
|
2028
|
-
throwIfStaleBondError(uuid) {
|
|
2029
|
-
const error = this.staleBondErrors.get(uuid);
|
|
2030
|
-
if (error) {
|
|
2031
|
-
throw error;
|
|
2032
|
-
}
|
|
2033
|
-
}
|
|
2034
1933
|
readProtocolV2Frame(uuid) {
|
|
2035
1934
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2036
1935
|
const queuedFrame = this.getProtocolV2FrameQueue(uuid).shift();
|
|
@@ -2079,11 +1978,6 @@ class ReactNativeBleTransport {
|
|
|
2079
1978
|
return;
|
|
2080
1979
|
}
|
|
2081
1980
|
catch (error) {
|
|
2082
|
-
if (isNativeBleStaleBondError(error) || hdShared.isBleStaleBondHardwareError(error)) {
|
|
2083
|
-
const bondError = toBleStaleBondHardwareError(error);
|
|
2084
|
-
this.rememberStaleBondError(uuid, bondError);
|
|
2085
|
-
throw bondError;
|
|
2086
|
-
}
|
|
2087
1981
|
if (getFirmwareUploadWriteRetryType(error) !== 'congested' ||
|
|
2088
1982
|
attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
|
|
2089
1983
|
throw error;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-transport-react-native",
|
|
3
|
-
"version": "1.2.2-alpha.
|
|
3
|
+
"version": "1.2.2-alpha.100",
|
|
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.2-alpha.
|
|
24
|
-
"@onekeyfe/hd-shared": "1.2.2-alpha.
|
|
25
|
-
"@onekeyfe/hd-transport": "1.2.2-alpha.
|
|
23
|
+
"@onekeyfe/hd-core": "1.2.2-alpha.100",
|
|
24
|
+
"@onekeyfe/hd-shared": "1.2.2-alpha.100",
|
|
25
|
+
"@onekeyfe/hd-transport": "1.2.2-alpha.100",
|
|
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": "c40dad085297b0e3cc2020bd218c146dff6ed3e1"
|
|
30
30
|
}
|
|
@@ -109,11 +109,9 @@ const schemas = {
|
|
|
109
109
|
const createHarness = ({
|
|
110
110
|
deviceName = 'OneKey Pro 2',
|
|
111
111
|
isWritableWithResponse = true,
|
|
112
|
-
monitorError,
|
|
113
112
|
}: {
|
|
114
113
|
deviceName?: string;
|
|
115
114
|
isWritableWithResponse?: boolean;
|
|
116
|
-
monitorError?: (Error & { reason?: string; attErrorCode?: number; iosErrorCode?: number }) | null;
|
|
117
115
|
} = {}) => {
|
|
118
116
|
const uuid = 'rn-pro2-id';
|
|
119
117
|
const sentSeqs: number[] = [];
|
|
@@ -132,9 +130,6 @@ const createHarness = ({
|
|
|
132
130
|
isNotifiable: true,
|
|
133
131
|
monitor: jest.fn(callback => {
|
|
134
132
|
notifyCallback = callback;
|
|
135
|
-
if (monitorError) {
|
|
136
|
-
queueMicrotask(() => callback(monitorError, null));
|
|
137
|
-
}
|
|
138
133
|
return { remove: jest.fn() };
|
|
139
134
|
}),
|
|
140
135
|
};
|
|
@@ -410,22 +405,6 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
410
405
|
await transport.release(uuid, true);
|
|
411
406
|
});
|
|
412
407
|
|
|
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
|
-
|
|
429
408
|
test('detects Protocol V2 on iOS without using the BLE name as a protocol hint', async () => {
|
|
430
409
|
const { transport, uuid, device, sentSeqs, writeCharacteristic } = createHarness({
|
|
431
410
|
deviceName: 'Pro2 6E9E',
|
|
@@ -445,93 +424,6 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
445
424
|
await transport.release(uuid, true);
|
|
446
425
|
});
|
|
447
426
|
|
|
448
|
-
test('physically refreshes an uncached Protocol V2 firmware install link without Ping', async () => {
|
|
449
|
-
const { transport, uuid, device, bleManager, writeCharacteristic } = createHarness();
|
|
450
|
-
const probeProtocolV2 = jest.spyOn(transport as any, 'probeProtocolV2');
|
|
451
|
-
(transport as any).sessionProtocols.set(uuid, 'V2');
|
|
452
|
-
device.connect = jest.fn().mockResolvedValue(device);
|
|
453
|
-
device.isConnected.mockResolvedValueOnce(false);
|
|
454
|
-
|
|
455
|
-
await expect(
|
|
456
|
-
transport.acquire({ uuid, expectedProtocol: 'V2', skipProtocolProbe: true })
|
|
457
|
-
).resolves.toEqual({
|
|
458
|
-
uuid,
|
|
459
|
-
protocolType: 'V2',
|
|
460
|
-
});
|
|
461
|
-
|
|
462
|
-
expect(bleManager.cancelDeviceConnection).toHaveBeenCalledWith(uuid);
|
|
463
|
-
expect(device.cancelConnection).not.toHaveBeenCalled();
|
|
464
|
-
expect(device.connect).toHaveBeenCalled();
|
|
465
|
-
expect(probeProtocolV2).not.toHaveBeenCalled();
|
|
466
|
-
expect(writeCharacteristic.writeWithResponse).not.toHaveBeenCalled();
|
|
467
|
-
expect(writeCharacteristic.writeWithoutResponse).not.toHaveBeenCalled();
|
|
468
|
-
expect(transport.getProtocolType(uuid)).toBe('V2');
|
|
469
|
-
await transport.release(uuid, true);
|
|
470
|
-
});
|
|
471
|
-
|
|
472
|
-
test('refreshes a cached firmware install connection instead of trusting GATT state', async () => {
|
|
473
|
-
const { transport, uuid, device, bleManager, writeCharacteristic } = createHarness();
|
|
474
|
-
const probeProtocolV2 = jest.spyOn(transport as any, 'probeProtocolV2');
|
|
475
|
-
|
|
476
|
-
await transport.acquire({ uuid, expectedProtocol: 'V2' });
|
|
477
|
-
device.connect = jest.fn().mockResolvedValue(device);
|
|
478
|
-
device.isConnected.mockResolvedValueOnce(false);
|
|
479
|
-
writeCharacteristic.writeWithResponse.mockClear();
|
|
480
|
-
writeCharacteristic.writeWithoutResponse.mockClear();
|
|
481
|
-
|
|
482
|
-
await expect(
|
|
483
|
-
transport.acquire({ uuid, expectedProtocol: 'V2', skipProtocolProbe: true })
|
|
484
|
-
).resolves.toEqual({
|
|
485
|
-
uuid,
|
|
486
|
-
protocolType: 'V2',
|
|
487
|
-
});
|
|
488
|
-
|
|
489
|
-
expect(bleManager.cancelDeviceConnection).toHaveBeenCalledWith(uuid);
|
|
490
|
-
expect(device.cancelConnection).toHaveBeenCalled();
|
|
491
|
-
expect(device.connect).toHaveBeenCalled();
|
|
492
|
-
expect(probeProtocolV2).toHaveBeenCalledTimes(1);
|
|
493
|
-
expect(writeCharacteristic.writeWithResponse).not.toHaveBeenCalled();
|
|
494
|
-
expect(writeCharacteristic.writeWithoutResponse).not.toHaveBeenCalled();
|
|
495
|
-
await transport.release(uuid, true);
|
|
496
|
-
});
|
|
497
|
-
|
|
498
|
-
test('rejects no-probe acquire before the BLE endpoint has confirmed a protocol', async () => {
|
|
499
|
-
const { transport, uuid } = createHarness();
|
|
500
|
-
|
|
501
|
-
await expect(
|
|
502
|
-
transport.acquire({ uuid, expectedProtocol: 'V2', skipProtocolProbe: true })
|
|
503
|
-
).rejects.toThrow('previously confirmed protocol');
|
|
504
|
-
|
|
505
|
-
expect(transport.getProtocolType(uuid)).toBeUndefined();
|
|
506
|
-
});
|
|
507
|
-
|
|
508
|
-
test('keeps confirmed V2 authorization across a BLE manager reset', async () => {
|
|
509
|
-
const { transport, uuid, device, bleManager, writeCharacteristic } = createHarness();
|
|
510
|
-
const probeProtocolV2 = jest.spyOn(transport as any, 'probeProtocolV2');
|
|
511
|
-
|
|
512
|
-
await transport.acquire({ uuid, expectedProtocol: 'V2' });
|
|
513
|
-
expect(probeProtocolV2).toHaveBeenCalledTimes(1);
|
|
514
|
-
|
|
515
|
-
(transport as any).resetPlxManager();
|
|
516
|
-
transport.blePlxManager = bleManager as any;
|
|
517
|
-
device.connect = jest.fn().mockResolvedValue(device);
|
|
518
|
-
device.isConnected.mockResolvedValueOnce(false);
|
|
519
|
-
writeCharacteristic.writeWithResponse.mockClear();
|
|
520
|
-
writeCharacteristic.writeWithoutResponse.mockClear();
|
|
521
|
-
|
|
522
|
-
await expect(
|
|
523
|
-
transport.acquire({ uuid, expectedProtocol: 'V2', skipProtocolProbe: true })
|
|
524
|
-
).resolves.toEqual({
|
|
525
|
-
uuid,
|
|
526
|
-
protocolType: 'V2',
|
|
527
|
-
});
|
|
528
|
-
|
|
529
|
-
expect(probeProtocolV2).toHaveBeenCalledTimes(1);
|
|
530
|
-
expect(writeCharacteristic.writeWithResponse).not.toHaveBeenCalled();
|
|
531
|
-
expect(writeCharacteristic.writeWithoutResponse).not.toHaveBeenCalled();
|
|
532
|
-
await transport.release(uuid, true);
|
|
533
|
-
});
|
|
534
|
-
|
|
535
427
|
test.each(['ios', 'android'] as const)(
|
|
536
428
|
'keeps a first expected Protocol V2 probe miss retryable on %s',
|
|
537
429
|
async platform => {
|
|
@@ -567,37 +459,8 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
567
459
|
}
|
|
568
460
|
);
|
|
569
461
|
|
|
570
|
-
test.each([
|
|
571
|
-
[
|
|
572
|
-
'Encryption is insufficient',
|
|
573
|
-
{ reason: 'Encryption is insufficient', attErrorCode: 15 },
|
|
574
|
-
HardwareErrorCode.BleDeviceBondError,
|
|
575
|
-
],
|
|
576
|
-
[
|
|
577
|
-
'Peer removed pairing information',
|
|
578
|
-
{ reason: 'Peer removed pairing information', iosErrorCode: 14 },
|
|
579
|
-
HardwareErrorCode.BlePeerRemovedPairingInformation,
|
|
580
|
-
],
|
|
581
|
-
] as const)(
|
|
582
|
-
'fails Protocol V2 acquire immediately on %s instead of waiting for Ping',
|
|
583
|
-
async (_label, nativeError, errorCode) => {
|
|
584
|
-
const { transport, uuid, device } = createHarness({
|
|
585
|
-
monitorError: Object.assign(new Error(nativeError.reason), nativeError),
|
|
586
|
-
});
|
|
587
|
-
const probe = jest.spyOn(transport as any, 'probeProtocolV2');
|
|
588
|
-
|
|
589
|
-
await expect(transport.acquire({ uuid, expectedProtocol: 'V2' })).rejects.toMatchObject({
|
|
590
|
-
errorCode,
|
|
591
|
-
});
|
|
592
|
-
|
|
593
|
-
expect(probe).not.toHaveBeenCalled();
|
|
594
|
-
expect(device.cancelConnection).toHaveBeenCalled();
|
|
595
|
-
expect(transport.getProtocolType(uuid)).toBeUndefined();
|
|
596
|
-
}
|
|
597
|
-
);
|
|
598
|
-
|
|
599
462
|
test.each(['ios', 'android'] as const)(
|
|
600
|
-
'
|
|
463
|
+
'reports a stale bond on %s when a previously confirmed Protocol V2 device stops responding',
|
|
601
464
|
async platform => {
|
|
602
465
|
setPlatformOS(platform);
|
|
603
466
|
const { transport, uuid, device } = createHarness();
|
|
@@ -606,10 +469,10 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
606
469
|
jest.spyOn(transport as any, 'probeProtocolV2').mockResolvedValue(false);
|
|
607
470
|
|
|
608
471
|
await expect(transport.acquire({ uuid, expectedProtocol: 'V2' })).rejects.toMatchObject({
|
|
609
|
-
errorCode: HardwareErrorCode.
|
|
472
|
+
errorCode: HardwareErrorCode.BleDeviceBondError,
|
|
610
473
|
});
|
|
611
474
|
|
|
612
|
-
expect(device.cancelConnection).
|
|
475
|
+
expect(device.cancelConnection).toHaveBeenCalled();
|
|
613
476
|
expect(transport.getProtocolType(uuid)).toBeUndefined();
|
|
614
477
|
}
|
|
615
478
|
);
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PermissionsAndroid, Platform } from 'react-native';
|
|
2
2
|
import { Buffer } from 'buffer';
|
|
3
3
|
import {
|
|
4
|
+
BleATTErrorCode,
|
|
4
5
|
BleError,
|
|
5
6
|
BleErrorCode,
|
|
6
7
|
BleManager as BlePlxManager,
|
|
@@ -47,11 +48,6 @@ import {
|
|
|
47
48
|
getInfosForServiceUuid,
|
|
48
49
|
isSameBleUuid,
|
|
49
50
|
} from './constants';
|
|
50
|
-
import {
|
|
51
|
-
isBleStaleBondHardwareError,
|
|
52
|
-
isNativeBleStaleBondError,
|
|
53
|
-
toBleStaleBondHardwareError,
|
|
54
|
-
} from './bleStaleBond';
|
|
55
51
|
import { isHeaderChunk } from './utils/validateNotify';
|
|
56
52
|
import BleTransport from './BleTransport';
|
|
57
53
|
import timer from './utils/timer';
|
|
@@ -62,15 +58,6 @@ import type { Characteristic, Device, Subscription } from 'react-native-ble-plx'
|
|
|
62
58
|
import type EventEmitter from 'events';
|
|
63
59
|
import type { BleAcquireInput, TransportOptions } from './types';
|
|
64
60
|
|
|
65
|
-
type FirmwareInstallBleAcquireInput = BleAcquireInput & {
|
|
66
|
-
/**
|
|
67
|
-
* Reuse the already-verified protocol after an expected firmware-install
|
|
68
|
-
* disconnect. The install loader accepts status requests but may not answer
|
|
69
|
-
* the generic protocol probe used by a normal acquire.
|
|
70
|
-
*/
|
|
71
|
-
skipProtocolProbe?: boolean;
|
|
72
|
-
};
|
|
73
|
-
|
|
74
61
|
const { check, ProtocolV1, parseConfigure } = transport;
|
|
75
62
|
|
|
76
63
|
const Log = bleLogger;
|
|
@@ -320,10 +307,15 @@ const resolveNegotiatedMtu = (device: Device) => requestNegotiatedMtu(device, 'c
|
|
|
320
307
|
|
|
321
308
|
type IOBleErrorRemap = Error | BleError | null | undefined;
|
|
322
309
|
|
|
323
|
-
function remapError(error: IOBleErrorRemap
|
|
310
|
+
function remapError(error: IOBleErrorRemap) {
|
|
324
311
|
if (error instanceof BleError) {
|
|
325
|
-
if (
|
|
326
|
-
|
|
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);
|
|
327
319
|
}
|
|
328
320
|
|
|
329
321
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
@@ -398,16 +390,6 @@ export default class ReactNativeBleTransport {
|
|
|
398
390
|
/** Consecutive detections that failed while trusting sessionProtocols. */
|
|
399
391
|
private protocolReprobeFailures: Map<string, number> = new Map();
|
|
400
392
|
|
|
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
|
-
|
|
411
393
|
private protocolV2Assemblers: Map<string, ProtocolV2FrameAssembler> = new Map();
|
|
412
394
|
|
|
413
395
|
private protocolV2FrameQueues: Map<string, Uint8Array[]> = new Map();
|
|
@@ -883,7 +865,7 @@ export default class ReactNativeBleTransport {
|
|
|
883
865
|
return transport;
|
|
884
866
|
}
|
|
885
867
|
|
|
886
|
-
async acquire(input:
|
|
868
|
+
async acquire(input: BleAcquireInput) {
|
|
887
869
|
const { uuid } = input;
|
|
888
870
|
|
|
889
871
|
if (!uuid) {
|
|
@@ -893,73 +875,28 @@ export default class ReactNativeBleTransport {
|
|
|
893
875
|
return this.runLifecycleOperation(uuid, () => this.acquireUnlocked(input));
|
|
894
876
|
}
|
|
895
877
|
|
|
896
|
-
private async acquireUnlocked(input:
|
|
897
|
-
const { uuid, forceCleanRunPromise, expectedProtocol
|
|
898
|
-
const shouldMapProtocolV2StaleBond = expectedProtocol
|
|
899
|
-
? expectedProtocol === 'V2'
|
|
900
|
-
: this.confirmedProtocolV2.has(uuid);
|
|
878
|
+
private async acquireUnlocked(input: BleAcquireInput) {
|
|
879
|
+
const { uuid, forceCleanRunPromise, expectedProtocol } = input;
|
|
901
880
|
|
|
902
881
|
const cachedTransport = transportCache[uuid];
|
|
903
|
-
if (skipProtocolProbe && !cachedTransport && this.blePlxManager) {
|
|
904
|
-
Log?.debug(
|
|
905
|
-
'[ReactNativeBleTransport] refresh uncached BLE connection for firmware install:',
|
|
906
|
-
uuid
|
|
907
|
-
);
|
|
908
|
-
const manager = this.blePlxManager;
|
|
909
|
-
await this.runNativeTeardown(uuid, manager, async () => {
|
|
910
|
-
await this.runBestEffortNativeOperation(
|
|
911
|
-
'firmware install reconnect: cancel uncached device connection',
|
|
912
|
-
() => manager.cancelDeviceConnection(uuid)
|
|
913
|
-
);
|
|
914
|
-
});
|
|
915
|
-
}
|
|
916
882
|
if (cachedTransport) {
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
if (manager) {
|
|
927
|
-
operations.push(
|
|
928
|
-
this.runBestEffortNativeOperation(
|
|
929
|
-
'firmware install reconnect: cancel device connection',
|
|
930
|
-
() => manager.cancelDeviceConnection(uuid)
|
|
931
|
-
)
|
|
932
|
-
);
|
|
933
|
-
}
|
|
934
|
-
operations.push(
|
|
935
|
-
this.runBestEffortNativeOperation(
|
|
936
|
-
'firmware install reconnect: device cancel connection',
|
|
937
|
-
() => cachedTransport.device.cancelConnection()
|
|
938
|
-
)
|
|
939
|
-
);
|
|
940
|
-
await Promise.all(operations);
|
|
941
|
-
});
|
|
942
|
-
} else {
|
|
943
|
-
const cachedProtocol = this.deviceProtocol.get(uuid);
|
|
944
|
-
const isCachedDeviceConnected = await cachedTransport.device
|
|
945
|
-
.isConnected()
|
|
946
|
-
.catch(() => false);
|
|
947
|
-
if (
|
|
948
|
-
isCachedDeviceConnected &&
|
|
949
|
-
cachedProtocol &&
|
|
950
|
-
(!expectedProtocol || cachedProtocol === expectedProtocol)
|
|
951
|
-
) {
|
|
952
|
-
Log?.debug('[ReactNativeBleTransport] reuse cached BLE transport:', uuid, cachedProtocol);
|
|
953
|
-
return { uuid, protocolType: cachedProtocol };
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
/**
|
|
957
|
-
* If the transport is not reusable due to a protocol mismatch or stale
|
|
958
|
-
* connection, clean it up before creating a new transport instance.
|
|
959
|
-
*/
|
|
960
|
-
Log?.debug('transport not reusable, will release: ', uuid);
|
|
961
|
-
await this.releaseUnlocked(uuid, true);
|
|
883
|
+
const cachedProtocol = this.deviceProtocol.get(uuid);
|
|
884
|
+
const isCachedDeviceConnected = await cachedTransport.device.isConnected().catch(() => false);
|
|
885
|
+
if (
|
|
886
|
+
isCachedDeviceConnected &&
|
|
887
|
+
cachedProtocol &&
|
|
888
|
+
(!expectedProtocol || cachedProtocol === expectedProtocol)
|
|
889
|
+
) {
|
|
890
|
+
Log?.debug('[ReactNativeBleTransport] reuse cached BLE transport:', uuid, cachedProtocol);
|
|
891
|
+
return { uuid, protocolType: cachedProtocol };
|
|
962
892
|
}
|
|
893
|
+
|
|
894
|
+
/**
|
|
895
|
+
* If the transport is not reusable due to a protocol mismatch or stale
|
|
896
|
+
* connection, clean it up before creating a new transport instance.
|
|
897
|
+
*/
|
|
898
|
+
Log?.debug('transport not reusable, will release: ', uuid);
|
|
899
|
+
await this.releaseUnlocked(uuid, true);
|
|
963
900
|
}
|
|
964
901
|
|
|
965
902
|
let device: Device | null = null;
|
|
@@ -1024,7 +961,7 @@ export default class ReactNativeBleTransport {
|
|
|
1024
961
|
Log?.debug('device already connected');
|
|
1025
962
|
throw ERRORS.TypedError(HardwareErrorCode.BleAlreadyConnected);
|
|
1026
963
|
} else {
|
|
1027
|
-
remapError(e
|
|
964
|
+
remapError(e);
|
|
1028
965
|
}
|
|
1029
966
|
}
|
|
1030
967
|
}
|
|
@@ -1068,7 +1005,7 @@ export default class ReactNativeBleTransport {
|
|
|
1068
1005
|
}
|
|
1069
1006
|
}
|
|
1070
1007
|
} else {
|
|
1071
|
-
remapError(e
|
|
1008
|
+
remapError(e);
|
|
1072
1009
|
}
|
|
1073
1010
|
}
|
|
1074
1011
|
}
|
|
@@ -1088,48 +1025,12 @@ export default class ReactNativeBleTransport {
|
|
|
1088
1025
|
this.deviceProtocolHints.set(uuid, protocolHint);
|
|
1089
1026
|
}
|
|
1090
1027
|
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1028
|
+
await this.installTransportForAcquire(uuid, acquiredDevice, {
|
|
1029
|
+
writeCharacteristic,
|
|
1030
|
+
notifyCharacteristic,
|
|
1031
|
+
});
|
|
1094
1032
|
|
|
1095
1033
|
try {
|
|
1096
|
-
await this.installTransportForAcquire(uuid, acquiredDevice, {
|
|
1097
|
-
writeCharacteristic,
|
|
1098
|
-
notifyCharacteristic,
|
|
1099
|
-
});
|
|
1100
|
-
|
|
1101
|
-
if (skipProtocolProbe) {
|
|
1102
|
-
if (!expectedProtocol) {
|
|
1103
|
-
throw ERRORS.TypedError(
|
|
1104
|
-
HardwareErrorCode.RuntimeError,
|
|
1105
|
-
'skipProtocolProbe requires an expected BLE protocol'
|
|
1106
|
-
);
|
|
1107
|
-
}
|
|
1108
|
-
const hasConfirmedProtocol =
|
|
1109
|
-
this.sessionProtocols.get(uuid) === expectedProtocol ||
|
|
1110
|
-
(expectedProtocol === 'V2' && this.confirmedProtocolV2.has(uuid));
|
|
1111
|
-
if (!hasConfirmedProtocol) {
|
|
1112
|
-
throw ERRORS.TypedError(
|
|
1113
|
-
HardwareErrorCode.RuntimeError,
|
|
1114
|
-
'skipProtocolProbe requires a previously confirmed protocol for this BLE endpoint'
|
|
1115
|
-
);
|
|
1116
|
-
}
|
|
1117
|
-
this.deviceProtocol.set(uuid, expectedProtocol);
|
|
1118
|
-
this.sessionProtocols.set(uuid, expectedProtocol);
|
|
1119
|
-
this.protocolReprobeFailures.delete(uuid);
|
|
1120
|
-
Log?.debug('[ReactNativeBleTransport] protocol selected without probe', {
|
|
1121
|
-
deviceId: uuid,
|
|
1122
|
-
protocol: expectedProtocol,
|
|
1123
|
-
source: 'firmware-install-reconnect',
|
|
1124
|
-
});
|
|
1125
|
-
const currentTransport = transportCache[uuid];
|
|
1126
|
-
if (!currentTransport) {
|
|
1127
|
-
throw ERRORS.TypedError(HardwareErrorCode.TransportNotFound);
|
|
1128
|
-
}
|
|
1129
|
-
this.attachDisconnectSubscription(currentTransport, currentTransport.device, uuid);
|
|
1130
|
-
return { uuid, protocolType: expectedProtocol };
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
1034
|
const protocolType = await this.detectProtocol(
|
|
1134
1035
|
uuid,
|
|
1135
1036
|
expectedProtocol,
|
|
@@ -1145,14 +1046,12 @@ export default class ReactNativeBleTransport {
|
|
|
1145
1046
|
this.attachDisconnectSubscription(currentTransport, currentTransport.device, uuid);
|
|
1146
1047
|
return { uuid, protocolType };
|
|
1147
1048
|
} catch (error) {
|
|
1148
|
-
if (
|
|
1049
|
+
if ((error as { errorCode?: unknown })?.errorCode === HardwareErrorCode.BleDeviceBondError) {
|
|
1149
1050
|
await this.disconnectUnlocked(uuid);
|
|
1150
1051
|
} else {
|
|
1151
1052
|
await this.releaseUnlocked(uuid, true);
|
|
1152
1053
|
}
|
|
1153
1054
|
throw error;
|
|
1154
|
-
} finally {
|
|
1155
|
-
this.acquiringProtocolV2.delete(uuid);
|
|
1156
1055
|
}
|
|
1157
1056
|
}
|
|
1158
1057
|
|
|
@@ -1180,21 +1079,17 @@ export default class ReactNativeBleTransport {
|
|
|
1180
1079
|
Log?.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
|
|
1181
1080
|
return;
|
|
1182
1081
|
}
|
|
1183
|
-
if (
|
|
1184
|
-
(this.getActiveProtocol(uuid) === 'V2' || this.acquiringProtocolV2.has(uuid)) &&
|
|
1185
|
-
isNativeBleStaleBondError(error)
|
|
1186
|
-
) {
|
|
1187
|
-
this.rememberStaleBondError(uuid, toBleStaleBondHardwareError(error));
|
|
1188
|
-
return;
|
|
1189
|
-
}
|
|
1190
1082
|
if (this.getActiveProtocol(uuid) === 'V2') {
|
|
1191
1083
|
let errorCode:
|
|
1084
|
+
| typeof HardwareErrorCode.BleDeviceBondError
|
|
1192
1085
|
| typeof HardwareErrorCode.BleCharacteristicNotifyError
|
|
1193
1086
|
| typeof HardwareErrorCode.BleCharacteristicNotifyChangeFailure
|
|
1194
1087
|
| typeof HardwareErrorCode.BleTimeoutError =
|
|
1195
1088
|
HardwareErrorCode.BleCharacteristicNotifyError;
|
|
1196
1089
|
if (error.reason?.includes('The connection has timed out unexpectedly')) {
|
|
1197
1090
|
errorCode = HardwareErrorCode.BleTimeoutError;
|
|
1091
|
+
} else if (error.reason?.includes('Encryption is insufficient')) {
|
|
1092
|
+
errorCode = HardwareErrorCode.BleDeviceBondError;
|
|
1198
1093
|
} else if (
|
|
1199
1094
|
error.reason?.includes('Cannot write client characteristic config descriptor') ||
|
|
1200
1095
|
error.reason?.includes('Cannot find client characteristic config descriptor') ||
|
|
@@ -1209,12 +1104,16 @@ export default class ReactNativeBleTransport {
|
|
|
1209
1104
|
}
|
|
1210
1105
|
if (this.runPromise && this.runPromiseDeviceId === uuid) {
|
|
1211
1106
|
let ERROR:
|
|
1107
|
+
| typeof HardwareErrorCode.BleDeviceBondError
|
|
1212
1108
|
| typeof HardwareErrorCode.BleCharacteristicNotifyError
|
|
1213
1109
|
| typeof HardwareErrorCode.BleTimeoutError =
|
|
1214
1110
|
HardwareErrorCode.BleCharacteristicNotifyError;
|
|
1215
1111
|
if (error.reason?.includes('The connection has timed out unexpectedly')) {
|
|
1216
1112
|
ERROR = HardwareErrorCode.BleTimeoutError;
|
|
1217
1113
|
}
|
|
1114
|
+
if (error.reason?.includes('Encryption is insufficient')) {
|
|
1115
|
+
ERROR = HardwareErrorCode.BleDeviceBondError;
|
|
1116
|
+
}
|
|
1218
1117
|
if (
|
|
1219
1118
|
error.reason?.includes('Cannot write client characteristic config descriptor') ||
|
|
1220
1119
|
error.reason?.includes('Cannot find client characteristic config descriptor') || // pro firmware 2.3.0 upgrade
|
|
@@ -1349,8 +1248,6 @@ export default class ReactNativeBleTransport {
|
|
|
1349
1248
|
|
|
1350
1249
|
this.deviceProtocol.delete(uuid);
|
|
1351
1250
|
this.probingProtocols.delete(uuid);
|
|
1352
|
-
this.staleBondErrors.delete(uuid);
|
|
1353
|
-
this.acquiringProtocolV2.delete(uuid);
|
|
1354
1251
|
// Confirmed protocol and caller hints stay in deviceProtocol / protocolHint.
|
|
1355
1252
|
this.protocolV2Assemblers.get(uuid)?.reset();
|
|
1356
1253
|
this.protocolV2Assemblers.delete(uuid);
|
|
@@ -1723,7 +1620,6 @@ export default class ReactNativeBleTransport {
|
|
|
1723
1620
|
}
|
|
1724
1621
|
this.deviceProtocol.delete(session);
|
|
1725
1622
|
this.probingProtocols.delete(session);
|
|
1726
|
-
this.staleBondErrors.delete(session);
|
|
1727
1623
|
this.deviceProtocolHints.delete(session);
|
|
1728
1624
|
this.sessionProtocols.delete(session);
|
|
1729
1625
|
this.protocolReprobeFailures.delete(session);
|
|
@@ -1947,8 +1843,6 @@ export default class ReactNativeBleTransport {
|
|
|
1947
1843
|
}
|
|
1948
1844
|
this.deviceProtocol.delete(uuid);
|
|
1949
1845
|
this.probingProtocols.delete(uuid);
|
|
1950
|
-
this.staleBondErrors.delete(uuid);
|
|
1951
|
-
this.acquiringProtocolV2.delete(uuid);
|
|
1952
1846
|
this.protocolV2Assemblers.delete(uuid);
|
|
1953
1847
|
this.resetProtocolV2Frames(uuid);
|
|
1954
1848
|
|
|
@@ -2045,8 +1939,6 @@ export default class ReactNativeBleTransport {
|
|
|
2045
1939
|
}
|
|
2046
1940
|
this.deviceProtocol.delete(uuid);
|
|
2047
1941
|
this.probingProtocols.delete(uuid);
|
|
2048
|
-
this.staleBondErrors.delete(uuid);
|
|
2049
|
-
this.acquiringProtocolV2.delete(uuid);
|
|
2050
1942
|
this.protocolV2Assemblers.delete(uuid);
|
|
2051
1943
|
this.resetProtocolV2Frames(uuid);
|
|
2052
1944
|
|
|
@@ -2097,11 +1989,8 @@ export default class ReactNativeBleTransport {
|
|
|
2097
1989
|
});
|
|
2098
1990
|
this.deviceProtocol.clear();
|
|
2099
1991
|
this.probingProtocols.clear();
|
|
2100
|
-
this.staleBondErrors.clear();
|
|
2101
|
-
this.acquiringProtocolV2.clear();
|
|
2102
1992
|
this.sessionProtocols.clear();
|
|
2103
|
-
|
|
2104
|
-
// firmware reconnect after the native BLE manager is recreated.
|
|
1993
|
+
this.confirmedProtocolV2.clear();
|
|
2105
1994
|
this.protocolReprobeFailures.clear();
|
|
2106
1995
|
this.writeTimeoutCounts.clear();
|
|
2107
1996
|
this.connectionSetupTimeoutCounts.clear();
|
|
@@ -2114,11 +2003,12 @@ export default class ReactNativeBleTransport {
|
|
|
2114
2003
|
}
|
|
2115
2004
|
}
|
|
2116
2005
|
|
|
2117
|
-
private createProtocolMismatchError(expected: ProtocolType) {
|
|
2118
|
-
// A
|
|
2119
|
-
//
|
|
2006
|
+
private createProtocolMismatchError(expected: ProtocolType, uuid: string) {
|
|
2007
|
+
// A generic Ping miss is not a bond failure. Only a later miss after this
|
|
2008
|
+
// endpoint already answered V2, or a native encryption/pairing error, is.
|
|
2009
|
+
const isStaleV2Bond = expected === 'V2' && this.confirmedProtocolV2.has(uuid);
|
|
2120
2010
|
return ERRORS.TypedError(
|
|
2121
|
-
HardwareErrorCode.RuntimeError,
|
|
2011
|
+
isStaleV2Bond ? HardwareErrorCode.BleDeviceBondError : HardwareErrorCode.RuntimeError,
|
|
2122
2012
|
`Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`
|
|
2123
2013
|
);
|
|
2124
2014
|
}
|
|
@@ -2150,28 +2040,31 @@ export default class ReactNativeBleTransport {
|
|
|
2150
2040
|
protocolHint?: ProtocolType,
|
|
2151
2041
|
rebuildTransport?: () => Promise<void>
|
|
2152
2042
|
): Promise<ProtocolType> {
|
|
2153
|
-
//
|
|
2154
|
-
//
|
|
2155
|
-
//
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
// RuntimeError. sessionProtocols is deliberately NOT stamped here: that map
|
|
2159
|
-
// records protocols the device actually answered on (it gates the
|
|
2160
|
-
// trustSessionProtocol narrowing in forced detection), and this branch has
|
|
2161
|
-
// received no response. skipProtocolProbe does not need it either — its only
|
|
2162
|
-
// caller is the V2 firmware-install reconnect.
|
|
2163
|
-
if (expectedProtocol === 'V1') {
|
|
2164
|
-
this.deviceProtocol.set(uuid, 'V1');
|
|
2043
|
+
// iOS still skips an extra V1 Initialize during acquire. Expected V2 must
|
|
2044
|
+
// Ping so USB-priority `link disabled` can surface instead of a later
|
|
2045
|
+
// unmapped RuntimeError.
|
|
2046
|
+
if (Platform.OS === 'ios' && expectedProtocol === 'V1') {
|
|
2047
|
+
this.deviceProtocol.set(uuid, expectedProtocol);
|
|
2165
2048
|
Log?.debug('[ReactNativeBleTransport] protocol selected', {
|
|
2166
2049
|
deviceId: uuid,
|
|
2167
|
-
protocol:
|
|
2050
|
+
protocol: expectedProtocol,
|
|
2168
2051
|
source: 'expected',
|
|
2169
2052
|
});
|
|
2170
|
-
return
|
|
2053
|
+
return expectedProtocol;
|
|
2171
2054
|
}
|
|
2172
2055
|
|
|
2173
|
-
if (expectedProtocol === '
|
|
2174
|
-
this.
|
|
2056
|
+
if (expectedProtocol === 'V1') {
|
|
2057
|
+
if (await this.probeProtocolV1(uuid)) {
|
|
2058
|
+
this.deviceProtocol.set(uuid, 'V1');
|
|
2059
|
+
this.sessionProtocols.set(uuid, 'V1');
|
|
2060
|
+
Log?.debug('[ReactNativeBleTransport] protocol detected', {
|
|
2061
|
+
deviceId: uuid,
|
|
2062
|
+
protocol: 'V1',
|
|
2063
|
+
source: 'expected',
|
|
2064
|
+
});
|
|
2065
|
+
return 'V1';
|
|
2066
|
+
}
|
|
2067
|
+
throw this.createProtocolMismatchError(expectedProtocol, uuid);
|
|
2175
2068
|
}
|
|
2176
2069
|
|
|
2177
2070
|
if (expectedProtocol === 'V2') {
|
|
@@ -2186,7 +2079,7 @@ export default class ReactNativeBleTransport {
|
|
|
2186
2079
|
});
|
|
2187
2080
|
return 'V2';
|
|
2188
2081
|
}
|
|
2189
|
-
throw this.createProtocolMismatchError(expectedProtocol);
|
|
2082
|
+
throw this.createProtocolMismatchError(expectedProtocol, uuid);
|
|
2190
2083
|
}
|
|
2191
2084
|
|
|
2192
2085
|
// Protocol must be actively probed after connection. Name, PID, and descriptors only
|
|
@@ -2330,7 +2223,6 @@ export default class ReactNativeBleTransport {
|
|
|
2330
2223
|
|
|
2331
2224
|
this.probingProtocols.set(uuid, 'V2');
|
|
2332
2225
|
this.protocolV2Assemblers.get(uuid)?.reset();
|
|
2333
|
-
this.throwIfStaleBondError(uuid);
|
|
2334
2226
|
const detected = await probeProtocolV2Helper({
|
|
2335
2227
|
call: (name: string, data: Record<string, unknown>, options?: TransportCallOptions) =>
|
|
2336
2228
|
this.callProtocolV2(uuid, name, data, options),
|
|
@@ -2341,7 +2233,6 @@ export default class ReactNativeBleTransport {
|
|
|
2341
2233
|
this.protocolV2Assemblers.get(uuid)?.reset();
|
|
2342
2234
|
this.resetProtocolV2Frames(uuid);
|
|
2343
2235
|
},
|
|
2344
|
-
shouldRethrow: isBleStaleBondHardwareError,
|
|
2345
2236
|
});
|
|
2346
2237
|
if (!detected) {
|
|
2347
2238
|
this.clearProbeProtocol(uuid, 'V2');
|
|
@@ -2410,21 +2301,6 @@ export default class ReactNativeBleTransport {
|
|
|
2410
2301
|
}
|
|
2411
2302
|
}
|
|
2412
2303
|
|
|
2413
|
-
private rememberStaleBondError(uuid: string, error: Error) {
|
|
2414
|
-
this.staleBondErrors.set(uuid, error);
|
|
2415
|
-
this.rejectProtocolV2Frames(uuid, error);
|
|
2416
|
-
if (this.runPromise && this.runPromiseDeviceId === uuid) {
|
|
2417
|
-
this.runPromise.reject(error);
|
|
2418
|
-
}
|
|
2419
|
-
}
|
|
2420
|
-
|
|
2421
|
-
private throwIfStaleBondError(uuid: string) {
|
|
2422
|
-
const error = this.staleBondErrors.get(uuid);
|
|
2423
|
-
if (error) {
|
|
2424
|
-
throw error;
|
|
2425
|
-
}
|
|
2426
|
-
}
|
|
2427
|
-
|
|
2428
2304
|
private async readProtocolV2Frame(uuid: string) {
|
|
2429
2305
|
const queuedFrame = this.getProtocolV2FrameQueue(uuid).shift();
|
|
2430
2306
|
if (queuedFrame) {
|
|
@@ -2483,11 +2359,6 @@ export default class ReactNativeBleTransport {
|
|
|
2483
2359
|
assertCurrentGeneration();
|
|
2484
2360
|
return;
|
|
2485
2361
|
} catch (error) {
|
|
2486
|
-
if (isNativeBleStaleBondError(error) || isBleStaleBondHardwareError(error)) {
|
|
2487
|
-
const bondError = toBleStaleBondHardwareError(error);
|
|
2488
|
-
this.rememberStaleBondError(uuid, bondError);
|
|
2489
|
-
throw bondError;
|
|
2490
|
-
}
|
|
2491
2362
|
if (
|
|
2492
2363
|
getFirmwareUploadWriteRetryType(error) !== 'congested' ||
|
|
2493
2364
|
attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES
|
package/dist/bleStaleBond.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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,UAkBzD,CAAC"}
|
|
@@ -1,39 +0,0 @@
|
|
|
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
|
-
{ reason: 'peer removed pairing information' },
|
|
21
|
-
HardwareErrorCode.BlePeerRemovedPairingInformation,
|
|
22
|
-
],
|
|
23
|
-
[
|
|
24
|
-
{ message: 'PEER REMOVED PAIRING INFORMATION' },
|
|
25
|
-
HardwareErrorCode.BlePeerRemovedPairingInformation,
|
|
26
|
-
],
|
|
27
|
-
])('maps %j to %s', (nativeError, errorCode) => {
|
|
28
|
-
expect(isNativeBleStaleBondError(nativeError)).toBe(true);
|
|
29
|
-
expect(toBleStaleBondHardwareError(nativeError)).toMatchObject({ errorCode });
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test('does not treat the generic ATT unlikely error as a stale bond', () => {
|
|
33
|
-
expect(isNativeBleStaleBondError({ attErrorCode: 14, reason: 'Unlikely error' })).toBe(false);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test('does not treat a generic disconnect as a stale bond', () => {
|
|
37
|
-
expect(isNativeBleStaleBondError({ reason: 'Device disconnected' })).toBe(false);
|
|
38
|
-
});
|
|
39
|
-
});
|
package/src/bleStaleBond.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
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 normalizedText = text.toLowerCase();
|
|
51
|
-
const peerRemoved =
|
|
52
|
-
nativeError.iosErrorCode === IOS_PEER_REMOVED_PAIRING_INFORMATION ||
|
|
53
|
-
normalizedText.includes('peer removed pairing information');
|
|
54
|
-
|
|
55
|
-
return ERRORS.TypedError(
|
|
56
|
-
peerRemoved
|
|
57
|
-
? HardwareErrorCode.BlePeerRemovedPairingInformation
|
|
58
|
-
: HardwareErrorCode.BleDeviceBondError,
|
|
59
|
-
text || undefined
|
|
60
|
-
);
|
|
61
|
-
};
|