@onekeyfe/hd-transport-react-native 1.2.0-alpha.21 → 1.2.0-alpha.22
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/constants.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +62 -15
- package/package.json +5 -5
- package/src/__tests__/protocolV2Link.test.ts +69 -2
- package/src/constants.ts +9 -1
- package/src/index.ts +67 -18
package/dist/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,qBAAqB,MAAM,CAAC;AACzC,eAAO,MAAM,mBAAmB,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,qBAAqB,MAAM,CAAC;AACzC,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAoCtC,eAAO,MAAM,wBAAwB,gBAA0B,CAAC;AAChE,eAAO,MAAM,sBAAsB,gBAAiB,MAAM,cAAc,SAAS;iBAhChE,MAAM;;;QAiDtB,CAAC;AAEF,eAAO,MAAM,gBAAgB,UAAW,MAAM,GAAG,IAAI,WACP,CAAC;AAE/C,eAAO,MAAM,aAAa,UAAW,MAAM,GAAG,IAAI,WAGjD,CAAC;AAEF,eAAO,MAAM,aAAa,UAAW,MAAM,GAAG,IAAI,UAAU,MAAM,GAAG,IAAI,YAQxE,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -113,6 +113,7 @@ declare class ReactNativeBleTransport {
|
|
|
113
113
|
private resetProtocolV2Frames;
|
|
114
114
|
private rejectProtocolV2Frames;
|
|
115
115
|
private readProtocolV2Frame;
|
|
116
|
+
private writeProtocolV2Packet;
|
|
116
117
|
private writeProtocolV2Frame;
|
|
117
118
|
private callProtocolV2;
|
|
118
119
|
private createProtocolV2Adapter;
|
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,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -95,6 +95,7 @@ const IOS_PACKET_LENGTH = 128;
|
|
|
95
95
|
const ANDROID_PACKET_LENGTH = 192;
|
|
96
96
|
const ANDROID_DEFAULT_MTU = 23;
|
|
97
97
|
const ClassicServiceUUID = '00000001-0000-1000-8000-00805f9b34fb';
|
|
98
|
+
const Pro2ServiceUUID = 'fffd';
|
|
98
99
|
const OneKeyServices = {
|
|
99
100
|
classic: {
|
|
100
101
|
[ClassicServiceUUID]: {
|
|
@@ -102,6 +103,11 @@ const OneKeyServices = {
|
|
|
102
103
|
writeUuid: '00000002-0000-1000-8000-00805f9b34fb',
|
|
103
104
|
notifyUuid: '00000003-0000-1000-8000-00805f9b34fb',
|
|
104
105
|
},
|
|
106
|
+
[Pro2ServiceUUID]: {
|
|
107
|
+
serviceUuid: Pro2ServiceUUID,
|
|
108
|
+
writeUuid: '00000002-0000-1000-8000-00805f9b34fb',
|
|
109
|
+
notifyUuid: '00000003-0000-1000-8000-00805f9b34fb',
|
|
110
|
+
},
|
|
105
111
|
},
|
|
106
112
|
};
|
|
107
113
|
const bluetoothServices = [];
|
|
@@ -117,7 +123,8 @@ const getInfosForServiceUuid = (serviceUuid, deviceType) => {
|
|
|
117
123
|
return null;
|
|
118
124
|
}
|
|
119
125
|
const normalizedServiceUuid = normalizeBleUuid(serviceUuid);
|
|
120
|
-
const service = (_a = services[serviceUuid]) !== null && _a !== void 0 ? _a : Object.values(services).find(item => normalizeBleUuid(item.serviceUuid) === normalizedServiceUuid
|
|
126
|
+
const service = (_a = services[serviceUuid]) !== null && _a !== void 0 ? _a : Object.values(services).find(item => normalizeBleUuid(item.serviceUuid) === normalizedServiceUuid ||
|
|
127
|
+
getBleUuidKey(item.serviceUuid) === getBleUuidKey(serviceUuid));
|
|
121
128
|
if (!service) {
|
|
122
129
|
return null;
|
|
123
130
|
}
|
|
@@ -289,7 +296,6 @@ const getFirmwareUploadWriteRetryType = (error) => {
|
|
|
289
296
|
return /GATT_CONGESTED|status\s*[:=]?\s*143/.test(text) ? 'congested' : null;
|
|
290
297
|
};
|
|
291
298
|
const resolveFirmwareUploadRetryDelay = (attempt, baseDelayMs = 200, maxDelayMs = 1200) => Math.min(baseDelayMs * Math.pow(2, attempt), maxDelayMs);
|
|
292
|
-
const BLE_RESPONSE_TIMEOUT_MS = 30000;
|
|
293
299
|
const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
|
|
294
300
|
const PROTOCOL_V2_PROBE_TIMEOUT_MS = 10000;
|
|
295
301
|
const DEVICE_SCAN_TIMEOUT_MS = 8000;
|
|
@@ -1326,13 +1332,16 @@ class ReactNativeBleTransport {
|
|
|
1326
1332
|
throw this.createProtocolMismatchError(expectedProtocol);
|
|
1327
1333
|
}
|
|
1328
1334
|
if (expectedProtocol === 'V2') {
|
|
1329
|
-
this.
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1335
|
+
if (yield this.probeProtocolV2(uuid)) {
|
|
1336
|
+
this.deviceProtocol.set(uuid, 'V2');
|
|
1337
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
|
|
1338
|
+
deviceId: uuid,
|
|
1339
|
+
protocol: 'V2',
|
|
1340
|
+
source: 'expected',
|
|
1341
|
+
});
|
|
1342
|
+
return 'V2';
|
|
1343
|
+
}
|
|
1344
|
+
throw this.createProtocolMismatchError(expectedProtocol);
|
|
1336
1345
|
}
|
|
1337
1346
|
const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
|
|
1338
1347
|
for (let i = 0; i < probeOrder.length; i += 1) {
|
|
@@ -1515,7 +1524,37 @@ class ReactNativeBleTransport {
|
|
|
1515
1524
|
}
|
|
1516
1525
|
});
|
|
1517
1526
|
}
|
|
1518
|
-
|
|
1527
|
+
writeProtocolV2Packet(transport, base64, context, assertCurrentGeneration) {
|
|
1528
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1529
|
+
let attempt = 0;
|
|
1530
|
+
for (;;) {
|
|
1531
|
+
assertCurrentGeneration();
|
|
1532
|
+
if (context.signal.aborted) {
|
|
1533
|
+
throw new Error(`Protocol V2 BLE write aborted for ${context.messageName}`);
|
|
1534
|
+
}
|
|
1535
|
+
try {
|
|
1536
|
+
yield transport.writeCharacteristic.writeWithoutResponse(base64);
|
|
1537
|
+
assertCurrentGeneration();
|
|
1538
|
+
return;
|
|
1539
|
+
}
|
|
1540
|
+
catch (error) {
|
|
1541
|
+
if (getFirmwareUploadWriteRetryType(error) !== 'congested' ||
|
|
1542
|
+
attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
|
|
1543
|
+
throw error;
|
|
1544
|
+
}
|
|
1545
|
+
const delayMs = resolveFirmwareUploadRetryDelay(attempt);
|
|
1546
|
+
attempt += 1;
|
|
1547
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 congested write retry:', {
|
|
1548
|
+
name: context.messageName,
|
|
1549
|
+
attempt,
|
|
1550
|
+
delayMs,
|
|
1551
|
+
});
|
|
1552
|
+
yield delay(delayMs);
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
});
|
|
1556
|
+
}
|
|
1557
|
+
writeProtocolV2Frame(transport, frame, context, assertCurrentGeneration) {
|
|
1519
1558
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1520
1559
|
const tuning = getProtocolV2BleTuning();
|
|
1521
1560
|
const packetCapacity = resolveProtocolV2PacketCapacity({
|
|
@@ -1524,20 +1563,28 @@ class ReactNativeBleTransport {
|
|
|
1524
1563
|
androidPacketLength: tuning.androidPacketLength,
|
|
1525
1564
|
mtu: reactNative.Platform.OS === 'android' ? transport.mtuSize : undefined,
|
|
1526
1565
|
});
|
|
1566
|
+
let packetsWritten = 0;
|
|
1527
1567
|
for (let offset = 0; offset < frame.length; offset += packetCapacity) {
|
|
1528
1568
|
const chunk = frame.slice(offset, offset + packetCapacity);
|
|
1529
1569
|
const base64 = buffer.Buffer.from(chunk).toString('base64');
|
|
1530
|
-
yield
|
|
1570
|
+
yield this.writeProtocolV2Packet(transport, base64, context, assertCurrentGeneration);
|
|
1571
|
+
packetsWritten += 1;
|
|
1572
|
+
if (offset + packetCapacity < frame.length &&
|
|
1573
|
+
packetsWritten % FIRMWARE_UPLOAD_WRITE_BURST_SIZE === 0) {
|
|
1574
|
+
yield delay(FIRMWARE_UPLOAD_WRITE_PAUSE_MS);
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
if (packetsWritten > FIRMWARE_UPLOAD_WRITE_BURST_SIZE) {
|
|
1578
|
+
yield delay(FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS);
|
|
1531
1579
|
}
|
|
1532
1580
|
});
|
|
1533
1581
|
}
|
|
1534
1582
|
callProtocolV2(uuid, name, data, options) {
|
|
1535
|
-
var _a;
|
|
1536
1583
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1537
1584
|
if (!this._messages || !this._messagesV2) {
|
|
1538
1585
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
1539
1586
|
}
|
|
1540
|
-
const callOptions =
|
|
1587
|
+
const callOptions = options;
|
|
1541
1588
|
const highVolumeWrite = transport.LogBlockCommand.has(name);
|
|
1542
1589
|
if (highVolumeWrite) {
|
|
1543
1590
|
const tuning = getProtocolV2BleTuning();
|
|
@@ -1574,10 +1621,10 @@ class ReactNativeBleTransport {
|
|
|
1574
1621
|
(_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1575
1622
|
this.resetProtocolV2Frames(uuid);
|
|
1576
1623
|
},
|
|
1577
|
-
writeFrame: (frame) => __awaiter(this, void 0, void 0, function* () {
|
|
1624
|
+
writeFrame: (frame, context) => __awaiter(this, void 0, void 0, function* () {
|
|
1578
1625
|
assertCurrentGeneration();
|
|
1579
1626
|
const currentTransport = this.getCachedTransport(uuid);
|
|
1580
|
-
yield this.writeProtocolV2Frame(currentTransport, frame);
|
|
1627
|
+
yield this.writeProtocolV2Frame(currentTransport, frame, context, assertCurrentGeneration);
|
|
1581
1628
|
}),
|
|
1582
1629
|
readFrame: () => __awaiter(this, void 0, void 0, function* () {
|
|
1583
1630
|
assertCurrentGeneration();
|
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.22",
|
|
4
4
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -19,11 +19,11 @@
|
|
|
19
19
|
"lint:fix": "eslint . --fix"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@onekeyfe/hd-core": "1.2.0-alpha.
|
|
23
|
-
"@onekeyfe/hd-shared": "1.2.0-alpha.
|
|
24
|
-
"@onekeyfe/hd-transport": "1.2.0-alpha.
|
|
22
|
+
"@onekeyfe/hd-core": "1.2.0-alpha.22",
|
|
23
|
+
"@onekeyfe/hd-shared": "1.2.0-alpha.22",
|
|
24
|
+
"@onekeyfe/hd-transport": "1.2.0-alpha.22",
|
|
25
25
|
"@onekeyfe/react-native-ble-utils": "^0.1.6",
|
|
26
26
|
"react-native-ble-plx": "3.5.1"
|
|
27
27
|
},
|
|
28
|
-
"gitHead": "
|
|
28
|
+
"gitHead": "ce94a5ebbe141b0c15db7c68d08085ca1ddb0166"
|
|
29
29
|
}
|
|
@@ -6,7 +6,10 @@ import transportPackage, {
|
|
|
6
6
|
} from '@onekeyfe/hd-transport';
|
|
7
7
|
import { HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
8
8
|
|
|
9
|
-
import ReactNativeBleTransport
|
|
9
|
+
import ReactNativeBleTransport, {
|
|
10
|
+
configureProtocolV2BleTuning,
|
|
11
|
+
resetProtocolV2BleTuning,
|
|
12
|
+
} from '../index';
|
|
10
13
|
|
|
11
14
|
jest.mock(
|
|
12
15
|
'react-native',
|
|
@@ -91,6 +94,7 @@ const schemas = {
|
|
|
91
94
|
const createHarness = () => {
|
|
92
95
|
const uuid = 'rn-pro2-id';
|
|
93
96
|
const sentSeqs: number[] = [];
|
|
97
|
+
let responseSeq = 0;
|
|
94
98
|
let shouldRespond = true;
|
|
95
99
|
let notifyCallback:
|
|
96
100
|
| ((
|
|
@@ -111,11 +115,12 @@ const createHarness = () => {
|
|
|
111
115
|
const frame = Buffer.from(base64, 'base64');
|
|
112
116
|
sentSeqs.push(frame[6]);
|
|
113
117
|
if (shouldRespond) {
|
|
118
|
+
responseSeq += 1;
|
|
114
119
|
const response = ProtocolV2.encodeFrame(
|
|
115
120
|
schemas,
|
|
116
121
|
'Success',
|
|
117
122
|
{ message: 'ok' },
|
|
118
|
-
{ router: PROTOCOL_V2_CHANNEL_BLE_UART }
|
|
123
|
+
{ router: PROTOCOL_V2_CHANNEL_BLE_UART, seq: responseSeq }
|
|
119
124
|
);
|
|
120
125
|
notifyCallback?.(null, { value: Buffer.from(response).toString('base64') });
|
|
121
126
|
}
|
|
@@ -166,6 +171,10 @@ const createHarness = () => {
|
|
|
166
171
|
};
|
|
167
172
|
|
|
168
173
|
describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
174
|
+
afterEach(() => {
|
|
175
|
+
resetProtocolV2BleTuning();
|
|
176
|
+
});
|
|
177
|
+
|
|
169
178
|
test('keeps the Protocol V2 sequence across probe and the next call', async () => {
|
|
170
179
|
const { transport, uuid, sentSeqs } = createHarness();
|
|
171
180
|
|
|
@@ -240,4 +249,62 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
240
249
|
await transport.disconnect(uuid);
|
|
241
250
|
await rejection;
|
|
242
251
|
});
|
|
252
|
+
|
|
253
|
+
test('chunks large frames and retries only transient GATT congestion', async () => {
|
|
254
|
+
const transport = new ReactNativeBleTransport({ scanTimeout: 1 }) as any;
|
|
255
|
+
const congested = { status: 143, message: 'GATT_CONGESTED' };
|
|
256
|
+
const writeWithoutResponse = jest
|
|
257
|
+
.fn()
|
|
258
|
+
.mockRejectedValueOnce(congested)
|
|
259
|
+
.mockResolvedValue(undefined);
|
|
260
|
+
const bleTransport = {
|
|
261
|
+
mtuSize: 23,
|
|
262
|
+
writeCharacteristic: { writeWithoutResponse },
|
|
263
|
+
};
|
|
264
|
+
const context = {
|
|
265
|
+
messageName: 'Ping',
|
|
266
|
+
timeoutMs: 1000,
|
|
267
|
+
highVolume: false,
|
|
268
|
+
generation: 1,
|
|
269
|
+
signal: new AbortController().signal,
|
|
270
|
+
};
|
|
271
|
+
const assertCurrentGeneration = jest.fn();
|
|
272
|
+
configureProtocolV2BleTuning({ iosPacketLength: 20 });
|
|
273
|
+
|
|
274
|
+
await transport.writeProtocolV2Frame(
|
|
275
|
+
bleTransport,
|
|
276
|
+
new Uint8Array(30),
|
|
277
|
+
context,
|
|
278
|
+
assertCurrentGeneration
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
expect(writeWithoutResponse).toHaveBeenCalledTimes(3);
|
|
282
|
+
expect(writeWithoutResponse.mock.calls[0][0]).toBe(writeWithoutResponse.mock.calls[1][0]);
|
|
283
|
+
expect(Buffer.from(writeWithoutResponse.mock.calls[2][0], 'base64')).toHaveLength(10);
|
|
284
|
+
expect(assertCurrentGeneration).toHaveBeenCalled();
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
test('does not retry a disconnected Protocol V2 write inside a partial frame', async () => {
|
|
288
|
+
const transport = new ReactNativeBleTransport({ scanTimeout: 1 }) as any;
|
|
289
|
+
const writeWithoutResponse = jest
|
|
290
|
+
.fn()
|
|
291
|
+
.mockRejectedValue({ errorCode: 205, message: 'Device disconnected' });
|
|
292
|
+
const bleTransport = {
|
|
293
|
+
mtuSize: 23,
|
|
294
|
+
writeCharacteristic: { writeWithoutResponse },
|
|
295
|
+
};
|
|
296
|
+
const context = {
|
|
297
|
+
messageName: 'FileWrite',
|
|
298
|
+
timeoutMs: 1000,
|
|
299
|
+
highVolume: true,
|
|
300
|
+
generation: 1,
|
|
301
|
+
signal: new AbortController().signal,
|
|
302
|
+
};
|
|
303
|
+
configureProtocolV2BleTuning({ iosPacketLength: 20 });
|
|
304
|
+
|
|
305
|
+
await expect(
|
|
306
|
+
transport.writeProtocolV2Frame(bleTransport, new Uint8Array(30), context, jest.fn())
|
|
307
|
+
).rejects.toMatchObject({ errorCode: 205 });
|
|
308
|
+
expect(writeWithoutResponse).toHaveBeenCalledTimes(1);
|
|
309
|
+
});
|
|
243
310
|
});
|
package/src/constants.ts
CHANGED
|
@@ -12,6 +12,7 @@ type BluetoothServices = Record<
|
|
|
12
12
|
>;
|
|
13
13
|
|
|
14
14
|
const ClassicServiceUUID = '00000001-0000-1000-8000-00805f9b34fb';
|
|
15
|
+
const Pro2ServiceUUID = 'fffd';
|
|
15
16
|
|
|
16
17
|
const OneKeyServices: Record<string, BluetoothServices> = {
|
|
17
18
|
classic: {
|
|
@@ -20,6 +21,11 @@ const OneKeyServices: Record<string, BluetoothServices> = {
|
|
|
20
21
|
writeUuid: '00000002-0000-1000-8000-00805f9b34fb',
|
|
21
22
|
notifyUuid: '00000003-0000-1000-8000-00805f9b34fb',
|
|
22
23
|
},
|
|
24
|
+
[Pro2ServiceUUID]: {
|
|
25
|
+
serviceUuid: Pro2ServiceUUID,
|
|
26
|
+
writeUuid: '00000002-0000-1000-8000-00805f9b34fb',
|
|
27
|
+
notifyUuid: '00000003-0000-1000-8000-00805f9b34fb',
|
|
28
|
+
},
|
|
23
29
|
},
|
|
24
30
|
};
|
|
25
31
|
|
|
@@ -40,7 +46,9 @@ export const getInfosForServiceUuid = (serviceUuid: string, deviceType: 'classic
|
|
|
40
46
|
const service =
|
|
41
47
|
services[serviceUuid] ??
|
|
42
48
|
Object.values(services).find(
|
|
43
|
-
item =>
|
|
49
|
+
item =>
|
|
50
|
+
normalizeBleUuid(item.serviceUuid) === normalizedServiceUuid ||
|
|
51
|
+
getBleUuidKey(item.serviceUuid) === getBleUuidKey(serviceUuid)
|
|
44
52
|
);
|
|
45
53
|
if (!service) {
|
|
46
54
|
return null;
|
package/src/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ import transport, {
|
|
|
15
15
|
PROTOCOL_V2_BLE_FRAME_MAX_BYTES,
|
|
16
16
|
PROTOCOL_V2_CHANNEL_BLE_UART,
|
|
17
17
|
type ProtocolType,
|
|
18
|
+
type ProtocolV2CallContext,
|
|
18
19
|
ProtocolV2FrameAssembler,
|
|
19
20
|
ProtocolV2LinkManager,
|
|
20
21
|
type TransportCallOptions,
|
|
@@ -103,7 +104,6 @@ const getFirmwareUploadWriteRetryType = (error: unknown): FirmwareUploadWriteRet
|
|
|
103
104
|
|
|
104
105
|
const resolveFirmwareUploadRetryDelay = (attempt: number, baseDelayMs = 200, maxDelayMs = 1200) =>
|
|
105
106
|
Math.min(baseDelayMs * 2 ** attempt, maxDelayMs);
|
|
106
|
-
const BLE_RESPONSE_TIMEOUT_MS = 30_000;
|
|
107
107
|
const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
|
|
108
108
|
const PROTOCOL_V2_PROBE_TIMEOUT_MS = 10_000;
|
|
109
109
|
const DEVICE_SCAN_TIMEOUT_MS = 8000;
|
|
@@ -1392,15 +1392,16 @@ export default class ReactNativeBleTransport {
|
|
|
1392
1392
|
}
|
|
1393
1393
|
|
|
1394
1394
|
if (expectedProtocol === 'V2') {
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1395
|
+
if (await this.probeProtocolV2(uuid)) {
|
|
1396
|
+
this.deviceProtocol.set(uuid, 'V2');
|
|
1397
|
+
Log?.debug('[ReactNativeBleTransport] protocol detected', {
|
|
1398
|
+
deviceId: uuid,
|
|
1399
|
+
protocol: 'V2',
|
|
1400
|
+
source: 'expected',
|
|
1401
|
+
});
|
|
1402
|
+
return 'V2';
|
|
1403
|
+
}
|
|
1404
|
+
throw this.createProtocolMismatchError(expectedProtocol);
|
|
1404
1405
|
}
|
|
1405
1406
|
|
|
1406
1407
|
// Protocol must be actively probed after connection. Name, PID, and descriptors only
|
|
@@ -1608,7 +1609,47 @@ export default class ReactNativeBleTransport {
|
|
|
1608
1609
|
}
|
|
1609
1610
|
}
|
|
1610
1611
|
|
|
1611
|
-
private async
|
|
1612
|
+
private async writeProtocolV2Packet(
|
|
1613
|
+
transport: BleTransport,
|
|
1614
|
+
base64: string,
|
|
1615
|
+
context: ProtocolV2CallContext,
|
|
1616
|
+
assertCurrentGeneration: () => void
|
|
1617
|
+
) {
|
|
1618
|
+
let attempt = 0;
|
|
1619
|
+
for (;;) {
|
|
1620
|
+
assertCurrentGeneration();
|
|
1621
|
+
if (context.signal.aborted) {
|
|
1622
|
+
throw new Error(`Protocol V2 BLE write aborted for ${context.messageName}`);
|
|
1623
|
+
}
|
|
1624
|
+
try {
|
|
1625
|
+
await transport.writeCharacteristic.writeWithoutResponse(base64);
|
|
1626
|
+
assertCurrentGeneration();
|
|
1627
|
+
return;
|
|
1628
|
+
} catch (error) {
|
|
1629
|
+
if (
|
|
1630
|
+
getFirmwareUploadWriteRetryType(error) !== 'congested' ||
|
|
1631
|
+
attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES
|
|
1632
|
+
) {
|
|
1633
|
+
throw error;
|
|
1634
|
+
}
|
|
1635
|
+
const delayMs = resolveFirmwareUploadRetryDelay(attempt);
|
|
1636
|
+
attempt += 1;
|
|
1637
|
+
Log?.debug('[ReactNativeBleTransport] Protocol V2 congested write retry:', {
|
|
1638
|
+
name: context.messageName,
|
|
1639
|
+
attempt,
|
|
1640
|
+
delayMs,
|
|
1641
|
+
});
|
|
1642
|
+
await delay(delayMs);
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
private async writeProtocolV2Frame(
|
|
1648
|
+
transport: BleTransport,
|
|
1649
|
+
frame: Uint8Array,
|
|
1650
|
+
context: ProtocolV2CallContext,
|
|
1651
|
+
assertCurrentGeneration: () => void
|
|
1652
|
+
) {
|
|
1612
1653
|
const tuning = getProtocolV2BleTuning();
|
|
1613
1654
|
const packetCapacity = resolveProtocolV2PacketCapacity({
|
|
1614
1655
|
platform: Platform.OS,
|
|
@@ -1616,10 +1657,21 @@ export default class ReactNativeBleTransport {
|
|
|
1616
1657
|
androidPacketLength: tuning.androidPacketLength,
|
|
1617
1658
|
mtu: Platform.OS === 'android' ? transport.mtuSize : undefined,
|
|
1618
1659
|
});
|
|
1660
|
+
let packetsWritten = 0;
|
|
1619
1661
|
for (let offset = 0; offset < frame.length; offset += packetCapacity) {
|
|
1620
1662
|
const chunk = frame.slice(offset, offset + packetCapacity);
|
|
1621
1663
|
const base64 = Buffer.from(chunk).toString('base64');
|
|
1622
|
-
await
|
|
1664
|
+
await this.writeProtocolV2Packet(transport, base64, context, assertCurrentGeneration);
|
|
1665
|
+
packetsWritten += 1;
|
|
1666
|
+
if (
|
|
1667
|
+
offset + packetCapacity < frame.length &&
|
|
1668
|
+
packetsWritten % FIRMWARE_UPLOAD_WRITE_BURST_SIZE === 0
|
|
1669
|
+
) {
|
|
1670
|
+
await delay(FIRMWARE_UPLOAD_WRITE_PAUSE_MS);
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
if (packetsWritten > FIRMWARE_UPLOAD_WRITE_BURST_SIZE) {
|
|
1674
|
+
await delay(FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS);
|
|
1623
1675
|
}
|
|
1624
1676
|
}
|
|
1625
1677
|
|
|
@@ -1633,10 +1685,7 @@ export default class ReactNativeBleTransport {
|
|
|
1633
1685
|
throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
|
|
1634
1686
|
}
|
|
1635
1687
|
|
|
1636
|
-
const callOptions =
|
|
1637
|
-
...options,
|
|
1638
|
-
timeoutMs: options?.timeoutMs ?? BLE_RESPONSE_TIMEOUT_MS,
|
|
1639
|
-
};
|
|
1688
|
+
const callOptions = options;
|
|
1640
1689
|
const highVolumeWrite = LogBlockCommand.has(name);
|
|
1641
1690
|
|
|
1642
1691
|
if (highVolumeWrite) {
|
|
@@ -1679,10 +1728,10 @@ export default class ReactNativeBleTransport {
|
|
|
1679
1728
|
this.protocolV2Assemblers.get(uuid)?.reset();
|
|
1680
1729
|
this.resetProtocolV2Frames(uuid);
|
|
1681
1730
|
},
|
|
1682
|
-
writeFrame: async (frame: Uint8Array) => {
|
|
1731
|
+
writeFrame: async (frame: Uint8Array, context: ProtocolV2CallContext) => {
|
|
1683
1732
|
assertCurrentGeneration();
|
|
1684
1733
|
const currentTransport = this.getCachedTransport(uuid);
|
|
1685
|
-
await this.writeProtocolV2Frame(currentTransport, frame);
|
|
1734
|
+
await this.writeProtocolV2Frame(currentTransport, frame, context, assertCurrentGeneration);
|
|
1686
1735
|
},
|
|
1687
1736
|
readFrame: async () => {
|
|
1688
1737
|
assertCurrentGeneration();
|