@onekeyfe/hd-transport-react-native 1.2.0-alpha.62 → 1.2.0-alpha.64
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 +13 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -1
- package/package.json +5 -5
- package/src/__tests__/protocolReprobe.test.ts +133 -0
- package/src/index.ts +41 -1
package/dist/index.d.ts
CHANGED
|
@@ -64,6 +64,14 @@ declare function getProtocolV2BleTuning(): {
|
|
|
64
64
|
* inside the native budget, so this only fires when the native timeout did not.
|
|
65
65
|
*/
|
|
66
66
|
declare const BLE_CONNECT_TIMEOUT_MS: number;
|
|
67
|
+
/**
|
|
68
|
+
* How many times a known device may fail its own protocol before we probe the others
|
|
69
|
+
* again. Reconnect polling during a device reboot repeats this every few seconds, and
|
|
70
|
+
* probing Protocol V2 costs a 10s Ping timeout, so paying it on every attempt for a
|
|
71
|
+
* device we just spoke V1 to dominates the wait. A firmware update can legitimately
|
|
72
|
+
* change a device's protocol, so the shortcut has to expire rather than stick.
|
|
73
|
+
*/
|
|
74
|
+
declare const PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = 3;
|
|
67
75
|
/** Consecutive connect timeouts on one device before the BLE manager itself is recreated. */
|
|
68
76
|
declare const BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
|
|
69
77
|
type IOneKeyDevice = OneKeyDeviceInfoBase & Device;
|
|
@@ -94,6 +102,10 @@ declare class ReactNativeBleTransport {
|
|
|
94
102
|
/** Consecutive connect timeouts per device; reset by any connect that settles. */
|
|
95
103
|
private connectTimeoutCounts;
|
|
96
104
|
private deviceProtocolHints;
|
|
105
|
+
/** Protocol this device actually answered on, kept across reconnects of one session. */
|
|
106
|
+
private sessionProtocols;
|
|
107
|
+
/** Consecutive detections that failed while trusting sessionProtocols. */
|
|
108
|
+
private protocolReprobeFailures;
|
|
97
109
|
private protocolV2Assemblers;
|
|
98
110
|
private protocolV2FrameQueues;
|
|
99
111
|
private protocolV2FramePromises;
|
|
@@ -178,4 +190,4 @@ declare class ReactNativeBleTransport {
|
|
|
178
190
|
getProtocolType(path: string): ProtocolType | undefined;
|
|
179
191
|
}
|
|
180
192
|
|
|
181
|
-
export { BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD, BLE_CONNECT_TIMEOUT_MS, BLE_WRITE_PACKET_TIMEOUT_MS, BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD, IOneKeyDevice, ProtocolV2BleTuning, configureProtocolV2BleTuning, ReactNativeBleTransport as default, getFirmwareUploadWriteRetryType, getProtocolV2BleTuning, resetProtocolV2BleTuning };
|
|
193
|
+
export { BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD, BLE_CONNECT_TIMEOUT_MS, BLE_WRITE_PACKET_TIMEOUT_MS, BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD, IOneKeyDevice, PROTOCOL_REPROBE_FALLBACK_ATTEMPTS, ProtocolV2BleTuning, configureProtocolV2BleTuning, ReactNativeBleTransport as default, getFirmwareUploadWriteRetryType, getProtocolV2BleTuning, resetProtocolV2BleTuning };
|
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,EAIL,UAAU,IAAI,aAAa,EAE5B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,SAAS,EAAE,EAEhB,KAAK,oBAAoB,EAIzB,KAAK,YAAY,EAKjB,KAAK,oBAAoB,EAG1B,MAAM,wBAAwB,CAAC;AAmBhC,OAAO,YAAY,MAAM,gBAAgB,CAAC;AAK1C,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;AAgBjE,KAAK,4BAA4B,GAAG,WAAW,CAAC;AAChD,KAAK,0BAA0B,GAAG;IAChC,mBAAmB,EAAE,cAAc,CAAC;IACpC,oBAAoB,EAAE,cAAc,CAAC;CACtC,CAAC;AAOF,eAAO,MAAM,+BAA+B,UACnC,OAAO,KACb,4BAA4B,GAAG,IAsBjC,CAAC;AAcF,eAAO,MAAM,2BAA2B,QAAS,CAAC;AAOlD,eAAO,MAAM,yCAAyC,IAAI,CAAC;AAI3D,MAAM,MAAM,mBAAmB,GAAG;IAChC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAiBF,wBAAgB,4BAA4B,CAAC,MAAM,GAAE,mBAAwB,QAY5E;AAED,wBAAgB,wBAAwB,SAGvC;AAED,wBAAgB,sBAAsB;;;EAErC;AAgCD,eAAO,MAAM,sBAAsB,QAA2C,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -295,6 +295,7 @@ const fallbackConnectOptions = {
|
|
|
295
295
|
timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
|
|
296
296
|
};
|
|
297
297
|
const BLE_CONNECT_TIMEOUT_MS = BLE_NATIVE_CONNECT_TIMEOUT_MS * 2 + 2000;
|
|
298
|
+
const PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = 3;
|
|
298
299
|
const BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
|
|
299
300
|
const CONNECT_TIMEOUT_MESSAGE = 'BLE connect timeout after';
|
|
300
301
|
const isConnectTimeoutError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleConnectedError &&
|
|
@@ -361,6 +362,8 @@ class ReactNativeBleTransport {
|
|
|
361
362
|
this.writeTimeoutCounts = new Map();
|
|
362
363
|
this.connectTimeoutCounts = new Map();
|
|
363
364
|
this.deviceProtocolHints = new Map();
|
|
365
|
+
this.sessionProtocols = new Map();
|
|
366
|
+
this.protocolReprobeFailures = new Map();
|
|
364
367
|
this.protocolV2Assemblers = new Map();
|
|
365
368
|
this.protocolV2FrameQueues = new Map();
|
|
366
369
|
this.protocolV2FramePromises = new Map();
|
|
@@ -1304,6 +1307,8 @@ class ReactNativeBleTransport {
|
|
|
1304
1307
|
this.deviceProtocol.delete(session);
|
|
1305
1308
|
this.probingProtocols.delete(session);
|
|
1306
1309
|
this.deviceProtocolHints.delete(session);
|
|
1310
|
+
this.sessionProtocols.delete(session);
|
|
1311
|
+
this.protocolReprobeFailures.delete(session);
|
|
1307
1312
|
this.protocolV2Assemblers.delete(session);
|
|
1308
1313
|
this.resetProtocolV2Frames(session);
|
|
1309
1314
|
try {
|
|
@@ -1450,6 +1455,8 @@ class ReactNativeBleTransport {
|
|
|
1450
1455
|
});
|
|
1451
1456
|
this.deviceProtocol.clear();
|
|
1452
1457
|
this.probingProtocols.clear();
|
|
1458
|
+
this.sessionProtocols.clear();
|
|
1459
|
+
this.protocolReprobeFailures.clear();
|
|
1453
1460
|
this.monitorTokens.clear();
|
|
1454
1461
|
this.protocolV2Assemblers.clear();
|
|
1455
1462
|
try {
|
|
@@ -1478,10 +1485,12 @@ class ReactNativeBleTransport {
|
|
|
1478
1485
|
return (_a = this.deviceProtocol.get(uuid)) !== null && _a !== void 0 ? _a : this.probingProtocols.get(uuid);
|
|
1479
1486
|
}
|
|
1480
1487
|
detectProtocol(uuid, expectedProtocol, protocolHint, rebuildTransport) {
|
|
1488
|
+
var _a;
|
|
1481
1489
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1482
1490
|
if (expectedProtocol === 'V1') {
|
|
1483
1491
|
if (yield this.probeProtocolV1(uuid)) {
|
|
1484
1492
|
this.deviceProtocol.set(uuid, 'V1');
|
|
1493
|
+
this.sessionProtocols.set(uuid, 'V1');
|
|
1485
1494
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
|
|
1486
1495
|
deviceId: uuid,
|
|
1487
1496
|
protocol: 'V1',
|
|
@@ -1494,6 +1503,7 @@ class ReactNativeBleTransport {
|
|
|
1494
1503
|
if (expectedProtocol === 'V2') {
|
|
1495
1504
|
if (yield this.probeProtocolV2(uuid)) {
|
|
1496
1505
|
this.deviceProtocol.set(uuid, 'V2');
|
|
1506
|
+
this.sessionProtocols.set(uuid, 'V2');
|
|
1497
1507
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
|
|
1498
1508
|
deviceId: uuid,
|
|
1499
1509
|
protocol: 'V2',
|
|
@@ -1503,7 +1513,13 @@ class ReactNativeBleTransport {
|
|
|
1503
1513
|
}
|
|
1504
1514
|
throw this.createProtocolMismatchError(expectedProtocol);
|
|
1505
1515
|
}
|
|
1506
|
-
const
|
|
1516
|
+
const sessionProtocol = this.sessionProtocols.get(uuid);
|
|
1517
|
+
const reprobeFailures = (_a = this.protocolReprobeFailures.get(uuid)) !== null && _a !== void 0 ? _a : 0;
|
|
1518
|
+
const fullProbeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
|
|
1519
|
+
const trustSessionProtocol = sessionProtocol !== undefined &&
|
|
1520
|
+
!protocolHint &&
|
|
1521
|
+
reprobeFailures < PROTOCOL_REPROBE_FALLBACK_ATTEMPTS;
|
|
1522
|
+
const probeOrder = trustSessionProtocol ? [sessionProtocol] : fullProbeOrder;
|
|
1507
1523
|
for (let i = 0; i < probeOrder.length; i += 1) {
|
|
1508
1524
|
const protocol = probeOrder[i];
|
|
1509
1525
|
if (i > 0) {
|
|
@@ -1518,6 +1534,8 @@ class ReactNativeBleTransport {
|
|
|
1518
1534
|
const detected = protocol === 'V1' ? yield this.probeProtocolV1(uuid) : yield this.probeProtocolV2(uuid);
|
|
1519
1535
|
if (detected) {
|
|
1520
1536
|
this.deviceProtocol.set(uuid, protocol);
|
|
1537
|
+
this.sessionProtocols.set(uuid, protocol);
|
|
1538
|
+
this.protocolReprobeFailures.delete(uuid);
|
|
1521
1539
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
|
|
1522
1540
|
deviceId: uuid,
|
|
1523
1541
|
protocol,
|
|
@@ -1526,6 +1544,12 @@ class ReactNativeBleTransport {
|
|
|
1526
1544
|
return protocol;
|
|
1527
1545
|
}
|
|
1528
1546
|
}
|
|
1547
|
+
if (trustSessionProtocol) {
|
|
1548
|
+
this.protocolReprobeFailures.set(uuid, reprobeFailures + 1);
|
|
1549
|
+
}
|
|
1550
|
+
else {
|
|
1551
|
+
this.protocolReprobeFailures.delete(uuid);
|
|
1552
|
+
}
|
|
1529
1553
|
this.deviceProtocol.delete(uuid);
|
|
1530
1554
|
this.probingProtocols.delete(uuid);
|
|
1531
1555
|
throw this.createProtocolDetectionError();
|
|
@@ -1825,6 +1849,7 @@ exports.BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_CONNECT_TIMEOUT_MANAGE
|
|
|
1825
1849
|
exports.BLE_CONNECT_TIMEOUT_MS = BLE_CONNECT_TIMEOUT_MS;
|
|
1826
1850
|
exports.BLE_WRITE_PACKET_TIMEOUT_MS = BLE_WRITE_PACKET_TIMEOUT_MS;
|
|
1827
1851
|
exports.BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD;
|
|
1852
|
+
exports.PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = PROTOCOL_REPROBE_FALLBACK_ATTEMPTS;
|
|
1828
1853
|
exports.configureProtocolV2BleTuning = configureProtocolV2BleTuning;
|
|
1829
1854
|
exports["default"] = ReactNativeBleTransport;
|
|
1830
1855
|
exports.getFirmwareUploadWriteRetryType = getFirmwareUploadWriteRetryType;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-transport-react-native",
|
|
3
|
-
"version": "1.2.0-alpha.
|
|
3
|
+
"version": "1.2.0-alpha.64",
|
|
4
4
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -20,11 +20,11 @@
|
|
|
20
20
|
"lint:fix": "eslint . --fix"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@onekeyfe/hd-core": "1.2.0-alpha.
|
|
24
|
-
"@onekeyfe/hd-shared": "1.2.0-alpha.
|
|
25
|
-
"@onekeyfe/hd-transport": "1.2.0-alpha.
|
|
23
|
+
"@onekeyfe/hd-core": "1.2.0-alpha.64",
|
|
24
|
+
"@onekeyfe/hd-shared": "1.2.0-alpha.64",
|
|
25
|
+
"@onekeyfe/hd-transport": "1.2.0-alpha.64",
|
|
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": "636cd3d9874820414cee6faa7e521f90a42ce6d5"
|
|
30
30
|
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import ReactNativeBleTransport, { PROTOCOL_REPROBE_FALLBACK_ATTEMPTS } from '../index';
|
|
2
|
+
|
|
3
|
+
import messages from '@onekeyfe/hd-transport/messages.json';
|
|
4
|
+
|
|
5
|
+
jest.mock(
|
|
6
|
+
'react-native',
|
|
7
|
+
() => ({
|
|
8
|
+
Platform: { OS: 'ios', select: (spec: Record<string, unknown>) => spec.ios },
|
|
9
|
+
PermissionsAndroid: {
|
|
10
|
+
PERMISSIONS: {},
|
|
11
|
+
RESULTS: {},
|
|
12
|
+
request: jest.fn(),
|
|
13
|
+
requestMultiple: jest.fn(),
|
|
14
|
+
},
|
|
15
|
+
}),
|
|
16
|
+
{ virtual: true }
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
jest.mock('react-native-ble-plx', () => ({
|
|
20
|
+
BleATTErrorCode: { InvalidHandle: 1, UnlikelyError: 14 },
|
|
21
|
+
BleError: Error,
|
|
22
|
+
BleErrorCode: { DeviceDisconnected: 201, OperationStartFailed: 601 },
|
|
23
|
+
BleManager: jest.fn(),
|
|
24
|
+
ScanMode: { LowLatency: 2 },
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
jest.mock('@onekeyfe/react-native-ble-utils', () => ({
|
|
28
|
+
__esModule: true,
|
|
29
|
+
default: {
|
|
30
|
+
getConnectedPeripherals: jest.fn(() => Promise.resolve([])),
|
|
31
|
+
getBondedPeripherals: jest.fn(() => Promise.resolve([])),
|
|
32
|
+
pairDevice: jest.fn(() => Promise.resolve()),
|
|
33
|
+
},
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
const UUID = 'reprobe-device';
|
|
37
|
+
|
|
38
|
+
/** Drive detectProtocol with stubbed probes so we can observe which are attempted. */
|
|
39
|
+
function createHarness({ v1, v2 }: { v1: boolean; v2: boolean }) {
|
|
40
|
+
const transport = new ReactNativeBleTransport({});
|
|
41
|
+
transport.configure(messages);
|
|
42
|
+
const probeV1 = jest.fn(() => {
|
|
43
|
+
if (v1) {
|
|
44
|
+
(transport as any).deviceProtocol.set(UUID, 'V1');
|
|
45
|
+
}
|
|
46
|
+
return Promise.resolve(v1);
|
|
47
|
+
});
|
|
48
|
+
const probeV2 = jest.fn(() => {
|
|
49
|
+
if (v2) {
|
|
50
|
+
(transport as any).deviceProtocol.set(UUID, 'V2');
|
|
51
|
+
}
|
|
52
|
+
return Promise.resolve(v2);
|
|
53
|
+
});
|
|
54
|
+
(transport as any).probeProtocolV1 = probeV1;
|
|
55
|
+
(transport as any).probeProtocolV2 = probeV2;
|
|
56
|
+
(transport as any).resetProbeStateAfterProtocolProbe = jest.fn(() => Promise.resolve());
|
|
57
|
+
return { transport, probeV1, probeV2 };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const detect = (transport: ReactNativeBleTransport) =>
|
|
61
|
+
(transport as any).detectProtocol(UUID, undefined, undefined, () =>
|
|
62
|
+
Promise.resolve()
|
|
63
|
+
) as Promise<string>;
|
|
64
|
+
|
|
65
|
+
describe('protocol re-probe after a known device goes away', () => {
|
|
66
|
+
test('an unknown device still probes both protocols', async () => {
|
|
67
|
+
const { transport, probeV1, probeV2 } = createHarness({ v1: false, v2: false });
|
|
68
|
+
|
|
69
|
+
await expect(detect(transport)).rejects.toBeDefined();
|
|
70
|
+
|
|
71
|
+
expect(probeV1).toHaveBeenCalledTimes(1);
|
|
72
|
+
expect(probeV2).toHaveBeenCalledTimes(1);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('a device already known to speak V1 does not pay the V2 probe while it is away', async () => {
|
|
76
|
+
const known = createHarness({ v1: true, v2: false });
|
|
77
|
+
// First detection confirms V1 (this is the session that just talked to the device).
|
|
78
|
+
await expect(detect(known.transport)).resolves.toBe('V1');
|
|
79
|
+
|
|
80
|
+
// The device now reboots after a firmware install: V1 stops answering.
|
|
81
|
+
(known.transport as any).deviceProtocol.delete(UUID);
|
|
82
|
+
const probeV1 = jest.fn(() => Promise.resolve(false));
|
|
83
|
+
const probeV2 = jest.fn(() => Promise.resolve(false));
|
|
84
|
+
(known.transport as any).probeProtocolV1 = probeV1;
|
|
85
|
+
(known.transport as any).probeProtocolV2 = probeV2;
|
|
86
|
+
|
|
87
|
+
await expect(detect(known.transport)).rejects.toBeDefined();
|
|
88
|
+
|
|
89
|
+
expect(probeV1).toHaveBeenCalledTimes(1);
|
|
90
|
+
// The 10s Protocol V2 ping is pure waste for a device we just spoke V1 to.
|
|
91
|
+
expect(probeV2).not.toHaveBeenCalled();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('after repeated failures it re-probes every protocol again', async () => {
|
|
95
|
+
const known = createHarness({ v1: true, v2: false });
|
|
96
|
+
await expect(detect(known.transport)).resolves.toBe('V1');
|
|
97
|
+
|
|
98
|
+
(known.transport as any).deviceProtocol.delete(UUID);
|
|
99
|
+
const probeV1 = jest.fn(() => Promise.resolve(false));
|
|
100
|
+
const probeV2 = jest.fn(() => Promise.resolve(false));
|
|
101
|
+
(known.transport as any).probeProtocolV1 = probeV1;
|
|
102
|
+
(known.transport as any).probeProtocolV2 = probeV2;
|
|
103
|
+
|
|
104
|
+
for (let attempt = 0; attempt < PROTOCOL_REPROBE_FALLBACK_ATTEMPTS; attempt += 1) {
|
|
105
|
+
// eslint-disable-next-line no-await-in-loop
|
|
106
|
+
await expect(detect(known.transport)).rejects.toBeDefined();
|
|
107
|
+
}
|
|
108
|
+
expect(probeV2).not.toHaveBeenCalled();
|
|
109
|
+
|
|
110
|
+
// The device may genuinely have changed protocol, so the shortcut must expire.
|
|
111
|
+
await expect(detect(known.transport)).rejects.toBeDefined();
|
|
112
|
+
expect(probeV2).toHaveBeenCalled();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('a successful detection clears the failure streak', async () => {
|
|
116
|
+
const known = createHarness({ v1: true, v2: false });
|
|
117
|
+
await expect(detect(known.transport)).resolves.toBe('V1');
|
|
118
|
+
(known.transport as any).deviceProtocol.delete(UUID);
|
|
119
|
+
|
|
120
|
+
const failingV1 = jest.fn(() => Promise.resolve(false));
|
|
121
|
+
(known.transport as any).probeProtocolV1 = failingV1;
|
|
122
|
+
await expect(detect(known.transport)).rejects.toBeDefined();
|
|
123
|
+
|
|
124
|
+
// Device comes back.
|
|
125
|
+
(known.transport as any).probeProtocolV1 = jest.fn(() => {
|
|
126
|
+
(known.transport as any).deviceProtocol.set(UUID, 'V1');
|
|
127
|
+
return Promise.resolve(true);
|
|
128
|
+
});
|
|
129
|
+
await expect(detect(known.transport)).resolves.toBe('V1');
|
|
130
|
+
|
|
131
|
+
expect((known.transport as any).protocolReprobeFailures.get(UUID)).toBeUndefined();
|
|
132
|
+
});
|
|
133
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -199,6 +199,14 @@ const fallbackConnectOptions: Record<string, unknown> = {
|
|
|
199
199
|
* inside the native budget, so this only fires when the native timeout did not.
|
|
200
200
|
*/
|
|
201
201
|
export const BLE_CONNECT_TIMEOUT_MS = BLE_NATIVE_CONNECT_TIMEOUT_MS * 2 + 2000;
|
|
202
|
+
/**
|
|
203
|
+
* How many times a known device may fail its own protocol before we probe the others
|
|
204
|
+
* again. Reconnect polling during a device reboot repeats this every few seconds, and
|
|
205
|
+
* probing Protocol V2 costs a 10s Ping timeout, so paying it on every attempt for a
|
|
206
|
+
* device we just spoke V1 to dominates the wait. A firmware update can legitimately
|
|
207
|
+
* change a device's protocol, so the shortcut has to expire rather than stick.
|
|
208
|
+
*/
|
|
209
|
+
export const PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = 3;
|
|
202
210
|
/** Consecutive connect timeouts on one device before the BLE manager itself is recreated. */
|
|
203
211
|
export const BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
|
|
204
212
|
const CONNECT_TIMEOUT_MESSAGE = 'BLE connect timeout after';
|
|
@@ -311,6 +319,12 @@ export default class ReactNativeBleTransport {
|
|
|
311
319
|
|
|
312
320
|
private deviceProtocolHints: Map<string, ProtocolType> = new Map();
|
|
313
321
|
|
|
322
|
+
/** Protocol this device actually answered on, kept across reconnects of one session. */
|
|
323
|
+
private sessionProtocols: Map<string, ProtocolType> = new Map();
|
|
324
|
+
|
|
325
|
+
/** Consecutive detections that failed while trusting sessionProtocols. */
|
|
326
|
+
private protocolReprobeFailures: Map<string, number> = new Map();
|
|
327
|
+
|
|
314
328
|
private protocolV2Assemblers: Map<string, ProtocolV2FrameAssembler> = new Map();
|
|
315
329
|
|
|
316
330
|
private protocolV2FrameQueues: Map<string, Uint8Array[]> = new Map();
|
|
@@ -1482,6 +1496,8 @@ export default class ReactNativeBleTransport {
|
|
|
1482
1496
|
this.deviceProtocol.delete(session);
|
|
1483
1497
|
this.probingProtocols.delete(session);
|
|
1484
1498
|
this.deviceProtocolHints.delete(session);
|
|
1499
|
+
this.sessionProtocols.delete(session);
|
|
1500
|
+
this.protocolReprobeFailures.delete(session);
|
|
1485
1501
|
this.protocolV2Assemblers.delete(session);
|
|
1486
1502
|
this.resetProtocolV2Frames(session);
|
|
1487
1503
|
|
|
@@ -1674,6 +1690,8 @@ export default class ReactNativeBleTransport {
|
|
|
1674
1690
|
});
|
|
1675
1691
|
this.deviceProtocol.clear();
|
|
1676
1692
|
this.probingProtocols.clear();
|
|
1693
|
+
this.sessionProtocols.clear();
|
|
1694
|
+
this.protocolReprobeFailures.clear();
|
|
1677
1695
|
this.monitorTokens.clear();
|
|
1678
1696
|
this.protocolV2Assemblers.clear();
|
|
1679
1697
|
try {
|
|
@@ -1720,6 +1738,7 @@ export default class ReactNativeBleTransport {
|
|
|
1720
1738
|
if (expectedProtocol === 'V1') {
|
|
1721
1739
|
if (await this.probeProtocolV1(uuid)) {
|
|
1722
1740
|
this.deviceProtocol.set(uuid, 'V1');
|
|
1741
|
+
this.sessionProtocols.set(uuid, 'V1');
|
|
1723
1742
|
Log?.debug('[ReactNativeBleTransport] protocol detected', {
|
|
1724
1743
|
deviceId: uuid,
|
|
1725
1744
|
protocol: 'V1',
|
|
@@ -1733,6 +1752,7 @@ export default class ReactNativeBleTransport {
|
|
|
1733
1752
|
if (expectedProtocol === 'V2') {
|
|
1734
1753
|
if (await this.probeProtocolV2(uuid)) {
|
|
1735
1754
|
this.deviceProtocol.set(uuid, 'V2');
|
|
1755
|
+
this.sessionProtocols.set(uuid, 'V2');
|
|
1736
1756
|
Log?.debug('[ReactNativeBleTransport] protocol detected', {
|
|
1737
1757
|
deviceId: uuid,
|
|
1738
1758
|
protocol: 'V2',
|
|
@@ -1745,8 +1765,18 @@ export default class ReactNativeBleTransport {
|
|
|
1745
1765
|
|
|
1746
1766
|
// Protocol must be actively probed after connection. Name, PID, and descriptors only
|
|
1747
1767
|
// influence probe order; a V2 hint probes V2 first and falls back to V1.
|
|
1748
|
-
const
|
|
1768
|
+
const sessionProtocol = this.sessionProtocols.get(uuid);
|
|
1769
|
+
const reprobeFailures = this.protocolReprobeFailures.get(uuid) ?? 0;
|
|
1770
|
+
const fullProbeOrder: ProtocolType[] =
|
|
1749
1771
|
protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
|
|
1772
|
+
// A device that already answered on a protocol in this session keeps answering on
|
|
1773
|
+
// it; while it is rebooting nothing answers at all, so probing the other protocol
|
|
1774
|
+
// only adds its timeout to every poll.
|
|
1775
|
+
const trustSessionProtocol =
|
|
1776
|
+
sessionProtocol !== undefined &&
|
|
1777
|
+
!protocolHint &&
|
|
1778
|
+
reprobeFailures < PROTOCOL_REPROBE_FALLBACK_ATTEMPTS;
|
|
1779
|
+
const probeOrder: ProtocolType[] = trustSessionProtocol ? [sessionProtocol] : fullProbeOrder;
|
|
1750
1780
|
|
|
1751
1781
|
for (let i = 0; i < probeOrder.length; i += 1) {
|
|
1752
1782
|
const protocol = probeOrder[i];
|
|
@@ -1764,6 +1794,8 @@ export default class ReactNativeBleTransport {
|
|
|
1764
1794
|
protocol === 'V1' ? await this.probeProtocolV1(uuid) : await this.probeProtocolV2(uuid);
|
|
1765
1795
|
if (detected) {
|
|
1766
1796
|
this.deviceProtocol.set(uuid, protocol);
|
|
1797
|
+
this.sessionProtocols.set(uuid, protocol);
|
|
1798
|
+
this.protocolReprobeFailures.delete(uuid);
|
|
1767
1799
|
Log?.debug('[ReactNativeBleTransport] protocol detected', {
|
|
1768
1800
|
deviceId: uuid,
|
|
1769
1801
|
protocol,
|
|
@@ -1773,6 +1805,14 @@ export default class ReactNativeBleTransport {
|
|
|
1773
1805
|
}
|
|
1774
1806
|
}
|
|
1775
1807
|
|
|
1808
|
+
if (trustSessionProtocol) {
|
|
1809
|
+
// Still silent on its own protocol: count it, and let the streak expire the
|
|
1810
|
+
// shortcut so a device that genuinely switched protocols is found again.
|
|
1811
|
+
this.protocolReprobeFailures.set(uuid, reprobeFailures + 1);
|
|
1812
|
+
} else {
|
|
1813
|
+
this.protocolReprobeFailures.delete(uuid);
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1776
1816
|
this.deviceProtocol.delete(uuid);
|
|
1777
1817
|
this.probingProtocols.delete(uuid);
|
|
1778
1818
|
throw this.createProtocolDetectionError();
|