@onekeyfe/hd-transport-web-device 1.2.0-alpha.67 → 1.2.0-alpha.69
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 +101 -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 +75 -10
- package/package.json +5 -5
- package/src/ble-packet-capacity.ts +13 -0
- package/src/electron-ble-transport.ts +71 -6
|
@@ -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);
|
|
@@ -506,6 +560,53 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
506
560
|
}
|
|
507
561
|
});
|
|
508
562
|
|
|
563
|
+
test('ignores a delayed disconnect event from the previous BLE connection', async () => {
|
|
564
|
+
const device = { id: 'delayed-disconnect-pro2-id', name: 'OneKey Pro 2' };
|
|
565
|
+
const nobleBle = createNobleBle(device);
|
|
566
|
+
let notificationHandler: ((deviceId: string, data: string) => void) | undefined;
|
|
567
|
+
const disconnectHandlers: Array<
|
|
568
|
+
(disconnectedDevice: { id: string; name: string | null }) => void
|
|
569
|
+
> = [];
|
|
570
|
+
nobleBle.onNotification.mockImplementation(handler => {
|
|
571
|
+
notificationHandler = handler;
|
|
572
|
+
return jest.fn();
|
|
573
|
+
});
|
|
574
|
+
nobleBle.onDeviceDisconnected.mockImplementation(handler => {
|
|
575
|
+
disconnectHandlers.push(handler);
|
|
576
|
+
return jest.fn();
|
|
577
|
+
});
|
|
578
|
+
let responseSeq = 0;
|
|
579
|
+
nobleBle.write.mockImplementation(() => {
|
|
580
|
+
responseSeq += 1;
|
|
581
|
+
const response = ProtocolV2.encodeFrame(
|
|
582
|
+
schemas,
|
|
583
|
+
'Success',
|
|
584
|
+
{ message: 'ok' },
|
|
585
|
+
{ router: PROTOCOL_V2_CHANNEL_BLE_UART, seq: responseSeq }
|
|
586
|
+
);
|
|
587
|
+
setTimeout(() => notificationHandler?.(device.id, bytesToHex(response)), 0);
|
|
588
|
+
return Promise.resolve();
|
|
589
|
+
});
|
|
590
|
+
const bleTransport = configureTransport(nobleBle);
|
|
591
|
+
|
|
592
|
+
try {
|
|
593
|
+
await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
|
|
594
|
+
await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
|
|
595
|
+
|
|
596
|
+
disconnectHandlers[0]?.(device);
|
|
597
|
+
|
|
598
|
+
expect(bleTransport.getProtocolType(device.id)).toBe('V2');
|
|
599
|
+
await expect(
|
|
600
|
+
bleTransport.call(device.id, 'Ping', { message: 'after-stale-disconnect' })
|
|
601
|
+
).resolves.toEqual({
|
|
602
|
+
type: 'Success',
|
|
603
|
+
message: { message: 'ok' },
|
|
604
|
+
});
|
|
605
|
+
} finally {
|
|
606
|
+
await bleTransport.release(device.id);
|
|
607
|
+
}
|
|
608
|
+
});
|
|
609
|
+
|
|
509
610
|
test('preserves the active Protocol V2 link when the same schema is configured again', async () => {
|
|
510
611
|
const device = { id: 'stable-schema-pro2-id', name: 'OneKey Pro 2' };
|
|
511
612
|
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,15 +940,29 @@ 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);
|
|
953
|
+
const connectionToken = this.notificationTokens.get(uuid);
|
|
929
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
|
+
}
|
|
930
962
|
const disconnectCleanup = window.desktopApi.nobleBle.onDeviceDisconnected((disconnectedDevice) => {
|
|
931
963
|
var _a;
|
|
932
|
-
if (disconnectedDevice.id === uuid
|
|
964
|
+
if (disconnectedDevice.id === uuid &&
|
|
965
|
+
this.notificationTokens.get(uuid) === connectionToken) {
|
|
933
966
|
this.cleanupDeviceState(uuid);
|
|
934
967
|
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit(transport.TRANSPORT_EVENT.DEVICE_DISCONNECT, {
|
|
935
968
|
name: disconnectedDevice.name,
|
|
@@ -942,15 +975,15 @@ class ElectronBleTransport {
|
|
|
942
975
|
return Object.assign(Object.assign({}, toBleDescriptor({ id: device.id, name: device.name }, protocolType)), { uuid });
|
|
943
976
|
}
|
|
944
977
|
catch (error) {
|
|
945
|
-
(
|
|
978
|
+
(_f = this.Log) === null || _f === void 0 ? void 0 : _f.error('[Electron BLE] acquire failed:', error);
|
|
946
979
|
try {
|
|
947
|
-
if (((
|
|
980
|
+
if (((_g = window.desktopApi) === null || _g === void 0 ? void 0 : _g.nobleBle) && this.connectedDevices.has(uuid)) {
|
|
948
981
|
yield window.desktopApi.nobleBle.unsubscribe(uuid);
|
|
949
982
|
yield window.desktopApi.nobleBle.disconnect(uuid);
|
|
950
983
|
}
|
|
951
984
|
}
|
|
952
985
|
catch (cleanupError) {
|
|
953
|
-
(
|
|
986
|
+
(_h = this.Log) === null || _h === void 0 ? void 0 : _h.debug('[Electron BLE] acquire cleanup failed:', cleanupError);
|
|
954
987
|
}
|
|
955
988
|
this.cleanupDeviceState(uuid);
|
|
956
989
|
throw error;
|
|
@@ -1085,6 +1118,7 @@ class ElectronBleTransport {
|
|
|
1085
1118
|
yield ((_j = (_h = window.desktopApi) === null || _h === void 0 ? void 0 : _h.nobleBle) === null || _j === void 0 ? void 0 : _j.connect(uuid));
|
|
1086
1119
|
this.connectedDevices.add(uuid);
|
|
1087
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);
|
|
1088
1122
|
}
|
|
1089
1123
|
catch (error) {
|
|
1090
1124
|
(_m = this.Log) === null || _m === void 0 ? void 0 : _m.debug(`[Electron BLE] reconnect after Protocol ${protocol} probe failed:`, error);
|
|
@@ -1147,16 +1181,47 @@ class ElectronBleTransport {
|
|
|
1147
1181
|
yield nobleBle.write(uuid, hexData);
|
|
1148
1182
|
});
|
|
1149
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
|
+
}
|
|
1150
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;
|
|
1151
1216
|
return transport.writeProtocolV2BleFrame({
|
|
1152
1217
|
frame,
|
|
1153
|
-
packetCapacity
|
|
1218
|
+
packetCapacity,
|
|
1154
1219
|
assertActive: assertCurrentGeneration,
|
|
1155
1220
|
signal: context.signal,
|
|
1156
1221
|
abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
|
|
1157
|
-
initialDelayMs: frame.length <=
|
|
1158
|
-
burstSize: 1,
|
|
1159
|
-
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,
|
|
1160
1225
|
writePacket: packet => this.writeOnce(uuid, transport.bytesToHex(packet)),
|
|
1161
1226
|
});
|
|
1162
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.69",
|
|
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.69",
|
|
25
|
+
"@onekeyfe/hd-transport": "1.2.0-alpha.69"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@onekeyfe/hd-transport-electron": "1.2.0-alpha.
|
|
28
|
+
"@onekeyfe/hd-transport-electron": "1.2.0-alpha.69",
|
|
29
29
|
"@types/w3c-web-usb": "^1.0.6",
|
|
30
30
|
"@types/web-bluetooth": "^0.0.17"
|
|
31
31
|
},
|
|
32
|
-
"gitHead": "
|
|
32
|
+
"gitHead": "c6baed25917e3c3f027f83dfc23cde85400de558"
|
|
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,19 +322,36 @@ 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);
|
|
338
|
+
const connectionToken = this.notificationTokens.get(uuid);
|
|
316
339
|
|
|
317
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
|
+
}
|
|
318
348
|
|
|
319
349
|
const disconnectCleanup = window.desktopApi.nobleBle.onDeviceDisconnected(
|
|
320
350
|
(disconnectedDevice: any) => {
|
|
321
|
-
if (
|
|
351
|
+
if (
|
|
352
|
+
disconnectedDevice.id === uuid &&
|
|
353
|
+
this.notificationTokens.get(uuid) === connectionToken
|
|
354
|
+
) {
|
|
322
355
|
this.cleanupDeviceState(uuid);
|
|
323
356
|
this.emitter?.emit(TRANSPORT_EVENT.DEVICE_DISCONNECT, {
|
|
324
357
|
name: disconnectedDevice.name,
|
|
@@ -494,6 +527,7 @@ export default class ElectronBleTransport {
|
|
|
494
527
|
await window.desktopApi?.nobleBle?.connect(uuid);
|
|
495
528
|
this.connectedDevices.add(uuid);
|
|
496
529
|
await window.desktopApi?.nobleBle?.subscribe(uuid);
|
|
530
|
+
await this.refreshBlePacketCapacity(uuid);
|
|
497
531
|
} catch (error) {
|
|
498
532
|
this.Log?.debug(`[Electron BLE] reconnect after Protocol ${protocol} probe failed:`, error);
|
|
499
533
|
throw error;
|
|
@@ -553,21 +587,52 @@ export default class ElectronBleTransport {
|
|
|
553
587
|
await nobleBle.write(uuid, hexData);
|
|
554
588
|
}
|
|
555
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
|
+
|
|
556
619
|
private writeProtocolV2Frame(
|
|
557
620
|
uuid: string,
|
|
558
621
|
frame: Uint8Array,
|
|
559
622
|
context: ProtocolV2CallContext,
|
|
560
623
|
assertCurrentGeneration: () => void
|
|
561
624
|
) {
|
|
625
|
+
const packetCapacity = this.devicePacketCapacities.get(uuid) ?? BLE_PACKET_SIZE_FALLBACK;
|
|
626
|
+
const shouldPace = !context.highVolume;
|
|
562
627
|
return writeProtocolV2BleFrame({
|
|
563
628
|
frame,
|
|
564
|
-
packetCapacity
|
|
629
|
+
packetCapacity,
|
|
565
630
|
assertActive: assertCurrentGeneration,
|
|
566
631
|
signal: context.signal,
|
|
567
632
|
abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
|
|
568
|
-
initialDelayMs: frame.length <=
|
|
569
|
-
burstSize: 1,
|
|
570
|
-
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,
|
|
571
636
|
writePacket: packet => this.writeOnce(uuid, bytesToHex(packet)),
|
|
572
637
|
});
|
|
573
638
|
}
|