@onekeyfe/hd-core 1.2.0-alpha.10 → 1.2.0-alpha.12

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 (122) hide show
  1. package/__tests__/DeviceCommands.test.ts +25 -0
  2. package/__tests__/RequestQueue.test.ts +34 -0
  3. package/__tests__/pro2Wallpaper.test.ts +50 -0
  4. package/__tests__/protocol-v2.test.ts +511 -61
  5. package/__tests__/protocolV2FileWrite.test.ts +55 -0
  6. package/dist/api/BaseMethod.d.ts +4 -0
  7. package/dist/api/BaseMethod.d.ts.map +1 -1
  8. package/dist/api/FileWrite.d.ts.map +1 -1
  9. package/dist/api/FirmwareUpdateV4.d.ts +0 -4
  10. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  11. package/dist/api/GetPassphraseState.d.ts +1 -6
  12. package/dist/api/GetPassphraseState.d.ts.map +1 -1
  13. package/dist/api/UploadPortfolio.d.ts +18 -0
  14. package/dist/api/UploadPortfolio.d.ts.map +1 -0
  15. package/dist/api/helpers/protocolV2FileWrite.d.ts +32 -0
  16. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -0
  17. package/dist/api/index.d.ts +6 -0
  18. package/dist/api/index.d.ts.map +1 -1
  19. package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts +6 -0
  20. package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts.map +1 -0
  21. package/dist/api/protocol-v2/DeviceSessionGet.d.ts +1 -4
  22. package/dist/api/protocol-v2/DeviceSessionGet.d.ts.map +1 -1
  23. package/dist/api/protocol-v2/DeviceSettingsGet.d.ts +6 -0
  24. package/dist/api/protocol-v2/DeviceSettingsGet.d.ts.map +1 -0
  25. package/dist/api/protocol-v2/DeviceSettingsPageShow.d.ts +16 -0
  26. package/dist/api/protocol-v2/DeviceSettingsPageShow.d.ts.map +1 -0
  27. package/dist/api/protocol-v2/DeviceSettingsSet.d.ts +12 -0
  28. package/dist/api/protocol-v2/DeviceSettingsSet.d.ts.map +1 -0
  29. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts +26 -0
  30. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -0
  31. package/dist/core/RequestQueue.d.ts +2 -0
  32. package/dist/core/RequestQueue.d.ts.map +1 -1
  33. package/dist/core/index.d.ts +1 -0
  34. package/dist/core/index.d.ts.map +1 -1
  35. package/dist/device/Device.d.ts.map +1 -1
  36. package/dist/device/DeviceCommands.d.ts +4 -4
  37. package/dist/device/DeviceCommands.d.ts.map +1 -1
  38. package/dist/deviceProfile/buildDeviceFeatures.d.ts +2 -2
  39. package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
  40. package/dist/deviceProfile/buildDeviceProfile.d.ts.map +1 -1
  41. package/dist/events/call.d.ts +7 -0
  42. package/dist/events/call.d.ts.map +1 -1
  43. package/dist/events/core.d.ts +2 -2
  44. package/dist/events/core.d.ts.map +1 -1
  45. package/dist/events/iframe.d.ts +1 -0
  46. package/dist/events/iframe.d.ts.map +1 -1
  47. package/dist/index.d.ts +80 -23
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +922 -472
  50. package/dist/inject.d.ts +3 -2
  51. package/dist/inject.d.ts.map +1 -1
  52. package/dist/lowLevelInject.d.ts +2 -1
  53. package/dist/lowLevelInject.d.ts.map +1 -1
  54. package/dist/protocols/protocol-v2/features.d.ts +1 -0
  55. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  56. package/dist/protocols/protocol-v2/index.d.ts +1 -1
  57. package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
  58. package/dist/protocols/protocol-v2/unlockRetry.d.ts +9 -0
  59. package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +1 -0
  60. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  61. package/dist/topLevelInject.d.ts.map +1 -1
  62. package/dist/types/api/firmwareUpdate.d.ts +2 -0
  63. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  64. package/dist/types/api/getDeviceInfo.d.ts +1 -1
  65. package/dist/types/api/getDeviceInfo.d.ts.map +1 -1
  66. package/dist/types/api/getPassphraseState.d.ts +2 -10
  67. package/dist/types/api/getPassphraseState.d.ts.map +1 -1
  68. package/dist/types/api/index.d.ts +9 -2
  69. package/dist/types/api/index.d.ts.map +1 -1
  70. package/dist/types/api/protocolV2.d.ts +20 -4
  71. package/dist/types/api/protocolV2.d.ts.map +1 -1
  72. package/dist/types/device.d.ts +1 -1
  73. package/dist/types/device.d.ts.map +1 -1
  74. package/dist/utils/deviceFeaturesUtils.d.ts +0 -2
  75. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  76. package/dist/utils/index.d.ts +2 -0
  77. package/dist/utils/index.d.ts.map +1 -1
  78. package/dist/utils/patch.d.ts +1 -1
  79. package/dist/utils/patch.d.ts.map +1 -1
  80. package/dist/utils/pro2Wallpaper.d.ts +12 -0
  81. package/dist/utils/pro2Wallpaper.d.ts.map +1 -0
  82. package/package.json +4 -4
  83. package/src/api/BaseMethod.ts +18 -0
  84. package/src/api/FileWrite.ts +18 -178
  85. package/src/api/FirmwareUpdateV4.ts +21 -91
  86. package/src/api/GetPassphraseState.ts +9 -18
  87. package/src/api/UploadPortfolio.ts +37 -0
  88. package/src/api/helpers/protocolV2FileWrite.ts +164 -0
  89. package/src/api/index.ts +6 -0
  90. package/src/api/protocol-v2/DeviceGetOnboardingStatus.ts +18 -0
  91. package/src/api/protocol-v2/DeviceSessionGet.ts +3 -14
  92. package/src/api/protocol-v2/DeviceSettingsGet.ts +16 -0
  93. package/src/api/protocol-v2/DeviceSettingsPageShow.ts +59 -0
  94. package/src/api/protocol-v2/DeviceSettingsSet.ts +39 -0
  95. package/src/api/protocol-v2/DeviceUploadWallpaper.ts +124 -0
  96. package/src/core/RequestQueue.ts +19 -0
  97. package/src/core/index.ts +10 -1
  98. package/src/data/messages/messages-protocol-v2.json +92 -8
  99. package/src/device/Device.ts +17 -3
  100. package/src/device/DeviceCommands.ts +9 -3
  101. package/src/deviceProfile/buildDeviceFeatures.ts +20 -12
  102. package/src/deviceProfile/buildDeviceProfile.ts +8 -6
  103. package/src/events/call.ts +6 -0
  104. package/src/events/core.ts +2 -0
  105. package/src/events/iframe.ts +1 -0
  106. package/src/index.ts +4 -0
  107. package/src/inject.ts +15 -0
  108. package/src/lowLevelInject.ts +3 -0
  109. package/src/protocols/protocol-v2/features.ts +19 -1
  110. package/src/protocols/protocol-v2/index.ts +2 -0
  111. package/src/protocols/protocol-v2/unlockRetry.ts +46 -0
  112. package/src/protocols/protocol-v2/walletSession.ts +14 -5
  113. package/src/topLevelInject.ts +2 -0
  114. package/src/types/api/firmwareUpdate.ts +12 -0
  115. package/src/types/api/getDeviceInfo.ts +1 -1
  116. package/src/types/api/getPassphraseState.ts +2 -11
  117. package/src/types/api/index.ts +14 -1
  118. package/src/types/api/protocolV2.ts +52 -3
  119. package/src/types/device.ts +1 -0
  120. package/src/utils/deviceFeaturesUtils.ts +3 -8
  121. package/src/utils/index.ts +6 -0
  122. package/src/utils/pro2Wallpaper.ts +111 -0
@@ -1,9 +1,3 @@
1
- import {
2
- PROTOCOL_V2_BLE_FILE_CHUNK_SIZE,
3
- PROTOCOL_V2_WEBUSB_FILE_CHUNK_SIZE,
4
- } from '@onekeyfe/hd-transport';
5
- import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
6
-
7
1
  import { BaseMethod } from './BaseMethod';
8
2
  import {
9
3
  validateNonEmptyString,
@@ -12,8 +6,8 @@ import {
12
6
  validateOptionalPercentage,
13
7
  validateRequiredData,
14
8
  } from './helpers/filesystemValidation';
9
+ import { writeProtocolV2File } from './helpers/protocolV2FileWrite';
15
10
  import { UI_REQUEST, createUiMessage } from '../events/ui-request';
16
- import { DataManager } from '../data-manager';
17
11
 
18
12
  export type FileWriteParams = {
19
13
  path: string;
@@ -28,75 +22,6 @@ export type FileWriteParams = {
28
22
  timeoutMs?: number | string;
29
23
  };
30
24
 
31
- const MIN_FILE_CHUNK_SIZE = 64;
32
-
33
- function getProtocolV2FileChunkLimit() {
34
- const env = DataManager.getSettings('env');
35
- if (env && DataManager.isBleConnect(env)) {
36
- return PROTOCOL_V2_BLE_FILE_CHUNK_SIZE;
37
- }
38
- return PROTOCOL_V2_WEBUSB_FILE_CHUNK_SIZE;
39
- }
40
-
41
- async function dataToUint8Array(data: FileWriteParams['data'] | Blob): Promise<Uint8Array> {
42
- if (typeof data === 'string') {
43
- return new TextEncoder().encode(data);
44
- }
45
-
46
- if (data instanceof ArrayBuffer) {
47
- return new Uint8Array(data);
48
- }
49
-
50
- if (ArrayBuffer.isView(data)) {
51
- return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
52
- }
53
-
54
- if (typeof Blob !== 'undefined' && data instanceof Blob) {
55
- return new Uint8Array(await data.arrayBuffer());
56
- }
57
-
58
- throw ERRORS.TypedError(
59
- HardwareErrorCode.CallMethodInvalidParameter,
60
- 'Unsupported FilesystemFileWrite data'
61
- );
62
- }
63
-
64
- function normalizeChunkSize(value: unknown, maxChunkSize: number): number {
65
- const numeric = Number(value);
66
- if (!Number.isFinite(numeric) || numeric <= 0) return maxChunkSize;
67
- return Math.min(Math.max(Math.floor(numeric), MIN_FILE_CHUNK_SIZE), maxChunkSize);
68
- }
69
-
70
- function getConfirmedProgress(
71
- processedByte: number,
72
- totalSize: number,
73
- written: number,
74
- dataLength: number
75
- ) {
76
- if (Number.isFinite(processedByte) && Number.isFinite(totalSize) && totalSize > 0) {
77
- if (processedByte >= totalSize) return 100;
78
- return Math.min(Math.max(Math.floor((processedByte / totalSize) * 100), 0), 99);
79
- }
80
- if (dataLength > 0) {
81
- if (written >= dataLength) return 100;
82
- return Math.min(Math.max(Math.floor((written / dataLength) * 100), 0), 99);
83
- }
84
- return 100;
85
- }
86
-
87
- function getDeviceTransferProgress(bytesBeforeChunk: number, bytesAfterChunk: number, totalBytes: number) {
88
- if (!Number.isFinite(totalBytes) || totalBytes <= 0) {
89
- return 100;
90
- }
91
- if (bytesBeforeChunk <= 0 && bytesAfterChunk < totalBytes) {
92
- return 0;
93
- }
94
- if (bytesAfterChunk >= totalBytes) {
95
- return 100;
96
- }
97
- return Math.min(Math.max(Math.ceil((bytesAfterChunk / totalBytes) * 100), 1), 99);
98
- }
99
-
100
25
  export default class FileWrite extends BaseMethod<FileWriteParams> {
101
26
  init() {
102
27
  // Protocol V2 (Pro2) 专属方法,core 调度层统一做非 V2 设备守卫
@@ -121,109 +46,24 @@ export default class FileWrite extends BaseMethod<FileWriteParams> {
121
46
  }
122
47
 
123
48
  async run() {
124
- const data = await dataToUint8Array(this.params.data);
125
- const dataLength = data.byteLength;
126
- const offsetValue = Number(this.params.offset ?? 0);
127
- const startOffset = Number.isFinite(offsetValue) && offsetValue > 0 ? offsetValue : 0;
128
- const payloadTotalSize = Number(this.params.totalSize);
129
- const totalSize =
130
- Number.isFinite(payloadTotalSize) && payloadTotalSize > 0
131
- ? payloadTotalSize
132
- : startOffset + dataLength;
133
-
134
- if (totalSize < startOffset + dataLength) {
135
- throw ERRORS.TypedError(
136
- HardwareErrorCode.RuntimeError,
137
- `FilesystemFileWrite totalSize ${totalSize} is smaller than offset + data length ${
138
- startOffset + dataLength
139
- }`
140
- );
141
- }
142
-
143
- const chunkSize = normalizeChunkSize(
144
- this.params.chunkSize ?? this.params.chunkLen,
145
- getProtocolV2FileChunkLimit()
146
- );
147
- const overwrite = this.params.overwrite ?? false;
148
- const append = this.params.append ?? false;
149
- let written = 0;
150
- let chunkIndex = 0;
151
- let lastMessage: Record<string, unknown> | undefined;
152
- const startTime = Date.now();
153
- const timeoutMs =
154
- this.params.timeoutMs === undefined ? undefined : Number(this.params.timeoutMs);
155
-
156
- while (written < dataLength) {
157
- const chunkEnd = Math.min(written + chunkSize, dataLength);
158
- const chunk = data.slice(written, chunkEnd);
159
- const offset = startOffset + written;
160
- const isFirstChunk = chunkIndex === 0;
161
- const progress =
162
- this.params.uiPercentage ??
163
- getDeviceTransferProgress(offset, offset + chunk.byteLength, totalSize);
164
-
165
- const res = await this.device.commands.typedCall(
166
- 'FilesystemFileWrite',
167
- 'FilesystemFile',
168
- {
169
- file: {
170
- path: this.params.path,
171
- offset,
172
- total_size: totalSize,
173
- data: chunk,
174
- },
175
- overwrite: isFirstChunk ? overwrite : false,
176
- append,
177
- ui_percentage: progress,
178
- },
179
- {
180
- timeoutMs,
181
- }
182
- );
183
-
184
- lastMessage = res.message;
185
- const processedByte = Number(res.message?.processed_byte);
186
- if (Number.isFinite(processedByte) && processedByte > offset) {
187
- written = processedByte - startOffset;
188
- } else {
189
- written += chunk.byteLength;
190
- }
191
-
192
- if (written > dataLength) {
193
- throw ERRORS.TypedError(
194
- HardwareErrorCode.RuntimeError,
195
- `FilesystemFileWrite invalid processed_byte ${processedByte}`
196
- );
197
- }
198
-
199
- const confirmedProcessedByte =
200
- Number.isFinite(processedByte) && processedByte > offset
201
- ? processedByte
202
- : startOffset + written;
203
- if (typeof this.postMessage === 'function') {
204
- const elapsedMs = Date.now() - startTime;
205
- const transferredBytes = Math.min(written, dataLength);
206
- this.postMessage(
207
- createUiMessage(UI_REQUEST.DEVICE_PROGRESS, {
208
- progress: getConfirmedProgress(confirmedProcessedByte, totalSize, written, dataLength),
209
- transferredBytes,
210
- totalBytes: dataLength,
211
- rateBytesPerSecond:
212
- elapsedMs > 0 ? Math.round((transferredBytes / elapsedMs) * 1000) : undefined,
213
- elapsedMs,
214
- })
215
- );
216
- }
217
- chunkIndex += 1;
218
- }
219
-
220
- return Promise.resolve({
221
- ...lastMessage,
49
+ return writeProtocolV2File({
50
+ commands: this.device.commands,
222
51
  path: this.params.path,
223
- offset: startOffset,
224
- total_size: totalSize,
225
- processed_byte: startOffset + written,
226
- chunks: chunkIndex,
52
+ data: this.params.data,
53
+ offset: this.params.offset,
54
+ totalSize: this.params.totalSize,
55
+ chunkSize: this.params.chunkSize,
56
+ chunkLen: this.params.chunkLen,
57
+ overwrite: this.params.overwrite,
58
+ append: this.params.append,
59
+ uiPercentage: this.params.uiPercentage,
60
+ timeoutMs: this.params.timeoutMs === undefined ? undefined : Number(this.params.timeoutMs),
61
+ throwIfAborted: () => this.throwIfAborted(),
62
+ onProgress: payload => {
63
+ if (typeof this.postMessage === 'function') {
64
+ this.postMessage(createUiMessage(UI_REQUEST.DEVICE_PROGRESS, payload));
65
+ }
66
+ },
227
67
  });
228
68
  }
229
69
  }
@@ -30,7 +30,7 @@ import {
30
30
  isProtocolV2DeviceDisconnectedError,
31
31
  } from './protocol-v2/helpers';
32
32
 
33
- import type { FirmwareUpdateV4Params } from '../types/api/firmwareUpdate';
33
+ import type { FirmwareUpdateV4Params, FirmwareUpdateV4Target } from '../types/api/firmwareUpdate';
34
34
  import type { EFirmwareType } from '@onekeyfe/hd-shared';
35
35
  import type { PROTO } from '../constants';
36
36
  import type { TypedResponseMessage } from '../device/DeviceCommands';
@@ -188,6 +188,17 @@ const PROTOCOL_V2_REMOTE_COMPONENT_TARGETS: Readonly<
188
188
  },
189
189
  };
190
190
 
191
+ const PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID = new Map<number, FirmwareUpdateV4Target>([
192
+ [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER, 'boot'],
193
+ [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1, 'app_v1'],
194
+ [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P2, 'app_v2'],
195
+ [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_COPROCESSOR, 'coprocessor'],
196
+ [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE01, 'se01'],
197
+ [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE02, 'se02'],
198
+ [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE03, 'se03'],
199
+ [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE04, 'se04'],
200
+ ]);
201
+
191
202
  const PROTOCOL_V2_ROMLOADER_UNSUPPORTED_MESSAGE =
192
203
  'FW_MGMT_TARGET_ROMLOADER is not accepted by the current Pro2 bootloader update request. Flash romloader with the loader-specific flow instead of firmwareUpdateV4.';
193
204
 
@@ -282,13 +293,6 @@ const versionArrayToNumber = (version?: IVersionArray) => {
282
293
  return version[0] * 0x10000 + version[1] * 0x100 + version[2];
283
294
  };
284
295
 
285
- const versionStringToArray = (version?: string | null): IVersionArray | undefined => {
286
- if (!version) return undefined;
287
- const parts = version.split('.').map(part => Number(part));
288
- if (parts.length !== 3 || parts.some(part => !Number.isFinite(part))) return undefined;
289
- return parts as IVersionArray;
290
- };
291
-
292
296
  const compareProtocolV2Versions = (current?: IVersionArray, target?: IVersionArray) => {
293
297
  const currentNumber = versionArrayToNumber(current);
294
298
  const targetNumber = versionArrayToNumber(target);
@@ -418,6 +422,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
418
422
  { name: 'se03Binary', type: 'buffer' },
419
423
  { name: 'se04Binary', type: 'buffer' },
420
424
  { name: 'firmwareType', type: 'string' },
425
+ { name: 'targetsToUpdate', type: 'array', allowEmpty: true },
421
426
  { name: 'platform', type: 'string' },
422
427
  { name: 'resourceBundleFiles', type: 'array', allowEmpty: true },
423
428
  ]);
@@ -436,6 +441,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
436
441
  se04Binary: payload.se04Binary,
437
442
  resourceBundleFiles: payload.resourceBundleFiles,
438
443
  firmwareType: payload.firmwareType,
444
+ targetsToUpdate: payload.targetsToUpdate,
439
445
  platform: payload.platform,
440
446
  };
441
447
  }
@@ -601,63 +607,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
601
607
  return target;
602
608
  }
603
609
 
604
- private getProtocolV2ComponentTargetVersion(
605
- release: IFirmwareReleaseInfo,
606
- component: IProtocolV2FirmwareComponent,
607
- target: ProtocolV2RemoteComponentTarget
608
- ) {
609
- if (component.version) return component.version;
610
- if (target.targetId === ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER) {
611
- return release.bootloaderVersion;
612
- }
613
- if (
614
- target.targetId === ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1 ||
615
- target.targetId === ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P2
616
- ) {
617
- return release.version;
618
- }
619
- return undefined;
620
- }
621
-
622
- private getProtocolV2ComponentCurrentVersion(
623
- features: Features,
624
- target: ProtocolV2RemoteComponentTarget
625
- ) {
626
- switch (target.targetId) {
627
- case ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER:
628
- return getDeviceBootloaderVersion(features);
629
- case ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1:
630
- case ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P2:
631
- return getDeviceFirmwareVersion(features);
632
- case ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_COPROCESSOR:
633
- return getDeviceBLEFirmwareVersion(features);
634
- case ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE01:
635
- return versionStringToArray(features.se01Version);
636
- case ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE02:
637
- return versionStringToArray(features.se02Version);
638
- case ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE03:
639
- return versionStringToArray(features.se03Version);
640
- case ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE04:
641
- return versionStringToArray(features.se04Version);
642
- default:
643
- return undefined;
644
- }
645
- }
646
-
647
- private isProtocolV2ComponentVersionSatisfied(
648
- release: IFirmwareReleaseInfo,
649
- component: IProtocolV2FirmwareComponent,
650
- target: ProtocolV2RemoteComponentTarget,
651
- features: Features
652
- ) {
653
- const targetVersion = this.getProtocolV2ComponentTargetVersion(release, component, target);
654
- if (!targetVersion) return false;
655
-
656
- const currentVersion = this.getProtocolV2ComponentCurrentVersion(features, target);
657
- const compareResult = compareProtocolV2Versions(currentVersion, targetVersion);
658
- return compareResult !== undefined && compareResult >= 0;
659
- }
660
-
661
610
  private getProtocolV2ResourceFilePath(path: string) {
662
611
  if (path.startsWith('vol')) return path;
663
612
  if (path.startsWith('/')) return `vol0:${path}`;
@@ -709,25 +658,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
709
658
  return parseProtocolV2OkppHeader(headerBytes);
710
659
  }
711
660
 
712
- private shouldInstallRemoteProtocolV2Component(
713
- release: IFirmwareReleaseInfo,
714
- key: string,
715
- component: IProtocolV2FirmwareComponent,
716
- target: ProtocolV2RemoteComponentTarget,
717
- features: Features
718
- ) {
719
- const versionSatisfied = this.isProtocolV2ComponentVersionSatisfied(
720
- release,
721
- component,
722
- target,
723
- features
724
- );
725
- if (versionSatisfied) {
726
- Log.log(`[FirmwareUpdateV4] skip Protocol V2 component ${key}; version is up to date`);
727
- }
728
- return !versionSatisfied;
729
- }
730
-
731
661
  private async downloadRemoteProtocolV2Component(
732
662
  key: string,
733
663
  component: IProtocolV2FirmwareComponent
@@ -763,16 +693,12 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
763
693
  }
764
694
 
765
695
  const entries = this.getRemoteComponentEntries(release);
696
+ const targetsToUpdate = new Set(this.params.targetsToUpdate ?? []);
766
697
 
767
698
  for (const [key, component] of entries) {
768
699
  const target = this.getRemoteComponentTarget(key, component);
769
- const shouldInstall = this.shouldInstallRemoteProtocolV2Component(
770
- release,
771
- key,
772
- component,
773
- target,
774
- features
775
- );
700
+ const updateTarget = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(target.targetId);
701
+ const shouldInstall = updateTarget ? targetsToUpdate.has(updateTarget) : false;
776
702
  if (shouldInstall) {
777
703
  const remoteBinary = await this.downloadRemoteProtocolV2Component(key, component);
778
704
  if (remoteBinary.kind === 'bootloader') {
@@ -827,6 +753,10 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
827
753
  }));
828
754
  }
829
755
 
756
+ if (!this.params.targetsToUpdate?.includes('resource')) {
757
+ return undefined;
758
+ }
759
+
830
760
  // 模式2:远端配置模式,后续按需下载 + 比对
831
761
  const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
832
762
  if (!release?.resourceBundles?.length) return undefined;
@@ -11,27 +11,18 @@ export default class GetPassphraseState extends BaseMethod {
11
11
  }
12
12
 
13
13
  async run() {
14
- const { passphraseState, newSession, unlockedAttachPin } =
15
- await getPassphraseStateWithRefreshDeviceInfo(this.device, {
16
- expectPassphraseState: this.payload.passphraseState,
17
- onlyMainPin: this.payload.useEmptyPassphrase,
18
- allowCreateAttachPin: this.payload.allowCreateAttachPin,
19
- initSession: this.payload.initSession,
20
- });
14
+ const { passphraseState } = await getPassphraseStateWithRefreshDeviceInfo(this.device, {
15
+ expectPassphraseState: this.payload.passphraseState,
16
+ onlyMainPin: this.payload.useEmptyPassphrase,
17
+ initSession: this.payload.initSession,
18
+ });
21
19
 
22
- const { features } = this.device;
23
- const passphraseProtection = this.device.getCurrentPassphraseProtection() ?? null;
20
+ const passphraseProtection = this.device.getCurrentPassphraseProtection();
24
21
  const deviceType = this.device.getCurrentDeviceType();
25
22
  const isProSeries = deviceType === EDeviceType.Pro || deviceType === EDeviceType.Pro2;
26
23
 
27
- // refresh device info
28
- return Promise.resolve({
29
- passphraseState: isProSeries || passphraseProtection === true ? passphraseState : undefined,
30
- sessionId: this.payload.initSession
31
- ? newSession ?? undefined
32
- : newSession ?? features?.sessionId ?? undefined,
33
- unlockedAttachPin: unlockedAttachPin ?? features?.unlockedAttachPin,
34
- passphraseProtection,
35
- });
24
+ return Promise.resolve(
25
+ isProSeries || passphraseProtection === true ? passphraseState : undefined
26
+ );
36
27
  }
37
28
  }
@@ -0,0 +1,37 @@
1
+ import FileWrite from './FileWrite';
2
+
3
+ export type UploadPortfolioParams = {
4
+ packageBytes: ArrayBuffer | Uint8Array | Blob;
5
+ operationId?: string;
6
+ timeoutMs?: number | string;
7
+ };
8
+
9
+ const PORTFOLIO_PENDING_PATH = 'vol1:/portfolio/portfolio.pfol.pending';
10
+ const PORTFOLIO_CHUNK_SIZE = 2048;
11
+
12
+ export default class UploadPortfolio extends FileWrite {
13
+ init() {
14
+ const { packageBytes, timeoutMs } = this.payload as UploadPortfolioParams;
15
+ this.payload = {
16
+ ...this.payload,
17
+ path: PORTFOLIO_PENDING_PATH,
18
+ offset: 0,
19
+ data: packageBytes,
20
+ chunkSize: PORTFOLIO_CHUNK_SIZE,
21
+ overwrite: true,
22
+ append: false,
23
+ timeoutMs,
24
+ };
25
+ super.init();
26
+ }
27
+
28
+ async run() {
29
+ const stagedFile = await super.run();
30
+ this.throwIfAborted();
31
+ await this.device.commands.typedCall('PortfolioUpdate', 'Success', {});
32
+ return {
33
+ ...stagedFile,
34
+ portfolioUpdated: true,
35
+ };
36
+ }
37
+ }
@@ -0,0 +1,164 @@
1
+ import {
2
+ PROTOCOL_V2_BLE_FILE_CHUNK_SIZE,
3
+ PROTOCOL_V2_WEBUSB_FILE_CHUNK_SIZE,
4
+ } from '@onekeyfe/hd-transport';
5
+ import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
6
+
7
+ import { DataManager } from '../../data-manager';
8
+
9
+ import type { DeviceCommands } from '../../device/DeviceCommands';
10
+
11
+ export type ProtocolV2FileWriteData = ArrayBuffer | Uint8Array | Blob | string;
12
+
13
+ export type ProtocolV2FileWriteProgress = {
14
+ progress: number;
15
+ transferredBytes: number;
16
+ totalBytes: number;
17
+ rateBytesPerSecond?: number;
18
+ elapsedMs: number;
19
+ };
20
+
21
+ export type ProtocolV2FileWriteOptions = {
22
+ commands: Pick<DeviceCommands, 'typedCall'>;
23
+ path: string;
24
+ data: ProtocolV2FileWriteData;
25
+ offset?: number;
26
+ totalSize?: number;
27
+ chunkSize?: number;
28
+ chunkLen?: number;
29
+ overwrite?: boolean;
30
+ append?: boolean;
31
+ uiPercentage?: number;
32
+ timeoutMs?: number;
33
+ throwIfAborted?: () => void;
34
+ onProgress?: (progress: ProtocolV2FileWriteProgress) => void;
35
+ };
36
+
37
+ const MIN_FILE_CHUNK_SIZE = 64;
38
+
39
+ function getProtocolV2FileChunkLimit() {
40
+ const env = DataManager.getSettings('env');
41
+ return env && DataManager.isBleConnect(env)
42
+ ? PROTOCOL_V2_BLE_FILE_CHUNK_SIZE
43
+ : PROTOCOL_V2_WEBUSB_FILE_CHUNK_SIZE;
44
+ }
45
+
46
+ async function dataToUint8Array(data: ProtocolV2FileWriteData): Promise<Uint8Array> {
47
+ if (typeof data === 'string') return new TextEncoder().encode(data);
48
+ if (data instanceof ArrayBuffer) return new Uint8Array(data);
49
+ if (ArrayBuffer.isView(data)) {
50
+ return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
51
+ }
52
+ if (typeof Blob !== 'undefined' && data instanceof Blob) {
53
+ return new Uint8Array(await data.arrayBuffer());
54
+ }
55
+ throw ERRORS.TypedError(
56
+ HardwareErrorCode.CallMethodInvalidParameter,
57
+ 'Unsupported FilesystemFileWrite data'
58
+ );
59
+ }
60
+
61
+ function normalizeChunkSize(value: unknown, maxChunkSize: number): number {
62
+ const numeric = Number(value);
63
+ if (!Number.isFinite(numeric) || numeric <= 0) return maxChunkSize;
64
+ return Math.min(Math.max(Math.floor(numeric), MIN_FILE_CHUNK_SIZE), maxChunkSize);
65
+ }
66
+
67
+ function getDeviceTransferProgress(before: number, after: number, total: number) {
68
+ if (!Number.isFinite(total) || total <= 0) return 100;
69
+ if (before <= 0 && after < total) return 0;
70
+ if (after >= total) return 100;
71
+ return Math.min(Math.max(Math.ceil((after / total) * 100), 1), 99);
72
+ }
73
+
74
+ function getConfirmedProgress(processed: number, total: number, written: number, length: number) {
75
+ if (Number.isFinite(processed) && Number.isFinite(total) && total > 0) {
76
+ if (processed >= total) return 100;
77
+ return Math.min(Math.max(Math.floor((processed / total) * 100), 0), 99);
78
+ }
79
+ if (length > 0) return written >= length ? 100 : Math.floor((written / length) * 100);
80
+ return 100;
81
+ }
82
+
83
+ export async function writeProtocolV2File(options: ProtocolV2FileWriteOptions) {
84
+ options.throwIfAborted?.();
85
+ const data = await dataToUint8Array(options.data);
86
+ const dataLength = data.byteLength;
87
+ const startOffset =
88
+ Number.isFinite(options.offset) && Number(options.offset) > 0 ? Number(options.offset) : 0;
89
+ const totalSize =
90
+ Number.isFinite(options.totalSize) && Number(options.totalSize) > 0
91
+ ? Number(options.totalSize)
92
+ : startOffset + dataLength;
93
+
94
+ if (totalSize < startOffset + dataLength) {
95
+ throw ERRORS.TypedError(
96
+ HardwareErrorCode.RuntimeError,
97
+ `FilesystemFileWrite totalSize ${totalSize} is smaller than offset + data length ${
98
+ startOffset + dataLength
99
+ }`
100
+ );
101
+ }
102
+
103
+ const chunkSize = normalizeChunkSize(
104
+ options.chunkSize ?? options.chunkLen,
105
+ getProtocolV2FileChunkLimit()
106
+ );
107
+ let written = 0;
108
+ let chunks = 0;
109
+ let lastMessage: Record<string, unknown> | undefined;
110
+ const startTime = Date.now();
111
+
112
+ while (written < dataLength) {
113
+ options.throwIfAborted?.();
114
+ const chunk = data.slice(written, Math.min(written + chunkSize, dataLength));
115
+ const offset = startOffset + written;
116
+ const progress =
117
+ options.uiPercentage ??
118
+ getDeviceTransferProgress(offset, offset + chunk.byteLength, totalSize);
119
+ const response = await options.commands.typedCall(
120
+ 'FilesystemFileWrite',
121
+ 'FilesystemFile',
122
+ {
123
+ file: { path: options.path, offset, total_size: totalSize, data: chunk },
124
+ overwrite: chunks === 0 ? options.overwrite ?? false : false,
125
+ append: options.append ?? false,
126
+ ui_percentage: progress,
127
+ },
128
+ { timeoutMs: options.timeoutMs }
129
+ );
130
+ options.throwIfAborted?.();
131
+ lastMessage = response.message;
132
+ const processedByte = Number(response.message?.processed_byte);
133
+ written =
134
+ Number.isFinite(processedByte) && processedByte > offset
135
+ ? processedByte - startOffset
136
+ : written + chunk.byteLength;
137
+ if (written > dataLength) {
138
+ throw ERRORS.TypedError(
139
+ HardwareErrorCode.RuntimeError,
140
+ `FilesystemFileWrite invalid processed_byte ${processedByte}`
141
+ );
142
+ }
143
+ chunks += 1;
144
+ const elapsedMs = Date.now() - startTime;
145
+ const transferredBytes = Math.min(written, dataLength);
146
+ options.onProgress?.({
147
+ progress: getConfirmedProgress(startOffset + written, totalSize, written, dataLength),
148
+ transferredBytes,
149
+ totalBytes: dataLength,
150
+ rateBytesPerSecond:
151
+ elapsedMs > 0 ? Math.round((transferredBytes / elapsedMs) * 1000) : undefined,
152
+ elapsedMs,
153
+ });
154
+ }
155
+
156
+ return {
157
+ ...lastMessage,
158
+ path: options.path,
159
+ offset: startOffset,
160
+ total_size: totalSize,
161
+ processed_byte: startOffset + written,
162
+ chunks,
163
+ };
164
+ }
package/src/api/index.ts CHANGED
@@ -49,11 +49,16 @@ export { default as ping } from './protocol-v2/Ping';
49
49
  export { default as deviceReboot } from './protocol-v2/DeviceReboot';
50
50
  export { default as deviceInfoGet } from './protocol-v2/DeviceInfoGet';
51
51
  export { default as deviceStatusGet } from './protocol-v2/DeviceStatusGet';
52
+ export { default as deviceGetOnboardingStatus } from './protocol-v2/DeviceGetOnboardingStatus';
52
53
  export { default as deviceSessionGet } from './protocol-v2/DeviceSessionGet';
53
54
  export { default as deviceFirmwareUpdate } from './protocol-v2/DeviceFirmwareUpdate';
54
55
  export { default as deviceGetFirmwareUpdateStatus } from './protocol-v2/DeviceGetFirmwareUpdateStatus';
55
56
  export { default as deviceFactoryInfoSet } from './protocol-v2/DeviceFactoryInfoSet';
56
57
  export { default as deviceFactoryInfoGet } from './protocol-v2/DeviceFactoryInfoGet';
58
+ export { default as deviceSettingsGet } from './protocol-v2/DeviceSettingsGet';
59
+ export { default as deviceSettingsSet } from './protocol-v2/DeviceSettingsSet';
60
+ export { default as deviceSettingsPageShow } from './protocol-v2/DeviceSettingsPageShow';
61
+ export { default as deviceUploadWallpaper } from './protocol-v2/DeviceUploadWallpaper';
57
62
  export { default as filesystemPermissionFix } from './protocol-v2/FilesystemPermissionFix';
58
63
  export { default as filesystemFormat } from './protocol-v2/FilesystemFormat';
59
64
  export { default as filesystemDiskControl } from './protocol-v2/FilesystemDiskControl';
@@ -66,6 +71,7 @@ export { default as dirRemove } from './DirRemove';
66
71
  export { default as pathInfo } from './PathInfo';
67
72
  export { default as filesystemFileRead } from './FileRead';
68
73
  export { default as filesystemFileWrite } from './FileWrite';
74
+ export { default as uploadPortfolio } from './UploadPortfolio';
69
75
  export { default as filesystemFileDelete } from './FileDelete';
70
76
  export { default as filesystemDirList } from './DirList';
71
77
  export { default as filesystemDirMake } from './DirMake';
@@ -0,0 +1,18 @@
1
+ import { BaseMethod } from '../BaseMethod';
2
+
3
+ export default class DeviceGetOnboardingStatus extends BaseMethod {
4
+ init() {
5
+ this.requireProtocolV2 = true;
6
+ this.skipForceUpdateCheck = true;
7
+ this.useDevicePassphraseState = false;
8
+ }
9
+
10
+ async run() {
11
+ const { message } = await this.device.commands.typedCall(
12
+ 'DevGetOnboardingStatus',
13
+ 'DevOnboardingStatus',
14
+ {}
15
+ );
16
+ return message;
17
+ }
18
+ }