@onekeyfe/hd-core 1.2.0-alpha.7 → 1.2.0-alpha.71

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