@onekeyfe/hd-core 1.2.0-alpha.6 → 1.2.0-alpha.62

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 (740) hide show
  1. package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +77 -0
  2. package/__tests__/DeviceCommands.test.ts +517 -0
  3. package/__tests__/DeviceEventRegistration.test.ts +43 -0
  4. package/__tests__/chain-params-onekey-compatibility.test.ts +229 -0
  5. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +282 -0
  6. package/__tests__/connectSettings.test.ts +61 -0
  7. package/__tests__/core-dispose.test.ts +34 -0
  8. package/__tests__/core-error-output.test.ts +39 -0
  9. package/__tests__/device-features-state.test.ts +78 -0
  10. package/__tests__/device-identity.test.ts +52 -0
  11. package/__tests__/device-initialize-timeout.test.ts +56 -0
  12. package/__tests__/device-lifecycle-events.test.ts +249 -0
  13. package/__tests__/device-pool-state.test.ts +144 -0
  14. package/__tests__/device-settings.test.ts +416 -0
  15. package/__tests__/device-state-contract.test.ts +27 -0
  16. package/__tests__/device-state-events.test.ts +286 -0
  17. package/__tests__/device-state-mapper.test.ts +261 -0
  18. package/__tests__/device-state-projector.test.ts +94 -0
  19. package/__tests__/device-state-store.test.ts +82 -0
  20. package/__tests__/device-utils.test.ts +107 -0
  21. package/__tests__/device-wallet-session-store.test.ts +478 -0
  22. package/__tests__/deviceFeaturesUtils.test.ts +39 -0
  23. package/__tests__/deviceSettings.test.ts +103 -0
  24. package/__tests__/evmLedgerLegacySafety.test.ts +12 -6
  25. package/__tests__/evmSignTransaction.test.ts +69 -0
  26. package/__tests__/evmSignTypedData.test.ts +80 -0
  27. package/__tests__/filesystemValidation.test.ts +53 -0
  28. package/__tests__/firmware-update/check-all-firmware-release.test.ts +175 -0
  29. package/__tests__/firmware-update/firmware-artifact-source.test.ts +119 -0
  30. package/__tests__/firmware-update/firmware-host-binding.test.ts +70 -0
  31. package/__tests__/firmware-update/firmware-preparation-error.test.ts +27 -0
  32. package/__tests__/firmware-update/firmware-update-bootloader-poll.test.ts +200 -0
  33. package/__tests__/firmware-update/firmware-update-capabilities.test.ts +13 -0
  34. package/__tests__/firmware-update/firmware-update-plan-bootloader-identity.test.ts +294 -0
  35. package/__tests__/firmware-update/firmware-update-plan.test.ts +593 -0
  36. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +213 -0
  37. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +430 -0
  38. package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +58 -0
  39. package/__tests__/firmware-update/firmware-upload-source.test.ts +70 -0
  40. package/__tests__/get-device-state.test.ts +458 -0
  41. package/__tests__/homescreen.test.ts +24 -0
  42. package/__tests__/kaspa-public-types.type-test.ts +16 -0
  43. package/__tests__/kaspa-use-tweak-pro2.test.ts +20 -0
  44. package/__tests__/kaspaSignTransaction.test.ts +601 -0
  45. package/__tests__/known-device-public-types.type-test.ts +3 -0
  46. package/__tests__/logBlockEvent.test.ts +128 -1
  47. package/__tests__/method-protocol-support.test.ts +113 -0
  48. package/__tests__/networkUtils.test.ts +406 -0
  49. package/__tests__/open-wallet-session-error-response.test.ts +43 -0
  50. package/__tests__/open-wallet-session.test.ts +1368 -0
  51. package/__tests__/pro2Wallpaper.test.ts +39 -0
  52. package/__tests__/protocol-v2-bootloader-mode.test.ts +37 -0
  53. package/__tests__/protocol-v2-firmware-targets.test.ts +37 -0
  54. package/__tests__/protocol-v2-ping.test.ts +28 -0
  55. package/__tests__/protocol-v2-unlock-policy.test.ts +45 -0
  56. package/__tests__/protocol-v2.test.ts +6058 -2525
  57. package/__tests__/protocolV2FileWrite.test.ts +92 -0
  58. package/__tests__/protocolV2UiInteraction.test.ts +266 -0
  59. package/__tests__/public-device-state-api.test.ts +275 -0
  60. package/__tests__/public-pro2-api-boundary.test.ts +106 -0
  61. package/__tests__/refresh-device-state.test.ts +101 -0
  62. package/__tests__/request-context-logging.test.ts +16 -0
  63. package/__tests__/search-devices.test.ts +136 -0
  64. package/__tests__/tron-sign-message-init.test.ts +74 -0
  65. package/dist/api/BaseMethod.d.ts +14 -1
  66. package/dist/api/BaseMethod.d.ts.map +1 -1
  67. package/dist/api/CheckAllFirmwareRelease.d.ts +24 -1
  68. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  69. package/dist/api/CheckBootloaderRelease.d.ts.map +1 -1
  70. package/dist/api/CheckFirmwareRelease.d.ts.map +1 -1
  71. package/dist/api/CipherKeyValue.d.ts +1 -0
  72. package/dist/api/CipherKeyValue.d.ts.map +1 -1
  73. package/dist/api/ClearSessionCache.d.ts +9 -0
  74. package/dist/api/ClearSessionCache.d.ts.map +1 -0
  75. package/dist/api/DirList.d.ts +1 -0
  76. package/dist/api/DirList.d.ts.map +1 -1
  77. package/dist/api/DirMake.d.ts +1 -0
  78. package/dist/api/DirMake.d.ts.map +1 -1
  79. package/dist/api/DirRemove.d.ts +1 -0
  80. package/dist/api/DirRemove.d.ts.map +1 -1
  81. package/dist/api/FileDelete.d.ts +1 -0
  82. package/dist/api/FileDelete.d.ts.map +1 -1
  83. package/dist/api/FileRead.d.ts +1 -0
  84. package/dist/api/FileRead.d.ts.map +1 -1
  85. package/dist/api/FileWrite.d.ts +2 -0
  86. package/dist/api/FileWrite.d.ts.map +1 -1
  87. package/dist/api/FirmwareUpdate.d.ts.map +1 -1
  88. package/dist/api/FirmwareUpdateV2.d.ts +13 -2
  89. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  90. package/dist/api/FirmwareUpdateV3.d.ts +7 -3
  91. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  92. package/dist/api/FirmwareUpdateV4.d.ts +40 -16
  93. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  94. package/dist/api/GetDeviceState.d.ts +8 -0
  95. package/dist/api/GetDeviceState.d.ts.map +1 -0
  96. package/dist/api/GetFeatures.d.ts +1 -1
  97. package/dist/api/GetFeatures.d.ts.map +1 -1
  98. package/dist/api/GetOnekeyFeatures.d.ts.map +1 -1
  99. package/dist/api/GetPassphraseState.d.ts +2 -6
  100. package/dist/api/GetPassphraseState.d.ts.map +1 -1
  101. package/dist/api/OpenWalletSession.d.ts +8 -0
  102. package/dist/api/OpenWalletSession.d.ts.map +1 -0
  103. package/dist/api/PathInfo.d.ts +1 -0
  104. package/dist/api/PathInfo.d.ts.map +1 -1
  105. package/dist/api/PromptWebDeviceAccess.d.ts.map +1 -1
  106. package/dist/api/SearchDevices.d.ts +3 -0
  107. package/dist/api/SearchDevices.d.ts.map +1 -1
  108. package/dist/api/UploadPortfolio.d.ts +17 -0
  109. package/dist/api/UploadPortfolio.d.ts.map +1 -0
  110. package/dist/api/alephium/AlephiumGetAddress.d.ts.map +1 -1
  111. package/dist/api/alephium/AlephiumSignMessage.d.ts.map +1 -1
  112. package/dist/api/alephium/AlephiumSignTransaction.d.ts.map +1 -1
  113. package/dist/api/algo/AlgoGetAddress.d.ts +1 -0
  114. package/dist/api/algo/AlgoGetAddress.d.ts.map +1 -1
  115. package/dist/api/algo/AlgoSignTransaction.d.ts +1 -0
  116. package/dist/api/algo/AlgoSignTransaction.d.ts.map +1 -1
  117. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts +1 -0
  118. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
  119. package/dist/api/aptos/AptosGetAddress.d.ts +1 -0
  120. package/dist/api/aptos/AptosGetAddress.d.ts.map +1 -1
  121. package/dist/api/aptos/AptosGetPublicKey.d.ts +1 -0
  122. package/dist/api/aptos/AptosGetPublicKey.d.ts.map +1 -1
  123. package/dist/api/aptos/AptosSignInMessage.d.ts +1 -0
  124. package/dist/api/aptos/AptosSignInMessage.d.ts.map +1 -1
  125. package/dist/api/aptos/AptosSignMessage.d.ts +1 -0
  126. package/dist/api/aptos/AptosSignMessage.d.ts.map +1 -1
  127. package/dist/api/aptos/AptosSignTransaction.d.ts +1 -0
  128. package/dist/api/aptos/AptosSignTransaction.d.ts.map +1 -1
  129. package/dist/api/benfen/BenfenGetAddress.d.ts.map +1 -1
  130. package/dist/api/benfen/BenfenGetPublicKey.d.ts.map +1 -1
  131. package/dist/api/benfen/BenfenSignMessage.d.ts.map +1 -1
  132. package/dist/api/benfen/BenfenSignTransaction.d.ts.map +1 -1
  133. package/dist/api/btc/BTCGetAddress.d.ts +1 -0
  134. package/dist/api/btc/BTCGetAddress.d.ts.map +1 -1
  135. package/dist/api/btc/BTCGetPublicKey.d.ts +1 -0
  136. package/dist/api/btc/BTCGetPublicKey.d.ts.map +1 -1
  137. package/dist/api/btc/BTCSignMessage.d.ts +1 -0
  138. package/dist/api/btc/BTCSignMessage.d.ts.map +1 -1
  139. package/dist/api/btc/BTCSignPsbt.d.ts +1 -0
  140. package/dist/api/btc/BTCSignPsbt.d.ts.map +1 -1
  141. package/dist/api/btc/BTCSignTransaction.d.ts +1 -0
  142. package/dist/api/btc/BTCSignTransaction.d.ts.map +1 -1
  143. package/dist/api/btc/BTCVerifyMessage.d.ts +1 -0
  144. package/dist/api/btc/BTCVerifyMessage.d.ts.map +1 -1
  145. package/dist/api/btc/helpers/versionLimit.d.ts +2 -0
  146. package/dist/api/btc/helpers/versionLimit.d.ts.map +1 -1
  147. package/dist/api/cardano/CardanoGetAddress.d.ts +1 -0
  148. package/dist/api/cardano/CardanoGetAddress.d.ts.map +1 -1
  149. package/dist/api/cardano/CardanoGetPublicKey.d.ts +1 -0
  150. package/dist/api/cardano/CardanoGetPublicKey.d.ts.map +1 -1
  151. package/dist/api/cardano/CardanoSignMessage.d.ts +1 -0
  152. package/dist/api/cardano/CardanoSignMessage.d.ts.map +1 -1
  153. package/dist/api/cardano/CardanoSignTransaction.d.ts +1 -0
  154. package/dist/api/cardano/CardanoSignTransaction.d.ts.map +1 -1
  155. package/dist/api/conflux/ConfluxGetAddress.d.ts +2 -0
  156. package/dist/api/conflux/ConfluxGetAddress.d.ts.map +1 -1
  157. package/dist/api/conflux/ConfluxSignMessage.d.ts +2 -0
  158. package/dist/api/conflux/ConfluxSignMessage.d.ts.map +1 -1
  159. package/dist/api/conflux/ConfluxSignMessageCIP23.d.ts +2 -0
  160. package/dist/api/conflux/ConfluxSignMessageCIP23.d.ts.map +1 -1
  161. package/dist/api/conflux/ConfluxSignTransaction.d.ts +2 -0
  162. package/dist/api/conflux/ConfluxSignTransaction.d.ts.map +1 -1
  163. package/dist/api/cosmos/CosmosGetAddress.d.ts +1 -0
  164. package/dist/api/cosmos/CosmosGetAddress.d.ts.map +1 -1
  165. package/dist/api/cosmos/CosmosGetPublicKey.d.ts +1 -0
  166. package/dist/api/cosmos/CosmosGetPublicKey.d.ts.map +1 -1
  167. package/dist/api/cosmos/CosmosSignTransaction.d.ts +1 -0
  168. package/dist/api/cosmos/CosmosSignTransaction.d.ts.map +1 -1
  169. package/dist/api/device/DeviceCancel.d.ts +1 -0
  170. package/dist/api/device/DeviceCancel.d.ts.map +1 -1
  171. package/dist/api/device/DeviceChangePin.d.ts +1 -0
  172. package/dist/api/device/DeviceChangePin.d.ts.map +1 -1
  173. package/dist/api/device/DeviceFullyUploadResource.d.ts +1 -2
  174. package/dist/api/device/DeviceFullyUploadResource.d.ts.map +1 -1
  175. package/dist/api/device/DeviceLock.d.ts +1 -0
  176. package/dist/api/device/DeviceLock.d.ts.map +1 -1
  177. package/dist/api/device/DeviceRebootToBoardloader.d.ts +1 -0
  178. package/dist/api/device/DeviceRebootToBoardloader.d.ts.map +1 -1
  179. package/dist/api/device/DeviceRebootToBootloader.d.ts +1 -0
  180. package/dist/api/device/DeviceRebootToBootloader.d.ts.map +1 -1
  181. package/dist/api/device/DeviceSettings.d.ts +1 -0
  182. package/dist/api/device/DeviceSettings.d.ts.map +1 -1
  183. package/dist/api/device/DeviceSupportFeatures.d.ts +1 -0
  184. package/dist/api/device/DeviceSupportFeatures.d.ts.map +1 -1
  185. package/dist/api/device/DeviceUnlock.d.ts +1 -0
  186. package/dist/api/device/DeviceUnlock.d.ts.map +1 -1
  187. package/dist/api/device/DeviceUpdateBootloader.d.ts +5 -1
  188. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  189. package/dist/api/device/DeviceWipe.d.ts +1 -0
  190. package/dist/api/device/DeviceWipe.d.ts.map +1 -1
  191. package/dist/api/device/PreInitialize.d.ts +1 -0
  192. package/dist/api/device/PreInitialize.d.ts.map +1 -1
  193. package/dist/api/dynex/DnxGetAddress.d.ts.map +1 -1
  194. package/dist/api/dynex/DnxSignTransaction.d.ts.map +1 -1
  195. package/dist/api/evm/EVMGetAddress.d.ts +1 -0
  196. package/dist/api/evm/EVMGetAddress.d.ts.map +1 -1
  197. package/dist/api/evm/EVMGetPublicKey.d.ts +1 -0
  198. package/dist/api/evm/EVMGetPublicKey.d.ts.map +1 -1
  199. package/dist/api/evm/EVMSignMessage.d.ts +1 -0
  200. package/dist/api/evm/EVMSignMessage.d.ts.map +1 -1
  201. package/dist/api/evm/EVMSignMessageEIP712.d.ts.map +1 -1
  202. package/dist/api/evm/EVMSignTransaction.d.ts +1 -0
  203. package/dist/api/evm/EVMSignTransaction.d.ts.map +1 -1
  204. package/dist/api/evm/EVMSignTypedData.d.ts +1 -0
  205. package/dist/api/evm/EVMSignTypedData.d.ts.map +1 -1
  206. package/dist/api/evm/EVMVerifyMessage.d.ts +1 -0
  207. package/dist/api/evm/EVMVerifyMessage.d.ts.map +1 -1
  208. package/dist/api/evm/protocol.d.ts +3 -0
  209. package/dist/api/evm/protocol.d.ts.map +1 -0
  210. package/dist/api/filecoin/FilecoinGetAddress.d.ts +1 -0
  211. package/dist/api/filecoin/FilecoinGetAddress.d.ts.map +1 -1
  212. package/dist/api/filecoin/FilecoinSignTransaction.d.ts +1 -0
  213. package/dist/api/filecoin/FilecoinSignTransaction.d.ts.map +1 -1
  214. package/dist/api/firmware/FirmwareArtifactSource.d.ts +25 -0
  215. package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +1 -0
  216. package/dist/api/firmware/FirmwareHostBinding.d.ts +6 -0
  217. package/dist/api/firmware/FirmwareHostBinding.d.ts.map +1 -0
  218. package/dist/api/firmware/FirmwarePreparationError.d.ts +3 -0
  219. package/dist/api/firmware/FirmwarePreparationError.d.ts.map +1 -0
  220. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +8 -0
  221. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  222. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +3 -0
  223. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +1 -0
  224. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +25 -0
  225. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -0
  226. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +32 -0
  227. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -0
  228. package/dist/api/firmware/getBinary.d.ts +15 -4
  229. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  230. package/dist/api/firmware/updateBootloader.d.ts +2 -0
  231. package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
  232. package/dist/api/firmware/uploadFirmware.d.ts +9 -1
  233. package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
  234. package/dist/api/helpers/filesystemValidation.d.ts +3 -0
  235. package/dist/api/helpers/filesystemValidation.d.ts.map +1 -1
  236. package/dist/api/helpers/paramsValidator.d.ts +1 -0
  237. package/dist/api/helpers/paramsValidator.d.ts.map +1 -1
  238. package/dist/api/helpers/protocolV2FileWrite.d.ts +33 -0
  239. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -0
  240. package/dist/api/index.d.ts +7 -25
  241. package/dist/api/index.d.ts.map +1 -1
  242. package/dist/api/kaspa/KaspaGetAddress.d.ts +4 -0
  243. package/dist/api/kaspa/KaspaGetAddress.d.ts.map +1 -1
  244. package/dist/api/kaspa/KaspaSignTransaction.d.ts +9 -1
  245. package/dist/api/kaspa/KaspaSignTransaction.d.ts.map +1 -1
  246. package/dist/api/kaspa/helpers/TransferSerialize.d.ts.map +1 -1
  247. package/dist/api/lightning/LnurlAuth.d.ts +0 -4
  248. package/dist/api/lightning/LnurlAuth.d.ts.map +1 -1
  249. package/dist/api/near/NearGetAddress.d.ts +1 -0
  250. package/dist/api/near/NearGetAddress.d.ts.map +1 -1
  251. package/dist/api/near/NearSignTransaction.d.ts +1 -0
  252. package/dist/api/near/NearSignTransaction.d.ts.map +1 -1
  253. package/dist/api/nem/NEMGetAddress.d.ts +1 -0
  254. package/dist/api/nem/NEMGetAddress.d.ts.map +1 -1
  255. package/dist/api/nem/NEMSignTransaction.d.ts +1 -0
  256. package/dist/api/nem/NEMSignTransaction.d.ts.map +1 -1
  257. package/dist/api/neo/NeoGetAddress.d.ts.map +1 -1
  258. package/dist/api/neo/NeoSignTransaction.d.ts.map +1 -1
  259. package/dist/api/nervos/NervosGetAddress.d.ts.map +1 -1
  260. package/dist/api/nervos/NervosSignTransaction.d.ts.map +1 -1
  261. package/dist/api/nexa/NexaGetAddress.d.ts.map +1 -1
  262. package/dist/api/nexa/NexaSignTransaction.d.ts +1 -0
  263. package/dist/api/nexa/NexaSignTransaction.d.ts.map +1 -1
  264. package/dist/api/nostr/NostrDecryptMessage.d.ts +1 -0
  265. package/dist/api/nostr/NostrDecryptMessage.d.ts.map +1 -1
  266. package/dist/api/nostr/NostrEncryptMessage.d.ts +1 -0
  267. package/dist/api/nostr/NostrEncryptMessage.d.ts.map +1 -1
  268. package/dist/api/nostr/NostrGetPublicKey.d.ts +1 -0
  269. package/dist/api/nostr/NostrGetPublicKey.d.ts.map +1 -1
  270. package/dist/api/nostr/NostrSignEvent.d.ts +1 -0
  271. package/dist/api/nostr/NostrSignEvent.d.ts.map +1 -1
  272. package/dist/api/nostr/NostrSignSchnorr.d.ts +1 -0
  273. package/dist/api/nostr/NostrSignSchnorr.d.ts.map +1 -1
  274. package/dist/api/polkadot/PolkadotGetAddress.d.ts +1 -0
  275. package/dist/api/polkadot/PolkadotGetAddress.d.ts.map +1 -1
  276. package/dist/api/polkadot/PolkadotSignTransaction.d.ts +1 -0
  277. package/dist/api/polkadot/PolkadotSignTransaction.d.ts.map +1 -1
  278. package/dist/api/protocol-v2/DeviceFactoryInfoGet.d.ts +1 -0
  279. package/dist/api/protocol-v2/DeviceFactoryInfoGet.d.ts.map +1 -1
  280. package/dist/api/protocol-v2/DeviceFactoryInfoSet.d.ts +1 -0
  281. package/dist/api/protocol-v2/DeviceFactoryInfoSet.d.ts.map +1 -1
  282. package/dist/api/protocol-v2/DeviceFirmwareUpdate.d.ts +1 -0
  283. package/dist/api/protocol-v2/DeviceFirmwareUpdate.d.ts.map +1 -1
  284. package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts +1 -0
  285. package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts.map +1 -1
  286. package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts +7 -0
  287. package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts.map +1 -0
  288. package/dist/api/protocol-v2/DeviceInfoGet.d.ts +1 -1
  289. package/dist/api/protocol-v2/DeviceInfoGet.d.ts.map +1 -1
  290. package/dist/api/protocol-v2/DeviceReboot.d.ts +1 -0
  291. package/dist/api/protocol-v2/DeviceReboot.d.ts.map +1 -1
  292. package/dist/api/protocol-v2/DeviceStatusGet.d.ts +7 -0
  293. package/dist/api/protocol-v2/DeviceStatusGet.d.ts.map +1 -0
  294. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts +27 -0
  295. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -0
  296. package/dist/api/protocol-v2/FilesystemFormat.d.ts +1 -0
  297. package/dist/api/protocol-v2/FilesystemFormat.d.ts.map +1 -1
  298. package/dist/api/protocol-v2/FilesystemPermissionFix.d.ts +1 -0
  299. package/dist/api/protocol-v2/FilesystemPermissionFix.d.ts.map +1 -1
  300. package/dist/api/protocol-v2/Ping.d.ts +3 -0
  301. package/dist/api/protocol-v2/Ping.d.ts.map +1 -1
  302. package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts +1 -0
  303. package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -1
  304. package/dist/api/protocol-v2/helpers.d.ts +1 -1
  305. package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
  306. package/dist/api/scdo/ScdoGetAddress.d.ts.map +1 -1
  307. package/dist/api/scdo/ScdoSignMessage.d.ts.map +1 -1
  308. package/dist/api/scdo/ScdoSignTransaction.d.ts.map +1 -1
  309. package/dist/api/solana/SolGetAddress.d.ts +1 -0
  310. package/dist/api/solana/SolGetAddress.d.ts.map +1 -1
  311. package/dist/api/solana/SolSignMessage.d.ts +1 -0
  312. package/dist/api/solana/SolSignMessage.d.ts.map +1 -1
  313. package/dist/api/solana/SolSignOffchainMessage.d.ts +1 -0
  314. package/dist/api/solana/SolSignOffchainMessage.d.ts.map +1 -1
  315. package/dist/api/solana/SolSignTransaction.d.ts +1 -0
  316. package/dist/api/solana/SolSignTransaction.d.ts.map +1 -1
  317. package/dist/api/starcoin/StarcoinGetAddress.d.ts +1 -0
  318. package/dist/api/starcoin/StarcoinGetAddress.d.ts.map +1 -1
  319. package/dist/api/starcoin/StarcoinGetPublicKey.d.ts +1 -0
  320. package/dist/api/starcoin/StarcoinGetPublicKey.d.ts.map +1 -1
  321. package/dist/api/starcoin/StarcoinSignMessage.d.ts +1 -0
  322. package/dist/api/starcoin/StarcoinSignMessage.d.ts.map +1 -1
  323. package/dist/api/starcoin/StarcoinSignTransaction.d.ts +1 -0
  324. package/dist/api/starcoin/StarcoinSignTransaction.d.ts.map +1 -1
  325. package/dist/api/starcoin/StarcoinVerifyMessage.d.ts +1 -0
  326. package/dist/api/starcoin/StarcoinVerifyMessage.d.ts.map +1 -1
  327. package/dist/api/stellar/StellarGetAddress.d.ts +1 -2
  328. package/dist/api/stellar/StellarGetAddress.d.ts.map +1 -1
  329. package/dist/api/stellar/StellarSignTransaction.d.ts +2 -3
  330. package/dist/api/stellar/StellarSignTransaction.d.ts.map +1 -1
  331. package/dist/api/sui/SuiGetAddress.d.ts +1 -0
  332. package/dist/api/sui/SuiGetAddress.d.ts.map +1 -1
  333. package/dist/api/sui/SuiGetPublicKey.d.ts +1 -0
  334. package/dist/api/sui/SuiGetPublicKey.d.ts.map +1 -1
  335. package/dist/api/sui/SuiSignMessage.d.ts +1 -0
  336. package/dist/api/sui/SuiSignMessage.d.ts.map +1 -1
  337. package/dist/api/sui/SuiSignTransaction.d.ts +1 -0
  338. package/dist/api/sui/SuiSignTransaction.d.ts.map +1 -1
  339. package/dist/api/ton/TonGetAddress.d.ts +1 -0
  340. package/dist/api/ton/TonGetAddress.d.ts.map +1 -1
  341. package/dist/api/ton/TonSignData.d.ts +1 -0
  342. package/dist/api/ton/TonSignData.d.ts.map +1 -1
  343. package/dist/api/ton/TonSignMessage.d.ts +1 -0
  344. package/dist/api/ton/TonSignMessage.d.ts.map +1 -1
  345. package/dist/api/ton/TonSignProof.d.ts +1 -0
  346. package/dist/api/ton/TonSignProof.d.ts.map +1 -1
  347. package/dist/api/tron/TronGetAddress.d.ts +1 -0
  348. package/dist/api/tron/TronGetAddress.d.ts.map +1 -1
  349. package/dist/api/tron/TronSignMessage.d.ts +5 -0
  350. package/dist/api/tron/TronSignMessage.d.ts.map +1 -1
  351. package/dist/api/tron/TronSignTransaction.d.ts +1 -0
  352. package/dist/api/tron/TronSignTransaction.d.ts.map +1 -1
  353. package/dist/api/utils.d.ts +2 -1
  354. package/dist/api/utils.d.ts.map +1 -1
  355. package/dist/api/xrp/XrpGetAddress.d.ts +1 -0
  356. package/dist/api/xrp/XrpGetAddress.d.ts.map +1 -1
  357. package/dist/api/xrp/XrpSignTransaction.d.ts +1 -0
  358. package/dist/api/xrp/XrpSignTransaction.d.ts.map +1 -1
  359. package/dist/constants/index.d.ts +1 -1
  360. package/dist/constants/index.d.ts.map +1 -1
  361. package/dist/core/RequestQueue.d.ts.map +1 -1
  362. package/dist/core/deviceEventRegistration.d.ts +11 -0
  363. package/dist/core/deviceEventRegistration.d.ts.map +1 -0
  364. package/dist/core/index.d.ts +3 -1
  365. package/dist/core/index.d.ts.map +1 -1
  366. package/dist/data-manager/DataManager.d.ts +1 -0
  367. package/dist/data-manager/DataManager.d.ts.map +1 -1
  368. package/dist/data-manager/connectSettings.d.ts.map +1 -1
  369. package/dist/device/Device.d.ts +45 -14
  370. package/dist/device/Device.d.ts.map +1 -1
  371. package/dist/device/DeviceCommands.d.ts +8 -6
  372. package/dist/device/DeviceCommands.d.ts.map +1 -1
  373. package/dist/device/DeviceConnector.d.ts +1 -1
  374. package/dist/device/DeviceConnector.d.ts.map +1 -1
  375. package/dist/device/DeviceFeaturesState.d.ts +3 -0
  376. package/dist/device/DeviceFeaturesState.d.ts.map +1 -0
  377. package/dist/device/DevicePool.d.ts +5 -2
  378. package/dist/device/DevicePool.d.ts.map +1 -1
  379. package/dist/device/DeviceStateMapper.d.ts +13 -0
  380. package/dist/device/DeviceStateMapper.d.ts.map +1 -0
  381. package/dist/device/DeviceStateProjector.d.ts +7 -0
  382. package/dist/device/DeviceStateProjector.d.ts.map +1 -0
  383. package/dist/device/DeviceStateStore.d.ts +21 -0
  384. package/dist/device/DeviceStateStore.d.ts.map +1 -0
  385. package/dist/device/DeviceWalletSessionStore.d.ts +27 -0
  386. package/dist/device/DeviceWalletSessionStore.d.ts.map +1 -0
  387. package/dist/device/cloneDeviceState.d.ts +2 -0
  388. package/dist/device/cloneDeviceState.d.ts.map +1 -0
  389. package/dist/device/deviceIdentity.d.ts +5 -0
  390. package/dist/device/deviceIdentity.d.ts.map +1 -0
  391. package/dist/deviceProfile/buildDeviceFeatures.d.ts +9 -3
  392. package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
  393. package/dist/deviceProfile/index.d.ts +0 -1
  394. package/dist/deviceProfile/index.d.ts.map +1 -1
  395. package/dist/deviceProfile/protocolV2DeviceIdentity.d.ts +7 -0
  396. package/dist/deviceProfile/protocolV2DeviceIdentity.d.ts.map +1 -0
  397. package/dist/events/device.d.ts +12 -2
  398. package/dist/events/device.d.ts.map +1 -1
  399. package/dist/events/logBlockEvent.d.ts +2 -0
  400. package/dist/events/logBlockEvent.d.ts.map +1 -1
  401. package/dist/events/ui-request.d.ts +19 -2
  402. package/dist/events/ui-request.d.ts.map +1 -1
  403. package/dist/index.d.ts +1564 -739
  404. package/dist/index.d.ts.map +1 -1
  405. package/dist/index.js +9236 -3918
  406. package/dist/inject.d.ts.map +1 -1
  407. package/dist/lowLevelInject.d.ts.map +1 -1
  408. package/dist/protocols/protocol-v2/deviceSession.d.ts +7 -0
  409. package/dist/protocols/protocol-v2/deviceSession.d.ts.map +1 -0
  410. package/dist/protocols/protocol-v2/features.d.ts +25 -17
  411. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  412. package/dist/protocols/protocol-v2/index.d.ts +3 -2
  413. package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
  414. package/dist/protocols/protocol-v2/lockedError.d.ts +2 -0
  415. package/dist/protocols/protocol-v2/lockedError.d.ts.map +1 -0
  416. package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts +17 -0
  417. package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts.map +1 -0
  418. package/dist/protocols/protocol-v2/uiInteraction.d.ts +39 -0
  419. package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -0
  420. package/dist/protocols/protocol-v2/unlockPolicy.d.ts +4 -0
  421. package/dist/protocols/protocol-v2/unlockPolicy.d.ts.map +1 -0
  422. package/dist/protocols/protocol-v2/unlockRetry.d.ts +10 -0
  423. package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +1 -0
  424. package/dist/protocols/protocol-v2/walletSession.d.ts +22 -0
  425. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -0
  426. package/dist/topLevelInject.d.ts.map +1 -1
  427. package/dist/types/api/checkAllFirmwareRelease.d.ts +29 -4
  428. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  429. package/dist/types/api/deviceSettings.d.ts +11 -2
  430. package/dist/types/api/deviceSettings.d.ts.map +1 -1
  431. package/dist/types/api/deviceUnlock.d.ts +1 -1
  432. package/dist/types/api/deviceUnlock.d.ts.map +1 -1
  433. package/dist/types/api/deviceUpdateBootloader.d.ts +6 -0
  434. package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
  435. package/dist/types/api/export.d.ts +6 -2
  436. package/dist/types/api/export.d.ts.map +1 -1
  437. package/dist/types/api/firmwareUpdate.d.ts +75 -1
  438. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  439. package/dist/types/api/firmwareUpdateCapabilities.d.ts +9 -0
  440. package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +1 -0
  441. package/dist/types/api/firmwareUpdatePlan.d.ts +28 -0
  442. package/dist/types/api/firmwareUpdatePlan.d.ts.map +1 -0
  443. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +42 -0
  444. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +1 -0
  445. package/dist/types/api/getDeviceState.d.ts +12 -0
  446. package/dist/types/api/getDeviceState.d.ts.map +1 -0
  447. package/dist/types/api/getFeatures.d.ts.map +1 -1
  448. package/dist/types/api/getOnekeyFeatures.d.ts.map +1 -1
  449. package/dist/types/api/getPassphraseState.d.ts +2 -10
  450. package/dist/types/api/getPassphraseState.d.ts.map +1 -1
  451. package/dist/types/api/index.d.ts +27 -31
  452. package/dist/types/api/index.d.ts.map +1 -1
  453. package/dist/types/api/kaspaSignTransaction.d.ts +42 -4
  454. package/dist/types/api/kaspaSignTransaction.d.ts.map +1 -1
  455. package/dist/types/api/openWalletSession.d.ts +35 -0
  456. package/dist/types/api/openWalletSession.d.ts.map +1 -0
  457. package/dist/types/api/protocolV2.d.ts +15 -84
  458. package/dist/types/api/protocolV2.d.ts.map +1 -1
  459. package/dist/types/api/sessionCache.d.ts +13 -0
  460. package/dist/types/api/sessionCache.d.ts.map +1 -0
  461. package/dist/types/device.d.ts +132 -8
  462. package/dist/types/device.d.ts.map +1 -1
  463. package/dist/types/params.d.ts.map +1 -1
  464. package/dist/types/settings.d.ts +20 -14
  465. package/dist/types/settings.d.ts.map +1 -1
  466. package/dist/utils/assets.d.ts +2 -1
  467. package/dist/utils/assets.d.ts.map +1 -1
  468. package/dist/utils/deviceFeaturesCompat.d.ts +14 -0
  469. package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -0
  470. package/dist/utils/deviceFeaturesUtils.d.ts +6 -3
  471. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  472. package/dist/utils/deviceInfoUtils.d.ts +9 -9
  473. package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
  474. package/dist/utils/deviceSettings.d.ts +38 -7
  475. package/dist/utils/deviceSettings.d.ts.map +1 -1
  476. package/dist/utils/deviceVersionUtils.d.ts +7 -5
  477. package/dist/utils/deviceVersionUtils.d.ts.map +1 -1
  478. package/dist/utils/homescreen.d.ts.map +1 -1
  479. package/dist/utils/index.d.ts +5 -2
  480. package/dist/utils/index.d.ts.map +1 -1
  481. package/dist/utils/networkUtils.d.ts +10 -1
  482. package/dist/utils/networkUtils.d.ts.map +1 -1
  483. package/dist/utils/patch.d.ts +1 -1
  484. package/dist/utils/patch.d.ts.map +1 -1
  485. package/dist/utils/pro2Wallpaper.d.ts +12 -0
  486. package/dist/utils/pro2Wallpaper.d.ts.map +1 -0
  487. package/dist/utils/tracing.d.ts.map +1 -1
  488. package/package.json +4 -4
  489. package/src/api/BaseMethod.ts +52 -17
  490. package/src/api/CheckAllFirmwareRelease.ts +344 -4
  491. package/src/api/CheckBootloaderRelease.ts +1 -2
  492. package/src/api/CheckFirmwareRelease.ts +1 -2
  493. package/src/api/CipherKeyValue.ts +4 -0
  494. package/src/api/ClearSessionCache.ts +32 -0
  495. package/src/api/DirList.ts +9 -4
  496. package/src/api/DirMake.ts +7 -4
  497. package/src/api/DirRemove.ts +7 -4
  498. package/src/api/FileDelete.ts +7 -4
  499. package/src/api/FileRead.ts +15 -9
  500. package/src/api/FileWrite.ts +26 -181
  501. package/src/api/FirmwareUpdate.ts +24 -13
  502. package/src/api/FirmwareUpdateV2.ts +386 -113
  503. package/src/api/FirmwareUpdateV3.ts +266 -68
  504. package/src/api/FirmwareUpdateV4.ts +1222 -635
  505. package/src/api/GetDeviceState.ts +59 -0
  506. package/src/api/GetFeatures.ts +6 -4
  507. package/src/api/GetOnekeyFeatures.ts +1 -75
  508. package/src/api/GetPassphraseState.ts +30 -21
  509. package/src/api/OpenWalletSession.ts +214 -0
  510. package/src/api/PathInfo.ts +9 -4
  511. package/src/api/PromptWebDeviceAccess.ts +2 -7
  512. package/src/api/SearchDevices.ts +29 -4
  513. package/src/api/UploadPortfolio.ts +40 -0
  514. package/src/api/alephium/AlephiumGetAddress.ts +0 -4
  515. package/src/api/alephium/AlephiumSignMessage.ts +0 -4
  516. package/src/api/alephium/AlephiumSignTransaction.ts +2 -6
  517. package/src/api/algo/AlgoGetAddress.ts +4 -0
  518. package/src/api/algo/AlgoSignTransaction.ts +4 -0
  519. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +9 -21
  520. package/src/api/aptos/AptosGetAddress.ts +4 -0
  521. package/src/api/aptos/AptosGetPublicKey.ts +4 -0
  522. package/src/api/aptos/AptosSignInMessage.ts +4 -0
  523. package/src/api/aptos/AptosSignMessage.ts +4 -0
  524. package/src/api/aptos/AptosSignTransaction.ts +4 -0
  525. package/src/api/benfen/BenfenGetAddress.ts +11 -7
  526. package/src/api/benfen/BenfenGetPublicKey.ts +0 -4
  527. package/src/api/benfen/BenfenSignMessage.ts +0 -4
  528. package/src/api/benfen/BenfenSignTransaction.ts +0 -4
  529. package/src/api/btc/BTCGetAddress.ts +8 -1
  530. package/src/api/btc/BTCGetPublicKey.ts +9 -2
  531. package/src/api/btc/BTCSignMessage.ts +8 -1
  532. package/src/api/btc/BTCSignPsbt.ts +4 -0
  533. package/src/api/btc/BTCSignTransaction.ts +8 -1
  534. package/src/api/btc/BTCVerifyMessage.ts +8 -1
  535. package/src/api/btc/helpers/versionLimit.ts +7 -4
  536. package/src/api/cardano/CardanoGetAddress.ts +4 -0
  537. package/src/api/cardano/CardanoGetPublicKey.ts +4 -0
  538. package/src/api/cardano/CardanoSignMessage.ts +5 -1
  539. package/src/api/cardano/CardanoSignTransaction.ts +5 -1
  540. package/src/api/conflux/ConfluxGetAddress.ts +6 -0
  541. package/src/api/conflux/ConfluxSignMessage.ts +6 -0
  542. package/src/api/conflux/ConfluxSignMessageCIP23.ts +6 -0
  543. package/src/api/conflux/ConfluxSignTransaction.ts +8 -5
  544. package/src/api/cosmos/CosmosGetAddress.ts +4 -0
  545. package/src/api/cosmos/CosmosGetPublicKey.ts +4 -0
  546. package/src/api/cosmos/CosmosSignTransaction.ts +4 -0
  547. package/src/api/device/DeviceCancel.ts +5 -0
  548. package/src/api/device/DeviceChangePin.ts +28 -0
  549. package/src/api/device/DeviceFullyUploadResource.ts +4 -5
  550. package/src/api/device/DeviceLock.ts +5 -0
  551. package/src/api/device/DeviceRebootToBoardloader.ts +5 -0
  552. package/src/api/device/DeviceRebootToBootloader.ts +5 -0
  553. package/src/api/device/DeviceSettings.ts +194 -1
  554. package/src/api/device/DeviceSupportFeatures.ts +4 -0
  555. package/src/api/device/DeviceUnlock.ts +13 -0
  556. package/src/api/device/DeviceUpdateBootloader.ts +123 -8
  557. package/src/api/device/DeviceWipe.ts +25 -0
  558. package/src/api/device/PreInitialize.ts +4 -0
  559. package/src/api/dynex/DnxGetAddress.ts +0 -6
  560. package/src/api/dynex/DnxSignTransaction.ts +0 -6
  561. package/src/api/evm/EVMGetAddress.ts +6 -2
  562. package/src/api/evm/EVMGetPublicKey.ts +6 -2
  563. package/src/api/evm/EVMSignMessage.ts +6 -2
  564. package/src/api/evm/EVMSignMessageEIP712.ts +1 -11
  565. package/src/api/evm/EVMSignTransaction.ts +6 -2
  566. package/src/api/evm/EVMSignTypedData.ts +42 -50
  567. package/src/api/evm/EVMVerifyMessage.ts +6 -2
  568. package/src/api/evm/legacyV1/getAddress.ts +2 -2
  569. package/src/api/evm/legacyV1/getPublicKey.ts +2 -2
  570. package/src/api/evm/legacyV1/signMessage.ts +2 -2
  571. package/src/api/evm/legacyV1/signTypedData.ts +2 -2
  572. package/src/api/evm/legacyV1/signTypedHash.ts +2 -2
  573. package/src/api/evm/legacyV1/verifyMessage.ts +2 -2
  574. package/src/api/evm/protocol.ts +6 -0
  575. package/src/api/filecoin/FilecoinGetAddress.ts +4 -0
  576. package/src/api/filecoin/FilecoinSignTransaction.ts +4 -0
  577. package/src/api/firmware/FirmwareArtifactSource.ts +278 -0
  578. package/src/api/firmware/FirmwareHostBinding.ts +100 -0
  579. package/src/api/firmware/FirmwarePreparationError.ts +12 -0
  580. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +75 -4
  581. package/src/api/firmware/FirmwareUpdateCapabilities.ts +9 -0
  582. package/src/api/firmware/FirmwareUpdatePlan.ts +772 -0
  583. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +415 -0
  584. package/src/api/firmware/getBinary.ts +8 -3
  585. package/src/api/firmware/updateBootloader.ts +19 -4
  586. package/src/api/firmware/uploadFirmware.ts +140 -22
  587. package/src/api/helpers/batchGetPublickeys.ts +2 -2
  588. package/src/api/helpers/filesystemValidation.ts +61 -0
  589. package/src/api/helpers/paramsValidator.ts +1 -1
  590. package/src/api/helpers/protocolV2FileWrite.ts +199 -0
  591. package/src/api/index.ts +8 -26
  592. package/src/api/kaspa/KaspaGetAddress.ts +7 -0
  593. package/src/api/kaspa/KaspaSignTransaction.ts +380 -29
  594. package/src/api/kaspa/helpers/TransferSerialize.ts +17 -7
  595. package/src/api/lightning/LnurlAuth.ts +0 -4
  596. package/src/api/near/NearGetAddress.ts +4 -0
  597. package/src/api/near/NearSignTransaction.ts +4 -0
  598. package/src/api/nem/NEMGetAddress.ts +4 -0
  599. package/src/api/nem/NEMSignTransaction.ts +4 -0
  600. package/src/api/neo/NeoGetAddress.ts +0 -4
  601. package/src/api/neo/NeoSignTransaction.ts +0 -4
  602. package/src/api/nervos/NervosGetAddress.ts +0 -4
  603. package/src/api/nervos/NervosSignTransaction.ts +2 -6
  604. package/src/api/nexa/NexaGetAddress.ts +0 -4
  605. package/src/api/nexa/NexaSignTransaction.ts +3 -5
  606. package/src/api/nostr/NostrDecryptMessage.ts +4 -0
  607. package/src/api/nostr/NostrEncryptMessage.ts +4 -0
  608. package/src/api/nostr/NostrGetPublicKey.ts +4 -0
  609. package/src/api/nostr/NostrSignEvent.ts +4 -0
  610. package/src/api/nostr/NostrSignSchnorr.ts +4 -0
  611. package/src/api/polkadot/PolkadotGetAddress.ts +4 -0
  612. package/src/api/polkadot/PolkadotSignTransaction.ts +4 -0
  613. package/src/api/protocol-v2/DeviceFactoryInfoGet.ts +5 -2
  614. package/src/api/protocol-v2/DeviceFactoryInfoSet.ts +5 -2
  615. package/src/api/protocol-v2/DeviceFirmwareUpdate.ts +8 -5
  616. package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +10 -6
  617. package/src/api/protocol-v2/DeviceGetOnboardingStatus.ts +21 -0
  618. package/src/api/protocol-v2/DeviceInfoGet.ts +8 -15
  619. package/src/api/protocol-v2/DeviceReboot.ts +7 -3
  620. package/src/api/protocol-v2/DeviceStatusGet.ts +19 -0
  621. package/src/api/protocol-v2/DeviceUploadWallpaper.ts +134 -0
  622. package/src/api/protocol-v2/FilesystemFormat.ts +9 -3
  623. package/src/api/protocol-v2/FilesystemPermissionFix.ts +5 -2
  624. package/src/api/protocol-v2/Ping.ts +37 -3
  625. package/src/api/protocol-v2/ProtocolInfoRequest.ts +5 -2
  626. package/src/api/protocol-v2/helpers.ts +17 -17
  627. package/src/api/scdo/ScdoGetAddress.ts +0 -4
  628. package/src/api/scdo/ScdoSignMessage.ts +0 -4
  629. package/src/api/scdo/ScdoSignTransaction.ts +0 -4
  630. package/src/api/solana/SolGetAddress.ts +4 -0
  631. package/src/api/solana/SolSignMessage.ts +4 -0
  632. package/src/api/solana/SolSignOffchainMessage.ts +4 -0
  633. package/src/api/solana/SolSignTransaction.ts +4 -0
  634. package/src/api/starcoin/StarcoinGetAddress.ts +4 -0
  635. package/src/api/starcoin/StarcoinGetPublicKey.ts +4 -0
  636. package/src/api/starcoin/StarcoinSignMessage.ts +4 -0
  637. package/src/api/starcoin/StarcoinSignTransaction.ts +4 -0
  638. package/src/api/starcoin/StarcoinVerifyMessage.ts +4 -0
  639. package/src/api/stellar/StellarGetAddress.ts +1 -10
  640. package/src/api/stellar/StellarSignTransaction.ts +1 -14
  641. package/src/api/sui/SuiGetAddress.ts +4 -0
  642. package/src/api/sui/SuiGetPublicKey.ts +4 -0
  643. package/src/api/sui/SuiSignMessage.ts +4 -0
  644. package/src/api/sui/SuiSignTransaction.ts +4 -0
  645. package/src/api/ton/TonGetAddress.ts +4 -0
  646. package/src/api/ton/TonSignData.ts +5 -1
  647. package/src/api/ton/TonSignMessage.ts +4 -0
  648. package/src/api/ton/TonSignProof.ts +4 -0
  649. package/src/api/tron/TronGetAddress.ts +4 -0
  650. package/src/api/tron/TronSignMessage.ts +18 -7
  651. package/src/api/tron/TronSignTransaction.ts +4 -0
  652. package/src/api/utils.ts +7 -3
  653. package/src/api/xrp/XrpGetAddress.ts +4 -0
  654. package/src/api/xrp/XrpSignTransaction.ts +5 -1
  655. package/src/constants/index.ts +1 -4
  656. package/src/core/RequestQueue.ts +1 -0
  657. package/src/core/deviceEventRegistration.ts +23 -0
  658. package/src/core/index.ts +164 -53
  659. package/src/data/messages/messages-protocol-v2.json +1155 -483
  660. package/src/data/messages/messages.json +286 -1
  661. package/src/data-manager/DataManager.ts +39 -39
  662. package/src/data-manager/TransportManager.ts +1 -1
  663. package/src/data-manager/connectSettings.ts +15 -0
  664. package/src/device/Device.ts +652 -281
  665. package/src/device/DeviceCommands.ts +166 -158
  666. package/src/device/DeviceConnector.ts +10 -7
  667. package/src/device/DeviceFeaturesState.ts +25 -0
  668. package/src/device/DevicePool.ts +66 -16
  669. package/src/device/DeviceStateMapper.ts +374 -0
  670. package/src/device/DeviceStateProjector.ts +94 -0
  671. package/src/device/DeviceStateStore.ts +201 -0
  672. package/src/device/DeviceWalletSessionStore.ts +144 -0
  673. package/src/device/cloneDeviceState.ts +39 -0
  674. package/src/device/deviceIdentity.ts +18 -0
  675. package/src/deviceProfile/buildDeviceFeatures.ts +224 -190
  676. package/src/deviceProfile/index.ts +0 -1
  677. package/src/deviceProfile/protocolV2DeviceIdentity.ts +32 -0
  678. package/src/events/device.ts +18 -1
  679. package/src/events/logBlockEvent.ts +79 -2
  680. package/src/events/ui-request.ts +25 -2
  681. package/src/index.ts +5 -0
  682. package/src/inject.ts +37 -43
  683. package/src/lowLevelInject.ts +5 -1
  684. package/src/protocols/protocol-v2/deviceSession.ts +24 -0
  685. package/src/protocols/protocol-v2/features.ts +96 -32
  686. package/src/protocols/protocol-v2/firmware.ts +1 -1
  687. package/src/protocols/protocol-v2/index.ts +8 -1
  688. package/src/protocols/protocol-v2/lockedError.ts +10 -0
  689. package/src/protocols/protocol-v2/settingsUnlockPolicy.ts +65 -0
  690. package/src/protocols/protocol-v2/uiInteraction.ts +210 -0
  691. package/src/protocols/protocol-v2/unlockPolicy.ts +105 -0
  692. package/src/protocols/protocol-v2/unlockRetry.ts +109 -0
  693. package/src/protocols/protocol-v2/walletSession.ts +285 -0
  694. package/src/topLevelInject.ts +5 -1
  695. package/src/types/api/checkAllFirmwareRelease.ts +50 -4
  696. package/src/types/api/deviceSettings.ts +26 -2
  697. package/src/types/api/deviceUnlock.ts +1 -1
  698. package/src/types/api/deviceUpdateBootloader.ts +6 -0
  699. package/src/types/api/export.ts +26 -0
  700. package/src/types/api/firmwareUpdate.ts +99 -5
  701. package/src/types/api/firmwareUpdateCapabilities.ts +9 -0
  702. package/src/types/api/firmwareUpdatePlan.ts +38 -0
  703. package/src/types/api/firmwareUpdatePreparedPlan.ts +53 -0
  704. package/src/types/api/getDeviceState.ts +19 -0
  705. package/src/types/api/getFeatures.ts +3 -0
  706. package/src/types/api/getOnekeyFeatures.ts +3 -0
  707. package/src/types/api/getPassphraseState.ts +9 -11
  708. package/src/types/api/index.ts +39 -64
  709. package/src/types/api/kaspaSignTransaction.ts +60 -4
  710. package/src/types/api/openWalletSession.ts +54 -0
  711. package/src/types/api/protocolV2.ts +24 -145
  712. package/src/types/api/sessionCache.ts +19 -0
  713. package/src/types/device.ts +185 -7
  714. package/src/types/params.ts +3 -2
  715. package/src/types/settings.ts +30 -16
  716. package/src/utils/assets.ts +4 -1
  717. package/src/utils/deviceFeaturesCompat.ts +186 -0
  718. package/src/utils/deviceFeaturesUtils.ts +52 -45
  719. package/src/utils/deviceInfoUtils.ts +27 -69
  720. package/src/utils/deviceSettings.ts +171 -39
  721. package/src/utils/deviceVersionUtils.ts +25 -44
  722. package/src/utils/findDefectiveBatchDevice.ts +3 -3
  723. package/src/utils/homescreen.ts +10 -0
  724. package/src/utils/index.ts +21 -2
  725. package/src/utils/networkUtils.ts +274 -16
  726. package/src/utils/pro2Wallpaper.ts +103 -0
  727. package/src/utils/tracing.ts +3 -1
  728. package/tsconfig.type-tests.json +12 -0
  729. package/dist/api/GetDeviceInfo.d.ts +0 -9
  730. package/dist/api/GetDeviceInfo.d.ts.map +0 -1
  731. package/dist/api/protocol-v2/FilesystemDiskControl.d.ts +0 -13
  732. package/dist/api/protocol-v2/FilesystemDiskControl.d.ts.map +0 -1
  733. package/dist/deviceProfile/buildDeviceProfile.d.ts +0 -21
  734. package/dist/deviceProfile/buildDeviceProfile.d.ts.map +0 -1
  735. package/dist/types/api/getDeviceInfo.d.ts +0 -87
  736. package/dist/types/api/getDeviceInfo.d.ts.map +0 -1
  737. package/src/api/GetDeviceInfo.ts +0 -145
  738. package/src/api/protocol-v2/FilesystemDiskControl.ts +0 -50
  739. package/src/deviceProfile/buildDeviceProfile.ts +0 -352
  740. package/src/types/api/getDeviceInfo.ts +0 -101
@@ -1,11 +1,14 @@
1
- import { ERRORS, HardwareError, HardwareErrorCode, wait } from '@onekeyfe/hd-shared';
1
+ import { EDeviceType, ERRORS, HardwareError, HardwareErrorCode, wait } from '@onekeyfe/hd-shared';
2
2
  import {
3
3
  DeviceRebootType,
4
4
  PROTOCOL_V2_BLE_FILE_CHUNK_SIZE,
5
+ PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE,
5
6
  PROTOCOL_V2_WEBUSB_FILE_CHUNK_SIZE,
6
7
  } from '@onekeyfe/hd-transport';
8
+ import { sha256 } from '@noble/hashes/sha256';
7
9
 
8
10
  import { FirmwareUpdateTipMessage, UI_REQUEST } from '../events/ui-request';
11
+ import { validateProtocolV2FilesystemPath } from './helpers/filesystemValidation';
9
12
  import { validateParams } from './helpers/paramsValidator';
10
13
  import {
11
14
  LoggerNames,
@@ -16,6 +19,7 @@ import {
16
19
  getLogger,
17
20
  } from '../utils';
18
21
  import { getSysResourceBinary } from './firmware/getBinary';
22
+ import { normalizeFirmwarePreparationError } from './firmware/FirmwarePreparationError';
19
23
  import { DataManager } from '../data-manager';
20
24
  import { FirmwareUpdateBaseMethod } from './firmware/FirmwareUpdateBaseMethod';
21
25
  import { DevicePool } from '../device/DevicePool';
@@ -25,41 +29,52 @@ import {
25
29
  } from '../protocols/protocol-v2';
26
30
  import { requestProtocolV2DeviceInfo } from '../protocols/protocol-v2/features';
27
31
  import {
28
- PROTOCOL_V2_FIRMWARE_UPDATE_RESPONSE_TYPES,
29
32
  getProtocolV2UnknownErrorText,
30
33
  isProtocolV2DeviceDisconnectedError,
31
34
  } from './protocol-v2/helpers';
35
+ import { openFirmwareByteSource, writeFirmwareByteSource } from './firmware/FirmwareArtifactSource';
36
+ import { resolveFirmwareUpdateHostBinding } from './firmware/FirmwareHostBinding';
37
+ import {
38
+ assertFirmwareUpdatePreparedPlanBinding,
39
+ assertFirmwareUpdatePreparedPlanDeviceIdentity,
40
+ } from './firmware/FirmwareUpdatePreparedPlan';
32
41
 
33
- import type { FirmwareUpdateV4Params } from '../types/api/firmwareUpdate';
42
+ import type {
43
+ FirmwareArtifactReference,
44
+ FirmwareUpdateV4Params,
45
+ FirmwareUpdateV4Target,
46
+ } from '../types/api/firmwareUpdate';
34
47
  import type { EFirmwareType } from '@onekeyfe/hd-shared';
35
- import type { PROTO } from '../constants';
48
+ import type { ProtocolV2DeviceInfo } from '@onekeyfe/hd-transport';
36
49
  import type { TypedResponseMessage } from '../device/DeviceCommands';
37
50
  import type {
38
51
  Features,
39
52
  IFirmwareReleaseInfo,
40
53
  IProtocolV2FirmwareComponent,
41
- IProtocolV2ResourceManifestPackage,
42
54
  IVersionArray,
43
55
  } from '../types';
56
+ import type { FirmwareByteSource } from './firmware/FirmwareArtifactSource';
44
57
 
45
58
  const Log = getLogger(LoggerNames.Method);
46
59
 
47
60
  const SESSION_ERROR = 'session not found';
48
61
  const PROTOCOL_V2_BOOTLOADER_RECONNECT_TIMEOUT = 60 * 1000;
62
+ const PROTOCOL_V2_FINAL_RECONNECT_TIMEOUT = 2 * 60 * 1000;
49
63
  const PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT = 5 * 1000;
50
- const PROTOCOL_V2_START_UPDATE_TIMEOUT = 60 * 1000;
64
+ const PROTOCOL_V2_START_UPDATE_TIMEOUT = 3 * 60 * 1000;
51
65
  const PROTOCOL_V2_INSTALL_TIMEOUT = 5 * 60 * 1000;
52
66
  const PROTOCOL_V2_TARGET_STATUS_PENDING = 0;
53
67
  const PROTOCOL_V2_TARGET_STATUS_IN_PROGRESS = 1;
54
68
  const PROTOCOL_V2_TARGET_STATUS_FINISHED = 2;
55
69
  const PROTOCOL_V2_TARGET_STATUS_FAILED_MIN = 3;
56
70
  const PROTOCOL_V2_CONNECT_PROTOCOL = 'V2';
57
- const PROTOCOL_V2_FIRMWARE_STAGING_VOLUME = 'vol1:';
71
+ const PROTOCOL_V2_FIRMWARE_STAGING_VOLUME = 'vol0:/';
58
72
  const PROTOCOL_V2_MIN_FILE_CHUNK_SIZE = 64;
59
73
  const PROTOCOL_V2_CONNECT_RETRY_COUNT = 10;
60
74
  const PROTOCOL_V2_CONNECT_POLL_INTERVAL = 500;
61
75
  const PROTOCOL_V2_CONNECT_SINGLE_TIMEOUT = 75 * 1000;
62
76
  const PROTOCOL_V2_DEVICE_INFO_READY_TIMEOUT = 60 * 1000;
77
+ const PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT = 3;
63
78
  const PROTOCOL_V2_OKPP_HEADER_SIZE = 0x52a0;
64
79
  const PROTOCOL_V2_OKPP_PAYLOAD_HASH_OFFSET = 0x200;
65
80
  const PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET = 0x240;
@@ -82,11 +97,6 @@ const getProtocolV2DeviceTransferProgress = (
82
97
  return Math.min(Math.max(Math.ceil((bytesAfterChunk / totalBytes) * 100), 1), 99);
83
98
  };
84
99
 
85
- const formatProtocolV2TransferSpeed = (bytes: number, elapsedMs: number) => {
86
- const safeElapsedMs = Math.max(elapsedMs, 1);
87
- return (bytes / 1024 / (safeElapsedMs / 1000)).toFixed(2);
88
- };
89
-
90
100
  type ProtocolV2FirmwareUpdateStatusTarget = {
91
101
  target_id: number | string;
92
102
  status?: number | string;
@@ -94,17 +104,22 @@ type ProtocolV2FirmwareUpdateStatusTarget = {
94
104
  path?: string;
95
105
  };
96
106
 
97
- type ProtocolV2FirmwareUpdateStartResponse =
98
- | TypedResponseMessage<'Success'>
99
- | TypedResponseMessage<'DeviceFirmwareUpdateStatus'>
100
- | undefined;
107
+ type ProtocolV2FirmwareUpdateStartResponse = TypedResponseMessage<'Success'>;
101
108
 
102
109
  type ProtocolV2TargetBinary = { fileName: string; binary: ArrayBuffer; targetId: number };
103
110
  type ProtocolV2InstallItem = ProtocolV2TargetBinary & {
104
111
  kind: ProtocolV2RemoteComponentTarget['kind'];
105
112
  };
106
- type ProtocolV2InstallTarget = ProtocolV2InstallItem & {
107
- path: string;
113
+ /** RESC bundle okpkg written independently to devicePath through FileWrite. */
114
+ type ProtocolV2ResourceBundleBinary = {
115
+ name: string;
116
+ binary: ArrayBuffer;
117
+ devicePath: string;
118
+ /** Download URL for remote-config mode; omitted in manual mode. */
119
+ url?: string;
120
+ version?: IVersionArray;
121
+ payloadHash?: string;
122
+ headerHash?: string;
108
123
  };
109
124
 
110
125
  type ProtocolV2RemoteComponentBinary = ProtocolV2RemoteComponentTarget & {
@@ -114,7 +129,36 @@ type ProtocolV2RemoteComponentBinary = ProtocolV2RemoteComponentTarget & {
114
129
  type ProtocolV2RemoteComponentTarget = {
115
130
  fileName: string;
116
131
  targetId: number;
117
- kind: 'bootloader' | 'firmware' | 'resource';
132
+ kind: 'bootloader' | 'firmware';
133
+ };
134
+
135
+ type ProtocolV2InstallSource = {
136
+ fileName: string;
137
+ source: FirmwareByteSource;
138
+ targetId: number;
139
+ kind: ProtocolV2RemoteComponentTarget['kind'];
140
+ };
141
+
142
+ type ProtocolV2ResourceBundleSource = {
143
+ name: string;
144
+ source: FirmwareByteSource;
145
+ devicePath: string;
146
+ version?: IVersionArray;
147
+ payloadHash?: string;
148
+ headerHash?: string;
149
+ };
150
+
151
+ type ProtocolV2ExecutionPhaseKind =
152
+ | 'resource-sync'
153
+ | 'bootloader-install'
154
+ | 'bootloader-verify'
155
+ | 'component-install'
156
+ | 'final-verify';
157
+
158
+ type ProtocolV2ExecutionPhase = {
159
+ kind: ProtocolV2ExecutionPhaseKind;
160
+ installSources: ProtocolV2InstallSource[];
161
+ resourceSources: ProtocolV2ResourceBundleSource[];
118
162
  };
119
163
 
120
164
  type ProtocolV2OkppHeader = {
@@ -167,18 +211,24 @@ const PROTOCOL_V2_REMOTE_COMPONENT_TARGETS: Readonly<
167
211
  targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE04,
168
212
  kind: 'firmware',
169
213
  },
170
- CRATE: {
171
- fileName: 'resource.bin',
172
- targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_CRATE,
173
- kind: 'resource',
174
- },
175
214
  };
176
215
 
216
+ const PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID = new Map<number, FirmwareUpdateV4Target>([
217
+ [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER, 'boot'],
218
+ [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1, 'app_v1'],
219
+ [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P2, 'app_v2'],
220
+ [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_COPROCESSOR, 'coprocessor'],
221
+ [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE01, 'se01'],
222
+ [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE02, 'se02'],
223
+ [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE03, 'se03'],
224
+ [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE04, 'se04'],
225
+ ]);
226
+
177
227
  const PROTOCOL_V2_ROMLOADER_UNSUPPORTED_MESSAGE =
178
228
  'FW_MGMT_TARGET_ROMLOADER is not accepted by the current Pro2 bootloader update request. Flash romloader with the loader-specific flow instead of firmwareUpdateV4.';
179
229
 
180
- // hd-transport 的历史 decode 行为会把单值 enum 输出为枚举名字符串;
181
- // Protocol V2 沿用这个 SDK 语义,内部比较前再映射回固件协议数值。
230
+ // hd-transport historically decodes scalar enums as enum-name strings.
231
+ // Map them back to firmware protocol values before internal comparisons.
182
232
  const PROTOCOL_V2_TARGET_ID_BY_DECODED_NAME = new Map<string, number>(
183
233
  Object.entries(ProtocolV2FirmwareTargetType).map(([key, value]) => [key, value])
184
234
  );
@@ -204,25 +254,12 @@ const isProtocolV2ReconnectProbeError = (error: unknown) => {
204
254
  );
205
255
  };
206
256
 
207
- const isProtocolV2PollingTransientError = (error: unknown) => {
208
- const message = getProtocolV2UnknownErrorText(error).toLowerCase();
209
- return (
210
- isProtocolV2DeviceDisconnectedError(error) ||
211
- isProtocolV2ReconnectProbeError(error) ||
212
- message.includes('libusb_transfer_timed_out') ||
213
- (message.includes('response timeout') && message.includes('devicefirmwareupdatestatusget')) ||
214
- message.includes('device not found') ||
215
- message.includes('transportnotfound')
216
- );
217
- };
218
-
219
- const isProtocolV2StartUpdateTransientError = (error: unknown) => {
257
+ const isProtocolV2FirmwareStatusEndpointUnavailable = (error: unknown) => {
220
258
  const message = getProtocolV2UnknownErrorText(error).toLowerCase();
221
259
  return (
222
- isProtocolV2DeviceDisconnectedError(error) ||
223
- isProtocolV2ReconnectProbeError(error) ||
224
- message.includes('libusb_transfer_timed_out') ||
225
- (message.includes('response timeout') && message.includes('devicefirmwareupdaterequest'))
260
+ message.includes('handler not registered') ||
261
+ message.includes('message handler not found') ||
262
+ message.includes('unsupported message')
226
263
  );
227
264
  };
228
265
 
@@ -268,13 +305,6 @@ const versionArrayToNumber = (version?: IVersionArray) => {
268
305
  return version[0] * 0x10000 + version[1] * 0x100 + version[2];
269
306
  };
270
307
 
271
- const versionStringToArray = (version?: string | null): IVersionArray | undefined => {
272
- if (!version) return undefined;
273
- const parts = version.split('.').map(part => Number(part));
274
- if (parts.length !== 3 || parts.some(part => !Number.isFinite(part))) return undefined;
275
- return parts as IVersionArray;
276
- };
277
-
278
308
  const compareProtocolV2Versions = (current?: IVersionArray, target?: IVersionArray) => {
279
309
  const currentNumber = versionArrayToNumber(current);
280
310
  const targetNumber = versionArrayToNumber(target);
@@ -361,6 +391,33 @@ const parseProtocolV2OkppHeader = (bytes: Uint8Array): ProtocolV2OkppHeader | nu
361
391
  };
362
392
  };
363
393
 
394
+ export const isProtocolV2FirmwareFingerprintValid = (
395
+ binary: ArrayBuffer | Uint8Array,
396
+ fingerprint: string | undefined
397
+ ) => {
398
+ const expectedFingerprint = normalizeProtocolV2Hex(fingerprint);
399
+ if (!expectedFingerprint) return true;
400
+ return bytesToHex(sha256(toProtocolV2Bytes(binary))) === expectedFingerprint;
401
+ };
402
+
403
+ export const assertProtocolV2ReconnectIdentity = (
404
+ expectedSerialNumber?: string,
405
+ actualSerialNumber?: string
406
+ ) => {
407
+ if (!expectedSerialNumber || !actualSerialNumber) {
408
+ throw ERRORS.TypedError(
409
+ HardwareErrorCode.DeviceNotFound,
410
+ 'Protocol V2 reconnect physical identity is unavailable'
411
+ );
412
+ }
413
+ if (actualSerialNumber !== expectedSerialNumber) {
414
+ throw ERRORS.TypedError(
415
+ HardwareErrorCode.DeviceNotFound,
416
+ `Protocol V2 reconnect physical identity mismatch: expected ${expectedSerialNumber}, received ${actualSerialNumber}`
417
+ );
418
+ }
419
+ };
420
+
364
421
  /**
365
422
  * FirmwareUpdateV4 is the complete Protocol V2 firmware update flow.
366
423
  *
@@ -370,9 +427,26 @@ const parseProtocolV2OkppHeader = (bytes: Uint8Array): ProtocolV2OkppHeader | nu
370
427
  * - completion waits for target status to finish, reboots to normal, then polls DeviceInfo
371
428
  */
372
429
  export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareUpdateV4Params> {
430
+ private protocolV2ExpectedSerialNumber?: string;
431
+
432
+ getSupportedProtocols() {
433
+ return ['V2'] as const;
434
+ }
435
+
436
+ private protocolV2PreparedSources: FirmwareByteSource[] = [];
437
+
438
+ private protocolV2ExecutionInLoader = false;
439
+
440
+ private protocolV2CompletedTargetVersions = new Map<number, number>();
441
+
442
+ private protocolV2LatestFinalFeatures?: Features;
443
+
444
+ private protocolV2FinalStatusVerified = false;
445
+
373
446
  init() {
374
447
  this.allowDeviceMode = [UI_REQUEST.BOOTLOADER, UI_REQUEST.NOT_INITIALIZE];
375
448
  this.requireDeviceMode = [];
449
+ this.unlockPolicy = 'unlock-before-run';
376
450
  this.useDevicePassphraseState = false;
377
451
  this.skipForceUpdateCheck = true;
378
452
 
@@ -393,7 +467,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
393
467
 
394
468
  validateParams(payload, [
395
469
  { name: 'chunkSize', type: 'number' },
396
- { name: 'resourceBinaries', type: 'array', allowEmpty: true },
397
470
  { name: 'forcedUpdateRes', type: 'boolean' },
398
471
  { name: 'bootloaderBinary', type: 'buffer' },
399
472
  { name: 'romloaderBinary', type: 'buffer' },
@@ -405,10 +478,64 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
405
478
  { name: 'se03Binary', type: 'buffer' },
406
479
  { name: 'se04Binary', type: 'buffer' },
407
480
  { name: 'firmwareType', type: 'string' },
481
+ { name: 'targetsToUpdate', type: 'array', allowEmpty: true },
408
482
  { name: 'platform', type: 'string' },
483
+ { name: 'expectedDeviceId', type: 'string' },
484
+ { name: 'resourceBundleFiles', type: 'array', allowEmpty: true },
409
485
  ]);
486
+ if (
487
+ payload.expectedDeviceId !== undefined &&
488
+ (payload.expectedDeviceId.length === 0 || payload.expectedDeviceId.length > 160)
489
+ ) {
490
+ throw ERRORS.TypedError(
491
+ HardwareErrorCode.CallMethodInvalidParameter,
492
+ 'Protocol V2 expected device identity is invalid'
493
+ );
494
+ }
495
+ const hostBinding =
496
+ payload.preparedPlan || payload.componentArtifacts || payload.resourceBundleArtifacts
497
+ ? resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration)
498
+ : undefined;
499
+ if (hostBinding) {
500
+ assertFirmwareUpdatePreparedPlanBinding({
501
+ preparedPlan: payload.preparedPlan,
502
+ executor: 'v4',
503
+ platform: payload.platform,
504
+ scopeTargets: [
505
+ 'boot',
506
+ 'app_v1',
507
+ 'app_v2',
508
+ 'coprocessor',
509
+ 'resource',
510
+ 'se01',
511
+ 'se02',
512
+ 'se03',
513
+ 'se04',
514
+ ],
515
+ bindings: [
516
+ ...Object.entries(payload.componentArtifacts ?? {}).flatMap(([target, artifact]) =>
517
+ artifact
518
+ ? [
519
+ {
520
+ target: target as Exclude<FirmwareUpdateV4Target, 'resource'>,
521
+ artifact,
522
+ },
523
+ ]
524
+ : []
525
+ ),
526
+ ...(payload.resourceBundleArtifacts ?? []).map(
527
+ (entry: { name: string; artifact: FirmwareArtifactReference }) => ({
528
+ target: 'resource' as const,
529
+ logicalName: entry.name,
530
+ artifact: entry.artifact,
531
+ })
532
+ ),
533
+ ],
534
+ });
535
+ }
410
536
 
411
537
  this.params = {
538
+ preparedPlan: payload.preparedPlan,
412
539
  chunkSize: payload.chunkSize,
413
540
  forcedUpdateRes: payload.forcedUpdateRes,
414
541
  bootloaderBinary: payload.bootloaderBinary,
@@ -420,19 +547,29 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
420
547
  se02Binary: payload.se02Binary,
421
548
  se03Binary: payload.se03Binary,
422
549
  se04Binary: payload.se04Binary,
423
- resourceBinaries: payload.resourceBinaries,
550
+ resourceBundleFiles: payload.resourceBundleFiles,
424
551
  firmwareType: payload.firmwareType,
552
+ targetsToUpdate: payload.targetsToUpdate,
553
+ expectedTargetVersions: payload.expectedTargetVersions,
425
554
  platform: payload.platform,
555
+ expectedDeviceId: payload.expectedDeviceId,
556
+ artifactReader: hostBinding?.artifactReader ?? payload.artifactReader,
557
+ componentArtifacts: payload.componentArtifacts,
558
+ resourceBundleArtifacts: payload.resourceBundleArtifacts,
426
559
  };
427
560
  }
428
561
 
429
- private getProtocolV2FirmwareChunkSize() {
562
+ private getProtocolV2FirmwareChunkSize(direction: 'read' | 'write' = 'write') {
430
563
  const payloadChunkSize = Number(this.params?.chunkSize);
431
564
  const env = DataManager.getSettings('env');
432
- const maxChunkSize =
433
- this.params?.platform === 'native' || (env && DataManager.isBleConnect(env))
434
- ? PROTOCOL_V2_BLE_FILE_CHUNK_SIZE
435
- : PROTOCOL_V2_WEBUSB_FILE_CHUNK_SIZE;
565
+ const isBle = this.params?.platform === 'native' || (env && DataManager.isBleConnect(env));
566
+ let maxChunkSize = PROTOCOL_V2_WEBUSB_FILE_CHUNK_SIZE;
567
+ if (isBle) {
568
+ maxChunkSize =
569
+ direction === 'read'
570
+ ? PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE
571
+ : PROTOCOL_V2_BLE_FILE_CHUNK_SIZE;
572
+ }
436
573
  if (!Number.isFinite(payloadChunkSize) || payloadChunkSize <= 0) {
437
574
  return maxChunkSize;
438
575
  }
@@ -443,69 +580,355 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
443
580
  }
444
581
 
445
582
  async run() {
446
- if (!this.device.isProtocolV2()) {
447
- throw ERRORS.TypedError(
448
- HardwareErrorCode.RuntimeError,
449
- 'firmwareUpdateV4 requires a Protocol V2 device'
450
- );
451
- }
452
-
453
583
  Log.debug('FirmwareUpdateV4 strategy: Protocol V2');
454
584
  return this.runProtocolV2();
455
585
  }
456
586
 
457
587
  private async runProtocolV2() {
588
+ await this.captureProtocolV2PhysicalIdentity();
458
589
  const deviceFeatures = await this.getProtocolV2DeviceFeatures();
459
590
  const deviceFirmwareType = getFirmwareType(deviceFeatures);
460
591
  const firmwareType = this.params.firmwareType ?? deviceFirmwareType;
592
+ this.validateExpectedTargetVersions();
593
+
594
+ if (
595
+ (this.params.componentArtifacts && Object.keys(this.params.componentArtifacts).length > 0) ||
596
+ this.params.resourceBundleArtifacts?.length
597
+ ) {
598
+ return this.runProtocolV2PreparedArtifacts(deviceFeatures, firmwareType);
599
+ }
461
600
 
462
- let resourceBinaryMap: ProtocolV2TargetBinary[] = [];
463
601
  let fwBinaryMap: ProtocolV2TargetBinary[] = [];
464
602
  let bootloaderBinary: ArrayBuffer | null = null;
465
603
  let installItems: ProtocolV2InstallItem[] | undefined;
604
+ let resourceBundles: ProtocolV2ResourceBundleBinary[] | undefined;
466
605
  try {
467
606
  this.postTipMessage(FirmwareUpdateTipMessage.StartDownloadFirmware);
468
- resourceBinaryMap = await this.prepareResourceBinaries(firmwareType, deviceFeatures);
469
607
  fwBinaryMap = this.collectExplicitTargetBinaries();
470
608
  bootloaderBinary = this.prepareBootloaderBinary();
471
609
  if (!this.hasExplicitProtocolV2Payload(fwBinaryMap)) {
610
+ if (
611
+ this.params.artifactReader ||
612
+ DataManager.getSettings('firmwareManifestMode') === 'external-only'
613
+ ) {
614
+ throw ERRORS.TypedError(
615
+ HardwareErrorCode.RuntimeError,
616
+ 'Protocol V2 firmware artifacts must be prepared by the external firmware host',
617
+ {
618
+ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
619
+ }
620
+ );
621
+ }
472
622
  const remoteBinaries = await this.prepareRemoteProtocolV2Binaries(
473
623
  firmwareType,
474
624
  deviceFeatures
475
625
  );
476
- resourceBinaryMap = remoteBinaries.resourceBinaryMap;
477
626
  bootloaderBinary = remoteBinaries.bootloaderBinary;
478
627
  fwBinaryMap = remoteBinaries.fwBinaryMap;
479
628
  installItems = remoteBinaries.installItems;
480
629
  }
630
+ const resourceDescriptors = this.prepareProtocolV2ResourceBundles(
631
+ firmwareType,
632
+ deviceFeatures
633
+ );
634
+ resourceBundles = resourceDescriptors?.length
635
+ ? await this.materializeProtocolV2ResourceBundles(resourceDescriptors)
636
+ : undefined;
481
637
  this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
482
638
  } catch (err) {
483
- throw ERRORS.TypedError(HardwareErrorCode.FirmwareUpdateDownloadFailed, err.message ?? err);
639
+ throw normalizeFirmwarePreparationError(err);
484
640
  }
485
641
 
486
- if (resourceBinaryMap.length === 0 && !bootloaderBinary && fwBinaryMap.length === 0) {
642
+ if (!bootloaderBinary && fwBinaryMap.length === 0 && !resourceBundles?.length) {
487
643
  throw ERRORS.TypedError(
488
644
  HardwareErrorCode.FirmwareUpdateDownloadFailed,
489
645
  'No firmware to update'
490
646
  );
491
647
  }
492
648
 
493
- await this.enterProtocolV2BootloaderMode();
494
-
495
- await this.executeProtocolV2Update({
496
- resourceBinaryMap,
649
+ return this.executeProtocolV2Update({
497
650
  fwBinaryMap,
498
651
  bootloaderBinary,
499
652
  ...(installItems ? { installItems } : undefined),
653
+ ...(resourceBundles?.length ? { resourceBundles } : undefined),
500
654
  });
655
+ }
501
656
 
502
- await this.exitProtocolV2BootloaderToNormal();
657
+ private async openProtocolV2PreparedSource(artifact: FirmwareArtifactReference) {
658
+ const source = await openFirmwareByteSource({
659
+ artifact,
660
+ reader: this.params.artifactReader,
661
+ });
662
+ if (!source) {
663
+ throw ERRORS.TypedError(
664
+ HardwareErrorCode.RuntimeError,
665
+ `Firmware artifact ${artifact.artifactRef} is not prepared`,
666
+ {
667
+ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
668
+ }
669
+ );
670
+ }
671
+ this.protocolV2PreparedSources.push(source);
672
+ return source;
673
+ }
503
674
 
504
- const versions = await this.waitForProtocolV2FinalFeatures();
505
- this.postTipMessage(FirmwareUpdateTipMessage.FirmwareUpdateCompleted);
506
- DevicePool.resetState();
675
+ private async openProtocolV2MemorySource(binary: ArrayBuffer) {
676
+ const source = await openFirmwareByteSource({ binary });
677
+ if (!source) {
678
+ throw ERRORS.TypedError(
679
+ HardwareErrorCode.RuntimeError,
680
+ 'Protocol V2 firmware binary is empty'
681
+ );
682
+ }
683
+ this.protocolV2PreparedSources.push(source);
684
+ return source;
685
+ }
507
686
 
508
- return versions;
687
+ private async closeProtocolV2PreparedSources() {
688
+ const sources = this.protocolV2PreparedSources.splice(0);
689
+ await Promise.all(sources.map(source => source.close().catch(() => undefined)));
690
+ }
691
+
692
+ private async prepareProtocolV2InstallSources(
693
+ firmwareType: EFirmwareType,
694
+ features: Features
695
+ ): Promise<ProtocolV2InstallSource[]> {
696
+ const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
697
+ if (!release) {
698
+ throw ERRORS.TypedError(
699
+ HardwareErrorCode.RuntimeError,
700
+ 'Protocol V2 firmware release is unavailable'
701
+ );
702
+ }
703
+ const componentArtifacts = this.params.componentArtifacts ?? {};
704
+ const requestedTargets = new Set(
705
+ this.params.targetsToUpdate ?? (Object.keys(componentArtifacts) as FirmwareUpdateV4Target[])
706
+ );
707
+ const installSources: ProtocolV2InstallSource[] = [];
708
+ const preparedTargets = new Set<FirmwareUpdateV4Target>();
709
+
710
+ for (const [key, component] of this.getRemoteComponentEntries(release)) {
711
+ const target = this.getRemoteComponentTarget(key, component);
712
+ const updateTarget = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(target.targetId);
713
+ if (updateTarget && updateTarget !== 'resource' && requestedTargets.has(updateTarget)) {
714
+ const artifact = componentArtifacts[updateTarget];
715
+ if (!artifact) {
716
+ throw ERRORS.TypedError(
717
+ HardwareErrorCode.RuntimeError,
718
+ `Protocol V2 ${updateTarget} artifact is not prepared`,
719
+ {
720
+ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
721
+ artifactName: updateTarget,
722
+ }
723
+ );
724
+ }
725
+ installSources.push({
726
+ ...target,
727
+ source: await this.openProtocolV2PreparedSource(artifact),
728
+ });
729
+ preparedTargets.add(updateTarget);
730
+ }
731
+ }
732
+
733
+ const unknownTarget = Array.from(requestedTargets).find(
734
+ target => target !== 'resource' && !preparedTargets.has(target)
735
+ );
736
+ if (unknownTarget) {
737
+ throw ERRORS.TypedError(
738
+ HardwareErrorCode.RuntimeError,
739
+ `Protocol V2 release does not contain requested target ${unknownTarget}`
740
+ );
741
+ }
742
+ return installSources;
743
+ }
744
+
745
+ private async prepareProtocolV2ResourceSources(
746
+ firmwareType: EFirmwareType,
747
+ features: Features
748
+ ): Promise<ProtocolV2ResourceBundleSource[]> {
749
+ const preparedArtifacts = this.params.resourceBundleArtifacts ?? [];
750
+ const resourceRequested =
751
+ this.params.targetsToUpdate?.includes('resource') || preparedArtifacts.length > 0;
752
+ if (!resourceRequested) {
753
+ return [];
754
+ }
755
+ const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
756
+ const descriptors = release?.resourceBundles ?? [];
757
+ const artifactByName = new Map(
758
+ preparedArtifacts.map(item => [item.name, item.artifact] as const)
759
+ );
760
+ const sources: ProtocolV2ResourceBundleSource[] = [];
761
+ for (const descriptor of descriptors) {
762
+ const artifact = artifactByName.get(descriptor.name);
763
+ if (!artifact) {
764
+ throw ERRORS.TypedError(
765
+ HardwareErrorCode.RuntimeError,
766
+ `Protocol V2 resource bundle ${descriptor.name} is not prepared`,
767
+ {
768
+ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
769
+ artifactName: descriptor.name,
770
+ }
771
+ );
772
+ }
773
+ const devicePath = validateProtocolV2FilesystemPath(
774
+ descriptor.devicePath,
775
+ 'resourceBundles[].devicePath'
776
+ );
777
+ sources.push({
778
+ name: descriptor.name,
779
+ source: await this.openProtocolV2PreparedSource(artifact),
780
+ devicePath,
781
+ version: descriptor.version,
782
+ payloadHash: descriptor.payloadHash,
783
+ headerHash: descriptor.headerHash,
784
+ });
785
+ artifactByName.delete(descriptor.name);
786
+ }
787
+ if (artifactByName.size > 0) {
788
+ throw ERRORS.TypedError(
789
+ HardwareErrorCode.RuntimeError,
790
+ `Protocol V2 release does not contain resource bundle ${artifactByName.keys().next().value}`
791
+ );
792
+ }
793
+ return sources;
794
+ }
795
+
796
+ private async runProtocolV2PreparedArtifacts(features: Features, firmwareType: EFirmwareType) {
797
+ try {
798
+ this.postTipMessage(FirmwareUpdateTipMessage.StartDownloadFirmware);
799
+ const installSources = await this.prepareProtocolV2InstallSources(firmwareType, features);
800
+ const resourceSources = await this.prepareProtocolV2ResourceSources(firmwareType, features);
801
+ if (installSources.length === 0 && resourceSources.length === 0) {
802
+ throw ERRORS.TypedError(
803
+ HardwareErrorCode.FirmwareUpdateDownloadFailed,
804
+ 'No firmware to update'
805
+ );
806
+ }
807
+ this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
808
+
809
+ return await this.executeProtocolV2SourceUpdate({
810
+ installSources,
811
+ resourceSources,
812
+ });
813
+ } finally {
814
+ await this.closeProtocolV2PreparedSources();
815
+ }
816
+ }
817
+
818
+ private validateExpectedTargetVersions() {
819
+ const expected = this.params.expectedTargetVersions;
820
+ if (expected === undefined) return;
821
+ if (typeof expected !== 'object' || expected === null || Array.isArray(expected)) {
822
+ throw ERRORS.TypedError(
823
+ HardwareErrorCode.RuntimeError,
824
+ 'Protocol V2 expected target versions are invalid'
825
+ );
826
+ }
827
+ for (const [target, version] of Object.entries(expected)) {
828
+ if (
829
+ !Array.from(PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.values()).includes(
830
+ target as FirmwareUpdateV4Target
831
+ ) ||
832
+ typeof version !== 'string' ||
833
+ !/^\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.-]+)?$/u.test(version) ||
834
+ version.length > 64
835
+ ) {
836
+ throw ERRORS.TypedError(
837
+ HardwareErrorCode.RuntimeError,
838
+ 'Protocol V2 expected target version is invalid'
839
+ );
840
+ }
841
+ }
842
+ }
843
+
844
+ private getExpectedProtocolV2TargetVersion(targetId: number) {
845
+ const target = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(targetId);
846
+ const version = target ? this.params?.expectedTargetVersions?.[target] : undefined;
847
+ if (!version) return undefined;
848
+ const parts = version.split(/[+-]/u, 1)[0].split('.').map(Number);
849
+ if (
850
+ parts.length !== 3 ||
851
+ parts.some(part => !Number.isSafeInteger(part) || part < 0 || part > 0xff)
852
+ ) {
853
+ return undefined;
854
+ }
855
+ return parts[0] * 0x10000 + parts[1] * 0x100 + parts[2];
856
+ }
857
+
858
+ private assertExpectedProtocolV2Versions(targets?: readonly FirmwareUpdateV4Target[]) {
859
+ const expected = this.params?.expectedTargetVersions;
860
+ if (!expected) return;
861
+ const features = this.protocolV2LatestFinalFeatures;
862
+ const requestedTargets = this.getProtocolV2RequestedTargets();
863
+ const applicationTargets = requestedTargets.filter(
864
+ target => target === 'app_v1' || target === 'app_v2'
865
+ );
866
+ const visibleVersions: Partial<Record<FirmwareUpdateV4Target, string>> = {
867
+ boot: features ? getDeviceBootloaderVersion(features).join('.') : undefined,
868
+ coprocessor: features ? getDeviceBLEFirmwareVersion(features).join('.') : undefined,
869
+ se01: features?.se01Version ?? undefined,
870
+ se02: features?.se02Version ?? undefined,
871
+ se03: features?.se03Version ?? undefined,
872
+ se04: features?.se04Version ?? undefined,
873
+ };
874
+ if (features && applicationTargets.length === 1) {
875
+ visibleVersions[applicationTargets[0]] = getDeviceFirmwareVersion(features).join('.');
876
+ }
877
+ const expectedEntries = (
878
+ Object.entries(expected) as Array<[FirmwareUpdateV4Target, string]>
879
+ ).filter(([target]) => !targets || targets.includes(target));
880
+ for (const [target, expectedVersion] of expectedEntries) {
881
+ const targetId = Array.from(PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.entries()).find(
882
+ ([, mappedTarget]) => mappedTarget === target
883
+ )?.[0];
884
+ const statusVersion =
885
+ targetId === undefined ? undefined : this.protocolV2CompletedTargetVersions.get(targetId);
886
+ const observedVersion =
887
+ statusVersion === undefined
888
+ ? visibleVersions[target]
889
+ : `${Math.floor(statusVersion / 0x10000) % 0x100}.${
890
+ Math.floor(statusVersion / 0x100) % 0x100
891
+ }.${statusVersion % 0x100}`;
892
+ if (!observedVersion) {
893
+ if (this.protocolV2FinalStatusVerified) {
894
+ Log.warn(
895
+ `Protocol V2 target ${target} has no observable final version; completed target status is authoritative`
896
+ );
897
+ } else {
898
+ throw ERRORS.TypedError(
899
+ HardwareErrorCode.FirmwareVerificationFailed,
900
+ `Protocol V2 target ${target} has no observable final version after status fallback`
901
+ );
902
+ }
903
+ } else if (observedVersion !== expectedVersion) {
904
+ throw ERRORS.TypedError(
905
+ HardwareErrorCode.FirmwareVerificationFailed,
906
+ `Protocol V2 target ${target} reached ${observedVersion}, expected ${expectedVersion}`
907
+ );
908
+ }
909
+ }
910
+ }
911
+
912
+ private getProtocolV2RequestedTargets(): FirmwareUpdateV4Target[] {
913
+ if (this.params.targetsToUpdate?.length) {
914
+ return [...new Set(this.params.targetsToUpdate)];
915
+ }
916
+ const targets = new Set<FirmwareUpdateV4Target>();
917
+ Object.keys(this.params.componentArtifacts ?? {}).forEach(target =>
918
+ targets.add(target as FirmwareUpdateV4Target)
919
+ );
920
+ if (this.params.resourceBundleArtifacts?.length || this.params.resourceBundleFiles?.length) {
921
+ targets.add('resource');
922
+ }
923
+ if (this.params.bootloaderBinary) targets.add('boot');
924
+ if (this.params.applicationP1Binary) targets.add('app_v1');
925
+ if (this.params.applicationP2Binary) targets.add('app_v2');
926
+ if (this.params.coprocessorBinary) targets.add('coprocessor');
927
+ if (this.params.se01Binary) targets.add('se01');
928
+ if (this.params.se02Binary) targets.add('se02');
929
+ if (this.params.se03Binary) targets.add('se03');
930
+ if (this.params.se04Binary) targets.add('se04');
931
+ return Array.from(targets);
509
932
  }
510
933
 
511
934
  private async getProtocolV2DeviceFeatures(): Promise<Features> {
@@ -519,41 +942,45 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
519
942
  throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'Device features not available');
520
943
  }
521
944
 
522
- private async prepareResourceBinaries(firmwareType: EFirmwareType, features: Features) {
523
- if (this.params.resourceBinaries?.length) {
524
- this.params.resourceBinaries.forEach((binary, index) => {
525
- if (!(binary instanceof ArrayBuffer)) {
526
- throw ERRORS.TypedError(
527
- HardwareErrorCode.CallMethodInvalidParameter,
528
- `Parameter [resourceBinaries.${index}] is of type invalid and should be [buffer].`
529
- );
530
- }
531
- });
945
+ private getProtocolV2SerialNumber(deviceInfo: ProtocolV2DeviceInfo) {
946
+ const serialNumber = deviceInfo.hw?.serial_no?.trim();
947
+ return serialNumber || undefined;
948
+ }
532
949
 
533
- return this.params.resourceBinaries.map((binary, index) => ({
534
- fileName: `resource-${index + 1}.bin`,
535
- binary,
536
- targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_CRATE,
537
- }));
538
- }
539
- const resourceUrl = DataManager.getSysResourcesLatestRelease({
540
- features,
541
- forcedUpdateRes: this.params.forcedUpdateRes,
542
- firmwareType,
950
+ private assertProtocolV2DeviceInfoIdentity(deviceInfo: ProtocolV2DeviceInfo) {
951
+ assertProtocolV2ReconnectIdentity(
952
+ this.protocolV2ExpectedSerialNumber,
953
+ this.getProtocolV2SerialNumber(deviceInfo)
954
+ );
955
+ }
956
+
957
+ private async requestProtocolV2PhysicalIdentity() {
958
+ return requestProtocolV2DeviceInfo({
959
+ commands: this.device.getCommands(),
960
+ timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
543
961
  });
962
+ }
544
963
 
545
- if (resourceUrl) {
546
- const resource = (await getSysResourceBinary(resourceUrl)).binary;
547
- return [
548
- {
549
- fileName: 'resource.bin',
550
- binary: resource,
551
- targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_CRATE,
552
- },
553
- ];
964
+ private async captureProtocolV2PhysicalIdentity() {
965
+ const deviceInfo = await this.requestProtocolV2PhysicalIdentity();
966
+ const serialNumber = this.getProtocolV2SerialNumber(deviceInfo);
967
+ if (this.params?.preparedPlan) {
968
+ assertFirmwareUpdatePreparedPlanDeviceIdentity({
969
+ preparedPlan: this.params.preparedPlan,
970
+ deviceIdentity: serialNumber,
971
+ });
972
+ } else if (this.params?.expectedDeviceId) {
973
+ assertProtocolV2ReconnectIdentity(this.params?.expectedDeviceId, serialNumber);
974
+ } else {
975
+ assertProtocolV2ReconnectIdentity(serialNumber, serialNumber);
554
976
  }
555
- Log.warn('No resource url found');
556
- return [];
977
+ this.protocolV2ExpectedSerialNumber = serialNumber;
978
+ }
979
+
980
+ private async verifyProtocolV2ReconnectIdentity() {
981
+ const deviceInfo = await this.requestProtocolV2PhysicalIdentity();
982
+ this.assertProtocolV2DeviceInfoIdentity(deviceInfo);
983
+ return deviceInfo;
557
984
  }
558
985
 
559
986
  private prepareBootloaderBinary(): ArrayBuffer | null {
@@ -562,25 +989,20 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
562
989
 
563
990
  private hasExplicitProtocolV2Payload(fwBinaryMap: ProtocolV2TargetBinary[]) {
564
991
  return (
565
- !!this.params.resourceBinaries?.length ||
992
+ !!this.params.resourceBundleFiles?.length ||
566
993
  !!this.params.bootloaderBinary ||
567
994
  fwBinaryMap.length > 0
568
995
  );
569
996
  }
570
997
 
571
998
  private buildProtocolV2InstallItems({
572
- resourceBinaryMap,
573
999
  bootloaderBinary,
574
1000
  fwBinaryMap,
575
1001
  }: {
576
- resourceBinaryMap: ProtocolV2TargetBinary[];
577
1002
  bootloaderBinary: ArrayBuffer | null;
578
1003
  fwBinaryMap: ProtocolV2TargetBinary[];
579
1004
  }): ProtocolV2InstallItem[] {
580
- const installItems: ProtocolV2InstallItem[] = resourceBinaryMap.map(resource => ({
581
- ...resource,
582
- kind: 'resource',
583
- }));
1005
+ const installItems: ProtocolV2InstallItem[] = [];
584
1006
 
585
1007
  if (bootloaderBinary) {
586
1008
  installItems.push({
@@ -630,63 +1052,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
630
1052
  return target;
631
1053
  }
632
1054
 
633
- private getProtocolV2ComponentTargetVersion(
634
- release: IFirmwareReleaseInfo,
635
- component: IProtocolV2FirmwareComponent,
636
- target: ProtocolV2RemoteComponentTarget
637
- ) {
638
- if (component.version) return component.version;
639
- if (target.targetId === ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER) {
640
- return release.bootloaderVersion;
641
- }
642
- if (
643
- target.targetId === ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1 ||
644
- target.targetId === ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P2
645
- ) {
646
- return release.version;
647
- }
648
- return undefined;
649
- }
650
-
651
- private getProtocolV2ComponentCurrentVersion(
652
- features: Features,
653
- target: ProtocolV2RemoteComponentTarget
654
- ) {
655
- switch (target.targetId) {
656
- case ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER:
657
- return getDeviceBootloaderVersion(features);
658
- case ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1:
659
- case ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P2:
660
- return getDeviceFirmwareVersion(features);
661
- case ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_COPROCESSOR:
662
- return getDeviceBLEFirmwareVersion(features);
663
- case ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE01:
664
- return versionStringToArray(features.se01Version);
665
- case ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE02:
666
- return versionStringToArray(features.se02Version);
667
- case ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE03:
668
- return versionStringToArray(features.se03Version);
669
- case ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE04:
670
- return versionStringToArray(features.se04Version);
671
- default:
672
- return undefined;
673
- }
674
- }
675
-
676
- private isProtocolV2ComponentVersionSatisfied(
677
- release: IFirmwareReleaseInfo,
678
- component: IProtocolV2FirmwareComponent,
679
- target: ProtocolV2RemoteComponentTarget,
680
- features: Features
681
- ) {
682
- const targetVersion = this.getProtocolV2ComponentTargetVersion(release, component, target);
683
- if (!targetVersion) return false;
684
-
685
- const currentVersion = this.getProtocolV2ComponentCurrentVersion(features, target);
686
- const compareResult = compareProtocolV2Versions(currentVersion, targetVersion);
687
- return compareResult !== undefined && compareResult >= 0;
688
- }
689
-
690
1055
  private getProtocolV2ResourceFilePath(path: string) {
691
1056
  if (path.startsWith('vol')) return path;
692
1057
  if (path.startsWith('/')) return `vol0:${path}`;
@@ -709,7 +1074,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
709
1074
  return null;
710
1075
  }
711
1076
 
712
- const chunkSize = this.getProtocolV2FirmwareChunkSize();
1077
+ const chunkSize = this.getProtocolV2FirmwareChunkSize('read');
713
1078
  const chunks: Uint8Array[] = [];
714
1079
  let offset = 0;
715
1080
  while (offset < PROTOCOL_V2_OKPP_HEADER_SIZE) {
@@ -738,95 +1103,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
738
1103
  return parseProtocolV2OkppHeader(headerBytes);
739
1104
  }
740
1105
 
741
- private async isProtocolV2ResourcePackageMatched(
742
- pkg: IProtocolV2ResourceManifestPackage,
743
- manifestVersion?: IVersionArray
744
- ) {
745
- try {
746
- const header = await this.readProtocolV2DeviceFileHeader(pkg.path);
747
- if (!header) return false;
748
-
749
- const expectedType = pkg.type ?? 'RESC';
750
- const expectedVersion = pkg.version ?? manifestVersion;
751
- if (header.type !== expectedType) return false;
752
- if (expectedVersion && compareProtocolV2Versions(header.version, expectedVersion) !== 0) {
753
- return false;
754
- }
755
-
756
- const expectedPayloadHash = normalizeProtocolV2Hex(pkg.payloadHash);
757
- if (expectedPayloadHash && header.payloadHash !== expectedPayloadHash) return false;
758
-
759
- const expectedHeaderHash = normalizeProtocolV2Hex(pkg.headerHash);
760
- if (expectedHeaderHash && header.headerHash !== expectedHeaderHash) return false;
761
-
762
- return true;
763
- } catch (error) {
764
- Log.log(`Protocol V2 resource package check failed for ${pkg.path}: `, error);
765
- return false;
766
- }
767
- }
768
-
769
- private async isProtocolV2ResourceManifestSatisfied(
770
- manifest: IFirmwareReleaseInfo['resourceManifest']
771
- ) {
772
- if (!manifest?.packages?.length) return false;
773
-
774
- for (const pkg of manifest.packages) {
775
- if (!(await this.isProtocolV2ResourcePackageMatched(pkg, manifest.version))) {
776
- return false;
777
- }
778
- }
779
- return true;
780
- }
781
-
782
- private getProtocolV2ResourceManifest(
783
- release: IFirmwareReleaseInfo,
784
- component: IProtocolV2FirmwareComponent
785
- ) {
786
- return component.resourceManifest ?? release.resourceManifest;
787
- }
788
-
789
- private getProtocolV2ResourceComponentFileName(key: string) {
790
- const safeKey = key.replace(/[^a-z0-9_-]/gi, '_') || 'resource';
791
- return `resource-${safeKey}.bin`;
792
- }
793
-
794
- private async shouldInstallRemoteProtocolV2Component(
795
- release: IFirmwareReleaseInfo,
796
- key: string,
797
- component: IProtocolV2FirmwareComponent,
798
- target: ProtocolV2RemoteComponentTarget,
799
- features: Features
800
- ) {
801
- if (target.kind === 'resource') {
802
- if (
803
- this.params.forcedUpdateRes ||
804
- features.bootloaderMode ||
805
- features.mode === 'bootloader'
806
- ) {
807
- return true;
808
- }
809
- const resourceMatched = await this.isProtocolV2ResourceManifestSatisfied(
810
- this.getProtocolV2ResourceManifest(release, component)
811
- );
812
- if (resourceMatched) {
813
- Log.log(`[FirmwareUpdateV4] skip Protocol V2 resource component ${key}; manifest matched`);
814
- }
815
- return !resourceMatched;
816
- }
817
-
818
- const versionSatisfied = this.isProtocolV2ComponentVersionSatisfied(
819
- release,
820
- component,
821
- target,
822
- features
823
- );
824
- if (versionSatisfied) {
825
- Log.log(`[FirmwareUpdateV4] skip Protocol V2 component ${key}; version is up to date`);
826
- }
827
- return !versionSatisfied;
828
- }
829
-
830
1106
  private async downloadRemoteProtocolV2Component(
831
1107
  key: string,
832
1108
  component: IProtocolV2FirmwareComponent
@@ -840,23 +1116,37 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
840
1116
  }
841
1117
 
842
1118
  const { binary } = await getSysResourceBinary(component.url);
843
- return {
844
- ...target,
845
- binary,
846
- };
847
- }
848
-
849
- private async prepareRemoteProtocolV2Binaries(firmwareType: EFirmwareType, features: Features) {
1119
+ if (!isProtocolV2FirmwareFingerprintValid(binary, component.fingerprint)) {
1120
+ throw ERRORS.TypedError(
1121
+ HardwareErrorCode.RuntimeError,
1122
+ `Protocol V2 firmware fingerprint mismatch: ${key}/${component.target}`
1123
+ );
1124
+ }
1125
+ const expectedPayloadHash = normalizeProtocolV2Hex(component.payloadHash);
1126
+ if (expectedPayloadHash) {
1127
+ const header = parseProtocolV2OkppHeader(toProtocolV2Bytes(binary));
1128
+ if (!header || header.payloadHash !== expectedPayloadHash) {
1129
+ throw ERRORS.TypedError(
1130
+ HardwareErrorCode.RuntimeError,
1131
+ `Protocol V2 firmware payload hash mismatch: ${key}/${component.target}`
1132
+ );
1133
+ }
1134
+ }
1135
+ return {
1136
+ ...target,
1137
+ binary,
1138
+ };
1139
+ }
1140
+
1141
+ private async prepareRemoteProtocolV2Binaries(firmwareType: EFirmwareType, features: Features) {
850
1142
  const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
851
1143
 
852
- const resourceBinaryMap: ProtocolV2TargetBinary[] = [];
853
1144
  let bootloaderBinary: ArrayBuffer | null = null;
854
1145
  const fwBinaryMap: ProtocolV2TargetBinary[] = [];
855
1146
  const installItems: ProtocolV2InstallItem[] = [];
856
1147
 
857
1148
  if (!release) {
858
1149
  return {
859
- resourceBinaryMap,
860
1150
  bootloaderBinary,
861
1151
  fwBinaryMap,
862
1152
  installItems,
@@ -864,27 +1154,17 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
864
1154
  }
865
1155
 
866
1156
  const entries = this.getRemoteComponentEntries(release);
1157
+ const targetsToUpdate = new Set(this.params.targetsToUpdate ?? []);
867
1158
 
868
1159
  for (const [key, component] of entries) {
869
- const target = this.getRemoteComponentTarget(key, component);
870
- const shouldInstall = await this.shouldInstallRemoteProtocolV2Component(
871
- release,
872
- key,
873
- component,
874
- target,
875
- features
876
- );
877
- if (shouldInstall) {
1160
+ const targetName = component.target?.toUpperCase();
1161
+ const target = PROTOCOL_V2_REMOTE_COMPONENT_TARGETS[targetName];
1162
+ const updateTarget = target
1163
+ ? PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(target.targetId)
1164
+ : undefined;
1165
+ if (updateTarget && targetsToUpdate.has(updateTarget)) {
878
1166
  const remoteBinary = await this.downloadRemoteProtocolV2Component(key, component);
879
- if (remoteBinary.kind === 'resource') {
880
- const binaryEntry = {
881
- fileName: this.getProtocolV2ResourceComponentFileName(key),
882
- binary: remoteBinary.binary,
883
- targetId: remoteBinary.targetId,
884
- };
885
- resourceBinaryMap.push(binaryEntry);
886
- installItems.push({ ...binaryEntry, kind: remoteBinary.kind });
887
- } else if (remoteBinary.kind === 'bootloader') {
1167
+ if (remoteBinary.kind === 'bootloader') {
888
1168
  bootloaderBinary = remoteBinary.binary;
889
1169
  installItems.push({
890
1170
  fileName: remoteBinary.fileName,
@@ -905,23 +1185,189 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
905
1185
  }
906
1186
 
907
1187
  return {
908
- resourceBinaryMap,
909
1188
  bootloaderBinary,
910
1189
  fwBinaryMap,
911
1190
  installItems,
912
1191
  };
913
1192
  }
914
1193
 
1194
+ // ============================================================
1195
+ // Incremental RESC bundle sync through FileRead and FilesystemFileWrite
1196
+ // ============================================================
1197
+
1198
+ /**
1199
+ * Prepare the RESC bundle list.
1200
+ *
1201
+ * Two modes, matching FirmwareUpdateV3 binary-versus-version behavior:
1202
+ * - resourceBundleFiles supplied: use binaries directly without version checks.
1203
+ * - Otherwise load release.resourceBundles from remote config.json; the binaries
1204
+ * are prefetched before reboot and synchronization skips matching device headers.
1205
+ */
1206
+ private prepareProtocolV2ResourceBundles(
1207
+ firmwareType: EFirmwareType,
1208
+ features: Features
1209
+ ): ProtocolV2ResourceBundleBinary[] | undefined {
1210
+ // Manual binaries are installed directly without comparison.
1211
+ if (this.params.resourceBundleFiles?.length) {
1212
+ return this.params.resourceBundleFiles.map((file, index) => {
1213
+ const devicePath = validateProtocolV2FilesystemPath(
1214
+ file.devicePath,
1215
+ `resourceBundleFiles[${index}].devicePath`
1216
+ );
1217
+ return {
1218
+ name: devicePath.split('/').pop() ?? devicePath,
1219
+ binary: file.binary,
1220
+ devicePath,
1221
+ };
1222
+ });
1223
+ }
1224
+
1225
+ if (!this.params.targetsToUpdate?.includes('resource')) {
1226
+ return undefined;
1227
+ }
1228
+
1229
+ // Remote-config bundles are downloaded and compared later.
1230
+ const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
1231
+ if (!release?.resourceBundles?.length) return undefined;
1232
+
1233
+ return release.resourceBundles.map((bundle, index) => ({
1234
+ name: bundle.name,
1235
+ binary: new ArrayBuffer(0),
1236
+ devicePath: validateProtocolV2FilesystemPath(
1237
+ bundle.devicePath,
1238
+ `resourceBundles[${index}].devicePath`
1239
+ ),
1240
+ url: bundle.url,
1241
+ version: bundle.version,
1242
+ payloadHash: bundle.payloadHash,
1243
+ headerHash: bundle.headerHash,
1244
+ })) as ProtocolV2ResourceBundleBinary[];
1245
+ }
1246
+
1247
+ private async materializeProtocolV2ResourceBundles(
1248
+ bundles: ProtocolV2ResourceBundleBinary[]
1249
+ ): Promise<ProtocolV2ResourceBundleBinary[]> {
1250
+ for (const bundle of bundles) {
1251
+ let { binary } = bundle;
1252
+ let downloadedFromRemote = false;
1253
+ if (binary.byteLength === 0) {
1254
+ if (!bundle.url) {
1255
+ throw ERRORS.TypedError(
1256
+ HardwareErrorCode.FirmwareUpdateDownloadFailed,
1257
+ `Missing Protocol V2 RESC bundle binary: ${bundle.name}`
1258
+ );
1259
+ }
1260
+ if (
1261
+ this.params.artifactReader ||
1262
+ DataManager.getSettings('firmwareManifestMode') === 'external-only'
1263
+ ) {
1264
+ throw ERRORS.TypedError(
1265
+ HardwareErrorCode.RuntimeError,
1266
+ `Firmware resource bundle ${bundle.name} is not prepared`,
1267
+ {
1268
+ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
1269
+ artifactName: bundle.name,
1270
+ }
1271
+ );
1272
+ }
1273
+ Log.log(`[FirmwareUpdateV4] downloading remote RESC bundle ${bundle.name}`);
1274
+ ({ binary } = await getSysResourceBinary(bundle.url));
1275
+ bundle.binary = binary;
1276
+ downloadedFromRemote = true;
1277
+ }
1278
+ if (bundle.binary.byteLength === 0) {
1279
+ throw ERRORS.TypedError(
1280
+ HardwareErrorCode.FirmwareUpdateDownloadFailed,
1281
+ `Protocol V2 RESC bundle is empty: ${bundle.name}`
1282
+ );
1283
+ }
1284
+ if (downloadedFromRemote) {
1285
+ const header = parseProtocolV2OkppHeader(toProtocolV2Bytes(binary));
1286
+ if (!header || header.type !== 'RESC') {
1287
+ throw new Error(`Invalid Protocol V2 RESC bundle header: ${bundle.name}`);
1288
+ }
1289
+ if (bundle.version && compareProtocolV2Versions(header.version, bundle.version) !== 0) {
1290
+ throw new Error(`Protocol V2 RESC bundle version mismatch: ${bundle.name}`);
1291
+ }
1292
+ const expectedPayloadHash = normalizeProtocolV2Hex(bundle.payloadHash);
1293
+ if (expectedPayloadHash && header.payloadHash !== expectedPayloadHash) {
1294
+ throw new Error(`Protocol V2 RESC bundle payload hash mismatch: ${bundle.name}`);
1295
+ }
1296
+ const expectedHeaderHash = normalizeProtocolV2Hex(bundle.headerHash);
1297
+ if (expectedHeaderHash && header.headerHash !== expectedHeaderHash) {
1298
+ throw new Error(`Protocol V2 RESC bundle header hash mismatch: ${bundle.name}`);
1299
+ }
1300
+ }
1301
+ }
1302
+ return bundles;
1303
+ }
1304
+
1305
+ /**
1306
+ * Compare the existing okpkg OKPP header in remote-config mode.
1307
+ */
1308
+ private async isProtocolV2ResourceBundleUpToDate(
1309
+ bundle: Pick<
1310
+ ProtocolV2ResourceBundleBinary,
1311
+ 'name' | 'devicePath' | 'version' | 'payloadHash' | 'headerHash'
1312
+ >
1313
+ ): Promise<boolean> {
1314
+ if (this.params?.forcedUpdateRes) return false;
1315
+ if (!bundle.version && !bundle.payloadHash) return false;
1316
+
1317
+ try {
1318
+ const header = await this.readProtocolV2DeviceFileHeader(bundle.devicePath);
1319
+ if (!header) return false;
1320
+
1321
+ if (bundle.version) {
1322
+ const cmp = compareProtocolV2Versions(header.version, bundle.version);
1323
+ if (cmp === undefined || cmp !== 0) return false;
1324
+ }
1325
+ if (bundle.payloadHash) {
1326
+ const expected = normalizeProtocolV2Hex(bundle.payloadHash);
1327
+ if (expected && header.payloadHash !== expected) return false;
1328
+ }
1329
+ if (bundle.headerHash) {
1330
+ const expected = normalizeProtocolV2Hex(bundle.headerHash);
1331
+ if (expected && header.headerHash !== expected) return false;
1332
+ }
1333
+ return true;
1334
+ } catch (error) {
1335
+ Log.log(`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check failed: `, error);
1336
+ return false;
1337
+ }
1338
+ }
1339
+
915
1340
  private isProtocolV2BootloaderMode() {
916
1341
  if (typeof this.device.isBootloader === 'function') {
917
1342
  return this.device.isBootloader();
918
1343
  }
919
- return !!this.device.features?.bootloaderMode;
1344
+ return (
1345
+ this.device.features?.mode === 'bootloader' ||
1346
+ (this.device.features?.mode == null && !!this.device.features?.bootloaderMode)
1347
+ );
1348
+ }
1349
+
1350
+ private isProtocolV2RomloaderMode() {
1351
+ if (!this.device.isProtocolV2() || this.device.getCurrentDeviceType() !== EDeviceType.Pro2) {
1352
+ return false;
1353
+ }
1354
+ if (typeof this.device.isRomloader === 'function') {
1355
+ return this.device.isRomloader();
1356
+ }
1357
+ return this.device.features?.mode === 'romloader';
920
1358
  }
921
1359
 
922
1360
  async enterProtocolV2BootloaderMode() {
1361
+ // romloader is the first update environment and forwards targets to bootloader.
1362
+ // It rejects DeviceRebootType.Bootloader, so reuse the current connection.
1363
+ if (this.isProtocolV2RomloaderMode()) {
1364
+ Log.debug('Protocol V2 device is in romloader mode; start firmware update directly');
1365
+ this.protocolV2ExecutionInLoader = true;
1366
+ return false;
1367
+ }
923
1368
  if (this.isProtocolV2BootloaderMode()) {
924
- Log.debug('Protocol V2 device is already in bootloader mode, skip reboot to bootloader');
1369
+ Log.debug('Protocol V2 device is already in bootloader mode, skip reboot');
1370
+ this.protocolV2ExecutionInLoader = true;
925
1371
  return false;
926
1372
  }
927
1373
 
@@ -931,6 +1377,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
931
1377
  this.postTipMessage(FirmwareUpdateTipMessage.GoToBootloaderSuccess);
932
1378
  await wait(1000);
933
1379
  await this.waitForProtocolV2BootloaderMode();
1380
+ this.protocolV2ExecutionInLoader = true;
934
1381
  return true;
935
1382
  } catch (error) {
936
1383
  if (error instanceof HardwareError) {
@@ -955,12 +1402,19 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
955
1402
  commands: this.device.getCommands(),
956
1403
  timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
957
1404
  });
958
- const features = this.device.updateProtocolV2Features(deviceInfo);
959
- if (features?.bootloaderMode) {
1405
+ this.assertProtocolV2DeviceInfoIdentity(deviceInfo);
1406
+ const features = await this.device.probeProtocolV2RuntimeState(
1407
+ deviceInfo,
1408
+ PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT
1409
+ );
1410
+ if (features?.mode === 'bootloader') {
960
1411
  return features;
961
1412
  }
962
1413
  lastError = new Error('Protocol V2 device is reachable but is not in bootloader mode');
963
1414
  } catch (error) {
1415
+ if (this.isProtocolV2ReconnectIdentityError(error)) {
1416
+ throw error;
1417
+ }
964
1418
  lastError = error;
965
1419
  Log.log('Protocol V2 bootloader mode not ready, polling reconnect: ', error);
966
1420
  }
@@ -976,8 +1430,8 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
976
1430
  }
977
1431
 
978
1432
  /**
979
- * 收集按 DeviceFirmwareTargetType 拆分的显式目标二进制。
980
- * 文件名仅用于 staging 路径展示,target_id 已显式给定。
1433
+ * Collect explicit target binaries grouped by DeviceFirmwareTargetType.
1434
+ * Filenames are display-only staging paths because target_id is explicit.
981
1435
  */
982
1436
  private collectExplicitTargetBinaries() {
983
1437
  const entries: ProtocolV2TargetBinary[] = [];
@@ -1013,254 +1467,458 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1013
1467
  return entries;
1014
1468
  }
1015
1469
 
1470
+ private buildProtocolV2ExecutionPhases({
1471
+ installSources,
1472
+ resourceSources,
1473
+ }: {
1474
+ installSources: ProtocolV2InstallSource[];
1475
+ resourceSources: ProtocolV2ResourceBundleSource[];
1476
+ }): ProtocolV2ExecutionPhase[] {
1477
+ const phases: ProtocolV2ExecutionPhase[] = [];
1478
+ if (resourceSources.length > 0) {
1479
+ phases.push({
1480
+ kind: 'resource-sync',
1481
+ installSources: [],
1482
+ resourceSources,
1483
+ });
1484
+ }
1485
+ const bootloaderSources = installSources.filter(source => source.kind === 'bootloader');
1486
+ if (bootloaderSources.length > 0) {
1487
+ phases.push(
1488
+ {
1489
+ kind: 'bootloader-install',
1490
+ installSources: bootloaderSources,
1491
+ resourceSources: [],
1492
+ },
1493
+ {
1494
+ kind: 'bootloader-verify',
1495
+ installSources: [],
1496
+ resourceSources: [],
1497
+ }
1498
+ );
1499
+ }
1500
+ const componentSources = installSources.filter(source => source.kind !== 'bootloader');
1501
+ if (componentSources.length > 0) {
1502
+ phases.push({
1503
+ kind: 'component-install',
1504
+ installSources: componentSources,
1505
+ resourceSources: [],
1506
+ });
1507
+ }
1508
+ phases.push({
1509
+ kind: 'final-verify',
1510
+ installSources: [],
1511
+ resourceSources: [],
1512
+ });
1513
+ return phases;
1514
+ }
1515
+
1516
+ private async executeProtocolV2Phases({
1517
+ installSources,
1518
+ resourceSources,
1519
+ }: {
1520
+ installSources: ProtocolV2InstallSource[];
1521
+ resourceSources: ProtocolV2ResourceBundleSource[];
1522
+ }) {
1523
+ const phases = this.buildProtocolV2ExecutionPhases({
1524
+ installSources,
1525
+ resourceSources,
1526
+ });
1527
+ for (const phase of phases) {
1528
+ if (phase.kind === 'final-verify') {
1529
+ return this.completeProtocolV2FinalVerification();
1530
+ }
1531
+ if (phase.kind === 'resource-sync') {
1532
+ await this.enterProtocolV2BootloaderMode();
1533
+ await this.executeProtocolV2TransferPhase(phase);
1534
+ } else if (phase.kind === 'bootloader-install' || phase.kind === 'component-install') {
1535
+ await this.enterProtocolV2BootloaderMode();
1536
+ await this.executeProtocolV2TransferPhase(phase);
1537
+ await this.exitProtocolV2BootloaderToNormal();
1538
+ } else if (phase.kind === 'bootloader-verify') {
1539
+ await this.waitForProtocolV2FinalFeatures();
1540
+ this.assertExpectedProtocolV2Versions(['boot']);
1541
+ }
1542
+ }
1543
+ throw ERRORS.TypedError(
1544
+ HardwareErrorCode.RuntimeError,
1545
+ 'Protocol V2 execution has no final verification phase'
1546
+ );
1547
+ }
1548
+
1549
+ private async executeProtocolV2SourceUpdate({
1550
+ installSources,
1551
+ resourceSources,
1552
+ }: {
1553
+ installSources: ProtocolV2InstallSource[];
1554
+ resourceSources: ProtocolV2ResourceBundleSource[];
1555
+ }) {
1556
+ return this.executeProtocolV2Phases({
1557
+ installSources,
1558
+ resourceSources,
1559
+ });
1560
+ }
1561
+
1016
1562
  private async executeProtocolV2Update({
1017
- resourceBinaryMap,
1018
1563
  fwBinaryMap,
1019
1564
  bootloaderBinary,
1020
1565
  installItems,
1566
+ resourceBundles,
1021
1567
  }: {
1022
- resourceBinaryMap?: ProtocolV2TargetBinary[];
1023
1568
  fwBinaryMap?: ProtocolV2TargetBinary[];
1024
1569
  bootloaderBinary?: ArrayBuffer | null;
1025
1570
  installItems?: ProtocolV2InstallItem[];
1571
+ resourceBundles?: ProtocolV2ResourceBundleBinary[];
1026
1572
  }) {
1027
- const orderedInstallItems =
1573
+ const memoryInstallItems =
1028
1574
  installItems ??
1029
1575
  this.buildProtocolV2InstallItems({
1030
- resourceBinaryMap: resourceBinaryMap ?? [],
1031
- bootloaderBinary: bootloaderBinary ?? null,
1032
1576
  fwBinaryMap: fwBinaryMap ?? [],
1577
+ bootloaderBinary: bootloaderBinary ?? null,
1033
1578
  });
1034
- let totalSize = 0;
1035
- let processedSize = 0;
1036
- let transferredSize = 0;
1037
-
1038
- for (const item of orderedInstallItems) totalSize += item.binary.byteLength;
1039
-
1040
- this.postTipMessage(FirmwareUpdateTipMessage.StartTransferData);
1041
- const transferStartTime = Date.now();
1042
- const transferTransport = this.getProtocolV2FirmwareTransferTransport();
1043
- const chunkSize = this.getProtocolV2FirmwareChunkSize();
1044
- const onTransferredBytes = (bytes: number) => {
1045
- transferredSize = bytes;
1046
- };
1047
- Log.log(
1048
- `[FirmwareUpdateV4] transfer started transport=${transferTransport} total=${totalSize} bytes chunk=${chunkSize} bytes`
1049
- );
1050
-
1051
- const stagedInstallTargets: ProtocolV2InstallTarget[] = [];
1052
-
1053
1579
  try {
1054
- for (const item of orderedInstallItems) {
1055
- const filePath = this.getProtocolV2InstallItemStagingPath(item);
1056
- Log.log(
1057
- `[FirmwareUpdateV4] staging ${item.kind} via FilesystemFileWrite target=${item.targetId} path=${filePath} source=${item.fileName} bytes=${item.binary.byteLength}`
1058
- );
1059
- processedSize = await this.protocolV2CommonUpdateProcess({
1060
- payload: item.binary,
1061
- filePath,
1062
- processedSize,
1063
- totalSize,
1064
- onTransferredBytes,
1065
- });
1066
- transferredSize = processedSize;
1067
-
1068
- stagedInstallTargets.push({
1069
- ...item,
1070
- path: filePath,
1071
- });
1072
- }
1073
-
1074
- if (totalSize > 0) {
1075
- this.postProgressMessage(100, 'transferData');
1076
- }
1077
-
1078
- const elapsedMs = Date.now() - transferStartTime;
1079
- Log.log(
1080
- `[FirmwareUpdateV4] transfer finished transport=${transferTransport} bytes=${totalSize} elapsed=${(
1081
- elapsedMs / 1000
1082
- ).toFixed(2)}s speed=${formatProtocolV2TransferSpeed(totalSize, elapsedMs)} KB/s`
1580
+ const installSources = await Promise.all(
1581
+ memoryInstallItems.map(async item => ({
1582
+ fileName: item.fileName,
1583
+ source: await this.openProtocolV2MemorySource(item.binary),
1584
+ targetId: item.targetId,
1585
+ kind: item.kind,
1586
+ }))
1083
1587
  );
1084
- } catch (error) {
1085
- const elapsedMs = Date.now() - transferStartTime;
1086
- Log.warn(
1087
- `[FirmwareUpdateV4] transfer failed transport=${transferTransport} bytes=${transferredSize}/${totalSize} elapsed=${(
1088
- elapsedMs / 1000
1089
- ).toFixed(2)}s speed=${formatProtocolV2TransferSpeed(transferredSize, elapsedMs)} KB/s`
1588
+ const resourceSources = await Promise.all(
1589
+ (resourceBundles ?? []).map(async bundle => ({
1590
+ name: bundle.name,
1591
+ source: await this.openProtocolV2MemorySource(bundle.binary),
1592
+ devicePath: bundle.devicePath,
1593
+ version: bundle.version,
1594
+ payloadHash: bundle.payloadHash,
1595
+ headerHash: bundle.headerHash,
1596
+ }))
1090
1597
  );
1091
- throw error;
1598
+ return await this.executeProtocolV2Phases({
1599
+ installSources,
1600
+ resourceSources,
1601
+ });
1602
+ } finally {
1603
+ await this.closeProtocolV2PreparedSources();
1092
1604
  }
1605
+ }
1093
1606
 
1094
- this.postTipMessage(FirmwareUpdateTipMessage.ConfirmOnDevice);
1095
-
1096
- const firmwareTargets: Array<{ target_id: number; path: string }> = [];
1097
- const flushFirmwareTargets = async () => {
1098
- if (firmwareTargets.length === 0) return;
1099
- const targets = firmwareTargets.splice(0, firmwareTargets.length);
1100
- Log.log(`[FirmwareUpdateV4] DeviceFirmwareUpdateRequest targets=${JSON.stringify(targets)}`);
1101
- const startResponse = await this.protocolV2StartFirmwareUpdate({ targets });
1102
- await this.waitForProtocolV2FirmwareUpdateComplete(targets, startResponse);
1103
- };
1104
-
1105
- for (const item of stagedInstallTargets) {
1106
- const target = {
1107
- target_id: item.targetId,
1108
- path: item.path,
1109
- };
1110
- if (item.kind === 'resource') {
1111
- await flushFirmwareTargets();
1112
- const resourceTargets = [target];
1113
- Log.log(
1114
- `[FirmwareUpdateV4] DeviceFirmwareUpdateRequest resources=${JSON.stringify(
1115
- resourceTargets
1116
- )}`
1117
- );
1118
- const startResponse = await this.protocolV2StartFirmwareUpdate({
1119
- targets: resourceTargets,
1120
- });
1121
- await this.waitForProtocolV2FirmwareUpdateComplete(resourceTargets, startResponse);
1607
+ private async executeProtocolV2TransferPhase({
1608
+ installSources,
1609
+ resourceSources,
1610
+ }: ProtocolV2ExecutionPhase) {
1611
+ let totalSize = installSources.reduce((total, item) => total + item.source.size, 0);
1612
+ const resourcesToSync: ProtocolV2ResourceBundleSource[] = [];
1613
+ for (const resource of resourceSources) {
1614
+ if (await this.isProtocolV2ResourceBundleUpToDate(resource)) {
1615
+ Log.log(`[FirmwareUpdateV4] skip RESC bundle ${resource.name}; already up to date`);
1122
1616
  } else {
1123
- firmwareTargets.push(target);
1617
+ resourcesToSync.push(resource);
1618
+ totalSize += resource.source.size;
1124
1619
  }
1125
1620
  }
1126
1621
 
1127
- await flushFirmwareTargets();
1128
- }
1622
+ this.postTipMessage(FirmwareUpdateTipMessage.StartTransferData);
1623
+ let processedSize = 0;
1624
+ for (const resource of resourcesToSync) {
1625
+ processedSize = await this.protocolV2SourceUpdateProcess({
1626
+ source: resource.source,
1627
+ filePath: resource.devicePath,
1628
+ processedSize,
1629
+ totalSize,
1630
+ });
1631
+ await this.verifyProtocolV2StagedFile(resource.devicePath, resource.source.size);
1632
+ }
1129
1633
 
1130
- private getProtocolV2InstallItemStagingPath(item: ProtocolV2InstallItem) {
1131
- return `${PROTOCOL_V2_FIRMWARE_STAGING_VOLUME}${item.fileName}`;
1132
- }
1634
+ const stagedInstallTargets: Array<{ targetId: number; path: string }> = [];
1635
+ for (const item of installSources) {
1636
+ const filePath = this.getProtocolV2InstallItemStagingPath(item);
1637
+ processedSize = await this.protocolV2SourceUpdateProcess({
1638
+ source: item.source,
1639
+ filePath,
1640
+ processedSize,
1641
+ totalSize,
1642
+ });
1643
+ await this.verifyProtocolV2StagedFile(filePath, item.source.size);
1644
+ stagedInstallTargets.push({
1645
+ targetId: item.targetId,
1646
+ path: filePath,
1647
+ });
1648
+ }
1133
1649
 
1134
- private async queryProtocolV2FirmwareUpdateStatus() {
1135
- const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
1136
- return typedCall(
1137
- 'DeviceFirmwareUpdateStatusGet',
1138
- 'DeviceFirmwareUpdateStatus',
1139
- {},
1140
- {
1141
- timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
1142
- }
1143
- );
1650
+ if (totalSize > 0) {
1651
+ this.postProgressMessage(100, 'transferData');
1652
+ }
1653
+ if (stagedInstallTargets.length === 0) {
1654
+ return;
1655
+ }
1656
+
1657
+ this.postTipMessage(FirmwareUpdateTipMessage.ConfirmOnDevice);
1658
+ const targets = stagedInstallTargets.map(item => ({
1659
+ target_id: item.targetId,
1660
+ path: item.path,
1661
+ }));
1662
+ await this.protocolV2StartFirmwareUpdate({ targets });
1663
+ await this.waitForProtocolV2FirmwareUpdateComplete(targets);
1144
1664
  }
1145
1665
 
1146
- private async pingProtocolV2Device() {
1147
- const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
1148
- await typedCall(
1149
- 'Ping',
1150
- 'Success',
1151
- { message: 'firmware-update' },
1152
- {
1153
- timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
1666
+ private async protocolV2SourceUpdateProcess({
1667
+ source,
1668
+ filePath,
1669
+ processedSize,
1670
+ totalSize,
1671
+ }: {
1672
+ source: FirmwareByteSource;
1673
+ filePath: string;
1674
+ processedSize: number;
1675
+ totalSize: number;
1676
+ }) {
1677
+ let lastError: unknown;
1678
+ for (let attempt = 1; attempt <= PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT; attempt += 1) {
1679
+ try {
1680
+ await writeFirmwareByteSource({
1681
+ source,
1682
+ chunkSize: this.getProtocolV2FirmwareChunkSize(),
1683
+ write: async ({ data, sourceOffset, length, first }) => {
1684
+ const chunkEnd = sourceOffset + length;
1685
+ const progress = getProtocolV2DeviceTransferProgress(
1686
+ processedSize + sourceOffset,
1687
+ processedSize + chunkEnd,
1688
+ totalSize
1689
+ );
1690
+ const response = await this.fileWriteChunk(
1691
+ filePath,
1692
+ source.size,
1693
+ sourceOffset,
1694
+ data,
1695
+ first,
1696
+ progress
1697
+ );
1698
+ const rawProcessedByte = response.message.processed_byte;
1699
+ const nextOffset = rawProcessedByte === undefined ? chunkEnd : Number(rawProcessedByte);
1700
+ if (!Number.isFinite(nextOffset) || nextOffset !== chunkEnd) {
1701
+ throw ERRORS.TypedError(
1702
+ HardwareErrorCode.EmmcFileWriteFirmwareError,
1703
+ `invalid processed_byte ${rawProcessedByte} for offset ${sourceOffset}`
1704
+ );
1705
+ }
1706
+ this.postProgressMessage(progress, 'transferData');
1707
+ return length;
1708
+ },
1709
+ });
1710
+ return processedSize + source.size;
1711
+ } catch (error) {
1712
+ lastError = error;
1713
+ if (attempt < PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT) {
1714
+ await this.recoverProtocolV2FileTransfer();
1715
+ }
1154
1716
  }
1717
+ }
1718
+ throw ERRORS.TypedError(
1719
+ HardwareErrorCode.EmmcFileWriteFirmwareError,
1720
+ `transfer data error: ${getProtocolV2UnknownErrorText(lastError)}`
1155
1721
  );
1156
1722
  }
1157
1723
 
1158
- private isProtocolV2NormalModeFeatures(features?: Features | null) {
1159
- return !!features && !features.bootloaderMode && features.mode !== 'bootloader';
1724
+ private getProtocolV2InstallItemStagingPath(item: Pick<ProtocolV2InstallItem, 'fileName'>) {
1725
+ return `${PROTOCOL_V2_FIRMWARE_STAGING_VOLUME}${item.fileName}`;
1160
1726
  }
1161
1727
 
1162
- private async probeProtocolV2NormalMode() {
1163
- const deviceInfo = await requestProtocolV2DeviceInfo({
1164
- commands: this.device.getCommands(),
1165
- timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
1166
- request: PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST,
1167
- });
1168
- const features = this.device.updateProtocolV2Features(deviceInfo);
1169
- if (this.isProtocolV2NormalModeFeatures(features)) {
1170
- Log.log('Protocol V2 firmware install finished; device is back in normal mode');
1171
- return true;
1728
+ private async verifyProtocolV2StagedFile(path: string, expectedSize: number) {
1729
+ const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
1730
+ const response = await typedCall('FilesystemPathInfoQuery', 'FilesystemPathInfo', { path });
1731
+ const actualSize = toProtocolV2FiniteNumber(response.message?.size);
1732
+ if (!response.message?.exist || response.message?.directory || actualSize !== expectedSize) {
1733
+ throw ERRORS.TypedError(
1734
+ HardwareErrorCode.EmmcFileWriteFirmwareError,
1735
+ `staged file verification failed: path=${path} exist=${!!response.message
1736
+ ?.exist} expected=${expectedSize} actual=${actualSize ?? 'unknown'}`
1737
+ );
1172
1738
  }
1173
- return false;
1174
1739
  }
1175
1740
 
1176
1741
  private assertProtocolV2TargetStatus(
1177
1742
  statusTargets: ProtocolV2FirmwareUpdateStatusTarget[],
1178
- expectedTargetIds: Set<number>
1743
+ expectedTargetIds: Set<number>,
1744
+ expectedPaths = new Map<number, string>()
1179
1745
  ) {
1746
+ Log.log(
1747
+ `[FirmwareUpdateV4] DeviceFirmwareUpdateStatus records=${JSON.stringify(statusTargets)}`
1748
+ );
1180
1749
  const failedTarget = statusTargets.find(
1181
1750
  target =>
1182
1751
  expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1) &&
1183
1752
  isProtocolV2TargetStatusFailed(target.status)
1184
1753
  );
1185
1754
  if (failedTarget) {
1755
+ const failedTargetDetails = JSON.stringify({
1756
+ targetId: failedTarget.target_id,
1757
+ status: failedTarget.status,
1758
+ payloadVersion: failedTarget.payload_version,
1759
+ path: failedTarget.path,
1760
+ });
1761
+ Log.error(`[FirmwareUpdateV4] firmware install failed target=${failedTargetDetails}`);
1186
1762
  throw ERRORS.TypedError(
1187
1763
  HardwareErrorCode.FirmwareError,
1188
- `Protocol V2 firmware target ${failedTarget.target_id} failed`
1764
+ `Protocol V2 firmware target failed: target=${failedTarget.target_id} status=${
1765
+ failedTarget.status ?? 'unknown'
1766
+ } payloadVersion=${failedTarget.payload_version ?? 'unknown'} path=${
1767
+ failedTarget.path ?? 'unknown'
1768
+ }`
1189
1769
  );
1190
1770
  }
1191
1771
 
1192
- const completedTargets = statusTargets.filter(
1193
- target =>
1194
- expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1) &&
1195
- isProtocolV2TargetStatusFinished(target.status)
1772
+ const matchingTargets = statusTargets.filter(target =>
1773
+ expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1)
1774
+ );
1775
+ const seenTargetIds = new Set<number>();
1776
+ for (const target of matchingTargets) {
1777
+ const targetId = normalizeProtocolV2TargetId(target.target_id);
1778
+ if (
1779
+ targetId === undefined ||
1780
+ seenTargetIds.has(targetId) ||
1781
+ (target.path && expectedPaths.get(targetId) && target.path !== expectedPaths.get(targetId))
1782
+ ) {
1783
+ throw ERRORS.TypedError(
1784
+ HardwareErrorCode.RuntimeError,
1785
+ `Protocol V2 install status conflicts with target ${target.target_id}`
1786
+ );
1787
+ }
1788
+ seenTargetIds.add(targetId);
1789
+ }
1790
+ const completedTargets = matchingTargets.filter(target =>
1791
+ isProtocolV2TargetStatusFinished(target.status)
1196
1792
  );
1197
- if (completedTargets.length === expectedTargetIds.size && expectedTargetIds.size > 0) {
1793
+ const completedTargetIds = new Set<number>();
1794
+ completedTargets.forEach(target => {
1795
+ const targetId = normalizeProtocolV2TargetId(target.target_id);
1796
+ if (targetId !== undefined) {
1797
+ completedTargetIds.add(targetId);
1798
+ }
1799
+ });
1800
+ if (completedTargetIds.size === expectedTargetIds.size && expectedTargetIds.size > 0) {
1801
+ for (const target of completedTargets) {
1802
+ const targetId = normalizeProtocolV2TargetId(target.target_id);
1803
+ const payloadVersion = toProtocolV2FiniteNumber(target.payload_version);
1804
+ if (targetId === undefined) {
1805
+ throw ERRORS.TypedError(
1806
+ HardwareErrorCode.RuntimeError,
1807
+ 'Protocol V2 completed target identity is invalid'
1808
+ );
1809
+ }
1810
+ const expectedVersion = this.getExpectedProtocolV2TargetVersion(targetId);
1811
+ if (expectedVersion !== undefined && payloadVersion !== undefined) {
1812
+ if (payloadVersion !== expectedVersion) {
1813
+ throw ERRORS.TypedError(
1814
+ HardwareErrorCode.FirmwareVerificationFailed,
1815
+ `Protocol V2 target ${targetId} reached payload version ${payloadVersion}, expected ${expectedVersion}`
1816
+ );
1817
+ }
1818
+ }
1819
+ if (payloadVersion !== undefined) {
1820
+ this.protocolV2CompletedTargetVersions.set(targetId, payloadVersion);
1821
+ }
1822
+ }
1823
+ this.postProgressMessage(100, 'installingFirmware');
1198
1824
  return true;
1199
1825
  }
1200
1826
 
1201
- const inProgressTarget = statusTargets.find(
1202
- target =>
1203
- expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1) &&
1827
+ if (expectedTargetIds.size > 0 && matchingTargets.length > 0) {
1828
+ const hasInProgressTarget = matchingTargets.some(target =>
1204
1829
  isProtocolV2TargetStatusInProgress(target.status)
1205
- );
1206
- if (inProgressTarget) {
1207
- this.postProgressMessage(99, 'installingFirmware');
1830
+ );
1831
+ const completedProgress = Math.floor(
1832
+ (completedTargetIds.size / expectedTargetIds.size) * 100
1833
+ );
1834
+ // The protocol exposes no per-target percentage, so report coarse progress by
1835
+ // completed targets and use 1% once work starts to keep the UI responsive.
1836
+ const progress = Math.min(99, Math.max(completedProgress, hasInProgressTarget ? 1 : 0));
1837
+ this.postProgressMessage(progress, 'installingFirmware');
1208
1838
  }
1209
1839
 
1210
1840
  return false;
1211
1841
  }
1212
1842
 
1213
1843
  private async waitForProtocolV2FirmwareUpdateComplete(
1214
- targets: Array<{ target_id: number; path: string }>,
1215
- startResponse?: ProtocolV2FirmwareUpdateStartResponse
1844
+ targets: Array<{ target_id: number; path: string }>
1216
1845
  ) {
1846
+ this.protocolV2FinalStatusVerified = false;
1217
1847
  const expectedTargetIds = new Set(targets.map(target => target.target_id));
1218
- if (startResponse?.type === 'DeviceFirmwareUpdateStatus') {
1219
- const statusTargets = (startResponse.message.records ??
1220
- []) as ProtocolV2FirmwareUpdateStatusTarget[];
1221
- if (this.assertProtocolV2TargetStatus(statusTargets, expectedTargetIds)) {
1222
- return;
1223
- }
1224
- }
1225
-
1848
+ const expectedPaths = new Map(targets.map(target => [target.target_id, target.path]));
1226
1849
  const startTime = Date.now();
1227
1850
  let lastError: unknown;
1228
1851
 
1229
1852
  while (Date.now() - startTime < PROTOCOL_V2_INSTALL_TIMEOUT) {
1230
1853
  try {
1231
- const statusRes = await this.queryProtocolV2FirmwareUpdateStatus();
1232
- const statusTargets = (statusRes.message.records ??
1233
- []) as ProtocolV2FirmwareUpdateStatusTarget[];
1234
- if (this.assertProtocolV2TargetStatus(statusTargets, expectedTargetIds)) {
1235
- return;
1854
+ await this.reconnectProtocolV2Device();
1855
+ const deviceInfo = await this.verifyProtocolV2ReconnectIdentity();
1856
+ try {
1857
+ const statusResponse = await this.device.getCommands().typedCall(
1858
+ 'DeviceFirmwareUpdateStatusGet',
1859
+ 'DeviceFirmwareUpdateStatus',
1860
+ {
1861
+ fields: {
1862
+ status: true,
1863
+ payload_version: true,
1864
+ path: true,
1865
+ },
1866
+ },
1867
+ { timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT }
1868
+ );
1869
+ if (
1870
+ this.assertProtocolV2TargetStatus(
1871
+ (statusResponse.message.records ?? []) as ProtocolV2FirmwareUpdateStatusTarget[],
1872
+ expectedTargetIds,
1873
+ expectedPaths
1874
+ )
1875
+ ) {
1876
+ this.protocolV2FinalStatusVerified = true;
1877
+ return;
1878
+ }
1879
+ lastError = new Error('Protocol V2 firmware targets are still installing');
1880
+ } catch (error) {
1881
+ if (
1882
+ error instanceof HardwareError &&
1883
+ error.errorCode === HardwareErrorCode.FirmwareError
1884
+ ) {
1885
+ throw error;
1886
+ }
1887
+ // App firmware does not register DeviceFirmwareUpdateStatusGet. Treat the
1888
+ // missing endpoint as completion only after the runtime probe confirms App mode.
1889
+ if (isProtocolV2FirmwareStatusEndpointUnavailable(error)) {
1890
+ if (await this.probeProtocolV2NormalMode(deviceInfo)) {
1891
+ Log.log(
1892
+ '[FirmwareUpdateV4] firmware status endpoint unavailable after confirmed App reboot'
1893
+ );
1894
+ return;
1895
+ }
1896
+ lastError = new Error(
1897
+ 'Protocol V2 firmware status endpoint is unavailable while the device remains in loader mode'
1898
+ );
1899
+ } else {
1900
+ lastError = error;
1901
+ Log.log(
1902
+ '[FirmwareUpdateV4] DeviceFirmwareUpdateStatusGet unavailable during install: ',
1903
+ error
1904
+ );
1905
+ }
1236
1906
  }
1237
1907
  } catch (error) {
1238
1908
  lastError = error;
1909
+ if (this.isProtocolV2ReconnectIdentityError(error)) {
1910
+ throw error;
1911
+ }
1239
1912
  if (error instanceof HardwareError && error.errorCode === HardwareErrorCode.FirmwareError) {
1240
1913
  throw error;
1241
1914
  }
1242
- Log.log('Protocol V2 firmware install status polling failed: ', error);
1243
- if (isProtocolV2PollingTransientError(error)) {
1244
- try {
1245
- await this.reconnectProtocolV2Device();
1246
- if (await this.probeProtocolV2NormalMode()) {
1247
- return;
1248
- }
1249
- } catch (reconnectError) {
1250
- lastError = reconnectError;
1251
- Log.log(
1252
- 'Protocol V2 firmware install reconnect/normal-mode probe failed: ',
1253
- reconnectError
1254
- );
1255
- }
1256
- try {
1257
- await this.pingProtocolV2Device();
1258
- Log.log('Protocol V2 firmware status unavailable, Ping is ready');
1259
- } catch (pingError) {
1260
- lastError = pingError;
1261
- Log.log('Protocol V2 firmware install Ping polling failed: ', pingError);
1262
- }
1915
+ if (
1916
+ error instanceof HardwareError &&
1917
+ error.params?.firmwareUpdateCode === 'FirmwareInstallStatusUnavailable'
1918
+ ) {
1919
+ throw error;
1263
1920
  }
1921
+ Log.log('Protocol V2 firmware install device readiness probe failed: ', error);
1264
1922
  }
1265
1923
  await wait(1000);
1266
1924
  }
@@ -1272,27 +1930,31 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1272
1930
  }
1273
1931
 
1274
1932
  private async exitProtocolV2BootloaderToNormal() {
1275
- this.postTipMessage(FirmwareUpdateTipMessage.SwitchFirmwareReconnectDevice);
1276
- try {
1277
- await this.reconnectProtocolV2Device();
1278
- if (await this.probeProtocolV2NormalMode()) {
1279
- Log.log('Protocol V2 device is already in normal mode, skip normal reboot');
1280
- return;
1281
- }
1282
- } catch (error) {
1283
- Log.log('Protocol V2 normal-mode probe before reboot failed: ', error);
1284
- }
1933
+ await this.reconnectProtocolV2Device();
1934
+ await this.verifyProtocolV2ReconnectIdentity();
1935
+ // The connection may still be in bootloader. Request a Normal reboot directly;
1936
+ // repeating it after an automatic App reboot is idempotent.
1285
1937
  await this.protocolV2Reboot(DeviceRebootType.Normal);
1938
+ this.protocolV2ExecutionInLoader = false;
1939
+ }
1940
+
1941
+ private async probeProtocolV2NormalMode(deviceInfo: ProtocolV2DeviceInfo) {
1942
+ const features = await this.device.probeProtocolV2RuntimeState(
1943
+ deviceInfo,
1944
+ PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT
1945
+ );
1946
+ return features.mode === 'normal' && !features.bootloaderMode;
1286
1947
  }
1287
1948
 
1288
1949
  private async waitForProtocolV2FinalFeatures() {
1289
1950
  const features = await this.waitForProtocolV2ReconnectAndFeatures(
1290
- PROTOCOL_V2_BOOTLOADER_RECONNECT_TIMEOUT
1951
+ PROTOCOL_V2_FINAL_RECONNECT_TIMEOUT
1291
1952
  );
1292
1953
 
1293
1954
  const bootloaderVersion = getDeviceBootloaderVersion(features).join('.');
1294
1955
  const bleVersion = getDeviceBLEFirmwareVersion(features).join('.');
1295
1956
  const firmwareVersion = getDeviceFirmwareVersion(features).join('.');
1957
+ this.protocolV2LatestFinalFeatures = features;
1296
1958
  if (firmwareVersion === '0.0.0') {
1297
1959
  Log.warn(
1298
1960
  'Protocol V2 firmware update finished but app firmware version is still 0.0.0. This is allowed for Pro2 debug BLE-only update flows.'
@@ -1306,6 +1968,17 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1306
1968
  };
1307
1969
  }
1308
1970
 
1971
+ private async completeProtocolV2FinalVerification() {
1972
+ if (this.protocolV2ExecutionInLoader || this.isProtocolV2BootloaderMode()) {
1973
+ await this.exitProtocolV2BootloaderToNormal();
1974
+ }
1975
+ const versions = await this.waitForProtocolV2FinalFeatures();
1976
+ this.assertExpectedProtocolV2Versions();
1977
+ this.postTipMessage(FirmwareUpdateTipMessage.FirmwareUpdateCompleted);
1978
+ DevicePool.resetState();
1979
+ return versions;
1980
+ }
1981
+
1309
1982
  private async waitForProtocolV2ReconnectAndFeatures(timeout: number) {
1310
1983
  const startTime = Date.now();
1311
1984
  let lastError: unknown;
@@ -1316,11 +1989,15 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1316
1989
  const deviceInfo = await requestProtocolV2DeviceInfo({
1317
1990
  commands: this.device.getCommands(),
1318
1991
  timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
1319
- // 更新完成判定只需要各 target 版本号;scope 与请求内容保持一致
1992
+ // Completion needs target versions only; keep scope aligned with the request.
1320
1993
  request: PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST,
1321
1994
  });
1322
- const features = this.device.updateProtocolV2Features(deviceInfo);
1323
- if (features.bootloaderMode || features.mode === 'bootloader') {
1995
+ this.assertProtocolV2DeviceInfoIdentity(deviceInfo);
1996
+ const features = await this.device.probeProtocolV2RuntimeState(
1997
+ deviceInfo,
1998
+ PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT
1999
+ );
2000
+ if (features.mode !== 'normal' || features.bootloaderMode) {
1324
2001
  throw ERRORS.TypedError(
1325
2002
  HardwareErrorCode.DeviceNotFound,
1326
2003
  'Protocol V2 device is still in bootloader mode'
@@ -1328,6 +2005,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1328
2005
  }
1329
2006
  return features;
1330
2007
  } catch (error) {
2008
+ if (this.isProtocolV2ReconnectIdentityError(error)) {
2009
+ throw error;
2010
+ }
1331
2011
  lastError = error;
1332
2012
  Log.log('Protocol V2 normal mode not ready, polling Ping: ', error);
1333
2013
  await wait(1000);
@@ -1362,15 +2042,14 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1362
2042
  },
1363
2043
  true
1364
2044
  );
1365
- await this.device.acquire(PROTOCOL_V2_CONNECT_PROTOCOL, { throwOnRunPromiseError: true });
2045
+ await this.ensureProtocolV2DeviceAcquired();
1366
2046
  this.device.commands.disposed = false;
1367
2047
  this.device.getCommands().mainId = this.device.mainId ?? '';
1368
- await this.device.initialize();
1369
2048
  return;
1370
2049
  }
1371
2050
 
1372
- // App bootloader 序列号暂时可能不一致。V4 升级重连阶段只接受唯一枚举设备,
1373
- // 避免继续按旧 app connectId 查缓存导致反复输出 path mismatch 日志。
2051
+ // App and bootloader serials may differ temporarily. During V4 reconnect, accept
2052
+ // only a uniquely enumerated device instead of repeatedly using the old App path.
1374
2053
  const { deviceList } = await DevicePool.getDevices(devicesDescriptor, undefined, {
1375
2054
  connectProtocol: PROTOCOL_V2_CONNECT_PROTOCOL,
1376
2055
  });
@@ -1383,86 +2062,30 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1383
2062
  deviceList[0].getConnectId()
1384
2063
  );
1385
2064
  this.device.updateFromCache(deviceList[0]);
1386
- await this.device.acquire(PROTOCOL_V2_CONNECT_PROTOCOL, { throwOnRunPromiseError: true });
2065
+ await this.ensureProtocolV2DeviceAcquired();
1387
2066
  this.device.commands.disposed = false;
1388
2067
  this.device.getCommands().mainId = this.device.mainId ?? '';
1389
- await this.device.initialize();
1390
2068
  }
1391
2069
 
1392
- private async protocolV2CommonUpdateProcess({
1393
- payload,
1394
- filePath,
1395
- processedSize,
1396
- totalSize,
1397
- onTransferredBytes,
1398
- }: PROTO.FirmwareUpload & {
1399
- filePath: string;
1400
- processedSize?: number;
1401
- totalSize?: number;
1402
- onTransferredBytes?: (transferredBytes: number) => void;
1403
- }) {
1404
- const chunkSize = this.getProtocolV2FirmwareChunkSize();
1405
- let offset = 0;
1406
- const getUploadProgress = (fileOffset: number) => {
1407
- if (totalSize !== undefined && processedSize !== undefined) {
1408
- return Math.min(Math.ceil(((processedSize + fileOffset) / totalSize) * 100), 99);
1409
- }
1410
- return Math.min(Math.ceil((fileOffset / payload.byteLength) * 100), 99);
1411
- };
1412
-
1413
- while (offset < payload.byteLength) {
1414
- const chunkEnd = Math.min(offset + chunkSize, payload.byteLength);
1415
- const chunkLength = chunkEnd - offset;
1416
- const chunk = payload.slice(offset, chunkEnd);
1417
- const overwrite = offset === 0;
1418
- const progress = getProtocolV2DeviceTransferProgress(
1419
- (processedSize ?? 0) + offset,
1420
- (processedSize ?? 0) + chunkEnd,
1421
- totalSize ?? payload.byteLength
1422
- );
1423
-
1424
- const writeRes = await this.fileWriteWithRetry(
1425
- filePath,
1426
- payload.byteLength,
1427
- offset,
1428
- chunk,
1429
- overwrite,
1430
- progress
1431
- );
1432
- const processedByte = Number(writeRes.message.processed_byte);
1433
- const nextOffset =
1434
- Number.isFinite(processedByte) && processedByte > offset
1435
- ? processedByte
1436
- : offset + chunkLength;
1437
- if (nextOffset <= offset || nextOffset > payload.byteLength) {
1438
- throw ERRORS.TypedError(
1439
- HardwareErrorCode.EmmcFileWriteFirmwareError,
1440
- `invalid processed_byte ${writeRes.message.processed_byte} for offset ${offset}`
1441
- );
1442
- }
1443
- offset = nextOffset;
1444
- onTransferredBytes?.((processedSize ?? 0) + offset);
1445
- this.postProgressMessage(getUploadProgress(offset), 'transferData');
2070
+ /**
2071
+ * After acquiring a USB session, polling reuses its command channel. Reacquire only
2072
+ * after reboot or enumeration changes cause hasDeviceAcquire() to become false.
2073
+ */
2074
+ private async ensureProtocolV2DeviceAcquired() {
2075
+ const commands = this.device.getCommands();
2076
+ if (this.device.hasDeviceAcquire() && !commands.disposed) {
2077
+ return;
1446
2078
  }
1447
-
1448
- return totalSize !== undefined ? (processedSize ?? 0) + payload.byteLength : 0;
2079
+ await this.device.acquire(PROTOCOL_V2_CONNECT_PROTOCOL, {
2080
+ throwOnRunPromiseError: true,
2081
+ });
1449
2082
  }
1450
2083
 
1451
- private getProtocolV2FirmwareTransferTransport() {
1452
- const env = DataManager.getSettings('env');
1453
- if (env && DataManager.isBleConnect(env)) {
1454
- return 'BLE';
1455
- }
1456
- if (
1457
- env &&
1458
- (DataManager.isBrowserWebUsb(env) || DataManager.isDesktopWebUsb(env) || env === 'web')
1459
- ) {
1460
- return 'WebUSB';
1461
- }
1462
- return env ?? 'unknown';
2084
+ private isProtocolV2ReconnectIdentityError(error: unknown) {
2085
+ return this.normalizeErrorMessage(error).includes('Protocol V2 reconnect physical identity');
1463
2086
  }
1464
2087
 
1465
- private async fileWriteWithRetry(
2088
+ private async fileWriteChunk(
1466
2089
  filePath: string,
1467
2090
  totalFileSize: number,
1468
2091
  offset: number,
@@ -1470,57 +2093,38 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1470
2093
  overwrite: boolean,
1471
2094
  progress: number | null
1472
2095
  ): Promise<TypedResponseMessage<'FilesystemFile'>> {
1473
- const writeFunc = async () => {
1474
- const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
1475
- const writeRes = await typedCall('FilesystemFileWrite', 'FilesystemFile', {
1476
- file: {
1477
- path: filePath,
1478
- offset,
1479
- total_size: totalFileSize,
1480
- data: chunk,
1481
- },
1482
- overwrite,
1483
- append: false,
1484
- ui_percentage: progress ?? undefined,
1485
- });
1486
- if (writeRes.type !== 'FilesystemFile') {
1487
- if ((writeRes as any).type === 'CallMethodError') {
1488
- if (((writeRes as any).message.error ?? '').indexOf(SESSION_ERROR) > -1) {
1489
- throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, SESSION_ERROR);
1490
- }
2096
+ const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
2097
+ const writeRes = await typedCall('FilesystemFileWrite', 'FilesystemFile', {
2098
+ file: {
2099
+ path: filePath,
2100
+ offset,
2101
+ total_size: totalFileSize,
2102
+ data: chunk,
2103
+ },
2104
+ overwrite,
2105
+ append: false,
2106
+ ui_percentage: progress ?? undefined,
2107
+ });
2108
+ if (writeRes.type !== 'FilesystemFile') {
2109
+ if ((writeRes as any).type === 'CallMethodError') {
2110
+ if (((writeRes as any).message.error ?? '').indexOf(SESSION_ERROR) > -1) {
2111
+ throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, SESSION_ERROR);
1491
2112
  }
1492
- throw ERRORS.TypedError(
1493
- HardwareErrorCode.EmmcFileWriteFirmwareError,
1494
- 'transfer data error'
1495
- );
1496
2113
  }
1497
- return writeRes;
1498
- };
2114
+ throw ERRORS.TypedError(HardwareErrorCode.EmmcFileWriteFirmwareError, 'transfer data error');
2115
+ }
2116
+ return writeRes;
2117
+ }
1499
2118
 
1500
- let retryCount = 10;
1501
- while (retryCount > 0) {
1502
- try {
1503
- const result = await writeFunc();
1504
- return result;
1505
- } catch (error) {
1506
- Log.error(`fileWrite error: `, error);
1507
- retryCount--;
1508
- if (retryCount === 0) {
1509
- throw ERRORS.TypedError(
1510
- HardwareErrorCode.EmmcFileWriteFirmwareError,
1511
- 'transfer data error'
1512
- );
1513
- }
1514
- const env = DataManager.getSettings('env');
1515
- if (DataManager.isBleConnect(env)) {
1516
- await wait(3000);
1517
- await this.acquireProtocolV2BleDevice();
1518
- await this.device.initialize();
1519
- }
1520
- await wait(2000);
1521
- }
2119
+ private async recoverProtocolV2FileTransfer() {
2120
+ const env = DataManager.getSettings('env');
2121
+ if (DataManager.isBleConnect(env)) {
2122
+ await wait(3000);
1522
2123
  }
1523
- throw ERRORS.TypedError(HardwareErrorCode.EmmcFileWriteFirmwareError, 'transfer data error');
2124
+ await this.reconnectProtocolV2Device();
2125
+ await this.verifyProtocolV2ReconnectIdentity();
2126
+ await this.device.initialize();
2127
+ await wait(2000);
1524
2128
  }
1525
2129
 
1526
2130
  private async acquireProtocolV2BleDevice() {
@@ -1538,35 +2142,16 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1538
2142
  targets: Array<{ target_id: number; path: string }>;
1539
2143
  }) {
1540
2144
  const commands = this.device.getCommands();
1541
- let response: ProtocolV2FirmwareUpdateStartResponse;
1542
- try {
1543
- response = await commands.typedCall(
1544
- 'DeviceFirmwareUpdateRequest',
1545
- PROTOCOL_V2_FIRMWARE_UPDATE_RESPONSE_TYPES,
1546
- {
1547
- targets,
1548
- },
1549
- {
1550
- intermediateTypes: ['DeviceFirmwareUpdateStatus'],
1551
- timeoutMs: PROTOCOL_V2_START_UPDATE_TIMEOUT,
1552
- onIntermediateResponse: (response: { type?: string }) => {
1553
- if (response.type === 'DeviceFirmwareUpdateStatus') {
1554
- this.postProgressMessage(99, 'installingFirmware');
1555
- }
1556
- },
1557
- }
1558
- );
1559
- } catch (error) {
1560
- if (isProtocolV2StartUpdateTransientError(error)) {
1561
- Log.log(
1562
- 'Protocol V2 firmware update request did not return; continue status polling',
1563
- error
1564
- );
1565
- } else {
1566
- throw error;
1567
- }
1568
- }
2145
+ const response: ProtocolV2FirmwareUpdateStartResponse = await commands.typedCall(
2146
+ 'DeviceFirmwareUpdateRequest',
2147
+ 'Success',
2148
+ { targets },
2149
+ { timeoutMs: PROTOCOL_V2_START_UPDATE_TIMEOUT }
2150
+ );
2151
+ // Success acknowledges that the device accepted installation. End confirmation
2152
+ // and begin status polling only after receiving this ACK.
1569
2153
  this.postTipMessage(FirmwareUpdateTipMessage.FirmwareUpdating);
2154
+ this.postProgressMessage(0, 'installingFirmware');
1570
2155
  return response;
1571
2156
  }
1572
2157
 
@@ -1576,9 +2161,11 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1576
2161
  const res = await typedCall('DeviceReboot', 'Success', {
1577
2162
  reboot_type: rebootType,
1578
2163
  });
2164
+ this.device.markProtocolV2Reboot(rebootType);
1579
2165
  return res.message;
1580
2166
  } catch (error) {
1581
2167
  if (isProtocolV2DeviceDisconnectedError(error) || isProtocolV2ReconnectProbeError(error)) {
2168
+ this.device.markProtocolV2Reboot(rebootType);
1582
2169
  return { message: 'Device rebooted successfully' };
1583
2170
  }
1584
2171
  throw error;