@onekeyfe/hd-core 1.2.0-alpha.53 → 1.2.0-alpha.54

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 (251) hide show
  1. package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +0 -78
  2. package/__tests__/DeviceCommands.test.ts +0 -45
  3. package/__tests__/DeviceEventRegistration.test.ts +0 -6
  4. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +3 -198
  5. package/__tests__/connectSettings.test.ts +47 -5
  6. package/__tests__/device-initialize-timeout.test.ts +56 -0
  7. package/__tests__/device-lifecycle-events.test.ts +3 -105
  8. package/__tests__/firmware-update/check-all-firmware-release.test.ts +175 -0
  9. package/__tests__/firmware-update/firmware-artifact-source.test.ts +119 -0
  10. package/__tests__/firmware-update/firmware-host-binding.test.ts +70 -0
  11. package/__tests__/firmware-update/firmware-preparation-error.test.ts +27 -0
  12. package/__tests__/firmware-update/firmware-update-bootloader-poll.test.ts +200 -0
  13. package/__tests__/firmware-update/firmware-update-capabilities.test.ts +13 -0
  14. package/__tests__/firmware-update/firmware-update-plan-bootloader-identity.test.ts +294 -0
  15. package/__tests__/firmware-update/firmware-update-plan.test.ts +593 -0
  16. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +213 -0
  17. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +59 -31
  18. package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +58 -0
  19. package/__tests__/firmware-update/firmware-upload-source.test.ts +70 -0
  20. package/__tests__/get-device-state.test.ts +12 -132
  21. package/__tests__/homescreen.test.ts +1 -41
  22. package/__tests__/logBlockEvent.test.ts +1 -1
  23. package/__tests__/method-protocol-support.test.ts +0 -19
  24. package/__tests__/open-wallet-session.test.ts +33 -446
  25. package/__tests__/protocol-v2-unlock-policy.test.ts +35 -201
  26. package/__tests__/protocol-v2.test.ts +1034 -1378
  27. package/__tests__/protocolV2UiInteraction.test.ts +0 -63
  28. package/__tests__/public-pro2-api-boundary.test.ts +0 -1
  29. package/__tests__/search-devices.test.ts +3 -12
  30. package/dist/api/BaseMethod.d.ts +1 -3
  31. package/dist/api/BaseMethod.d.ts.map +1 -1
  32. package/dist/api/CheckAllFirmwareRelease.d.ts +1 -2
  33. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  34. package/dist/api/FirmwareUpdate.d.ts.map +1 -1
  35. package/dist/api/FirmwareUpdateV2.d.ts +11 -0
  36. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  37. package/dist/api/FirmwareUpdateV3.d.ts +7 -3
  38. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  39. package/dist/api/FirmwareUpdateV4.d.ts +25 -11
  40. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  41. package/dist/api/GetPassphraseState.d.ts.map +1 -1
  42. package/dist/api/OpenWalletSession.d.ts.map +1 -1
  43. package/dist/api/SearchDevices.d.ts.map +1 -1
  44. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
  45. package/dist/api/device/DeviceUnlock.d.ts +2 -2
  46. package/dist/api/device/DeviceUnlock.d.ts.map +1 -1
  47. package/dist/api/device/DeviceUpdateBootloader.d.ts +5 -1
  48. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  49. package/dist/api/firmware/FirmwareArtifactSource.d.ts +25 -0
  50. package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +1 -0
  51. package/dist/api/firmware/FirmwareHostBinding.d.ts +6 -0
  52. package/dist/api/firmware/FirmwareHostBinding.d.ts.map +1 -0
  53. package/dist/api/firmware/FirmwarePreparationError.d.ts +3 -0
  54. package/dist/api/firmware/FirmwarePreparationError.d.ts.map +1 -0
  55. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +9 -0
  56. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  57. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +3 -0
  58. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +1 -0
  59. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +25 -0
  60. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -0
  61. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +32 -0
  62. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -0
  63. package/dist/api/firmware/getBinary.d.ts +7 -0
  64. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  65. package/dist/api/firmware/progressThrottle.d.ts +3 -0
  66. package/dist/api/firmware/progressThrottle.d.ts.map +1 -0
  67. package/dist/api/firmware/updateBootloader.d.ts +2 -0
  68. package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
  69. package/dist/api/firmware/uploadFirmware.d.ts +9 -1
  70. package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
  71. package/dist/api/helpers/protocolV2FileWrite.d.ts +0 -2
  72. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
  73. package/dist/api/index.d.ts +0 -2
  74. package/dist/api/index.d.ts.map +1 -1
  75. package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -1
  76. package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
  77. package/dist/api/stellar/StellarSignTransaction.d.ts +1 -1
  78. package/dist/core/deviceEventRegistration.d.ts +0 -2
  79. package/dist/core/deviceEventRegistration.d.ts.map +1 -1
  80. package/dist/core/index.d.ts.map +1 -1
  81. package/dist/data-manager/DataManager.d.ts +2 -4
  82. package/dist/data-manager/DataManager.d.ts.map +1 -1
  83. package/dist/data-manager/connectSettings.d.ts.map +1 -1
  84. package/dist/device/Device.d.ts +6 -29
  85. package/dist/device/Device.d.ts.map +1 -1
  86. package/dist/device/DeviceCommands.d.ts +4 -4
  87. package/dist/device/DeviceCommands.d.ts.map +1 -1
  88. package/dist/device/DevicePool.d.ts.map +1 -1
  89. package/dist/events/device.d.ts +0 -4
  90. package/dist/events/device.d.ts.map +1 -1
  91. package/dist/events/logBlockEvent.d.ts.map +1 -1
  92. package/dist/events/ui-promise.d.ts +2 -4
  93. package/dist/events/ui-promise.d.ts.map +1 -1
  94. package/dist/events/ui-request.d.ts +2 -31
  95. package/dist/events/ui-request.d.ts.map +1 -1
  96. package/dist/events/ui-response.d.ts +2 -10
  97. package/dist/events/ui-response.d.ts.map +1 -1
  98. package/dist/index.d.ts +231 -214
  99. package/dist/index.d.ts.map +1 -1
  100. package/dist/index.js +3497 -2559
  101. package/dist/inject.d.ts +1 -6
  102. package/dist/inject.d.ts.map +1 -1
  103. package/dist/lowLevelInject.d.ts.map +1 -1
  104. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  105. package/dist/protocols/protocol-v2/lockedError.d.ts +2 -0
  106. package/dist/protocols/protocol-v2/lockedError.d.ts.map +1 -0
  107. package/dist/protocols/protocol-v2/uiInteraction.d.ts +4 -7
  108. package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -1
  109. package/dist/protocols/protocol-v2/unlockPolicy.d.ts +3 -1
  110. package/dist/protocols/protocol-v2/unlockPolicy.d.ts.map +1 -1
  111. package/dist/protocols/protocol-v2/unlockRetry.d.ts +10 -0
  112. package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +1 -0
  113. package/dist/protocols/protocol-v2/walletSession.d.ts +1 -5
  114. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  115. package/dist/topLevelInject.d.ts.map +1 -1
  116. package/dist/types/api/checkAllFirmwareRelease.d.ts +4 -2
  117. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  118. package/dist/types/api/deviceUnlock.d.ts +1 -5
  119. package/dist/types/api/deviceUnlock.d.ts.map +1 -1
  120. package/dist/types/api/deviceUpdateBootloader.d.ts +6 -0
  121. package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
  122. package/dist/types/api/export.d.ts +4 -2
  123. package/dist/types/api/export.d.ts.map +1 -1
  124. package/dist/types/api/firmwareUpdate.d.ts +70 -2
  125. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  126. package/dist/types/api/firmwareUpdateCapabilities.d.ts +9 -0
  127. package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +1 -0
  128. package/dist/types/api/firmwareUpdatePlan.d.ts +28 -0
  129. package/dist/types/api/firmwareUpdatePlan.d.ts.map +1 -0
  130. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +42 -0
  131. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +1 -0
  132. package/dist/types/api/index.d.ts +10 -7
  133. package/dist/types/api/index.d.ts.map +1 -1
  134. package/dist/types/api/protocolV2.d.ts +0 -3
  135. package/dist/types/api/protocolV2.d.ts.map +1 -1
  136. package/dist/types/device.d.ts.map +1 -1
  137. package/dist/types/params.d.ts +0 -1
  138. package/dist/types/params.d.ts.map +1 -1
  139. package/dist/types/settings.d.ts +13 -22
  140. package/dist/types/settings.d.ts.map +1 -1
  141. package/dist/utils/deviceFeaturesCompat.d.ts +1 -0
  142. package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -1
  143. package/dist/utils/deviceFeaturesUtils.d.ts +0 -4
  144. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  145. package/dist/utils/homescreen.d.ts +0 -4
  146. package/dist/utils/homescreen.d.ts.map +1 -1
  147. package/dist/utils/index.d.ts +1 -1
  148. package/dist/utils/index.d.ts.map +1 -1
  149. package/dist/utils/patch.d.ts +1 -1
  150. package/dist/utils/patch.d.ts.map +1 -1
  151. package/dist/utils/pro2Wallpaper.d.ts +0 -10
  152. package/dist/utils/pro2Wallpaper.d.ts.map +1 -1
  153. package/package.json +4 -4
  154. package/src/api/BaseMethod.ts +10 -8
  155. package/src/api/CheckAllFirmwareRelease.ts +57 -54
  156. package/src/api/FirmwareUpdate.ts +15 -7
  157. package/src/api/FirmwareUpdateV2.ts +316 -126
  158. package/src/api/FirmwareUpdateV3.ts +265 -63
  159. package/src/api/FirmwareUpdateV4.ts +965 -566
  160. package/src/api/GetPassphraseState.ts +1 -7
  161. package/src/api/OpenWalletSession.ts +17 -54
  162. package/src/api/SearchDevices.ts +1 -3
  163. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +11 -32
  164. package/src/api/device/DeviceUnlock.ts +3 -8
  165. package/src/api/device/DeviceUpdateBootloader.ts +122 -6
  166. package/src/api/firmware/FirmwareArtifactSource.ts +278 -0
  167. package/src/api/firmware/FirmwareHostBinding.ts +100 -0
  168. package/src/api/firmware/FirmwarePreparationError.ts +12 -0
  169. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +83 -16
  170. package/src/api/firmware/FirmwareUpdateCapabilities.ts +9 -0
  171. package/src/api/firmware/FirmwareUpdatePlan.ts +772 -0
  172. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +415 -0
  173. package/src/api/firmware/progressThrottle.ts +44 -0
  174. package/src/api/firmware/updateBootloader.ts +19 -4
  175. package/src/api/firmware/uploadFirmware.ts +144 -39
  176. package/src/api/helpers/protocolV2FileWrite.ts +4 -11
  177. package/src/api/index.ts +0 -2
  178. package/src/api/protocol-v2/ProtocolInfoRequest.ts +1 -3
  179. package/src/api/protocol-v2/helpers.ts +0 -1
  180. package/src/core/deviceEventRegistration.ts +0 -4
  181. package/src/core/index.ts +83 -167
  182. package/src/data/messages/messages-protocol-v2.json +0 -25
  183. package/src/data-manager/DataManager.ts +38 -67
  184. package/src/data-manager/connectSettings.ts +11 -0
  185. package/src/device/Device.ts +42 -258
  186. package/src/device/DeviceCommands.ts +1 -7
  187. package/src/device/DevicePool.ts +2 -7
  188. package/src/events/device.ts +0 -4
  189. package/src/events/logBlockEvent.ts +0 -1
  190. package/src/events/ui-promise.ts +2 -4
  191. package/src/events/ui-request.ts +2 -36
  192. package/src/events/ui-response.ts +2 -12
  193. package/src/index.ts +5 -0
  194. package/src/inject.ts +24 -60
  195. package/src/lowLevelInject.ts +8 -7
  196. package/src/protocols/protocol-v2/features.ts +3 -10
  197. package/src/protocols/protocol-v2/lockedError.ts +10 -0
  198. package/src/protocols/protocol-v2/uiInteraction.ts +6 -46
  199. package/src/protocols/protocol-v2/unlockPolicy.ts +105 -1
  200. package/src/protocols/protocol-v2/unlockRetry.ts +109 -0
  201. package/src/protocols/protocol-v2/walletSession.ts +30 -180
  202. package/src/topLevelInject.ts +8 -7
  203. package/src/types/api/checkAllFirmwareRelease.ts +4 -2
  204. package/src/types/api/deviceUnlock.ts +1 -12
  205. package/src/types/api/deviceUpdateBootloader.ts +6 -0
  206. package/src/types/api/export.ts +18 -1
  207. package/src/types/api/firmwareUpdate.ts +76 -3
  208. package/src/types/api/firmwareUpdateCapabilities.ts +9 -0
  209. package/src/types/api/firmwareUpdatePlan.ts +38 -0
  210. package/src/types/api/firmwareUpdatePreparedPlan.ts +53 -0
  211. package/src/types/api/index.ts +14 -13
  212. package/src/types/api/protocolV2.ts +0 -13
  213. package/src/types/device.ts +0 -3
  214. package/src/types/params.ts +1 -7
  215. package/src/types/settings.ts +13 -42
  216. package/src/utils/deviceFeaturesCompat.ts +6 -0
  217. package/src/utils/deviceFeaturesUtils.ts +0 -8
  218. package/src/utils/homescreen.ts +6 -32
  219. package/src/utils/index.ts +1 -6
  220. package/src/utils/pro2Wallpaper.ts +8 -35
  221. package/__tests__/deviceUploadNft.test.ts +0 -293
  222. package/__tests__/pro2Nft.test.ts +0 -108
  223. package/__tests__/protocol-binding.test.ts +0 -89
  224. package/__tests__/protocol-v2-resources.test.ts +0 -284
  225. package/__tests__/protocol-v2-ui-lifecycle.test.ts +0 -91
  226. package/__tests__/uiProgressThrottle.test.ts +0 -74
  227. package/__tests__/uiPromiseRegistry.test.ts +0 -86
  228. package/dist/api/DetectDeviceConnectProtocol.d.ts +0 -7
  229. package/dist/api/DetectDeviceConnectProtocol.d.ts.map +0 -1
  230. package/dist/api/protocol-v2/DeviceUploadNft.d.ts +0 -31
  231. package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +0 -1
  232. package/dist/core/uiPromiseRegistry.d.ts +0 -4
  233. package/dist/core/uiPromiseRegistry.d.ts.map +0 -1
  234. package/dist/protocols/protocol-v2/resources.d.ts +0 -30
  235. package/dist/protocols/protocol-v2/resources.d.ts.map +0 -1
  236. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +0 -22
  237. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +0 -1
  238. package/dist/types/api/detectDeviceConnectProtocol.d.ts +0 -4
  239. package/dist/types/api/detectDeviceConnectProtocol.d.ts.map +0 -1
  240. package/dist/utils/pro2Nft.d.ts +0 -31
  241. package/dist/utils/pro2Nft.d.ts.map +0 -1
  242. package/dist/utils/uiProgressThrottle.d.ts +0 -3
  243. package/dist/utils/uiProgressThrottle.d.ts.map +0 -1
  244. package/src/api/DetectDeviceConnectProtocol.ts +0 -18
  245. package/src/api/protocol-v2/DeviceUploadNft.ts +0 -189
  246. package/src/core/uiPromiseRegistry.ts +0 -41
  247. package/src/protocols/protocol-v2/resources.ts +0 -359
  248. package/src/protocols/protocol-v2/unlockPolicyRunner.ts +0 -118
  249. package/src/types/api/detectDeviceConnectProtocol.ts +0 -7
  250. package/src/utils/pro2Nft.ts +0 -142
  251. package/src/utils/uiProgressThrottle.ts +0 -63
@@ -14,18 +14,46 @@ import { BaseMethod } from './BaseMethod';
14
14
  import { validateParams } from './helpers/paramsValidator';
15
15
  import { DevicePool } from '../device/DevicePool';
16
16
  import { getBinary, getInfo, getSysResourceBinary } from './firmware/getBinary';
17
- import { updateResources, uploadFirmware } from './firmware/uploadFirmware';
18
- import { LoggerNames, getLogger, wait } from '../utils';
17
+ import { normalizeFirmwarePreparationError } from './firmware/FirmwarePreparationError';
18
+ import {
19
+ updateResources,
20
+ updateResourcesFromSources,
21
+ uploadFirmwareFromSource,
22
+ } from './firmware/uploadFirmware';
23
+ import { BOOTLOADER_POLL_INITIALIZE_TIMEOUT_MS } from './firmware/FirmwareUpdateBaseMethod';
24
+ import { LoggerNames, getDeviceType, getDeviceUUID, getLogger, wait } from '../utils';
25
+ import { resolveDeviceBootloaderMode } from '../utils/deviceFeaturesCompat';
19
26
  import { FirmwareUpdateTipMessage, createUiMessage } from '../events/ui-request';
20
27
  import { DeviceModelToTypes } from '../types';
21
28
  import { DataManager } from '../data-manager';
22
29
  import { DEVICE } from '../events';
30
+ import { type FirmwareByteSource, openFirmwareByteSource } from './firmware/FirmwareArtifactSource';
31
+ import { resolveFirmwareUpdateHostBinding } from './firmware/FirmwareHostBinding';
32
+ import {
33
+ assertFirmwareUpdatePreparedPlanBinding,
34
+ assertFirmwareUpdatePreparedPlanDeviceIdentity,
35
+ getFirmwareUpdateResourceName,
36
+ } from './firmware/FirmwareUpdatePreparedPlan';
23
37
 
24
38
  import type { Features, KnownDevice } from '../types';
25
39
  import type { FirmwareBinary } from './firmware/getBinary';
40
+ import type {
41
+ FirmwareArtifactReader,
42
+ FirmwareArtifactReference,
43
+ } from '../types/api/firmwareUpdate';
44
+ import type { FirmwareUpdatePreparedPlan } from '../types/api/firmwareUpdatePreparedPlan';
26
45
 
27
46
  type Params = {
28
47
  binary?: ArrayBuffer;
48
+ artifact?: FirmwareArtifactReference;
49
+ resourceArtifact?: FirmwareArtifactReference;
50
+ resourceEntries?: Array<{
51
+ entryName: string;
52
+ artifact: FirmwareArtifactReference;
53
+ }>;
54
+ artifactReader?: FirmwareArtifactReader;
55
+ preparedPlan?: FirmwareUpdatePreparedPlan;
56
+ platform?: FirmwareUpdatePreparedPlan['platform'];
29
57
  version?: number[];
30
58
  updateType: 'firmware' | 'ble';
31
59
  forcedUpdateRes?: boolean;
@@ -106,6 +134,13 @@ const normalizeFirmwareBinary = (binary: unknown): FirmwareBinary | undefined =>
106
134
  return normalized.buffer;
107
135
  };
108
136
 
137
+ const toArrayBuffer = (binary: FirmwareBinary): ArrayBuffer => {
138
+ if (binary instanceof ArrayBuffer) {
139
+ return binary;
140
+ }
141
+ return new Uint8Array(binary.buffer, binary.byteOffset, binary.byteLength).slice().buffer;
142
+ };
143
+
109
144
  export default class FirmwareUpdateV2 extends BaseMethod<Params> {
110
145
  checkPromise: Deferred<any> | null = null;
111
146
 
@@ -152,6 +187,38 @@ export default class FirmwareUpdateV2 extends BaseMethod<Params> {
152
187
  binary: payload.binary,
153
188
  };
154
189
  }
190
+
191
+ if ('artifact' in payload) {
192
+ const hostBinding = resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration);
193
+ const target = payload.isUpdateBootloader ? 'bootloader' : payload.updateType;
194
+ const resourceBindings = [
195
+ ...(payload.resourceArtifact
196
+ ? [{ target: 'resource' as const, artifact: payload.resourceArtifact }]
197
+ : []),
198
+ ...(payload.resourceEntries ?? []).map(
199
+ (entry: { entryName: string; artifact: FirmwareArtifactReference }) => ({
200
+ target: 'resource' as const,
201
+ entryName: entry.entryName,
202
+ artifact: entry.artifact,
203
+ })
204
+ ),
205
+ ];
206
+ assertFirmwareUpdatePreparedPlanBinding({
207
+ preparedPlan: payload.preparedPlan,
208
+ executor: 'v2',
209
+ platform: payload.platform,
210
+ scopeTargets: [target, ...(target === 'firmware' ? (['resource'] as const) : [])],
211
+ bindings: [{ target, artifact: payload.artifact }, ...resourceBindings],
212
+ });
213
+ this.params = {
214
+ ...this.params,
215
+ preparedPlan: payload.preparedPlan,
216
+ artifact: payload.artifact,
217
+ resourceArtifact: payload.resourceArtifact,
218
+ resourceEntries: payload.resourceEntries,
219
+ artifactReader: hostBinding.artifactReader,
220
+ };
221
+ }
155
222
  }
156
223
 
157
224
  postTipMessage = (message: string) => {
@@ -187,6 +254,10 @@ export default class FirmwareUpdateV2 extends BaseMethod<Params> {
187
254
  this.checkPromise = createDeferred();
188
255
  const env = DataManager.getSettings('env');
189
256
  const isBleReconnect = connectId && DataManager.isBleConnect(env);
257
+ // Tracks the in-flight BLE probe so interval ticks never overlap: a superseded
258
+ // Initialize left pending on the V1 transport is exactly what later times out
259
+ // and used to tear down the connection mid-firmware-upload.
260
+ let bleProbeInFlight = false;
190
261
 
191
262
  Log.log('FirmwareUpdateV2 [checkDeviceToBootloader] isBleReconnect: ', isBleReconnect);
192
263
 
@@ -234,14 +305,18 @@ export default class FirmwareUpdateV2 extends BaseMethod<Params> {
234
305
  }
235
306
 
236
307
  if (isBleReconnect) {
308
+ if (bleProbeInFlight) return;
309
+ bleProbeInFlight = true;
237
310
  try {
238
311
  await this.device.deviceConnector?.acquire(
239
312
  this.device.originalDescriptor.id,
240
313
  null,
241
- true,
242
- this.payload.connectProtocol ?? this.device.originalDescriptor.protocolType
314
+ true
243
315
  );
244
- await this.device.initialize();
316
+ // Bound each probe so a request the rebooting device never received
317
+ // frees the slot for the next tick instead of hanging into the
318
+ // 30s reboot budget.
319
+ await this.device.initialize({ timeoutMs: BOOTLOADER_POLL_INITIALIZE_TIMEOUT_MS });
245
320
  if (this.device.isBootloader()) {
246
321
  clearInterval(intervalTimer);
247
322
  this.checkPromise?.resolve(true);
@@ -249,6 +324,8 @@ export default class FirmwareUpdateV2 extends BaseMethod<Params> {
249
324
  } catch (e) {
250
325
  // ignore error because of device is not connected
251
326
  Log.log('catch Bluetooth error when device is restarting: ', e);
327
+ } finally {
328
+ bleProbeInFlight = false;
252
329
  }
253
330
  } else {
254
331
  await this._checkDeviceInBootloaderMode(connectId, intervalTimer, timeoutTimer);
@@ -273,9 +350,7 @@ export default class FirmwareUpdateV2 extends BaseMethod<Params> {
273
350
  ) {
274
351
  const deviceDiff = await this.device.deviceConnector?.enumerate();
275
352
  const devicesDescriptor = deviceDiff?.descriptors ?? [];
276
- const { deviceList } = await DevicePool.getDevices(devicesDescriptor, connectId, {
277
- connectProtocol: this.payload.connectProtocol ?? this.device.originalDescriptor.protocolType,
278
- });
353
+ const { deviceList } = await DevicePool.getDevices(devicesDescriptor, connectId);
279
354
 
280
355
  if (deviceList.length === 1 && deviceList[0]?.isBootloader()) {
281
356
  // should update current device from cache
@@ -344,151 +419,266 @@ export default class FirmwareUpdateV2 extends BaseMethod<Params> {
344
419
  const { features, commands } = device;
345
420
  const deviceType = device.getCurrentDeviceType();
346
421
 
422
+ // Protocol V2 (Pro2) uses DeviceFirmwareUpdate and must not enter this legacy flow.
423
+ if (device.isProtocolV2()) {
424
+ throw ERRORS.TypedError(
425
+ HardwareErrorCode.RuntimeError,
426
+ 'Protocol V2 firmware update must use firmwareUpdateV4'
427
+ );
428
+ }
429
+ if (params.preparedPlan) {
430
+ assertFirmwareUpdatePreparedPlanDeviceIdentity({
431
+ preparedPlan: params.preparedPlan,
432
+ deviceIdentity: getDeviceUUID(features) || undefined,
433
+ bootloaderMode: resolveDeviceBootloaderMode(features),
434
+ deviceModel: String(getDeviceType(features)),
435
+ });
436
+ }
437
+
347
438
  const deviceFirmwareType = device.getCurrentFirmwareType();
348
439
  const firmwareType = params.firmwareType ?? deviceFirmwareType;
349
440
 
350
441
  this.checkVersionForCopyTouchResource(features, firmwareType);
351
442
 
352
- let preparedBinary: FirmwareBinary | undefined;
353
- const acquireFirmwareBinary = async (): Promise<FirmwareBinary> => {
354
- try {
355
- if (preparedBinary) {
356
- return preparedBinary;
357
- }
358
-
359
- if (params.binary !== undefined) {
360
- preparedBinary = normalizeFirmwareBinary(params.binary);
361
- if (!preparedBinary) {
362
- throw new Error('firmware binary is empty or invalid');
443
+ let preparedSource: FirmwareByteSource | undefined;
444
+ try {
445
+ const acquireFirmwareSource = async (): Promise<FirmwareByteSource> => {
446
+ try {
447
+ if (preparedSource) {
448
+ return preparedSource;
363
449
  }
364
- return preparedBinary;
365
- }
366
450
 
367
- if (!device.features) {
368
- throw ERRORS.TypedError(
369
- HardwareErrorCode.RuntimeError,
370
- 'no features found for this device'
371
- );
372
- }
373
-
374
- this.postTipMessage('DownloadFirmware');
375
- const firmware = await getBinary({
376
- features: device.features,
377
- version: params.version,
378
- updateType: params.updateType,
379
- isUpdateBootloader: params.isUpdateBootloader,
380
- firmwareType,
381
- requestOptions: FIRMWARE_DOWNLOAD_REQUEST_OPTIONS,
382
- });
383
- preparedBinary = normalizeFirmwareBinary(firmware.binary);
384
- if (!preparedBinary) {
385
- throw new Error('downloaded firmware binary is empty or invalid');
386
- }
387
- this.postTipMessage('DownloadFirmwareSuccess');
388
- return preparedBinary;
389
- } catch (err) {
390
- throw ERRORS.TypedError(HardwareErrorCode.FirmwareUpdateDownloadFailed, err.message ?? err);
391
- }
392
- };
451
+ if (params.binary !== undefined) {
452
+ const binary = normalizeFirmwareBinary(params.binary);
453
+ if (!binary) {
454
+ throw new Error('firmware binary is empty or invalid');
455
+ }
456
+ const source = await openFirmwareByteSource({
457
+ binary: toArrayBuffer(binary),
458
+ });
459
+ if (!source) {
460
+ throw new Error('firmware binary is empty or invalid');
461
+ }
462
+ preparedSource = source;
463
+ return source;
464
+ }
393
465
 
394
- if (!device.isBootloader() && features) {
395
- const serialNo = device.getCurrentSerialNo();
396
- // should go to bootloader mode manually
397
- if (this.isEnteredManuallyBoot()) {
398
- return Promise.reject(ERRORS.TypedError(HardwareErrorCode.FirmwareUpdateManuallyEnterBoot));
399
- }
466
+ if (params.artifact) {
467
+ const source = await openFirmwareByteSource({
468
+ artifact: params.artifact,
469
+ reader: params.artifactReader,
470
+ });
471
+ if (!source) {
472
+ throw new Error('firmware artifact is not prepared');
473
+ }
474
+ preparedSource = source;
475
+ return source;
476
+ }
400
477
 
401
- // check & upgrade firmware resource
402
- if (this.isSupportResourceUpdate(params.updateType)) {
403
- this.postTipMessage('CheckLatestUiResource');
404
- const resourceUrl = DataManager.getSysResourcesLatestRelease({
405
- features,
406
- forcedUpdateRes: params.forcedUpdateRes,
407
- firmwareType,
408
- });
409
- if (resourceUrl) {
410
- this.postTipMessage('DownloadLatestUiResource');
411
- const resource = await getSysResourceBinary(resourceUrl);
412
- this.postTipMessage('DownloadLatestUiResourceSuccess');
413
- if (resource) {
414
- await updateResources(
415
- this.device.getCommands().typedCall.bind(this.device.getCommands()),
416
- this.postMessage,
417
- device,
418
- resource.binary
478
+ if (!device.features) {
479
+ throw ERRORS.TypedError(
480
+ HardwareErrorCode.RuntimeError,
481
+ 'no features found for this device'
419
482
  );
420
483
  }
421
- }
422
- }
423
-
424
- // check if the device commands has been disposed
425
- this.device?.commands?.checkDisposed();
426
484
 
427
- // A failed firmware download must leave the device in normal mode.
428
- await acquireFirmwareBinary();
485
+ if (
486
+ params.artifactReader ||
487
+ DataManager.getSettings('firmwareManifestMode') === 'external-only'
488
+ ) {
489
+ throw ERRORS.TypedError(
490
+ HardwareErrorCode.RuntimeError,
491
+ 'Firmware must be prepared by the external firmware host',
492
+ {
493
+ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
494
+ artifactName: 'firmware',
495
+ }
496
+ );
497
+ }
429
498
 
430
- // The request may outlive the current transport command instance.
431
- this.device?.commands?.checkDisposed();
499
+ this.postTipMessage('DownloadFirmware');
500
+ const firmware = await getBinary({
501
+ features: device.features,
502
+ version: params.version,
503
+ updateType: params.updateType,
504
+ isUpdateBootloader: params.isUpdateBootloader,
505
+ firmwareType,
506
+ requestOptions: FIRMWARE_DOWNLOAD_REQUEST_OPTIONS,
507
+ });
508
+ const binary = normalizeFirmwareBinary(firmware.binary);
509
+ if (!binary) {
510
+ throw new Error('downloaded firmware binary is empty or invalid');
511
+ }
512
+ const source = await openFirmwareByteSource({
513
+ binary: toArrayBuffer(binary),
514
+ });
515
+ if (!source) {
516
+ throw new Error('downloaded firmware binary is empty or invalid');
517
+ }
518
+ preparedSource = source;
519
+ this.postTipMessage('DownloadFirmwareSuccess');
520
+ return source;
521
+ } catch (err) {
522
+ throw normalizeFirmwarePreparationError(err);
523
+ }
524
+ };
432
525
 
433
- // auto go to bootloader mode
434
- try {
435
- this.postTipMessage('AutoRebootToBootloader');
436
- const bootRes = await commands.typedCall('DeviceBackToBoot', 'Success');
437
- // @ts-expect-error
438
- if (bootRes.type === 'CallMethodError') {
439
- throw ERRORS.TypedError(HardwareErrorCode.FirmwareUpdateAutoEnterBootFailure);
526
+ if (!device.isBootloader() && features) {
527
+ const serialNo = device.getCurrentSerialNo();
528
+ // should go to bootloader mode manually
529
+ if (this.isEnteredManuallyBoot()) {
530
+ throw ERRORS.TypedError(HardwareErrorCode.FirmwareUpdateManuallyEnterBoot);
440
531
  }
441
- this.postTipMessage('GoToBootloaderSuccess');
442
- this.checkDeviceToBootloader(this.payload.connectId);
443
532
 
444
- // force clean classic device cache so that the device can initialize again
445
- if (DeviceModelToTypes.model_classic.includes(deviceType)) {
446
- DevicePool.clearDeviceCache(serialNo);
533
+ // All network-backed input must be ready before the first device mutation.
534
+ await acquireFirmwareSource();
535
+
536
+ // check & upgrade firmware resource
537
+ if (this.isSupportResourceUpdate(params.updateType)) {
538
+ this.postTipMessage('CheckLatestUiResource');
539
+ const resourceUrl = DataManager.getSysResourcesLatestRelease({
540
+ features,
541
+ forcedUpdateRes: params.forcedUpdateRes,
542
+ firmwareType,
543
+ });
544
+ if (resourceUrl) {
545
+ this.postTipMessage('DownloadLatestUiResource');
546
+ if (params.resourceEntries?.length) {
547
+ const sources: Array<{
548
+ entryName: string;
549
+ source: FirmwareByteSource;
550
+ }> = [];
551
+ try {
552
+ for (const entry of params.resourceEntries) {
553
+ const resourceName = getFirmwareUpdateResourceName(entry.entryName);
554
+ const source = await openFirmwareByteSource({
555
+ artifact: entry.artifact,
556
+ reader: params.artifactReader,
557
+ });
558
+ if (!source) {
559
+ throw new Error('Firmware resource entry is not prepared');
560
+ }
561
+ sources.push({ entryName: resourceName, source });
562
+ }
563
+ await updateResourcesFromSources(
564
+ this.device.getCommands().typedCall.bind(this.device.getCommands()),
565
+ this.postMessage,
566
+ device,
567
+ sources.map(entry => ({
568
+ entryName: entry.entryName,
569
+ source: entry.source,
570
+ }))
571
+ );
572
+ } finally {
573
+ await Promise.all(
574
+ sources.map(entry => entry.source.close().catch(() => undefined))
575
+ );
576
+ }
577
+ this.postTipMessage('DownloadLatestUiResourceSuccess');
578
+ } else {
579
+ let resourceBinary: ArrayBuffer | Buffer;
580
+ if (params.resourceArtifact) {
581
+ throw ERRORS.TypedError(
582
+ HardwareErrorCode.RuntimeError,
583
+ 'Firmware resource archive must be materialized into prepared entries',
584
+ {
585
+ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
586
+ artifactName: 'resource',
587
+ }
588
+ );
589
+ } else {
590
+ if (
591
+ params.artifactReader ||
592
+ DataManager.getSettings('firmwareManifestMode') === 'external-only'
593
+ ) {
594
+ throw ERRORS.TypedError(
595
+ HardwareErrorCode.RuntimeError,
596
+ 'Firmware resource must be prepared by the external firmware host',
597
+ {
598
+ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
599
+ artifactName: 'resource',
600
+ }
601
+ );
602
+ }
603
+ resourceBinary = (await getSysResourceBinary(resourceUrl)).binary;
604
+ }
605
+ this.postTipMessage('DownloadLatestUiResourceSuccess');
606
+ await updateResources(
607
+ this.device.getCommands().typedCall.bind(this.device.getCommands()),
608
+ this.postMessage,
609
+ device,
610
+ resourceBinary
611
+ );
612
+ }
613
+ }
447
614
  }
448
- delete DevicePool.devicesCache[''];
449
- await this.checkPromise?.promise;
450
- this.checkPromise = null;
451
615
 
452
- // check if the device commands has been disposed
616
+ // The request may outlive the current transport command instance.
453
617
  this.device?.commands?.checkDisposed();
454
618
 
455
- /**
456
- * Touch 1 with bootloader v2.5.0 issue: BLE chip need more time for looking up name, here change the delay time to 3000ms after rebooting.
457
- */
458
- const isTouch = DeviceModelToTypes.model_touch.includes(deviceType);
459
- await wait(isTouch ? 3000 : 1500);
460
- } catch (e) {
461
- if (e instanceof HardwareError) {
462
- return Promise.reject(e);
619
+ // auto go to bootloader mode
620
+ try {
621
+ this.postTipMessage('AutoRebootToBootloader');
622
+ const bootRes = await commands.typedCall('DeviceBackToBoot', 'Success');
623
+ // @ts-expect-error
624
+ if (bootRes.type === 'CallMethodError') {
625
+ throw ERRORS.TypedError(HardwareErrorCode.FirmwareUpdateAutoEnterBootFailure);
626
+ }
627
+ this.postTipMessage('GoToBootloaderSuccess');
628
+ this.checkDeviceToBootloader(this.payload.connectId);
629
+
630
+ // force clean classic device cache so that the device can initialize again
631
+ if (DeviceModelToTypes.model_classic.includes(deviceType)) {
632
+ DevicePool.clearDeviceCache(serialNo);
633
+ }
634
+ delete DevicePool.devicesCache[''];
635
+ await this.checkPromise?.promise;
636
+ this.checkPromise = null;
637
+
638
+ // check if the device commands has been disposed
639
+ this.device?.commands?.checkDisposed();
640
+
641
+ /**
642
+ * Touch 1 with bootloader v2.5.0 issue: BLE chip need more time for looking up name, here change the delay time to 3000ms after rebooting.
643
+ */
644
+ const isTouch = DeviceModelToTypes.model_touch.includes(deviceType);
645
+ await wait(isTouch ? 3000 : 1500);
646
+ } catch (e) {
647
+ if (e instanceof HardwareError) {
648
+ return Promise.reject(e);
649
+ }
650
+ console.log('auto go to bootloader mode failed: ', e);
651
+ return Promise.reject(
652
+ ERRORS.TypedError(HardwareErrorCode.FirmwareUpdateAutoEnterBootFailure)
653
+ );
463
654
  }
464
- console.log('auto go to bootloader mode failed: ', e);
465
- return Promise.reject(
466
- ERRORS.TypedError(HardwareErrorCode.FirmwareUpdateAutoEnterBootFailure)
467
- );
468
655
  }
469
- }
470
656
 
471
- // Devices already in bootloader mode still acquire through the same helper.
472
- const binary = await acquireFirmwareBinary();
657
+ // Devices already in bootloader mode still acquire through the same helper.
658
+ const source = await acquireFirmwareSource();
473
659
 
474
- // check if the device commands has been disposed
475
- this.device?.commands?.checkDisposed();
660
+ // check if the device commands has been disposed
661
+ this.device?.commands?.checkDisposed();
476
662
 
477
- await this.device.acquire();
663
+ await this.device.acquire();
478
664
 
479
- const response = await uploadFirmware(
480
- params.updateType,
481
- this.device.getCommands().typedCall.bind(this.device.getCommands()),
482
- this.postMessage,
483
- device,
484
- { payload: binary, rebootOnSuccess: true },
485
- params.isUpdateBootloader
486
- );
665
+ const response = await uploadFirmwareFromSource(
666
+ params.updateType,
667
+ this.device.getCommands().typedCall.bind(this.device.getCommands()),
668
+ this.postMessage,
669
+ device,
670
+ source,
671
+ true,
672
+ params.isUpdateBootloader
673
+ );
487
674
 
488
- if (this.connectId) {
489
- DevicePool.clearDeviceCache(this.connectId);
490
- }
675
+ if (this.connectId) {
676
+ DevicePool.clearDeviceCache(this.connectId);
677
+ }
491
678
 
492
- return response;
679
+ return response;
680
+ } finally {
681
+ await preparedSource?.close().catch(() => undefined);
682
+ }
493
683
  }
494
684
  }