@onekeyfe/hd-transport-web-device 1.2.0-alpha.21 → 1.2.0-alpha.23
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 +19 -17
- package/__tests__/webusb-protocol-v2-timeout.test.ts +135 -2
- package/dist/electron-ble-transport.d.ts.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.js +45 -38
- package/dist/webusb.d.ts +1 -1
- package/dist/webusb.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/electron-ble-transport.ts +7 -10
- package/src/webusb.ts +53 -38
|
@@ -141,7 +141,6 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
141
141
|
{ message: 'ok' },
|
|
142
142
|
{ router: PROTOCOL_V2_CHANNEL_BLE_UART }
|
|
143
143
|
);
|
|
144
|
-
|
|
145
144
|
nobleBle.onNotification.mockImplementation(handler => {
|
|
146
145
|
notificationHandler = handler;
|
|
147
146
|
return jest.fn();
|
|
@@ -220,19 +219,21 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
220
219
|
const device = { id: 'named-pro2-id', name: 'OneKey Pro 2' };
|
|
221
220
|
const nobleBle = createNobleBle(device);
|
|
222
221
|
let notificationHandler: ((deviceId: string, data: string) => void) | undefined;
|
|
223
|
-
const probeResponse = ProtocolV2.encodeFrame(
|
|
224
|
-
schemas,
|
|
225
|
-
'Success',
|
|
226
|
-
{ message: 'ok' },
|
|
227
|
-
{ router: PROTOCOL_V2_CHANNEL_BLE_UART }
|
|
228
|
-
);
|
|
229
222
|
|
|
230
223
|
nobleBle.onNotification.mockImplementation(handler => {
|
|
231
224
|
notificationHandler = handler;
|
|
232
225
|
return jest.fn();
|
|
233
226
|
});
|
|
227
|
+
let responseSeq = 0;
|
|
234
228
|
nobleBle.write.mockImplementation(() => {
|
|
235
|
-
|
|
229
|
+
responseSeq += 1;
|
|
230
|
+
const response = ProtocolV2.encodeFrame(
|
|
231
|
+
schemas,
|
|
232
|
+
'Success',
|
|
233
|
+
{ message: 'ok' },
|
|
234
|
+
{ router: PROTOCOL_V2_CHANNEL_BLE_UART, seq: responseSeq }
|
|
235
|
+
);
|
|
236
|
+
setTimeout(() => notificationHandler?.(device.id, bytesToHex(response)), 0);
|
|
236
237
|
return Promise.resolve();
|
|
237
238
|
});
|
|
238
239
|
const transport = configureTransport(nobleBle);
|
|
@@ -304,19 +305,20 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
304
305
|
const device = { id: 'repeated-acquire-pro2-id', name: 'OneKey Pro 2' };
|
|
305
306
|
const nobleBle = createNobleBle(device);
|
|
306
307
|
let notificationHandler: ((deviceId: string, data: string) => void) | undefined;
|
|
307
|
-
const response = ProtocolV2.encodeFrame(
|
|
308
|
-
schemas,
|
|
309
|
-
'Success',
|
|
310
|
-
{ message: 'ok' },
|
|
311
|
-
{ router: PROTOCOL_V2_CHANNEL_BLE_UART }
|
|
312
|
-
);
|
|
313
|
-
|
|
314
308
|
nobleBle.onNotification.mockImplementation(handler => {
|
|
315
309
|
notificationHandler = handler;
|
|
316
310
|
return jest.fn();
|
|
317
311
|
});
|
|
312
|
+
let responseSeq = 0;
|
|
318
313
|
nobleBle.write.mockImplementation(() => {
|
|
319
|
-
|
|
314
|
+
responseSeq += 1;
|
|
315
|
+
const sequencedResponse = ProtocolV2.encodeFrame(
|
|
316
|
+
schemas,
|
|
317
|
+
'Success',
|
|
318
|
+
{ message: 'ok' },
|
|
319
|
+
{ router: PROTOCOL_V2_CHANNEL_BLE_UART, seq: responseSeq }
|
|
320
|
+
);
|
|
321
|
+
setTimeout(() => notificationHandler?.(device.id, bytesToHex(sequencedResponse)), 0);
|
|
320
322
|
return Promise.resolve();
|
|
321
323
|
});
|
|
322
324
|
const transport = configureTransport(nobleBle);
|
|
@@ -334,7 +336,7 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
334
336
|
const sentSeqs = nobleBle.write.mock.calls.map(([, hex]) =>
|
|
335
337
|
Number.parseInt(hex.slice(12, 14), 16)
|
|
336
338
|
);
|
|
337
|
-
expect(sentSeqs).toEqual([1, 2]);
|
|
339
|
+
expect(sentSeqs).toEqual([1, 2, 3]);
|
|
338
340
|
} finally {
|
|
339
341
|
await transport.release(device.id);
|
|
340
342
|
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import transport, {
|
|
1
|
+
import transport, {
|
|
2
|
+
ProtocolV2,
|
|
3
|
+
ProtocolV2FrameAssembler,
|
|
4
|
+
ProtocolV2LinkError,
|
|
5
|
+
} from '@onekeyfe/hd-transport';
|
|
2
6
|
|
|
3
7
|
import WebUsbTransport from '../src/webusb';
|
|
4
8
|
|
|
@@ -39,6 +43,38 @@ describe('WebUsbTransport Protocol V2 timeout recovery', () => {
|
|
|
39
43
|
expect(webusb.deviceProtocol.get(path)).toBe('V2');
|
|
40
44
|
});
|
|
41
45
|
|
|
46
|
+
test('retries an expected Protocol V2 probe once after resetting the connection', async () => {
|
|
47
|
+
const webusb = new WebUsbTransport() as any;
|
|
48
|
+
const path = 'pro2-webusb';
|
|
49
|
+
webusb.probeProtocolV1 = jest.fn();
|
|
50
|
+
webusb.probeProtocolV2 = jest.fn().mockResolvedValueOnce(false).mockResolvedValueOnce(true);
|
|
51
|
+
webusb.resetConnectionAfterProbe = jest.fn().mockResolvedValue(undefined);
|
|
52
|
+
|
|
53
|
+
await expect(webusb.detectProtocol(path, 'V2')).resolves.toBe('V2');
|
|
54
|
+
|
|
55
|
+
expect(webusb.probeProtocolV2).toHaveBeenCalledTimes(2);
|
|
56
|
+
expect(webusb.probeProtocolV1).not.toHaveBeenCalled();
|
|
57
|
+
expect(webusb.resetConnectionAfterProbe).toHaveBeenCalledTimes(1);
|
|
58
|
+
expect(webusb.deviceProtocol.get(path)).toBe('V2');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('reports a Protocol V2 probe timeout only after the bounded retry is exhausted', async () => {
|
|
62
|
+
const webusb = new WebUsbTransport() as any;
|
|
63
|
+
const path = 'pro2-webusb';
|
|
64
|
+
webusb.probeProtocolV1 = jest.fn();
|
|
65
|
+
webusb.probeProtocolV2 = jest.fn().mockResolvedValue(false);
|
|
66
|
+
webusb.resetConnectionAfterProbe = jest.fn().mockResolvedValue(undefined);
|
|
67
|
+
|
|
68
|
+
await expect(webusb.detectProtocol(path, 'V2')).rejects.toThrow(
|
|
69
|
+
'Protocol V2 probe timeout after 2 attempts'
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
expect(webusb.probeProtocolV2).toHaveBeenCalledTimes(2);
|
|
73
|
+
expect(webusb.probeProtocolV1).not.toHaveBeenCalled();
|
|
74
|
+
expect(webusb.resetConnectionAfterProbe).toHaveBeenCalledTimes(2);
|
|
75
|
+
expect(webusb.deviceProtocol.has(path)).toBe(false);
|
|
76
|
+
});
|
|
77
|
+
|
|
42
78
|
test('invalidates and resets the cached connection before another call can start', async () => {
|
|
43
79
|
const webusb = new WebUsbTransport() as any;
|
|
44
80
|
const path = 'pro2-webusb';
|
|
@@ -49,7 +85,6 @@ describe('WebUsbTransport Protocol V2 timeout recovery', () => {
|
|
|
49
85
|
webusb.receiveProtocolV2Frame = jest.fn(() => new Promise<void>(() => {}));
|
|
50
86
|
webusb.resetConnectionAfterProbe = jest.fn().mockImplementation(() => {
|
|
51
87
|
webusb.protocolV2Sessions.delete(path);
|
|
52
|
-
webusb.protocolV2ReadTimeouts.delete(path);
|
|
53
88
|
webusb.protocolV2Assemblers.get(path)?.reset();
|
|
54
89
|
});
|
|
55
90
|
|
|
@@ -60,4 +95,102 @@ describe('WebUsbTransport Protocol V2 timeout recovery', () => {
|
|
|
60
95
|
expect(webusb.resetConnectionAfterProbe).toHaveBeenCalledWith(path);
|
|
61
96
|
expect(webusb.protocolV2Sessions.has(path)).toBe(false);
|
|
62
97
|
});
|
|
98
|
+
|
|
99
|
+
test.each(['router', 'packet-source', 'ack-sequence', 'response-sequence', 'frame'] as const)(
|
|
100
|
+
'invalidates cached state for typed Protocol V2 %s errors',
|
|
101
|
+
async code => {
|
|
102
|
+
const webusb = new WebUsbTransport() as any;
|
|
103
|
+
const path = 'pro2-webusb';
|
|
104
|
+
webusb.messages = transport.parseConfigure(schema);
|
|
105
|
+
webusb.messagesV2 = transport.parseConfigure(schema);
|
|
106
|
+
webusb.protocolV2Assemblers.set(path, new ProtocolV2FrameAssembler());
|
|
107
|
+
webusb.transferOutOnce = jest.fn().mockResolvedValue(undefined);
|
|
108
|
+
const recoveredResponse = ProtocolV2.encodeFrame(
|
|
109
|
+
{ protocolV1: webusb.messages, protocolV2: webusb.messagesV2 },
|
|
110
|
+
'Success',
|
|
111
|
+
{ message: 'recovered' },
|
|
112
|
+
{ seq: 1 }
|
|
113
|
+
);
|
|
114
|
+
webusb.receiveProtocolV2Frame = jest
|
|
115
|
+
.fn()
|
|
116
|
+
.mockRejectedValueOnce(
|
|
117
|
+
new ProtocolV2LinkError(code, `Protocol V2 ${code} validation failed`)
|
|
118
|
+
)
|
|
119
|
+
.mockResolvedValue(recoveredResponse);
|
|
120
|
+
webusb.resetConnectionAfterProbe = jest.fn().mockImplementation(() => {
|
|
121
|
+
webusb.protocolV2Sessions.delete(path);
|
|
122
|
+
webusb.protocolV2Assemblers.get(path)?.reset();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
await expect(webusb.callProtocolV2(path, 'Ping', { message: 'mismatch' })).rejects.toThrow(
|
|
126
|
+
`${code} validation failed`
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
expect(webusb.resetConnectionAfterProbe).toHaveBeenCalledWith(path);
|
|
130
|
+
expect(webusb.protocolV2Sessions.has(path)).toBe(false);
|
|
131
|
+
|
|
132
|
+
await expect(
|
|
133
|
+
webusb.callProtocolV2(path, 'Ping', { message: 'after-reset' })
|
|
134
|
+
).resolves.toMatchObject({
|
|
135
|
+
type: 'Success',
|
|
136
|
+
message: { message: 'recovered' },
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
test('does not discard buffered Protocol V2 frames before each call', async () => {
|
|
142
|
+
const webusb = new WebUsbTransport() as any;
|
|
143
|
+
const path = 'pro2-webusb';
|
|
144
|
+
const assembler = new ProtocolV2FrameAssembler();
|
|
145
|
+
const reset = jest.spyOn(assembler, 'reset');
|
|
146
|
+
let responseSequence = 0;
|
|
147
|
+
webusb.messages = transport.parseConfigure(schema);
|
|
148
|
+
webusb.messagesV2 = transport.parseConfigure(schema);
|
|
149
|
+
webusb.protocolV2Assemblers.set(path, assembler);
|
|
150
|
+
webusb.transferOutOnce = jest.fn().mockResolvedValue(undefined);
|
|
151
|
+
webusb.receiveProtocolV2Frame = jest.fn().mockImplementation(() => {
|
|
152
|
+
responseSequence += 1;
|
|
153
|
+
const response = ProtocolV2.encodeFrame(
|
|
154
|
+
{ protocolV1: webusb.messages, protocolV2: webusb.messagesV2 },
|
|
155
|
+
'Success',
|
|
156
|
+
{ message: 'ok' },
|
|
157
|
+
{ seq: responseSequence }
|
|
158
|
+
);
|
|
159
|
+
return Promise.resolve(response);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
await webusb.callProtocolV2(path, 'Ping', { message: 'first' });
|
|
163
|
+
await webusb.callProtocolV2(path, 'Ping', { message: 'second' });
|
|
164
|
+
|
|
165
|
+
expect(reset).not.toHaveBeenCalled();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test('keeps queued Protocol V2 read timeouts scoped to each call', async () => {
|
|
169
|
+
const webusb = new WebUsbTransport() as any;
|
|
170
|
+
const path = 'pro2-webusb';
|
|
171
|
+
let responseSequence = 0;
|
|
172
|
+
webusb.messages = transport.parseConfigure(schema);
|
|
173
|
+
webusb.messagesV2 = transport.parseConfigure(schema);
|
|
174
|
+
webusb.transferOutOnce = jest.fn().mockResolvedValue(undefined);
|
|
175
|
+
webusb.receiveProtocolV2Frame = jest.fn().mockImplementation(() => {
|
|
176
|
+
responseSequence += 1;
|
|
177
|
+
return Promise.resolve(
|
|
178
|
+
ProtocolV2.encodeFrame(
|
|
179
|
+
{ protocolV1: webusb.messages, protocolV2: webusb.messagesV2 },
|
|
180
|
+
'Success',
|
|
181
|
+
{ message: 'ok' },
|
|
182
|
+
{ seq: responseSequence }
|
|
183
|
+
)
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
await Promise.all([
|
|
188
|
+
webusb.callProtocolV2(path, 'Ping', { message: 'long' }, { timeoutMs: 1_000 }),
|
|
189
|
+
webusb.callProtocolV2(path, 'Ping', { message: 'short' }, { timeoutMs: 25 }),
|
|
190
|
+
]);
|
|
191
|
+
|
|
192
|
+
expect(
|
|
193
|
+
webusb.receiveProtocolV2Frame.mock.calls.map(([, timeoutMs]: unknown[]) => timeoutMs)
|
|
194
|
+
).toEqual([1_000, 25]);
|
|
195
|
+
});
|
|
63
196
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"electron-ble-transport.d.ts","sourceRoot":"","sources":["../src/electron-ble-transport.ts"],"names":[],"mappings":";AAoBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnG,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;CACjC,CAAC;
|
|
1
|
+
{"version":3,"file":"electron-ble-transport.d.ts","sourceRoot":"","sources":["../src/electron-ble-transport.ts"],"names":[],"mappings":";AAoBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnG,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;CACjC,CAAC;AAoCF,MAAM,CAAC,OAAO,OAAO,oBAAoB;IACvC,OAAO,CAAC,SAAS,CAA0D;IAE3E,OAAO,CAAC,WAAW,CAA0D;IAE7E,IAAI,SAA0B;IAE9B,UAAU,UAAS;IAEnB,UAAU,EAAE,QAAQ,CAAC,UAAU,GAAG,MAAM,CAAC,GAAG,IAAI,CAAQ;IAExD,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;IA0B1B,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,YAAY;IAcxC,SAAS,CAAC,UAAU,EAAE,GAAG;IAKzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAO7B,MAAM;IAIN,SAAS,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAqBxC,OAAO,CAAC,KAAK,EAAE,eAAe;;;;;;;;;;;IA0F9B,OAAO,CAAC,EAAE,EAAE,MAAM;IAgBxB,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;IAOpC,OAAO,CAAC,kBAAkB;YAMZ,cAAc;IA+C5B,OAAO,CAAC,8BAA8B;YAgBxB,iCAAiC;YAqCjC,eAAe;YAgBf,eAAe;YAuBf,iBAAiB;YAsBjB,SAAS;IASvB,OAAO,CAAC,kBAAkB;IAwB1B,OAAO,CAAC,4BAA4B;IAkBpC,OAAO,CAAC,uBAAuB;IAS/B,OAAO,CAAC,sBAAsB;IAU9B,OAAO,CAAC,yBAAyB;IAQjC,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,sBAAsB;YAShB,mBAAmB;IAiBjC,OAAO,CAAC,4BAA4B;IAgB9B,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;YAuEd,cAAc;IA0B5B,OAAO,CAAC,uBAAuB;IA0C/B,OAAO,CAAC,6BAA6B;IAsCrC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;CAGxD"}
|
package/dist/index.d.ts
CHANGED
|
@@ -25,8 +25,6 @@ declare class WebUsbTransport {
|
|
|
25
25
|
private protocolV2Sessions;
|
|
26
26
|
/** Sequence cursors survive ordinary reconnects and cached session rebuilds. */
|
|
27
27
|
private protocolV2Sequences;
|
|
28
|
-
/** Read timeout for the current Protocol V2 call, consumed by cached readFrame. */
|
|
29
|
-
private protocolV2ReadTimeouts;
|
|
30
28
|
/** Per-path USB endpoint / interface numbers (discovered from USB descriptors) */
|
|
31
29
|
private deviceEndpoints;
|
|
32
30
|
/**
|
|
@@ -90,6 +88,7 @@ declare class WebUsbTransport {
|
|
|
90
88
|
* fall back to a Protocol V2 Ping probe.
|
|
91
89
|
*/
|
|
92
90
|
private createProtocolMismatchError;
|
|
91
|
+
private createProtocolProbeTimeoutError;
|
|
93
92
|
private createProtocolDetectionError;
|
|
94
93
|
private detectProtocol;
|
|
95
94
|
/**
|
package/dist/index.js
CHANGED
|
@@ -60,6 +60,7 @@ const HEADER_LENGTH = transport.PROTOCOL_V1_MESSAGE_HEADER_SIZE;
|
|
|
60
60
|
const PACKET_IO_MAX_RETRIES = 3;
|
|
61
61
|
const PACKET_IO_RETRY_DELAY = 300;
|
|
62
62
|
const PROTOCOL_PROBE_TIMEOUT = 1000;
|
|
63
|
+
const EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS = 2;
|
|
63
64
|
function inferProtocolHintFromDeviceName$1(name) {
|
|
64
65
|
return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
|
|
65
66
|
}
|
|
@@ -70,7 +71,6 @@ class WebUsbTransport {
|
|
|
70
71
|
this.protocolV2Assemblers = new Map();
|
|
71
72
|
this.protocolV2Sessions = new Map();
|
|
72
73
|
this.protocolV2Sequences = new Map();
|
|
73
|
-
this.protocolV2ReadTimeouts = new Map();
|
|
74
74
|
this.deviceEndpoints = new Map();
|
|
75
75
|
this.mockSerialPaths = new WeakMap();
|
|
76
76
|
this.mockSerialCounter = 0;
|
|
@@ -98,7 +98,6 @@ class WebUsbTransport {
|
|
|
98
98
|
configureProtocolV2(signedData) {
|
|
99
99
|
this.messagesV2 = parseConfigure$1(signedData);
|
|
100
100
|
this.protocolV2Sessions.clear();
|
|
101
|
-
this.protocolV2ReadTimeouts.clear();
|
|
102
101
|
}
|
|
103
102
|
promptDeviceAccess() {
|
|
104
103
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -185,10 +184,14 @@ class WebUsbTransport {
|
|
|
185
184
|
createProtocolMismatchError(expected) {
|
|
186
185
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
|
|
187
186
|
}
|
|
187
|
+
createProtocolProbeTimeoutError(expected, attempts) {
|
|
188
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol ${expected} probe timeout after ${attempts} attempts`);
|
|
189
|
+
}
|
|
188
190
|
createProtocolDetectionError() {
|
|
189
191
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Unable to detect USB protocol: device did not respond to Protocol V1 Initialize or Protocol V2 Ping');
|
|
190
192
|
}
|
|
191
193
|
detectProtocol(path, expectedProtocol, protocolHint) {
|
|
194
|
+
var _a;
|
|
192
195
|
return __awaiter(this, void 0, void 0, function* () {
|
|
193
196
|
if (expectedProtocol === 'V1') {
|
|
194
197
|
if (yield this.probeProtocolV1(path)) {
|
|
@@ -199,8 +202,18 @@ class WebUsbTransport {
|
|
|
199
202
|
throw this.createProtocolMismatchError(expectedProtocol);
|
|
200
203
|
}
|
|
201
204
|
if (expectedProtocol === 'V2') {
|
|
202
|
-
|
|
203
|
-
|
|
205
|
+
for (let attempt = 1; attempt <= EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS; attempt += 1) {
|
|
206
|
+
if (yield this.probeProtocolV2(path)) {
|
|
207
|
+
this.deviceProtocol.set(path, 'V2');
|
|
208
|
+
return 'V2';
|
|
209
|
+
}
|
|
210
|
+
yield this.resetConnectionAfterProbe(path);
|
|
211
|
+
if (attempt < EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS) {
|
|
212
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[WebUsbTransport] Protocol V2 probe timed out, retrying ${attempt + 1}/${EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
this.deviceProtocol.delete(path);
|
|
216
|
+
throw this.createProtocolProbeTimeoutError(expectedProtocol, EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS);
|
|
204
217
|
}
|
|
205
218
|
const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(path) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
|
|
206
219
|
for (const protocol of probeOrder) {
|
|
@@ -209,9 +222,7 @@ class WebUsbTransport {
|
|
|
209
222
|
this.deviceProtocol.set(path, protocol);
|
|
210
223
|
return protocol;
|
|
211
224
|
}
|
|
212
|
-
|
|
213
|
-
yield this.resetConnectionAfterProbe(path);
|
|
214
|
-
}
|
|
225
|
+
yield this.resetConnectionAfterProbe(path);
|
|
215
226
|
}
|
|
216
227
|
this.deviceProtocol.delete(path);
|
|
217
228
|
throw this.createProtocolDetectionError();
|
|
@@ -495,7 +506,6 @@ class WebUsbTransport {
|
|
|
495
506
|
return __awaiter(this, void 0, void 0, function* () {
|
|
496
507
|
(_a = this.protocolV2Assemblers.get(path)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
497
508
|
this.protocolV2Sessions.delete(path);
|
|
498
|
-
this.protocolV2ReadTimeouts.delete(path);
|
|
499
509
|
try {
|
|
500
510
|
const device = yield this.findDevice(path);
|
|
501
511
|
if (device.opened) {
|
|
@@ -517,7 +527,7 @@ class WebUsbTransport {
|
|
|
517
527
|
yield this.connect(path, false);
|
|
518
528
|
});
|
|
519
529
|
}
|
|
520
|
-
withProtocolReadTimeout(
|
|
530
|
+
withProtocolReadTimeout(path, promise, timeoutMs, protocol, onTimeout) {
|
|
521
531
|
return __awaiter(this, void 0, void 0, function* () {
|
|
522
532
|
let timer;
|
|
523
533
|
let timedOut = false;
|
|
@@ -532,11 +542,19 @@ class WebUsbTransport {
|
|
|
532
542
|
return yield Promise.race([
|
|
533
543
|
guardedPromise,
|
|
534
544
|
new Promise((_, reject) => {
|
|
535
|
-
timer = setTimeout(() => {
|
|
545
|
+
timer = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
|
|
536
546
|
timedOut = true;
|
|
537
547
|
onTimeout === null || onTimeout === void 0 ? void 0 : onTimeout();
|
|
548
|
+
if (protocol === 'V1') {
|
|
549
|
+
try {
|
|
550
|
+
yield this.resetConnectionAfterProbe(path);
|
|
551
|
+
}
|
|
552
|
+
catch (error) {
|
|
553
|
+
this.Log.debug('[WebUsbTransport] reset after Protocol V1 timeout failed:', error);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
538
556
|
reject(new Error(`Protocol ${protocol} read timeout after ${timeoutMs}ms`));
|
|
539
|
-
}, timeoutMs);
|
|
557
|
+
}), timeoutMs);
|
|
540
558
|
}),
|
|
541
559
|
]);
|
|
542
560
|
}
|
|
@@ -566,7 +584,7 @@ class WebUsbTransport {
|
|
|
566
584
|
return false;
|
|
567
585
|
}
|
|
568
586
|
return transport.probeProtocolV2({
|
|
569
|
-
call: (name, data, options) => this.callProtocolV2(path, name, data, options),
|
|
587
|
+
call: (name, data, options) => this.callProtocolV2(path, name, data, options, false),
|
|
570
588
|
timeoutMs: PROTOCOL_PROBE_TIMEOUT,
|
|
571
589
|
logger: this.Log,
|
|
572
590
|
logPrefix: 'ProtocolV2 WebUSB',
|
|
@@ -616,8 +634,7 @@ class WebUsbTransport {
|
|
|
616
634
|
return check$1.call(jsonData);
|
|
617
635
|
});
|
|
618
636
|
}
|
|
619
|
-
callProtocolV2(path, name, data, options) {
|
|
620
|
-
var _a;
|
|
637
|
+
callProtocolV2(path, name, data, options, resetOnError = true) {
|
|
621
638
|
return __awaiter(this, void 0, void 0, function* () {
|
|
622
639
|
const protocolV1Messages = this.messages;
|
|
623
640
|
if (!this.messagesV2) {
|
|
@@ -641,33 +658,27 @@ class WebUsbTransport {
|
|
|
641
658
|
router: transport.PROTOCOL_V2_CHANNEL_USB,
|
|
642
659
|
sequenceCursor,
|
|
643
660
|
writeFrame: (frame) => this.transferOutOnce(path, frame),
|
|
644
|
-
readFrame:
|
|
661
|
+
readFrame: context => this.receiveProtocolV2Frame(path, context.timeoutMs),
|
|
645
662
|
logger: this.Log,
|
|
646
663
|
logPrefix: 'ProtocolV2 WebUSB',
|
|
647
|
-
createTimeoutError: (messageName, timeoutMs) => new
|
|
664
|
+
createTimeoutError: (messageName, timeoutMs) => new transport.ProtocolV2LinkError('response-timeout', `Protocol V2 response timeout after ${timeoutMs}ms for ${messageName}`),
|
|
648
665
|
});
|
|
649
666
|
this.protocolV2Sessions.set(path, session);
|
|
650
667
|
}
|
|
651
|
-
this.protocolV2ReadTimeouts.set(path, options === null || options === void 0 ? void 0 : options.timeoutMs);
|
|
652
|
-
(_a = this.protocolV2Assemblers.get(path)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
653
668
|
try {
|
|
654
669
|
return yield session.call(name, data, options);
|
|
655
670
|
}
|
|
656
671
|
catch (error) {
|
|
657
|
-
|
|
658
|
-
if (message.includes('protocol v2 read timeout') || message.includes('response timeout')) {
|
|
672
|
+
if (resetOnError && (transport.isProtocolV2LinkError(error) || this.isRetryablePacketIoError(error))) {
|
|
659
673
|
try {
|
|
660
674
|
yield this.resetConnectionAfterProbe(path);
|
|
661
675
|
}
|
|
662
676
|
catch (resetError) {
|
|
663
|
-
this.Log.debug('[WebUsbTransport] Protocol V2
|
|
677
|
+
this.Log.debug('[WebUsbTransport] Protocol V2 link reset failed:', resetError);
|
|
664
678
|
}
|
|
665
679
|
}
|
|
666
680
|
throw error;
|
|
667
681
|
}
|
|
668
|
-
finally {
|
|
669
|
-
this.protocolV2ReadTimeouts.delete(path);
|
|
670
|
-
}
|
|
671
682
|
});
|
|
672
683
|
}
|
|
673
684
|
receiveProtocolV2Frame(path, timeoutMs) {
|
|
@@ -688,12 +699,7 @@ class WebUsbTransport {
|
|
|
688
699
|
})
|
|
689
700
|
: yield transferIn;
|
|
690
701
|
const bytes = new Uint8Array(this.toArrayBuffer(dataView.buffer.slice(dataView.byteOffset, dataView.byteOffset + dataView.byteLength)));
|
|
691
|
-
|
|
692
|
-
frame = assembler.push(bytes);
|
|
693
|
-
}
|
|
694
|
-
catch (error) {
|
|
695
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.NetworkError, error instanceof Error ? error.message : String(error));
|
|
696
|
-
}
|
|
702
|
+
frame = assembler.push(bytes);
|
|
697
703
|
}
|
|
698
704
|
return frame;
|
|
699
705
|
});
|
|
@@ -748,7 +754,6 @@ class WebUsbTransport {
|
|
|
748
754
|
(_b = this.protocolV2Assemblers.get(path)) === null || _b === void 0 ? void 0 : _b.reset();
|
|
749
755
|
this.protocolV2Assemblers.delete(path);
|
|
750
756
|
this.protocolV2Sessions.delete(path);
|
|
751
|
-
this.protocolV2ReadTimeouts.delete(path);
|
|
752
757
|
this.deviceEndpoints.delete(path);
|
|
753
758
|
});
|
|
754
759
|
}
|
|
@@ -764,7 +769,6 @@ function inferProtocolHintFromDeviceName(name) {
|
|
|
764
769
|
const toBleDescriptor = (device, protocolType) => (Object.assign({ id: device.id, name: device.name, path: device.id, debug: false, commType: 'electron-ble' }, (protocolType ? { protocolType } : {})));
|
|
765
770
|
const BLE_PACKET_SIZE = 192;
|
|
766
771
|
const BLE_WRITE_DELAY_MS = 5;
|
|
767
|
-
const BLE_RESPONSE_TIMEOUT_MS = 30000;
|
|
768
772
|
const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
|
|
769
773
|
const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
|
|
770
774
|
class ElectronBleTransport {
|
|
@@ -1023,9 +1027,12 @@ class ElectronBleTransport {
|
|
|
1023
1027
|
throw this.createProtocolMismatchError(expectedProtocol);
|
|
1024
1028
|
}
|
|
1025
1029
|
if (expectedProtocol === 'V2') {
|
|
1026
|
-
this.
|
|
1027
|
-
|
|
1028
|
-
|
|
1030
|
+
if (yield this.probeProtocolV2(uuid)) {
|
|
1031
|
+
this.deviceProtocol.set(uuid, 'V2');
|
|
1032
|
+
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> V2 (expected)`);
|
|
1033
|
+
return 'V2';
|
|
1034
|
+
}
|
|
1035
|
+
throw this.createProtocolMismatchError(expectedProtocol);
|
|
1029
1036
|
}
|
|
1030
1037
|
const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
|
|
1031
1038
|
for (let i = 0; i < probeOrder.length; i += 1) {
|
|
@@ -1361,17 +1368,17 @@ class ElectronBleTransport {
|
|
|
1361
1368
|
});
|
|
1362
1369
|
}
|
|
1363
1370
|
callProtocolV2(uuid, name, data, options) {
|
|
1364
|
-
var _a
|
|
1371
|
+
var _a;
|
|
1365
1372
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1366
1373
|
if (!this._messages || !this._messagesV2) {
|
|
1367
1374
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
1368
1375
|
}
|
|
1369
|
-
const callOptions =
|
|
1376
|
+
const callOptions = options;
|
|
1370
1377
|
try {
|
|
1371
1378
|
return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, callOptions);
|
|
1372
1379
|
}
|
|
1373
1380
|
catch (e) {
|
|
1374
|
-
(
|
|
1381
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.error('[Electron BLE] Protocol V2 call error:', e);
|
|
1375
1382
|
throw e;
|
|
1376
1383
|
}
|
|
1377
1384
|
});
|
package/dist/webusb.d.ts
CHANGED
|
@@ -14,7 +14,6 @@ export default class WebUsbTransport {
|
|
|
14
14
|
private protocolV2Assemblers;
|
|
15
15
|
private protocolV2Sessions;
|
|
16
16
|
private protocolV2Sequences;
|
|
17
|
-
private protocolV2ReadTimeouts;
|
|
18
17
|
private deviceEndpoints;
|
|
19
18
|
private mockSerialPaths;
|
|
20
19
|
private mockSerialCounter;
|
|
@@ -36,6 +35,7 @@ export default class WebUsbTransport {
|
|
|
36
35
|
getConnectedDevices(): Promise<DeviceInfo[]>;
|
|
37
36
|
acquire(input: AcquireInput): Promise<string | undefined>;
|
|
38
37
|
private createProtocolMismatchError;
|
|
38
|
+
private createProtocolProbeTimeoutError;
|
|
39
39
|
private createProtocolDetectionError;
|
|
40
40
|
private detectProtocol;
|
|
41
41
|
findDevice(path: string): Promise<USBDevice>;
|
package/dist/webusb.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":";AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":";AACA,OAAO,SAaN,MAAM,wBAAwB,CAAC;AAYhC,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EACpB,YAAY,EACZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAsBhC,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,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;IAU7B,kBAAkB;IAmBlB,SAAS;IAQf,OAAO,CAAC,aAAa;IAmBf,mBAAmB;IAgCnB,OAAO,CAAC,KAAK,EAAE,YAAY;IA0BjC,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,+BAA+B;IAOvC,OAAO,CAAC,4BAA4B;YAOtB,cAAc;IA6DtB,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;YAwBzB,uBAAuB;YA0CvB,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;YA2Dd,sBAAsB;IAqC9B,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;IAgD5C,OAAO,CAAC,IAAI,EAAE,MAAM;IAkB1B,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.
|
|
3
|
+
"version": "1.2.0-alpha.23",
|
|
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.
|
|
24
|
-
"@onekeyfe/hd-transport": "1.2.0-alpha.
|
|
23
|
+
"@onekeyfe/hd-shared": "1.2.0-alpha.23",
|
|
24
|
+
"@onekeyfe/hd-transport": "1.2.0-alpha.23"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@onekeyfe/hd-transport-electron": "1.2.0-alpha.
|
|
27
|
+
"@onekeyfe/hd-transport-electron": "1.2.0-alpha.23",
|
|
28
28
|
"@types/w3c-web-usb": "^1.0.6",
|
|
29
29
|
"@types/web-bluetooth": "^0.0.17"
|
|
30
30
|
},
|
|
31
|
-
"gitHead": "
|
|
31
|
+
"gitHead": "7cadb40ca6414053a488756f28a9f413164b235d"
|
|
32
32
|
}
|
|
@@ -62,7 +62,6 @@ const toBleDescriptor = (
|
|
|
62
62
|
|
|
63
63
|
const BLE_PACKET_SIZE = 192;
|
|
64
64
|
const BLE_WRITE_DELAY_MS = 5;
|
|
65
|
-
const BLE_RESPONSE_TIMEOUT_MS = 30_000;
|
|
66
65
|
const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
|
|
67
66
|
const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
|
|
68
67
|
|
|
@@ -379,11 +378,12 @@ export default class ElectronBleTransport {
|
|
|
379
378
|
}
|
|
380
379
|
|
|
381
380
|
if (expectedProtocol === 'V2') {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
381
|
+
if (await this.probeProtocolV2(uuid)) {
|
|
382
|
+
this.deviceProtocol.set(uuid, 'V2');
|
|
383
|
+
this.Log?.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> V2 (expected)`);
|
|
384
|
+
return 'V2';
|
|
385
|
+
}
|
|
386
|
+
throw this.createProtocolMismatchError(expectedProtocol);
|
|
387
387
|
}
|
|
388
388
|
|
|
389
389
|
// Protocol must be actively probed after connection. Name, PID, and descriptors only
|
|
@@ -761,10 +761,7 @@ export default class ElectronBleTransport {
|
|
|
761
761
|
throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
|
|
762
762
|
}
|
|
763
763
|
|
|
764
|
-
const callOptions =
|
|
765
|
-
...options,
|
|
766
|
-
timeoutMs: options?.timeoutMs ?? BLE_RESPONSE_TIMEOUT_MS,
|
|
767
|
-
};
|
|
764
|
+
const callOptions = options;
|
|
768
765
|
|
|
769
766
|
try {
|
|
770
767
|
return await this.protocolV2Links.call(
|
package/src/webusb.ts
CHANGED
|
@@ -7,8 +7,10 @@ import transport, {
|
|
|
7
7
|
PROTOCOL_V2_CHANNEL_USB,
|
|
8
8
|
PROTOCOL_V2_FRAME_MAX_BYTES,
|
|
9
9
|
ProtocolV2FrameAssembler,
|
|
10
|
+
ProtocolV2LinkError,
|
|
10
11
|
ProtocolV2SequenceCursor,
|
|
11
12
|
ProtocolV2Session,
|
|
13
|
+
isProtocolV2LinkError,
|
|
12
14
|
probeProtocolV2 as probeProtocolV2Helper,
|
|
13
15
|
} from '@onekeyfe/hd-transport';
|
|
14
16
|
import {
|
|
@@ -41,6 +43,7 @@ const HEADER_LENGTH = PROTOCOL_V1_MESSAGE_HEADER_SIZE;
|
|
|
41
43
|
const PACKET_IO_MAX_RETRIES = 3;
|
|
42
44
|
const PACKET_IO_RETRY_DELAY = 300;
|
|
43
45
|
const PROTOCOL_PROBE_TIMEOUT = 1000;
|
|
46
|
+
const EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS = 2;
|
|
44
47
|
function inferProtocolHintFromDeviceName(name?: string | null): ProtocolType | undefined {
|
|
45
48
|
return /\bpro\s*2\b/i.test(name ?? '') ? 'V2' : undefined;
|
|
46
49
|
}
|
|
@@ -85,9 +88,6 @@ export default class WebUsbTransport {
|
|
|
85
88
|
/** Sequence cursors survive ordinary reconnects and cached session rebuilds. */
|
|
86
89
|
private protocolV2Sequences: Map<string, ProtocolV2SequenceCursor> = new Map();
|
|
87
90
|
|
|
88
|
-
/** Read timeout for the current Protocol V2 call, consumed by cached readFrame. */
|
|
89
|
-
private protocolV2ReadTimeouts: Map<string, number | undefined> = new Map();
|
|
90
|
-
|
|
91
91
|
/** Per-path USB endpoint / interface numbers (discovered from USB descriptors) */
|
|
92
92
|
private deviceEndpoints: Map<string, DeviceEndpoints> = new Map();
|
|
93
93
|
|
|
@@ -152,7 +152,6 @@ export default class WebUsbTransport {
|
|
|
152
152
|
configureProtocolV2(signedData: any) {
|
|
153
153
|
this.messagesV2 = parseConfigure(signedData);
|
|
154
154
|
this.protocolV2Sessions.clear();
|
|
155
|
-
this.protocolV2ReadTimeouts.clear();
|
|
156
155
|
}
|
|
157
156
|
|
|
158
157
|
/**
|
|
@@ -271,6 +270,13 @@ export default class WebUsbTransport {
|
|
|
271
270
|
);
|
|
272
271
|
}
|
|
273
272
|
|
|
273
|
+
private createProtocolProbeTimeoutError(expected: ProtocolType, attempts: number) {
|
|
274
|
+
return ERRORS.TypedError(
|
|
275
|
+
HardwareErrorCode.RuntimeError,
|
|
276
|
+
`Protocol ${expected} probe timeout after ${attempts} attempts`
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
274
280
|
private createProtocolDetectionError() {
|
|
275
281
|
return ERRORS.TypedError(
|
|
276
282
|
HardwareErrorCode.RuntimeError,
|
|
@@ -293,10 +299,25 @@ export default class WebUsbTransport {
|
|
|
293
299
|
}
|
|
294
300
|
|
|
295
301
|
if (expectedProtocol === 'V2') {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
302
|
+
for (let attempt = 1; attempt <= EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS; attempt += 1) {
|
|
303
|
+
if (await this.probeProtocolV2(path)) {
|
|
304
|
+
this.deviceProtocol.set(path, 'V2');
|
|
305
|
+
return 'V2';
|
|
306
|
+
}
|
|
307
|
+
await this.resetConnectionAfterProbe(path);
|
|
308
|
+
if (attempt < EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS) {
|
|
309
|
+
this.Log?.debug(
|
|
310
|
+
`[WebUsbTransport] Protocol V2 probe timed out, retrying ${
|
|
311
|
+
attempt + 1
|
|
312
|
+
}/${EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS}`
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
this.deviceProtocol.delete(path);
|
|
317
|
+
throw this.createProtocolProbeTimeoutError(
|
|
318
|
+
expectedProtocol,
|
|
319
|
+
EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS
|
|
320
|
+
);
|
|
300
321
|
}
|
|
301
322
|
|
|
302
323
|
// Protocol must be actively probed after connection. Name, PID, and descriptors only
|
|
@@ -311,12 +332,10 @@ export default class WebUsbTransport {
|
|
|
311
332
|
this.deviceProtocol.set(path, protocol);
|
|
312
333
|
return protocol;
|
|
313
334
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
await this.resetConnectionAfterProbe(path);
|
|
319
|
-
}
|
|
335
|
+
// A timed-out WebUSB transferIn cannot be cancelled in place. Closing and
|
|
336
|
+
// reopening the device guarantees the next protocol probe cannot consume a
|
|
337
|
+
// late response from the previous protocol generation.
|
|
338
|
+
await this.resetConnectionAfterProbe(path);
|
|
320
339
|
}
|
|
321
340
|
|
|
322
341
|
this.deviceProtocol.delete(path);
|
|
@@ -635,7 +654,6 @@ export default class WebUsbTransport {
|
|
|
635
654
|
private async resetConnectionAfterProbe(path: string) {
|
|
636
655
|
this.protocolV2Assemblers.get(path)?.reset();
|
|
637
656
|
this.protocolV2Sessions.delete(path);
|
|
638
|
-
this.protocolV2ReadTimeouts.delete(path);
|
|
639
657
|
|
|
640
658
|
try {
|
|
641
659
|
const device = await this.findDevice(path);
|
|
@@ -658,7 +676,7 @@ export default class WebUsbTransport {
|
|
|
658
676
|
}
|
|
659
677
|
|
|
660
678
|
private async withProtocolReadTimeout<T>(
|
|
661
|
-
|
|
679
|
+
path: string,
|
|
662
680
|
promise: Promise<T>,
|
|
663
681
|
timeoutMs: number,
|
|
664
682
|
protocol: ProtocolType,
|
|
@@ -680,9 +698,16 @@ export default class WebUsbTransport {
|
|
|
680
698
|
return await Promise.race([
|
|
681
699
|
guardedPromise,
|
|
682
700
|
new Promise<never>((_, reject) => {
|
|
683
|
-
timer = setTimeout(() => {
|
|
701
|
+
timer = setTimeout(async () => {
|
|
684
702
|
timedOut = true;
|
|
685
703
|
onTimeout?.();
|
|
704
|
+
if (protocol === 'V1') {
|
|
705
|
+
try {
|
|
706
|
+
await this.resetConnectionAfterProbe(path);
|
|
707
|
+
} catch (error) {
|
|
708
|
+
this.Log.debug('[WebUsbTransport] reset after Protocol V1 timeout failed:', error);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
686
711
|
reject(new Error(`Protocol ${protocol} read timeout after ${timeoutMs}ms`));
|
|
687
712
|
}, timeoutMs);
|
|
688
713
|
}),
|
|
@@ -711,7 +736,7 @@ export default class WebUsbTransport {
|
|
|
711
736
|
}
|
|
712
737
|
|
|
713
738
|
return probeProtocolV2Helper({
|
|
714
|
-
call: (name, data, options) => this.callProtocolV2(path, name, data, options),
|
|
739
|
+
call: (name, data, options) => this.callProtocolV2(path, name, data, options, false),
|
|
715
740
|
timeoutMs: PROTOCOL_PROBE_TIMEOUT,
|
|
716
741
|
logger: this.Log,
|
|
717
742
|
logPrefix: 'ProtocolV2 WebUSB',
|
|
@@ -793,7 +818,8 @@ export default class WebUsbTransport {
|
|
|
793
818
|
path: string,
|
|
794
819
|
name: string,
|
|
795
820
|
data: Record<string, unknown>,
|
|
796
|
-
options?: TransportCallOptions
|
|
821
|
+
options?: TransportCallOptions,
|
|
822
|
+
resetOnError = true
|
|
797
823
|
) {
|
|
798
824
|
const protocolV1Messages = this.messages;
|
|
799
825
|
if (!this.messagesV2) {
|
|
@@ -821,32 +847,29 @@ export default class WebUsbTransport {
|
|
|
821
847
|
router: PROTOCOL_V2_CHANNEL_USB,
|
|
822
848
|
sequenceCursor,
|
|
823
849
|
writeFrame: (frame: Uint8Array) => this.transferOutOnce(path, frame),
|
|
824
|
-
readFrame:
|
|
850
|
+
readFrame: context => this.receiveProtocolV2Frame(path, context.timeoutMs),
|
|
825
851
|
logger: this.Log,
|
|
826
852
|
logPrefix: 'ProtocolV2 WebUSB',
|
|
827
853
|
createTimeoutError: (messageName: string, timeoutMs: number) =>
|
|
828
|
-
new
|
|
854
|
+
new ProtocolV2LinkError(
|
|
855
|
+
'response-timeout',
|
|
856
|
+
`Protocol V2 response timeout after ${timeoutMs}ms for ${messageName}`
|
|
857
|
+
),
|
|
829
858
|
});
|
|
830
859
|
this.protocolV2Sessions.set(path, session);
|
|
831
860
|
}
|
|
832
861
|
|
|
833
|
-
this.protocolV2ReadTimeouts.set(path, options?.timeoutMs);
|
|
834
|
-
this.protocolV2Assemblers.get(path)?.reset();
|
|
835
862
|
try {
|
|
836
863
|
return await session.call(name, data, options);
|
|
837
864
|
} catch (error) {
|
|
838
|
-
|
|
839
|
-
error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
840
|
-
if (message.includes('protocol v2 read timeout') || message.includes('response timeout')) {
|
|
865
|
+
if (resetOnError && (isProtocolV2LinkError(error) || this.isRetryablePacketIoError(error))) {
|
|
841
866
|
try {
|
|
842
867
|
await this.resetConnectionAfterProbe(path);
|
|
843
868
|
} catch (resetError) {
|
|
844
|
-
this.Log.debug('[WebUsbTransport] Protocol V2
|
|
869
|
+
this.Log.debug('[WebUsbTransport] Protocol V2 link reset failed:', resetError);
|
|
845
870
|
}
|
|
846
871
|
}
|
|
847
872
|
throw error;
|
|
848
|
-
} finally {
|
|
849
|
-
this.protocolV2ReadTimeouts.delete(path);
|
|
850
873
|
}
|
|
851
874
|
}
|
|
852
875
|
|
|
@@ -879,14 +902,7 @@ export default class WebUsbTransport {
|
|
|
879
902
|
dataView.buffer.slice(dataView.byteOffset, dataView.byteOffset + dataView.byteLength)
|
|
880
903
|
)
|
|
881
904
|
);
|
|
882
|
-
|
|
883
|
-
frame = assembler.push(bytes);
|
|
884
|
-
} catch (error) {
|
|
885
|
-
throw ERRORS.TypedError(
|
|
886
|
-
HardwareErrorCode.NetworkError,
|
|
887
|
-
error instanceof Error ? error.message : String(error)
|
|
888
|
-
);
|
|
889
|
-
}
|
|
905
|
+
frame = assembler.push(bytes);
|
|
890
906
|
}
|
|
891
907
|
return frame;
|
|
892
908
|
}
|
|
@@ -953,7 +969,6 @@ export default class WebUsbTransport {
|
|
|
953
969
|
this.protocolV2Assemblers.get(path)?.reset();
|
|
954
970
|
this.protocolV2Assemblers.delete(path);
|
|
955
971
|
this.protocolV2Sessions.delete(path);
|
|
956
|
-
this.protocolV2ReadTimeouts.delete(path);
|
|
957
972
|
this.deviceEndpoints.delete(path);
|
|
958
973
|
}
|
|
959
974
|
|