@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.
@@ -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,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;;;;;;;;;;;IA0F9B,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,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
- (_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);
948
979
  try {
949
- 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)) {
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
- (_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);
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: BLE_PACKET_SIZE,
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 <= BLE_PACKET_SIZE ? BLE_WRITE_DELAY_MS : 0,
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.70",
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.70",
25
- "@onekeyfe/hd-transport": "1.2.0-alpha.70"
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.70",
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": "d86b033cf970f97e060c49fe4cf618f38b9ce666"
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 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,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: BLE_PACKET_SIZE,
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 <= BLE_PACKET_SIZE ? BLE_WRITE_DELAY_MS : 0,
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
  }