@onekeyfe/hd-transport-web-device 1.2.0-alpha.2 → 1.2.0-alpha.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/electron-ble-transport.test.ts +102 -8
- package/__tests__/webusb-protocol-v2-timeout.test.ts +88 -0
- package/dist/electron-ble-transport.d.ts +4 -4
- package/dist/electron-ble-transport.d.ts.map +1 -1
- package/dist/index.d.ts +97 -4
- package/dist/index.js +167 -177
- package/dist/transportLog.d.ts +7 -0
- package/dist/transportLog.d.ts.map +1 -0
- package/dist/webusb.d.ts +3 -0
- package/dist/webusb.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/electron-ble-transport.ts +111 -138
- package/src/transportLog.ts +11 -0
- package/src/webusb.ts +85 -78
package/dist/index.js
CHANGED
|
@@ -41,6 +41,14 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
41
41
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
+
const HIGH_VOLUME_CALLS = new Set(['FileWrite', 'FilesystemFileWrite', 'EmmcFileWrite']);
|
|
45
|
+
function shouldSuppressHighVolumeCallLog(name) {
|
|
46
|
+
return HIGH_VOLUME_CALLS.has(name);
|
|
47
|
+
}
|
|
48
|
+
function createTransportCallLog(name, protocol) {
|
|
49
|
+
return { name, protocol };
|
|
50
|
+
}
|
|
51
|
+
|
|
44
52
|
const { parseConfigure: parseConfigure$1, check: check$1, ProtocolV1: ProtocolV1$1 } = transport__default["default"];
|
|
45
53
|
const CONFIGURATION_ID = 1;
|
|
46
54
|
const INTERFACE_ID = 0;
|
|
@@ -52,19 +60,6 @@ const HEADER_LENGTH = transport.PROTOCOL_V1_MESSAGE_HEADER_SIZE;
|
|
|
52
60
|
const PACKET_IO_MAX_RETRIES = 3;
|
|
53
61
|
const PACKET_IO_RETRY_DELAY = 300;
|
|
54
62
|
const PROTOCOL_PROBE_TIMEOUT = 1000;
|
|
55
|
-
const WEBUSB_FILE_WRITE_LOG_BLOCK_PATTERN = /(?:^|[^a-z])(?:raw)?(?:filesystem|emmc)?filewrite$/i;
|
|
56
|
-
function shouldSuppressWebUsbCallLog(name) {
|
|
57
|
-
const normalized = name.replace(/[_\s-]/g, '');
|
|
58
|
-
return WEBUSB_FILE_WRITE_LOG_BLOCK_PATTERN.test(normalized);
|
|
59
|
-
}
|
|
60
|
-
function isLogBlockCommand$1(name) {
|
|
61
|
-
var _a, _b;
|
|
62
|
-
return (_b = (_a = transport.LogBlockCommand === null || transport.LogBlockCommand === void 0 ? void 0 : transport.LogBlockCommand.has) === null || _a === void 0 ? void 0 : _a.call(transport.LogBlockCommand, name)) !== null && _b !== void 0 ? _b : false;
|
|
63
|
-
}
|
|
64
|
-
function shouldBlockWebUsbCallDataLog(name) {
|
|
65
|
-
const normalized = name.replace(/[_\s-]/g, '');
|
|
66
|
-
return isLogBlockCommand$1(name) || WEBUSB_FILE_WRITE_LOG_BLOCK_PATTERN.test(normalized);
|
|
67
|
-
}
|
|
68
63
|
function inferProtocolHintFromDeviceName$1(name) {
|
|
69
64
|
return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
|
|
70
65
|
}
|
|
@@ -74,7 +69,9 @@ class WebUsbTransport {
|
|
|
74
69
|
this.deviceProtocolHints = new Map();
|
|
75
70
|
this.protocolV2Assemblers = new Map();
|
|
76
71
|
this.protocolV2Sessions = new Map();
|
|
72
|
+
this.protocolV2Sequences = new Map();
|
|
77
73
|
this.protocolV2ReadTimeouts = new Map();
|
|
74
|
+
this.protocolV2WriteTimeoutMs = transport.PROTOCOL_V2_WRITE_WATCHDOG_TIMEOUT_MS;
|
|
78
75
|
this.deviceEndpoints = new Map();
|
|
79
76
|
this.mockSerialPaths = new WeakMap();
|
|
80
77
|
this.mockSerialCounter = 0;
|
|
@@ -100,11 +97,9 @@ class WebUsbTransport {
|
|
|
100
97
|
this.messages = messages;
|
|
101
98
|
}
|
|
102
99
|
configureProtocolV2(signedData) {
|
|
103
|
-
var _a;
|
|
104
100
|
this.messagesV2 = parseConfigure$1(signedData);
|
|
105
101
|
this.protocolV2Sessions.clear();
|
|
106
102
|
this.protocolV2ReadTimeouts.clear();
|
|
107
|
-
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[WebUsbTransport] Protocol V2 schema configured');
|
|
108
103
|
}
|
|
109
104
|
promptDeviceAccess() {
|
|
110
105
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -145,7 +140,10 @@ class WebUsbTransport {
|
|
|
145
140
|
if (!this.usb)
|
|
146
141
|
return [];
|
|
147
142
|
const devices = yield this.usb.getDevices();
|
|
148
|
-
const onekeyDevices = devices.filter(dev =>
|
|
143
|
+
const onekeyDevices = devices.filter(dev => {
|
|
144
|
+
const isOneKey = hdShared.ONEKEY_WEBUSB_FILTER.some((desc) => dev.vendorId === desc.vendorId && dev.productId === desc.productId);
|
|
145
|
+
return isOneKey && !hdShared.isKnownTrezorWebUsbDevice(dev);
|
|
146
|
+
});
|
|
149
147
|
this.deviceList = onekeyDevices.map(device => {
|
|
150
148
|
const path = this.getDevicePath(device);
|
|
151
149
|
const protocolHint = inferProtocolHintFromDeviceName$1(device.productName);
|
|
@@ -158,10 +156,6 @@ class WebUsbTransport {
|
|
|
158
156
|
commType: 'webusb',
|
|
159
157
|
};
|
|
160
158
|
});
|
|
161
|
-
for (const dev of onekeyDevices) {
|
|
162
|
-
this.Log.debug(`[WebUSB] Device: name="${dev.productName}" serial="${dev.serialNumber}" ` +
|
|
163
|
-
`VID=0x${dev.vendorId.toString(16)} PID=0x${dev.productId.toString(16)}`);
|
|
164
|
-
}
|
|
165
159
|
return this.deviceList;
|
|
166
160
|
});
|
|
167
161
|
}
|
|
@@ -200,14 +194,13 @@ class WebUsbTransport {
|
|
|
200
194
|
if (expectedProtocol === 'V1') {
|
|
201
195
|
if (yield this.probeProtocolV1(path)) {
|
|
202
196
|
this.deviceProtocol.set(path, 'V1');
|
|
203
|
-
this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V1 (expected)`);
|
|
204
197
|
return 'V1';
|
|
205
198
|
}
|
|
199
|
+
yield this.resetConnectionAfterProbe(path);
|
|
206
200
|
throw this.createProtocolMismatchError(expectedProtocol);
|
|
207
201
|
}
|
|
208
202
|
if (expectedProtocol === 'V2') {
|
|
209
203
|
this.deviceProtocol.set(path, 'V2');
|
|
210
|
-
this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V2 (expected)`);
|
|
211
204
|
return 'V2';
|
|
212
205
|
}
|
|
213
206
|
const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(path) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
|
|
@@ -215,9 +208,11 @@ class WebUsbTransport {
|
|
|
215
208
|
const detected = protocol === 'V1' ? yield this.probeProtocolV1(path) : yield this.probeProtocolV2(path);
|
|
216
209
|
if (detected) {
|
|
217
210
|
this.deviceProtocol.set(path, protocol);
|
|
218
|
-
this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> ${protocol}`);
|
|
219
211
|
return protocol;
|
|
220
212
|
}
|
|
213
|
+
if (protocol === 'V1') {
|
|
214
|
+
yield this.resetConnectionAfterProbe(path);
|
|
215
|
+
}
|
|
221
216
|
}
|
|
222
217
|
this.deviceProtocol.delete(path);
|
|
223
218
|
throw this.createProtocolDetectionError();
|
|
@@ -287,7 +282,6 @@ class WebUsbTransport {
|
|
|
287
282
|
var _a, _b;
|
|
288
283
|
return __awaiter(this, void 0, void 0, function* () {
|
|
289
284
|
let device = yield this.findDevice(path);
|
|
290
|
-
this.Log.debug('[WebUsbTransport] connecting to device:', device.productName, 'PID:', device.productId);
|
|
291
285
|
if (!device.opened) {
|
|
292
286
|
yield device.open();
|
|
293
287
|
}
|
|
@@ -420,19 +414,11 @@ class WebUsbTransport {
|
|
|
420
414
|
return copied.buffer;
|
|
421
415
|
}
|
|
422
416
|
transferOutWithRetry(path, packet) {
|
|
423
|
-
var _a;
|
|
424
417
|
return __awaiter(this, void 0, void 0, function* () {
|
|
425
418
|
let lastError;
|
|
426
419
|
for (let attempt = 1; attempt <= PACKET_IO_MAX_RETRIES; attempt += 1) {
|
|
427
420
|
try {
|
|
428
|
-
|
|
429
|
-
if (!device.opened) {
|
|
430
|
-
yield this.connect(path, false);
|
|
431
|
-
}
|
|
432
|
-
const endpoints = this.deviceEndpoints.get(path);
|
|
433
|
-
const endpointOut = (_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints.endpointOut) !== null && _a !== void 0 ? _a : this.endpointId;
|
|
434
|
-
const transferBuffer = this.toArrayBuffer(packet.buffer.slice(packet.byteOffset, packet.byteOffset + packet.byteLength));
|
|
435
|
-
yield device.transferOut(endpointOut, transferBuffer);
|
|
421
|
+
yield this.transferOutOnce(path, packet);
|
|
436
422
|
return;
|
|
437
423
|
}
|
|
438
424
|
catch (error) {
|
|
@@ -453,6 +439,19 @@ class WebUsbTransport {
|
|
|
453
439
|
throw lastError;
|
|
454
440
|
});
|
|
455
441
|
}
|
|
442
|
+
transferOutOnce(path, packet) {
|
|
443
|
+
var _a;
|
|
444
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
445
|
+
const device = yield this.findDevice(path);
|
|
446
|
+
if (!device.opened) {
|
|
447
|
+
yield this.connect(path, false);
|
|
448
|
+
}
|
|
449
|
+
const endpoints = this.deviceEndpoints.get(path);
|
|
450
|
+
const endpointOut = (_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints.endpointOut) !== null && _a !== void 0 ? _a : this.endpointId;
|
|
451
|
+
const transferBuffer = this.toArrayBuffer(packet.buffer.slice(packet.byteOffset, packet.byteOffset + packet.byteLength));
|
|
452
|
+
yield device.transferOut(endpointOut, transferBuffer);
|
|
453
|
+
});
|
|
454
|
+
}
|
|
456
455
|
transferInWithRetry(path, length, cancelToken) {
|
|
457
456
|
var _a;
|
|
458
457
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -519,7 +518,7 @@ class WebUsbTransport {
|
|
|
519
518
|
yield this.connect(path, false);
|
|
520
519
|
});
|
|
521
520
|
}
|
|
522
|
-
withProtocolReadTimeout(
|
|
521
|
+
withProtocolReadTimeout(_path, promise, timeoutMs, protocol, onTimeout) {
|
|
523
522
|
return __awaiter(this, void 0, void 0, function* () {
|
|
524
523
|
let timer;
|
|
525
524
|
let timedOut = false;
|
|
@@ -534,11 +533,11 @@ class WebUsbTransport {
|
|
|
534
533
|
return yield Promise.race([
|
|
535
534
|
guardedPromise,
|
|
536
535
|
new Promise((_, reject) => {
|
|
537
|
-
timer = setTimeout(() =>
|
|
536
|
+
timer = setTimeout(() => {
|
|
538
537
|
timedOut = true;
|
|
539
538
|
onTimeout === null || onTimeout === void 0 ? void 0 : onTimeout();
|
|
540
539
|
reject(new Error(`Protocol ${protocol} read timeout after ${timeoutMs}ms`));
|
|
541
|
-
}
|
|
540
|
+
}, timeoutMs);
|
|
542
541
|
}),
|
|
543
542
|
]);
|
|
544
543
|
}
|
|
@@ -557,8 +556,7 @@ class WebUsbTransport {
|
|
|
557
556
|
yield this.callProtocolV1(path, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT });
|
|
558
557
|
return true;
|
|
559
558
|
}
|
|
560
|
-
catch (
|
|
561
|
-
this.Log.debug('[WebUsbTransport] Protocol V1 Initialize probe failed:', error);
|
|
559
|
+
catch (_error) {
|
|
562
560
|
return false;
|
|
563
561
|
}
|
|
564
562
|
});
|
|
@@ -573,7 +571,6 @@ class WebUsbTransport {
|
|
|
573
571
|
timeoutMs: PROTOCOL_PROBE_TIMEOUT,
|
|
574
572
|
logger: this.Log,
|
|
575
573
|
logPrefix: 'ProtocolV2 WebUSB',
|
|
576
|
-
onProbeFailed: () => this.resetConnectionAfterProbe(path),
|
|
577
574
|
});
|
|
578
575
|
});
|
|
579
576
|
}
|
|
@@ -590,12 +587,8 @@ class WebUsbTransport {
|
|
|
590
587
|
if (!protocol) {
|
|
591
588
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${path}`);
|
|
592
589
|
}
|
|
593
|
-
if (
|
|
594
|
-
|
|
595
|
-
this.Log.debug('call-', ' name: ', name, ' protocol: ', protocol);
|
|
596
|
-
}
|
|
597
|
-
else {
|
|
598
|
-
this.Log.debug('call-', ' name: ', name, ' data: ', data, ' protocol: ', protocol);
|
|
590
|
+
if (!shouldSuppressHighVolumeCallLog(name)) {
|
|
591
|
+
this.Log.debug('transport call', createTransportCallLog(name, protocol));
|
|
599
592
|
}
|
|
600
593
|
if (protocol === 'V2') {
|
|
601
594
|
return this.callProtocolV2(path, name, data, options);
|
|
@@ -636,13 +629,20 @@ class WebUsbTransport {
|
|
|
636
629
|
}
|
|
637
630
|
let session = this.protocolV2Sessions.get(path);
|
|
638
631
|
if (!session) {
|
|
632
|
+
let sequenceCursor = this.protocolV2Sequences.get(path);
|
|
633
|
+
if (!sequenceCursor) {
|
|
634
|
+
sequenceCursor = new transport.ProtocolV2SequenceCursor();
|
|
635
|
+
this.protocolV2Sequences.set(path, sequenceCursor);
|
|
636
|
+
}
|
|
639
637
|
session = new transport.ProtocolV2Session({
|
|
640
638
|
schemas: {
|
|
641
639
|
protocolV1: protocolV1Messages,
|
|
642
640
|
protocolV2: this.messagesV2,
|
|
643
641
|
},
|
|
644
642
|
router: transport.PROTOCOL_V2_CHANNEL_USB,
|
|
645
|
-
|
|
643
|
+
sequenceCursor,
|
|
644
|
+
writeTimeoutMs: this.protocolV2WriteTimeoutMs,
|
|
645
|
+
writeFrame: (frame) => this.transferOutOnce(path, frame),
|
|
646
646
|
readFrame: () => this.receiveProtocolV2Frame(path, this.protocolV2ReadTimeouts.get(path)),
|
|
647
647
|
logger: this.Log,
|
|
648
648
|
logPrefix: 'ProtocolV2 WebUSB',
|
|
@@ -655,6 +655,21 @@ class WebUsbTransport {
|
|
|
655
655
|
try {
|
|
656
656
|
return yield session.call(name, data, options);
|
|
657
657
|
}
|
|
658
|
+
catch (error) {
|
|
659
|
+
const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
660
|
+
if (message.includes('protocol v2 read timeout') ||
|
|
661
|
+
message.includes('protocol v2 write timeout') ||
|
|
662
|
+
message.includes('protocol v2 delivery timeout') ||
|
|
663
|
+
message.includes('response timeout')) {
|
|
664
|
+
try {
|
|
665
|
+
yield this.resetConnectionAfterProbe(path);
|
|
666
|
+
}
|
|
667
|
+
catch (resetError) {
|
|
668
|
+
this.Log.debug('[WebUsbTransport] Protocol V2 timeout reset failed:', resetError);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
throw error;
|
|
672
|
+
}
|
|
658
673
|
finally {
|
|
659
674
|
this.protocolV2ReadTimeouts.delete(path);
|
|
660
675
|
}
|
|
@@ -737,6 +752,8 @@ class WebUsbTransport {
|
|
|
737
752
|
this.deviceProtocolHints.delete(path);
|
|
738
753
|
(_b = this.protocolV2Assemblers.get(path)) === null || _b === void 0 ? void 0 : _b.reset();
|
|
739
754
|
this.protocolV2Assemblers.delete(path);
|
|
755
|
+
this.protocolV2Sessions.delete(path);
|
|
756
|
+
this.protocolV2ReadTimeouts.delete(path);
|
|
740
757
|
this.deviceEndpoints.delete(path);
|
|
741
758
|
});
|
|
742
759
|
}
|
|
@@ -745,15 +762,6 @@ class WebUsbTransport {
|
|
|
745
762
|
}
|
|
746
763
|
}
|
|
747
764
|
|
|
748
|
-
const FILE_WRITE_LOG_BLOCK_PATTERN = /(?:^|[^a-z])(?:raw)?(?:filesystem|emmc)?filewrite$/i;
|
|
749
|
-
function shouldSuppressHighVolumeCallLog(name) {
|
|
750
|
-
const normalized = name.replace(/[_\s-]/g, '');
|
|
751
|
-
return FILE_WRITE_LOG_BLOCK_PATTERN.test(normalized);
|
|
752
|
-
}
|
|
753
|
-
function isLogBlockCommand(name) {
|
|
754
|
-
var _a, _b;
|
|
755
|
-
return (_b = (_a = transport.LogBlockCommand === null || transport.LogBlockCommand === void 0 ? void 0 : transport.LogBlockCommand.has) === null || _a === void 0 ? void 0 : _a.call(transport.LogBlockCommand, name)) !== null && _b !== void 0 ? _b : false;
|
|
756
|
-
}
|
|
757
765
|
const { parseConfigure, ProtocolV1, check } = transport__default["default"];
|
|
758
766
|
function inferProtocolHintFromDeviceName(name) {
|
|
759
767
|
return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
|
|
@@ -761,8 +769,6 @@ function inferProtocolHintFromDeviceName(name) {
|
|
|
761
769
|
const toBleDescriptor = (device, protocolType) => (Object.assign({ id: device.id, name: device.name, path: device.id, debug: false, commType: 'electron-ble' }, (protocolType ? { protocolType } : {})));
|
|
762
770
|
const BLE_PACKET_SIZE = 192;
|
|
763
771
|
const BLE_WRITE_DELAY_MS = 5;
|
|
764
|
-
const BLE_WRITE_MAX_RETRIES = 3;
|
|
765
|
-
const BLE_WRITE_RETRY_DELAY_MS = 300;
|
|
766
772
|
const BLE_RESPONSE_TIMEOUT_MS = 30000;
|
|
767
773
|
const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
|
|
768
774
|
const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
|
|
@@ -778,8 +784,27 @@ class ElectronBleTransport {
|
|
|
778
784
|
this.v2Assemblers = new Map();
|
|
779
785
|
this.v2FrameQueues = new Map();
|
|
780
786
|
this.v2FramePromises = new Map();
|
|
781
|
-
this.
|
|
782
|
-
|
|
787
|
+
this.protocolV2Links = new transport.ProtocolV2LinkManager({
|
|
788
|
+
getSchemas: () => {
|
|
789
|
+
if (!this._messages || !this._messagesV2) {
|
|
790
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
791
|
+
}
|
|
792
|
+
return {
|
|
793
|
+
protocolV1: this._messages,
|
|
794
|
+
protocolV2: this._messagesV2,
|
|
795
|
+
};
|
|
796
|
+
},
|
|
797
|
+
classifyError: () => 'link-fatal',
|
|
798
|
+
onLinkInvalidated: (uuid, reason) => __awaiter(this, void 0, void 0, function* () {
|
|
799
|
+
var _a, _b;
|
|
800
|
+
(_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
801
|
+
this.rejectProtocolV2Frames(uuid, new Error(reason));
|
|
802
|
+
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('[Electron BLE] Protocol V2 link invalidated:', uuid, reason);
|
|
803
|
+
if (reason.startsWith('Protocol V2 link-fatal error:')) {
|
|
804
|
+
yield this.release(uuid);
|
|
805
|
+
}
|
|
806
|
+
}),
|
|
807
|
+
});
|
|
783
808
|
this.notificationCleanups = new Map();
|
|
784
809
|
this.disconnectCleanups = new Map();
|
|
785
810
|
this.notificationTokens = new Map();
|
|
@@ -815,15 +840,14 @@ class ElectronBleTransport {
|
|
|
815
840
|
throw error;
|
|
816
841
|
}
|
|
817
842
|
cleanupDeviceState(deviceId) {
|
|
818
|
-
|
|
843
|
+
this.protocolV2Links
|
|
844
|
+
.invalidateLink(deviceId, 'Electron BLE device state cleaned')
|
|
845
|
+
.catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] link cleanup failed:', error); });
|
|
819
846
|
this.connectedDevices.delete(deviceId);
|
|
820
847
|
this.deviceProtocol.delete(deviceId);
|
|
821
848
|
this.v1Buffers.delete(deviceId);
|
|
822
849
|
this.v2Assemblers.delete(deviceId);
|
|
823
850
|
this.resetProtocolV2Frames(deviceId);
|
|
824
|
-
if (((_a = this.activeProtocolV2Call) === null || _a === void 0 ? void 0 : _a.uuid) === deviceId) {
|
|
825
|
-
this.activeProtocolV2Call = null;
|
|
826
|
-
}
|
|
827
851
|
this.notificationTokens.delete(deviceId);
|
|
828
852
|
const notifyCleanup = this.notificationCleanups.get(deviceId);
|
|
829
853
|
if (notifyCleanup) {
|
|
@@ -850,9 +874,10 @@ class ElectronBleTransport {
|
|
|
850
874
|
this.configured = true;
|
|
851
875
|
}
|
|
852
876
|
configureProtocolV2(signedData) {
|
|
853
|
-
var _a;
|
|
854
877
|
this._messagesV2 = parseConfigure(signedData);
|
|
855
|
-
|
|
878
|
+
this.protocolV2Links
|
|
879
|
+
.invalidateAllLinks('Protocol V2 schema reconfigured')
|
|
880
|
+
.catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] schema link cleanup failed:', error); });
|
|
856
881
|
}
|
|
857
882
|
listen() {
|
|
858
883
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -890,12 +915,14 @@ class ElectronBleTransport {
|
|
|
890
915
|
if (!uuid) {
|
|
891
916
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleRequiredUUID);
|
|
892
917
|
}
|
|
918
|
+
if (this.connectedDevices.has(uuid)) {
|
|
919
|
+
yield this.release(uuid);
|
|
920
|
+
}
|
|
893
921
|
if (forceCleanRunPromise && this.runPromise) {
|
|
894
922
|
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
|
|
895
923
|
this.runPromise.reject(error);
|
|
896
924
|
this.rejectAllProtocolV2Frames(error);
|
|
897
925
|
this.runPromise = null;
|
|
898
|
-
this.activeProtocolV2Call = null;
|
|
899
926
|
}
|
|
900
927
|
try {
|
|
901
928
|
if (!((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle)) {
|
|
@@ -963,6 +990,7 @@ class ElectronBleTransport {
|
|
|
963
990
|
var _a, _b;
|
|
964
991
|
return __awaiter(this, void 0, void 0, function* () {
|
|
965
992
|
try {
|
|
993
|
+
yield this.protocolV2Links.invalidateLink(id, 'Electron BLE transport released');
|
|
966
994
|
if (this.connectedDevices.has(id)) {
|
|
967
995
|
if ((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle) {
|
|
968
996
|
yield window.desktopApi.nobleBle.unsubscribe(id);
|
|
@@ -1036,14 +1064,12 @@ class ElectronBleTransport {
|
|
|
1036
1064
|
});
|
|
1037
1065
|
}
|
|
1038
1066
|
resetProbeStateAfterProtocolProbe(uuid, protocol) {
|
|
1039
|
-
var _a, _b, _c, _d, _e, _f, _g
|
|
1067
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
1040
1068
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1069
|
+
yield this.protocolV2Links.invalidateLink(uuid, `Reset notify state after Protocol ${protocol} probe`);
|
|
1041
1070
|
this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
|
|
1042
1071
|
(_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1043
1072
|
this.resetProtocolV2Frames(uuid);
|
|
1044
|
-
if (((_b = this.activeProtocolV2Call) === null || _b === void 0 ? void 0 : _b.uuid) === uuid) {
|
|
1045
|
-
this.activeProtocolV2Call = null;
|
|
1046
|
-
}
|
|
1047
1073
|
if (this.runPromise) {
|
|
1048
1074
|
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
|
|
1049
1075
|
this.runPromise.reject(error);
|
|
@@ -1056,16 +1082,16 @@ class ElectronBleTransport {
|
|
|
1056
1082
|
}
|
|
1057
1083
|
this.notificationTokens.delete(uuid);
|
|
1058
1084
|
try {
|
|
1059
|
-
yield ((
|
|
1085
|
+
yield ((_c = (_b = window.desktopApi) === null || _b === void 0 ? void 0 : _b.nobleBle) === null || _c === void 0 ? void 0 : _c.unsubscribe(uuid));
|
|
1060
1086
|
}
|
|
1061
1087
|
catch (error) {
|
|
1062
|
-
(
|
|
1088
|
+
(_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug(`[Electron BLE] unsubscribe after Protocol ${protocol} probe failed:`, error);
|
|
1063
1089
|
}
|
|
1064
1090
|
try {
|
|
1065
|
-
yield ((
|
|
1091
|
+
yield ((_f = (_e = window.desktopApi) === null || _e === void 0 ? void 0 : _e.nobleBle) === null || _f === void 0 ? void 0 : _f.subscribe(uuid));
|
|
1066
1092
|
}
|
|
1067
1093
|
catch (error) {
|
|
1068
|
-
(
|
|
1094
|
+
(_g = this.Log) === null || _g === void 0 ? void 0 : _g.debug(`[Electron BLE] resubscribe after Protocol ${protocol} probe failed:`, error);
|
|
1069
1095
|
throw error;
|
|
1070
1096
|
}
|
|
1071
1097
|
const cleanup = this.createNotificationSubscription(uuid);
|
|
@@ -1120,52 +1146,40 @@ class ElectronBleTransport {
|
|
|
1120
1146
|
const totalBytes = hexData.length / 2;
|
|
1121
1147
|
if (totalBytes <= BLE_PACKET_SIZE) {
|
|
1122
1148
|
yield hdShared.wait(BLE_WRITE_DELAY_MS);
|
|
1123
|
-
yield this.
|
|
1149
|
+
yield this.writeOnce(uuid, hexData);
|
|
1124
1150
|
return;
|
|
1125
1151
|
}
|
|
1126
1152
|
for (let offset = 0; offset < hexData.length;) {
|
|
1127
1153
|
const chunkHexLen = Math.min(BLE_PACKET_SIZE * 2, hexData.length - offset);
|
|
1128
1154
|
const chunkHex = hexData.substring(offset, offset + chunkHexLen);
|
|
1129
1155
|
offset += chunkHexLen;
|
|
1130
|
-
yield this.
|
|
1156
|
+
yield this.writeOnce(uuid, chunkHex);
|
|
1131
1157
|
if (offset < hexData.length) {
|
|
1132
1158
|
yield hdShared.wait(BLE_WRITE_DELAY_MS);
|
|
1133
1159
|
}
|
|
1134
1160
|
}
|
|
1135
1161
|
});
|
|
1136
1162
|
}
|
|
1137
|
-
|
|
1138
|
-
var _a
|
|
1163
|
+
writeOnce(uuid, hexData) {
|
|
1164
|
+
var _a;
|
|
1139
1165
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1140
|
-
let lastError;
|
|
1141
1166
|
const nobleBle = (_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle;
|
|
1142
1167
|
if (!nobleBle) {
|
|
1143
1168
|
throw new Error('Noble BLE API not available');
|
|
1144
1169
|
}
|
|
1145
|
-
|
|
1146
|
-
try {
|
|
1147
|
-
yield nobleBle.write(uuid, hexData);
|
|
1148
|
-
return;
|
|
1149
|
-
}
|
|
1150
|
-
catch (error) {
|
|
1151
|
-
lastError = error;
|
|
1152
|
-
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.error(`[Electron BLE] write failed (attempt ${attempt}/${BLE_WRITE_MAX_RETRIES}):`, error);
|
|
1153
|
-
if (attempt < BLE_WRITE_MAX_RETRIES) {
|
|
1154
|
-
yield hdShared.wait(BLE_WRITE_RETRY_DELAY_MS);
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError, `BLE write failed after ${BLE_WRITE_MAX_RETRIES} attempts: ${(_c = lastError === null || lastError === void 0 ? void 0 : lastError.message) !== null && _c !== void 0 ? _c : lastError}`);
|
|
1170
|
+
yield nobleBle.write(uuid, hexData);
|
|
1159
1171
|
});
|
|
1160
1172
|
}
|
|
1161
1173
|
handleNotification(deviceId, hexData) {
|
|
1162
1174
|
var _a, _b;
|
|
1163
1175
|
if (hexData === 'PAIRING_REJECTED') {
|
|
1164
1176
|
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] Pairing rejection detected for device:', deviceId);
|
|
1165
|
-
|
|
1166
|
-
|
|
1177
|
+
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceBondedCanceled);
|
|
1178
|
+
if (this.deviceProtocol.get(deviceId) === 'V2') {
|
|
1179
|
+
this.rejectProtocolV2Frames(deviceId, error);
|
|
1180
|
+
}
|
|
1181
|
+
else if (this.runPromise) {
|
|
1167
1182
|
this.runPromise.reject(error);
|
|
1168
|
-
this.rejectAllProtocolV2Frames(error);
|
|
1169
1183
|
}
|
|
1170
1184
|
return;
|
|
1171
1185
|
}
|
|
@@ -1181,13 +1195,8 @@ class ElectronBleTransport {
|
|
|
1181
1195
|
this.handleProtocolV1Notification(deviceId, hexData);
|
|
1182
1196
|
}
|
|
1183
1197
|
handleProtocolV2Notification(deviceId, hexData) {
|
|
1184
|
-
var _a
|
|
1198
|
+
var _a;
|
|
1185
1199
|
try {
|
|
1186
|
-
if (!this.runPromise || ((_a = this.activeProtocolV2Call) === null || _a === void 0 ? void 0 : _a.uuid) !== deviceId) {
|
|
1187
|
-
(_b = this.v2Assemblers.get(deviceId)) === null || _b === void 0 ? void 0 : _b.reset();
|
|
1188
|
-
this.resetProtocolV2Frames(deviceId);
|
|
1189
|
-
return;
|
|
1190
|
-
}
|
|
1191
1200
|
const bytes = transport.hexToBytes(hexData);
|
|
1192
1201
|
if (bytes.length === 0)
|
|
1193
1202
|
return;
|
|
@@ -1199,12 +1208,9 @@ class ElectronBleTransport {
|
|
|
1199
1208
|
}
|
|
1200
1209
|
}
|
|
1201
1210
|
catch (error) {
|
|
1202
|
-
(
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
this.runPromise.reject(notifyError);
|
|
1206
|
-
this.rejectAllProtocolV2Frames(notifyError);
|
|
1207
|
-
}
|
|
1211
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.error('[Electron BLE] Protocol V2 notification error:', error);
|
|
1212
|
+
const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
|
|
1213
|
+
this.rejectProtocolV2Frames(deviceId, notifyError);
|
|
1208
1214
|
}
|
|
1209
1215
|
}
|
|
1210
1216
|
getProtocolV2FrameQueue(uuid) {
|
|
@@ -1235,9 +1241,13 @@ class ElectronBleTransport {
|
|
|
1235
1241
|
this.v2FrameQueues.delete(uuid);
|
|
1236
1242
|
this.v2FramePromises.delete(uuid);
|
|
1237
1243
|
}
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1244
|
+
rejectProtocolV2Frames(uuid, error) {
|
|
1245
|
+
this.v2FrameQueues.delete(uuid);
|
|
1246
|
+
const framePromise = this.v2FramePromises.get(uuid);
|
|
1247
|
+
if (framePromise) {
|
|
1248
|
+
this.v2FramePromises.delete(uuid);
|
|
1249
|
+
framePromise.reject(error);
|
|
1250
|
+
}
|
|
1241
1251
|
}
|
|
1242
1252
|
readProtocolV2Frame(uuid) {
|
|
1243
1253
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -1272,7 +1282,7 @@ class ElectronBleTransport {
|
|
|
1272
1282
|
}
|
|
1273
1283
|
}
|
|
1274
1284
|
call(uuid, name, data, options) {
|
|
1275
|
-
var _a
|
|
1285
|
+
var _a;
|
|
1276
1286
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1277
1287
|
if (!this._messages) {
|
|
1278
1288
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
@@ -1284,12 +1294,8 @@ class ElectronBleTransport {
|
|
|
1284
1294
|
if (!protocol) {
|
|
1285
1295
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${uuid}`);
|
|
1286
1296
|
}
|
|
1287
|
-
if (shouldSuppressHighVolumeCallLog(name))
|
|
1288
|
-
|
|
1289
|
-
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] call', 'name:', name, 'protocol:', protocol);
|
|
1290
|
-
}
|
|
1291
|
-
else {
|
|
1292
|
-
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('[Electron BLE] call', 'name:', name, 'data:', data, 'protocol:', protocol);
|
|
1297
|
+
if (!shouldSuppressHighVolumeCallLog(name)) {
|
|
1298
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('transport call', createTransportCallLog(name, protocol));
|
|
1293
1299
|
}
|
|
1294
1300
|
if (protocol === 'V2') {
|
|
1295
1301
|
return this.callProtocolV2(uuid, name, data, options);
|
|
@@ -1360,76 +1366,60 @@ class ElectronBleTransport {
|
|
|
1360
1366
|
});
|
|
1361
1367
|
}
|
|
1362
1368
|
callProtocolV2(uuid, name, data, options) {
|
|
1363
|
-
var _a, _b
|
|
1369
|
+
var _a, _b;
|
|
1364
1370
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1365
1371
|
if (!this._messages || !this._messagesV2) {
|
|
1366
1372
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
1367
1373
|
}
|
|
1368
|
-
const
|
|
1369
|
-
if (this.runPromise) {
|
|
1370
|
-
if (!forceRun) {
|
|
1371
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportCallInProgress);
|
|
1372
|
-
}
|
|
1373
|
-
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
|
|
1374
|
-
this.runPromise.reject(error);
|
|
1375
|
-
this.rejectAllProtocolV2Frames(error);
|
|
1376
|
-
this.runPromise = null;
|
|
1377
|
-
this.activeProtocolV2Call = null;
|
|
1378
|
-
}
|
|
1379
|
-
const runPromise = hdShared.createDeferred();
|
|
1380
|
-
runPromise.promise.catch(() => undefined);
|
|
1381
|
-
this.runPromise = runPromise;
|
|
1382
|
-
const callToken = this.nextProtocolV2CallToken++;
|
|
1383
|
-
this.activeProtocolV2Call = { uuid, token: callToken };
|
|
1384
|
-
(_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1385
|
-
this.resetProtocolV2Frames(uuid);
|
|
1386
|
-
let completed = false;
|
|
1387
|
-
const callOptions = Object.assign(Object.assign({}, options), { timeoutMs: (_b = options === null || options === void 0 ? void 0 : options.timeoutMs) !== null && _b !== void 0 ? _b : BLE_RESPONSE_TIMEOUT_MS });
|
|
1374
|
+
const callOptions = Object.assign(Object.assign({}, options), { timeoutMs: (_a = options === null || options === void 0 ? void 0 : options.timeoutMs) !== null && _a !== void 0 ? _a : BLE_RESPONSE_TIMEOUT_MS });
|
|
1388
1375
|
try {
|
|
1389
|
-
|
|
1390
|
-
schemas: {
|
|
1391
|
-
protocolV1: this._messages,
|
|
1392
|
-
protocolV2: this._messagesV2,
|
|
1393
|
-
},
|
|
1394
|
-
router: transport.PROTOCOL_V2_CHANNEL_BLE_UART,
|
|
1395
|
-
writeFrame: (frame) => this.writeWithChunking(uuid, transport.bytesToHex(frame)),
|
|
1396
|
-
readFrame: () => __awaiter(this, void 0, void 0, function* () {
|
|
1397
|
-
const rxFrame = yield this.readProtocolV2Frame(uuid);
|
|
1398
|
-
if (!(rxFrame instanceof Uint8Array)) {
|
|
1399
|
-
throw new Error('Response is not Uint8Array');
|
|
1400
|
-
}
|
|
1401
|
-
return rxFrame;
|
|
1402
|
-
}),
|
|
1403
|
-
logger: this.Log,
|
|
1404
|
-
logPrefix: 'ProtocolV2 BLE',
|
|
1405
|
-
createTimeoutError: (_messageName, timeout) => hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, `BLE response timeout after ${timeout}ms for ${name}`),
|
|
1406
|
-
});
|
|
1407
|
-
const result = yield session.call(name, data, callOptions);
|
|
1408
|
-
completed = true;
|
|
1409
|
-
return result;
|
|
1376
|
+
return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, callOptions);
|
|
1410
1377
|
}
|
|
1411
1378
|
catch (e) {
|
|
1412
|
-
|
|
1413
|
-
(_c = this.v2Assemblers.get(uuid)) === null || _c === void 0 ? void 0 : _c.reset();
|
|
1414
|
-
this.resetProtocolV2Frames(uuid);
|
|
1415
|
-
}
|
|
1416
|
-
(_d = this.Log) === null || _d === void 0 ? void 0 : _d.error('[Electron BLE] Protocol V2 call error:', e);
|
|
1379
|
+
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.error('[Electron BLE] Protocol V2 call error:', e);
|
|
1417
1380
|
throw e;
|
|
1418
1381
|
}
|
|
1419
|
-
finally {
|
|
1420
|
-
if (this.isActiveProtocolV2Call(uuid, callToken)) {
|
|
1421
|
-
if (!completed) {
|
|
1422
|
-
(_e = this.v2Assemblers.get(uuid)) === null || _e === void 0 ? void 0 : _e.reset();
|
|
1423
|
-
}
|
|
1424
|
-
this.resetProtocolV2Frames(uuid);
|
|
1425
|
-
this.activeProtocolV2Call = null;
|
|
1426
|
-
}
|
|
1427
|
-
if (this.runPromise === runPromise) {
|
|
1428
|
-
this.runPromise = null;
|
|
1429
|
-
}
|
|
1430
|
-
}
|
|
1431
1382
|
});
|
|
1432
1383
|
}
|
|
1384
|
+
createProtocolV2Adapter(uuid) {
|
|
1385
|
+
var _a;
|
|
1386
|
+
const generation = (_a = this.notificationTokens.get(uuid)) !== null && _a !== void 0 ? _a : 0;
|
|
1387
|
+
const assertCurrentGeneration = () => {
|
|
1388
|
+
if (this.notificationTokens.get(uuid) !== generation) {
|
|
1389
|
+
throw new Error(`Protocol V2 notification generation changed for ${uuid}`);
|
|
1390
|
+
}
|
|
1391
|
+
};
|
|
1392
|
+
return {
|
|
1393
|
+
router: transport.PROTOCOL_V2_CHANNEL_BLE_UART,
|
|
1394
|
+
generation,
|
|
1395
|
+
prepareCall: () => {
|
|
1396
|
+
var _a;
|
|
1397
|
+
assertCurrentGeneration();
|
|
1398
|
+
(_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1399
|
+
this.resetProtocolV2Frames(uuid);
|
|
1400
|
+
},
|
|
1401
|
+
writeFrame: (frame) => {
|
|
1402
|
+
assertCurrentGeneration();
|
|
1403
|
+
return this.writeWithChunking(uuid, transport.bytesToHex(frame));
|
|
1404
|
+
},
|
|
1405
|
+
readFrame: () => __awaiter(this, void 0, void 0, function* () {
|
|
1406
|
+
assertCurrentGeneration();
|
|
1407
|
+
const rxFrame = yield this.readProtocolV2Frame(uuid);
|
|
1408
|
+
if (!(rxFrame instanceof Uint8Array)) {
|
|
1409
|
+
throw new Error('Response is not Uint8Array');
|
|
1410
|
+
}
|
|
1411
|
+
return rxFrame;
|
|
1412
|
+
}),
|
|
1413
|
+
reset: (reason) => {
|
|
1414
|
+
var _a;
|
|
1415
|
+
(_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1416
|
+
this.rejectProtocolV2Frames(uuid, new Error(reason));
|
|
1417
|
+
},
|
|
1418
|
+
logger: this.Log,
|
|
1419
|
+
logPrefix: 'ProtocolV2 BLE',
|
|
1420
|
+
createTimeoutError: (messageName, timeout) => hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, `BLE response timeout after ${timeout}ms for ${messageName}`),
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1433
1423
|
processProtocolV1Notification(deviceId, hexData) {
|
|
1434
1424
|
try {
|
|
1435
1425
|
if (typeof hexData !== 'string') {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ProtocolType } from '@onekeyfe/hd-transport';
|
|
2
|
+
export declare function shouldSuppressHighVolumeCallLog(name: string): boolean;
|
|
3
|
+
export declare function createTransportCallLog(name: string, protocol: ProtocolType): {
|
|
4
|
+
name: string;
|
|
5
|
+
protocol: ProtocolType;
|
|
6
|
+
};
|
|
7
|
+
//# sourceMappingURL=transportLog.d.ts.map
|