@onekeyfe/hd-core 1.2.0-alpha.4 → 1.2.0-alpha.41

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 (742) 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-lifecycle-events.test.ts +249 -0
  12. package/__tests__/device-pool-state.test.ts +144 -0
  13. package/__tests__/device-settings.test.ts +416 -0
  14. package/__tests__/device-state-contract.test.ts +27 -0
  15. package/__tests__/device-state-events.test.ts +286 -0
  16. package/__tests__/device-state-mapper.test.ts +261 -0
  17. package/__tests__/device-state-projector.test.ts +94 -0
  18. package/__tests__/device-state-store.test.ts +82 -0
  19. package/__tests__/device-utils.test.ts +107 -0
  20. package/__tests__/device-wallet-session-store.test.ts +478 -0
  21. package/__tests__/deviceFeaturesUtils.test.ts +39 -0
  22. package/__tests__/deviceSettings.test.ts +103 -0
  23. package/__tests__/evmLedgerLegacySafety.test.ts +12 -6
  24. package/__tests__/evmSignTransaction.test.ts +69 -0
  25. package/__tests__/evmSignTypedData.test.ts +80 -0
  26. package/__tests__/filesystemValidation.test.ts +53 -0
  27. package/__tests__/firmware-update/check-all-firmware-release.test.ts +175 -0
  28. package/__tests__/firmware-update/firmware-artifact-source.test.ts +119 -0
  29. package/__tests__/firmware-update/firmware-host-binding.test.ts +70 -0
  30. package/__tests__/firmware-update/firmware-preparation-error.test.ts +27 -0
  31. package/__tests__/firmware-update/firmware-update-capabilities.test.ts +13 -0
  32. package/__tests__/firmware-update/firmware-update-plan.test.ts +593 -0
  33. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +213 -0
  34. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +430 -0
  35. package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +58 -0
  36. package/__tests__/firmware-update/firmware-upload-source.test.ts +70 -0
  37. package/__tests__/get-device-state.test.ts +458 -0
  38. package/__tests__/homescreen.test.ts +24 -0
  39. package/__tests__/kaspa-public-types.type-test.ts +16 -0
  40. package/__tests__/kaspa-use-tweak-pro2.test.ts +20 -0
  41. package/__tests__/kaspaSignTransaction.test.ts +601 -0
  42. package/__tests__/known-device-public-types.type-test.ts +3 -0
  43. package/__tests__/logBlockEvent.test.ts +128 -1
  44. package/__tests__/method-protocol-support.test.ts +113 -0
  45. package/__tests__/networkUtils.test.ts +406 -0
  46. package/__tests__/open-wallet-session-error-response.test.ts +43 -0
  47. package/__tests__/open-wallet-session.test.ts +1368 -0
  48. package/__tests__/pro2Wallpaper.test.ts +39 -0
  49. package/__tests__/protocol-v2-bootloader-mode.test.ts +37 -0
  50. package/__tests__/protocol-v2-firmware-targets.test.ts +37 -0
  51. package/__tests__/protocol-v2-ping.test.ts +28 -0
  52. package/__tests__/protocol-v2-unlock-policy.test.ts +45 -0
  53. package/__tests__/protocol-v2.test.ts +6082 -2422
  54. package/__tests__/protocolV2FileWrite.test.ts +92 -0
  55. package/__tests__/protocolV2UiInteraction.test.ts +266 -0
  56. package/__tests__/public-device-state-api.test.ts +275 -0
  57. package/__tests__/public-pro2-api-boundary.test.ts +106 -0
  58. package/__tests__/refresh-device-state.test.ts +101 -0
  59. package/__tests__/request-context-logging.test.ts +16 -0
  60. package/__tests__/search-devices.test.ts +136 -0
  61. package/__tests__/tron-sign-message-init.test.ts +74 -0
  62. package/dist/api/BaseMethod.d.ts +14 -1
  63. package/dist/api/BaseMethod.d.ts.map +1 -1
  64. package/dist/api/CheckAllFirmwareRelease.d.ts +24 -1
  65. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  66. package/dist/api/CheckBootloaderRelease.d.ts.map +1 -1
  67. package/dist/api/CheckFirmwareRelease.d.ts.map +1 -1
  68. package/dist/api/CipherKeyValue.d.ts +1 -0
  69. package/dist/api/CipherKeyValue.d.ts.map +1 -1
  70. package/dist/api/ClearSessionCache.d.ts +9 -0
  71. package/dist/api/ClearSessionCache.d.ts.map +1 -0
  72. package/dist/api/DirList.d.ts +1 -0
  73. package/dist/api/DirList.d.ts.map +1 -1
  74. package/dist/api/DirMake.d.ts +1 -0
  75. package/dist/api/DirMake.d.ts.map +1 -1
  76. package/dist/api/DirRemove.d.ts +1 -0
  77. package/dist/api/DirRemove.d.ts.map +1 -1
  78. package/dist/api/FileDelete.d.ts +1 -0
  79. package/dist/api/FileDelete.d.ts.map +1 -1
  80. package/dist/api/FileRead.d.ts +1 -0
  81. package/dist/api/FileRead.d.ts.map +1 -1
  82. package/dist/api/FileWrite.d.ts +2 -0
  83. package/dist/api/FileWrite.d.ts.map +1 -1
  84. package/dist/api/FirmwareUpdate.d.ts.map +1 -1
  85. package/dist/api/FirmwareUpdateV2.d.ts +13 -2
  86. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  87. package/dist/api/FirmwareUpdateV3.d.ts +7 -3
  88. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  89. package/dist/api/FirmwareUpdateV4.d.ts +42 -16
  90. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  91. package/dist/api/GetDeviceState.d.ts +8 -0
  92. package/dist/api/GetDeviceState.d.ts.map +1 -0
  93. package/dist/api/GetFeatures.d.ts +1 -1
  94. package/dist/api/GetFeatures.d.ts.map +1 -1
  95. package/dist/api/GetOnekeyFeatures.d.ts.map +1 -1
  96. package/dist/api/GetPassphraseState.d.ts +2 -6
  97. package/dist/api/GetPassphraseState.d.ts.map +1 -1
  98. package/dist/api/OpenWalletSession.d.ts +8 -0
  99. package/dist/api/OpenWalletSession.d.ts.map +1 -0
  100. package/dist/api/PathInfo.d.ts +1 -0
  101. package/dist/api/PathInfo.d.ts.map +1 -1
  102. package/dist/api/PromptWebDeviceAccess.d.ts.map +1 -1
  103. package/dist/api/SearchDevices.d.ts +3 -0
  104. package/dist/api/SearchDevices.d.ts.map +1 -1
  105. package/dist/api/UploadPortfolio.d.ts +17 -0
  106. package/dist/api/UploadPortfolio.d.ts.map +1 -0
  107. package/dist/api/alephium/AlephiumGetAddress.d.ts.map +1 -1
  108. package/dist/api/alephium/AlephiumSignMessage.d.ts.map +1 -1
  109. package/dist/api/alephium/AlephiumSignTransaction.d.ts.map +1 -1
  110. package/dist/api/algo/AlgoGetAddress.d.ts +1 -0
  111. package/dist/api/algo/AlgoGetAddress.d.ts.map +1 -1
  112. package/dist/api/algo/AlgoSignTransaction.d.ts +1 -0
  113. package/dist/api/algo/AlgoSignTransaction.d.ts.map +1 -1
  114. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts +1 -0
  115. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
  116. package/dist/api/aptos/AptosGetAddress.d.ts +1 -0
  117. package/dist/api/aptos/AptosGetAddress.d.ts.map +1 -1
  118. package/dist/api/aptos/AptosGetPublicKey.d.ts +1 -0
  119. package/dist/api/aptos/AptosGetPublicKey.d.ts.map +1 -1
  120. package/dist/api/aptos/AptosSignInMessage.d.ts +1 -0
  121. package/dist/api/aptos/AptosSignInMessage.d.ts.map +1 -1
  122. package/dist/api/aptos/AptosSignMessage.d.ts +1 -0
  123. package/dist/api/aptos/AptosSignMessage.d.ts.map +1 -1
  124. package/dist/api/aptos/AptosSignTransaction.d.ts +1 -0
  125. package/dist/api/aptos/AptosSignTransaction.d.ts.map +1 -1
  126. package/dist/api/benfen/BenfenGetAddress.d.ts.map +1 -1
  127. package/dist/api/benfen/BenfenGetPublicKey.d.ts.map +1 -1
  128. package/dist/api/benfen/BenfenSignMessage.d.ts.map +1 -1
  129. package/dist/api/benfen/BenfenSignTransaction.d.ts.map +1 -1
  130. package/dist/api/btc/BTCGetAddress.d.ts +1 -0
  131. package/dist/api/btc/BTCGetAddress.d.ts.map +1 -1
  132. package/dist/api/btc/BTCGetPublicKey.d.ts +1 -0
  133. package/dist/api/btc/BTCGetPublicKey.d.ts.map +1 -1
  134. package/dist/api/btc/BTCSignMessage.d.ts +1 -0
  135. package/dist/api/btc/BTCSignMessage.d.ts.map +1 -1
  136. package/dist/api/btc/BTCSignPsbt.d.ts +1 -0
  137. package/dist/api/btc/BTCSignPsbt.d.ts.map +1 -1
  138. package/dist/api/btc/BTCSignTransaction.d.ts +1 -0
  139. package/dist/api/btc/BTCSignTransaction.d.ts.map +1 -1
  140. package/dist/api/btc/BTCVerifyMessage.d.ts +1 -0
  141. package/dist/api/btc/BTCVerifyMessage.d.ts.map +1 -1
  142. package/dist/api/btc/helpers/versionLimit.d.ts +2 -0
  143. package/dist/api/btc/helpers/versionLimit.d.ts.map +1 -1
  144. package/dist/api/cardano/CardanoGetAddress.d.ts +1 -0
  145. package/dist/api/cardano/CardanoGetAddress.d.ts.map +1 -1
  146. package/dist/api/cardano/CardanoGetPublicKey.d.ts +1 -0
  147. package/dist/api/cardano/CardanoGetPublicKey.d.ts.map +1 -1
  148. package/dist/api/cardano/CardanoSignMessage.d.ts +1 -0
  149. package/dist/api/cardano/CardanoSignMessage.d.ts.map +1 -1
  150. package/dist/api/cardano/CardanoSignTransaction.d.ts +1 -0
  151. package/dist/api/cardano/CardanoSignTransaction.d.ts.map +1 -1
  152. package/dist/api/conflux/ConfluxGetAddress.d.ts +2 -0
  153. package/dist/api/conflux/ConfluxGetAddress.d.ts.map +1 -1
  154. package/dist/api/conflux/ConfluxSignMessage.d.ts +2 -0
  155. package/dist/api/conflux/ConfluxSignMessage.d.ts.map +1 -1
  156. package/dist/api/conflux/ConfluxSignMessageCIP23.d.ts +2 -0
  157. package/dist/api/conflux/ConfluxSignMessageCIP23.d.ts.map +1 -1
  158. package/dist/api/conflux/ConfluxSignTransaction.d.ts +2 -0
  159. package/dist/api/conflux/ConfluxSignTransaction.d.ts.map +1 -1
  160. package/dist/api/cosmos/CosmosGetAddress.d.ts +1 -0
  161. package/dist/api/cosmos/CosmosGetAddress.d.ts.map +1 -1
  162. package/dist/api/cosmos/CosmosGetPublicKey.d.ts +1 -0
  163. package/dist/api/cosmos/CosmosGetPublicKey.d.ts.map +1 -1
  164. package/dist/api/cosmos/CosmosSignTransaction.d.ts +1 -0
  165. package/dist/api/cosmos/CosmosSignTransaction.d.ts.map +1 -1
  166. package/dist/api/device/DeviceCancel.d.ts +1 -0
  167. package/dist/api/device/DeviceCancel.d.ts.map +1 -1
  168. package/dist/api/device/DeviceChangePin.d.ts +1 -0
  169. package/dist/api/device/DeviceChangePin.d.ts.map +1 -1
  170. package/dist/api/device/DeviceFullyUploadResource.d.ts +1 -2
  171. package/dist/api/device/DeviceFullyUploadResource.d.ts.map +1 -1
  172. package/dist/api/device/DeviceLock.d.ts +1 -0
  173. package/dist/api/device/DeviceLock.d.ts.map +1 -1
  174. package/dist/api/device/DeviceRebootToBoardloader.d.ts +1 -0
  175. package/dist/api/device/DeviceRebootToBoardloader.d.ts.map +1 -1
  176. package/dist/api/device/DeviceRebootToBootloader.d.ts +1 -0
  177. package/dist/api/device/DeviceRebootToBootloader.d.ts.map +1 -1
  178. package/dist/api/device/DeviceSettings.d.ts +1 -0
  179. package/dist/api/device/DeviceSettings.d.ts.map +1 -1
  180. package/dist/api/device/DeviceSupportFeatures.d.ts +1 -0
  181. package/dist/api/device/DeviceSupportFeatures.d.ts.map +1 -1
  182. package/dist/api/device/DeviceUnlock.d.ts +1 -0
  183. package/dist/api/device/DeviceUnlock.d.ts.map +1 -1
  184. package/dist/api/device/DeviceUpdateBootloader.d.ts +5 -1
  185. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  186. package/dist/api/device/DeviceWipe.d.ts +1 -0
  187. package/dist/api/device/DeviceWipe.d.ts.map +1 -1
  188. package/dist/api/device/PreInitialize.d.ts +1 -0
  189. package/dist/api/device/PreInitialize.d.ts.map +1 -1
  190. package/dist/api/dynex/DnxGetAddress.d.ts.map +1 -1
  191. package/dist/api/dynex/DnxSignTransaction.d.ts.map +1 -1
  192. package/dist/api/evm/EVMGetAddress.d.ts +1 -0
  193. package/dist/api/evm/EVMGetAddress.d.ts.map +1 -1
  194. package/dist/api/evm/EVMGetPublicKey.d.ts +1 -0
  195. package/dist/api/evm/EVMGetPublicKey.d.ts.map +1 -1
  196. package/dist/api/evm/EVMSignMessage.d.ts +1 -0
  197. package/dist/api/evm/EVMSignMessage.d.ts.map +1 -1
  198. package/dist/api/evm/EVMSignMessageEIP712.d.ts.map +1 -1
  199. package/dist/api/evm/EVMSignTransaction.d.ts +1 -0
  200. package/dist/api/evm/EVMSignTransaction.d.ts.map +1 -1
  201. package/dist/api/evm/EVMSignTypedData.d.ts +1 -0
  202. package/dist/api/evm/EVMSignTypedData.d.ts.map +1 -1
  203. package/dist/api/evm/EVMVerifyMessage.d.ts +1 -0
  204. package/dist/api/evm/EVMVerifyMessage.d.ts.map +1 -1
  205. package/dist/api/evm/protocol.d.ts +3 -0
  206. package/dist/api/evm/protocol.d.ts.map +1 -0
  207. package/dist/api/filecoin/FilecoinGetAddress.d.ts +1 -0
  208. package/dist/api/filecoin/FilecoinGetAddress.d.ts.map +1 -1
  209. package/dist/api/filecoin/FilecoinSignTransaction.d.ts +1 -0
  210. package/dist/api/filecoin/FilecoinSignTransaction.d.ts.map +1 -1
  211. package/dist/api/firmware/FirmwareArtifactSource.d.ts +25 -0
  212. package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +1 -0
  213. package/dist/api/firmware/FirmwareHostBinding.d.ts +6 -0
  214. package/dist/api/firmware/FirmwareHostBinding.d.ts.map +1 -0
  215. package/dist/api/firmware/FirmwarePreparationError.d.ts +3 -0
  216. package/dist/api/firmware/FirmwarePreparationError.d.ts.map +1 -0
  217. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +8 -0
  218. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  219. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +3 -0
  220. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +1 -0
  221. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +25 -0
  222. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -0
  223. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +30 -0
  224. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -0
  225. package/dist/api/firmware/getBinary.d.ts +15 -4
  226. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  227. package/dist/api/firmware/progressThrottle.d.ts +3 -0
  228. package/dist/api/firmware/progressThrottle.d.ts.map +1 -0
  229. package/dist/api/firmware/updateBootloader.d.ts +2 -0
  230. package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
  231. package/dist/api/firmware/uploadFirmware.d.ts +9 -1
  232. package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
  233. package/dist/api/helpers/filesystemValidation.d.ts +3 -0
  234. package/dist/api/helpers/filesystemValidation.d.ts.map +1 -1
  235. package/dist/api/helpers/paramsValidator.d.ts +1 -0
  236. package/dist/api/helpers/paramsValidator.d.ts.map +1 -1
  237. package/dist/api/helpers/protocolV2FileWrite.d.ts +33 -0
  238. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -0
  239. package/dist/api/index.d.ts +7 -25
  240. package/dist/api/index.d.ts.map +1 -1
  241. package/dist/api/kaspa/KaspaGetAddress.d.ts +4 -0
  242. package/dist/api/kaspa/KaspaGetAddress.d.ts.map +1 -1
  243. package/dist/api/kaspa/KaspaSignTransaction.d.ts +9 -1
  244. package/dist/api/kaspa/KaspaSignTransaction.d.ts.map +1 -1
  245. package/dist/api/kaspa/helpers/TransferSerialize.d.ts.map +1 -1
  246. package/dist/api/lightning/LnurlAuth.d.ts +0 -4
  247. package/dist/api/lightning/LnurlAuth.d.ts.map +1 -1
  248. package/dist/api/near/NearGetAddress.d.ts +1 -0
  249. package/dist/api/near/NearGetAddress.d.ts.map +1 -1
  250. package/dist/api/near/NearSignTransaction.d.ts +1 -0
  251. package/dist/api/near/NearSignTransaction.d.ts.map +1 -1
  252. package/dist/api/nem/NEMGetAddress.d.ts +1 -0
  253. package/dist/api/nem/NEMGetAddress.d.ts.map +1 -1
  254. package/dist/api/nem/NEMSignTransaction.d.ts +1 -0
  255. package/dist/api/nem/NEMSignTransaction.d.ts.map +1 -1
  256. package/dist/api/neo/NeoGetAddress.d.ts.map +1 -1
  257. package/dist/api/neo/NeoSignTransaction.d.ts.map +1 -1
  258. package/dist/api/nervos/NervosGetAddress.d.ts.map +1 -1
  259. package/dist/api/nervos/NervosSignTransaction.d.ts.map +1 -1
  260. package/dist/api/nexa/NexaGetAddress.d.ts.map +1 -1
  261. package/dist/api/nexa/NexaSignTransaction.d.ts +1 -0
  262. package/dist/api/nexa/NexaSignTransaction.d.ts.map +1 -1
  263. package/dist/api/nostr/NostrDecryptMessage.d.ts +1 -0
  264. package/dist/api/nostr/NostrDecryptMessage.d.ts.map +1 -1
  265. package/dist/api/nostr/NostrEncryptMessage.d.ts +1 -0
  266. package/dist/api/nostr/NostrEncryptMessage.d.ts.map +1 -1
  267. package/dist/api/nostr/NostrGetPublicKey.d.ts +1 -0
  268. package/dist/api/nostr/NostrGetPublicKey.d.ts.map +1 -1
  269. package/dist/api/nostr/NostrSignEvent.d.ts +1 -0
  270. package/dist/api/nostr/NostrSignEvent.d.ts.map +1 -1
  271. package/dist/api/nostr/NostrSignSchnorr.d.ts +1 -0
  272. package/dist/api/nostr/NostrSignSchnorr.d.ts.map +1 -1
  273. package/dist/api/polkadot/PolkadotGetAddress.d.ts +1 -0
  274. package/dist/api/polkadot/PolkadotGetAddress.d.ts.map +1 -1
  275. package/dist/api/polkadot/PolkadotSignTransaction.d.ts +1 -0
  276. package/dist/api/polkadot/PolkadotSignTransaction.d.ts.map +1 -1
  277. package/dist/api/protocol-v2/DeviceFactoryInfoGet.d.ts +1 -0
  278. package/dist/api/protocol-v2/DeviceFactoryInfoGet.d.ts.map +1 -1
  279. package/dist/api/protocol-v2/DeviceFactoryInfoSet.d.ts +1 -0
  280. package/dist/api/protocol-v2/DeviceFactoryInfoSet.d.ts.map +1 -1
  281. package/dist/api/protocol-v2/DeviceFirmwareUpdate.d.ts +1 -0
  282. package/dist/api/protocol-v2/DeviceFirmwareUpdate.d.ts.map +1 -1
  283. package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts +4 -2
  284. package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts.map +1 -1
  285. package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts +7 -0
  286. package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts.map +1 -0
  287. package/dist/api/protocol-v2/DeviceInfoGet.d.ts +1 -1
  288. package/dist/api/protocol-v2/DeviceInfoGet.d.ts.map +1 -1
  289. package/dist/api/protocol-v2/DeviceReboot.d.ts +1 -0
  290. package/dist/api/protocol-v2/DeviceReboot.d.ts.map +1 -1
  291. package/dist/api/protocol-v2/DeviceStatusGet.d.ts +7 -0
  292. package/dist/api/protocol-v2/DeviceStatusGet.d.ts.map +1 -0
  293. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts +27 -0
  294. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -0
  295. package/dist/api/protocol-v2/FilesystemFormat.d.ts +1 -0
  296. package/dist/api/protocol-v2/FilesystemFormat.d.ts.map +1 -1
  297. package/dist/api/protocol-v2/FilesystemPermissionFix.d.ts +1 -0
  298. package/dist/api/protocol-v2/FilesystemPermissionFix.d.ts.map +1 -1
  299. package/dist/api/protocol-v2/Ping.d.ts +3 -0
  300. package/dist/api/protocol-v2/Ping.d.ts.map +1 -1
  301. package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts +1 -0
  302. package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -1
  303. package/dist/api/protocol-v2/helpers.d.ts +4 -1
  304. package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
  305. package/dist/api/scdo/ScdoGetAddress.d.ts.map +1 -1
  306. package/dist/api/scdo/ScdoSignMessage.d.ts.map +1 -1
  307. package/dist/api/scdo/ScdoSignTransaction.d.ts.map +1 -1
  308. package/dist/api/solana/SolGetAddress.d.ts +1 -0
  309. package/dist/api/solana/SolGetAddress.d.ts.map +1 -1
  310. package/dist/api/solana/SolSignMessage.d.ts +1 -0
  311. package/dist/api/solana/SolSignMessage.d.ts.map +1 -1
  312. package/dist/api/solana/SolSignOffchainMessage.d.ts +1 -0
  313. package/dist/api/solana/SolSignOffchainMessage.d.ts.map +1 -1
  314. package/dist/api/solana/SolSignTransaction.d.ts +1 -0
  315. package/dist/api/solana/SolSignTransaction.d.ts.map +1 -1
  316. package/dist/api/starcoin/StarcoinGetAddress.d.ts +1 -0
  317. package/dist/api/starcoin/StarcoinGetAddress.d.ts.map +1 -1
  318. package/dist/api/starcoin/StarcoinGetPublicKey.d.ts +1 -0
  319. package/dist/api/starcoin/StarcoinGetPublicKey.d.ts.map +1 -1
  320. package/dist/api/starcoin/StarcoinSignMessage.d.ts +1 -0
  321. package/dist/api/starcoin/StarcoinSignMessage.d.ts.map +1 -1
  322. package/dist/api/starcoin/StarcoinSignTransaction.d.ts +1 -0
  323. package/dist/api/starcoin/StarcoinSignTransaction.d.ts.map +1 -1
  324. package/dist/api/starcoin/StarcoinVerifyMessage.d.ts +1 -0
  325. package/dist/api/starcoin/StarcoinVerifyMessage.d.ts.map +1 -1
  326. package/dist/api/stellar/StellarGetAddress.d.ts +1 -2
  327. package/dist/api/stellar/StellarGetAddress.d.ts.map +1 -1
  328. package/dist/api/stellar/StellarSignTransaction.d.ts +2 -3
  329. package/dist/api/stellar/StellarSignTransaction.d.ts.map +1 -1
  330. package/dist/api/sui/SuiGetAddress.d.ts +1 -0
  331. package/dist/api/sui/SuiGetAddress.d.ts.map +1 -1
  332. package/dist/api/sui/SuiGetPublicKey.d.ts +1 -0
  333. package/dist/api/sui/SuiGetPublicKey.d.ts.map +1 -1
  334. package/dist/api/sui/SuiSignMessage.d.ts +1 -0
  335. package/dist/api/sui/SuiSignMessage.d.ts.map +1 -1
  336. package/dist/api/sui/SuiSignTransaction.d.ts +1 -0
  337. package/dist/api/sui/SuiSignTransaction.d.ts.map +1 -1
  338. package/dist/api/ton/TonGetAddress.d.ts +1 -0
  339. package/dist/api/ton/TonGetAddress.d.ts.map +1 -1
  340. package/dist/api/ton/TonSignData.d.ts +1 -0
  341. package/dist/api/ton/TonSignData.d.ts.map +1 -1
  342. package/dist/api/ton/TonSignMessage.d.ts +1 -0
  343. package/dist/api/ton/TonSignMessage.d.ts.map +1 -1
  344. package/dist/api/ton/TonSignProof.d.ts +1 -0
  345. package/dist/api/ton/TonSignProof.d.ts.map +1 -1
  346. package/dist/api/tron/TronGetAddress.d.ts +1 -0
  347. package/dist/api/tron/TronGetAddress.d.ts.map +1 -1
  348. package/dist/api/tron/TronSignMessage.d.ts +5 -0
  349. package/dist/api/tron/TronSignMessage.d.ts.map +1 -1
  350. package/dist/api/tron/TronSignTransaction.d.ts +1 -0
  351. package/dist/api/tron/TronSignTransaction.d.ts.map +1 -1
  352. package/dist/api/utils.d.ts +2 -1
  353. package/dist/api/utils.d.ts.map +1 -1
  354. package/dist/api/xrp/XrpGetAddress.d.ts +1 -0
  355. package/dist/api/xrp/XrpGetAddress.d.ts.map +1 -1
  356. package/dist/api/xrp/XrpSignTransaction.d.ts +1 -0
  357. package/dist/api/xrp/XrpSignTransaction.d.ts.map +1 -1
  358. package/dist/constants/index.d.ts +1 -1
  359. package/dist/constants/index.d.ts.map +1 -1
  360. package/dist/core/RequestQueue.d.ts.map +1 -1
  361. package/dist/core/deviceEventRegistration.d.ts +11 -0
  362. package/dist/core/deviceEventRegistration.d.ts.map +1 -0
  363. package/dist/core/index.d.ts +3 -1
  364. package/dist/core/index.d.ts.map +1 -1
  365. package/dist/data-manager/DataManager.d.ts +1 -0
  366. package/dist/data-manager/DataManager.d.ts.map +1 -1
  367. package/dist/data-manager/connectSettings.d.ts.map +1 -1
  368. package/dist/device/Device.d.ts +44 -14
  369. package/dist/device/Device.d.ts.map +1 -1
  370. package/dist/device/DeviceCommands.d.ts +8 -6
  371. package/dist/device/DeviceCommands.d.ts.map +1 -1
  372. package/dist/device/DeviceConnector.d.ts +1 -1
  373. package/dist/device/DeviceConnector.d.ts.map +1 -1
  374. package/dist/device/DeviceFeaturesState.d.ts +3 -0
  375. package/dist/device/DeviceFeaturesState.d.ts.map +1 -0
  376. package/dist/device/DevicePool.d.ts +5 -2
  377. package/dist/device/DevicePool.d.ts.map +1 -1
  378. package/dist/device/DeviceStateMapper.d.ts +13 -0
  379. package/dist/device/DeviceStateMapper.d.ts.map +1 -0
  380. package/dist/device/DeviceStateProjector.d.ts +7 -0
  381. package/dist/device/DeviceStateProjector.d.ts.map +1 -0
  382. package/dist/device/DeviceStateStore.d.ts +21 -0
  383. package/dist/device/DeviceStateStore.d.ts.map +1 -0
  384. package/dist/device/DeviceWalletSessionStore.d.ts +27 -0
  385. package/dist/device/DeviceWalletSessionStore.d.ts.map +1 -0
  386. package/dist/device/cloneDeviceState.d.ts +2 -0
  387. package/dist/device/cloneDeviceState.d.ts.map +1 -0
  388. package/dist/device/deviceIdentity.d.ts +5 -0
  389. package/dist/device/deviceIdentity.d.ts.map +1 -0
  390. package/dist/deviceProfile/buildDeviceFeatures.d.ts +9 -3
  391. package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
  392. package/dist/deviceProfile/index.d.ts +0 -1
  393. package/dist/deviceProfile/index.d.ts.map +1 -1
  394. package/dist/deviceProfile/protocolV2DeviceIdentity.d.ts +7 -0
  395. package/dist/deviceProfile/protocolV2DeviceIdentity.d.ts.map +1 -0
  396. package/dist/events/device.d.ts +12 -2
  397. package/dist/events/device.d.ts.map +1 -1
  398. package/dist/events/logBlockEvent.d.ts +2 -0
  399. package/dist/events/logBlockEvent.d.ts.map +1 -1
  400. package/dist/events/ui-request.d.ts +19 -2
  401. package/dist/events/ui-request.d.ts.map +1 -1
  402. package/dist/index.d.ts +1559 -736
  403. package/dist/index.d.ts.map +1 -1
  404. package/dist/index.js +9238 -3898
  405. package/dist/inject.d.ts.map +1 -1
  406. package/dist/lowLevelInject.d.ts.map +1 -1
  407. package/dist/protocols/protocol-v2/deviceSession.d.ts +7 -0
  408. package/dist/protocols/protocol-v2/deviceSession.d.ts.map +1 -0
  409. package/dist/protocols/protocol-v2/features.d.ts +25 -17
  410. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  411. package/dist/protocols/protocol-v2/firmware.d.ts +1 -0
  412. package/dist/protocols/protocol-v2/firmware.d.ts.map +1 -1
  413. package/dist/protocols/protocol-v2/index.d.ts +3 -2
  414. package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
  415. package/dist/protocols/protocol-v2/lockedError.d.ts +2 -0
  416. package/dist/protocols/protocol-v2/lockedError.d.ts.map +1 -0
  417. package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts +17 -0
  418. package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts.map +1 -0
  419. package/dist/protocols/protocol-v2/uiInteraction.d.ts +39 -0
  420. package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -0
  421. package/dist/protocols/protocol-v2/unlockPolicy.d.ts +4 -0
  422. package/dist/protocols/protocol-v2/unlockPolicy.d.ts.map +1 -0
  423. package/dist/protocols/protocol-v2/unlockRetry.d.ts +10 -0
  424. package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +1 -0
  425. package/dist/protocols/protocol-v2/walletSession.d.ts +22 -0
  426. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -0
  427. package/dist/topLevelInject.d.ts.map +1 -1
  428. package/dist/types/api/checkAllFirmwareRelease.d.ts +29 -4
  429. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  430. package/dist/types/api/deviceSettings.d.ts +11 -2
  431. package/dist/types/api/deviceSettings.d.ts.map +1 -1
  432. package/dist/types/api/deviceUnlock.d.ts +1 -1
  433. package/dist/types/api/deviceUnlock.d.ts.map +1 -1
  434. package/dist/types/api/deviceUpdateBootloader.d.ts +6 -0
  435. package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
  436. package/dist/types/api/export.d.ts +6 -2
  437. package/dist/types/api/export.d.ts.map +1 -1
  438. package/dist/types/api/firmwareUpdate.d.ts +75 -1
  439. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  440. package/dist/types/api/firmwareUpdateCapabilities.d.ts +9 -0
  441. package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +1 -0
  442. package/dist/types/api/firmwareUpdatePlan.d.ts +28 -0
  443. package/dist/types/api/firmwareUpdatePlan.d.ts.map +1 -0
  444. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +42 -0
  445. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +1 -0
  446. package/dist/types/api/getDeviceState.d.ts +12 -0
  447. package/dist/types/api/getDeviceState.d.ts.map +1 -0
  448. package/dist/types/api/getFeatures.d.ts.map +1 -1
  449. package/dist/types/api/getOnekeyFeatures.d.ts.map +1 -1
  450. package/dist/types/api/getPassphraseState.d.ts +2 -10
  451. package/dist/types/api/getPassphraseState.d.ts.map +1 -1
  452. package/dist/types/api/index.d.ts +27 -31
  453. package/dist/types/api/index.d.ts.map +1 -1
  454. package/dist/types/api/kaspaSignTransaction.d.ts +42 -4
  455. package/dist/types/api/kaspaSignTransaction.d.ts.map +1 -1
  456. package/dist/types/api/openWalletSession.d.ts +35 -0
  457. package/dist/types/api/openWalletSession.d.ts.map +1 -0
  458. package/dist/types/api/protocolV2.d.ts +15 -84
  459. package/dist/types/api/protocolV2.d.ts.map +1 -1
  460. package/dist/types/api/sessionCache.d.ts +13 -0
  461. package/dist/types/api/sessionCache.d.ts.map +1 -0
  462. package/dist/types/device.d.ts +132 -8
  463. package/dist/types/device.d.ts.map +1 -1
  464. package/dist/types/params.d.ts.map +1 -1
  465. package/dist/types/settings.d.ts +20 -14
  466. package/dist/types/settings.d.ts.map +1 -1
  467. package/dist/utils/assets.d.ts +2 -1
  468. package/dist/utils/assets.d.ts.map +1 -1
  469. package/dist/utils/deviceFeaturesCompat.d.ts +13 -0
  470. package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -0
  471. package/dist/utils/deviceFeaturesUtils.d.ts +6 -3
  472. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  473. package/dist/utils/deviceInfoUtils.d.ts +9 -9
  474. package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
  475. package/dist/utils/deviceSettings.d.ts +38 -7
  476. package/dist/utils/deviceSettings.d.ts.map +1 -1
  477. package/dist/utils/deviceVersionUtils.d.ts +7 -5
  478. package/dist/utils/deviceVersionUtils.d.ts.map +1 -1
  479. package/dist/utils/homescreen.d.ts.map +1 -1
  480. package/dist/utils/index.d.ts +5 -2
  481. package/dist/utils/index.d.ts.map +1 -1
  482. package/dist/utils/networkUtils.d.ts +10 -1
  483. package/dist/utils/networkUtils.d.ts.map +1 -1
  484. package/dist/utils/patch.d.ts +1 -1
  485. package/dist/utils/patch.d.ts.map +1 -1
  486. package/dist/utils/pro2Wallpaper.d.ts +12 -0
  487. package/dist/utils/pro2Wallpaper.d.ts.map +1 -0
  488. package/dist/utils/tracing.d.ts.map +1 -1
  489. package/package.json +4 -4
  490. package/src/api/BaseMethod.ts +52 -17
  491. package/src/api/CheckAllFirmwareRelease.ts +344 -4
  492. package/src/api/CheckBootloaderRelease.ts +1 -2
  493. package/src/api/CheckFirmwareRelease.ts +1 -2
  494. package/src/api/CipherKeyValue.ts +4 -0
  495. package/src/api/ClearSessionCache.ts +32 -0
  496. package/src/api/DirList.ts +9 -4
  497. package/src/api/DirMake.ts +7 -4
  498. package/src/api/DirRemove.ts +7 -4
  499. package/src/api/FileDelete.ts +7 -4
  500. package/src/api/FileRead.ts +15 -9
  501. package/src/api/FileWrite.ts +26 -181
  502. package/src/api/FirmwareUpdate.ts +11 -12
  503. package/src/api/FirmwareUpdateV2.ts +370 -112
  504. package/src/api/FirmwareUpdateV3.ts +266 -68
  505. package/src/api/FirmwareUpdateV4.ts +1276 -651
  506. package/src/api/GetDeviceState.ts +59 -0
  507. package/src/api/GetFeatures.ts +6 -4
  508. package/src/api/GetOnekeyFeatures.ts +1 -75
  509. package/src/api/GetPassphraseState.ts +30 -21
  510. package/src/api/OpenWalletSession.ts +214 -0
  511. package/src/api/PathInfo.ts +9 -4
  512. package/src/api/PromptWebDeviceAccess.ts +2 -7
  513. package/src/api/SearchDevices.ts +29 -4
  514. package/src/api/UploadPortfolio.ts +40 -0
  515. package/src/api/alephium/AlephiumGetAddress.ts +0 -4
  516. package/src/api/alephium/AlephiumSignMessage.ts +0 -4
  517. package/src/api/alephium/AlephiumSignTransaction.ts +2 -6
  518. package/src/api/algo/AlgoGetAddress.ts +4 -0
  519. package/src/api/algo/AlgoSignTransaction.ts +4 -0
  520. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +9 -21
  521. package/src/api/aptos/AptosGetAddress.ts +4 -0
  522. package/src/api/aptos/AptosGetPublicKey.ts +4 -0
  523. package/src/api/aptos/AptosSignInMessage.ts +4 -0
  524. package/src/api/aptos/AptosSignMessage.ts +4 -0
  525. package/src/api/aptos/AptosSignTransaction.ts +4 -0
  526. package/src/api/benfen/BenfenGetAddress.ts +11 -7
  527. package/src/api/benfen/BenfenGetPublicKey.ts +0 -4
  528. package/src/api/benfen/BenfenSignMessage.ts +0 -4
  529. package/src/api/benfen/BenfenSignTransaction.ts +0 -4
  530. package/src/api/btc/BTCGetAddress.ts +8 -1
  531. package/src/api/btc/BTCGetPublicKey.ts +9 -2
  532. package/src/api/btc/BTCSignMessage.ts +8 -1
  533. package/src/api/btc/BTCSignPsbt.ts +4 -0
  534. package/src/api/btc/BTCSignTransaction.ts +8 -1
  535. package/src/api/btc/BTCVerifyMessage.ts +8 -1
  536. package/src/api/btc/helpers/versionLimit.ts +7 -4
  537. package/src/api/cardano/CardanoGetAddress.ts +4 -0
  538. package/src/api/cardano/CardanoGetPublicKey.ts +4 -0
  539. package/src/api/cardano/CardanoSignMessage.ts +5 -1
  540. package/src/api/cardano/CardanoSignTransaction.ts +5 -1
  541. package/src/api/conflux/ConfluxGetAddress.ts +6 -0
  542. package/src/api/conflux/ConfluxSignMessage.ts +6 -0
  543. package/src/api/conflux/ConfluxSignMessageCIP23.ts +6 -0
  544. package/src/api/conflux/ConfluxSignTransaction.ts +8 -5
  545. package/src/api/cosmos/CosmosGetAddress.ts +4 -0
  546. package/src/api/cosmos/CosmosGetPublicKey.ts +4 -0
  547. package/src/api/cosmos/CosmosSignTransaction.ts +4 -0
  548. package/src/api/device/DeviceCancel.ts +5 -0
  549. package/src/api/device/DeviceChangePin.ts +28 -0
  550. package/src/api/device/DeviceFullyUploadResource.ts +4 -5
  551. package/src/api/device/DeviceLock.ts +5 -0
  552. package/src/api/device/DeviceRebootToBoardloader.ts +5 -0
  553. package/src/api/device/DeviceRebootToBootloader.ts +5 -0
  554. package/src/api/device/DeviceSettings.ts +194 -1
  555. package/src/api/device/DeviceSupportFeatures.ts +4 -0
  556. package/src/api/device/DeviceUnlock.ts +13 -0
  557. package/src/api/device/DeviceUpdateBootloader.ts +123 -8
  558. package/src/api/device/DeviceWipe.ts +25 -0
  559. package/src/api/device/PreInitialize.ts +4 -0
  560. package/src/api/dynex/DnxGetAddress.ts +0 -6
  561. package/src/api/dynex/DnxSignTransaction.ts +0 -6
  562. package/src/api/evm/EVMGetAddress.ts +6 -2
  563. package/src/api/evm/EVMGetPublicKey.ts +6 -2
  564. package/src/api/evm/EVMSignMessage.ts +6 -2
  565. package/src/api/evm/EVMSignMessageEIP712.ts +1 -11
  566. package/src/api/evm/EVMSignTransaction.ts +6 -2
  567. package/src/api/evm/EVMSignTypedData.ts +42 -50
  568. package/src/api/evm/EVMVerifyMessage.ts +6 -2
  569. package/src/api/evm/legacyV1/getAddress.ts +2 -2
  570. package/src/api/evm/legacyV1/getPublicKey.ts +2 -2
  571. package/src/api/evm/legacyV1/signMessage.ts +2 -2
  572. package/src/api/evm/legacyV1/signTypedData.ts +2 -2
  573. package/src/api/evm/legacyV1/signTypedHash.ts +2 -2
  574. package/src/api/evm/legacyV1/verifyMessage.ts +2 -2
  575. package/src/api/evm/protocol.ts +6 -0
  576. package/src/api/filecoin/FilecoinGetAddress.ts +4 -0
  577. package/src/api/filecoin/FilecoinSignTransaction.ts +4 -0
  578. package/src/api/firmware/FirmwareArtifactSource.ts +278 -0
  579. package/src/api/firmware/FirmwareHostBinding.ts +100 -0
  580. package/src/api/firmware/FirmwarePreparationError.ts +12 -0
  581. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +60 -3
  582. package/src/api/firmware/FirmwareUpdateCapabilities.ts +9 -0
  583. package/src/api/firmware/FirmwareUpdatePlan.ts +745 -0
  584. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +392 -0
  585. package/src/api/firmware/getBinary.ts +8 -3
  586. package/src/api/firmware/progressThrottle.ts +44 -0
  587. package/src/api/firmware/updateBootloader.ts +19 -4
  588. package/src/api/firmware/uploadFirmware.ts +140 -22
  589. package/src/api/helpers/batchGetPublickeys.ts +2 -2
  590. package/src/api/helpers/filesystemValidation.ts +61 -0
  591. package/src/api/helpers/paramsValidator.ts +1 -1
  592. package/src/api/helpers/protocolV2FileWrite.ts +199 -0
  593. package/src/api/index.ts +8 -26
  594. package/src/api/kaspa/KaspaGetAddress.ts +7 -0
  595. package/src/api/kaspa/KaspaSignTransaction.ts +380 -29
  596. package/src/api/kaspa/helpers/TransferSerialize.ts +17 -7
  597. package/src/api/lightning/LnurlAuth.ts +0 -4
  598. package/src/api/near/NearGetAddress.ts +4 -0
  599. package/src/api/near/NearSignTransaction.ts +4 -0
  600. package/src/api/nem/NEMGetAddress.ts +4 -0
  601. package/src/api/nem/NEMSignTransaction.ts +4 -0
  602. package/src/api/neo/NeoGetAddress.ts +0 -4
  603. package/src/api/neo/NeoSignTransaction.ts +0 -4
  604. package/src/api/nervos/NervosGetAddress.ts +0 -4
  605. package/src/api/nervos/NervosSignTransaction.ts +2 -6
  606. package/src/api/nexa/NexaGetAddress.ts +0 -4
  607. package/src/api/nexa/NexaSignTransaction.ts +3 -5
  608. package/src/api/nostr/NostrDecryptMessage.ts +4 -0
  609. package/src/api/nostr/NostrEncryptMessage.ts +4 -0
  610. package/src/api/nostr/NostrGetPublicKey.ts +4 -0
  611. package/src/api/nostr/NostrSignEvent.ts +4 -0
  612. package/src/api/nostr/NostrSignSchnorr.ts +4 -0
  613. package/src/api/polkadot/PolkadotGetAddress.ts +4 -0
  614. package/src/api/polkadot/PolkadotSignTransaction.ts +4 -0
  615. package/src/api/protocol-v2/DeviceFactoryInfoGet.ts +5 -2
  616. package/src/api/protocol-v2/DeviceFactoryInfoSet.ts +5 -2
  617. package/src/api/protocol-v2/DeviceFirmwareUpdate.ts +8 -5
  618. package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +43 -5
  619. package/src/api/protocol-v2/DeviceGetOnboardingStatus.ts +21 -0
  620. package/src/api/protocol-v2/DeviceInfoGet.ts +8 -15
  621. package/src/api/protocol-v2/DeviceReboot.ts +7 -3
  622. package/src/api/protocol-v2/DeviceStatusGet.ts +19 -0
  623. package/src/api/protocol-v2/DeviceUploadWallpaper.ts +134 -0
  624. package/src/api/protocol-v2/FilesystemFormat.ts +9 -3
  625. package/src/api/protocol-v2/FilesystemPermissionFix.ts +5 -2
  626. package/src/api/protocol-v2/Ping.ts +37 -3
  627. package/src/api/protocol-v2/ProtocolInfoRequest.ts +5 -2
  628. package/src/api/protocol-v2/helpers.ts +21 -16
  629. package/src/api/scdo/ScdoGetAddress.ts +0 -4
  630. package/src/api/scdo/ScdoSignMessage.ts +0 -4
  631. package/src/api/scdo/ScdoSignTransaction.ts +0 -4
  632. package/src/api/solana/SolGetAddress.ts +4 -0
  633. package/src/api/solana/SolSignMessage.ts +4 -0
  634. package/src/api/solana/SolSignOffchainMessage.ts +4 -0
  635. package/src/api/solana/SolSignTransaction.ts +4 -0
  636. package/src/api/starcoin/StarcoinGetAddress.ts +4 -0
  637. package/src/api/starcoin/StarcoinGetPublicKey.ts +4 -0
  638. package/src/api/starcoin/StarcoinSignMessage.ts +4 -0
  639. package/src/api/starcoin/StarcoinSignTransaction.ts +4 -0
  640. package/src/api/starcoin/StarcoinVerifyMessage.ts +4 -0
  641. package/src/api/stellar/StellarGetAddress.ts +1 -10
  642. package/src/api/stellar/StellarSignTransaction.ts +1 -14
  643. package/src/api/sui/SuiGetAddress.ts +4 -0
  644. package/src/api/sui/SuiGetPublicKey.ts +4 -0
  645. package/src/api/sui/SuiSignMessage.ts +4 -0
  646. package/src/api/sui/SuiSignTransaction.ts +4 -0
  647. package/src/api/ton/TonGetAddress.ts +4 -0
  648. package/src/api/ton/TonSignData.ts +5 -1
  649. package/src/api/ton/TonSignMessage.ts +4 -0
  650. package/src/api/ton/TonSignProof.ts +4 -0
  651. package/src/api/tron/TronGetAddress.ts +4 -0
  652. package/src/api/tron/TronSignMessage.ts +18 -7
  653. package/src/api/tron/TronSignTransaction.ts +4 -0
  654. package/src/api/utils.ts +7 -3
  655. package/src/api/xrp/XrpGetAddress.ts +4 -0
  656. package/src/api/xrp/XrpSignTransaction.ts +5 -1
  657. package/src/constants/index.ts +1 -4
  658. package/src/core/RequestQueue.ts +1 -0
  659. package/src/core/deviceEventRegistration.ts +23 -0
  660. package/src/core/index.ts +164 -53
  661. package/src/data/messages/messages-protocol-v2.json +1155 -481
  662. package/src/data/messages/messages.json +286 -1
  663. package/src/data-manager/DataManager.ts +39 -39
  664. package/src/data-manager/TransportManager.ts +1 -1
  665. package/src/data-manager/connectSettings.ts +15 -0
  666. package/src/device/Device.ts +647 -281
  667. package/src/device/DeviceCommands.ts +166 -158
  668. package/src/device/DeviceConnector.ts +10 -7
  669. package/src/device/DeviceFeaturesState.ts +25 -0
  670. package/src/device/DevicePool.ts +66 -16
  671. package/src/device/DeviceStateMapper.ts +374 -0
  672. package/src/device/DeviceStateProjector.ts +94 -0
  673. package/src/device/DeviceStateStore.ts +201 -0
  674. package/src/device/DeviceWalletSessionStore.ts +144 -0
  675. package/src/device/cloneDeviceState.ts +39 -0
  676. package/src/device/deviceIdentity.ts +18 -0
  677. package/src/deviceProfile/buildDeviceFeatures.ts +224 -190
  678. package/src/deviceProfile/index.ts +0 -1
  679. package/src/deviceProfile/protocolV2DeviceIdentity.ts +32 -0
  680. package/src/events/device.ts +18 -1
  681. package/src/events/logBlockEvent.ts +79 -2
  682. package/src/events/ui-request.ts +25 -2
  683. package/src/index.ts +5 -0
  684. package/src/inject.ts +37 -43
  685. package/src/lowLevelInject.ts +5 -1
  686. package/src/protocols/protocol-v2/deviceSession.ts +24 -0
  687. package/src/protocols/protocol-v2/features.ts +96 -32
  688. package/src/protocols/protocol-v2/firmware.ts +2 -1
  689. package/src/protocols/protocol-v2/index.ts +8 -1
  690. package/src/protocols/protocol-v2/lockedError.ts +10 -0
  691. package/src/protocols/protocol-v2/settingsUnlockPolicy.ts +65 -0
  692. package/src/protocols/protocol-v2/uiInteraction.ts +210 -0
  693. package/src/protocols/protocol-v2/unlockPolicy.ts +105 -0
  694. package/src/protocols/protocol-v2/unlockRetry.ts +109 -0
  695. package/src/protocols/protocol-v2/walletSession.ts +285 -0
  696. package/src/topLevelInject.ts +5 -1
  697. package/src/types/api/checkAllFirmwareRelease.ts +50 -4
  698. package/src/types/api/deviceSettings.ts +26 -2
  699. package/src/types/api/deviceUnlock.ts +1 -1
  700. package/src/types/api/deviceUpdateBootloader.ts +6 -0
  701. package/src/types/api/export.ts +26 -0
  702. package/src/types/api/firmwareUpdate.ts +99 -5
  703. package/src/types/api/firmwareUpdateCapabilities.ts +9 -0
  704. package/src/types/api/firmwareUpdatePlan.ts +38 -0
  705. package/src/types/api/firmwareUpdatePreparedPlan.ts +53 -0
  706. package/src/types/api/getDeviceState.ts +19 -0
  707. package/src/types/api/getFeatures.ts +3 -0
  708. package/src/types/api/getOnekeyFeatures.ts +3 -0
  709. package/src/types/api/getPassphraseState.ts +9 -11
  710. package/src/types/api/index.ts +39 -64
  711. package/src/types/api/kaspaSignTransaction.ts +60 -4
  712. package/src/types/api/openWalletSession.ts +54 -0
  713. package/src/types/api/protocolV2.ts +24 -143
  714. package/src/types/api/sessionCache.ts +19 -0
  715. package/src/types/device.ts +185 -7
  716. package/src/types/params.ts +3 -2
  717. package/src/types/settings.ts +30 -16
  718. package/src/utils/assets.ts +4 -1
  719. package/src/utils/deviceFeaturesCompat.ts +180 -0
  720. package/src/utils/deviceFeaturesUtils.ts +52 -45
  721. package/src/utils/deviceInfoUtils.ts +27 -69
  722. package/src/utils/deviceSettings.ts +171 -39
  723. package/src/utils/deviceVersionUtils.ts +25 -44
  724. package/src/utils/findDefectiveBatchDevice.ts +3 -3
  725. package/src/utils/homescreen.ts +10 -0
  726. package/src/utils/index.ts +21 -2
  727. package/src/utils/networkUtils.ts +274 -16
  728. package/src/utils/pro2Wallpaper.ts +103 -0
  729. package/src/utils/tracing.ts +3 -1
  730. package/tsconfig.type-tests.json +12 -0
  731. package/dist/api/GetDeviceInfo.d.ts +0 -9
  732. package/dist/api/GetDeviceInfo.d.ts.map +0 -1
  733. package/dist/api/protocol-v2/FilesystemDiskControl.d.ts +0 -13
  734. package/dist/api/protocol-v2/FilesystemDiskControl.d.ts.map +0 -1
  735. package/dist/deviceProfile/buildDeviceProfile.d.ts +0 -21
  736. package/dist/deviceProfile/buildDeviceProfile.d.ts.map +0 -1
  737. package/dist/types/api/getDeviceInfo.d.ts +0 -87
  738. package/dist/types/api/getDeviceInfo.d.ts.map +0 -1
  739. package/src/api/GetDeviceInfo.ts +0 -145
  740. package/src/api/protocol-v2/FilesystemDiskControl.ts +0 -50
  741. package/src/deviceProfile/buildDeviceProfile.ts +0 -352
  742. 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,12 +104,23 @@ 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 };
110
+ type ProtocolV2InstallItem = ProtocolV2TargetBinary & {
111
+ kind: ProtocolV2RemoteComponentTarget['kind'];
112
+ };
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;
123
+ };
103
124
 
104
125
  type ProtocolV2RemoteComponentBinary = ProtocolV2RemoteComponentTarget & {
105
126
  binary: ArrayBuffer;
@@ -108,7 +129,36 @@ type ProtocolV2RemoteComponentBinary = ProtocolV2RemoteComponentTarget & {
108
129
  type ProtocolV2RemoteComponentTarget = {
109
130
  fileName: string;
110
131
  targetId: number;
111
- 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[];
112
162
  };
113
163
 
114
164
  type ProtocolV2OkppHeader = {
@@ -161,18 +211,24 @@ const PROTOCOL_V2_REMOTE_COMPONENT_TARGETS: Readonly<
161
211
  targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE04,
162
212
  kind: 'firmware',
163
213
  },
164
- CRATE: {
165
- fileName: 'resource.bin',
166
- targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_CRATE,
167
- kind: 'resource',
168
- },
169
214
  };
170
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
+
171
227
  const PROTOCOL_V2_ROMLOADER_UNSUPPORTED_MESSAGE =
172
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.';
173
229
 
174
- // hd-transport 的历史 decode 行为会把单值 enum 输出为枚举名字符串;
175
- // 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.
176
232
  const PROTOCOL_V2_TARGET_ID_BY_DECODED_NAME = new Map<string, number>(
177
233
  Object.entries(ProtocolV2FirmwareTargetType).map(([key, value]) => [key, value])
178
234
  );
@@ -198,25 +254,12 @@ const isProtocolV2ReconnectProbeError = (error: unknown) => {
198
254
  );
199
255
  };
200
256
 
201
- const isProtocolV2PollingTransientError = (error: unknown) => {
202
- const message = getProtocolV2UnknownErrorText(error).toLowerCase();
203
- return (
204
- isProtocolV2DeviceDisconnectedError(error) ||
205
- isProtocolV2ReconnectProbeError(error) ||
206
- message.includes('libusb_transfer_timed_out') ||
207
- (message.includes('response timeout') && message.includes('devicefirmwareupdatestatusget')) ||
208
- message.includes('device not found') ||
209
- message.includes('transportnotfound')
210
- );
211
- };
212
-
213
- const isProtocolV2StartUpdateTransientError = (error: unknown) => {
257
+ const isProtocolV2FirmwareStatusEndpointUnavailable = (error: unknown) => {
214
258
  const message = getProtocolV2UnknownErrorText(error).toLowerCase();
215
259
  return (
216
- isProtocolV2DeviceDisconnectedError(error) ||
217
- isProtocolV2ReconnectProbeError(error) ||
218
- message.includes('libusb_transfer_timed_out') ||
219
- (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')
220
263
  );
221
264
  };
222
265
 
@@ -262,13 +305,6 @@ const versionArrayToNumber = (version?: IVersionArray) => {
262
305
  return version[0] * 0x10000 + version[1] * 0x100 + version[2];
263
306
  };
264
307
 
265
- const versionStringToArray = (version?: string | null): IVersionArray | undefined => {
266
- if (!version) return undefined;
267
- const parts = version.split('.').map(part => Number(part));
268
- if (parts.length !== 3 || parts.some(part => !Number.isFinite(part))) return undefined;
269
- return parts as IVersionArray;
270
- };
271
-
272
308
  const compareProtocolV2Versions = (current?: IVersionArray, target?: IVersionArray) => {
273
309
  const currentNumber = versionArrayToNumber(current);
274
310
  const targetNumber = versionArrayToNumber(target);
@@ -355,6 +391,33 @@ const parseProtocolV2OkppHeader = (bytes: Uint8Array): ProtocolV2OkppHeader | nu
355
391
  };
356
392
  };
357
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
+
358
421
  /**
359
422
  * FirmwareUpdateV4 is the complete Protocol V2 firmware update flow.
360
423
  *
@@ -364,9 +427,26 @@ const parseProtocolV2OkppHeader = (bytes: Uint8Array): ProtocolV2OkppHeader | nu
364
427
  * - completion waits for target status to finish, reboots to normal, then polls DeviceInfo
365
428
  */
366
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
+
367
446
  init() {
368
447
  this.allowDeviceMode = [UI_REQUEST.BOOTLOADER, UI_REQUEST.NOT_INITIALIZE];
369
448
  this.requireDeviceMode = [];
449
+ this.unlockPolicy = 'unlock-before-run';
370
450
  this.useDevicePassphraseState = false;
371
451
  this.skipForceUpdateCheck = true;
372
452
 
@@ -387,7 +467,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
387
467
 
388
468
  validateParams(payload, [
389
469
  { name: 'chunkSize', type: 'number' },
390
- { name: 'resourceBinaries', type: 'array', allowEmpty: true },
391
470
  { name: 'forcedUpdateRes', type: 'boolean' },
392
471
  { name: 'bootloaderBinary', type: 'buffer' },
393
472
  { name: 'romloaderBinary', type: 'buffer' },
@@ -399,10 +478,64 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
399
478
  { name: 'se03Binary', type: 'buffer' },
400
479
  { name: 'se04Binary', type: 'buffer' },
401
480
  { name: 'firmwareType', type: 'string' },
481
+ { name: 'targetsToUpdate', type: 'array', allowEmpty: true },
402
482
  { name: 'platform', type: 'string' },
483
+ { name: 'expectedDeviceId', type: 'string' },
484
+ { name: 'resourceBundleFiles', type: 'array', allowEmpty: true },
403
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
+ }
404
536
 
405
537
  this.params = {
538
+ preparedPlan: payload.preparedPlan,
406
539
  chunkSize: payload.chunkSize,
407
540
  forcedUpdateRes: payload.forcedUpdateRes,
408
541
  bootloaderBinary: payload.bootloaderBinary,
@@ -414,19 +547,29 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
414
547
  se02Binary: payload.se02Binary,
415
548
  se03Binary: payload.se03Binary,
416
549
  se04Binary: payload.se04Binary,
417
- resourceBinaries: payload.resourceBinaries,
550
+ resourceBundleFiles: payload.resourceBundleFiles,
418
551
  firmwareType: payload.firmwareType,
552
+ targetsToUpdate: payload.targetsToUpdate,
553
+ expectedTargetVersions: payload.expectedTargetVersions,
419
554
  platform: payload.platform,
555
+ expectedDeviceId: payload.expectedDeviceId,
556
+ artifactReader: hostBinding?.artifactReader ?? payload.artifactReader,
557
+ componentArtifacts: payload.componentArtifacts,
558
+ resourceBundleArtifacts: payload.resourceBundleArtifacts,
420
559
  };
421
560
  }
422
561
 
423
- private getProtocolV2FirmwareChunkSize() {
562
+ private getProtocolV2FirmwareChunkSize(direction: 'read' | 'write' = 'write') {
424
563
  const payloadChunkSize = Number(this.params?.chunkSize);
425
564
  const env = DataManager.getSettings('env');
426
- const maxChunkSize =
427
- this.params?.platform === 'native' || (env && DataManager.isBleConnect(env))
428
- ? PROTOCOL_V2_BLE_FILE_CHUNK_SIZE
429
- : 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
+ }
430
573
  if (!Number.isFinite(payloadChunkSize) || payloadChunkSize <= 0) {
431
574
  return maxChunkSize;
432
575
  }
@@ -437,66 +580,355 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
437
580
  }
438
581
 
439
582
  async run() {
440
- if (!this.device.isProtocolV2()) {
441
- throw ERRORS.TypedError(
442
- HardwareErrorCode.RuntimeError,
443
- 'firmwareUpdateV4 requires a Protocol V2 device'
444
- );
445
- }
446
-
447
583
  Log.debug('FirmwareUpdateV4 strategy: Protocol V2');
448
584
  return this.runProtocolV2();
449
585
  }
450
586
 
451
587
  private async runProtocolV2() {
588
+ await this.captureProtocolV2PhysicalIdentity();
452
589
  const deviceFeatures = await this.getProtocolV2DeviceFeatures();
453
590
  const deviceFirmwareType = getFirmwareType(deviceFeatures);
454
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
+ }
455
600
 
456
- let resourceBinaryMap: ProtocolV2TargetBinary[] = [];
457
601
  let fwBinaryMap: ProtocolV2TargetBinary[] = [];
458
602
  let bootloaderBinary: ArrayBuffer | null = null;
603
+ let installItems: ProtocolV2InstallItem[] | undefined;
604
+ let resourceBundles: ProtocolV2ResourceBundleBinary[] | undefined;
459
605
  try {
460
606
  this.postTipMessage(FirmwareUpdateTipMessage.StartDownloadFirmware);
461
- resourceBinaryMap = await this.prepareResourceBinaries(firmwareType, deviceFeatures);
462
607
  fwBinaryMap = this.collectExplicitTargetBinaries();
463
608
  bootloaderBinary = this.prepareBootloaderBinary();
464
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
+ }
465
622
  const remoteBinaries = await this.prepareRemoteProtocolV2Binaries(
466
623
  firmwareType,
467
624
  deviceFeatures
468
625
  );
469
- resourceBinaryMap = remoteBinaries.resourceBinaryMap;
470
626
  bootloaderBinary = remoteBinaries.bootloaderBinary;
471
627
  fwBinaryMap = remoteBinaries.fwBinaryMap;
628
+ installItems = remoteBinaries.installItems;
472
629
  }
630
+ const resourceDescriptors = this.prepareProtocolV2ResourceBundles(
631
+ firmwareType,
632
+ deviceFeatures
633
+ );
634
+ resourceBundles = resourceDescriptors?.length
635
+ ? await this.materializeProtocolV2ResourceBundles(resourceDescriptors)
636
+ : undefined;
473
637
  this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
474
638
  } catch (err) {
475
- throw ERRORS.TypedError(HardwareErrorCode.FirmwareUpdateDownloadFailed, err.message ?? err);
639
+ throw normalizeFirmwarePreparationError(err);
476
640
  }
477
641
 
478
- if (resourceBinaryMap.length === 0 && !bootloaderBinary && fwBinaryMap.length === 0) {
642
+ if (!bootloaderBinary && fwBinaryMap.length === 0 && !resourceBundles?.length) {
479
643
  throw ERRORS.TypedError(
480
644
  HardwareErrorCode.FirmwareUpdateDownloadFailed,
481
645
  'No firmware to update'
482
646
  );
483
647
  }
484
648
 
485
- await this.enterProtocolV2BootloaderMode();
486
-
487
- await this.executeProtocolV2Update({
488
- resourceBinaryMap,
649
+ return this.executeProtocolV2Update({
489
650
  fwBinaryMap,
490
651
  bootloaderBinary,
652
+ ...(installItems ? { installItems } : undefined),
653
+ ...(resourceBundles?.length ? { resourceBundles } : undefined),
654
+ });
655
+ }
656
+
657
+ private async openProtocolV2PreparedSource(artifact: FirmwareArtifactReference) {
658
+ const source = await openFirmwareByteSource({
659
+ artifact,
660
+ reader: this.params.artifactReader,
491
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
+ }
492
674
 
493
- await this.exitProtocolV2BootloaderToNormal();
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
+ }
494
686
 
495
- const versions = await this.waitForProtocolV2FinalFeatures();
496
- this.postTipMessage(FirmwareUpdateTipMessage.FirmwareUpdateCompleted);
497
- DevicePool.resetState();
687
+ private async closeProtocolV2PreparedSources() {
688
+ const sources = this.protocolV2PreparedSources.splice(0);
689
+ await Promise.all(sources.map(source => source.close().catch(() => undefined)));
690
+ }
498
691
 
499
- return versions;
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);
500
932
  }
501
933
 
502
934
  private async getProtocolV2DeviceFeatures(): Promise<Features> {
@@ -510,41 +942,45 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
510
942
  throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'Device features not available');
511
943
  }
512
944
 
513
- private async prepareResourceBinaries(firmwareType: EFirmwareType, features: Features) {
514
- if (this.params.resourceBinaries?.length) {
515
- this.params.resourceBinaries.forEach((binary, index) => {
516
- if (!(binary instanceof ArrayBuffer)) {
517
- throw ERRORS.TypedError(
518
- HardwareErrorCode.CallMethodInvalidParameter,
519
- `Parameter [resourceBinaries.${index}] is of type invalid and should be [buffer].`
520
- );
521
- }
522
- });
945
+ private getProtocolV2SerialNumber(deviceInfo: ProtocolV2DeviceInfo) {
946
+ const serialNumber = deviceInfo.hw?.serial_no?.trim();
947
+ return serialNumber || undefined;
948
+ }
523
949
 
524
- return this.params.resourceBinaries.map((binary, index) => ({
525
- fileName: `resource-${index + 1}.bin`,
526
- binary,
527
- targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_CRATE,
528
- }));
529
- }
530
- const resourceUrl = DataManager.getSysResourcesLatestRelease({
531
- features,
532
- forcedUpdateRes: this.params.forcedUpdateRes,
533
- 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,
534
961
  });
962
+ }
535
963
 
536
- if (resourceUrl) {
537
- const resource = (await getSysResourceBinary(resourceUrl)).binary;
538
- return [
539
- {
540
- fileName: 'resource.bin',
541
- binary: resource,
542
- targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_CRATE,
543
- },
544
- ];
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);
545
976
  }
546
- Log.warn('No resource url found');
547
- return [];
977
+ this.protocolV2ExpectedSerialNumber = serialNumber;
978
+ }
979
+
980
+ private async verifyProtocolV2ReconnectIdentity() {
981
+ const deviceInfo = await this.requestProtocolV2PhysicalIdentity();
982
+ this.assertProtocolV2DeviceInfoIdentity(deviceInfo);
983
+ return deviceInfo;
548
984
  }
549
985
 
550
986
  private prepareBootloaderBinary(): ArrayBuffer | null {
@@ -553,12 +989,34 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
553
989
 
554
990
  private hasExplicitProtocolV2Payload(fwBinaryMap: ProtocolV2TargetBinary[]) {
555
991
  return (
556
- !!this.params.resourceBinaries?.length ||
992
+ !!this.params.resourceBundleFiles?.length ||
557
993
  !!this.params.bootloaderBinary ||
558
994
  fwBinaryMap.length > 0
559
995
  );
560
996
  }
561
997
 
998
+ private buildProtocolV2InstallItems({
999
+ bootloaderBinary,
1000
+ fwBinaryMap,
1001
+ }: {
1002
+ bootloaderBinary: ArrayBuffer | null;
1003
+ fwBinaryMap: ProtocolV2TargetBinary[];
1004
+ }): ProtocolV2InstallItem[] {
1005
+ const installItems: ProtocolV2InstallItem[] = [];
1006
+
1007
+ if (bootloaderBinary) {
1008
+ installItems.push({
1009
+ fileName: 'bootloader.bin',
1010
+ binary: bootloaderBinary,
1011
+ targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER,
1012
+ kind: 'bootloader',
1013
+ });
1014
+ }
1015
+
1016
+ installItems.push(...fwBinaryMap.map(item => ({ ...item, kind: 'firmware' as const })));
1017
+ return installItems;
1018
+ }
1019
+
562
1020
  private getRemoteComponentEntries(release: IFirmwareReleaseInfo) {
563
1021
  const { components } = release;
564
1022
  if (!components) return [];
@@ -594,63 +1052,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
594
1052
  return target;
595
1053
  }
596
1054
 
597
- private getProtocolV2ComponentTargetVersion(
598
- release: IFirmwareReleaseInfo,
599
- component: IProtocolV2FirmwareComponent,
600
- target: ProtocolV2RemoteComponentTarget
601
- ) {
602
- if (component.version) return component.version;
603
- if (target.targetId === ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER) {
604
- return release.bootloaderVersion;
605
- }
606
- if (
607
- target.targetId === ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1 ||
608
- target.targetId === ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P2
609
- ) {
610
- return release.version;
611
- }
612
- return undefined;
613
- }
614
-
615
- private getProtocolV2ComponentCurrentVersion(
616
- features: Features,
617
- target: ProtocolV2RemoteComponentTarget
618
- ) {
619
- switch (target.targetId) {
620
- case ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER:
621
- return getDeviceBootloaderVersion(features);
622
- case ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1:
623
- case ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P2:
624
- return getDeviceFirmwareVersion(features);
625
- case ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_COPROCESSOR:
626
- return getDeviceBLEFirmwareVersion(features);
627
- case ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE01:
628
- return versionStringToArray(features.se01Version);
629
- case ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE02:
630
- return versionStringToArray(features.se02Version);
631
- case ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE03:
632
- return versionStringToArray(features.se03Version);
633
- case ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE04:
634
- return versionStringToArray(features.se04Version);
635
- default:
636
- return undefined;
637
- }
638
- }
639
-
640
- private isProtocolV2ComponentVersionSatisfied(
641
- release: IFirmwareReleaseInfo,
642
- component: IProtocolV2FirmwareComponent,
643
- target: ProtocolV2RemoteComponentTarget,
644
- features: Features
645
- ) {
646
- const targetVersion = this.getProtocolV2ComponentTargetVersion(release, component, target);
647
- if (!targetVersion) return false;
648
-
649
- const currentVersion = this.getProtocolV2ComponentCurrentVersion(features, target);
650
- const compareResult = compareProtocolV2Versions(currentVersion, targetVersion);
651
- return compareResult !== undefined && compareResult >= 0;
652
- }
653
-
654
1055
  private getProtocolV2ResourceFilePath(path: string) {
655
1056
  if (path.startsWith('vol')) return path;
656
1057
  if (path.startsWith('/')) return `vol0:${path}`;
@@ -673,7 +1074,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
673
1074
  return null;
674
1075
  }
675
1076
 
676
- const chunkSize = this.getProtocolV2FirmwareChunkSize();
1077
+ const chunkSize = this.getProtocolV2FirmwareChunkSize('read');
677
1078
  const chunks: Uint8Array[] = [];
678
1079
  let offset = 0;
679
1080
  while (offset < PROTOCOL_V2_OKPP_HEADER_SIZE) {
@@ -702,177 +1103,271 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
702
1103
  return parseProtocolV2OkppHeader(headerBytes);
703
1104
  }
704
1105
 
705
- private async isProtocolV2ResourcePackageMatched(
706
- pkg: IProtocolV2ResourceManifestPackage,
707
- manifestVersion?: IVersionArray
708
- ) {
709
- try {
710
- const header = await this.readProtocolV2DeviceFileHeader(pkg.path);
711
- if (!header) return false;
712
-
713
- const expectedType = pkg.type ?? 'RESC';
714
- const expectedVersion = pkg.version ?? manifestVersion;
715
- if (header.type !== expectedType) return false;
716
- if (expectedVersion && compareProtocolV2Versions(header.version, expectedVersion) !== 0) {
717
- return false;
718
- }
719
-
720
- const expectedPayloadHash = normalizeProtocolV2Hex(pkg.payloadHash);
721
- if (expectedPayloadHash && header.payloadHash !== expectedPayloadHash) return false;
722
-
723
- const expectedHeaderHash = normalizeProtocolV2Hex(pkg.headerHash);
724
- if (expectedHeaderHash && header.headerHash !== expectedHeaderHash) return false;
725
-
726
- return true;
727
- } catch (error) {
728
- Log.log(`Protocol V2 resource package check failed for ${pkg.path}: `, error);
729
- return false;
1106
+ private async downloadRemoteProtocolV2Component(
1107
+ key: string,
1108
+ component: IProtocolV2FirmwareComponent
1109
+ ): Promise<ProtocolV2RemoteComponentBinary> {
1110
+ const target = this.getRemoteComponentTarget(key, component);
1111
+ if (!component.url) {
1112
+ throw ERRORS.TypedError(
1113
+ HardwareErrorCode.RuntimeError,
1114
+ `Missing Protocol V2 firmware component url: ${key}/${component.target}`
1115
+ );
730
1116
  }
731
- }
732
1117
 
733
- private async isProtocolV2ResourceManifestSatisfied(
734
- manifest: IFirmwareReleaseInfo['resourceManifest']
735
- ) {
736
- if (!manifest?.packages?.length) return false;
737
-
738
- for (const pkg of manifest.packages) {
739
- if (!(await this.isProtocolV2ResourcePackageMatched(pkg, manifest.version))) {
740
- return false;
1118
+ const { binary } = await getSysResourceBinary(component.url);
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
+ );
741
1133
  }
742
1134
  }
743
- return true;
744
- }
745
-
746
- private getProtocolV2ResourceManifest(
747
- release: IFirmwareReleaseInfo,
748
- component: IProtocolV2FirmwareComponent
749
- ) {
750
- return component.resourceManifest ?? release.resourceManifest;
751
- }
752
-
753
- private getProtocolV2ResourceComponentFileName(key: string) {
754
- const safeKey = key.replace(/[^a-z0-9_-]/gi, '_') || 'resource';
755
- return `resource-${safeKey}.bin`;
756
- }
757
-
758
- private async shouldInstallRemoteProtocolV2Component(
759
- release: IFirmwareReleaseInfo,
760
- key: string,
761
- component: IProtocolV2FirmwareComponent,
762
- target: ProtocolV2RemoteComponentTarget,
763
- features: Features
764
- ) {
765
- if (target.kind === 'resource') {
766
- if (
767
- this.params.forcedUpdateRes ||
768
- features.bootloaderMode ||
769
- features.mode === 'bootloader'
770
- ) {
771
- return true;
772
- }
773
- const resourceMatched = await this.isProtocolV2ResourceManifestSatisfied(
774
- this.getProtocolV2ResourceManifest(release, component)
775
- );
776
- if (resourceMatched) {
777
- Log.log(`[FirmwareUpdateV4] skip Protocol V2 resource component ${key}; manifest matched`);
778
- }
779
- return !resourceMatched;
780
- }
781
-
782
- const versionSatisfied = this.isProtocolV2ComponentVersionSatisfied(
783
- release,
784
- component,
785
- target,
786
- features
787
- );
788
- if (versionSatisfied) {
789
- Log.log(`[FirmwareUpdateV4] skip Protocol V2 component ${key}; version is up to date`);
790
- }
791
- return !versionSatisfied;
792
- }
793
-
794
- private async downloadRemoteProtocolV2Component(
795
- key: string,
796
- component: IProtocolV2FirmwareComponent
797
- ): Promise<ProtocolV2RemoteComponentBinary> {
798
- const target = this.getRemoteComponentTarget(key, component);
799
- if (!component.url) {
800
- throw ERRORS.TypedError(
801
- HardwareErrorCode.RuntimeError,
802
- `Missing Protocol V2 firmware component url: ${key}/${component.target}`
803
- );
804
- }
805
-
806
- const { binary } = await getSysResourceBinary(component.url);
807
- return {
808
- ...target,
809
- binary,
810
- };
1135
+ return {
1136
+ ...target,
1137
+ binary,
1138
+ };
811
1139
  }
812
1140
 
813
1141
  private async prepareRemoteProtocolV2Binaries(firmwareType: EFirmwareType, features: Features) {
814
1142
  const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
815
1143
 
816
- const resourceBinaryMap: ProtocolV2TargetBinary[] = [];
817
1144
  let bootloaderBinary: ArrayBuffer | null = null;
818
1145
  const fwBinaryMap: ProtocolV2TargetBinary[] = [];
1146
+ const installItems: ProtocolV2InstallItem[] = [];
819
1147
 
820
1148
  if (!release) {
821
1149
  return {
822
- resourceBinaryMap,
823
1150
  bootloaderBinary,
824
1151
  fwBinaryMap,
1152
+ installItems,
825
1153
  };
826
1154
  }
827
1155
 
828
1156
  const entries = this.getRemoteComponentEntries(release);
1157
+ const targetsToUpdate = new Set(this.params.targetsToUpdate ?? []);
829
1158
 
830
1159
  for (const [key, component] of entries) {
831
- const target = this.getRemoteComponentTarget(key, component);
832
- const shouldInstall = await this.shouldInstallRemoteProtocolV2Component(
833
- release,
834
- key,
835
- component,
836
- target,
837
- features
838
- );
839
- 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)) {
840
1166
  const remoteBinary = await this.downloadRemoteProtocolV2Component(key, component);
841
- if (remoteBinary.kind === 'resource') {
842
- resourceBinaryMap.push({
843
- fileName: this.getProtocolV2ResourceComponentFileName(key),
1167
+ if (remoteBinary.kind === 'bootloader') {
1168
+ bootloaderBinary = remoteBinary.binary;
1169
+ installItems.push({
1170
+ fileName: remoteBinary.fileName,
844
1171
  binary: remoteBinary.binary,
845
1172
  targetId: remoteBinary.targetId,
1173
+ kind: remoteBinary.kind,
846
1174
  });
847
- } else if (remoteBinary.kind === 'bootloader') {
848
- bootloaderBinary = remoteBinary.binary;
849
1175
  } else {
850
- fwBinaryMap.push({
1176
+ const binaryEntry = {
851
1177
  fileName: remoteBinary.fileName,
852
1178
  binary: remoteBinary.binary,
853
1179
  targetId: remoteBinary.targetId,
854
- });
1180
+ };
1181
+ fwBinaryMap.push(binaryEntry);
1182
+ installItems.push({ ...binaryEntry, kind: remoteBinary.kind });
855
1183
  }
856
1184
  }
857
1185
  }
858
1186
 
859
1187
  return {
860
- resourceBinaryMap,
861
1188
  bootloaderBinary,
862
1189
  fwBinaryMap,
1190
+ installItems,
863
1191
  };
864
1192
  }
865
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
+
866
1340
  private isProtocolV2BootloaderMode() {
867
1341
  if (typeof this.device.isBootloader === 'function') {
868
1342
  return this.device.isBootloader();
869
1343
  }
870
- 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';
871
1358
  }
872
1359
 
873
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
+ }
874
1368
  if (this.isProtocolV2BootloaderMode()) {
875
- 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;
876
1371
  return false;
877
1372
  }
878
1373
 
@@ -882,6 +1377,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
882
1377
  this.postTipMessage(FirmwareUpdateTipMessage.GoToBootloaderSuccess);
883
1378
  await wait(1000);
884
1379
  await this.waitForProtocolV2BootloaderMode();
1380
+ this.protocolV2ExecutionInLoader = true;
885
1381
  return true;
886
1382
  } catch (error) {
887
1383
  if (error instanceof HardwareError) {
@@ -906,12 +1402,19 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
906
1402
  commands: this.device.getCommands(),
907
1403
  timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
908
1404
  });
909
- const features = this.device.updateProtocolV2Features(deviceInfo);
910
- 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') {
911
1411
  return features;
912
1412
  }
913
1413
  lastError = new Error('Protocol V2 device is reachable but is not in bootloader mode');
914
1414
  } catch (error) {
1415
+ if (this.isProtocolV2ReconnectIdentityError(error)) {
1416
+ throw error;
1417
+ }
915
1418
  lastError = error;
916
1419
  Log.log('Protocol V2 bootloader mode not ready, polling reconnect: ', error);
917
1420
  }
@@ -927,8 +1430,8 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
927
1430
  }
928
1431
 
929
1432
  /**
930
- * 收集按 DeviceFirmwareTargetType 拆分的显式目标二进制。
931
- * 文件名仅用于 staging 路径展示,target_id 已显式给定。
1433
+ * Collect explicit target binaries grouped by DeviceFirmwareTargetType.
1434
+ * Filenames are display-only staging paths because target_id is explicit.
932
1435
  */
933
1436
  private collectExplicitTargetBinaries() {
934
1437
  const entries: ProtocolV2TargetBinary[] = [];
@@ -964,265 +1467,458 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
964
1467
  return entries;
965
1468
  }
966
1469
 
967
- private async executeProtocolV2Update({
968
- resourceBinaryMap,
969
- fwBinaryMap,
970
- bootloaderBinary,
1470
+ private buildProtocolV2ExecutionPhases({
1471
+ installSources,
1472
+ resourceSources,
971
1473
  }: {
972
- resourceBinaryMap: ProtocolV2TargetBinary[];
973
- fwBinaryMap: ProtocolV2TargetBinary[];
974
- bootloaderBinary: ArrayBuffer | null;
975
- }) {
976
- let totalSize = 0;
977
- let processedSize = 0;
978
- let transferredSize = 0;
979
-
980
- for (const resource of resourceBinaryMap) totalSize += resource.binary.byteLength;
981
- for (const fwbinary of fwBinaryMap) totalSize += fwbinary.binary.byteLength;
982
- if (bootloaderBinary) totalSize += bootloaderBinary.byteLength;
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
+ }
983
1515
 
984
- this.postTipMessage(FirmwareUpdateTipMessage.StartTransferData);
985
- const transferStartTime = Date.now();
986
- const transferTransport = this.getProtocolV2FirmwareTransferTransport();
987
- const chunkSize = this.getProtocolV2FirmwareChunkSize();
988
- const onTransferredBytes = (bytes: number) => {
989
- transferredSize = bytes;
990
- };
991
- Log.log(
992
- `[FirmwareUpdateV4] transfer started transport=${transferTransport} total=${totalSize} bytes chunk=${chunkSize} bytes`
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'
993
1546
  );
1547
+ }
994
1548
 
995
- const resourceTargets: Array<{ target_id: number; path: string }> = [];
996
- const targets: Array<{ target_id: number; path: string }> = [];
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
+ }
997
1561
 
1562
+ private async executeProtocolV2Update({
1563
+ fwBinaryMap,
1564
+ bootloaderBinary,
1565
+ installItems,
1566
+ resourceBundles,
1567
+ }: {
1568
+ fwBinaryMap?: ProtocolV2TargetBinary[];
1569
+ bootloaderBinary?: ArrayBuffer | null;
1570
+ installItems?: ProtocolV2InstallItem[];
1571
+ resourceBundles?: ProtocolV2ResourceBundleBinary[];
1572
+ }) {
1573
+ const memoryInstallItems =
1574
+ installItems ??
1575
+ this.buildProtocolV2InstallItems({
1576
+ fwBinaryMap: fwBinaryMap ?? [],
1577
+ bootloaderBinary: bootloaderBinary ?? null,
1578
+ });
998
1579
  try {
999
- for (const resource of resourceBinaryMap) {
1000
- const resourceFilePath = `${PROTOCOL_V2_FIRMWARE_STAGING_VOLUME}${resource.fileName}`;
1001
- Log.log(
1002
- `[FirmwareUpdateV4] staging resource via FilesystemFileWrite target=${resource.targetId} path=${resourceFilePath} bytes=${resource.binary.byteLength}`
1003
- );
1004
- processedSize = await this.protocolV2CommonUpdateProcess({
1005
- payload: resource.binary,
1006
- filePath: resourceFilePath,
1007
- processedSize,
1008
- totalSize,
1009
- onTransferredBytes,
1010
- });
1011
- transferredSize = processedSize;
1012
- resourceTargets.push({
1013
- target_id: resource.targetId,
1014
- path: resourceFilePath,
1015
- });
1016
- }
1017
-
1018
- if (bootloaderBinary) {
1019
- const bootloaderPath = `${PROTOCOL_V2_FIRMWARE_STAGING_VOLUME}bootloader.bin`;
1020
- Log.log(
1021
- `[FirmwareUpdateV4] staging bootloader via FilesystemFileWrite target=${ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER} path=${bootloaderPath} bytes=${bootloaderBinary.byteLength}`
1022
- );
1023
- processedSize = await this.protocolV2CommonUpdateProcess({
1024
- payload: bootloaderBinary,
1025
- filePath: bootloaderPath,
1026
- processedSize,
1027
- totalSize,
1028
- onTransferredBytes,
1029
- });
1030
- transferredSize = processedSize;
1031
- targets.push({
1032
- target_id: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER,
1033
- path: bootloaderPath,
1034
- });
1035
- }
1036
-
1037
- for (const fwbinary of fwBinaryMap) {
1038
- const firmwarePath = `${PROTOCOL_V2_FIRMWARE_STAGING_VOLUME}${fwbinary.fileName}`;
1039
- Log.log(
1040
- `[FirmwareUpdateV4] staging firmware via FilesystemFileWrite target=${fwbinary.targetId} path=${firmwarePath} bytes=${fwbinary.binary.byteLength}`
1041
- );
1042
- processedSize = await this.protocolV2CommonUpdateProcess({
1043
- payload: fwbinary.binary,
1044
- filePath: firmwarePath,
1045
- processedSize,
1046
- totalSize,
1047
- onTransferredBytes,
1048
- });
1049
- transferredSize = processedSize;
1050
- targets.push({
1051
- target_id: fwbinary.targetId,
1052
- path: firmwarePath,
1053
- });
1054
- }
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
+ }))
1587
+ );
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
+ }))
1597
+ );
1598
+ return await this.executeProtocolV2Phases({
1599
+ installSources,
1600
+ resourceSources,
1601
+ });
1602
+ } finally {
1603
+ await this.closeProtocolV2PreparedSources();
1604
+ }
1605
+ }
1055
1606
 
1056
- if (totalSize > 0) {
1057
- this.postProgressMessage(100, 'transferData');
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`);
1616
+ } else {
1617
+ resourcesToSync.push(resource);
1618
+ totalSize += resource.source.size;
1058
1619
  }
1620
+ }
1059
1621
 
1060
- const elapsedMs = Date.now() - transferStartTime;
1061
- Log.log(
1062
- `[FirmwareUpdateV4] transfer finished transport=${transferTransport} bytes=${totalSize} elapsed=${(
1063
- elapsedMs / 1000
1064
- ).toFixed(2)}s speed=${formatProtocolV2TransferSpeed(totalSize, elapsedMs)} KB/s`
1065
- );
1066
- } catch (error) {
1067
- const elapsedMs = Date.now() - transferStartTime;
1068
- Log.warn(
1069
- `[FirmwareUpdateV4] transfer failed transport=${transferTransport} bytes=${transferredSize}/${totalSize} elapsed=${(
1070
- elapsedMs / 1000
1071
- ).toFixed(2)}s speed=${formatProtocolV2TransferSpeed(transferredSize, elapsedMs)} KB/s`
1072
- );
1073
- throw error;
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);
1074
1632
  }
1075
1633
 
1076
- this.postTipMessage(FirmwareUpdateTipMessage.ConfirmOnDevice);
1077
- for (const resourceTarget of resourceTargets) {
1078
- const resourceTargetsBatch = [resourceTarget];
1079
- Log.log(
1080
- `[FirmwareUpdateV4] DeviceFirmwareUpdateRequest resources=${JSON.stringify(
1081
- resourceTargetsBatch
1082
- )}`
1083
- );
1084
- const startResponse = await this.protocolV2StartFirmwareUpdate({
1085
- targets: resourceTargetsBatch,
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,
1086
1647
  });
1087
- await this.waitForProtocolV2FirmwareUpdateComplete(resourceTargetsBatch, startResponse);
1088
1648
  }
1089
- if (targets.length > 0) {
1090
- Log.log(`[FirmwareUpdateV4] DeviceFirmwareUpdateRequest targets=${JSON.stringify(targets)}`);
1091
- const startResponse = await this.protocolV2StartFirmwareUpdate({ targets });
1092
- await this.waitForProtocolV2FirmwareUpdateComplete(targets, startResponse);
1649
+
1650
+ if (totalSize > 0) {
1651
+ this.postProgressMessage(100, 'transferData');
1652
+ }
1653
+ if (stagedInstallTargets.length === 0) {
1654
+ return;
1093
1655
  }
1094
- }
1095
1656
 
1096
- private async queryProtocolV2FirmwareUpdateStatus() {
1097
- const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
1098
- return typedCall(
1099
- 'DeviceFirmwareUpdateStatusGet',
1100
- 'DeviceFirmwareUpdateStatus',
1101
- {},
1102
- {
1103
- timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
1104
- }
1105
- );
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);
1106
1664
  }
1107
1665
 
1108
- private async pingProtocolV2Device() {
1109
- const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
1110
- await typedCall(
1111
- 'Ping',
1112
- 'Success',
1113
- { message: 'firmware-update' },
1114
- {
1115
- 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
+ }
1116
1716
  }
1717
+ }
1718
+ throw ERRORS.TypedError(
1719
+ HardwareErrorCode.EmmcFileWriteFirmwareError,
1720
+ `transfer data error: ${getProtocolV2UnknownErrorText(lastError)}`
1117
1721
  );
1118
1722
  }
1119
1723
 
1120
- private isProtocolV2NormalModeFeatures(features?: Features | null) {
1121
- return !!features && !features.bootloaderMode && features.mode !== 'bootloader';
1724
+ private getProtocolV2InstallItemStagingPath(item: Pick<ProtocolV2InstallItem, 'fileName'>) {
1725
+ return `${PROTOCOL_V2_FIRMWARE_STAGING_VOLUME}${item.fileName}`;
1122
1726
  }
1123
1727
 
1124
- private async probeProtocolV2NormalMode() {
1125
- const deviceInfo = await requestProtocolV2DeviceInfo({
1126
- commands: this.device.getCommands(),
1127
- timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
1128
- request: PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST,
1129
- });
1130
- const features = this.device.updateProtocolV2Features(deviceInfo);
1131
- if (this.isProtocolV2NormalModeFeatures(features)) {
1132
- Log.log('Protocol V2 firmware install finished; device is back in normal mode');
1133
- 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
+ );
1134
1738
  }
1135
- return false;
1136
1739
  }
1137
1740
 
1138
1741
  private assertProtocolV2TargetStatus(
1139
1742
  statusTargets: ProtocolV2FirmwareUpdateStatusTarget[],
1140
- expectedTargetIds: Set<number>
1743
+ expectedTargetIds: Set<number>,
1744
+ expectedPaths = new Map<number, string>()
1141
1745
  ) {
1746
+ Log.log(
1747
+ `[FirmwareUpdateV4] DeviceFirmwareUpdateStatus records=${JSON.stringify(statusTargets)}`
1748
+ );
1142
1749
  const failedTarget = statusTargets.find(
1143
1750
  target =>
1144
1751
  expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1) &&
1145
1752
  isProtocolV2TargetStatusFailed(target.status)
1146
1753
  );
1147
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}`);
1148
1762
  throw ERRORS.TypedError(
1149
1763
  HardwareErrorCode.FirmwareError,
1150
- `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
+ }`
1151
1769
  );
1152
1770
  }
1153
1771
 
1154
- const completedTargets = statusTargets.filter(
1155
- target =>
1156
- expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1) &&
1157
- isProtocolV2TargetStatusFinished(target.status)
1772
+ const matchingTargets = statusTargets.filter(target =>
1773
+ expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1)
1158
1774
  );
1159
- if (completedTargets.length === expectedTargetIds.size && expectedTargetIds.size > 0) {
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)
1792
+ );
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');
1160
1824
  return true;
1161
1825
  }
1162
1826
 
1163
- const inProgressTarget = statusTargets.find(
1164
- target =>
1165
- expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1) &&
1827
+ if (expectedTargetIds.size > 0 && matchingTargets.length > 0) {
1828
+ const hasInProgressTarget = matchingTargets.some(target =>
1166
1829
  isProtocolV2TargetStatusInProgress(target.status)
1167
- );
1168
- if (inProgressTarget) {
1169
- 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');
1170
1838
  }
1171
1839
 
1172
1840
  return false;
1173
1841
  }
1174
1842
 
1175
1843
  private async waitForProtocolV2FirmwareUpdateComplete(
1176
- targets: Array<{ target_id: number; path: string }>,
1177
- startResponse?: ProtocolV2FirmwareUpdateStartResponse
1844
+ targets: Array<{ target_id: number; path: string }>
1178
1845
  ) {
1846
+ this.protocolV2FinalStatusVerified = false;
1179
1847
  const expectedTargetIds = new Set(targets.map(target => target.target_id));
1180
- if (startResponse?.type === 'DeviceFirmwareUpdateStatus') {
1181
- const statusTargets = (startResponse.message.records ??
1182
- []) as ProtocolV2FirmwareUpdateStatusTarget[];
1183
- if (this.assertProtocolV2TargetStatus(statusTargets, expectedTargetIds)) {
1184
- return;
1185
- }
1186
- }
1187
-
1848
+ const expectedPaths = new Map(targets.map(target => [target.target_id, target.path]));
1188
1849
  const startTime = Date.now();
1189
1850
  let lastError: unknown;
1190
1851
 
1191
1852
  while (Date.now() - startTime < PROTOCOL_V2_INSTALL_TIMEOUT) {
1192
1853
  try {
1193
- const statusRes = await this.queryProtocolV2FirmwareUpdateStatus();
1194
- const statusTargets = (statusRes.message.records ??
1195
- []) as ProtocolV2FirmwareUpdateStatusTarget[];
1196
- if (this.assertProtocolV2TargetStatus(statusTargets, expectedTargetIds)) {
1197
- 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
+ }
1198
1906
  }
1199
1907
  } catch (error) {
1200
1908
  lastError = error;
1909
+ if (this.isProtocolV2ReconnectIdentityError(error)) {
1910
+ throw error;
1911
+ }
1201
1912
  if (error instanceof HardwareError && error.errorCode === HardwareErrorCode.FirmwareError) {
1202
1913
  throw error;
1203
1914
  }
1204
- Log.log('Protocol V2 firmware install status polling failed: ', error);
1205
- if (isProtocolV2PollingTransientError(error)) {
1206
- try {
1207
- await this.reconnectProtocolV2Device();
1208
- if (await this.probeProtocolV2NormalMode()) {
1209
- return;
1210
- }
1211
- } catch (reconnectError) {
1212
- lastError = reconnectError;
1213
- Log.log(
1214
- 'Protocol V2 firmware install reconnect/normal-mode probe failed: ',
1215
- reconnectError
1216
- );
1217
- }
1218
- try {
1219
- await this.pingProtocolV2Device();
1220
- Log.log('Protocol V2 firmware status unavailable, Ping is ready');
1221
- } catch (pingError) {
1222
- lastError = pingError;
1223
- Log.log('Protocol V2 firmware install Ping polling failed: ', pingError);
1224
- }
1915
+ if (
1916
+ error instanceof HardwareError &&
1917
+ error.params?.firmwareUpdateCode === 'FirmwareInstallStatusUnavailable'
1918
+ ) {
1919
+ throw error;
1225
1920
  }
1921
+ Log.log('Protocol V2 firmware install device readiness probe failed: ', error);
1226
1922
  }
1227
1923
  await wait(1000);
1228
1924
  }
@@ -1234,27 +1930,31 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1234
1930
  }
1235
1931
 
1236
1932
  private async exitProtocolV2BootloaderToNormal() {
1237
- this.postTipMessage(FirmwareUpdateTipMessage.SwitchFirmwareReconnectDevice);
1238
- try {
1239
- await this.reconnectProtocolV2Device();
1240
- if (await this.probeProtocolV2NormalMode()) {
1241
- Log.log('Protocol V2 device is already in normal mode, skip normal reboot');
1242
- return;
1243
- }
1244
- } catch (error) {
1245
- Log.log('Protocol V2 normal-mode probe before reboot failed: ', error);
1246
- }
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.
1247
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;
1248
1947
  }
1249
1948
 
1250
1949
  private async waitForProtocolV2FinalFeatures() {
1251
1950
  const features = await this.waitForProtocolV2ReconnectAndFeatures(
1252
- PROTOCOL_V2_BOOTLOADER_RECONNECT_TIMEOUT
1951
+ PROTOCOL_V2_FINAL_RECONNECT_TIMEOUT
1253
1952
  );
1254
1953
 
1255
1954
  const bootloaderVersion = getDeviceBootloaderVersion(features).join('.');
1256
1955
  const bleVersion = getDeviceBLEFirmwareVersion(features).join('.');
1257
1956
  const firmwareVersion = getDeviceFirmwareVersion(features).join('.');
1957
+ this.protocolV2LatestFinalFeatures = features;
1258
1958
  if (firmwareVersion === '0.0.0') {
1259
1959
  Log.warn(
1260
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.'
@@ -1268,6 +1968,17 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1268
1968
  };
1269
1969
  }
1270
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
+
1271
1982
  private async waitForProtocolV2ReconnectAndFeatures(timeout: number) {
1272
1983
  const startTime = Date.now();
1273
1984
  let lastError: unknown;
@@ -1278,11 +1989,15 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1278
1989
  const deviceInfo = await requestProtocolV2DeviceInfo({
1279
1990
  commands: this.device.getCommands(),
1280
1991
  timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
1281
- // 更新完成判定只需要各 target 版本号;scope 与请求内容保持一致
1992
+ // Completion needs target versions only; keep scope aligned with the request.
1282
1993
  request: PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST,
1283
1994
  });
1284
- const features = this.device.updateProtocolV2Features(deviceInfo);
1285
- 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) {
1286
2001
  throw ERRORS.TypedError(
1287
2002
  HardwareErrorCode.DeviceNotFound,
1288
2003
  'Protocol V2 device is still in bootloader mode'
@@ -1290,6 +2005,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1290
2005
  }
1291
2006
  return features;
1292
2007
  } catch (error) {
2008
+ if (this.isProtocolV2ReconnectIdentityError(error)) {
2009
+ throw error;
2010
+ }
1293
2011
  lastError = error;
1294
2012
  Log.log('Protocol V2 normal mode not ready, polling Ping: ', error);
1295
2013
  await wait(1000);
@@ -1324,15 +2042,14 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1324
2042
  },
1325
2043
  true
1326
2044
  );
1327
- await this.device.acquire(PROTOCOL_V2_CONNECT_PROTOCOL, { throwOnRunPromiseError: true });
2045
+ await this.ensureProtocolV2DeviceAcquired();
1328
2046
  this.device.commands.disposed = false;
1329
2047
  this.device.getCommands().mainId = this.device.mainId ?? '';
1330
- await this.device.initialize();
1331
2048
  return;
1332
2049
  }
1333
2050
 
1334
- // App bootloader 序列号暂时可能不一致。V4 升级重连阶段只接受唯一枚举设备,
1335
- // 避免继续按旧 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.
1336
2053
  const { deviceList } = await DevicePool.getDevices(devicesDescriptor, undefined, {
1337
2054
  connectProtocol: PROTOCOL_V2_CONNECT_PROTOCOL,
1338
2055
  });
@@ -1345,86 +2062,30 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1345
2062
  deviceList[0].getConnectId()
1346
2063
  );
1347
2064
  this.device.updateFromCache(deviceList[0]);
1348
- await this.device.acquire(PROTOCOL_V2_CONNECT_PROTOCOL, { throwOnRunPromiseError: true });
2065
+ await this.ensureProtocolV2DeviceAcquired();
1349
2066
  this.device.commands.disposed = false;
1350
2067
  this.device.getCommands().mainId = this.device.mainId ?? '';
1351
- await this.device.initialize();
1352
2068
  }
1353
2069
 
1354
- private async protocolV2CommonUpdateProcess({
1355
- payload,
1356
- filePath,
1357
- processedSize,
1358
- totalSize,
1359
- onTransferredBytes,
1360
- }: PROTO.FirmwareUpload & {
1361
- filePath: string;
1362
- processedSize?: number;
1363
- totalSize?: number;
1364
- onTransferredBytes?: (transferredBytes: number) => void;
1365
- }) {
1366
- const chunkSize = this.getProtocolV2FirmwareChunkSize();
1367
- let offset = 0;
1368
- const getUploadProgress = (fileOffset: number) => {
1369
- if (totalSize !== undefined && processedSize !== undefined) {
1370
- return Math.min(Math.ceil(((processedSize + fileOffset) / totalSize) * 100), 99);
1371
- }
1372
- return Math.min(Math.ceil((fileOffset / payload.byteLength) * 100), 99);
1373
- };
1374
-
1375
- while (offset < payload.byteLength) {
1376
- const chunkEnd = Math.min(offset + chunkSize, payload.byteLength);
1377
- const chunkLength = chunkEnd - offset;
1378
- const chunk = payload.slice(offset, chunkEnd);
1379
- const overwrite = offset === 0;
1380
- const progress = getProtocolV2DeviceTransferProgress(
1381
- (processedSize ?? 0) + offset,
1382
- (processedSize ?? 0) + chunkEnd,
1383
- totalSize ?? payload.byteLength
1384
- );
1385
-
1386
- const writeRes = await this.fileWriteWithRetry(
1387
- filePath,
1388
- payload.byteLength,
1389
- offset,
1390
- chunk,
1391
- overwrite,
1392
- progress
1393
- );
1394
- const processedByte = Number(writeRes.message.processed_byte);
1395
- const nextOffset =
1396
- Number.isFinite(processedByte) && processedByte > offset
1397
- ? processedByte
1398
- : offset + chunkLength;
1399
- if (nextOffset <= offset || nextOffset > payload.byteLength) {
1400
- throw ERRORS.TypedError(
1401
- HardwareErrorCode.EmmcFileWriteFirmwareError,
1402
- `invalid processed_byte ${writeRes.message.processed_byte} for offset ${offset}`
1403
- );
1404
- }
1405
- offset = nextOffset;
1406
- onTransferredBytes?.((processedSize ?? 0) + offset);
1407
- 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;
1408
2078
  }
1409
-
1410
- return totalSize !== undefined ? (processedSize ?? 0) + payload.byteLength : 0;
2079
+ await this.device.acquire(PROTOCOL_V2_CONNECT_PROTOCOL, {
2080
+ throwOnRunPromiseError: true,
2081
+ });
1411
2082
  }
1412
2083
 
1413
- private getProtocolV2FirmwareTransferTransport() {
1414
- const env = DataManager.getSettings('env');
1415
- if (env && DataManager.isBleConnect(env)) {
1416
- return 'BLE';
1417
- }
1418
- if (
1419
- env &&
1420
- (DataManager.isBrowserWebUsb(env) || DataManager.isDesktopWebUsb(env) || env === 'web')
1421
- ) {
1422
- return 'WebUSB';
1423
- }
1424
- return env ?? 'unknown';
2084
+ private isProtocolV2ReconnectIdentityError(error: unknown) {
2085
+ return this.normalizeErrorMessage(error).includes('Protocol V2 reconnect physical identity');
1425
2086
  }
1426
2087
 
1427
- private async fileWriteWithRetry(
2088
+ private async fileWriteChunk(
1428
2089
  filePath: string,
1429
2090
  totalFileSize: number,
1430
2091
  offset: number,
@@ -1432,57 +2093,38 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1432
2093
  overwrite: boolean,
1433
2094
  progress: number | null
1434
2095
  ): Promise<TypedResponseMessage<'FilesystemFile'>> {
1435
- const writeFunc = async () => {
1436
- const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
1437
- const writeRes = await typedCall('FilesystemFileWrite', 'FilesystemFile', {
1438
- file: {
1439
- path: filePath,
1440
- offset,
1441
- total_size: totalFileSize,
1442
- data: chunk,
1443
- },
1444
- overwrite,
1445
- append: false,
1446
- ui_percentage: progress ?? undefined,
1447
- });
1448
- if (writeRes.type !== 'FilesystemFile') {
1449
- if ((writeRes as any).type === 'CallMethodError') {
1450
- if (((writeRes as any).message.error ?? '').indexOf(SESSION_ERROR) > -1) {
1451
- throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, SESSION_ERROR);
1452
- }
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);
1453
2112
  }
1454
- throw ERRORS.TypedError(
1455
- HardwareErrorCode.EmmcFileWriteFirmwareError,
1456
- 'transfer data error'
1457
- );
1458
2113
  }
1459
- return writeRes;
1460
- };
2114
+ throw ERRORS.TypedError(HardwareErrorCode.EmmcFileWriteFirmwareError, 'transfer data error');
2115
+ }
2116
+ return writeRes;
2117
+ }
1461
2118
 
1462
- let retryCount = 10;
1463
- while (retryCount > 0) {
1464
- try {
1465
- const result = await writeFunc();
1466
- return result;
1467
- } catch (error) {
1468
- Log.error(`fileWrite error: `, error);
1469
- retryCount--;
1470
- if (retryCount === 0) {
1471
- throw ERRORS.TypedError(
1472
- HardwareErrorCode.EmmcFileWriteFirmwareError,
1473
- 'transfer data error'
1474
- );
1475
- }
1476
- const env = DataManager.getSettings('env');
1477
- if (DataManager.isBleConnect(env)) {
1478
- await wait(3000);
1479
- await this.acquireProtocolV2BleDevice();
1480
- await this.device.initialize();
1481
- }
1482
- await wait(2000);
1483
- }
2119
+ private async recoverProtocolV2FileTransfer() {
2120
+ const env = DataManager.getSettings('env');
2121
+ if (DataManager.isBleConnect(env)) {
2122
+ await wait(3000);
1484
2123
  }
1485
- 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);
1486
2128
  }
1487
2129
 
1488
2130
  private async acquireProtocolV2BleDevice() {
@@ -1500,35 +2142,16 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1500
2142
  targets: Array<{ target_id: number; path: string }>;
1501
2143
  }) {
1502
2144
  const commands = this.device.getCommands();
1503
- let response: ProtocolV2FirmwareUpdateStartResponse;
1504
- try {
1505
- response = await commands.typedCall(
1506
- 'DeviceFirmwareUpdateRequest',
1507
- PROTOCOL_V2_FIRMWARE_UPDATE_RESPONSE_TYPES,
1508
- {
1509
- targets,
1510
- },
1511
- {
1512
- intermediateTypes: ['DeviceFirmwareUpdateStatus'],
1513
- timeoutMs: PROTOCOL_V2_START_UPDATE_TIMEOUT,
1514
- onIntermediateResponse: (response: { type?: string }) => {
1515
- if (response.type === 'DeviceFirmwareUpdateStatus') {
1516
- this.postProgressMessage(99, 'installingFirmware');
1517
- }
1518
- },
1519
- }
1520
- );
1521
- } catch (error) {
1522
- if (isProtocolV2StartUpdateTransientError(error)) {
1523
- Log.log(
1524
- 'Protocol V2 firmware update request did not return; continue status polling',
1525
- error
1526
- );
1527
- } else {
1528
- throw error;
1529
- }
1530
- }
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.
1531
2153
  this.postTipMessage(FirmwareUpdateTipMessage.FirmwareUpdating);
2154
+ this.postProgressMessage(0, 'installingFirmware');
1532
2155
  return response;
1533
2156
  }
1534
2157
 
@@ -1538,9 +2161,11 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1538
2161
  const res = await typedCall('DeviceReboot', 'Success', {
1539
2162
  reboot_type: rebootType,
1540
2163
  });
2164
+ this.device.markProtocolV2Reboot(rebootType);
1541
2165
  return res.message;
1542
2166
  } catch (error) {
1543
2167
  if (isProtocolV2DeviceDisconnectedError(error) || isProtocolV2ReconnectProbeError(error)) {
2168
+ this.device.markProtocolV2Reboot(rebootType);
1544
2169
  return { message: 'Device rebooted successfully' };
1545
2170
  }
1546
2171
  throw error;