@onekeyfe/hd-transport-web-device 1.2.0-alpha.70 → 1.2.0-alpha.71
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/__tests__/electron-ble-transport.test.ts +54 -0
- package/dist/ble-packet-capacity.d.ts +2 -0
- package/dist/ble-packet-capacity.d.ts.map +1 -0
- package/dist/electron-ble-transport.d.ts +6 -0
- package/dist/electron-ble-transport.d.ts.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +72 -9
- package/package.json +5 -5
- package/src/ble-packet-capacity.ts +13 -0
- package/src/electron-ble-transport.ts +66 -5
|
@@ -105,6 +105,7 @@ const createNobleBle = (device = { id: 'flaky-pro2-id', name: 'Unknown BLE Devic
|
|
|
105
105
|
unsubscribe: jest.fn(() => Promise.resolve()),
|
|
106
106
|
write: jest.fn(() => Promise.resolve()),
|
|
107
107
|
onNotification: jest.fn(() => jest.fn()),
|
|
108
|
+
onMtuChanged: jest.fn(() => jest.fn()),
|
|
108
109
|
onDeviceDisconnected: jest.fn(() => jest.fn()),
|
|
109
110
|
checkAvailability: jest.fn(() =>
|
|
110
111
|
Promise.resolve({
|
|
@@ -176,6 +177,9 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
176
177
|
const bleTransport = configureTransport(nobleBle, emitter);
|
|
177
178
|
|
|
178
179
|
await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
|
|
180
|
+
expect(nobleBle.subscribe.mock.invocationCallOrder[0]).toBeLessThan(
|
|
181
|
+
nobleBle.getDevice.mock.invocationCallOrder[1]
|
|
182
|
+
);
|
|
179
183
|
disconnectHandler?.(device);
|
|
180
184
|
|
|
181
185
|
expect(publicConnect).not.toHaveBeenCalled();
|
|
@@ -205,6 +209,56 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
205
209
|
expect(nobleBle.write.mock.calls.map(([, hex]) => hex.length / 2)).toEqual([192, 1]);
|
|
206
210
|
});
|
|
207
211
|
|
|
212
|
+
test('uses the negotiated Noble MTU for Protocol V2 BLE writes', async () => {
|
|
213
|
+
const device = { id: 'mtu-pro2-id', name: 'OneKey Pro 2', mtu: 247 };
|
|
214
|
+
const nobleBle = createNobleBle(device);
|
|
215
|
+
const bleTransport = configureTransport(nobleBle) as any;
|
|
216
|
+
const context = {
|
|
217
|
+
messageName: 'FilesystemFileWrite',
|
|
218
|
+
timeoutMs: 1000,
|
|
219
|
+
highVolume: true,
|
|
220
|
+
generation: 1,
|
|
221
|
+
signal: new AbortController().signal,
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const setTimeoutSpy = jest.spyOn(global, 'setTimeout');
|
|
225
|
+
try {
|
|
226
|
+
await bleTransport.refreshBlePacketCapacity(device.id);
|
|
227
|
+
await bleTransport.writeProtocolV2Frame(device.id, new Uint8Array(245), context, jest.fn());
|
|
228
|
+
|
|
229
|
+
expect(setTimeoutSpy).not.toHaveBeenCalled();
|
|
230
|
+
} finally {
|
|
231
|
+
setTimeoutSpy.mockRestore();
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
expect(nobleBle.write).toHaveBeenCalledTimes(2);
|
|
235
|
+
expect(nobleBle.write.mock.calls.map(([, hex]) => hex.length / 2)).toEqual([244, 1]);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
test('updates Protocol V2 packet capacity when Noble reports a new MTU', async () => {
|
|
239
|
+
const device = { id: 'mtu-event-pro2-id', name: 'OneKey Pro 2' };
|
|
240
|
+
const nobleBle = createNobleBle(device);
|
|
241
|
+
let mtuHandler: ((changedDevice: { id: string; mtu: number }) => void) | undefined;
|
|
242
|
+
nobleBle.onMtuChanged.mockImplementation(handler => {
|
|
243
|
+
mtuHandler = handler;
|
|
244
|
+
return jest.fn();
|
|
245
|
+
});
|
|
246
|
+
const bleTransport = configureTransport(nobleBle) as any;
|
|
247
|
+
const context = {
|
|
248
|
+
messageName: 'FilesystemFileWrite',
|
|
249
|
+
timeoutMs: 1000,
|
|
250
|
+
highVolume: true,
|
|
251
|
+
generation: 1,
|
|
252
|
+
signal: new AbortController().signal,
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
bleTransport.createMtuSubscription(device.id);
|
|
256
|
+
mtuHandler?.({ id: device.id, mtu: 247 });
|
|
257
|
+
await bleTransport.writeProtocolV2Frame(device.id, new Uint8Array(245), context, jest.fn());
|
|
258
|
+
|
|
259
|
+
expect(nobleBle.write.mock.calls.map(([, hex]) => hex.length / 2)).toEqual([244, 1]);
|
|
260
|
+
});
|
|
261
|
+
|
|
208
262
|
test('detects Protocol V2 after Protocol V1 probe timeout', async () => {
|
|
209
263
|
const device = { id: 'unknown-pro2-id', name: 'Unknown BLE Device' };
|
|
210
264
|
const nobleBle = createNobleBle(device);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ble-packet-capacity.d.ts","sourceRoot":"","sources":["../src/ble-packet-capacity.ts"],"names":[],"mappings":"AAEA,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC9B,qBAAqB,EAAE,MAAM,EAC7B,sBAAsB,EAAE,MAAM,GAC7B,MAAM,CAMR"}
|
|
@@ -27,12 +27,15 @@ export default class ElectronBleTransport {
|
|
|
27
27
|
private connectedDevices;
|
|
28
28
|
private deviceProtocol;
|
|
29
29
|
private deviceProtocolHints;
|
|
30
|
+
private deviceMtus;
|
|
31
|
+
private devicePacketCapacities;
|
|
30
32
|
private v1Buffers;
|
|
31
33
|
private v2Assemblers;
|
|
32
34
|
private v2FrameQueues;
|
|
33
35
|
private v2FramePromises;
|
|
34
36
|
private protocolV2Links;
|
|
35
37
|
private notificationCleanups;
|
|
38
|
+
private mtuCleanups;
|
|
36
39
|
private disconnectCleanups;
|
|
37
40
|
private notificationTokens;
|
|
38
41
|
private nextNotificationToken;
|
|
@@ -65,6 +68,9 @@ export default class ElectronBleTransport {
|
|
|
65
68
|
private probeProtocolV1;
|
|
66
69
|
private probeProtocolV2;
|
|
67
70
|
private writeOnce;
|
|
71
|
+
private refreshBlePacketCapacity;
|
|
72
|
+
private updateBlePacketCapacity;
|
|
73
|
+
private createMtuSubscription;
|
|
68
74
|
private writeProtocolV2Frame;
|
|
69
75
|
private handleNotification;
|
|
70
76
|
private handleProtocolV2Notification;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"electron-ble-transport.d.ts","sourceRoot":"","sources":["../src/electron-ble-transport.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"electron-ble-transport.d.ts","sourceRoot":"","sources":["../src/electron-ble-transport.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -176,12 +176,15 @@ declare class ElectronBleTransport {
|
|
|
176
176
|
private connectedDevices;
|
|
177
177
|
private deviceProtocol;
|
|
178
178
|
private deviceProtocolHints;
|
|
179
|
+
private deviceMtus;
|
|
180
|
+
private devicePacketCapacities;
|
|
179
181
|
private v1Buffers;
|
|
180
182
|
private v2Assemblers;
|
|
181
183
|
private v2FrameQueues;
|
|
182
184
|
private v2FramePromises;
|
|
183
185
|
private protocolV2Links;
|
|
184
186
|
private notificationCleanups;
|
|
187
|
+
private mtuCleanups;
|
|
185
188
|
private disconnectCleanups;
|
|
186
189
|
private notificationTokens;
|
|
187
190
|
private nextNotificationToken;
|
|
@@ -214,6 +217,9 @@ declare class ElectronBleTransport {
|
|
|
214
217
|
private probeProtocolV1;
|
|
215
218
|
private probeProtocolV2;
|
|
216
219
|
private writeOnce;
|
|
220
|
+
private refreshBlePacketCapacity;
|
|
221
|
+
private updateBlePacketCapacity;
|
|
222
|
+
private createMtuSubscription;
|
|
217
223
|
private writeProtocolV2Frame;
|
|
218
224
|
private handleNotification;
|
|
219
225
|
private handleProtocolV2Notification;
|
package/dist/index.js
CHANGED
|
@@ -725,12 +725,21 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
725
725
|
}
|
|
726
726
|
}
|
|
727
727
|
|
|
728
|
+
const BLE_ATT_HEADER_BYTES = 3;
|
|
729
|
+
function resolveBlePacketCapacity(mtu, maximumPacketCapacity, fallbackPacketCapacity) {
|
|
730
|
+
if (typeof mtu !== 'number' || !Number.isFinite(mtu) || mtu <= BLE_ATT_HEADER_BYTES) {
|
|
731
|
+
return fallbackPacketCapacity;
|
|
732
|
+
}
|
|
733
|
+
return Math.min(maximumPacketCapacity, Math.floor(mtu) - BLE_ATT_HEADER_BYTES);
|
|
734
|
+
}
|
|
735
|
+
|
|
728
736
|
const { parseConfigure, ProtocolV1, check } = transport__default["default"];
|
|
729
737
|
function inferProtocolHintFromDeviceName(name) {
|
|
730
738
|
return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
|
|
731
739
|
}
|
|
732
740
|
const toBleDescriptor = (device, protocolType) => (Object.assign({ id: device.id, name: device.name, path: device.id, debug: false, commType: 'electron-ble' }, (protocolType ? { protocolType } : {})));
|
|
733
|
-
const
|
|
741
|
+
const BLE_PACKET_SIZE_FALLBACK = 192;
|
|
742
|
+
const BLE_PACKET_SIZE_MAXIMUM = 244;
|
|
734
743
|
const BLE_WRITE_DELAY_MS = 5;
|
|
735
744
|
const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
|
|
736
745
|
const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
|
|
@@ -743,6 +752,8 @@ class ElectronBleTransport {
|
|
|
743
752
|
this.connectedDevices = new Set();
|
|
744
753
|
this.deviceProtocol = new Map();
|
|
745
754
|
this.deviceProtocolHints = new Map();
|
|
755
|
+
this.deviceMtus = new Map();
|
|
756
|
+
this.devicePacketCapacities = new Map();
|
|
746
757
|
this.v1Buffers = new Map();
|
|
747
758
|
this.v2Assemblers = new Map();
|
|
748
759
|
this.v2FrameQueues = new Map();
|
|
@@ -769,6 +780,7 @@ class ElectronBleTransport {
|
|
|
769
780
|
}),
|
|
770
781
|
});
|
|
771
782
|
this.notificationCleanups = new Map();
|
|
783
|
+
this.mtuCleanups = new Map();
|
|
772
784
|
this.disconnectCleanups = new Map();
|
|
773
785
|
this.notificationTokens = new Map();
|
|
774
786
|
this.nextNotificationToken = 1;
|
|
@@ -808,6 +820,8 @@ class ElectronBleTransport {
|
|
|
808
820
|
.catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] link cleanup failed:', error); });
|
|
809
821
|
this.connectedDevices.delete(deviceId);
|
|
810
822
|
this.deviceProtocol.delete(deviceId);
|
|
823
|
+
this.deviceMtus.delete(deviceId);
|
|
824
|
+
this.devicePacketCapacities.delete(deviceId);
|
|
811
825
|
this.v1Buffers.delete(deviceId);
|
|
812
826
|
this.v2Assemblers.delete(deviceId);
|
|
813
827
|
this.resetProtocolV2Frames(deviceId);
|
|
@@ -822,6 +836,11 @@ class ElectronBleTransport {
|
|
|
822
836
|
notifyCleanup();
|
|
823
837
|
this.notificationCleanups.delete(deviceId);
|
|
824
838
|
}
|
|
839
|
+
const mtuCleanup = this.mtuCleanups.get(deviceId);
|
|
840
|
+
if (mtuCleanup) {
|
|
841
|
+
mtuCleanup();
|
|
842
|
+
this.mtuCleanups.delete(deviceId);
|
|
843
|
+
}
|
|
825
844
|
const disconnectCleanup = this.disconnectCleanups.get(deviceId);
|
|
826
845
|
if (disconnectCleanup) {
|
|
827
846
|
disconnectCleanup();
|
|
@@ -885,7 +904,7 @@ class ElectronBleTransport {
|
|
|
885
904
|
});
|
|
886
905
|
}
|
|
887
906
|
acquire(input) {
|
|
888
|
-
var _a, _b, _c, _d, _e, _f;
|
|
907
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
889
908
|
return __awaiter(this, void 0, void 0, function* () {
|
|
890
909
|
const { uuid, forceCleanRunPromise, expectedProtocol } = input;
|
|
891
910
|
if (!uuid) {
|
|
@@ -921,13 +940,25 @@ class ElectronBleTransport {
|
|
|
921
940
|
catch (error) {
|
|
922
941
|
this.handleBluetoothError(error);
|
|
923
942
|
}
|
|
943
|
+
const mtuCleanup = this.createMtuSubscription(uuid);
|
|
944
|
+
if (mtuCleanup) {
|
|
945
|
+
this.mtuCleanups.set(uuid, mtuCleanup);
|
|
946
|
+
}
|
|
924
947
|
this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
|
|
925
948
|
this.v2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES));
|
|
926
949
|
yield window.desktopApi.nobleBle.subscribe(uuid);
|
|
950
|
+
yield this.refreshBlePacketCapacity(uuid);
|
|
927
951
|
const cleanup = this.createNotificationSubscription(uuid);
|
|
928
952
|
this.notificationCleanups.set(uuid, cleanup);
|
|
929
953
|
const connectionToken = this.notificationTokens.get(uuid);
|
|
930
954
|
const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint);
|
|
955
|
+
if (protocolType === 'V2') {
|
|
956
|
+
(_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug('[Electron BLE] Protocol V2 write configured', {
|
|
957
|
+
writeMode: 'withoutResponse',
|
|
958
|
+
negotiatedMtu: this.deviceMtus.get(uuid),
|
|
959
|
+
packetCapacity: (_e = this.devicePacketCapacities.get(uuid)) !== null && _e !== void 0 ? _e : BLE_PACKET_SIZE_FALLBACK,
|
|
960
|
+
});
|
|
961
|
+
}
|
|
931
962
|
const disconnectCleanup = window.desktopApi.nobleBle.onDeviceDisconnected((disconnectedDevice) => {
|
|
932
963
|
var _a;
|
|
933
964
|
if (disconnectedDevice.id === uuid &&
|
|
@@ -944,15 +975,15 @@ class ElectronBleTransport {
|
|
|
944
975
|
return Object.assign(Object.assign({}, toBleDescriptor({ id: device.id, name: device.name }, protocolType)), { uuid });
|
|
945
976
|
}
|
|
946
977
|
catch (error) {
|
|
947
|
-
(
|
|
978
|
+
(_f = this.Log) === null || _f === void 0 ? void 0 : _f.error('[Electron BLE] acquire failed:', error);
|
|
948
979
|
try {
|
|
949
|
-
if (((
|
|
980
|
+
if (((_g = window.desktopApi) === null || _g === void 0 ? void 0 : _g.nobleBle) && this.connectedDevices.has(uuid)) {
|
|
950
981
|
yield window.desktopApi.nobleBle.unsubscribe(uuid);
|
|
951
982
|
yield window.desktopApi.nobleBle.disconnect(uuid);
|
|
952
983
|
}
|
|
953
984
|
}
|
|
954
985
|
catch (cleanupError) {
|
|
955
|
-
(
|
|
986
|
+
(_h = this.Log) === null || _h === void 0 ? void 0 : _h.debug('[Electron BLE] acquire cleanup failed:', cleanupError);
|
|
956
987
|
}
|
|
957
988
|
this.cleanupDeviceState(uuid);
|
|
958
989
|
throw error;
|
|
@@ -1087,6 +1118,7 @@ class ElectronBleTransport {
|
|
|
1087
1118
|
yield ((_j = (_h = window.desktopApi) === null || _h === void 0 ? void 0 : _h.nobleBle) === null || _j === void 0 ? void 0 : _j.connect(uuid));
|
|
1088
1119
|
this.connectedDevices.add(uuid);
|
|
1089
1120
|
yield ((_l = (_k = window.desktopApi) === null || _k === void 0 ? void 0 : _k.nobleBle) === null || _l === void 0 ? void 0 : _l.subscribe(uuid));
|
|
1121
|
+
yield this.refreshBlePacketCapacity(uuid);
|
|
1090
1122
|
}
|
|
1091
1123
|
catch (error) {
|
|
1092
1124
|
(_m = this.Log) === null || _m === void 0 ? void 0 : _m.debug(`[Electron BLE] reconnect after Protocol ${protocol} probe failed:`, error);
|
|
@@ -1149,16 +1181,47 @@ class ElectronBleTransport {
|
|
|
1149
1181
|
yield nobleBle.write(uuid, hexData);
|
|
1150
1182
|
});
|
|
1151
1183
|
}
|
|
1184
|
+
refreshBlePacketCapacity(uuid) {
|
|
1185
|
+
var _a, _b;
|
|
1186
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1187
|
+
const device = yield ((_b = (_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle) === null || _b === void 0 ? void 0 : _b.getDevice(uuid));
|
|
1188
|
+
this.updateBlePacketCapacity(uuid, device === null || device === void 0 ? void 0 : device.mtu);
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1191
|
+
updateBlePacketCapacity(uuid, mtu) {
|
|
1192
|
+
const packetCapacity = resolveBlePacketCapacity(mtu, BLE_PACKET_SIZE_MAXIMUM, BLE_PACKET_SIZE_FALLBACK);
|
|
1193
|
+
if (typeof mtu === 'number') {
|
|
1194
|
+
this.deviceMtus.set(uuid, mtu);
|
|
1195
|
+
}
|
|
1196
|
+
else {
|
|
1197
|
+
this.deviceMtus.delete(uuid);
|
|
1198
|
+
}
|
|
1199
|
+
this.devicePacketCapacities.set(uuid, packetCapacity);
|
|
1200
|
+
}
|
|
1201
|
+
createMtuSubscription(uuid) {
|
|
1202
|
+
var _a, _b;
|
|
1203
|
+
const onMtuChanged = (_b = (_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle) === null || _b === void 0 ? void 0 : _b.onMtuChanged;
|
|
1204
|
+
if (!onMtuChanged)
|
|
1205
|
+
return undefined;
|
|
1206
|
+
return onMtuChanged(device => {
|
|
1207
|
+
if (device.id === uuid) {
|
|
1208
|
+
this.updateBlePacketCapacity(uuid, device.mtu);
|
|
1209
|
+
}
|
|
1210
|
+
});
|
|
1211
|
+
}
|
|
1152
1212
|
writeProtocolV2Frame(uuid, frame, context, assertCurrentGeneration) {
|
|
1213
|
+
var _a;
|
|
1214
|
+
const packetCapacity = (_a = this.devicePacketCapacities.get(uuid)) !== null && _a !== void 0 ? _a : BLE_PACKET_SIZE_FALLBACK;
|
|
1215
|
+
const shouldPace = !context.highVolume;
|
|
1153
1216
|
return transport.writeProtocolV2BleFrame({
|
|
1154
1217
|
frame,
|
|
1155
|
-
packetCapacity
|
|
1218
|
+
packetCapacity,
|
|
1156
1219
|
assertActive: assertCurrentGeneration,
|
|
1157
1220
|
signal: context.signal,
|
|
1158
1221
|
abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
|
|
1159
|
-
initialDelayMs: frame.length <=
|
|
1160
|
-
burstSize: 1,
|
|
1161
|
-
burstPauseMs: BLE_WRITE_DELAY_MS,
|
|
1222
|
+
initialDelayMs: shouldPace && frame.length <= packetCapacity ? BLE_WRITE_DELAY_MS : 0,
|
|
1223
|
+
burstSize: shouldPace ? 1 : undefined,
|
|
1224
|
+
burstPauseMs: shouldPace ? BLE_WRITE_DELAY_MS : 0,
|
|
1162
1225
|
writePacket: packet => this.writeOnce(uuid, transport.bytesToHex(packet)),
|
|
1163
1226
|
});
|
|
1164
1227
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-transport-web-device",
|
|
3
|
-
"version": "1.2.0-alpha.
|
|
3
|
+
"version": "1.2.0-alpha.71",
|
|
4
4
|
"author": "OneKey",
|
|
5
5
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,13 +21,13 @@
|
|
|
21
21
|
"lint:fix": "eslint . --fix"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@onekeyfe/hd-shared": "1.2.0-alpha.
|
|
25
|
-
"@onekeyfe/hd-transport": "1.2.0-alpha.
|
|
24
|
+
"@onekeyfe/hd-shared": "1.2.0-alpha.71",
|
|
25
|
+
"@onekeyfe/hd-transport": "1.2.0-alpha.71"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@onekeyfe/hd-transport-electron": "1.2.0-alpha.
|
|
28
|
+
"@onekeyfe/hd-transport-electron": "1.2.0-alpha.71",
|
|
29
29
|
"@types/w3c-web-usb": "^1.0.6",
|
|
30
30
|
"@types/web-bluetooth": "^0.0.17"
|
|
31
31
|
},
|
|
32
|
-
"gitHead": "
|
|
32
|
+
"gitHead": "c58f39f2e5c54a98faa8844cd8b6b0453a17d80c"
|
|
33
33
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const BLE_ATT_HEADER_BYTES = 3;
|
|
2
|
+
|
|
3
|
+
export function resolveBlePacketCapacity(
|
|
4
|
+
mtu: number | null | undefined,
|
|
5
|
+
maximumPacketCapacity: number,
|
|
6
|
+
fallbackPacketCapacity: number
|
|
7
|
+
): number {
|
|
8
|
+
if (typeof mtu !== 'number' || !Number.isFinite(mtu) || mtu <= BLE_ATT_HEADER_BYTES) {
|
|
9
|
+
return fallbackPacketCapacity;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return Math.min(maximumPacketCapacity, Math.floor(mtu) - BLE_ATT_HEADER_BYTES);
|
|
13
|
+
}
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
} from '@onekeyfe/hd-shared';
|
|
20
20
|
|
|
21
21
|
import { createTransportCallLog, shouldSuppressHighVolumeCallLog } from './transportLog';
|
|
22
|
+
import { resolveBlePacketCapacity } from './ble-packet-capacity';
|
|
22
23
|
|
|
23
24
|
import type { Deferred } from '@onekeyfe/hd-shared';
|
|
24
25
|
import type { DesktopAPI } from '@onekeyfe/hd-transport-electron';
|
|
@@ -68,7 +69,8 @@ const toBleDescriptor = (
|
|
|
68
69
|
...(protocolType ? { protocolType } : {}),
|
|
69
70
|
} as OneKeyDeviceInfo);
|
|
70
71
|
|
|
71
|
-
const
|
|
72
|
+
const BLE_PACKET_SIZE_FALLBACK = 192;
|
|
73
|
+
const BLE_PACKET_SIZE_MAXIMUM = 244;
|
|
72
74
|
const BLE_WRITE_DELAY_MS = 5;
|
|
73
75
|
const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
|
|
74
76
|
const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
|
|
@@ -104,6 +106,10 @@ export default class ElectronBleTransport {
|
|
|
104
106
|
|
|
105
107
|
private deviceProtocolHints: Map<string, ProtocolType> = new Map();
|
|
106
108
|
|
|
109
|
+
private deviceMtus: Map<string, number> = new Map();
|
|
110
|
+
|
|
111
|
+
private devicePacketCapacities: Map<string, number> = new Map();
|
|
112
|
+
|
|
107
113
|
private v1Buffers: Map<string, { buffer: number[]; bufferLength: number }> = new Map();
|
|
108
114
|
|
|
109
115
|
private v2Assemblers: Map<string, ProtocolV2FrameAssembler> = new Map();
|
|
@@ -135,6 +141,8 @@ export default class ElectronBleTransport {
|
|
|
135
141
|
|
|
136
142
|
private notificationCleanups: Map<string, () => void> = new Map();
|
|
137
143
|
|
|
144
|
+
private mtuCleanups: Map<string, () => void> = new Map();
|
|
145
|
+
|
|
138
146
|
private disconnectCleanups: Map<string, () => void> = new Map();
|
|
139
147
|
|
|
140
148
|
private notificationTokens: Map<string, number> = new Map();
|
|
@@ -178,6 +186,8 @@ export default class ElectronBleTransport {
|
|
|
178
186
|
.catch(error => this.Log?.debug('[Electron BLE] link cleanup failed:', error));
|
|
179
187
|
this.connectedDevices.delete(deviceId);
|
|
180
188
|
this.deviceProtocol.delete(deviceId);
|
|
189
|
+
this.deviceMtus.delete(deviceId);
|
|
190
|
+
this.devicePacketCapacities.delete(deviceId);
|
|
181
191
|
// Keep deviceProtocolHints — it's inferred from device name (e.g. "Pro 2" → V2)
|
|
182
192
|
// and doesn't depend on connection state. Preserving it avoids redundant V1 probe on reconnect.
|
|
183
193
|
this.v1Buffers.delete(deviceId);
|
|
@@ -196,6 +206,12 @@ export default class ElectronBleTransport {
|
|
|
196
206
|
this.notificationCleanups.delete(deviceId);
|
|
197
207
|
}
|
|
198
208
|
|
|
209
|
+
const mtuCleanup = this.mtuCleanups.get(deviceId);
|
|
210
|
+
if (mtuCleanup) {
|
|
211
|
+
mtuCleanup();
|
|
212
|
+
this.mtuCleanups.delete(deviceId);
|
|
213
|
+
}
|
|
214
|
+
|
|
199
215
|
const disconnectCleanup = this.disconnectCleanups.get(deviceId);
|
|
200
216
|
if (disconnectCleanup) {
|
|
201
217
|
disconnectCleanup();
|
|
@@ -306,16 +322,29 @@ export default class ElectronBleTransport {
|
|
|
306
322
|
this.handleBluetoothError(error);
|
|
307
323
|
}
|
|
308
324
|
|
|
325
|
+
const mtuCleanup = this.createMtuSubscription(uuid);
|
|
326
|
+
if (mtuCleanup) {
|
|
327
|
+
this.mtuCleanups.set(uuid, mtuCleanup);
|
|
328
|
+
}
|
|
329
|
+
|
|
309
330
|
this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
|
|
310
331
|
this.v2Assemblers.set(uuid, new ProtocolV2FrameAssembler(PROTOCOL_V2_BLE_FRAME_MAX_BYTES));
|
|
311
332
|
|
|
312
333
|
await window.desktopApi.nobleBle.subscribe(uuid);
|
|
334
|
+
await this.refreshBlePacketCapacity(uuid);
|
|
313
335
|
|
|
314
336
|
const cleanup = this.createNotificationSubscription(uuid);
|
|
315
337
|
this.notificationCleanups.set(uuid, cleanup);
|
|
316
338
|
const connectionToken = this.notificationTokens.get(uuid);
|
|
317
339
|
|
|
318
340
|
const protocolType = await this.detectProtocol(uuid, expectedProtocol, protocolHint);
|
|
341
|
+
if (protocolType === 'V2') {
|
|
342
|
+
this.Log?.debug('[Electron BLE] Protocol V2 write configured', {
|
|
343
|
+
writeMode: 'withoutResponse',
|
|
344
|
+
negotiatedMtu: this.deviceMtus.get(uuid),
|
|
345
|
+
packetCapacity: this.devicePacketCapacities.get(uuid) ?? BLE_PACKET_SIZE_FALLBACK,
|
|
346
|
+
});
|
|
347
|
+
}
|
|
319
348
|
|
|
320
349
|
const disconnectCleanup = window.desktopApi.nobleBle.onDeviceDisconnected(
|
|
321
350
|
(disconnectedDevice: any) => {
|
|
@@ -498,6 +527,7 @@ export default class ElectronBleTransport {
|
|
|
498
527
|
await window.desktopApi?.nobleBle?.connect(uuid);
|
|
499
528
|
this.connectedDevices.add(uuid);
|
|
500
529
|
await window.desktopApi?.nobleBle?.subscribe(uuid);
|
|
530
|
+
await this.refreshBlePacketCapacity(uuid);
|
|
501
531
|
} catch (error) {
|
|
502
532
|
this.Log?.debug(`[Electron BLE] reconnect after Protocol ${protocol} probe failed:`, error);
|
|
503
533
|
throw error;
|
|
@@ -557,21 +587,52 @@ export default class ElectronBleTransport {
|
|
|
557
587
|
await nobleBle.write(uuid, hexData);
|
|
558
588
|
}
|
|
559
589
|
|
|
590
|
+
private async refreshBlePacketCapacity(uuid: string): Promise<void> {
|
|
591
|
+
const device = await window.desktopApi?.nobleBle?.getDevice(uuid);
|
|
592
|
+
this.updateBlePacketCapacity(uuid, device?.mtu);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
private updateBlePacketCapacity(uuid: string, mtu?: number): void {
|
|
596
|
+
const packetCapacity = resolveBlePacketCapacity(
|
|
597
|
+
mtu,
|
|
598
|
+
BLE_PACKET_SIZE_MAXIMUM,
|
|
599
|
+
BLE_PACKET_SIZE_FALLBACK
|
|
600
|
+
);
|
|
601
|
+
if (typeof mtu === 'number') {
|
|
602
|
+
this.deviceMtus.set(uuid, mtu);
|
|
603
|
+
} else {
|
|
604
|
+
this.deviceMtus.delete(uuid);
|
|
605
|
+
}
|
|
606
|
+
this.devicePacketCapacities.set(uuid, packetCapacity);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
private createMtuSubscription(uuid: string): (() => void) | undefined {
|
|
610
|
+
const onMtuChanged = window.desktopApi?.nobleBle?.onMtuChanged;
|
|
611
|
+
if (!onMtuChanged) return undefined;
|
|
612
|
+
return onMtuChanged(device => {
|
|
613
|
+
if (device.id === uuid) {
|
|
614
|
+
this.updateBlePacketCapacity(uuid, device.mtu);
|
|
615
|
+
}
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
|
|
560
619
|
private writeProtocolV2Frame(
|
|
561
620
|
uuid: string,
|
|
562
621
|
frame: Uint8Array,
|
|
563
622
|
context: ProtocolV2CallContext,
|
|
564
623
|
assertCurrentGeneration: () => void
|
|
565
624
|
) {
|
|
625
|
+
const packetCapacity = this.devicePacketCapacities.get(uuid) ?? BLE_PACKET_SIZE_FALLBACK;
|
|
626
|
+
const shouldPace = !context.highVolume;
|
|
566
627
|
return writeProtocolV2BleFrame({
|
|
567
628
|
frame,
|
|
568
|
-
packetCapacity
|
|
629
|
+
packetCapacity,
|
|
569
630
|
assertActive: assertCurrentGeneration,
|
|
570
631
|
signal: context.signal,
|
|
571
632
|
abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
|
|
572
|
-
initialDelayMs: frame.length <=
|
|
573
|
-
burstSize: 1,
|
|
574
|
-
burstPauseMs: BLE_WRITE_DELAY_MS,
|
|
633
|
+
initialDelayMs: shouldPace && frame.length <= packetCapacity ? BLE_WRITE_DELAY_MS : 0,
|
|
634
|
+
burstSize: shouldPace ? 1 : undefined,
|
|
635
|
+
burstPauseMs: shouldPace ? BLE_WRITE_DELAY_MS : 0,
|
|
575
636
|
writePacket: packet => this.writeOnce(uuid, bytesToHex(packet)),
|
|
576
637
|
});
|
|
577
638
|
}
|