@onekeyfe/hd-core 1.2.0-alpha.8 → 1.2.0-alpha.81

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