@onekeyfe/hd-core 1.2.0-alpha.23 → 1.2.0-alpha.24

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.
Files changed (131) hide show
  1. package/__tests__/DeviceCommands.test.ts +35 -0
  2. package/__tests__/connectSettings.test.ts +5 -47
  3. package/__tests__/device-lifecycle-events.test.ts +59 -0
  4. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +30 -39
  5. package/__tests__/open-wallet-session.test.ts +192 -129
  6. package/__tests__/protocol-v2-ping.test.ts +28 -0
  7. package/__tests__/protocol-v2.test.ts +338 -376
  8. package/__tests__/public-pro2-api-boundary.test.ts +22 -0
  9. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  10. package/dist/api/FirmwareUpdateV2.d.ts +0 -11
  11. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  12. package/dist/api/FirmwareUpdateV3.d.ts +3 -7
  13. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  14. package/dist/api/FirmwareUpdateV4.d.ts +5 -21
  15. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  16. package/dist/api/OpenWalletSession.d.ts +1 -8
  17. package/dist/api/OpenWalletSession.d.ts.map +1 -1
  18. package/dist/api/device/DeviceUpdateBootloader.d.ts +1 -5
  19. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  20. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +0 -7
  21. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  22. package/dist/api/firmware/getBinary.d.ts +0 -7
  23. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  24. package/dist/api/firmware/updateBootloader.d.ts +0 -2
  25. package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
  26. package/dist/api/firmware/uploadFirmware.d.ts +1 -9
  27. package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
  28. package/dist/api/index.d.ts +1 -0
  29. package/dist/api/index.d.ts.map +1 -1
  30. package/dist/api/protocol-v2/Ping.d.ts +2 -0
  31. package/dist/api/protocol-v2/Ping.d.ts.map +1 -1
  32. package/dist/api/stellar/StellarSignTransaction.d.ts +1 -1
  33. package/dist/core/index.d.ts.map +1 -1
  34. package/dist/data-manager/DataManager.d.ts +0 -1
  35. package/dist/data-manager/DataManager.d.ts.map +1 -1
  36. package/dist/data-manager/connectSettings.d.ts.map +1 -1
  37. package/dist/device/Device.d.ts.map +1 -1
  38. package/dist/device/DeviceCommands.d.ts.map +1 -1
  39. package/dist/index.d.ts +89 -273
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +783 -2443
  42. package/dist/inject.d.ts.map +1 -1
  43. package/dist/lowLevelInject.d.ts.map +1 -1
  44. package/dist/protocols/protocol-v2/unlockRetry.d.ts +2 -3
  45. package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +1 -1
  46. package/dist/protocols/protocol-v2/walletSession.d.ts +12 -0
  47. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  48. package/dist/topLevelInject.d.ts.map +1 -1
  49. package/dist/types/api/checkAllFirmwareRelease.d.ts +0 -3
  50. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  51. package/dist/types/api/deviceUpdateBootloader.d.ts +0 -6
  52. package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
  53. package/dist/types/api/export.d.ts +1 -4
  54. package/dist/types/api/export.d.ts.map +1 -1
  55. package/dist/types/api/firmwareUpdate.d.ts +0 -69
  56. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  57. package/dist/types/api/index.d.ts +4 -10
  58. package/dist/types/api/index.d.ts.map +1 -1
  59. package/dist/types/api/openWalletSession.d.ts +0 -12
  60. package/dist/types/api/openWalletSession.d.ts.map +1 -1
  61. package/dist/types/api/protocolV2.d.ts +4 -0
  62. package/dist/types/api/protocolV2.d.ts.map +1 -1
  63. package/dist/types/settings.d.ts +0 -13
  64. package/dist/types/settings.d.ts.map +1 -1
  65. package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
  66. package/dist/utils/patch.d.ts +1 -1
  67. package/dist/utils/patch.d.ts.map +1 -1
  68. package/package.json +4 -4
  69. package/src/api/CheckAllFirmwareRelease.ts +3 -23
  70. package/src/api/FirmwareUpdateV2.ts +120 -292
  71. package/src/api/FirmwareUpdateV3.ts +55 -256
  72. package/src/api/FirmwareUpdateV4.ts +297 -757
  73. package/src/api/OpenWalletSession.ts +23 -86
  74. package/src/api/device/DeviceUpdateBootloader.ts +6 -122
  75. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +0 -50
  76. package/src/api/firmware/updateBootloader.ts +4 -19
  77. package/src/api/firmware/uploadFirmware.ts +22 -140
  78. package/src/api/index.ts +1 -0
  79. package/src/api/protocol-v2/Ping.ts +32 -1
  80. package/src/core/index.ts +39 -7
  81. package/src/data/messages/messages-protocol-v2.json +33 -1
  82. package/src/data-manager/DataManager.ts +33 -33
  83. package/src/data-manager/connectSettings.ts +0 -11
  84. package/src/device/Device.ts +7 -9
  85. package/src/device/DeviceCommands.ts +19 -0
  86. package/src/index.ts +0 -5
  87. package/src/inject.ts +4 -22
  88. package/src/lowLevelInject.ts +1 -5
  89. package/src/protocols/protocol-v2/unlockRetry.ts +20 -3
  90. package/src/protocols/protocol-v2/walletSession.ts +26 -8
  91. package/src/topLevelInject.ts +1 -5
  92. package/src/types/api/checkAllFirmwareRelease.ts +0 -3
  93. package/src/types/api/deviceUpdateBootloader.ts +0 -6
  94. package/src/types/api/export.ts +0 -18
  95. package/src/types/api/firmwareUpdate.ts +0 -76
  96. package/src/types/api/index.ts +3 -14
  97. package/src/types/api/openWalletSession.ts +0 -19
  98. package/src/types/api/protocolV2.ts +10 -0
  99. package/src/types/settings.ts +0 -13
  100. package/src/utils/deviceInfoUtils.ts +15 -3
  101. package/__tests__/firmware-update/check-all-firmware-release.test.ts +0 -86
  102. package/__tests__/firmware-update/firmware-artifact-source.test.ts +0 -119
  103. package/__tests__/firmware-update/firmware-host-binding.test.ts +0 -70
  104. package/__tests__/firmware-update/firmware-update-capabilities.test.ts +0 -13
  105. package/__tests__/firmware-update/firmware-update-plan.test.ts +0 -326
  106. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +0 -213
  107. package/__tests__/firmware-update/firmware-upload-source.test.ts +0 -70
  108. package/dist/api/firmware/FirmwareArtifactSource.d.ts +0 -25
  109. package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +0 -1
  110. package/dist/api/firmware/FirmwareHostBinding.d.ts +0 -6
  111. package/dist/api/firmware/FirmwareHostBinding.d.ts.map +0 -1
  112. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +0 -3
  113. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +0 -1
  114. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +0 -23
  115. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +0 -1
  116. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +0 -30
  117. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +0 -1
  118. package/dist/types/api/firmwareUpdateCapabilities.d.ts +0 -9
  119. package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +0 -1
  120. package/dist/types/api/firmwareUpdatePlan.d.ts +0 -27
  121. package/dist/types/api/firmwareUpdatePlan.d.ts.map +0 -1
  122. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +0 -42
  123. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +0 -1
  124. package/src/api/firmware/FirmwareArtifactSource.ts +0 -278
  125. package/src/api/firmware/FirmwareHostBinding.ts +0 -100
  126. package/src/api/firmware/FirmwareUpdateCapabilities.ts +0 -9
  127. package/src/api/firmware/FirmwareUpdatePlan.ts +0 -621
  128. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +0 -392
  129. package/src/types/api/firmwareUpdateCapabilities.ts +0 -9
  130. package/src/types/api/firmwareUpdatePlan.ts +0 -36
  131. package/src/types/api/firmwareUpdatePreparedPlan.ts +0 -53
@@ -10,7 +10,6 @@ import { bytesToHex } from '../helpers/hexUtils';
10
10
  import { DataManager } from '../../data-manager';
11
11
  import { DevicePool } from '../../device/DevicePool';
12
12
  import { buildProtocolV1FeaturesPayload } from '../../deviceProfile';
13
- import { openFirmwareByteSource, readFirmwareByteSourceFully } from './FirmwareArtifactSource';
14
13
 
15
14
  import type { KnownDevice } from '../../types';
16
15
  import type { TypedCall, TypedResponseMessage } from '../../device/DeviceCommands';
@@ -18,7 +17,6 @@ import type { PROTO } from '../../constants';
18
17
  import type { CoreMessage, IFirmwareUpdateProgressType } from '../../events';
19
18
  import type { Success } from '@onekeyfe/hd-transport';
20
19
  import type { Device } from '../../device/Device';
21
- import type { FirmwareByteSource } from './FirmwareArtifactSource';
22
20
 
23
21
  const NEW_BOOT_UPRATE_FIRMWARE_VERSION = '2.4.5';
24
22
  const SESSION_ERROR = 'session not found';
@@ -90,20 +88,17 @@ export const waitBleInstall = async (updateType: string) => {
90
88
  }
91
89
  };
92
90
 
93
- const toArrayBuffer = (payload: ArrayBuffer | Buffer): ArrayBuffer => {
94
- if (payload instanceof ArrayBuffer) {
95
- return payload;
96
- }
97
- return new Uint8Array(payload.buffer, payload.byteOffset, payload.byteLength).slice().buffer;
98
- };
99
-
100
- export const uploadFirmwareFromSource = async (
91
+ export const uploadFirmware = async (
101
92
  updateType: 'firmware' | 'ble',
102
93
  typedCall: TypedCall,
103
94
  postMessage: (message: CoreMessage) => void,
104
95
  device: Device,
105
- source: FirmwareByteSource,
106
- rebootOnSuccess?: boolean,
96
+ {
97
+ payload,
98
+ rebootOnSuccess,
99
+ }: PROTO.FirmwareUpload & {
100
+ rebootOnSuccess?: boolean;
101
+ },
107
102
  isUpdateBootloader?: boolean
108
103
  ) => {
109
104
  const deviceType = device.getCurrentDeviceType();
@@ -124,20 +119,20 @@ export const uploadFirmwareFromSource = async (
124
119
  if (supportUpgradeFileHeader) {
125
120
  // Extract and validate firmware header (first 1KB)
126
121
  const HEADER_SIZE = 1024;
127
- if (source.size < HEADER_SIZE) {
122
+ if (payload.byteLength < HEADER_SIZE) {
128
123
  throw ERRORS.TypedError(
129
124
  HardwareErrorCode.RuntimeError,
130
- `firmware payload too small: ${source.size} bytes, expected at least ${HEADER_SIZE} bytes`
125
+ `firmware payload too small: ${payload.byteLength} bytes, expected at least ${HEADER_SIZE} bytes`
131
126
  );
132
127
  }
133
128
 
134
129
  Log.debug('Uploading firmware header:', {
135
130
  size: HEADER_SIZE,
136
- totalSize: source.size,
131
+ totalSize: payload.byteLength,
137
132
  });
138
133
  postProgressTip(device, 'UploadingFirmwareHeader', postMessage);
139
134
 
140
- const header = new Uint8Array(await source.readAt(0, HEADER_SIZE));
135
+ const header = new Uint8Array(payload.slice(0, HEADER_SIZE));
141
136
 
142
137
  try {
143
138
  const headerRes = await typedCall('UpgradeFileHeader', 'Success', {
@@ -174,9 +169,6 @@ export const uploadFirmwareFromSource = async (
174
169
  postProgressMessage(device, 0, 'installingFirmware', postMessage);
175
170
  let updateResponse: TypedResponseMessage<'Success'>;
176
171
  try {
177
- // Classic/Mini bootloaders accept one protobuf payload and do not expose
178
- // a request-range protocol. Other device families remain fully streamed.
179
- const payload = await readFirmwareByteSourceFully(source);
180
172
  updateResponse = await typedCall('FirmwareUpload', 'Success', {
181
173
  payload,
182
174
  });
@@ -208,7 +200,9 @@ export const uploadFirmwareFromSource = async (
208
200
  updateType,
209
201
  postMessage,
210
202
  device,
211
- source,
203
+ {
204
+ payload,
205
+ },
212
206
  rebootOnSuccess
213
207
  );
214
208
  return response.message;
@@ -217,7 +211,7 @@ export const uploadFirmwareFromSource = async (
217
211
 
218
212
  postConfirmationMessage(device);
219
213
  postProgressTip(device, 'ConfirmOnDevice', postMessage);
220
- const length = source.size;
214
+ const length = payload.byteLength;
221
215
 
222
216
  let response = await typedCall('FirmwareErase', ['FirmwareRequest', 'Success'], { length });
223
217
  postProgressTip(device, 'FirmwareEraseSuccess', postMessage);
@@ -226,7 +220,7 @@ export const uploadFirmwareFromSource = async (
226
220
  const start = response.message.offset!;
227
221
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
228
222
  const end = response.message.offset! + response.message.length!;
229
- const chunk = await source.readAt(start, end - start);
223
+ const chunk = payload.slice(start, end);
230
224
 
231
225
  if (start > 0) {
232
226
  postProgressMessage(
@@ -255,45 +249,11 @@ export const uploadFirmwareFromSource = async (
255
249
  throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'uploadFirmware: unknown device model');
256
250
  };
257
251
 
258
- export const uploadFirmware = async (
259
- updateType: 'firmware' | 'ble',
260
- typedCall: TypedCall,
261
- postMessage: (message: CoreMessage) => void,
262
- device: Device,
263
- {
264
- payload,
265
- rebootOnSuccess,
266
- }: PROTO.FirmwareUpload & {
267
- rebootOnSuccess?: boolean;
268
- },
269
- isUpdateBootloader?: boolean
270
- ) => {
271
- const source = await openFirmwareByteSource({
272
- binary: toArrayBuffer(payload),
273
- });
274
- if (!source) {
275
- throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'firmware payload is empty');
276
- }
277
- try {
278
- return await uploadFirmwareFromSource(
279
- updateType,
280
- typedCall,
281
- postMessage,
282
- device,
283
- source,
284
- rebootOnSuccess,
285
- isUpdateBootloader
286
- );
287
- } finally {
288
- await source.close();
289
- }
290
- };
291
-
292
252
  const newTouchUpdateProcess = async (
293
253
  updateType: 'firmware' | 'ble',
294
254
  postMessage: (message: CoreMessage) => void,
295
255
  device: Device,
296
- source: FirmwareByteSource,
256
+ { payload }: PROTO.FirmwareUpload,
297
257
  rebootOnSuccess = true
298
258
  ) => {
299
259
  let typedCall = device.getCommands().typedCall.bind(device.getCommands());
@@ -303,13 +263,13 @@ const newTouchUpdateProcess = async (
303
263
  const env = DataManager.getSettings('env');
304
264
  const perPackageSize = DataManager.isBleConnect(env) ? 16 : 128;
305
265
  const chunkSize = 1024 * perPackageSize;
306
- const totalChunks = Math.ceil(source.size / chunkSize);
266
+ const totalChunks = Math.ceil(payload.byteLength / chunkSize);
307
267
  let offset = 0;
308
268
  for (let i = 0; i < totalChunks; i++) {
309
269
  const chunkStart = i * chunkSize;
310
- const chunkEnd = Math.min(chunkStart + chunkSize, source.size);
270
+ const chunkEnd = Math.min(chunkStart + chunkSize, payload.byteLength);
311
271
  const chunkLength = chunkEnd - chunkStart;
312
- const chunk = await source.readAt(chunkStart, chunkLength);
272
+ const chunk = payload.slice(chunkStart, chunkEnd);
313
273
  const overwrite = i === 0;
314
274
  const progress = Math.round(((i + 1) / totalChunks) * 100);
315
275
  const writeRes = await emmcFileWriteWithRetry(
@@ -530,67 +490,6 @@ export const updateResource = async (
530
490
  return processResourceRequest(typedCall, res, data);
531
491
  };
532
492
 
533
- export const updateResourceFromSource = async (
534
- typedCall: TypedCall,
535
- fileName: string,
536
- source: FirmwareByteSource,
537
- onConfirmAfter?: () => void
538
- ) => {
539
- const initialLength = Math.min(INIT_DATA_CHUNK_SIZE, source.size);
540
- const initialChunk = new Uint8Array(await source.readAt(0, initialLength));
541
- let response = await typedCall('ResourceUpdate', ['ResourceRequest', 'Success'], {
542
- file_name: fileName,
543
- data_length: source.size,
544
- initial_data_chunk: bytesToHex(initialChunk),
545
- hash: bytesToHex(blake2s(initialChunk)),
546
- });
547
- onConfirmAfter?.();
548
-
549
- while (response.type !== 'Success') {
550
- const { offset, data_length: dataLength } = response.message;
551
- if (
552
- !Number.isSafeInteger(offset) ||
553
- offset === undefined ||
554
- offset < 0 ||
555
- !Number.isSafeInteger(dataLength) ||
556
- dataLength === undefined ||
557
- dataLength <= 0 ||
558
- offset + dataLength > source.size
559
- ) {
560
- throw ERRORS.TypedError(
561
- HardwareErrorCode.RuntimeError,
562
- 'Device requested an invalid firmware resource range'
563
- );
564
- }
565
- const chunk = new Uint8Array(await source.readAt(offset, dataLength));
566
- response = await typedCall('ResourceAck', ['ResourceRequest', 'Success'], {
567
- data_chunk: bytesToHex(chunk),
568
- hash: bytesToHex(blake2s(chunk)),
569
- });
570
- }
571
- return response.message;
572
- };
573
-
574
- export const updateResourcesFromSources = async (
575
- typedCall: TypedCall,
576
- postMessage: (message: CoreMessage) => void,
577
- device: Device,
578
- entries: ReadonlyArray<{ entryName: string; source: FirmwareByteSource }>
579
- ) => {
580
- postProgressTip(device, 'UpdateSysResource', postMessage);
581
- for (const [index, entry] of entries.entries()) {
582
- await updateResourceFromSource(typedCall, entry.entryName, entry.source);
583
- postProgressMessage(
584
- device,
585
- Math.floor(((index + 1) / entries.length) * 100),
586
- 'installingFirmware',
587
- postMessage
588
- );
589
- }
590
- postProgressTip(device, 'UpdateSysResourceSuccess', postMessage);
591
- return true;
592
- };
593
-
594
493
  export const updateResources = async (
595
494
  typedCall: TypedCall,
596
495
  postMessage: (message: CoreMessage) => void,
@@ -625,28 +524,11 @@ export const updateBootloader = async (
625
524
  typedCall: TypedCall,
626
525
  postMessage: (message: CoreMessage) => void,
627
526
  device: Device,
628
- data: ArrayBuffer
629
- ) => {
630
- const source = await openFirmwareByteSource({ binary: data });
631
- if (!source) {
632
- throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'bootloader payload is empty');
633
- }
634
- try {
635
- return await updateBootloaderFromSource(typedCall, postMessage, device, source);
636
- } finally {
637
- await source.close();
638
- }
639
- };
640
-
641
- export const updateBootloaderFromSource = async (
642
- typedCall: TypedCall,
643
- postMessage: (message: CoreMessage) => void,
644
- device: Device,
645
- source: FirmwareByteSource
527
+ source: ArrayBuffer
646
528
  ) => {
647
529
  postProgressTip(device, 'UpdateBootloader', postMessage);
648
530
  postProgressMessage(device, Math.floor(0), 'installingFirmware', postMessage);
649
- await updateResourceFromSource(typedCall, 'bootloader.bin', source, () => {
531
+ await updateResource(typedCall, 'bootloader.bin', source, () => {
650
532
  postProcessingMessage('resource', postMessage);
651
533
  });
652
534
  postProgressMessage(device, Math.floor(100), 'installingFirmware', postMessage);
package/src/api/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { default as testInitializeDeviceDuration } from './test/TestInitializeDeviceDuration';
2
+ export { default as testProtocolV2Ping } from './protocol-v2/Ping';
2
3
  export { default as preInitialize } from './device/PreInitialize';
3
4
 
4
5
  export { default as searchDevices } from './SearchDevices';
@@ -1,12 +1,43 @@
1
+ import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
2
+
1
3
  import { BaseMethod } from '../BaseMethod';
2
4
 
5
+ // nanopb max_size:64 includes the terminating NUL for string fields.
6
+ export const PROTOCOL_V2_PING_MAX_MESSAGE_BYTES = 63;
7
+
8
+ const getUtf8ByteLength = (value: string) =>
9
+ Array.from(value).reduce((length, character) => {
10
+ const codePoint = character.codePointAt(0) ?? 0;
11
+ if (codePoint <= 0x7f) return length + 1;
12
+ if (codePoint <= 0x7ff) return length + 2;
13
+ if (codePoint <= 0xffff) return length + 3;
14
+ return length + 4;
15
+ }, 0);
16
+
17
+ export function validateProtocolV2PingMessage(value: unknown): string {
18
+ if (value !== undefined && typeof value !== 'string') {
19
+ throw ERRORS.TypedError(
20
+ HardwareErrorCode.CallMethodInvalidParameter,
21
+ 'Protocol V2 Ping message must be a string.'
22
+ );
23
+ }
24
+ const message = value ?? '';
25
+ if (getUtf8ByteLength(message) > PROTOCOL_V2_PING_MAX_MESSAGE_BYTES) {
26
+ throw ERRORS.TypedError(
27
+ HardwareErrorCode.CallMethodInvalidParameter,
28
+ `Protocol V2 Ping message must not exceed ${PROTOCOL_V2_PING_MAX_MESSAGE_BYTES} UTF-8 bytes.`
29
+ );
30
+ }
31
+ return message;
32
+ }
33
+
3
34
  export default class Ping extends BaseMethod<{ message?: string }> {
4
35
  init() {
5
36
  // Protocol V2 (Pro2) only; Core rejects non-V2 devices.
6
37
  this.requireProtocolV2 = true;
7
38
  this.skipForceUpdateCheck = true;
8
39
  this.useDevicePassphraseState = false;
9
- this.params = { message: this.payload.message };
40
+ this.params = { message: validateProtocolV2PingMessage(this.payload.message) };
10
41
  }
11
42
 
12
43
  async run() {
package/src/core/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import semver from 'semver';
2
2
  import EventEmitter from 'events';
3
+ import { TRANSPORT_EVENT } from '@onekeyfe/hd-transport';
3
4
  import {
4
5
  ERRORS,
5
6
  ERROR_CODES_REQUIRE_RELEASE,
@@ -70,7 +71,11 @@ import type { CoreMessage, IFrameCallMessage, UiPromise, UiPromiseResponse } fro
70
71
  import type { DeviceEvents, InitOptions, RunOptions } from '../device/Device';
71
72
  import type { SdkTracingContext } from '../utils/tracing';
72
73
  import type { Deferred } from '@onekeyfe/hd-shared';
73
- import type { LowlevelTransportSharedPlugin, OneKeyDeviceInfo } from '@onekeyfe/hd-transport';
74
+ import type {
75
+ LowlevelTransportSharedPlugin,
76
+ OneKeyDeviceInfo,
77
+ TransportDeviceDisconnectEvent,
78
+ } from '@onekeyfe/hd-transport';
74
79
  import type { BaseMethod } from '../api/BaseMethod';
75
80
 
76
81
  const Log = getLogger(LoggerNames.Core);
@@ -173,8 +178,6 @@ export const callAPI = async (context: CoreContext, message: CoreMessage) => {
173
178
  return createResponseMessage(method?.responseID ?? message.id, false, { error });
174
179
  }
175
180
 
176
- DevicePool.emitter.on(DEVICE.CONNECT, onDeviceConnectHandler);
177
-
178
181
  if (!method.useDevice) {
179
182
  updateMethodRequestContext(method, { status: 'running' });
180
183
  try {
@@ -236,7 +239,6 @@ const handlePreWarmSignal = async (
236
239
  method: BaseMethod
237
240
  ): Promise<any> => {
238
241
  const createAckResponse = () => {
239
- DevicePool.emitter.removeListener(DEVICE.CONNECT, onDeviceConnectHandler);
240
242
  completeMethodRequestContext(method);
241
243
  method.dispose();
242
244
  return createResponseMessage(method.responseID, true, true);
@@ -874,8 +876,15 @@ function isRetryableBleProtocolV2ProbeError(method: BaseMethod, error: unknown)
874
876
  */
875
877
  async function connectDeviceForBle(method: BaseMethod, device: Device, retryCount = 0) {
876
878
  try {
877
- await device.acquire(method.payload.connectProtocol);
879
+ const shouldAcquire =
880
+ !device.hasDeviceAcquire() || !device.commands || device.commands.disposed;
881
+ if (shouldAcquire) {
882
+ await device.acquire(method.payload.connectProtocol);
883
+ }
878
884
  if (method.payload?.onlyConnectBleDevice) {
885
+ if (shouldAcquire) {
886
+ DevicePool.emitter.emit(DEVICE.CONNECT, device);
887
+ }
879
888
  return;
880
889
  }
881
890
  // Skip initialize if conditions are met
@@ -886,6 +895,9 @@ async function connectDeviceForBle(method: BaseMethod, device: Device, retryCoun
886
895
  passphraseState: initOptions.passphraseState,
887
896
  });
888
897
  }
898
+ if (shouldAcquire) {
899
+ DevicePool.emitter.emit(DEVICE.CONNECT, device);
900
+ }
889
901
  } catch (err) {
890
902
  if (
891
903
  (err.errorCode === HardwareErrorCode.BleTimeoutError ||
@@ -1186,8 +1198,6 @@ const cleanup = () => {
1186
1198
 
1187
1199
  const removeDeviceListener = (device: Device) => {
1188
1200
  device.removeAllListeners();
1189
- DevicePool.emitter.removeAllListeners(DEVICE.CONNECT);
1190
- // DevicePool.emitter.removeListener(DEVICE.DISCONNECT, onDeviceDisconnectHandler);
1191
1201
  };
1192
1202
 
1193
1203
  /**
@@ -1210,6 +1220,20 @@ const onDeviceDisconnectHandler = (device: Device) => {
1210
1220
  postMessage(createDeviceMessage(DEVICE.DISCONNECT, { device: deviceObject }));
1211
1221
  };
1212
1222
 
1223
+ const onTransportDeviceDisconnectHandler = (event: TransportDeviceDisconnectEvent) => {
1224
+ const device =
1225
+ deviceCacheMap.get(event.connectId) ??
1226
+ DevicePool.devicesCache[event.connectId] ??
1227
+ DevicePool.getDeviceByPath(event.connectId);
1228
+ if (!device) {
1229
+ Log.debug('Ignoring transport disconnect for an unknown device:', event.connectId);
1230
+ return;
1231
+ }
1232
+
1233
+ device.markTransportDisconnected();
1234
+ DevicePool.emitter.emit(DEVICE.DISCONNECT, device);
1235
+ };
1236
+
1213
1237
  const onDevicePinHandler = async (...[device, type, callback]: DeviceEvents['pin']) => {
1214
1238
  Log.log('request Input PIN');
1215
1239
  // create ui promise
@@ -1528,7 +1552,15 @@ export const initCore = () => {
1528
1552
 
1529
1553
  export const initConnector = () => {
1530
1554
  _connector = new DeviceConnector();
1555
+ DevicePool.emitter.removeListener(DEVICE.CONNECT, onDeviceConnectHandler);
1556
+ DevicePool.emitter.removeListener(DEVICE.DISCONNECT, onDeviceDisconnectHandler);
1557
+ DevicePool.emitter.removeListener(
1558
+ TRANSPORT_EVENT.DEVICE_DISCONNECT,
1559
+ onTransportDeviceDisconnectHandler
1560
+ );
1561
+ DevicePool.emitter.on(DEVICE.CONNECT, onDeviceConnectHandler);
1531
1562
  DevicePool.emitter.on(DEVICE.DISCONNECT, onDeviceDisconnectHandler);
1563
+ DevicePool.emitter.on(TRANSPORT_EVENT.DEVICE_DISCONNECT, onTransportDeviceDisconnectHandler);
1532
1564
  return _connector;
1533
1565
  };
1534
1566
 
@@ -11506,6 +11506,33 @@
11506
11506
  }
11507
11507
  }
11508
11508
  },
11509
+ "UiAnimationType": {
11510
+ "values": {
11511
+ "Unknown": 0,
11512
+ "Signing": 1
11513
+ }
11514
+ },
11515
+ "UiAnimationCommand": {
11516
+ "values": {
11517
+ "CommandUnknown": 0,
11518
+ "Start": 1,
11519
+ "Stop": 2,
11520
+ "Refresh": 3
11521
+ }
11522
+ },
11523
+ "UiAnimationRequest": {
11524
+ "fields": {
11525
+ "command": {
11526
+ "rule": "required",
11527
+ "type": "UiAnimationCommand",
11528
+ "id": 1
11529
+ },
11530
+ "type": {
11531
+ "type": "UiAnimationType",
11532
+ "id": 2
11533
+ }
11534
+ }
11535
+ },
11509
11536
  "ViewAmount": {
11510
11537
  "fields": {
11511
11538
  "is_unlimited": {
@@ -12482,7 +12509,12 @@
12482
12509
  }
12483
12510
  },
12484
12511
  "DeviceSessionAskPassphrase": {
12485
- "fields": {}
12512
+ "fields": {
12513
+ "passphrase": {
12514
+ "type": "string",
12515
+ "id": 1
12516
+ }
12517
+ }
12486
12518
  },
12487
12519
  "DeviceSessionAskPin_FailureSubCodes": {
12488
12520
  "values": {
@@ -399,17 +399,7 @@ export default class DataManager {
399
399
 
400
400
  static async load(settings: ConnectSettings) {
401
401
  this.settings = settings;
402
- const manifestMode =
403
- settings.firmwareManifestMode ?? (settings.fetchConfig ? 'sdk-managed' : undefined);
404
- if (settings.preloadedConfig) {
405
- this.applyRemoteConfig(settings.preloadedConfig);
406
- Log.log('[DataConfig] Preloaded firmware config loaded');
407
- return;
408
- }
409
- if (manifestMode === 'external-only') {
410
- throw new Error('External firmware config snapshot is required');
411
- }
412
- if (manifestMode !== 'sdk-managed') {
402
+ if (!settings.fetchConfig) {
413
403
  return;
414
404
  }
415
405
 
@@ -419,50 +409,60 @@ export default class DataManager {
419
409
 
420
410
  const urlWithCache = `${url}?noCache=${getTimeStamp()}`;
421
411
  let data: RemoteConfigResponse | null = null;
412
+ let fetchMethod: 'configFetcher' | 'axios' | 'none' = 'none';
413
+
414
+ // 1. Try custom configFetcher first (client-side IP direct connection support)
422
415
  if (settings.configFetcher) {
423
- Log.debug('[DataConfig] Trying client configFetcher...');
416
+ Log.debug('[DataConfig] Trying configFetcher (client-side fetcher)...');
424
417
  try {
425
418
  data = await settings.configFetcher(urlWithCache);
419
+ if (data) {
420
+ fetchMethod = 'configFetcher';
421
+ Log.log('[DataConfig] ConfigFetcher success');
422
+ } else {
423
+ Log.debug('[DataConfig] ConfigFetcher returned null, will fallback to axios');
424
+ }
426
425
  } catch (e) {
427
- Log.warn('[DataConfig] Client configFetcher failed:', e);
426
+ Log.warn('[DataConfig] ConfigFetcher error, will fallback to axios:', e);
428
427
  }
429
428
  }
429
+
430
+ // 2. Fallback to default axios request
430
431
  if (!data) {
431
- Log.debug('[DataConfig] Trying SDK-managed config request...');
432
+ Log.debug('[DataConfig] Trying axios (SDK default fetcher)...');
432
433
  try {
433
434
  const response = await axios.get<RemoteConfigResponse>(urlWithCache, {
434
435
  // because of iframe timeout is 10000
435
436
  timeout: 7000,
436
437
  });
437
438
  data = response.data;
438
- Log.log('[DataConfig] SDK-managed config request succeeded');
439
+ fetchMethod = 'axios';
440
+ Log.log('[DataConfig] Axios fetch success');
439
441
  } catch (e) {
440
- Log.warn('[DataConfig] SDK-managed config request failed:', e);
442
+ Log.warn('[DataConfig] Axios fetch error:', e);
441
443
  }
442
444
  }
443
445
 
446
+ // 3. Apply config if available
444
447
  if (data) {
445
- this.applyRemoteConfig(data);
448
+ Log.log(`[DataConfig] Config loaded successfully via [${fetchMethod}]`);
449
+ this.deviceMap = {
450
+ [EDeviceType.Classic]: this.enrichFirmwareReleaseInfo(data.classic),
451
+ [EDeviceType.Classic1s]: this.enrichFirmwareReleaseInfo(data.classic1s),
452
+ [EDeviceType.ClassicPure]: this.enrichFirmwareReleaseInfo(data.classicpure),
453
+ [EDeviceType.Mini]: this.enrichFirmwareReleaseInfo(data.mini),
454
+ [EDeviceType.Touch]: this.enrichFirmwareReleaseInfo(data.touch),
455
+ [EDeviceType.Pro]: this.enrichFirmwareReleaseInfo(data.pro),
456
+ [EDeviceType.Pro2]: this.enrichFirmwareReleaseInfo(data.pro2),
457
+ };
458
+ this.assets = {
459
+ bridge: data.bridge,
460
+ };
446
461
  } else {
447
- Log.warn('[DataConfig] SDK-managed config request failed, using built-in default config');
462
+ Log.warn('[DataConfig] All fetch methods failed, using built-in default config');
448
463
  }
449
464
  }
450
465
 
451
- private static applyRemoteConfig(data: RemoteConfigResponse) {
452
- this.deviceMap = {
453
- [EDeviceType.Classic]: this.enrichFirmwareReleaseInfo(data.classic),
454
- [EDeviceType.Classic1s]: this.enrichFirmwareReleaseInfo(data.classic1s),
455
- [EDeviceType.ClassicPure]: this.enrichFirmwareReleaseInfo(data.classicpure),
456
- [EDeviceType.Mini]: this.enrichFirmwareReleaseInfo(data.mini),
457
- [EDeviceType.Touch]: this.enrichFirmwareReleaseInfo(data.touch),
458
- [EDeviceType.Pro]: this.enrichFirmwareReleaseInfo(data.pro),
459
- [EDeviceType.Pro2]: this.enrichFirmwareReleaseInfo(data.pro2),
460
- };
461
- this.assets = {
462
- bridge: data.bridge,
463
- };
464
- }
465
-
466
466
  static updateEnv(newEnv: ConnectSettings['env']) {
467
467
  if (this.settings) {
468
468
  const prevEnv = this.settings.env;
@@ -115,21 +115,10 @@ export const parseConnectSettings = (input: Partial<ConnectSettings> = {}) => {
115
115
  settings.fetchConfig = input.fetchConfig;
116
116
  }
117
117
 
118
- if (
119
- input.firmwareManifestMode === 'sdk-managed' ||
120
- input.firmwareManifestMode === 'external-only'
121
- ) {
122
- settings.firmwareManifestMode = input.firmwareManifestMode;
123
- }
124
-
125
118
  if (input.configFetcher) {
126
119
  settings.configFetcher = input.configFetcher;
127
120
  }
128
121
 
129
- if (input.preloadedConfig) {
130
- settings.preloadedConfig = input.preloadedConfig;
131
- }
132
-
133
122
  return settings;
134
123
  };
135
124
 
@@ -344,6 +344,10 @@ export class Device extends EventEmitter {
344
344
  // eslint-disable-next-line no-async-promise-executor
345
345
  return new Promise<boolean>(async (resolve, reject) => {
346
346
  if (DataManager.isBleConnect(env)) {
347
+ if (this.hasDeviceAcquire() && this.commands && !this.commands.disposed) {
348
+ resolve(true);
349
+ return;
350
+ }
347
351
  try {
348
352
  await this.acquire(connectProtocol);
349
353
  resolve(true);
@@ -1065,6 +1069,7 @@ export class Device extends EventEmitter {
1065
1069
  }
1066
1070
 
1067
1071
  markTransportDisconnected() {
1072
+ this.deviceAcquired = false;
1068
1073
  if (!this.isProtocolV2()) return;
1069
1074
  this.protocolV2StateNeedsReload = true;
1070
1075
  this.clearPreInitialized();
@@ -1357,7 +1362,7 @@ export class Device extends EventEmitter {
1357
1362
  isUsedHere() {
1358
1363
  const env = DataManager.getSettings('env');
1359
1364
  if (DataManager.isBleConnect(env)) {
1360
- return false;
1365
+ return this.deviceAcquired;
1361
1366
  }
1362
1367
  return this.isUsed() && this.originalDescriptor.session === this.mainId;
1363
1368
  }
@@ -1439,14 +1444,7 @@ export class Device extends EventEmitter {
1439
1444
  async unlockDevice(pinType: DeviceSessionPinType = DeviceSessionPinType.Main) {
1440
1445
  if (this.isProtocolV2()) {
1441
1446
  try {
1442
- await this.commands.typedCall(
1443
- 'DeviceSessionAskPin',
1444
- 'Success',
1445
- { type: pinType },
1446
- {
1447
- timeoutMs: 120_000,
1448
- }
1449
- );
1447
+ await this.commands.typedCall('DeviceSessionAskPin', 'Success', { type: pinType });
1450
1448
  } catch (error) {
1451
1449
  const errorText =
1452
1450
  error instanceof Error
@@ -515,6 +515,25 @@ export class DeviceCommands {
515
515
  subcode,
516
516
  firmwareMessage: message,
517
517
  });
518
+ } else if (
519
+ DEVICE_SESSION_CALLS.has(callType) &&
520
+ (subcode === DeviceSessionErrorCode.DeviceSessionError_Busy ||
521
+ /^(?:busy|another flow in progress)$/i.test(normalizedMessage))
522
+ ) {
523
+ error = ERRORS.TypedError(HardwareErrorCode.DeviceBusy, message, {
524
+ failureCode: code,
525
+ subcode,
526
+ firmwareMessage: message,
527
+ });
528
+ } else if (
529
+ callType === 'DeviceSessionAskPin' &&
530
+ /^passphrase disabled$/i.test(normalizedMessage)
531
+ ) {
532
+ error = ERRORS.TypedError(HardwareErrorCode.DeviceNotOpenedPassphrase, message, {
533
+ failureCode: code,
534
+ subcode,
535
+ firmwareMessage: message,
536
+ });
518
537
  } else if (
519
538
  subcode === DeviceErrorCode.DeviceError_ActionCancelled ||
520
539
  isLegacyProtocolV2ActionCancelledFailure
package/src/index.ts CHANGED
@@ -19,11 +19,6 @@ export * from './types';
19
19
  export { whitelist, whitelistExtension } from './data/config';
20
20
  export { executeCallback, cleanupCallback };
21
21
  export { preloadSessionCache } from './device/Device';
22
- export {
23
- getFirmwareUpdateHostBindingGeneration,
24
- registerFirmwareUpdateHostBinding,
25
- unregisterFirmwareUpdateHostBinding,
26
- } from './api/firmware/FirmwareHostBinding';
27
22
 
28
23
  const HardwareSdk = ({
29
24
  init,