@onekeyfe/hd-transport-web-device 1.2.8-alpha.52 → 1.2.8-alpha.53

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/index.js CHANGED
@@ -51,11 +51,15 @@ const REPORT_ID = transport.PROTOCOL_V1_REPORT_ID;
51
51
  const HEADER_LENGTH = transport.PROTOCOL_V1_MESSAGE_HEADER_SIZE;
52
52
  const PACKET_IO_MAX_RETRIES = 3;
53
53
  const PACKET_IO_RETRY_DELAY = 300;
54
+ const USB_DEVICE_IN_USE = 'USB device is already in use';
54
55
  const PROTOCOL_V1_PROBE_TIMEOUT = 5000;
55
56
  const PROTOCOL_V2_PROBE_TIMEOUT = 1000;
56
57
  const EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS = 2;
57
58
  let activeWebUsbTransport;
58
59
  let usbDisconnectListenerAttached = false;
60
+ const identityProbeHandles = new WeakSet();
61
+ const openingHandles = new WeakSet();
62
+ const normalHandleOwners = new WeakMap();
59
63
  function registerActiveWebUsbTransport(instance, usb) {
60
64
  activeWebUsbTransport = instance;
61
65
  if (usbDisconnectListenerAttached)
@@ -72,17 +76,22 @@ function registerActiveWebUsbTransport(instance, usb) {
72
76
  });
73
77
  }
74
78
  class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
75
- constructor() {
79
+ constructor(identityProbe = false) {
76
80
  super({
77
81
  router: transport.PROTOCOL_V2_CHANNEL_USB,
78
82
  maxFrameBytes: transport.PROTOCOL_V2_FRAME_MAX_BYTES,
79
83
  logPrefix: 'ProtocolV2 WebUSB',
80
84
  });
85
+ this.identityProbe = identityProbe;
81
86
  this.deviceProtocol = new Map();
82
87
  this.confirmedDeviceProtocols = new Map();
83
88
  this.deviceProtocolHints = new Map();
84
89
  this.staleProtocolPaths = new Set();
85
90
  this.acquiredPaths = new Set();
91
+ this.identityProbeDevices = new Map();
92
+ this.normalDevices = new Map();
93
+ this.pendingHandleOperations = new Set();
94
+ this.identityProbeCleanupPaths = new Set();
86
95
  this.probedDeviceObjects = new Map();
87
96
  this.deviceEndpoints = new Map();
88
97
  this.name = 'WebUsbTransport';
@@ -101,7 +110,104 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
101
110
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'WebUSB is not supported by current browsers');
102
111
  }
103
112
  this.usb = usb;
104
- registerActiveWebUsbTransport(this, usb);
113
+ if (!this.identityProbe)
114
+ registerActiveWebUsbTransport(this, usb);
115
+ }
116
+ stop() {
117
+ this.stopped = true;
118
+ this.acquiredPaths.clear();
119
+ if (activeWebUsbTransport === this)
120
+ activeWebUsbTransport = undefined;
121
+ if (!this.stopPromise) {
122
+ this.stopPromise = this.stopResources().catch(error => {
123
+ this.stopPromise = undefined;
124
+ throw error;
125
+ });
126
+ }
127
+ return this.stopPromise;
128
+ }
129
+ stopResources() {
130
+ var _a, _b, _c;
131
+ return __awaiter(this, void 0, void 0, function* () {
132
+ let cleanupError;
133
+ try {
134
+ yield this.disposeProtocolV2UsbLinks('WebUSB transport stopped');
135
+ }
136
+ catch (error) {
137
+ cleanupError = error;
138
+ }
139
+ yield Promise.allSettled([...this.pendingHandleOperations]);
140
+ const handles = this.identityProbe
141
+ ? [...this.identityProbeDevices].map(([path, device]) => [device, path])
142
+ : [...this.normalDevices];
143
+ for (const [device, path] of handles) {
144
+ if (!this.identityProbe && normalHandleOwners.get(device) !== this) {
145
+ this.normalDevices.delete(device);
146
+ }
147
+ else {
148
+ try {
149
+ if (device.opened) {
150
+ const iface = (_b = (_a = this.deviceEndpoints.get(path)) === null || _a === void 0 ? void 0 : _a.interfaceNumber) !== null && _b !== void 0 ? _b : this.interfaceId;
151
+ try {
152
+ yield device.releaseInterface(iface);
153
+ }
154
+ catch (error) {
155
+ (_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug('[WebUsbTransport] stop releaseInterface failed:', error);
156
+ }
157
+ yield device.close();
158
+ if (device.opened)
159
+ throw new Error('WebUSB handle remains open after stop');
160
+ }
161
+ if (this.identityProbe) {
162
+ identityProbeHandles.delete(device);
163
+ this.identityProbeDevices.delete(path);
164
+ this.identityProbeCleanupPaths.delete(path);
165
+ }
166
+ else {
167
+ normalHandleOwners.delete(device);
168
+ this.normalDevices.delete(device);
169
+ }
170
+ }
171
+ catch (error) {
172
+ cleanupError !== null && cleanupError !== void 0 ? cleanupError : (cleanupError = error);
173
+ }
174
+ }
175
+ }
176
+ this.deviceList = [];
177
+ this.deviceProtocol.clear();
178
+ this.confirmedDeviceProtocols.clear();
179
+ this.deviceProtocolHints.clear();
180
+ this.staleProtocolPaths.clear();
181
+ this.probedDeviceObjects.clear();
182
+ if (!this.normalDevices.size && !this.identityProbeDevices.size) {
183
+ this.deviceEndpoints.clear();
184
+ }
185
+ if (cleanupError) {
186
+ throw cleanupError instanceof Error
187
+ ? cleanupError
188
+ : new Error(this.getErrorMessage(cleanupError));
189
+ }
190
+ });
191
+ }
192
+ assertRunning() {
193
+ if (this.stopped) {
194
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'WebUSB transport is stopped');
195
+ }
196
+ }
197
+ runHandleOperation(operation) {
198
+ return __awaiter(this, void 0, void 0, function* () {
199
+ this.assertRunning();
200
+ const pending = Promise.resolve(operation());
201
+ this.pendingHandleOperations.add(pending);
202
+ try {
203
+ const result = yield pending;
204
+ this.assertRunning();
205
+ return result;
206
+ }
207
+ finally {
208
+ this.pendingHandleOperations.delete(pending);
209
+ }
210
+ });
105
211
  }
106
212
  emitDeviceDisconnect(path, device) {
107
213
  var _a, _b;
@@ -150,15 +256,36 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
150
256
  }
151
257
  enumerate() {
152
258
  return __awaiter(this, void 0, void 0, function* () {
259
+ for (const path of this.identityProbeCleanupPaths) {
260
+ try {
261
+ yield this.release(path);
262
+ }
263
+ catch (_a) {
264
+ }
265
+ }
153
266
  yield this.getConnectedDevices();
154
267
  return this.deviceList;
155
268
  });
156
269
  }
157
270
  getConnectedDevices() {
271
+ var _a;
158
272
  return __awaiter(this, void 0, void 0, function* () {
273
+ this.assertRunning();
159
274
  if (!this.usb)
160
275
  return [];
161
276
  const devices = yield this.usb.getDevices();
277
+ this.assertRunning();
278
+ for (const owner of new Set(devices.map(device => normalHandleOwners.get(device)))) {
279
+ if (owner === null || owner === void 0 ? void 0 : owner.stopped) {
280
+ try {
281
+ yield owner.stop();
282
+ }
283
+ catch (error) {
284
+ (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[WebUsbTransport] stopped owner cleanup failed:', error);
285
+ }
286
+ }
287
+ }
288
+ this.assertRunning();
162
289
  const onekeyDevices = devices.filter(dev => {
163
290
  const isOneKey = hdShared.ONEKEY_WEBUSB_FILTER.some((desc) => dev.vendorId === desc.vendorId && dev.productId === desc.productId);
164
291
  return isOneKey && !hdShared.isKnownTrezorWebUsbDevice(dev);
@@ -185,8 +312,16 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
185
312
  acquire(input) {
186
313
  var _a, _b, _c, _d, _e, _f, _g;
187
314
  return __awaiter(this, void 0, void 0, function* () {
315
+ this.assertRunning();
188
316
  if (!input.path)
189
317
  return;
318
+ if (this.identityProbe) {
319
+ yield this.getConnectedDevices();
320
+ this.claimIdentityProbeHandle(input.path, yield this.findDevice(input.path));
321
+ }
322
+ else {
323
+ this.claimNormalHandle(input.path, yield this.findDevice(input.path));
324
+ }
190
325
  try {
191
326
  yield this.rotateProtocolV2UsbGeneration(input.path, 'WebUSB transport acquired');
192
327
  yield this.closeOpenDevice(input.path);
@@ -236,31 +371,44 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
236
371
  this.deviceProtocolHints.set(input.path, protocolHint);
237
372
  }
238
373
  const detectedProtocol = yield this.detectProtocol(input.path, input.expectedProtocol, protocolHint);
374
+ this.assertRunning();
239
375
  this.confirmedDeviceProtocols.set(input.path, detectedProtocol);
240
376
  const probedDevice = (_g = this.deviceList.find(d => d.path === input.path)) === null || _g === void 0 ? void 0 : _g.device;
241
377
  if (probedDevice) {
242
378
  this.probedDeviceObjects.set(input.path, probedDevice);
243
379
  }
244
380
  }
381
+ this.assertRunning();
245
382
  this.acquiredPaths.add(input.path);
246
383
  return yield Promise.resolve(input.path);
247
384
  }
248
385
  catch (e) {
249
386
  this.Log.debug('acquire error: ', e instanceof Error ? `${e.name}: ${e.message}` : String(e));
250
387
  this.acquiredPaths.delete(input.path);
251
- yield this.closeOpenDevice(input.path);
388
+ try {
389
+ yield this.closeOpenDevice(input.path);
390
+ }
391
+ finally {
392
+ this.releaseIdentityProbeHandle(input.path);
393
+ this.releaseNormalHandles(input.path);
394
+ }
252
395
  throw e;
253
396
  }
254
397
  });
255
398
  }
256
399
  createProtocolMismatchError(expected) {
257
- return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
400
+ return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportProtocolProbeFailed, undefined, {
401
+ expectedProtocol: expected,
402
+ });
258
403
  }
259
404
  createProtocolProbeTimeoutError(expected, attempts) {
260
- return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceInitializeFailed, `Protocol ${expected} probe timeout after ${attempts} attempts`);
405
+ return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportProtocolProbeFailed, undefined, {
406
+ expectedProtocol: expected,
407
+ attempts,
408
+ });
261
409
  }
262
410
  createProtocolDetectionError() {
263
- return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound);
411
+ return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportProtocolProbeFailed);
264
412
  }
265
413
  detectProtocol(path, expectedProtocol, protocolHint) {
266
414
  var _a;
@@ -328,10 +476,12 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
328
476
  return __awaiter(this, void 0, void 0, function* () {
329
477
  const maxRetries = 5;
330
478
  for (let i = 0; i < maxRetries; i++) {
479
+ this.assertRunning();
331
480
  try {
332
481
  return yield this.connectToDevice(path, first);
333
482
  }
334
483
  catch (e) {
484
+ this.assertRunning();
335
485
  if (e instanceof hdShared.HardwareError && e.errorCode === hdShared.HardwareErrorCode.BridgeNeedsPermission) {
336
486
  throw e;
337
487
  }
@@ -374,42 +524,68 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
374
524
  connectToDevice(path, first) {
375
525
  var _a;
376
526
  return __awaiter(this, void 0, void 0, function* () {
527
+ this.assertRunning();
377
528
  let device = yield this.findDevice(path);
529
+ if (this.identityProbe && this.identityProbeDevices.get(path) !== device) {
530
+ yield this.closeOpenDevice(path);
531
+ this.releaseIdentityProbeHandle(path);
532
+ this.claimIdentityProbeHandle(path, device);
533
+ }
534
+ if (!this.identityProbe)
535
+ this.claimNormalHandle(path, device);
536
+ this.assertHandleAvailable(device);
378
537
  if (!device.opened) {
538
+ openingHandles.add(device);
379
539
  try {
380
- yield device.open();
540
+ yield this.runHandleOperation(() => device.open());
381
541
  }
382
542
  catch (error) {
383
- return this.throwWebUsbIoError(path, 'open', error);
543
+ return yield this.throwWebUsbIoError(path, 'open', error);
544
+ }
545
+ finally {
546
+ openingHandles.delete(device);
384
547
  }
385
548
  }
386
549
  if (first || this.deviceProtocol.get(path) !== 'V1') {
387
550
  try {
388
- yield device.reset();
551
+ yield this.runHandleOperation(() => device.reset());
389
552
  }
390
553
  catch (error) {
554
+ this.assertRunning();
391
555
  (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[WebUsbTransport] reset before claim failed, continuing:', error);
392
556
  }
393
557
  }
394
558
  yield this.getConnectedDevices();
395
559
  device = yield this.findDevice(path);
560
+ if (this.identityProbe && this.identityProbeDevices.get(path) !== device) {
561
+ yield this.closeOpenDevice(path);
562
+ this.releaseIdentityProbeHandle(path);
563
+ this.claimIdentityProbeHandle(path, device);
564
+ }
565
+ if (!this.identityProbe)
566
+ this.claimNormalHandle(path, device);
567
+ this.assertHandleAvailable(device);
396
568
  if (!device.opened) {
569
+ openingHandles.add(device);
397
570
  try {
398
- yield device.open();
571
+ yield this.runHandleOperation(() => device.open());
399
572
  }
400
573
  catch (error) {
401
- return this.throwWebUsbIoError(path, 'open', error);
574
+ return yield this.throwWebUsbIoError(path, 'open', error);
575
+ }
576
+ finally {
577
+ openingHandles.delete(device);
402
578
  }
403
579
  }
404
580
  if (first ||
405
581
  !device.configuration ||
406
582
  device.configuration.configurationValue !== this.configurationId) {
407
- yield device.selectConfiguration(this.configurationId);
583
+ yield this.runHandleOperation(() => device.selectConfiguration(this.configurationId));
408
584
  }
409
585
  const endpoints = this.discoverEndpoints(device);
410
586
  this.deviceEndpoints.set(path, endpoints);
411
587
  try {
412
- yield device.claimInterface(endpoints.interfaceNumber);
588
+ yield this.runHandleOperation(() => device.claimInterface(endpoints.interfaceNumber));
413
589
  }
414
590
  catch (error) {
415
591
  return this.throwWebUsbIoError(path, 'claimInterface', error);
@@ -418,40 +594,104 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
418
594
  yield this.clearEndpointHalt(device, 'out', endpoints.endpointOut);
419
595
  });
420
596
  }
597
+ assertHandleAvailable(device) {
598
+ this.assertRunning();
599
+ const normalOwner = normalHandleOwners.get(device);
600
+ if ((normalOwner && normalOwner !== this) ||
601
+ (identityProbeHandles.has(device) &&
602
+ ![...this.identityProbeDevices.values()].includes(device))) {
603
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, USB_DEVICE_IN_USE);
604
+ }
605
+ }
606
+ claimNormalHandle(path, device) {
607
+ this.assertHandleAvailable(device);
608
+ normalHandleOwners.set(device, this);
609
+ this.normalDevices.set(device, path);
610
+ }
611
+ releaseNormalHandles(path) {
612
+ if (this.stopped)
613
+ return;
614
+ for (const [device, ownedPath] of this.normalDevices) {
615
+ if (ownedPath === path) {
616
+ normalHandleOwners.delete(device);
617
+ this.normalDevices.delete(device);
618
+ }
619
+ }
620
+ }
621
+ isHandleOwnershipError(error) {
622
+ return (error instanceof hdShared.HardwareError &&
623
+ error.errorCode === hdShared.HardwareErrorCode.RuntimeError &&
624
+ this.getErrorMessage(error).includes(USB_DEVICE_IN_USE));
625
+ }
626
+ claimIdentityProbeHandle(path, device) {
627
+ this.assertRunning();
628
+ if (device.opened ||
629
+ openingHandles.has(device) ||
630
+ identityProbeHandles.has(device) ||
631
+ normalHandleOwners.has(device)) {
632
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, USB_DEVICE_IN_USE);
633
+ }
634
+ identityProbeHandles.add(device);
635
+ this.identityProbeDevices.set(path, device);
636
+ }
637
+ releaseIdentityProbeHandle(path) {
638
+ if (this.stopped)
639
+ return;
640
+ const device = this.identityProbeDevices.get(path);
641
+ if (device === null || device === void 0 ? void 0 : device.opened) {
642
+ this.identityProbeCleanupPaths.add(path);
643
+ return;
644
+ }
645
+ if (device)
646
+ identityProbeHandles.delete(device);
647
+ this.identityProbeDevices.delete(path);
648
+ this.identityProbeCleanupPaths.delete(path);
649
+ }
421
650
  closeOpenDevice(path) {
422
651
  var _a, _b, _c, _d;
423
652
  return __awaiter(this, void 0, void 0, function* () {
424
- const current = (_a = this.deviceList.find(device => device.path === path)) === null || _a === void 0 ? void 0 : _a.device;
653
+ if (this.stopped)
654
+ return;
655
+ const current = this.identityProbe
656
+ ? this.identityProbeDevices.get(path)
657
+ : (_a = this.deviceList.find(device => device.path === path)) === null || _a === void 0 ? void 0 : _a.device;
425
658
  if (!(current === null || current === void 0 ? void 0 : current.opened))
426
659
  return;
660
+ this.assertHandleAvailable(current);
427
661
  const endpoints = this.deviceEndpoints.get(path);
428
662
  const ifaceNum = (_b = endpoints === null || endpoints === void 0 ? void 0 : endpoints.interfaceNumber) !== null && _b !== void 0 ? _b : this.interfaceId;
429
663
  try {
430
- yield current.releaseInterface(ifaceNum);
664
+ yield this.runHandleOperation(() => current.releaseInterface(ifaceNum));
431
665
  }
432
666
  catch (error) {
433
667
  (_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug('[WebUsbTransport] releaseInterface before reconnect failed:', error);
434
668
  }
669
+ this.assertHandleAvailable(current);
435
670
  try {
436
- yield current.close();
671
+ yield this.runHandleOperation(() => current.close());
437
672
  }
438
673
  catch (error) {
439
674
  (_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug('[WebUsbTransport] close before reconnect failed:', error);
675
+ if (this.identityProbe)
676
+ throw error;
440
677
  }
441
678
  });
442
679
  }
443
680
  clearEndpointHalt(device, direction, endpointNumber) {
444
681
  var _a;
445
682
  return __awaiter(this, void 0, void 0, function* () {
683
+ this.assertHandleAvailable(device);
446
684
  try {
447
- yield device.clearHalt(direction, endpointNumber);
685
+ yield this.runHandleOperation(() => device.clearHalt(direction, endpointNumber));
448
686
  }
449
687
  catch (error) {
688
+ this.assertRunning();
450
689
  (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[WebUsbTransport] clearHalt ${direction} endpoint ${endpointNumber} failed, continuing:`, error);
451
690
  }
452
691
  });
453
692
  }
454
693
  assertAcquired(path) {
694
+ this.assertRunning();
455
695
  if (!this.acquiredPaths.has(path)) {
456
696
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device is not acquired for ${path}`);
457
697
  }
@@ -508,7 +748,8 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
508
748
  throwWebUsbIoError(path, operation, error) {
509
749
  var _a;
510
750
  return __awaiter(this, void 0, void 0, function* () {
511
- if (this.isWebUsbIoHardwareError(error))
751
+ this.assertRunning();
752
+ if (this.isHandleOwnershipError(error) || this.isWebUsbIoHardwareError(error))
512
753
  throw error;
513
754
  if (error instanceof transport.ProtocolV2LinkError && error.code !== 'io')
514
755
  throw error;
@@ -542,7 +783,9 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
542
783
  return /LIBUSB_ERROR_ACCESS|\b(?:access|permission) denied\b/i.test(message);
543
784
  }
544
785
  isRetryablePacketIoError(error) {
545
- if (this.isWebUsbIoHardwareError(error))
786
+ if (this.stopped)
787
+ return false;
788
+ if (this.isHandleOwnershipError(error) || this.isWebUsbIoHardwareError(error))
546
789
  return false;
547
790
  const message = this.getErrorMessage(error).toLowerCase();
548
791
  if (this.isWebUsbPermissionDeniedMessage(message))
@@ -561,21 +804,27 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
561
804
  this.Log.debug(`[WebUsbTransport] transfer${direction} failed, retry ${attempt}/${PACKET_IO_MAX_RETRIES}: ${this.getErrorMessage(error)}`);
562
805
  this.staleProtocolPaths.add(path);
563
806
  yield hdShared.wait(attempt * PACKET_IO_RETRY_DELAY);
807
+ this.assertRunning();
564
808
  try {
565
809
  const currentDevice = yield this.findDevice(path);
810
+ this.assertHandleAvailable(currentDevice);
566
811
  if (currentDevice.opened) {
567
812
  const endpoints = this.deviceEndpoints.get(path);
568
813
  const ifaceNum = (_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints.interfaceNumber) !== null && _a !== void 0 ? _a : this.interfaceId;
569
814
  try {
570
- yield currentDevice.releaseInterface(ifaceNum);
815
+ yield this.runHandleOperation(() => currentDevice.releaseInterface(ifaceNum));
571
816
  }
572
817
  catch (releaseError) {
573
818
  this.Log.debug('[WebUsbTransport] releaseInterface before retry error:', releaseError);
574
819
  }
575
- yield currentDevice.close();
820
+ this.assertHandleAvailable(currentDevice);
821
+ yield this.runHandleOperation(() => currentDevice.close());
576
822
  }
577
823
  }
578
824
  catch (closeError) {
825
+ this.assertRunning();
826
+ if (this.isHandleOwnershipError(closeError))
827
+ throw closeError;
579
828
  this.Log.debug('[WebUsbTransport] close device before retry error:', closeError);
580
829
  }
581
830
  yield this.getConnectedDevices();
@@ -617,6 +866,9 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
617
866
  yield this.reconnectForPacketIoRetry(path, 'out', attempt, error);
618
867
  }
619
868
  catch (reconnectError) {
869
+ this.assertRunning();
870
+ if (this.isHandleOwnershipError(reconnectError))
871
+ throw reconnectError;
620
872
  lastError = reconnectError;
621
873
  this.Log.debug(`[WebUsbTransport] transferout reconnect failed on retry ${attempt}/${PACKET_IO_MAX_RETRIES}: ${this.getErrorMessage(reconnectError)}`);
622
874
  }
@@ -635,7 +887,9 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
635
887
  const endpoints = this.deviceEndpoints.get(path);
636
888
  const endpointOut = (_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints.endpointOut) !== null && _a !== void 0 ? _a : this.endpointId;
637
889
  const transferBuffer = this.toArrayBuffer(packet.buffer.slice(packet.byteOffset, packet.byteOffset + packet.byteLength));
890
+ this.assertHandleAvailable(device);
638
891
  const result = yield device.transferOut(endpointOut, transferBuffer);
892
+ this.assertHandleAvailable(device);
639
893
  if (result.status !== 'ok') {
640
894
  return this.throwWebUsbIoError(path, 'transferOut', new Error(`transferOut status: ${String(result.status)}`));
641
895
  }
@@ -656,7 +910,9 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
656
910
  }
657
911
  const endpoints = this.deviceEndpoints.get(path);
658
912
  const endpointIn = (_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints.endpointIn) !== null && _a !== void 0 ? _a : this.endpointId;
913
+ this.assertHandleAvailable(device);
659
914
  const result = yield device.transferIn(endpointIn, length);
915
+ this.assertHandleAvailable(device);
660
916
  return this.getTransferInData(result);
661
917
  }
662
918
  catch (error) {
@@ -672,6 +928,9 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
672
928
  yield this.reconnectForPacketIoRetry(path, 'in', attempt, error);
673
929
  }
674
930
  catch (reconnectError) {
931
+ this.assertRunning();
932
+ if (this.isHandleOwnershipError(reconnectError))
933
+ throw reconnectError;
675
934
  lastError = reconnectError;
676
935
  this.Log.debug(`[WebUsbTransport] transferin reconnect failed on retry ${attempt}/${PACKET_IO_MAX_RETRIES}: ${this.getErrorMessage(reconnectError)}`);
677
936
  }
@@ -689,7 +948,9 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
689
948
  }
690
949
  const endpoints = this.deviceEndpoints.get(path);
691
950
  const endpointIn = (_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints.endpointIn) !== null && _a !== void 0 ? _a : this.endpointId;
951
+ this.assertHandleAvailable(device);
692
952
  const result = yield device.transferIn(endpointIn, length);
953
+ this.assertHandleAvailable(device);
693
954
  return this.getTransferInData(result);
694
955
  });
695
956
  }
@@ -749,10 +1010,10 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
749
1010
  return false;
750
1011
  }
751
1012
  try {
752
- yield this.callProtocolV1(path, 'Initialize', {}, {
1013
+ const response = yield this.callProtocolV1(path, this.identityProbe ? 'GetFeatures' : 'Initialize', {}, {
753
1014
  timeoutMs: PROTOCOL_V1_PROBE_TIMEOUT,
754
1015
  });
755
- return true;
1016
+ return !this.identityProbe || response.type === 'Features';
756
1017
  }
757
1018
  catch (error) {
758
1019
  if (this.isWebUsbIoHardwareError(error))
@@ -869,8 +1130,14 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
869
1130
  release(path) {
870
1131
  return __awaiter(this, void 0, void 0, function* () {
871
1132
  this.acquiredPaths.delete(path);
872
- yield this.invalidateProtocolV2UsbLink(path, 'WebUSB transport released');
873
- yield this.closeOpenDevice(path);
1133
+ try {
1134
+ yield this.invalidateProtocolV2UsbLink(path, 'WebUSB transport released');
1135
+ yield this.closeOpenDevice(path);
1136
+ }
1137
+ finally {
1138
+ this.releaseIdentityProbeHandle(path);
1139
+ this.releaseNormalHandles(path);
1140
+ }
874
1141
  this.deviceEndpoints.delete(path);
875
1142
  });
876
1143
  }
@@ -1277,10 +1544,12 @@ class ElectronBleTransport {
1277
1544
  });
1278
1545
  }
1279
1546
  createProtocolMismatchError(expected) {
1280
- return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
1547
+ return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportProtocolProbeFailed, undefined, {
1548
+ expectedProtocol: expected,
1549
+ });
1281
1550
  }
1282
1551
  createProtocolDetectionError() {
1283
- return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1 GetFeatures or Protocol V2 Ping');
1552
+ return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportProtocolProbeFailed);
1284
1553
  }
1285
1554
  clearProbeProtocol(uuid, protocol) {
1286
1555
  if (this.deviceProtocol.get(uuid) === protocol) {
package/dist/webusb.d.ts CHANGED
@@ -9,6 +9,7 @@ export interface DeviceInfo extends OneKeyDeviceInfoBase {
9
9
  protocolType?: ProtocolType;
10
10
  }
11
11
  export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string> {
12
+ private readonly identityProbe;
12
13
  messages: ReturnType<typeof transport.parseConfigure> | undefined;
13
14
  messagesV2: ReturnType<typeof transport.parseConfigure> | undefined;
14
15
  private protocolV2SchemaSource;
@@ -17,6 +18,11 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
17
18
  private deviceProtocolHints;
18
19
  private staleProtocolPaths;
19
20
  private acquiredPaths;
21
+ private identityProbeDevices;
22
+ private normalDevices;
23
+ private pendingHandleOperations;
24
+ private stopPromise?;
25
+ private identityProbeCleanupPaths;
20
26
  private probedDeviceObjects;
21
27
  private deviceEndpoints;
22
28
  name: string;
@@ -29,8 +35,12 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
29
35
  configurationId: number;
30
36
  endpointId: number;
31
37
  interfaceId: number;
32
- constructor();
38
+ constructor(identityProbe?: boolean);
33
39
  init(logger: any, emitter?: EventEmitter): void;
40
+ stop(): Promise<void>;
41
+ private stopResources;
42
+ private assertRunning;
43
+ private runHandleOperation;
34
44
  emitDeviceDisconnect(path: string, device?: USBDevice): void;
35
45
  markProtocolStale(path: string): void;
36
46
  configure(signedData: any): void;
@@ -47,6 +57,12 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
47
57
  connect(path: string, first: boolean): Promise<undefined>;
48
58
  private discoverEndpoints;
49
59
  connectToDevice(path: string, first: boolean): Promise<undefined>;
60
+ private assertHandleAvailable;
61
+ private claimNormalHandle;
62
+ private releaseNormalHandles;
63
+ private isHandleOwnershipError;
64
+ private claimIdentityProbeHandle;
65
+ private releaseIdentityProbeHandle;
50
66
  private closeOpenDevice;
51
67
  private clearEndpointHalt;
52
68
  private assertAcquired;
@@ -1 +1 @@
1
- {"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":"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"}