@onekeyfe/hd-transport-react-native 1.2.2-alpha.100 → 1.2.2-alpha.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bleStaleBond.d.ts +5 -0
- package/dist/bleStaleBond.d.ts.map +1 -0
- package/dist/bleStrategy.d.ts +1 -1
- package/dist/bleStrategy.d.ts.map +1 -1
- package/dist/index.d.ts +19 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +170 -64
- package/package.json +5 -5
- package/src/__tests__/bleStaleBond.test.ts +39 -0
- package/src/__tests__/bleStrategy.test.ts +2 -3
- package/src/__tests__/protocolV2Link.test.ts +158 -16
- package/src/bleStaleBond.ts +61 -0
- package/src/bleStrategy.ts +1 -3
- package/src/index.ts +197 -68
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';
|
|
@@ -58,6 +62,15 @@ import type { Characteristic, Device, Subscription } from 'react-native-ble-plx'
|
|
|
58
62
|
import type EventEmitter from 'events';
|
|
59
63
|
import type { BleAcquireInput, TransportOptions } from './types';
|
|
60
64
|
|
|
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
|
+
|
|
61
74
|
const { check, ProtocolV1, parseConfigure } = transport;
|
|
62
75
|
|
|
63
76
|
const Log = bleLogger;
|
|
@@ -307,15 +320,10 @@ const resolveNegotiatedMtu = (device: Device) => requestNegotiatedMtu(device, 'c
|
|
|
307
320
|
|
|
308
321
|
type IOBleErrorRemap = Error | BleError | null | undefined;
|
|
309
322
|
|
|
310
|
-
function remapError(error: IOBleErrorRemap) {
|
|
323
|
+
function remapError(error: IOBleErrorRemap, mapProtocolV2StaleBond: boolean) {
|
|
311
324
|
if (error instanceof BleError) {
|
|
312
|
-
if (
|
|
313
|
-
|
|
314
|
-
// @ts-expect-error
|
|
315
|
-
error.iosErrorCode === BleATTErrorCode.UnlikelyError ||
|
|
316
|
-
error.reason === 'Peer removed pairing information'
|
|
317
|
-
) {
|
|
318
|
-
throw ERRORS.TypedError(HardwareErrorCode.BlePeerRemovedPairingInformation);
|
|
325
|
+
if (mapProtocolV2StaleBond && isNativeBleStaleBondError(error)) {
|
|
326
|
+
throw toBleStaleBondHardwareError(error);
|
|
319
327
|
}
|
|
320
328
|
|
|
321
329
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
@@ -390,6 +398,16 @@ export default class ReactNativeBleTransport {
|
|
|
390
398
|
/** Consecutive detections that failed while trusting sessionProtocols. */
|
|
391
399
|
private protocolReprobeFailures: Map<string, number> = new Map();
|
|
392
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
|
+
|
|
393
411
|
private protocolV2Assemblers: Map<string, ProtocolV2FrameAssembler> = new Map();
|
|
394
412
|
|
|
395
413
|
private protocolV2FrameQueues: Map<string, Uint8Array[]> = new Map();
|
|
@@ -865,7 +883,7 @@ export default class ReactNativeBleTransport {
|
|
|
865
883
|
return transport;
|
|
866
884
|
}
|
|
867
885
|
|
|
868
|
-
async acquire(input:
|
|
886
|
+
async acquire(input: FirmwareInstallBleAcquireInput) {
|
|
869
887
|
const { uuid } = input;
|
|
870
888
|
|
|
871
889
|
if (!uuid) {
|
|
@@ -875,28 +893,73 @@ export default class ReactNativeBleTransport {
|
|
|
875
893
|
return this.runLifecycleOperation(uuid, () => this.acquireUnlocked(input));
|
|
876
894
|
}
|
|
877
895
|
|
|
878
|
-
private async acquireUnlocked(input:
|
|
879
|
-
const { uuid, forceCleanRunPromise, expectedProtocol } = input;
|
|
896
|
+
private async acquireUnlocked(input: FirmwareInstallBleAcquireInput) {
|
|
897
|
+
const { uuid, forceCleanRunPromise, expectedProtocol, skipProtocolProbe } = input;
|
|
898
|
+
const shouldMapProtocolV2StaleBond = expectedProtocol
|
|
899
|
+
? expectedProtocol === 'V2'
|
|
900
|
+
: this.confirmedProtocolV2.has(uuid);
|
|
880
901
|
|
|
881
902
|
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
|
+
}
|
|
882
916
|
if (cachedTransport) {
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
917
|
+
if (skipProtocolProbe) {
|
|
918
|
+
Log?.debug(
|
|
919
|
+
'[ReactNativeBleTransport] refresh cached BLE connection for firmware install:',
|
|
920
|
+
uuid
|
|
921
|
+
);
|
|
922
|
+
const manager = this.blePlxManager;
|
|
923
|
+
await this.releaseUnlocked(uuid, true);
|
|
924
|
+
await this.runNativeTeardown(uuid, manager, async () => {
|
|
925
|
+
const operations: Promise<unknown>[] = [];
|
|
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
|
+
}
|
|
893
955
|
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
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);
|
|
962
|
+
}
|
|
900
963
|
}
|
|
901
964
|
|
|
902
965
|
let device: Device | null = null;
|
|
@@ -961,7 +1024,7 @@ export default class ReactNativeBleTransport {
|
|
|
961
1024
|
Log?.debug('device already connected');
|
|
962
1025
|
throw ERRORS.TypedError(HardwareErrorCode.BleAlreadyConnected);
|
|
963
1026
|
} else {
|
|
964
|
-
remapError(e);
|
|
1027
|
+
remapError(e, shouldMapProtocolV2StaleBond);
|
|
965
1028
|
}
|
|
966
1029
|
}
|
|
967
1030
|
}
|
|
@@ -1005,7 +1068,7 @@ export default class ReactNativeBleTransport {
|
|
|
1005
1068
|
}
|
|
1006
1069
|
}
|
|
1007
1070
|
} else {
|
|
1008
|
-
remapError(e);
|
|
1071
|
+
remapError(e, shouldMapProtocolV2StaleBond);
|
|
1009
1072
|
}
|
|
1010
1073
|
}
|
|
1011
1074
|
}
|
|
@@ -1025,12 +1088,48 @@ export default class ReactNativeBleTransport {
|
|
|
1025
1088
|
this.deviceProtocolHints.set(uuid, protocolHint);
|
|
1026
1089
|
}
|
|
1027
1090
|
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
});
|
|
1091
|
+
if (shouldMapProtocolV2StaleBond) {
|
|
1092
|
+
this.acquiringProtocolV2.add(uuid);
|
|
1093
|
+
}
|
|
1032
1094
|
|
|
1033
1095
|
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
|
+
|
|
1034
1133
|
const protocolType = await this.detectProtocol(
|
|
1035
1134
|
uuid,
|
|
1036
1135
|
expectedProtocol,
|
|
@@ -1046,12 +1145,14 @@ export default class ReactNativeBleTransport {
|
|
|
1046
1145
|
this.attachDisconnectSubscription(currentTransport, currentTransport.device, uuid);
|
|
1047
1146
|
return { uuid, protocolType };
|
|
1048
1147
|
} catch (error) {
|
|
1049
|
-
if ((error
|
|
1148
|
+
if (isBleStaleBondHardwareError(error)) {
|
|
1050
1149
|
await this.disconnectUnlocked(uuid);
|
|
1051
1150
|
} else {
|
|
1052
1151
|
await this.releaseUnlocked(uuid, true);
|
|
1053
1152
|
}
|
|
1054
1153
|
throw error;
|
|
1154
|
+
} finally {
|
|
1155
|
+
this.acquiringProtocolV2.delete(uuid);
|
|
1055
1156
|
}
|
|
1056
1157
|
}
|
|
1057
1158
|
|
|
@@ -1079,17 +1180,21 @@ export default class ReactNativeBleTransport {
|
|
|
1079
1180
|
Log?.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
|
|
1080
1181
|
return;
|
|
1081
1182
|
}
|
|
1183
|
+
if (
|
|
1184
|
+
(this.getActiveProtocol(uuid) === 'V2' || this.acquiringProtocolV2.has(uuid)) &&
|
|
1185
|
+
isNativeBleStaleBondError(error)
|
|
1186
|
+
) {
|
|
1187
|
+
this.rememberStaleBondError(uuid, toBleStaleBondHardwareError(error));
|
|
1188
|
+
return;
|
|
1189
|
+
}
|
|
1082
1190
|
if (this.getActiveProtocol(uuid) === 'V2') {
|
|
1083
1191
|
let errorCode:
|
|
1084
|
-
| typeof HardwareErrorCode.BleDeviceBondError
|
|
1085
1192
|
| typeof HardwareErrorCode.BleCharacteristicNotifyError
|
|
1086
1193
|
| typeof HardwareErrorCode.BleCharacteristicNotifyChangeFailure
|
|
1087
1194
|
| typeof HardwareErrorCode.BleTimeoutError =
|
|
1088
1195
|
HardwareErrorCode.BleCharacteristicNotifyError;
|
|
1089
1196
|
if (error.reason?.includes('The connection has timed out unexpectedly')) {
|
|
1090
1197
|
errorCode = HardwareErrorCode.BleTimeoutError;
|
|
1091
|
-
} else if (error.reason?.includes('Encryption is insufficient')) {
|
|
1092
|
-
errorCode = HardwareErrorCode.BleDeviceBondError;
|
|
1093
1198
|
} else if (
|
|
1094
1199
|
error.reason?.includes('Cannot write client characteristic config descriptor') ||
|
|
1095
1200
|
error.reason?.includes('Cannot find client characteristic config descriptor') ||
|
|
@@ -1104,16 +1209,12 @@ export default class ReactNativeBleTransport {
|
|
|
1104
1209
|
}
|
|
1105
1210
|
if (this.runPromise && this.runPromiseDeviceId === uuid) {
|
|
1106
1211
|
let ERROR:
|
|
1107
|
-
| typeof HardwareErrorCode.BleDeviceBondError
|
|
1108
1212
|
| typeof HardwareErrorCode.BleCharacteristicNotifyError
|
|
1109
1213
|
| typeof HardwareErrorCode.BleTimeoutError =
|
|
1110
1214
|
HardwareErrorCode.BleCharacteristicNotifyError;
|
|
1111
1215
|
if (error.reason?.includes('The connection has timed out unexpectedly')) {
|
|
1112
1216
|
ERROR = HardwareErrorCode.BleTimeoutError;
|
|
1113
1217
|
}
|
|
1114
|
-
if (error.reason?.includes('Encryption is insufficient')) {
|
|
1115
|
-
ERROR = HardwareErrorCode.BleDeviceBondError;
|
|
1116
|
-
}
|
|
1117
1218
|
if (
|
|
1118
1219
|
error.reason?.includes('Cannot write client characteristic config descriptor') ||
|
|
1119
1220
|
error.reason?.includes('Cannot find client characteristic config descriptor') || // pro firmware 2.3.0 upgrade
|
|
@@ -1248,6 +1349,8 @@ export default class ReactNativeBleTransport {
|
|
|
1248
1349
|
|
|
1249
1350
|
this.deviceProtocol.delete(uuid);
|
|
1250
1351
|
this.probingProtocols.delete(uuid);
|
|
1352
|
+
this.staleBondErrors.delete(uuid);
|
|
1353
|
+
this.acquiringProtocolV2.delete(uuid);
|
|
1251
1354
|
// Confirmed protocol and caller hints stay in deviceProtocol / protocolHint.
|
|
1252
1355
|
this.protocolV2Assemblers.get(uuid)?.reset();
|
|
1253
1356
|
this.protocolV2Assemblers.delete(uuid);
|
|
@@ -1620,6 +1723,7 @@ export default class ReactNativeBleTransport {
|
|
|
1620
1723
|
}
|
|
1621
1724
|
this.deviceProtocol.delete(session);
|
|
1622
1725
|
this.probingProtocols.delete(session);
|
|
1726
|
+
this.staleBondErrors.delete(session);
|
|
1623
1727
|
this.deviceProtocolHints.delete(session);
|
|
1624
1728
|
this.sessionProtocols.delete(session);
|
|
1625
1729
|
this.protocolReprobeFailures.delete(session);
|
|
@@ -1843,6 +1947,8 @@ export default class ReactNativeBleTransport {
|
|
|
1843
1947
|
}
|
|
1844
1948
|
this.deviceProtocol.delete(uuid);
|
|
1845
1949
|
this.probingProtocols.delete(uuid);
|
|
1950
|
+
this.staleBondErrors.delete(uuid);
|
|
1951
|
+
this.acquiringProtocolV2.delete(uuid);
|
|
1846
1952
|
this.protocolV2Assemblers.delete(uuid);
|
|
1847
1953
|
this.resetProtocolV2Frames(uuid);
|
|
1848
1954
|
|
|
@@ -1939,6 +2045,8 @@ export default class ReactNativeBleTransport {
|
|
|
1939
2045
|
}
|
|
1940
2046
|
this.deviceProtocol.delete(uuid);
|
|
1941
2047
|
this.probingProtocols.delete(uuid);
|
|
2048
|
+
this.staleBondErrors.delete(uuid);
|
|
2049
|
+
this.acquiringProtocolV2.delete(uuid);
|
|
1942
2050
|
this.protocolV2Assemblers.delete(uuid);
|
|
1943
2051
|
this.resetProtocolV2Frames(uuid);
|
|
1944
2052
|
|
|
@@ -1989,8 +2097,11 @@ export default class ReactNativeBleTransport {
|
|
|
1989
2097
|
});
|
|
1990
2098
|
this.deviceProtocol.clear();
|
|
1991
2099
|
this.probingProtocols.clear();
|
|
2100
|
+
this.staleBondErrors.clear();
|
|
2101
|
+
this.acquiringProtocolV2.clear();
|
|
1992
2102
|
this.sessionProtocols.clear();
|
|
1993
|
-
|
|
2103
|
+
// Keep transport-lifetime V2 proof so the same endpoint can finish a no-probe
|
|
2104
|
+
// firmware reconnect after the native BLE manager is recreated.
|
|
1994
2105
|
this.protocolReprobeFailures.clear();
|
|
1995
2106
|
this.writeTimeoutCounts.clear();
|
|
1996
2107
|
this.connectionSetupTimeoutCounts.clear();
|
|
@@ -2003,12 +2114,11 @@ export default class ReactNativeBleTransport {
|
|
|
2003
2114
|
}
|
|
2004
2115
|
}
|
|
2005
2116
|
|
|
2006
|
-
private createProtocolMismatchError(expected: ProtocolType
|
|
2007
|
-
// A
|
|
2008
|
-
//
|
|
2009
|
-
const isStaleV2Bond = expected === 'V2' && this.confirmedProtocolV2.has(uuid);
|
|
2117
|
+
private createProtocolMismatchError(expected: ProtocolType) {
|
|
2118
|
+
// A protocol probe miss alone does not prove that the OS bond is stale.
|
|
2119
|
+
// Native authentication/encryption failures are mapped separately.
|
|
2010
2120
|
return ERRORS.TypedError(
|
|
2011
|
-
|
|
2121
|
+
HardwareErrorCode.RuntimeError,
|
|
2012
2122
|
`Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`
|
|
2013
2123
|
);
|
|
2014
2124
|
}
|
|
@@ -2040,31 +2150,28 @@ export default class ReactNativeBleTransport {
|
|
|
2040
2150
|
protocolHint?: ProtocolType,
|
|
2041
2151
|
rebuildTransport?: () => Promise<void>
|
|
2042
2152
|
): Promise<ProtocolType> {
|
|
2043
|
-
//
|
|
2044
|
-
//
|
|
2045
|
-
//
|
|
2046
|
-
|
|
2047
|
-
|
|
2153
|
+
// A declared V1 is taken at face value on every platform, as iOS has done
|
|
2154
|
+
// since protocol probing arrived: the caller reads the protocol off its own
|
|
2155
|
+
// device record, so the probe re-asks a question that is already answered
|
|
2156
|
+
// and costs a round trip on every acquire. Expected V2 must still Ping so
|
|
2157
|
+
// USB-priority `link disabled` surfaces here instead of as a later unmapped
|
|
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');
|
|
2048
2165
|
Log?.debug('[ReactNativeBleTransport] protocol selected', {
|
|
2049
2166
|
deviceId: uuid,
|
|
2050
|
-
protocol:
|
|
2167
|
+
protocol: 'V1',
|
|
2051
2168
|
source: 'expected',
|
|
2052
2169
|
});
|
|
2053
|
-
return
|
|
2170
|
+
return 'V1';
|
|
2054
2171
|
}
|
|
2055
2172
|
|
|
2056
|
-
if (expectedProtocol === '
|
|
2057
|
-
|
|
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);
|
|
2173
|
+
if (expectedProtocol === 'V2' || this.acquiringProtocolV2.has(uuid)) {
|
|
2174
|
+
this.throwIfStaleBondError(uuid);
|
|
2068
2175
|
}
|
|
2069
2176
|
|
|
2070
2177
|
if (expectedProtocol === 'V2') {
|
|
@@ -2079,7 +2186,7 @@ export default class ReactNativeBleTransport {
|
|
|
2079
2186
|
});
|
|
2080
2187
|
return 'V2';
|
|
2081
2188
|
}
|
|
2082
|
-
throw this.createProtocolMismatchError(expectedProtocol
|
|
2189
|
+
throw this.createProtocolMismatchError(expectedProtocol);
|
|
2083
2190
|
}
|
|
2084
2191
|
|
|
2085
2192
|
// Protocol must be actively probed after connection. Name, PID, and descriptors only
|
|
@@ -2223,6 +2330,7 @@ export default class ReactNativeBleTransport {
|
|
|
2223
2330
|
|
|
2224
2331
|
this.probingProtocols.set(uuid, 'V2');
|
|
2225
2332
|
this.protocolV2Assemblers.get(uuid)?.reset();
|
|
2333
|
+
this.throwIfStaleBondError(uuid);
|
|
2226
2334
|
const detected = await probeProtocolV2Helper({
|
|
2227
2335
|
call: (name: string, data: Record<string, unknown>, options?: TransportCallOptions) =>
|
|
2228
2336
|
this.callProtocolV2(uuid, name, data, options),
|
|
@@ -2233,6 +2341,7 @@ export default class ReactNativeBleTransport {
|
|
|
2233
2341
|
this.protocolV2Assemblers.get(uuid)?.reset();
|
|
2234
2342
|
this.resetProtocolV2Frames(uuid);
|
|
2235
2343
|
},
|
|
2344
|
+
shouldRethrow: isBleStaleBondHardwareError,
|
|
2236
2345
|
});
|
|
2237
2346
|
if (!detected) {
|
|
2238
2347
|
this.clearProbeProtocol(uuid, 'V2');
|
|
@@ -2301,6 +2410,21 @@ export default class ReactNativeBleTransport {
|
|
|
2301
2410
|
}
|
|
2302
2411
|
}
|
|
2303
2412
|
|
|
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
|
+
|
|
2304
2428
|
private async readProtocolV2Frame(uuid: string) {
|
|
2305
2429
|
const queuedFrame = this.getProtocolV2FrameQueue(uuid).shift();
|
|
2306
2430
|
if (queuedFrame) {
|
|
@@ -2359,6 +2483,11 @@ export default class ReactNativeBleTransport {
|
|
|
2359
2483
|
assertCurrentGeneration();
|
|
2360
2484
|
return;
|
|
2361
2485
|
} catch (error) {
|
|
2486
|
+
if (isNativeBleStaleBondError(error) || isBleStaleBondHardwareError(error)) {
|
|
2487
|
+
const bondError = toBleStaleBondHardwareError(error);
|
|
2488
|
+
this.rememberStaleBondError(uuid, bondError);
|
|
2489
|
+
throw bondError;
|
|
2490
|
+
}
|
|
2362
2491
|
if (
|
|
2363
2492
|
getFirmwareUploadWriteRetryType(error) !== 'congested' ||
|
|
2364
2493
|
attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES
|