@onekeyfe/hd-transport-react-native 1.2.3 → 1.2.4-alpha.0
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 +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +138 -14
- package/package.json +5 -5
- package/src/__tests__/protocolReprobe.test.ts +116 -1
- package/src/index.ts +183 -13
package/dist/index.d.ts
CHANGED
|
@@ -165,6 +165,13 @@ declare class ReactNativeBleTransport {
|
|
|
165
165
|
private protocolReprobeFailures;
|
|
166
166
|
/** Endpoints whose last detection got no answer; 'woken' once their Initialize wake is spent. */
|
|
167
167
|
private silentDetections;
|
|
168
|
+
/**
|
|
169
|
+
* iOS can report a reset Pro2 as a generic peripheral disconnect instead of
|
|
170
|
+
* CBErrorPeerRemovedPairingInformation. Keep that first probe failure across
|
|
171
|
+
* the automatic cold reconnect and require a second failed probe before
|
|
172
|
+
* classifying the OS bond as invalid.
|
|
173
|
+
*/
|
|
174
|
+
private iosProbeDisconnectEvidence;
|
|
168
175
|
/** Android endpoints whose cached GATT table is suspect; the next connect refreshes it. */
|
|
169
176
|
private androidGattCacheRefreshes;
|
|
170
177
|
/**
|
|
@@ -279,6 +286,9 @@ declare class ReactNativeBleTransport {
|
|
|
279
286
|
private resetPlxManager;
|
|
280
287
|
private createProtocolMismatchError;
|
|
281
288
|
private createProtocolDetectionError;
|
|
289
|
+
private rememberIosProbeDisconnect;
|
|
290
|
+
private getIosProbeDisconnectEvidence;
|
|
291
|
+
private confirmIosProbeDisconnect;
|
|
282
292
|
private clearProbeProtocol;
|
|
283
293
|
/** Protocol to route a call with: confirmed if known, otherwise the one being probed. */
|
|
284
294
|
private getActiveProtocol;
|
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,EAGL,UAAU,IAAI,aAAa,EAG5B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,SAAS,EAAE,EAChB,KAAK,oBAAoB,EAIzB,KAAK,YAAY,EAKjB,KAAK,oBAAoB,EAI1B,MAAM,wBAAwB,CAAC;AA4ChC,OAAO,YAAY,MAAM,gBAAgB,CAAC;AAI1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACjF,OAAO,KAAK,YAAY,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAGjE,KAAK,8BAA8B,GAAG,eAAe,GAAG;IAMtD,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAkBF,KAAK,4BAA4B,GAAG,WAAW,CAAC;AAChD,KAAK,0BAA0B,GAAG;IAChC,mBAAmB,EAAE,cAAc,CAAC;IACpC,oBAAoB,EAAE,cAAc,CAAC;CACtC,CAAC;AAkCF,eAAO,MAAM,+BAA+B,UACnC,OAAO,KACb,4BAA4B,GAAG,IAsBjC,CAAC;AAcF,eAAO,MAAM,2BAA2B,QAAS,CAAC;AAClD,eAAO,MAAM,8BAA8B,OAAQ,CAAC;AA4BpD,eAAO,MAAM,kCAAkC,QAAS,CAAC;AAEzD,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAKhD,eAAO,MAAM,oCAAoC,OAAO,CAAC;AAKzD,eAAO,MAAM,gCAAgC,QAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -661,6 +661,33 @@ const isAndroidLinkLossError = (error) => {
|
|
|
661
661
|
code === hdShared.HardwareErrorCode.BleCharacteristicNotifyError ||
|
|
662
662
|
code === hdShared.HardwareErrorCode.BleConnectedError);
|
|
663
663
|
};
|
|
664
|
+
const logIosBleBondDiagnostic = (phase, error, outcome) => {
|
|
665
|
+
if (reactNative.Platform.OS !== 'ios')
|
|
666
|
+
return;
|
|
667
|
+
const nativeError = (error !== null && error !== void 0 ? error : {});
|
|
668
|
+
const scalar = (value) => typeof value === 'string' || typeof value === 'number' ? value : undefined;
|
|
669
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport][BleBondDiagnostic] probe error', {
|
|
670
|
+
phase,
|
|
671
|
+
outcome,
|
|
672
|
+
errorName: scalar(nativeError.name),
|
|
673
|
+
errorCode: scalar(nativeError.errorCode),
|
|
674
|
+
attErrorCode: scalar(nativeError.attErrorCode),
|
|
675
|
+
iosErrorCode: scalar(nativeError.iosErrorCode),
|
|
676
|
+
androidErrorCode: scalar(nativeError.androidErrorCode),
|
|
677
|
+
nativeStaleBond: isNativeBleStaleBondError(error),
|
|
678
|
+
hardwareStaleBond: hdShared.isBleStaleBondHardwareError(error),
|
|
679
|
+
nativeDisconnect: isNativeBleDisconnectError(error),
|
|
680
|
+
});
|
|
681
|
+
};
|
|
682
|
+
const IOS_PROBE_DISCONNECT_EVIDENCE_TTL_MS = 5 * 60000;
|
|
683
|
+
const isIosProbeDisconnectError = (error) => {
|
|
684
|
+
if (reactNative.Platform.OS !== 'ios')
|
|
685
|
+
return false;
|
|
686
|
+
const code = error === null || error === void 0 ? void 0 : error.errorCode;
|
|
687
|
+
return (isNativeBleDisconnectError(error) ||
|
|
688
|
+
code === hdShared.HardwareErrorCode.BleDeviceNotBonded ||
|
|
689
|
+
code === hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
690
|
+
};
|
|
664
691
|
const BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
|
|
665
692
|
const DEVICE_SCAN_TIMEOUT_MS = 3000;
|
|
666
693
|
const IOS_NOTIFY_READY_DELAY_MS = 150;
|
|
@@ -856,6 +883,7 @@ class ReactNativeBleTransport {
|
|
|
856
883
|
this.confirmedProtocolV2 = new Set();
|
|
857
884
|
this.protocolReprobeFailures = new Map();
|
|
858
885
|
this.silentDetections = new Map();
|
|
886
|
+
this.iosProbeDisconnectEvidence = new Map();
|
|
859
887
|
this.androidGattCacheRefreshes = new Set();
|
|
860
888
|
this.staleBondErrors = new Map();
|
|
861
889
|
this.androidLinkStartedAt = new Map();
|
|
@@ -2090,6 +2118,7 @@ class ReactNativeBleTransport {
|
|
|
2090
2118
|
this.androidLinkStartedAt.clear();
|
|
2091
2119
|
stopBleKeyMissingTracking();
|
|
2092
2120
|
stopBleEncryptionTracking();
|
|
2121
|
+
this.iosProbeDisconnectEvidence.clear();
|
|
2093
2122
|
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
2094
2123
|
(_a = this.runPromise) === null || _a === void 0 ? void 0 : _a.reject(error);
|
|
2095
2124
|
this.runPromise = null;
|
|
@@ -2575,6 +2604,50 @@ class ReactNativeBleTransport {
|
|
|
2575
2604
|
createProtocolDetectionError() {
|
|
2576
2605
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1 GetFeatures or Protocol V2 Ping');
|
|
2577
2606
|
}
|
|
2607
|
+
rememberIosProbeDisconnect(uuid, phase, error) {
|
|
2608
|
+
if (!isIosProbeDisconnectError(error))
|
|
2609
|
+
return undefined;
|
|
2610
|
+
const previous = this.getIosProbeDisconnectEvidence(uuid);
|
|
2611
|
+
const errorCode = error === null || error === void 0 ? void 0 : error.errorCode;
|
|
2612
|
+
const evidence = Object.assign(Object.assign({ phase }, (typeof errorCode === 'string' || typeof errorCode === 'number' ? { errorCode } : {})), { recordedAt: Date.now() });
|
|
2613
|
+
this.iosProbeDisconnectEvidence.set(uuid, evidence);
|
|
2614
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport][BleBondDiagnostic] probe disconnect recorded', {
|
|
2615
|
+
phase,
|
|
2616
|
+
errorCode: evidence.errorCode,
|
|
2617
|
+
repeated: Boolean(previous),
|
|
2618
|
+
});
|
|
2619
|
+
if (!previous)
|
|
2620
|
+
return undefined;
|
|
2621
|
+
return this.confirmIosProbeDisconnect(uuid, 'repeated-probe-disconnect');
|
|
2622
|
+
}
|
|
2623
|
+
getIosProbeDisconnectEvidence(uuid) {
|
|
2624
|
+
const evidence = this.iosProbeDisconnectEvidence.get(uuid);
|
|
2625
|
+
if (!evidence)
|
|
2626
|
+
return undefined;
|
|
2627
|
+
const ageMs = Date.now() - evidence.recordedAt;
|
|
2628
|
+
if (ageMs <= IOS_PROBE_DISCONNECT_EVIDENCE_TTL_MS)
|
|
2629
|
+
return evidence;
|
|
2630
|
+
this.iosProbeDisconnectEvidence.delete(uuid);
|
|
2631
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport][BleBondDiagnostic] probe disconnect expired', {
|
|
2632
|
+
triggerPhase: evidence.phase,
|
|
2633
|
+
triggerErrorCode: evidence.errorCode,
|
|
2634
|
+
ageMs,
|
|
2635
|
+
});
|
|
2636
|
+
return undefined;
|
|
2637
|
+
}
|
|
2638
|
+
confirmIosProbeDisconnect(uuid, confirmation) {
|
|
2639
|
+
const evidence = this.getIosProbeDisconnectEvidence(uuid);
|
|
2640
|
+
if (!evidence)
|
|
2641
|
+
return undefined;
|
|
2642
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport][BleBondDiagnostic] stale bond confirmed', {
|
|
2643
|
+
confirmation,
|
|
2644
|
+
triggerPhase: evidence.phase,
|
|
2645
|
+
triggerErrorCode: evidence.errorCode,
|
|
2646
|
+
evidenceAgeMs: Date.now() - evidence.recordedAt,
|
|
2647
|
+
finalErrorCode: hdShared.HardwareErrorCode.BleBondInvalid,
|
|
2648
|
+
});
|
|
2649
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleBondInvalid);
|
|
2650
|
+
}
|
|
2578
2651
|
clearProbeProtocol(uuid, protocol) {
|
|
2579
2652
|
if (this.probingProtocols.get(uuid) === protocol) {
|
|
2580
2653
|
this.probingProtocols.delete(uuid);
|
|
@@ -2588,9 +2661,10 @@ class ReactNativeBleTransport {
|
|
|
2588
2661
|
return (_a = this.deviceProtocol.get(uuid)) !== null && _a !== void 0 ? _a : this.probingProtocols.get(uuid);
|
|
2589
2662
|
}
|
|
2590
2663
|
detectProtocol(uuid, expectedProtocol, protocolHint, rebuildTransport) {
|
|
2591
|
-
var _a;
|
|
2664
|
+
var _a, _b;
|
|
2592
2665
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2593
2666
|
if (expectedProtocol === 'V1') {
|
|
2667
|
+
this.iosProbeDisconnectEvidence.delete(uuid);
|
|
2594
2668
|
this.deviceProtocol.set(uuid, 'V1');
|
|
2595
2669
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol selected', {
|
|
2596
2670
|
deviceId: uuid,
|
|
@@ -2604,6 +2678,7 @@ class ReactNativeBleTransport {
|
|
|
2604
2678
|
}
|
|
2605
2679
|
if (expectedProtocol === 'V2') {
|
|
2606
2680
|
if (yield this.probeProtocolV2(uuid)) {
|
|
2681
|
+
this.iosProbeDisconnectEvidence.delete(uuid);
|
|
2607
2682
|
this.deviceProtocol.set(uuid, 'V2');
|
|
2608
2683
|
this.sessionProtocols.set(uuid, 'V2');
|
|
2609
2684
|
this.confirmedProtocolV2.add(uuid);
|
|
@@ -2614,6 +2689,19 @@ class ReactNativeBleTransport {
|
|
|
2614
2689
|
});
|
|
2615
2690
|
return 'V2';
|
|
2616
2691
|
}
|
|
2692
|
+
const staleBondError = this.confirmIosProbeDisconnect(uuid, 'follow-up-no-response');
|
|
2693
|
+
if (reactNative.Platform.OS === 'ios') {
|
|
2694
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport][BleBondDiagnostic] protocol detection ended', {
|
|
2695
|
+
outcome: 'expected-v2-no-response',
|
|
2696
|
+
expectedProtocol,
|
|
2697
|
+
protocolHint: protocolHint !== null && protocolHint !== void 0 ? protocolHint : 'none',
|
|
2698
|
+
finalErrorCode: staleBondError
|
|
2699
|
+
? hdShared.HardwareErrorCode.BleBondInvalid
|
|
2700
|
+
: hdShared.HardwareErrorCode.RuntimeError,
|
|
2701
|
+
});
|
|
2702
|
+
}
|
|
2703
|
+
if (staleBondError)
|
|
2704
|
+
throw staleBondError;
|
|
2617
2705
|
throw this.createProtocolMismatchError(expectedProtocol);
|
|
2618
2706
|
}
|
|
2619
2707
|
const sessionProtocol = this.sessionProtocols.get(uuid);
|
|
@@ -2637,6 +2725,7 @@ class ReactNativeBleTransport {
|
|
|
2637
2725
|
}
|
|
2638
2726
|
const detected = protocol === 'V1' ? yield this.probeProtocolV1(uuid) : yield this.probeProtocolV2(uuid);
|
|
2639
2727
|
if (detected) {
|
|
2728
|
+
this.iosProbeDisconnectEvidence.delete(uuid);
|
|
2640
2729
|
this.deviceProtocol.set(uuid, protocol);
|
|
2641
2730
|
this.sessionProtocols.set(uuid, protocol);
|
|
2642
2731
|
if (protocol === 'V2') {
|
|
@@ -2662,6 +2751,26 @@ class ReactNativeBleTransport {
|
|
|
2662
2751
|
}
|
|
2663
2752
|
this.deviceProtocol.delete(uuid);
|
|
2664
2753
|
this.probingProtocols.delete(uuid);
|
|
2754
|
+
const staleBondError = this.confirmIosProbeDisconnect(uuid, 'follow-up-no-response');
|
|
2755
|
+
if (reactNative.Platform.OS === 'ios') {
|
|
2756
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport][BleBondDiagnostic] protocol detection ended', {
|
|
2757
|
+
outcome: 'all-probes-no-response',
|
|
2758
|
+
expectedProtocol: expectedProtocol !== null && expectedProtocol !== void 0 ? expectedProtocol : 'none',
|
|
2759
|
+
protocolHint: protocolHint !== null && protocolHint !== void 0 ? protocolHint : 'none',
|
|
2760
|
+
sessionProtocol: sessionProtocol !== null && sessionProtocol !== void 0 ? sessionProtocol : 'none',
|
|
2761
|
+
probeOrder: probeOrder.join(','),
|
|
2762
|
+
trustSessionProtocol,
|
|
2763
|
+
reprobeFailures,
|
|
2764
|
+
transportCached: Boolean(transportCache[uuid]),
|
|
2765
|
+
notifyActive: Boolean((_b = transportCache[uuid]) === null || _b === void 0 ? void 0 : _b.notifySubscription),
|
|
2766
|
+
staleBondRecorded: this.iosProbeDisconnectEvidence.has(uuid),
|
|
2767
|
+
finalErrorCode: staleBondError
|
|
2768
|
+
? hdShared.HardwareErrorCode.BleBondInvalid
|
|
2769
|
+
: hdShared.HardwareErrorCode.BleTimeoutError,
|
|
2770
|
+
});
|
|
2771
|
+
}
|
|
2772
|
+
if (staleBondError)
|
|
2773
|
+
throw staleBondError;
|
|
2665
2774
|
throw this.createProtocolDetectionError();
|
|
2666
2775
|
});
|
|
2667
2776
|
}
|
|
@@ -2745,7 +2854,12 @@ class ReactNativeBleTransport {
|
|
|
2745
2854
|
catch (error) {
|
|
2746
2855
|
this.clearProbeProtocol(uuid, 'V1');
|
|
2747
2856
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe failed:', error);
|
|
2748
|
-
|
|
2857
|
+
const shouldRethrow = shouldRethrowProtocolProbeError(error);
|
|
2858
|
+
const staleBondError = this.rememberIosProbeDisconnect(uuid, 'protocol-v1-get-features', error);
|
|
2859
|
+
logIosBleBondDiagnostic('protocol-v1-get-features', error, shouldRethrow ? 'rethrow' : 'fallback');
|
|
2860
|
+
if (staleBondError)
|
|
2861
|
+
throw staleBondError;
|
|
2862
|
+
if (shouldRethrow) {
|
|
2749
2863
|
throw error;
|
|
2750
2864
|
}
|
|
2751
2865
|
return false;
|
|
@@ -2761,18 +2875,28 @@ class ReactNativeBleTransport {
|
|
|
2761
2875
|
this.probingProtocols.set(uuid, 'V2');
|
|
2762
2876
|
(_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
2763
2877
|
this.throwIfStaleBondError(uuid);
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2878
|
+
let detected;
|
|
2879
|
+
try {
|
|
2880
|
+
detected = yield transport.probeProtocolV2({
|
|
2881
|
+
call: (name, data, options) => this.callProtocolV2(uuid, name, data, options),
|
|
2882
|
+
timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT_MS,
|
|
2883
|
+
logger: Log,
|
|
2884
|
+
logPrefix: 'ProtocolV2 RN-BLE',
|
|
2885
|
+
onProbeFailed: () => {
|
|
2886
|
+
var _a;
|
|
2887
|
+
(_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
2888
|
+
this.resetProtocolV2Frames(uuid);
|
|
2889
|
+
},
|
|
2890
|
+
shouldRethrow: shouldRethrowProtocolProbeError,
|
|
2891
|
+
});
|
|
2892
|
+
}
|
|
2893
|
+
catch (error) {
|
|
2894
|
+
const staleBondError = this.rememberIosProbeDisconnect(uuid, 'protocol-v2-ping', error);
|
|
2895
|
+
logIosBleBondDiagnostic('protocol-v2-ping', error, 'rethrow');
|
|
2896
|
+
if (staleBondError)
|
|
2897
|
+
throw staleBondError;
|
|
2898
|
+
throw error;
|
|
2899
|
+
}
|
|
2776
2900
|
if (!detected) {
|
|
2777
2901
|
this.clearProbeProtocol(uuid, 'V2');
|
|
2778
2902
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-transport-react-native",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4-alpha.0",
|
|
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.
|
|
24
|
-
"@onekeyfe/hd-shared": "1.2.
|
|
25
|
-
"@onekeyfe/hd-transport": "1.2.
|
|
23
|
+
"@onekeyfe/hd-core": "1.2.4-alpha.0",
|
|
24
|
+
"@onekeyfe/hd-shared": "1.2.4-alpha.0",
|
|
25
|
+
"@onekeyfe/hd-transport": "1.2.4-alpha.0",
|
|
26
26
|
"@onekeyfe/react-native-ble-utils": "0.1.9",
|
|
27
27
|
"react-native-ble-plx": "3.5.1"
|
|
28
28
|
},
|
|
29
|
-
"gitHead": "
|
|
29
|
+
"gitHead": "4996c67dbb5a3ac85c2559b164d9a931274890a7"
|
|
30
30
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { Platform } from 'react-native';
|
|
2
|
+
import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
3
|
+
|
|
1
4
|
import ReactNativeBleTransport, { PROTOCOL_REPROBE_FALLBACK_ATTEMPTS } from '../index';
|
|
2
5
|
import protocolV1Schema from './protocolV1SchemaFixture';
|
|
3
6
|
|
|
@@ -89,7 +92,9 @@ describe('protocol re-probe after a known device goes away', () => {
|
|
|
89
92
|
test('an unknown device still probes both protocols', async () => {
|
|
90
93
|
const { transport, probeV1, probeV2 } = createHarness({ v1: false, v2: false });
|
|
91
94
|
|
|
92
|
-
await expect(detect(transport)).rejects.
|
|
95
|
+
await expect(detect(transport)).rejects.toMatchObject({
|
|
96
|
+
errorCode: HardwareErrorCode.BleTimeoutError,
|
|
97
|
+
});
|
|
93
98
|
|
|
94
99
|
expect(probeV1).toHaveBeenCalledTimes(1);
|
|
95
100
|
expect(probeV2).toHaveBeenCalledTimes(1);
|
|
@@ -154,3 +159,113 @@ describe('protocol re-probe after a known device goes away', () => {
|
|
|
154
159
|
expect((known.transport as any).protocolReprobeFailures.get(UUID)).toBeUndefined();
|
|
155
160
|
});
|
|
156
161
|
});
|
|
162
|
+
|
|
163
|
+
describe('iOS reset-device stale bond confirmation', () => {
|
|
164
|
+
afterEach(() => {
|
|
165
|
+
Object.assign(Platform, { OS: 'ios' });
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test('promotes a probe disconnect followed by a silent reconnect to BleBondInvalid', async () => {
|
|
169
|
+
const transport = new ReactNativeBleTransport({});
|
|
170
|
+
transport.configure(protocolV1Schema);
|
|
171
|
+
(transport as any).callProtocolV1 = jest
|
|
172
|
+
.fn()
|
|
173
|
+
.mockRejectedValue(ERRORS.TypedError(HardwareErrorCode.BleDeviceNotBonded));
|
|
174
|
+
|
|
175
|
+
await expect(detect(transport)).rejects.toMatchObject({
|
|
176
|
+
errorCode: HardwareErrorCode.BleDeviceNotBonded,
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
(transport as any).probeProtocolV1 = jest.fn(() => Promise.resolve(false));
|
|
180
|
+
(transport as any).probeProtocolV2 = jest.fn(() => Promise.resolve(false));
|
|
181
|
+
(transport as any).resetProbeStateAfterProtocolProbe = jest.fn(() => Promise.resolve());
|
|
182
|
+
|
|
183
|
+
await expect(detect(transport)).rejects.toMatchObject({
|
|
184
|
+
errorCode: HardwareErrorCode.BleBondInvalid,
|
|
185
|
+
});
|
|
186
|
+
await expect(detect(transport)).rejects.toMatchObject({
|
|
187
|
+
errorCode: HardwareErrorCode.BleBondInvalid,
|
|
188
|
+
});
|
|
189
|
+
expect((transport as any).iosProbeDisconnectEvidence.has(UUID)).toBe(true);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test('promotes a repeated iOS probe disconnect without waiting for probe timeouts', () => {
|
|
193
|
+
const { transport } = createHarness({ v1: false, v2: false });
|
|
194
|
+
const firstError = Object.assign(new Error('The specified device has disconnected from us'), {
|
|
195
|
+
errorCode: 201,
|
|
196
|
+
iosErrorCode: 7,
|
|
197
|
+
});
|
|
198
|
+
const secondError = ERRORS.TypedError(HardwareErrorCode.BleDeviceDisconnected);
|
|
199
|
+
|
|
200
|
+
expect(
|
|
201
|
+
(transport as any).rememberIosProbeDisconnect(UUID, 'protocol-v1-get-features', firstError)
|
|
202
|
+
).toBeUndefined();
|
|
203
|
+
expect(
|
|
204
|
+
(transport as any).rememberIosProbeDisconnect(UUID, 'protocol-v2-ping', secondError)
|
|
205
|
+
).toMatchObject({ errorCode: HardwareErrorCode.BleBondInvalid });
|
|
206
|
+
expect((transport as any).iosProbeDisconnectEvidence.has(UUID)).toBe(true);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test('clears the disconnect evidence after a valid protocol response', async () => {
|
|
210
|
+
const { transport } = createHarness({ v1: true, v2: false });
|
|
211
|
+
(transport as any).rememberIosProbeDisconnect(
|
|
212
|
+
UUID,
|
|
213
|
+
'protocol-v1-get-features',
|
|
214
|
+
ERRORS.TypedError(HardwareErrorCode.BleDeviceNotBonded)
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
await expect(detect(transport)).resolves.toBe('V1');
|
|
218
|
+
expect((transport as any).iosProbeDisconnectEvidence.has(UUID)).toBe(false);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test('preserves evidence across disconnect and stalled setup cleanup', async () => {
|
|
222
|
+
const { transport } = createHarness({ v1: false, v2: false });
|
|
223
|
+
(transport as any).rememberIosProbeDisconnect(
|
|
224
|
+
UUID,
|
|
225
|
+
'protocol-v1-get-features',
|
|
226
|
+
ERRORS.TypedError(HardwareErrorCode.BleDeviceNotBonded)
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
await (transport as any).disconnectUnlocked(UUID);
|
|
230
|
+
expect((transport as any).iosProbeDisconnectEvidence.has(UUID)).toBe(true);
|
|
231
|
+
|
|
232
|
+
(transport as any).abandonStalledConnection(UUID, 'connect-native');
|
|
233
|
+
expect((transport as any).iosProbeDisconnectEvidence.has(UUID)).toBe(true);
|
|
234
|
+
|
|
235
|
+
await expect(detect(transport)).rejects.toMatchObject({
|
|
236
|
+
errorCode: HardwareErrorCode.BleBondInvalid,
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test('expires old disconnect evidence before classifying a later timeout', async () => {
|
|
241
|
+
const { transport } = createHarness({ v1: false, v2: false });
|
|
242
|
+
(transport as any).rememberIosProbeDisconnect(
|
|
243
|
+
UUID,
|
|
244
|
+
'protocol-v1-get-features',
|
|
245
|
+
ERRORS.TypedError(HardwareErrorCode.BleDeviceNotBonded)
|
|
246
|
+
);
|
|
247
|
+
const evidence = (transport as any).iosProbeDisconnectEvidence.get(UUID);
|
|
248
|
+
evidence.recordedAt = Date.now() - 5 * 60_000 - 1;
|
|
249
|
+
|
|
250
|
+
await expect(detect(transport)).rejects.toMatchObject({
|
|
251
|
+
errorCode: HardwareErrorCode.BleTimeoutError,
|
|
252
|
+
});
|
|
253
|
+
expect((transport as any).iosProbeDisconnectEvidence.has(UUID)).toBe(false);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test('does not classify the same sequence as a stale bond on Android', async () => {
|
|
257
|
+
Object.assign(Platform, { OS: 'android' });
|
|
258
|
+
const { transport } = createHarness({ v1: false, v2: false });
|
|
259
|
+
|
|
260
|
+
expect(
|
|
261
|
+
(transport as any).rememberIosProbeDisconnect(
|
|
262
|
+
UUID,
|
|
263
|
+
'protocol-v1-get-features',
|
|
264
|
+
ERRORS.TypedError(HardwareErrorCode.BleDeviceNotBonded)
|
|
265
|
+
)
|
|
266
|
+
).toBeUndefined();
|
|
267
|
+
await expect(detect(transport)).rejects.toMatchObject({
|
|
268
|
+
errorCode: HardwareErrorCode.BleTimeoutError,
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -233,6 +233,55 @@ const isAndroidLinkLossError = (error: unknown): boolean => {
|
|
|
233
233
|
code === HardwareErrorCode.BleConnectedError
|
|
234
234
|
);
|
|
235
235
|
};
|
|
236
|
+
|
|
237
|
+
const logIosBleBondDiagnostic = (
|
|
238
|
+
phase: string,
|
|
239
|
+
error: unknown,
|
|
240
|
+
outcome: 'fallback' | 'rethrow'
|
|
241
|
+
) => {
|
|
242
|
+
if (Platform.OS !== 'ios') return;
|
|
243
|
+
|
|
244
|
+
const nativeError = (error ?? {}) as {
|
|
245
|
+
name?: unknown;
|
|
246
|
+
errorCode?: unknown;
|
|
247
|
+
attErrorCode?: unknown;
|
|
248
|
+
iosErrorCode?: unknown;
|
|
249
|
+
androidErrorCode?: unknown;
|
|
250
|
+
};
|
|
251
|
+
const scalar = (value: unknown) =>
|
|
252
|
+
typeof value === 'string' || typeof value === 'number' ? value : undefined;
|
|
253
|
+
|
|
254
|
+
Log?.debug('[ReactNativeBleTransport][BleBondDiagnostic] probe error', {
|
|
255
|
+
phase,
|
|
256
|
+
outcome,
|
|
257
|
+
errorName: scalar(nativeError.name),
|
|
258
|
+
errorCode: scalar(nativeError.errorCode),
|
|
259
|
+
attErrorCode: scalar(nativeError.attErrorCode),
|
|
260
|
+
iosErrorCode: scalar(nativeError.iosErrorCode),
|
|
261
|
+
androidErrorCode: scalar(nativeError.androidErrorCode),
|
|
262
|
+
nativeStaleBond: isNativeBleStaleBondError(error),
|
|
263
|
+
hardwareStaleBond: isBleStaleBondHardwareError(error),
|
|
264
|
+
nativeDisconnect: isNativeBleDisconnectError(error),
|
|
265
|
+
});
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
type IosProbeDisconnectEvidence = {
|
|
269
|
+
phase: 'protocol-v1-get-features' | 'protocol-v2-ping';
|
|
270
|
+
errorCode?: string | number;
|
|
271
|
+
recordedAt: number;
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const IOS_PROBE_DISCONNECT_EVIDENCE_TTL_MS = 5 * 60_000;
|
|
275
|
+
|
|
276
|
+
const isIosProbeDisconnectError = (error: unknown): boolean => {
|
|
277
|
+
if (Platform.OS !== 'ios') return false;
|
|
278
|
+
const code = (error as { errorCode?: unknown })?.errorCode;
|
|
279
|
+
return (
|
|
280
|
+
isNativeBleDisconnectError(error) ||
|
|
281
|
+
code === HardwareErrorCode.BleDeviceNotBonded ||
|
|
282
|
+
code === HardwareErrorCode.BleDeviceDisconnected
|
|
283
|
+
);
|
|
284
|
+
};
|
|
236
285
|
/** Consecutive wedged writes on one device before the BLE manager itself is recreated. */
|
|
237
286
|
export const BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
|
|
238
287
|
const DEVICE_SCAN_TIMEOUT_MS = 3000;
|
|
@@ -575,6 +624,14 @@ export default class ReactNativeBleTransport {
|
|
|
575
624
|
/** Endpoints whose last detection got no answer; 'woken' once their Initialize wake is spent. */
|
|
576
625
|
private silentDetections = new Map<string, 'silent' | 'woken'>();
|
|
577
626
|
|
|
627
|
+
/**
|
|
628
|
+
* iOS can report a reset Pro2 as a generic peripheral disconnect instead of
|
|
629
|
+
* CBErrorPeerRemovedPairingInformation. Keep that first probe failure across
|
|
630
|
+
* the automatic cold reconnect and require a second failed probe before
|
|
631
|
+
* classifying the OS bond as invalid.
|
|
632
|
+
*/
|
|
633
|
+
private iosProbeDisconnectEvidence = new Map<string, IosProbeDisconnectEvidence>();
|
|
634
|
+
|
|
578
635
|
/** Android endpoints whose cached GATT table is suspect; the next connect refreshes it. */
|
|
579
636
|
private androidGattCacheRefreshes = new Set<string>();
|
|
580
637
|
|
|
@@ -2111,6 +2168,7 @@ export default class ReactNativeBleTransport {
|
|
|
2111
2168
|
this.androidLinkStartedAt.clear();
|
|
2112
2169
|
stopBleKeyMissingTracking();
|
|
2113
2170
|
stopBleEncryptionTracking();
|
|
2171
|
+
this.iosProbeDisconnectEvidence.clear();
|
|
2114
2172
|
const error = ERRORS.TypedError(HardwareErrorCode.BleDeviceDisconnected);
|
|
2115
2173
|
this.runPromise?.reject(error);
|
|
2116
2174
|
this.runPromise = null;
|
|
@@ -2730,6 +2788,64 @@ export default class ReactNativeBleTransport {
|
|
|
2730
2788
|
);
|
|
2731
2789
|
}
|
|
2732
2790
|
|
|
2791
|
+
private rememberIosProbeDisconnect(
|
|
2792
|
+
uuid: string,
|
|
2793
|
+
phase: IosProbeDisconnectEvidence['phase'],
|
|
2794
|
+
error: unknown
|
|
2795
|
+
): Error | undefined {
|
|
2796
|
+
if (!isIosProbeDisconnectError(error)) return undefined;
|
|
2797
|
+
|
|
2798
|
+
const previous = this.getIosProbeDisconnectEvidence(uuid);
|
|
2799
|
+
const errorCode = (error as { errorCode?: unknown })?.errorCode;
|
|
2800
|
+
const evidence: IosProbeDisconnectEvidence = {
|
|
2801
|
+
phase,
|
|
2802
|
+
...(typeof errorCode === 'string' || typeof errorCode === 'number' ? { errorCode } : {}),
|
|
2803
|
+
recordedAt: Date.now(),
|
|
2804
|
+
};
|
|
2805
|
+
this.iosProbeDisconnectEvidence.set(uuid, evidence);
|
|
2806
|
+
Log?.debug('[ReactNativeBleTransport][BleBondDiagnostic] probe disconnect recorded', {
|
|
2807
|
+
phase,
|
|
2808
|
+
errorCode: evidence.errorCode,
|
|
2809
|
+
repeated: Boolean(previous),
|
|
2810
|
+
});
|
|
2811
|
+
|
|
2812
|
+
if (!previous) return undefined;
|
|
2813
|
+
return this.confirmIosProbeDisconnect(uuid, 'repeated-probe-disconnect');
|
|
2814
|
+
}
|
|
2815
|
+
|
|
2816
|
+
private getIosProbeDisconnectEvidence(uuid: string): IosProbeDisconnectEvidence | undefined {
|
|
2817
|
+
const evidence = this.iosProbeDisconnectEvidence.get(uuid);
|
|
2818
|
+
if (!evidence) return undefined;
|
|
2819
|
+
|
|
2820
|
+
const ageMs = Date.now() - evidence.recordedAt;
|
|
2821
|
+
if (ageMs <= IOS_PROBE_DISCONNECT_EVIDENCE_TTL_MS) return evidence;
|
|
2822
|
+
|
|
2823
|
+
this.iosProbeDisconnectEvidence.delete(uuid);
|
|
2824
|
+
Log?.debug('[ReactNativeBleTransport][BleBondDiagnostic] probe disconnect expired', {
|
|
2825
|
+
triggerPhase: evidence.phase,
|
|
2826
|
+
triggerErrorCode: evidence.errorCode,
|
|
2827
|
+
ageMs,
|
|
2828
|
+
});
|
|
2829
|
+
return undefined;
|
|
2830
|
+
}
|
|
2831
|
+
|
|
2832
|
+
private confirmIosProbeDisconnect(
|
|
2833
|
+
uuid: string,
|
|
2834
|
+
confirmation: 'repeated-probe-disconnect' | 'follow-up-no-response'
|
|
2835
|
+
): Error | undefined {
|
|
2836
|
+
const evidence = this.getIosProbeDisconnectEvidence(uuid);
|
|
2837
|
+
if (!evidence) return undefined;
|
|
2838
|
+
|
|
2839
|
+
Log?.debug('[ReactNativeBleTransport][BleBondDiagnostic] stale bond confirmed', {
|
|
2840
|
+
confirmation,
|
|
2841
|
+
triggerPhase: evidence.phase,
|
|
2842
|
+
triggerErrorCode: evidence.errorCode,
|
|
2843
|
+
evidenceAgeMs: Date.now() - evidence.recordedAt,
|
|
2844
|
+
finalErrorCode: HardwareErrorCode.BleBondInvalid,
|
|
2845
|
+
});
|
|
2846
|
+
return ERRORS.TypedError(HardwareErrorCode.BleBondInvalid);
|
|
2847
|
+
}
|
|
2848
|
+
|
|
2733
2849
|
private clearProbeProtocol(uuid: string, protocol: ProtocolType) {
|
|
2734
2850
|
if (this.probingProtocols.get(uuid) === protocol) {
|
|
2735
2851
|
this.probingProtocols.delete(uuid);
|
|
@@ -2761,6 +2877,7 @@ export default class ReactNativeBleTransport {
|
|
|
2761
2877
|
// received no response. skipProtocolProbe does not need it either — its only
|
|
2762
2878
|
// caller is the V2 firmware-install reconnect.
|
|
2763
2879
|
if (expectedProtocol === 'V1') {
|
|
2880
|
+
this.iosProbeDisconnectEvidence.delete(uuid);
|
|
2764
2881
|
this.deviceProtocol.set(uuid, 'V1');
|
|
2765
2882
|
Log?.debug('[ReactNativeBleTransport] protocol selected', {
|
|
2766
2883
|
deviceId: uuid,
|
|
@@ -2776,6 +2893,7 @@ export default class ReactNativeBleTransport {
|
|
|
2776
2893
|
|
|
2777
2894
|
if (expectedProtocol === 'V2') {
|
|
2778
2895
|
if (await this.probeProtocolV2(uuid)) {
|
|
2896
|
+
this.iosProbeDisconnectEvidence.delete(uuid);
|
|
2779
2897
|
this.deviceProtocol.set(uuid, 'V2');
|
|
2780
2898
|
this.sessionProtocols.set(uuid, 'V2');
|
|
2781
2899
|
this.confirmedProtocolV2.add(uuid);
|
|
@@ -2786,6 +2904,18 @@ export default class ReactNativeBleTransport {
|
|
|
2786
2904
|
});
|
|
2787
2905
|
return 'V2';
|
|
2788
2906
|
}
|
|
2907
|
+
const staleBondError = this.confirmIosProbeDisconnect(uuid, 'follow-up-no-response');
|
|
2908
|
+
if (Platform.OS === 'ios') {
|
|
2909
|
+
Log?.debug('[ReactNativeBleTransport][BleBondDiagnostic] protocol detection ended', {
|
|
2910
|
+
outcome: 'expected-v2-no-response',
|
|
2911
|
+
expectedProtocol,
|
|
2912
|
+
protocolHint: protocolHint ?? 'none',
|
|
2913
|
+
finalErrorCode: staleBondError
|
|
2914
|
+
? HardwareErrorCode.BleBondInvalid
|
|
2915
|
+
: HardwareErrorCode.RuntimeError,
|
|
2916
|
+
});
|
|
2917
|
+
}
|
|
2918
|
+
if (staleBondError) throw staleBondError;
|
|
2789
2919
|
throw this.createProtocolMismatchError(expectedProtocol);
|
|
2790
2920
|
}
|
|
2791
2921
|
|
|
@@ -2821,6 +2951,7 @@ export default class ReactNativeBleTransport {
|
|
|
2821
2951
|
const detected =
|
|
2822
2952
|
protocol === 'V1' ? await this.probeProtocolV1(uuid) : await this.probeProtocolV2(uuid);
|
|
2823
2953
|
if (detected) {
|
|
2954
|
+
this.iosProbeDisconnectEvidence.delete(uuid);
|
|
2824
2955
|
this.deviceProtocol.set(uuid, protocol);
|
|
2825
2956
|
this.sessionProtocols.set(uuid, protocol);
|
|
2826
2957
|
if (protocol === 'V2') {
|
|
@@ -2850,6 +2981,25 @@ export default class ReactNativeBleTransport {
|
|
|
2850
2981
|
|
|
2851
2982
|
this.deviceProtocol.delete(uuid);
|
|
2852
2983
|
this.probingProtocols.delete(uuid);
|
|
2984
|
+
const staleBondError = this.confirmIosProbeDisconnect(uuid, 'follow-up-no-response');
|
|
2985
|
+
if (Platform.OS === 'ios') {
|
|
2986
|
+
Log?.debug('[ReactNativeBleTransport][BleBondDiagnostic] protocol detection ended', {
|
|
2987
|
+
outcome: 'all-probes-no-response',
|
|
2988
|
+
expectedProtocol: expectedProtocol ?? 'none',
|
|
2989
|
+
protocolHint: protocolHint ?? 'none',
|
|
2990
|
+
sessionProtocol: sessionProtocol ?? 'none',
|
|
2991
|
+
probeOrder: probeOrder.join(','),
|
|
2992
|
+
trustSessionProtocol,
|
|
2993
|
+
reprobeFailures,
|
|
2994
|
+
transportCached: Boolean(transportCache[uuid]),
|
|
2995
|
+
notifyActive: Boolean(transportCache[uuid]?.notifySubscription),
|
|
2996
|
+
staleBondRecorded: this.iosProbeDisconnectEvidence.has(uuid),
|
|
2997
|
+
finalErrorCode: staleBondError
|
|
2998
|
+
? HardwareErrorCode.BleBondInvalid
|
|
2999
|
+
: HardwareErrorCode.BleTimeoutError,
|
|
3000
|
+
});
|
|
3001
|
+
}
|
|
3002
|
+
if (staleBondError) throw staleBondError;
|
|
2853
3003
|
throw this.createProtocolDetectionError();
|
|
2854
3004
|
}
|
|
2855
3005
|
|
|
@@ -2948,7 +3098,19 @@ export default class ReactNativeBleTransport {
|
|
|
2948
3098
|
} catch (error) {
|
|
2949
3099
|
this.clearProbeProtocol(uuid, 'V1');
|
|
2950
3100
|
Log?.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe failed:', error);
|
|
2951
|
-
|
|
3101
|
+
const shouldRethrow = shouldRethrowProtocolProbeError(error);
|
|
3102
|
+
const staleBondError = this.rememberIosProbeDisconnect(
|
|
3103
|
+
uuid,
|
|
3104
|
+
'protocol-v1-get-features',
|
|
3105
|
+
error
|
|
3106
|
+
);
|
|
3107
|
+
logIosBleBondDiagnostic(
|
|
3108
|
+
'protocol-v1-get-features',
|
|
3109
|
+
error,
|
|
3110
|
+
shouldRethrow ? 'rethrow' : 'fallback'
|
|
3111
|
+
);
|
|
3112
|
+
if (staleBondError) throw staleBondError;
|
|
3113
|
+
if (shouldRethrow) {
|
|
2952
3114
|
throw error;
|
|
2953
3115
|
}
|
|
2954
3116
|
return false;
|
|
@@ -2963,18 +3125,26 @@ export default class ReactNativeBleTransport {
|
|
|
2963
3125
|
this.probingProtocols.set(uuid, 'V2');
|
|
2964
3126
|
this.protocolV2Assemblers.get(uuid)?.reset();
|
|
2965
3127
|
this.throwIfStaleBondError(uuid);
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
3128
|
+
let detected: boolean;
|
|
3129
|
+
try {
|
|
3130
|
+
detected = await probeProtocolV2Helper({
|
|
3131
|
+
call: (name: string, data: Record<string, unknown>, options?: TransportCallOptions) =>
|
|
3132
|
+
this.callProtocolV2(uuid, name, data, options),
|
|
3133
|
+
timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT_MS,
|
|
3134
|
+
logger: Log,
|
|
3135
|
+
logPrefix: 'ProtocolV2 RN-BLE',
|
|
3136
|
+
onProbeFailed: () => {
|
|
3137
|
+
this.protocolV2Assemblers.get(uuid)?.reset();
|
|
3138
|
+
this.resetProtocolV2Frames(uuid);
|
|
3139
|
+
},
|
|
3140
|
+
shouldRethrow: shouldRethrowProtocolProbeError,
|
|
3141
|
+
});
|
|
3142
|
+
} catch (error) {
|
|
3143
|
+
const staleBondError = this.rememberIosProbeDisconnect(uuid, 'protocol-v2-ping', error);
|
|
3144
|
+
logIosBleBondDiagnostic('protocol-v2-ping', error, 'rethrow');
|
|
3145
|
+
if (staleBondError) throw staleBondError;
|
|
3146
|
+
throw error;
|
|
3147
|
+
}
|
|
2978
3148
|
if (!detected) {
|
|
2979
3149
|
this.clearProbeProtocol(uuid, 'V2');
|
|
2980
3150
|
} else {
|