@onekeyfe/hd-transport-lowlevel 1.2.0-alpha.18 → 1.2.0-alpha.181

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.
@@ -68,12 +68,25 @@ const protocolV2Schema = {
68
68
  },
69
69
  },
70
70
  },
71
+ Failure: {
72
+ fields: {
73
+ code: {
74
+ type: 'uint32',
75
+ id: 1,
76
+ },
77
+ message: {
78
+ type: 'string',
79
+ id: 2,
80
+ },
81
+ },
82
+ },
71
83
  MessageType: {
72
84
  values: {
73
85
  MessageType_ProtocolInfoRequest: 60200,
74
86
  MessageType_ProtocolInfo: 60201,
75
87
  MessageType_Ping: 60206,
76
88
  MessageType_Success: 60207,
89
+ MessageType_Failure: 60208,
77
90
  },
78
91
  },
79
92
  },
@@ -122,6 +135,49 @@ const splitFrame = (frame, index) => [
122
135
  ];
123
136
 
124
137
  describe('LowlevelTransport protocol framing', () => {
138
+ test('falls back to Protocol V2 when a cached V1 hint is stale', async () => {
139
+ const plugin = createPlugin({ devices: [], responses: [] });
140
+ const lowlevel = configureTransport(plugin);
141
+ const events = [];
142
+ lowlevel.probeProtocolV1 = jest.fn().mockImplementation(() => {
143
+ events.push('probe-v1');
144
+ return Promise.resolve(false);
145
+ });
146
+ lowlevel.resetConnectionAfterProbe = jest.fn().mockImplementation(() => {
147
+ events.push('reset');
148
+ return Promise.resolve();
149
+ });
150
+ lowlevel.probeProtocolV2 = jest.fn().mockImplementation(() => {
151
+ events.push('probe-v2');
152
+ return Promise.resolve(true);
153
+ });
154
+
155
+ await expect(lowlevel.detectProtocol('pro-lowlevel', undefined, 'V1')).resolves.toBe('V2');
156
+
157
+ expect(events).toEqual(['probe-v1', 'reset', 'probe-v2']);
158
+ expect(lowlevel.getProtocolType('pro-lowlevel')).toBe('V2');
159
+ });
160
+
161
+ test('keeps active links when the Protocol V2 schema is configured repeatedly', () => {
162
+ const lowlevel = new LowlevelTransport();
163
+ const invalidateAllLinks = jest.fn().mockResolvedValue(undefined);
164
+ lowlevel.protocolV2Links.invalidateAllLinks = invalidateAllLinks;
165
+
166
+ lowlevel.configureProtocolV2(protocolV2Schema);
167
+ lowlevel.configureProtocolV2(protocolV2Schema);
168
+
169
+ expect(invalidateAllLinks).not.toHaveBeenCalled();
170
+
171
+ lowlevel.configureProtocolV2({
172
+ nested: {
173
+ ...protocolV2Schema.nested,
174
+ ExtraMessage: { fields: {} },
175
+ },
176
+ });
177
+
178
+ expect(invalidateAllLinks).toHaveBeenCalledWith('Protocol V2 schema reconfigured');
179
+ });
180
+
125
181
  test('keeps Protocol V1 raw notification chunks compatible', async () => {
126
182
  const responseChunks = ProtocolV1.encodeTransportPackets(schemas.protocolV1, 'Success', {
127
183
  message: 'ok',
@@ -142,6 +198,23 @@ describe('LowlevelTransport protocol framing', () => {
142
198
  });
143
199
  });
144
200
 
201
+ test('uses the Protocol V2 BLE writer with the lowlevel compatibility packet size', async () => {
202
+ const plugin = createPlugin({ devices: [], responses: [] });
203
+ const lowlevel = configureTransport(plugin);
204
+ const context = {
205
+ messageName: 'Ping',
206
+ timeoutMs: 1000,
207
+ highThroughput: false,
208
+ generation: 1,
209
+ signal: new AbortController().signal,
210
+ };
211
+
212
+ await lowlevel.writeProtocolV2Frame('pro2-id', new Uint8Array(130), context, jest.fn());
213
+
214
+ expect(plugin.send).toHaveBeenCalledTimes(3);
215
+ expect(plugin.send.mock.calls.map(([, hex]) => hex.length / 2)).toEqual([64, 64, 2]);
216
+ });
217
+
145
218
  test('rejects calls before protocol detection', async () => {
146
219
  const responseChunks = ProtocolV1.encodeTransportPackets(schemas.protocolV1, 'Success', {
147
220
  message: 'ok',
@@ -172,11 +245,15 @@ describe('LowlevelTransport protocol framing', () => {
172
245
  supported_messages: [60200, 60201, 60206, 60207],
173
246
  protobuf_definition: 'onekey-protocol-v2',
174
247
  },
175
- { router: PROTOCOL_V2_CHANNEL_BLE_UART }
248
+ { router: PROTOCOL_V2_CHANNEL_BLE_UART, seq: 2 }
176
249
  );
177
250
  const plugin = createPlugin({
178
251
  devices: [{ id: 'pro2-id', name: 'OneKey Pro 2', commType: 'ble' }],
179
- responses: [...splitFrame(probeResponse, 4), ...splitFrame(callResponse, 5)],
252
+ responses: [
253
+ new Error('Protocol V1 probe timed out'),
254
+ ...splitFrame(probeResponse, 4),
255
+ ...splitFrame(callResponse, 5),
256
+ ],
180
257
  });
181
258
  const lowlevel = configureTransport(plugin);
182
259
 
@@ -196,9 +273,9 @@ describe('LowlevelTransport protocol framing', () => {
196
273
  },
197
274
  });
198
275
  expect(plugin.send).toHaveBeenCalled();
199
- const sentSeqs = plugin.send.mock.calls.map(([, hex]) =>
200
- Number.parseInt(hex.slice(12, 14), 16)
201
- );
276
+ const sentSeqs = plugin.send.mock.calls
277
+ .map(([, hex]) => Number.parseInt(hex.slice(12, 14), 16))
278
+ .filter(seq => seq > 0);
202
279
  expect(sentSeqs).toEqual([1, 2]);
203
280
  });
204
281
 
@@ -222,7 +299,7 @@ describe('LowlevelTransport protocol framing', () => {
222
299
  expect(lowlevel.getProtocolType('unknown-pro2-id')).toBe('V2');
223
300
  });
224
301
 
225
- test('retains the Protocol V2 hint and sequence cursor across release and reacquire', async () => {
302
+ test('detects Protocol V2 again after release without a name-derived hint', async () => {
226
303
  const probeResponse = ProtocolV2.encodeFrame(
227
304
  schemas,
228
305
  'Success',
@@ -231,7 +308,12 @@ describe('LowlevelTransport protocol framing', () => {
231
308
  );
232
309
  const plugin = createPlugin({
233
310
  devices: [{ id: 'reconnect-pro2-id', name: 'OneKey Pro 2', commType: 'ble' }],
234
- responses: [bytesToHex(probeResponse), bytesToHex(probeResponse)],
311
+ responses: [
312
+ new Error('Protocol V1 probe timed out'),
313
+ bytesToHex(probeResponse),
314
+ new Error('Protocol V1 probe timed out'),
315
+ bytesToHex(probeResponse),
316
+ ],
235
317
  });
236
318
  const lowlevel = configureTransport(plugin);
237
319
 
@@ -246,22 +328,26 @@ describe('LowlevelTransport protocol framing', () => {
246
328
  protocolType: 'V2',
247
329
  });
248
330
 
249
- const sentSeqs = plugin.send.mock.calls.map(([, hex]) =>
250
- Number.parseInt(hex.slice(12, 14), 16)
251
- );
331
+ const sentSeqs = plugin.send.mock.calls
332
+ .map(([, hex]) => Number.parseInt(hex.slice(12, 14), 16))
333
+ .filter(seq => seq > 0);
252
334
  expect(sentSeqs).toEqual([1, 2]);
253
335
  });
254
336
 
255
337
  test('reuses the active generation when Core acquires the same BLE connection again', async () => {
256
- const probeResponse = ProtocolV2.encodeFrame(
257
- schemas,
258
- 'Success',
259
- { message: 'ok' },
260
- { router: PROTOCOL_V2_CHANNEL_BLE_UART }
338
+ const responses = [1, 2, 3, 4].map(seq =>
339
+ bytesToHex(
340
+ ProtocolV2.encodeFrame(
341
+ schemas,
342
+ 'Success',
343
+ { message: 'ok' },
344
+ { router: PROTOCOL_V2_CHANNEL_BLE_UART, seq }
345
+ )
346
+ )
261
347
  );
262
348
  const plugin = createPlugin({
263
349
  devices: [{ id: 'repeated-acquire-id', name: 'OneKey Pro 2', commType: 'ble' }],
264
- responses: [bytesToHex(probeResponse), bytesToHex(probeResponse)],
350
+ responses,
265
351
  });
266
352
  const lowlevel = configureTransport(plugin);
267
353
 
@@ -283,13 +369,19 @@ describe('LowlevelTransport protocol framing', () => {
283
369
  const sentSeqs = plugin.send.mock.calls.map(([, hex]) =>
284
370
  Number.parseInt(hex.slice(12, 14), 16)
285
371
  );
286
- expect(sentSeqs).toEqual([1, 2]);
372
+ expect(sentSeqs).toEqual([1, 2, 3, 4]);
287
373
  });
288
374
 
289
- test('trusts explicit Protocol V2 during bootloader reconnect without probing Ping', async () => {
375
+ test('actively probes explicit Protocol V2 during bootloader reconnect', async () => {
376
+ const probeResponse = ProtocolV2.encodeFrame(
377
+ schemas,
378
+ 'Success',
379
+ { message: 'ok' },
380
+ { router: PROTOCOL_V2_CHANNEL_BLE_UART }
381
+ );
290
382
  const plugin = createPlugin({
291
383
  devices: [{ id: 'bootloader-v2-id', name: 'OneKey Pro 2', commType: 'ble' }],
292
- responses: [],
384
+ responses: [bytesToHex(probeResponse)],
293
385
  });
294
386
  const lowlevel = configureTransport(plugin);
295
387
 
@@ -299,8 +391,35 @@ describe('LowlevelTransport protocol framing', () => {
299
391
  uuid: 'bootloader-v2-id',
300
392
  protocolType: 'V2',
301
393
  });
302
- expect(plugin.send).not.toHaveBeenCalled();
303
- expect(plugin.receive).not.toHaveBeenCalled();
394
+ expect(plugin.send).toHaveBeenCalledTimes(1);
395
+ expect(plugin.receive).toHaveBeenCalledTimes(1);
396
+ });
397
+
398
+ test('disconnects and clears a lowlevel connection when Protocol V2 reports link disabled', async () => {
399
+ const failureResponse = ProtocolV2.encodeFrame(
400
+ schemas,
401
+ 'Failure',
402
+ { code: 5, message: 'link disabled' },
403
+ { router: PROTOCOL_V2_CHANNEL_BLE_UART }
404
+ );
405
+ const plugin = createPlugin({
406
+ devices: [{ id: 'usb-owned-id', name: 'OneKey Pro 2', commType: 'ble' }],
407
+ responses: [bytesToHex(failureResponse)],
408
+ });
409
+ const lowlevel = configureTransport(plugin);
410
+
411
+ await expect(
412
+ lowlevel.acquire({ uuid: 'usb-owned-id', expectedProtocol: 'V2' })
413
+ ).rejects.toMatchObject({
414
+ name: 'ProtocolV2LinkDisabledError',
415
+ failureCode: 5,
416
+ firmwareMessage: 'link disabled',
417
+ });
418
+
419
+ expect(plugin.disconnect).toHaveBeenCalledWith('usb-owned-id');
420
+ expect(lowlevel.connectedDevices.has('usb-owned-id')).toBe(false);
421
+ expect(lowlevel.getProtocolType('usb-owned-id')).toBeUndefined();
422
+ expect(lowlevel.protocolV2Assemblers.has('usb-owned-id')).toBe(false);
304
423
  });
305
424
 
306
425
  test('resets the lowlevel connection before probing Protocol V2 after a V1 timeout', async () => {
@@ -342,11 +461,23 @@ describe('LowlevelTransport protocol framing', () => {
342
461
  });
343
462
 
344
463
  test('disconnects a tainted Protocol V2 link after a response timeout', async () => {
464
+ const probeResponse = ProtocolV2.encodeFrame(
465
+ schemas,
466
+ 'Success',
467
+ { message: 'ok' },
468
+ { router: PROTOCOL_V2_CHANNEL_BLE_UART }
469
+ );
345
470
  const plugin = createPlugin({
346
471
  devices: [{ id: 'timeout-v2-id', name: 'OneKey Pro 2', commType: 'ble' }],
347
- responses: [],
472
+ responses: [bytesToHex(probeResponse)],
473
+ });
474
+ let receiveCount = 0;
475
+ plugin.receive.mockImplementation(() => {
476
+ receiveCount += 1;
477
+ return receiveCount === 1
478
+ ? Promise.resolve(bytesToHex(probeResponse))
479
+ : new Promise(() => {});
348
480
  });
349
- plugin.receive.mockImplementation(() => new Promise(() => {}));
350
481
  const lowlevel = configureTransport(plugin);
351
482
 
352
483
  await lowlevel.acquire({ uuid: 'timeout-v2-id', expectedProtocol: 'V2' });
@@ -358,6 +489,28 @@ describe('LowlevelTransport protocol framing', () => {
358
489
  expect(plugin.disconnect).toHaveBeenCalledWith('timeout-v2-id');
359
490
  });
360
491
 
492
+ test('preserves an undefined business timeout outside explicit probes', async () => {
493
+ const plugin = createPlugin({
494
+ devices: [{ id: 'v2-id', name: 'OneKey Pro 2', commType: 'ble' }],
495
+ responses: [],
496
+ });
497
+ const lowlevel = configureTransport(plugin);
498
+ lowlevel.deviceProtocol.set('v2-id', 'V2');
499
+ const linkCall = jest
500
+ .spyOn(lowlevel.protocolV2Links, 'call')
501
+ .mockResolvedValue({ type: 'Success', message: {} });
502
+
503
+ await lowlevel.call('v2-id', 'Ping', { message: 'no-business-timeout' });
504
+
505
+ expect(linkCall).toHaveBeenCalledWith(
506
+ 'v2-id',
507
+ expect.any(Function),
508
+ 'Ping',
509
+ { message: 'no-business-timeout' },
510
+ undefined
511
+ );
512
+ });
513
+
361
514
  test('verifies expected Protocol V1 instead of trusting the requested protocol', async () => {
362
515
  const plugin = createPlugin({
363
516
  devices: [{ id: 'v2-id', name: 'Unknown BLE Device', commType: 'ble' }],
package/dist/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  import * as transport from '@onekeyfe/hd-transport';
2
- import transport__default, { ProtocolType, LowlevelTransportSharedPlugin, LowLevelDevice, TransportCallOptions } from '@onekeyfe/hd-transport';
2
+ import transport__default, { ProtocolType, LowlevelTransportSharedPlugin, TransportCallOptions } from '@onekeyfe/hd-transport';
3
3
  import EventEmitter from 'events';
4
4
 
5
5
  type LowLevelAcquireInput = {
6
6
  uuid: string;
7
7
  expectedProtocol?: ProtocolType;
8
+ protocolHint?: ProtocolType;
8
9
  };
9
10
 
10
11
  declare function shouldLogFirmwareUploadProgress({ percent, lastLoggedPercent, now, lastLoggedAt, }: {
@@ -29,18 +30,20 @@ declare class LowlevelTransport {
29
30
  private protocolV2Generations;
30
31
  private connectedDevices;
31
32
  private protocolV2Links;
33
+ private protocolV2SchemaConfiguration;
32
34
  getProtocolType(path: string): ProtocolType | undefined;
33
35
  init(logger: any, emitter: EventEmitter, plugin: LowlevelTransportSharedPlugin): void;
34
36
  configure(signedData: any): void;
35
37
  configureProtocolV2(signedData: any): void;
36
38
  listen(): void;
37
- enumerate(): Promise<LowLevelDevice[]>;
39
+ enumerate(): Promise<transport.LowLevelDevice[]>;
38
40
  acquire(input: LowLevelAcquireInput): Promise<{
39
41
  uuid: string;
40
42
  protocolType: ProtocolType;
41
43
  }>;
42
44
  release(uuid: string): Promise<boolean>;
43
45
  call(uuid: string, name: string, data: Record<string, unknown>, options?: TransportCallOptions): Promise<transport.MessageFromOneKey>;
46
+ post(uuid: string, name: string, data: Record<string, unknown>): Promise<void>;
44
47
  private callProtocolV1;
45
48
  private createProtocolTimeoutError;
46
49
  private createProtocolMismatchError;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,SAWN,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,YAAY,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EACV,cAAc,EACd,6BAA6B,EAC7B,YAAY,EACZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAWpD,wBAAgB,+BAA+B,CAAC,EAC9C,OAAO,EACP,iBAAiB,EACjB,GAAG,EACH,YAAY,GACb,EAAE;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;CACtB,WAMA;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM;;cAEpD;AAcD,MAAM,CAAC,OAAO,OAAO,iBAAiB;IACpC,SAAS,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAEnE,WAAW,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAErE,UAAU,UAAS;IAEnB,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB,MAAM,EAAE,6BAA6B,CAAuC;IAE5E,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAEnE,OAAO,CAAC,oBAAoB,CAAoD;IAEhF,OAAO,CAAC,qBAAqB,CAAkC;IAE/D,OAAO,CAAC,gBAAgB,CAA0B;IAElD,OAAO,CAAC,eAAe,CA6BpB;IAEH,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAIvD,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,6BAA6B;IAO9E,SAAS,CAAC,UAAU,EAAE,GAAG;IAMzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAOnC,MAAM;IAIA,SAAS;IAWT,OAAO,CAAC,KAAK,EAAE,oBAAoB;;;;IA6BnC,OAAO,CAAC,IAAI,EAAE,MAAM;IAgBpB,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YAsBlB,cAAc;IAmE5B,OAAO,CAAC,0BAA0B;IAOlC,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;IAOpC,OAAO,CAAC,kBAAkB;YAMZ,cAAc;YAsDd,yBAAyB;YAiCzB,eAAe;YAgBf,eAAe;YA6Bf,UAAU;YAUV,qBAAqB;YAmBrB,mBAAmB;YAqBnB,oBAAoB;YAOpB,cAAc;IA6B5B,OAAO,CAAC,uBAAuB;IAkC/B,OAAO,CAAC,2BAA2B;IAMnC,MAAM;CAGP"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,SAYN,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,YAAY,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EACV,6BAA6B,EAC7B,YAAY,EAEZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAUpD,wBAAgB,+BAA+B,CAAC,EAC9C,OAAO,EACP,iBAAiB,EACjB,GAAG,EACH,YAAY,GACb,EAAE;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;CACtB,WAMA;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM;;cAEpD;AAUD,MAAM,CAAC,OAAO,OAAO,iBAAiB;IACpC,SAAS,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAEnE,WAAW,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAErE,UAAU,UAAS;IAEnB,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB,MAAM,EAAE,6BAA6B,CAAuC;IAE5E,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAEnE,OAAO,CAAC,oBAAoB,CAAoD;IAEhF,OAAO,CAAC,qBAAqB,CAAkC;IAE/D,OAAO,CAAC,gBAAgB,CAA0B;IAElD,OAAO,CAAC,eAAe,CA6BpB;IAEH,OAAO,CAAC,6BAA6B,CAAqB;IAE1D,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAIvD,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,6BAA6B;IAO9E,SAAS,CAAC,UAAU,EAAE,GAAG;IAMzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAiBnC,MAAM;IAIA,SAAS;IAKT,OAAO,CAAC,KAAK,EAAE,oBAAoB;;;;IAmDnC,OAAO,CAAC,IAAI,EAAE,MAAM;IAepB,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;IAoB1B,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;YAatD,cAAc;IA6E5B,OAAO,CAAC,0BAA0B;IAOlC,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;IAOpC,OAAO,CAAC,kBAAkB;YAMZ,cAAc;YA2Cd,yBAAyB;YAiCzB,eAAe;YAgBf,eAAe;YA6Bf,UAAU;YAUV,qBAAqB;YAmBrB,mBAAmB;YAqBnB,oBAAoB;YAgBpB,cAAc;IAwB5B,OAAO,CAAC,uBAAuB;IAgC/B,OAAO,CAAC,2BAA2B;IAMnC,MAAM;CAGP"}
package/dist/index.js CHANGED
@@ -42,7 +42,6 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
42
42
  const { check, ProtocolV1, parseConfigure } = transport__default["default"];
43
43
  const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
44
44
  const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
45
- const LOWLEVEL_PROTOCOL_TIMEOUT_MS = 30000;
46
45
  const LOWLEVEL_PROTOCOL_V2_PACKET_LENGTH = 64;
47
46
  const FIRMWARE_UPLOAD_LOG_PERCENT_STEP = 5;
48
47
  const FIRMWARE_UPLOAD_LOG_INTERVAL_MS = 10000;
@@ -54,9 +53,6 @@ function shouldLogFirmwareUploadProgress({ percent, lastLoggedPercent, now, last
54
53
  function getProtocolV1SendOptions(name) {
55
54
  return name === 'FirmwareUpload' ? { withoutResponse: false } : undefined;
56
55
  }
57
- function inferProtocolHintFromDeviceName(name) {
58
- return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
59
- }
60
56
  function isProtocolV1TransportChunk(data) {
61
57
  return data.length >= 9 && data[0] === 0x3f && data[1] === 0x23 && data[2] === 0x23;
62
58
  }
@@ -118,27 +114,29 @@ class LowlevelTransport {
118
114
  this._messages = messages;
119
115
  }
120
116
  configureProtocolV2(signedData) {
117
+ const configuration = typeof signedData === 'string' ? signedData : JSON.stringify(signedData);
118
+ if (this.protocolV2SchemaConfiguration === configuration) {
119
+ return;
120
+ }
121
+ const isReconfiguration = this.protocolV2SchemaConfiguration !== undefined;
121
122
  this._messagesV2 = parseConfigure(signedData);
122
- this.protocolV2Links
123
- .invalidateAllLinks('Protocol V2 schema reconfigured')
124
- .catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('Protocol V2 schema link cleanup failed:', error); });
123
+ this.protocolV2SchemaConfiguration = configuration;
124
+ if (isReconfiguration) {
125
+ this.protocolV2Links
126
+ .invalidateAllLinks('Protocol V2 schema reconfigured')
127
+ .catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('Protocol V2 schema link cleanup failed:', error); });
128
+ }
125
129
  }
126
130
  listen() {
127
131
  }
128
132
  enumerate() {
129
133
  return __awaiter(this, void 0, void 0, function* () {
130
134
  const devices = yield this.plugin.enumerate();
131
- return devices.map((device) => {
132
- const protocolHint = inferProtocolHintFromDeviceName(device.name);
133
- if (protocolHint) {
134
- this.deviceProtocolHints.set(device.id, protocolHint);
135
- }
136
- return device;
137
- });
135
+ return devices;
138
136
  });
139
137
  }
140
138
  acquire(input) {
141
- var _a;
139
+ var _a, _b, _c, _d;
142
140
  return __awaiter(this, void 0, void 0, function* () {
143
141
  const alreadyConnected = this.connectedDevices.has(input.uuid);
144
142
  try {
@@ -153,12 +151,35 @@ class LowlevelTransport {
153
151
  this.Log.debug('lowlelvel transport connect error: ', error);
154
152
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.LowlevelTrasnportConnectError, (_a = error.message) !== null && _a !== void 0 ? _a : error);
155
153
  }
156
- this.protocolV2Assemblers.set(input.uuid, new transport.ProtocolV2FrameAssembler());
154
+ this.protocolV2Assemblers.set(input.uuid, new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES));
157
155
  const protocolHint = input.expectedProtocol
158
156
  ? undefined
159
- : this.deviceProtocolHints.get(input.uuid);
160
- const protocolType = yield this.detectProtocol(input.uuid, input.expectedProtocol, protocolHint);
161
- return { uuid: input.uuid, protocolType };
157
+ : (_b = input.protocolHint) !== null && _b !== void 0 ? _b : this.deviceProtocolHints.get(input.uuid);
158
+ try {
159
+ const protocolType = yield this.detectProtocol(input.uuid, input.expectedProtocol, protocolHint);
160
+ return { uuid: input.uuid, protocolType };
161
+ }
162
+ catch (error) {
163
+ try {
164
+ yield this.protocolV2Links.invalidateLink(input.uuid, 'Lowlevel transport acquire failed');
165
+ }
166
+ catch (cleanupError) {
167
+ (_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug('[LowlevelTransport] acquire link cleanup failed:', cleanupError);
168
+ }
169
+ try {
170
+ yield this.plugin.disconnect(input.uuid);
171
+ }
172
+ catch (cleanupError) {
173
+ (_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug('[LowlevelTransport] acquire disconnect failed:', cleanupError);
174
+ }
175
+ finally {
176
+ this.connectedDevices.delete(input.uuid);
177
+ this.deviceProtocol.delete(input.uuid);
178
+ this.protocolV2Assemblers.delete(input.uuid);
179
+ this.advanceProtocolV2Generation(input.uuid);
180
+ }
181
+ throw error;
182
+ }
162
183
  });
163
184
  }
164
185
  release(uuid) {
@@ -186,13 +207,21 @@ class LowlevelTransport {
186
207
  if (!protocol) {
187
208
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${uuid}`);
188
209
  }
189
- this.Log.debug('transport call', { name, protocol });
190
210
  if (protocol === 'V2') {
191
211
  return this.callProtocolV2(uuid, name, data, options);
192
212
  }
193
213
  return this.callProtocolV1(uuid, name, data, options);
194
214
  });
195
215
  }
216
+ post(uuid, name, data) {
217
+ return __awaiter(this, void 0, void 0, function* () {
218
+ if (this.getProtocolType(uuid) === 'V2') {
219
+ yield this.protocolV2Links.sendFlowControl(uuid, () => this.createProtocolV2Adapter(uuid), name, data);
220
+ return;
221
+ }
222
+ yield this.callProtocolV1(uuid, name, data);
223
+ });
224
+ }
196
225
  callProtocolV1(uuid, name, data, options) {
197
226
  var _a;
198
227
  return __awaiter(this, void 0, void 0, function* () {
@@ -242,6 +271,15 @@ class LowlevelTransport {
242
271
  return check.call(jsonData);
243
272
  }
244
273
  catch (e) {
274
+ if ((e === null || e === void 0 ? void 0 : e.errorCode) === hdShared.HardwareErrorCode.BleTimeoutError &&
275
+ (options === null || options === void 0 ? void 0 : options.timeoutMs) !== PROTOCOL_PROBE_TIMEOUT_MS) {
276
+ try {
277
+ yield this.resetConnectionAfterProbe(uuid, 'V1');
278
+ }
279
+ catch (resetError) {
280
+ this.Log.debug('[LowlevelTransport] reset after Protocol V1 timeout failed:', resetError);
281
+ }
282
+ }
245
283
  if (name === 'Initialize' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS) {
246
284
  this.Log.debug('[LowlevelTransport] Protocol V1 Initialize probe call failed:', e);
247
285
  }
@@ -267,12 +305,15 @@ class LowlevelTransport {
267
305
  }
268
306
  }
269
307
  detectProtocol(uuid, expectedProtocol, protocolHint) {
270
- var _a, _b, _c, _d, _e, _f;
308
+ var _a, _b, _c;
271
309
  return __awaiter(this, void 0, void 0, function* () {
272
310
  if (expectedProtocol === 'V2') {
273
- this.deviceProtocol.set(uuid, 'V2');
274
- (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V2 (expected)`);
275
- return 'V2';
311
+ if (yield this.probeProtocolV2(uuid)) {
312
+ this.deviceProtocol.set(uuid, 'V2');
313
+ (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V2 (expected)`);
314
+ return 'V2';
315
+ }
316
+ throw this.createProtocolMismatchError(expectedProtocol);
276
317
  }
277
318
  if (expectedProtocol === 'V1') {
278
319
  if (yield this.probeProtocolV1(uuid)) {
@@ -282,28 +323,17 @@ class LowlevelTransport {
282
323
  }
283
324
  throw this.createProtocolMismatchError(expectedProtocol);
284
325
  }
285
- if (protocolHint === 'V2' && (yield this.probeProtocolV2(uuid))) {
286
- this.deviceProtocol.set(uuid, 'V2');
287
- (_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V2 (hint)`);
288
- return 'V2';
289
- }
290
- const cachedProtocol = this.deviceProtocol.get(uuid);
291
- if (cachedProtocol === 'V2' && (yield this.probeProtocolV2(uuid))) {
292
- this.deviceProtocol.set(uuid, 'V2');
293
- (_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V2 (cached)`);
294
- return 'V2';
295
- }
296
- const protocolV1Detected = yield this.probeProtocolV1(uuid);
297
- if (protocolV1Detected) {
298
- this.deviceProtocol.set(uuid, 'V1');
299
- (_e = this.Log) === null || _e === void 0 ? void 0 : _e.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V1`);
300
- return 'V1';
301
- }
302
- yield this.resetConnectionAfterProbe(uuid, 'V1');
303
- if (yield this.probeProtocolV2(uuid)) {
304
- this.deviceProtocol.set(uuid, 'V2');
305
- (_f = this.Log) === null || _f === void 0 ? void 0 : _f.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V2`);
306
- return 'V2';
326
+ const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
327
+ for (const [index, protocol] of probeOrder.entries()) {
328
+ if (index > 0) {
329
+ yield this.resetConnectionAfterProbe(uuid, probeOrder[index - 1]);
330
+ }
331
+ const detected = protocol === 'V1' ? yield this.probeProtocolV1(uuid) : yield this.probeProtocolV2(uuid);
332
+ if (detected) {
333
+ this.deviceProtocol.set(uuid, protocol);
334
+ (_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> ${protocol}`);
335
+ return protocol;
336
+ }
307
337
  }
308
338
  this.deviceProtocol.delete(uuid);
309
339
  throw this.createProtocolDetectionError();
@@ -411,7 +441,7 @@ class LowlevelTransport {
411
441
  return __awaiter(this, void 0, void 0, function* () {
412
442
  let assembler = this.protocolV2Assemblers.get(uuid);
413
443
  if (!assembler) {
414
- assembler = new transport.ProtocolV2FrameAssembler();
444
+ assembler = new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES);
415
445
  this.protocolV2Assemblers.set(uuid, assembler);
416
446
  }
417
447
  const queuedFrame = assembler.push(new Uint8Array(0));
@@ -428,23 +458,25 @@ class LowlevelTransport {
428
458
  return frame;
429
459
  });
430
460
  }
431
- writeProtocolV2Frame(uuid, frame) {
461
+ writeProtocolV2Frame(uuid, frame, context, assertCurrentGeneration) {
432
462
  return __awaiter(this, void 0, void 0, function* () {
433
- for (let offset = 0; offset < frame.length; offset += LOWLEVEL_PROTOCOL_V2_PACKET_LENGTH) {
434
- const chunk = frame.slice(offset, offset + LOWLEVEL_PROTOCOL_V2_PACKET_LENGTH);
435
- yield this.plugin.send(uuid, transport.bytesToHex(chunk));
436
- }
463
+ yield transport.writeProtocolV2BleFrame({
464
+ frame,
465
+ packetCapacity: LOWLEVEL_PROTOCOL_V2_PACKET_LENGTH,
466
+ assertActive: assertCurrentGeneration,
467
+ signal: context.signal,
468
+ abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
469
+ writePacket: packet => this.plugin.send(uuid, transport.bytesToHex(packet)),
470
+ });
437
471
  });
438
472
  }
439
473
  callProtocolV2(uuid, name, data, options) {
440
- var _a;
441
474
  return __awaiter(this, void 0, void 0, function* () {
442
475
  if (!this._messages || !this._messagesV2) {
443
476
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
444
477
  }
445
- const timeoutMs = (_a = options === null || options === void 0 ? void 0 : options.timeoutMs) !== null && _a !== void 0 ? _a : LOWLEVEL_PROTOCOL_TIMEOUT_MS;
446
478
  try {
447
- return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, Object.assign(Object.assign({}, options), { timeoutMs }));
479
+ return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, options);
448
480
  }
449
481
  catch (e) {
450
482
  this.Log.error('lowlevel Protocol V2 call error: ', e);
@@ -469,10 +501,7 @@ class LowlevelTransport {
469
501
  assertCurrentGeneration();
470
502
  (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
471
503
  },
472
- writeFrame: (frame) => {
473
- assertCurrentGeneration();
474
- return this.writeProtocolV2Frame(uuid, frame);
475
- },
504
+ writeFrame: (frame, context) => this.writeProtocolV2Frame(uuid, frame, context, assertCurrentGeneration),
476
505
  readFrame: (context) => {
477
506
  assertCurrentGeneration();
478
507
  return this.readProtocolV2Frame(uuid, context.timeoutMs, context.messageName);
package/dist/types.d.ts CHANGED
@@ -2,5 +2,6 @@ import type { ProtocolType } from '@onekeyfe/hd-transport';
2
2
  export type LowLevelAcquireInput = {
3
3
  uuid: string;
4
4
  expectedProtocol?: ProtocolType;
5
+ protocolHint?: ProtocolType;
5
6
  };
6
7
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,YAAY,CAAC;CACjC,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,YAAY,CAAC;IAChC,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-lowlevel",
3
- "version": "1.2.0-alpha.18",
3
+ "version": "1.2.0-alpha.181",
4
4
  "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -20,8 +20,8 @@
20
20
  "lint:fix": "eslint . --fix"
21
21
  },
22
22
  "dependencies": {
23
- "@onekeyfe/hd-shared": "1.2.0-alpha.18",
24
- "@onekeyfe/hd-transport": "1.2.0-alpha.18"
23
+ "@onekeyfe/hd-shared": "1.2.0-alpha.181",
24
+ "@onekeyfe/hd-transport": "1.2.0-alpha.181"
25
25
  },
26
- "gitHead": "bdbc5bf74e48ca90f5c56f3079b2b974e61f7aa4"
26
+ "gitHead": "cb79652ebe130ceb01490195aa8903715f267e36"
27
27
  }
package/src/index.ts CHANGED
@@ -10,13 +10,14 @@ import transport, {
10
10
  hexToBytes,
11
11
  probeProtocolV2 as probeProtocolV2Helper,
12
12
  withProtocolTimeout,
13
+ writeProtocolV2BleFrame,
13
14
  } from '@onekeyfe/hd-transport';
14
15
 
15
16
  import type EventEmitter from 'events';
16
17
  import type {
17
- LowLevelDevice,
18
18
  LowlevelTransportSharedPlugin,
19
19
  ProtocolType,
20
+ ProtocolV2CallContext,
20
21
  TransportCallOptions,
21
22
  } from '@onekeyfe/hd-transport';
22
23
  import type { LowLevelAcquireInput } from './types';
@@ -25,7 +26,6 @@ const { check, ProtocolV1, parseConfigure } = transport;
25
26
 
26
27
  const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
27
28
  const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
28
- const LOWLEVEL_PROTOCOL_TIMEOUT_MS = 30_000;
29
29
  const LOWLEVEL_PROTOCOL_V2_PACKET_LENGTH = 64;
30
30
  const FIRMWARE_UPLOAD_LOG_PERCENT_STEP = 5;
31
31
  const FIRMWARE_UPLOAD_LOG_INTERVAL_MS = 10_000;
@@ -52,10 +52,6 @@ export function getProtocolV1SendOptions(name: string) {
52
52
  return name === 'FirmwareUpload' ? { withoutResponse: false } : undefined;
53
53
  }
54
54
 
55
- function inferProtocolHintFromDeviceName(name?: string | null): ProtocolType | undefined {
56
- return /\bpro\s*2\b/i.test(name ?? '') ? 'V2' : undefined;
57
- }
58
-
59
55
  function isProtocolV1TransportChunk(data: Uint8Array) {
60
56
  return data.length >= 9 && data[0] === 0x3f && data[1] === 0x23 && data[2] === 0x23;
61
57
  }
@@ -118,6 +114,8 @@ export default class LowlevelTransport {
118
114
  },
119
115
  });
120
116
 
117
+ private protocolV2SchemaConfiguration: string | undefined;
118
+
121
119
  getProtocolType(path: string): ProtocolType | undefined {
122
120
  return this.deviceProtocol.get(path);
123
121
  }
@@ -136,10 +134,20 @@ export default class LowlevelTransport {
136
134
  }
137
135
 
138
136
  configureProtocolV2(signedData: any) {
137
+ const configuration = typeof signedData === 'string' ? signedData : JSON.stringify(signedData);
138
+ if (this.protocolV2SchemaConfiguration === configuration) {
139
+ return;
140
+ }
141
+
142
+ const isReconfiguration = this.protocolV2SchemaConfiguration !== undefined;
139
143
  this._messagesV2 = parseConfigure(signedData);
140
- this.protocolV2Links
141
- .invalidateAllLinks('Protocol V2 schema reconfigured')
142
- .catch(error => this.Log?.debug('Protocol V2 schema link cleanup failed:', error));
144
+ this.protocolV2SchemaConfiguration = configuration;
145
+
146
+ if (isReconfiguration) {
147
+ this.protocolV2Links
148
+ .invalidateAllLinks('Protocol V2 schema reconfigured')
149
+ .catch(error => this.Log?.debug('Protocol V2 schema link cleanup failed:', error));
150
+ }
143
151
  }
144
152
 
145
153
  listen() {
@@ -148,13 +156,7 @@ export default class LowlevelTransport {
148
156
 
149
157
  async enumerate() {
150
158
  const devices = await this.plugin.enumerate();
151
- return devices.map((device: LowLevelDevice) => {
152
- const protocolHint = inferProtocolHintFromDeviceName(device.name);
153
- if (protocolHint) {
154
- this.deviceProtocolHints.set(device.id, protocolHint);
155
- }
156
- return device;
157
- });
159
+ return devices;
158
160
  }
159
161
 
160
162
  async acquire(input: LowLevelAcquireInput) {
@@ -174,16 +176,38 @@ export default class LowlevelTransport {
174
176
  );
175
177
  }
176
178
 
177
- this.protocolV2Assemblers.set(input.uuid, new ProtocolV2FrameAssembler());
178
- const protocolHint = input.expectedProtocol
179
- ? undefined
180
- : this.deviceProtocolHints.get(input.uuid);
181
- const protocolType = await this.detectProtocol(
179
+ this.protocolV2Assemblers.set(
182
180
  input.uuid,
183
- input.expectedProtocol,
184
- protocolHint
181
+ new ProtocolV2FrameAssembler(PROTOCOL_V2_BLE_FRAME_MAX_BYTES)
185
182
  );
186
- return { uuid: input.uuid, protocolType };
183
+ const protocolHint = input.expectedProtocol
184
+ ? undefined
185
+ : input.protocolHint ?? this.deviceProtocolHints.get(input.uuid);
186
+ try {
187
+ const protocolType = await this.detectProtocol(
188
+ input.uuid,
189
+ input.expectedProtocol,
190
+ protocolHint
191
+ );
192
+ return { uuid: input.uuid, protocolType };
193
+ } catch (error) {
194
+ try {
195
+ await this.protocolV2Links.invalidateLink(input.uuid, 'Lowlevel transport acquire failed');
196
+ } catch (cleanupError) {
197
+ this.Log?.debug('[LowlevelTransport] acquire link cleanup failed:', cleanupError);
198
+ }
199
+ try {
200
+ await this.plugin.disconnect(input.uuid);
201
+ } catch (cleanupError) {
202
+ this.Log?.debug('[LowlevelTransport] acquire disconnect failed:', cleanupError);
203
+ } finally {
204
+ this.connectedDevices.delete(input.uuid);
205
+ this.deviceProtocol.delete(input.uuid);
206
+ this.protocolV2Assemblers.delete(input.uuid);
207
+ this.advanceProtocolV2Generation(input.uuid);
208
+ }
209
+ throw error;
210
+ }
187
211
  }
188
212
 
189
213
  async release(uuid: string) {
@@ -192,8 +216,7 @@ export default class LowlevelTransport {
192
216
  await this.plugin.disconnect(uuid);
193
217
  this.connectedDevices.delete(uuid);
194
218
  this.deviceProtocol.delete(uuid);
195
- // 设备名称推断出的协议提示不依赖当前连接,保留它可以让快速重连
196
- // 直接执行 Protocol V2 探测,避免先发送一次无意义的 V1 Initialize。
219
+ // Confirmed protocol stays on the device endpoint; BLE names are not a probe hint.
197
220
  this.protocolV2Assemblers.delete(uuid);
198
221
  return true;
199
222
  } catch (error) {
@@ -219,8 +242,6 @@ export default class LowlevelTransport {
219
242
  `Device protocol has not been detected for ${uuid}`
220
243
  );
221
244
  }
222
- this.Log.debug('transport call', { name, protocol });
223
-
224
245
  if (protocol === 'V2') {
225
246
  return this.callProtocolV2(uuid, name, data, options);
226
247
  }
@@ -228,6 +249,19 @@ export default class LowlevelTransport {
228
249
  return this.callProtocolV1(uuid, name, data, options);
229
250
  }
230
251
 
252
+ async post(uuid: string, name: string, data: Record<string, unknown>) {
253
+ if (this.getProtocolType(uuid) === 'V2') {
254
+ await this.protocolV2Links.sendFlowControl(
255
+ uuid,
256
+ () => this.createProtocolV2Adapter(uuid),
257
+ name,
258
+ data
259
+ );
260
+ return;
261
+ }
262
+ await this.callProtocolV1(uuid, name, data);
263
+ }
264
+
231
265
  private async callProtocolV1(
232
266
  uuid: string,
233
267
  name: string,
@@ -286,6 +320,16 @@ export default class LowlevelTransport {
286
320
  const jsonData = ProtocolV1.decodeMessage(messages, response);
287
321
  return check.call(jsonData);
288
322
  } catch (e) {
323
+ if (
324
+ e?.errorCode === HardwareErrorCode.BleTimeoutError &&
325
+ options?.timeoutMs !== PROTOCOL_PROBE_TIMEOUT_MS
326
+ ) {
327
+ try {
328
+ await this.resetConnectionAfterProbe(uuid, 'V1');
329
+ } catch (resetError) {
330
+ this.Log.debug('[LowlevelTransport] reset after Protocol V1 timeout failed:', resetError);
331
+ }
332
+ }
289
333
  if (name === 'Initialize' && options?.timeoutMs === PROTOCOL_PROBE_TIMEOUT_MS) {
290
334
  this.Log.debug('[LowlevelTransport] Protocol V1 Initialize probe call failed:', e);
291
335
  } else {
@@ -328,12 +372,12 @@ export default class LowlevelTransport {
328
372
  protocolHint?: ProtocolType
329
373
  ): Promise<ProtocolType> {
330
374
  if (expectedProtocol === 'V2') {
331
- // 固件升级重启后的 bootloader 不保证响应 Ping。上层显式传入 V2
332
- // 表示协议已经在重启前确认,这里与 RN/Electron BLE 保持一致,
333
- // 直接建立 V2 Link,首个业务命令负责验证链路是否可用。
334
- this.deviceProtocol.set(uuid, 'V2');
335
- this.Log?.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V2 (expected)`);
336
- return 'V2';
375
+ if (await this.probeProtocolV2(uuid)) {
376
+ this.deviceProtocol.set(uuid, 'V2');
377
+ this.Log?.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V2 (expected)`);
378
+ return 'V2';
379
+ }
380
+ throw this.createProtocolMismatchError(expectedProtocol);
337
381
  }
338
382
 
339
383
  if (expectedProtocol === 'V1') {
@@ -345,31 +389,20 @@ export default class LowlevelTransport {
345
389
  throw this.createProtocolMismatchError(expectedProtocol);
346
390
  }
347
391
 
348
- if (protocolHint === 'V2' && (await this.probeProtocolV2(uuid))) {
349
- this.deviceProtocol.set(uuid, 'V2');
350
- this.Log?.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V2 (hint)`);
351
- return 'V2';
352
- }
353
-
354
- const cachedProtocol = this.deviceProtocol.get(uuid);
355
- if (cachedProtocol === 'V2' && (await this.probeProtocolV2(uuid))) {
356
- this.deviceProtocol.set(uuid, 'V2');
357
- this.Log?.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V2 (cached)`);
358
- return 'V2';
359
- }
360
-
361
- const protocolV1Detected = await this.probeProtocolV1(uuid);
362
- if (protocolV1Detected) {
363
- this.deviceProtocol.set(uuid, 'V1');
364
- this.Log?.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V1`);
365
- return 'V1';
366
- }
392
+ const probeOrder: ProtocolType[] =
393
+ protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
367
394
 
368
- await this.resetConnectionAfterProbe(uuid, 'V1');
369
- if (await this.probeProtocolV2(uuid)) {
370
- this.deviceProtocol.set(uuid, 'V2');
371
- this.Log?.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> V2`);
372
- return 'V2';
395
+ for (const [index, protocol] of probeOrder.entries()) {
396
+ if (index > 0) {
397
+ await this.resetConnectionAfterProbe(uuid, probeOrder[index - 1]);
398
+ }
399
+ const detected =
400
+ protocol === 'V1' ? await this.probeProtocolV1(uuid) : await this.probeProtocolV2(uuid);
401
+ if (detected) {
402
+ this.deviceProtocol.set(uuid, protocol);
403
+ this.Log?.debug(`[LowlevelTransport] detectProtocol: uuid=${uuid} -> ${protocol}`);
404
+ return protocol;
405
+ }
373
406
  }
374
407
 
375
408
  this.deviceProtocol.delete(uuid);
@@ -486,7 +519,7 @@ export default class LowlevelTransport {
486
519
  private async readProtocolV2Frame(uuid: string, timeoutMs?: number, commandName = 'ProtocolV2') {
487
520
  let assembler = this.protocolV2Assemblers.get(uuid);
488
521
  if (!assembler) {
489
- assembler = new ProtocolV2FrameAssembler();
522
+ assembler = new ProtocolV2FrameAssembler(PROTOCOL_V2_BLE_FRAME_MAX_BYTES);
490
523
  this.protocolV2Assemblers.set(uuid, assembler);
491
524
  }
492
525
 
@@ -504,11 +537,20 @@ export default class LowlevelTransport {
504
537
  return frame;
505
538
  }
506
539
 
507
- private async writeProtocolV2Frame(uuid: string, frame: Uint8Array) {
508
- for (let offset = 0; offset < frame.length; offset += LOWLEVEL_PROTOCOL_V2_PACKET_LENGTH) {
509
- const chunk = frame.slice(offset, offset + LOWLEVEL_PROTOCOL_V2_PACKET_LENGTH);
510
- await this.plugin.send(uuid, bytesToHex(chunk));
511
- }
540
+ private async writeProtocolV2Frame(
541
+ uuid: string,
542
+ frame: Uint8Array,
543
+ context: ProtocolV2CallContext,
544
+ assertCurrentGeneration: () => void
545
+ ) {
546
+ await writeProtocolV2BleFrame({
547
+ frame,
548
+ packetCapacity: LOWLEVEL_PROTOCOL_V2_PACKET_LENGTH,
549
+ assertActive: assertCurrentGeneration,
550
+ signal: context.signal,
551
+ abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
552
+ writePacket: packet => this.plugin.send(uuid, bytesToHex(packet)),
553
+ });
512
554
  }
513
555
 
514
556
  private async callProtocolV2(
@@ -521,18 +563,13 @@ export default class LowlevelTransport {
521
563
  throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
522
564
  }
523
565
 
524
- const timeoutMs = options?.timeoutMs ?? LOWLEVEL_PROTOCOL_TIMEOUT_MS;
525
-
526
566
  try {
527
567
  return await this.protocolV2Links.call(
528
568
  uuid,
529
569
  () => this.createProtocolV2Adapter(uuid),
530
570
  name,
531
571
  data,
532
- {
533
- ...options,
534
- timeoutMs,
535
- }
572
+ options
536
573
  );
537
574
  } catch (e) {
538
575
  this.Log.error('lowlevel Protocol V2 call error: ', e);
@@ -556,10 +593,8 @@ export default class LowlevelTransport {
556
593
  assertCurrentGeneration();
557
594
  this.protocolV2Assemblers.get(uuid)?.reset();
558
595
  },
559
- writeFrame: (frame: Uint8Array) => {
560
- assertCurrentGeneration();
561
- return this.writeProtocolV2Frame(uuid, frame);
562
- },
596
+ writeFrame: (frame: Uint8Array, context: ProtocolV2CallContext) =>
597
+ this.writeProtocolV2Frame(uuid, frame, context, assertCurrentGeneration),
563
598
  readFrame: (context: { messageName: string; timeoutMs?: number }) => {
564
599
  assertCurrentGeneration();
565
600
  return this.readProtocolV2Frame(uuid, context.timeoutMs, context.messageName);
package/src/types.ts CHANGED
@@ -3,4 +3,5 @@ import type { ProtocolType } from '@onekeyfe/hd-transport';
3
3
  export type LowLevelAcquireInput = {
4
4
  uuid: string;
5
5
  expectedProtocol?: ProtocolType;
6
+ protocolHint?: ProtocolType;
6
7
  };