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

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