@onekeyfe/hd-core 1.1.34-alpha.2 → 1.1.34-alpha.3

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