@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.
@@ -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,2 @@
1
+ export declare function resolveBlePacketCapacity(mtu: number | null | undefined, maximumPacketCapacity: number, fallbackPacketCapacity: number): number;
2
+ //# sourceMappingURL=ble-packet-capacity.d.ts.map
@@ -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":";AAsBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EAEZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,YAAY,MAAM,QAAQ,CAAC;AAIvC,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,UAAU,CAAC,EAAE,UAAU,CAAC;KACzB;CACF;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,gBAAgB,CAAC,EAAE,YAAY,CAAC;IAChC,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B,CAAC;AAoCF,MAAM,CAAC,OAAO,OAAO,oBAAoB;IACvC,OAAO,CAAC,SAAS,CAA0D;IAE3E,OAAO,CAAC,WAAW,CAA0D;IAE7E,OAAO,CAAC,6BAA6B,CAAqB;IAE1D,IAAI,SAA0B;IAE9B,UAAU,UAAS;IAEnB,UAAU,EAAE,QAAQ,CAAC,UAAU,GAAG,MAAM,CAAC,GAAG,IAAI,CAAQ;IAExD,OAAO,CAAC,kBAAkB,CAAuB;IAEjD,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB,OAAO,CAAC,gBAAgB,CAA0B;IAElD,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAEnE,OAAO,CAAC,SAAS,CAAsE;IAEvF,OAAO,CAAC,YAAY,CAAoD;IAExE,OAAO,CAAC,aAAa,CAAwC;IAE7D,OAAO,CAAC,eAAe,CAAgD;IAEvE,OAAO,CAAC,eAAe,CAmBpB;IAEH,OAAO,CAAC,oBAAoB,CAAsC;IAElE,OAAO,CAAC,kBAAkB,CAAsC;IAEhE,OAAO,CAAC,kBAAkB,CAAkC;IAE5D,OAAO,CAAC,qBAAqB,CAAK;IAElC,OAAO,CAAC,oBAAoB;IA+B5B,OAAO,CAAC,kBAAkB;IA+B1B,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,YAAY;IAcxC,SAAS,CAAC,UAAU,EAAE,GAAG;IAKzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAgB7B,MAAM;IAIN,SAAS,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAqBxC,OAAO,CAAC,KAAK,EAAE,eAAe;;;;;;;;;;;IAsF9B,OAAO,CAAC,EAAE,EAAE,MAAM;YAUV,aAAa;IAe3B,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;IAOpC,OAAO,CAAC,kBAAkB;YAMZ,cAAc;IA+C5B,OAAO,CAAC,8BAA8B;YAgBxB,iCAAiC;YA8CjC,eAAe;YAkBf,eAAe;YAuBf,SAAS;IASvB,OAAO,CAAC,oBAAoB;IAmB5B,OAAO,CAAC,kBAAkB;IAwB1B,OAAO,CAAC,4BAA4B;IAkBpC,OAAO,CAAC,uBAAuB;IAS/B,OAAO,CAAC,sBAAsB;IAU9B,OAAO,CAAC,qBAAqB;IAI7B,OAAO,CAAC,sBAAsB;YAShB,mBAAmB;IAiBjC,OAAO,CAAC,4BAA4B;IAqB9B,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YA2BlB,cAAc;YAqFd,cAAc;IA0B5B,OAAO,CAAC,uBAAuB;IAyC/B,OAAO,CAAC,6BAA6B;IAsCrC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;CAGxD"}
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 BLE_PACKET_SIZE = 192;
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
- (_d = this.Log) === null || _d === void 0 ? void 0 : _d.error('[Electron BLE] acquire failed:', error);
978
+ (_f = this.Log) === null || _f === void 0 ? void 0 : _f.error('[Electron BLE] acquire failed:', error);
946
979
  try {
947
- if (((_e = window.desktopApi) === null || _e === void 0 ? void 0 : _e.nobleBle) && this.connectedDevices.has(uuid)) {
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
- (_f = this.Log) === null || _f === void 0 ? void 0 : _f.debug('[Electron BLE] acquire cleanup failed:', cleanupError);
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: BLE_PACKET_SIZE,
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 <= BLE_PACKET_SIZE ? BLE_WRITE_DELAY_MS : 0,
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.67",
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.67",
25
- "@onekeyfe/hd-transport": "1.2.0-alpha.67"
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.67",
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": "5fea66e8c65a1fab2ccc099b2811c8749b2487e0"
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 BLE_PACKET_SIZE = 192;
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 (disconnectedDevice.id === uuid) {
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: BLE_PACKET_SIZE,
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 <= BLE_PACKET_SIZE ? BLE_WRITE_DELAY_MS : 0,
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
  }