@onekeyfe/hd-transport-web-device 1.2.0-alpha.2 → 1.2.0-alpha.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/webusb.d.ts CHANGED
@@ -13,6 +13,7 @@ export default class WebUsbTransport {
13
13
  private deviceProtocolHints;
14
14
  private protocolV2Assemblers;
15
15
  private protocolV2Sessions;
16
+ private protocolV2Sequences;
16
17
  private protocolV2ReadTimeouts;
17
18
  private deviceEndpoints;
18
19
  private mockSerialPaths;
@@ -50,6 +51,7 @@ export default class WebUsbTransport {
50
51
  private getTransferInData;
51
52
  private toArrayBuffer;
52
53
  private transferOutWithRetry;
54
+ private transferOutOnce;
53
55
  private transferInWithRetry;
54
56
  private resetConnectionAfterProbe;
55
57
  private withProtocolReadTimeout;
@@ -1 +1 @@
1
- {"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":";AACA,OAAO,SAWN,MAAM,wBAAwB,CAAC;AAIhC,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EACpB,YAAY,EACZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAqChC,MAAM,WAAW,UAAW,SAAQ,oBAAoB;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,CAAC;IAClB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAaD,MAAM,CAAC,OAAO,OAAO,eAAe;IAClC,QAAQ,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAGlE,UAAU,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAGpE,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAGnE,OAAO,CAAC,oBAAoB,CAAoD;IAGhF,OAAO,CAAC,kBAAkB,CAA6C;IAGvE,OAAO,CAAC,sBAAsB,CAA8C;IAG5E,OAAO,CAAC,eAAe,CAA2C;IAOlE,OAAO,CAAC,eAAe,CAA6C;IAEpE,OAAO,CAAC,iBAAiB,CAAK;IAE9B,IAAI,SAAqB;IAEzB,OAAO,UAAS;IAEhB,UAAU,UAAS;IAEnB,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,GAAG,CAAC,EAAE,GAAG,CAAC;IAMV,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAM;IAEnC,eAAe,SAAoB;IAEnC,UAAU,SAAe;IAEzB,WAAW,SAAgB;IAK3B,IAAI,CAAC,MAAM,EAAE,GAAG;IAgBhB,SAAS,CAAC,UAAU,EAAE,GAAG;IASzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAY7B,kBAAkB;IAmBlB,SAAS;IASf,OAAO,CAAC,aAAa;IAmBf,mBAAmB;IAsCnB,OAAO,CAAC,KAAK,EAAE,YAAY;IA0BjC,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;YAOtB,cAAc;IA6CtB,UAAU,CAAC,IAAI,EAAE,MAAM;IAwBvB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;IAkB1C,OAAO,CAAC,iBAAiB;IAiCnB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;YA0CpC,eAAe;YAmBf,iBAAiB;IAezB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAIvE,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,wBAAwB;YAalB,yBAAyB;IAiCvC,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,aAAa;YASP,oBAAoB;YAoCpB,mBAAmB;YA2CnB,yBAAyB;YAyBzB,uBAAuB;YAoCvB,eAAe;YAcf,eAAe;IAiBvB,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YAkClB,cAAc;YAkCd,cAAc;YA4Cd,sBAAsB;IA4C9B,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;IAgD5C,OAAO,CAAC,IAAI,EAAE,MAAM;IAiB1B,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;CAGxD"}
1
+ {"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":";AACA,OAAO,SAWN,MAAM,wBAAwB,CAAC;AAYhC,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EACpB,YAAY,EACZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAqBhC,MAAM,WAAW,UAAW,SAAQ,oBAAoB;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,CAAC;IAClB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAaD,MAAM,CAAC,OAAO,OAAO,eAAe;IAClC,QAAQ,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAGlE,UAAU,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAGpE,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAGnE,OAAO,CAAC,oBAAoB,CAAoD;IAGhF,OAAO,CAAC,kBAAkB,CAA6C;IAGvE,OAAO,CAAC,mBAAmB,CAAoD;IAG/E,OAAO,CAAC,sBAAsB,CAA8C;IAG5E,OAAO,CAAC,eAAe,CAA2C;IAMlE,OAAO,CAAC,eAAe,CAA6C;IAEpE,OAAO,CAAC,iBAAiB,CAAK;IAE9B,IAAI,SAAqB;IAEzB,OAAO,UAAS;IAEhB,UAAU,UAAS;IAEnB,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,GAAG,CAAC,EAAE,GAAG,CAAC;IAMV,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAM;IAEnC,eAAe,SAAoB;IAEnC,UAAU,SAAe;IAEzB,WAAW,SAAgB;IAK3B,IAAI,CAAC,MAAM,EAAE,GAAG;IAgBhB,SAAS,CAAC,UAAU,EAAE,GAAG;IASzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAW7B,kBAAkB;IAmBlB,SAAS;IAQf,OAAO,CAAC,aAAa;IAmBf,mBAAmB;IAgCnB,OAAO,CAAC,KAAK,EAAE,YAAY;IA0BjC,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;YAOtB,cAAc;IAgDtB,UAAU,CAAC,IAAI,EAAE,MAAM;IAwBvB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;IAkB1C,OAAO,CAAC,iBAAiB;IAiCnB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;YAmCpC,eAAe;YAmBf,iBAAiB;IAezB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAIvE,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,wBAAwB;YAalB,yBAAyB;IAiCvC,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,aAAa;YASP,oBAAoB;YA2BpB,eAAe;YAaf,mBAAmB;YA2CnB,yBAAyB;YAyBzB,uBAAuB;YAmCvB,eAAe;YAaf,eAAe;IAgBvB,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YA8BlB,cAAc;YAkCd,cAAc;YA6Dd,sBAAsB;IA4C9B,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;IAgD5C,OAAO,CAAC,IAAI,EAAE,MAAM;IAmB1B,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;CAGxD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-web-device",
3
- "version": "1.2.0-alpha.2",
3
+ "version": "1.2.0-alpha.21",
4
4
  "author": "OneKey",
5
5
  "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
6
6
  "license": "MIT",
@@ -20,13 +20,13 @@
20
20
  "lint:fix": "eslint . --fix"
21
21
  },
22
22
  "dependencies": {
23
- "@onekeyfe/hd-shared": "1.2.0-alpha.2",
24
- "@onekeyfe/hd-transport": "1.2.0-alpha.2"
23
+ "@onekeyfe/hd-shared": "1.2.0-alpha.21",
24
+ "@onekeyfe/hd-transport": "1.2.0-alpha.21"
25
25
  },
26
26
  "devDependencies": {
27
- "@onekeyfe/hd-transport-electron": "1.2.0-alpha.2",
27
+ "@onekeyfe/hd-transport-electron": "1.2.0-alpha.21",
28
28
  "@types/w3c-web-usb": "^1.0.6",
29
29
  "@types/web-bluetooth": "^0.0.17"
30
30
  },
31
- "gitHead": "6c183b7f7027f4b4d3db4f427f6360351a6c6680"
31
+ "gitHead": "efe594367fb3b196a5126baee7e2aba3e94986cd"
32
32
  }
@@ -1,9 +1,8 @@
1
1
  import transport, {
2
- LogBlockCommand,
3
2
  PROTOCOL_V1_MESSAGE_HEADER_SIZE,
4
3
  PROTOCOL_V2_CHANNEL_BLE_UART,
5
4
  ProtocolV2FrameAssembler,
6
- ProtocolV2Session,
5
+ ProtocolV2LinkManager,
7
6
  bytesToHex,
8
7
  hexToBytes,
9
8
  probeProtocolV2 as probeProtocolV2Helper,
@@ -17,22 +16,13 @@ import {
17
16
  wait,
18
17
  } from '@onekeyfe/hd-shared';
19
18
 
19
+ import { createTransportCallLog, shouldSuppressHighVolumeCallLog } from './transportLog';
20
+
20
21
  import type { Deferred } from '@onekeyfe/hd-shared';
21
22
  import type { DesktopAPI } from '@onekeyfe/hd-transport-electron';
22
23
  import type { OneKeyDeviceInfo, ProtocolType, TransportCallOptions } from '@onekeyfe/hd-transport';
23
24
  import type EventEmitter from 'events';
24
25
 
25
- const FILE_WRITE_LOG_BLOCK_PATTERN = /(?:^|[^a-z])(?:raw)?(?:filesystem|emmc)?filewrite$/i;
26
-
27
- function shouldSuppressHighVolumeCallLog(name: string) {
28
- const normalized = name.replace(/[_\s-]/g, '');
29
- return FILE_WRITE_LOG_BLOCK_PATTERN.test(normalized);
30
- }
31
-
32
- function isLogBlockCommand(name: string) {
33
- return (LogBlockCommand as Set<string> | undefined)?.has?.(name) ?? false;
34
- }
35
-
36
26
  const { parseConfigure, ProtocolV1, check } = transport;
37
27
 
38
28
  declare global {
@@ -72,8 +62,6 @@ const toBleDescriptor = (
72
62
 
73
63
  const BLE_PACKET_SIZE = 192;
74
64
  const BLE_WRITE_DELAY_MS = 5;
75
- const BLE_WRITE_MAX_RETRIES = 3;
76
- const BLE_WRITE_RETRY_DELAY_MS = 300;
77
65
  const BLE_RESPONSE_TIMEOUT_MS = 30_000;
78
66
  const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
79
67
  const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
@@ -113,9 +101,26 @@ export default class ElectronBleTransport {
113
101
 
114
102
  private v2FramePromises: Map<string, Deferred<Uint8Array>> = new Map();
115
103
 
116
- private activeProtocolV2Call: { uuid: string; token: number } | null = null;
117
-
118
- private nextProtocolV2CallToken = 1;
104
+ private protocolV2Links = new ProtocolV2LinkManager<string>({
105
+ getSchemas: () => {
106
+ if (!this._messages || !this._messagesV2) {
107
+ throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
108
+ }
109
+ return {
110
+ protocolV1: this._messages,
111
+ protocolV2: this._messagesV2,
112
+ };
113
+ },
114
+ classifyError: () => 'link-fatal',
115
+ onLinkInvalidated: async (uuid, reason) => {
116
+ this.v2Assemblers.get(uuid)?.reset();
117
+ this.rejectProtocolV2Frames(uuid, new Error(reason));
118
+ this.Log?.debug('[Electron BLE] Protocol V2 link invalidated:', uuid, reason);
119
+ if (reason.startsWith('Protocol V2 link-fatal error:')) {
120
+ await this.release(uuid);
121
+ }
122
+ },
123
+ });
119
124
 
120
125
  private notificationCleanups: Map<string, () => void> = new Map();
121
126
 
@@ -157,6 +162,9 @@ export default class ElectronBleTransport {
157
162
  }
158
163
 
159
164
  private cleanupDeviceState(deviceId: string): void {
165
+ this.protocolV2Links
166
+ .invalidateLink(deviceId, 'Electron BLE device state cleaned')
167
+ .catch(error => this.Log?.debug('[Electron BLE] link cleanup failed:', error));
160
168
  this.connectedDevices.delete(deviceId);
161
169
  this.deviceProtocol.delete(deviceId);
162
170
  // Keep deviceProtocolHints — it's inferred from device name (e.g. "Pro 2" → V2)
@@ -164,9 +172,6 @@ export default class ElectronBleTransport {
164
172
  this.v1Buffers.delete(deviceId);
165
173
  this.v2Assemblers.delete(deviceId);
166
174
  this.resetProtocolV2Frames(deviceId);
167
- if (this.activeProtocolV2Call?.uuid === deviceId) {
168
- this.activeProtocolV2Call = null;
169
- }
170
175
  this.notificationTokens.delete(deviceId);
171
176
 
172
177
  const notifyCleanup = this.notificationCleanups.get(deviceId);
@@ -203,7 +208,9 @@ export default class ElectronBleTransport {
203
208
 
204
209
  configureProtocolV2(signedData: any) {
205
210
  this._messagesV2 = parseConfigure(signedData);
206
- this.Log?.debug('[Electron BLE] Protocol V2 schema configured');
211
+ this.protocolV2Links
212
+ .invalidateAllLinks('Protocol V2 schema reconfigured')
213
+ .catch(error => this.Log?.debug('[Electron BLE] schema link cleanup failed:', error));
207
214
  }
208
215
 
209
216
  async listen() {
@@ -238,12 +245,15 @@ export default class ElectronBleTransport {
238
245
  throw ERRORS.TypedError(HardwareErrorCode.BleRequiredUUID);
239
246
  }
240
247
 
248
+ if (this.connectedDevices.has(uuid)) {
249
+ await this.release(uuid);
250
+ }
251
+
241
252
  if (forceCleanRunPromise && this.runPromise) {
242
253
  const error = ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise);
243
254
  this.runPromise.reject(error);
244
255
  this.rejectAllProtocolV2Frames(error);
245
256
  this.runPromise = null;
246
- this.activeProtocolV2Call = null;
247
257
  }
248
258
 
249
259
  try {
@@ -320,6 +330,7 @@ export default class ElectronBleTransport {
320
330
 
321
331
  async release(id: string) {
322
332
  try {
333
+ await this.protocolV2Links.invalidateLink(id, 'Electron BLE transport released');
323
334
  if (this.connectedDevices.has(id)) {
324
335
  if (window.desktopApi?.nobleBle) {
325
336
  await window.desktopApi.nobleBle.unsubscribe(id);
@@ -368,23 +379,22 @@ export default class ElectronBleTransport {
368
379
  }
369
380
 
370
381
  if (expectedProtocol === 'V2') {
371
- // 免探测路径:调用方显式承诺该设备是 V2(例如固件升级重启后的重连场景,
372
- // 上层已经探测过协议并通过 expectedProtocol 传回),这里不再重复探测。
382
+ // Skip probing when the caller explicitly confirms V2, such as reconnect after a
383
+ // firmware reboot where expectedProtocol carries the previously probed result.
373
384
  this.deviceProtocol.set(uuid, 'V2');
374
385
  this.Log?.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> V2 (expected)`);
375
386
  return 'V2';
376
387
  }
377
388
 
378
- // 项目约束:协议判断必须在连接后主动探测,不能依赖设备名/PID/descriptor。
379
- // 设备名 hint(如 "Pro 2")只用于调整探测顺序:hint=V2 时先探 V2、失败回落 V1
380
- // 不能作为最终结论。
389
+ // Protocol must be actively probed after connection. Name, PID, and descriptors only
390
+ // influence probe order; a V2 hint probes V2 first and falls back to V1.
381
391
  const probeOrder: ProtocolType[] =
382
392
  protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
383
393
 
384
394
  for (let i = 0; i < probeOrder.length; i += 1) {
385
395
  const protocol = probeOrder[i];
386
396
  if (i > 0) {
387
- // 上一个协议探测失败后,重置订阅与缓冲,避免残留数据干扰下一个协议的探测。
397
+ // Reset subscriptions and buffers after a failed probe before trying another protocol.
388
398
  await this.resetProbeStateAfterProtocolProbe(uuid, probeOrder[i - 1]);
389
399
  }
390
400
  const detected =
@@ -417,12 +427,13 @@ export default class ElectronBleTransport {
417
427
  }
418
428
 
419
429
  private async resetProbeStateAfterProtocolProbe(uuid: string, protocol: ProtocolType) {
430
+ await this.protocolV2Links.invalidateLink(
431
+ uuid,
432
+ `Reset notify state after Protocol ${protocol} probe`
433
+ );
420
434
  this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
421
435
  this.v2Assemblers.get(uuid)?.reset();
422
436
  this.resetProtocolV2Frames(uuid);
423
- if (this.activeProtocolV2Call?.uuid === uuid) {
424
- this.activeProtocolV2Call = null;
425
- }
426
437
  if (this.runPromise) {
427
438
  const error = ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise);
428
439
  this.runPromise.reject(error);
@@ -496,7 +507,7 @@ export default class ElectronBleTransport {
496
507
 
497
508
  if (totalBytes <= BLE_PACKET_SIZE) {
498
509
  await wait(BLE_WRITE_DELAY_MS);
499
- await this.writeWithRetry(uuid, hexData);
510
+ await this.writeOnce(uuid, hexData);
500
511
  return;
501
512
  }
502
513
 
@@ -505,7 +516,7 @@ export default class ElectronBleTransport {
505
516
  const chunkHex = hexData.substring(offset, offset + chunkHexLen);
506
517
  offset += chunkHexLen;
507
518
 
508
- await this.writeWithRetry(uuid, chunkHex);
519
+ await this.writeOnce(uuid, chunkHex);
509
520
 
510
521
  if (offset < hexData.length) {
511
522
  await wait(BLE_WRITE_DELAY_MS);
@@ -513,41 +524,23 @@ export default class ElectronBleTransport {
513
524
  }
514
525
  }
515
526
 
516
- private async writeWithRetry(uuid: string, hexData: string): Promise<void> {
517
- let lastError: any;
527
+ private async writeOnce(uuid: string, hexData: string): Promise<void> {
518
528
  const nobleBle = window.desktopApi?.nobleBle;
519
529
  if (!nobleBle) {
520
530
  throw new Error('Noble BLE API not available');
521
531
  }
522
532
 
523
- for (let attempt = 1; attempt <= BLE_WRITE_MAX_RETRIES; attempt++) {
524
- try {
525
- await nobleBle.write(uuid, hexData);
526
- return;
527
- } catch (error) {
528
- lastError = error;
529
- this.Log?.error(
530
- `[Electron BLE] write failed (attempt ${attempt}/${BLE_WRITE_MAX_RETRIES}):`,
531
- error
532
- );
533
- if (attempt < BLE_WRITE_MAX_RETRIES) {
534
- await wait(BLE_WRITE_RETRY_DELAY_MS);
535
- }
536
- }
537
- }
538
- throw ERRORS.TypedError(
539
- HardwareErrorCode.BleWriteCharacteristicError,
540
- `BLE write failed after ${BLE_WRITE_MAX_RETRIES} attempts: ${lastError?.message ?? lastError}`
541
- );
533
+ await nobleBle.write(uuid, hexData);
542
534
  }
543
535
 
544
536
  private handleNotification(deviceId: string, hexData: string): void {
545
537
  if (hexData === 'PAIRING_REJECTED') {
546
538
  this.Log?.debug('[Electron BLE] Pairing rejection detected for device:', deviceId);
547
- if (this.runPromise) {
548
- const error = ERRORS.TypedError(HardwareErrorCode.BleDeviceBondedCanceled);
539
+ const error = ERRORS.TypedError(HardwareErrorCode.BleDeviceBondedCanceled);
540
+ if (this.deviceProtocol.get(deviceId) === 'V2') {
541
+ this.rejectProtocolV2Frames(deviceId, error);
542
+ } else if (this.runPromise) {
549
543
  this.runPromise.reject(error);
550
- this.rejectAllProtocolV2Frames(error);
551
544
  }
552
545
  return;
553
546
  }
@@ -566,12 +559,6 @@ export default class ElectronBleTransport {
566
559
 
567
560
  private handleProtocolV2Notification(deviceId: string, hexData: string): void {
568
561
  try {
569
- if (!this.runPromise || this.activeProtocolV2Call?.uuid !== deviceId) {
570
- this.v2Assemblers.get(deviceId)?.reset();
571
- this.resetProtocolV2Frames(deviceId);
572
- return;
573
- }
574
-
575
562
  const bytes = hexToBytes(hexData);
576
563
  if (bytes.length === 0) return;
577
564
 
@@ -583,11 +570,8 @@ export default class ElectronBleTransport {
583
570
  }
584
571
  } catch (error) {
585
572
  this.Log?.error('[Electron BLE] Protocol V2 notification error:', error);
586
- if (this.runPromise) {
587
- const notifyError = ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError);
588
- this.runPromise.reject(notifyError);
589
- this.rejectAllProtocolV2Frames(notifyError);
590
- }
573
+ const notifyError = ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError);
574
+ this.rejectProtocolV2Frames(deviceId, notifyError);
591
575
  }
592
576
  }
593
577
 
@@ -623,8 +607,13 @@ export default class ElectronBleTransport {
623
607
  this.v2FramePromises.delete(uuid);
624
608
  }
625
609
 
626
- private isActiveProtocolV2Call(uuid: string, token: number) {
627
- return this.activeProtocolV2Call?.uuid === uuid && this.activeProtocolV2Call.token === token;
610
+ private rejectProtocolV2Frames(uuid: string, error: Error) {
611
+ this.v2FrameQueues.delete(uuid);
612
+ const framePromise = this.v2FramePromises.get(uuid);
613
+ if (framePromise) {
614
+ this.v2FramePromises.delete(uuid);
615
+ framePromise.reject(error);
616
+ }
628
617
  }
629
618
 
630
619
  private async readProtocolV2Frame(uuid: string) {
@@ -681,12 +670,8 @@ export default class ElectronBleTransport {
681
670
  `Device protocol has not been detected for ${uuid}`
682
671
  );
683
672
  }
684
- if (shouldSuppressHighVolumeCallLog(name)) {
685
- // 高频文件写入不要逐包发 debug 事件,否则调试日志会反向拖慢传输。
686
- } else if (isLogBlockCommand(name)) {
687
- this.Log?.debug('[Electron BLE] call', 'name:', name, 'protocol:', protocol);
688
- } else {
689
- this.Log?.debug('[Electron BLE] call', 'name:', name, 'data:', data, 'protocol:', protocol);
673
+ if (!shouldSuppressHighVolumeCallLog(name)) {
674
+ this.Log?.debug('transport call', createTransportCallLog(name, protocol));
690
675
  }
691
676
 
692
677
  if (protocol === 'V2') {
@@ -776,79 +761,67 @@ export default class ElectronBleTransport {
776
761
  throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
777
762
  }
778
763
 
779
- const forceRun = name === 'Initialize' || name === 'Cancel' || name === 'Ping';
780
- if (this.runPromise) {
781
- if (!forceRun) {
782
- throw ERRORS.TypedError(HardwareErrorCode.TransportCallInProgress);
783
- }
784
- const error = ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise);
785
- this.runPromise.reject(error);
786
- this.rejectAllProtocolV2Frames(error);
787
- this.runPromise = null;
788
- this.activeProtocolV2Call = null;
789
- }
790
-
791
- const runPromise = createDeferred<Uint8Array | string>();
792
- runPromise.promise.catch(() => undefined);
793
- this.runPromise = runPromise;
794
- const callToken = this.nextProtocolV2CallToken++;
795
- this.activeProtocolV2Call = { uuid, token: callToken };
796
- this.v2Assemblers.get(uuid)?.reset();
797
- this.resetProtocolV2Frames(uuid);
798
- let completed = false;
799
764
  const callOptions = {
800
765
  ...options,
801
766
  timeoutMs: options?.timeoutMs ?? BLE_RESPONSE_TIMEOUT_MS,
802
767
  };
803
768
 
804
769
  try {
805
- const session = new ProtocolV2Session({
806
- schemas: {
807
- protocolV1: this._messages,
808
- protocolV2: this._messagesV2,
809
- },
810
- router: PROTOCOL_V2_CHANNEL_BLE_UART,
811
- writeFrame: (frame: Uint8Array) => this.writeWithChunking(uuid, bytesToHex(frame)),
812
- readFrame: async () => {
813
- const rxFrame = await this.readProtocolV2Frame(uuid);
814
- if (!(rxFrame instanceof Uint8Array)) {
815
- throw new Error('Response is not Uint8Array');
816
- }
817
- return rxFrame;
818
- },
819
- logger: this.Log,
820
- logPrefix: 'ProtocolV2 BLE',
821
- createTimeoutError: (_messageName: string, timeout: number) =>
822
- ERRORS.TypedError(
823
- HardwareErrorCode.BleTimeoutError,
824
- `BLE response timeout after ${timeout}ms for ${name}`
825
- ),
826
- });
827
-
828
- const result = await session.call(name, data, callOptions);
829
- completed = true;
830
- return result;
770
+ return await this.protocolV2Links.call(
771
+ uuid,
772
+ () => this.createProtocolV2Adapter(uuid),
773
+ name,
774
+ data,
775
+ callOptions
776
+ );
831
777
  } catch (e) {
832
- if (this.isActiveProtocolV2Call(uuid, callToken)) {
833
- this.v2Assemblers.get(uuid)?.reset();
834
- this.resetProtocolV2Frames(uuid);
835
- }
836
778
  this.Log?.error('[Electron BLE] Protocol V2 call error:', e);
837
779
  throw e;
838
- } finally {
839
- if (this.isActiveProtocolV2Call(uuid, callToken)) {
840
- if (!completed) {
841
- this.v2Assemblers.get(uuid)?.reset();
842
- }
843
- this.resetProtocolV2Frames(uuid);
844
- this.activeProtocolV2Call = null;
845
- }
846
- if (this.runPromise === runPromise) {
847
- this.runPromise = null;
848
- }
849
780
  }
850
781
  }
851
782
 
783
+ private createProtocolV2Adapter(uuid: string) {
784
+ const generation = this.notificationTokens.get(uuid) ?? 0;
785
+ const assertCurrentGeneration = () => {
786
+ if (this.notificationTokens.get(uuid) !== generation) {
787
+ throw new Error(`Protocol V2 notification generation changed for ${uuid}`);
788
+ }
789
+ };
790
+
791
+ return {
792
+ router: PROTOCOL_V2_CHANNEL_BLE_UART,
793
+ generation,
794
+ prepareCall: () => {
795
+ assertCurrentGeneration();
796
+ this.v2Assemblers.get(uuid)?.reset();
797
+ this.resetProtocolV2Frames(uuid);
798
+ },
799
+ writeFrame: (frame: Uint8Array) => {
800
+ assertCurrentGeneration();
801
+ return this.writeWithChunking(uuid, bytesToHex(frame));
802
+ },
803
+ readFrame: async () => {
804
+ assertCurrentGeneration();
805
+ const rxFrame = await this.readProtocolV2Frame(uuid);
806
+ if (!(rxFrame instanceof Uint8Array)) {
807
+ throw new Error('Response is not Uint8Array');
808
+ }
809
+ return rxFrame;
810
+ },
811
+ reset: (reason: string) => {
812
+ this.v2Assemblers.get(uuid)?.reset();
813
+ this.rejectProtocolV2Frames(uuid, new Error(reason));
814
+ },
815
+ logger: this.Log,
816
+ logPrefix: 'ProtocolV2 BLE',
817
+ createTimeoutError: (messageName: string, timeout: number) =>
818
+ ERRORS.TypedError(
819
+ HardwareErrorCode.BleTimeoutError,
820
+ `BLE response timeout after ${timeout}ms for ${messageName}`
821
+ ),
822
+ };
823
+ }
824
+
852
825
  private processProtocolV1Notification(deviceId: string, hexData: string): PacketProcessResult {
853
826
  try {
854
827
  if (typeof hexData !== 'string') {
@@ -0,0 +1,11 @@
1
+ import type { ProtocolType } from '@onekeyfe/hd-transport';
2
+
3
+ const HIGH_VOLUME_CALLS = new Set(['FileWrite', 'FilesystemFileWrite', 'EmmcFileWrite']);
4
+
5
+ export function shouldSuppressHighVolumeCallLog(name: string) {
6
+ return HIGH_VOLUME_CALLS.has(name);
7
+ }
8
+
9
+ export function createTransportCallLog(name: string, protocol: ProtocolType) {
10
+ return { name, protocol };
11
+ }