@onekeyfe/hd-core 1.2.0-alpha.9 → 1.2.0-alpha.90
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.
- package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +390 -0
- package/__tests__/DeviceCommands.test.ts +563 -0
- package/__tests__/DeviceEventRegistration.test.ts +49 -0
- package/__tests__/chain-params-onekey-compatibility.test.ts +229 -0
- package/__tests__/check-all-firmware-release-protocol-v2.test.ts +825 -0
- package/__tests__/check-firmware-release-protocol-v2.test.ts +199 -0
- package/__tests__/connectSettings.test.ts +45 -5
- package/__tests__/core-dispose.test.ts +34 -0
- package/__tests__/core-error-output.test.ts +39 -0
- package/__tests__/core-initialization.test.ts +23 -0
- package/__tests__/device-connector-protocol.test.ts +81 -0
- package/__tests__/device-features-state.test.ts +78 -0
- package/__tests__/device-identity.test.ts +52 -0
- package/__tests__/device-initialize-timeout.test.ts +56 -0
- package/__tests__/device-lifecycle-events.test.ts +351 -0
- package/__tests__/device-pool-state.test.ts +166 -0
- package/__tests__/device-settings.test.ts +437 -0
- package/__tests__/device-state-contract.test.ts +28 -0
- package/__tests__/device-state-events.test.ts +286 -0
- package/__tests__/device-state-mapper.test.ts +288 -0
- package/__tests__/device-state-projector.test.ts +114 -0
- package/__tests__/device-state-store.test.ts +109 -0
- package/__tests__/device-utils.test.ts +154 -0
- package/__tests__/device-wallet-session-store.test.ts +348 -1
- package/__tests__/deviceFeaturesUtils.test.ts +39 -0
- package/__tests__/devicePoolBootloaderPath.test.ts +64 -0
- package/__tests__/deviceSettings.test.ts +113 -0
- package/__tests__/deviceUploadNft.test.ts +316 -0
- package/__tests__/evmLedgerLegacySafety.test.ts +12 -6
- package/__tests__/evmSignTransaction.test.ts +69 -0
- package/__tests__/evmSignTypedData.test.ts +80 -0
- package/__tests__/filesystemValidation.test.ts +53 -0
- package/__tests__/firmware-memory-host.test.ts +112 -0
- package/__tests__/firmware-update/check-all-firmware-release.test.ts +175 -0
- package/__tests__/firmware-update/device-update-bootloader.test.ts +91 -0
- package/__tests__/firmware-update/firmware-artifact-source.test.ts +119 -0
- package/__tests__/firmware-update/firmware-host-binding.test.ts +79 -0
- package/__tests__/firmware-update/firmware-preparation-error.test.ts +27 -0
- package/__tests__/firmware-update/firmware-prepared-host-digest.test.ts +209 -0
- package/__tests__/firmware-update/firmware-update-bootloader-poll.test.ts +200 -0
- package/__tests__/firmware-update/firmware-update-capabilities.test.ts +13 -0
- package/__tests__/firmware-update/firmware-update-plan-bootloader-identity.test.ts +394 -0
- package/__tests__/firmware-update/firmware-update-plan.test.ts +654 -0
- package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +283 -0
- package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +457 -0
- package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +121 -0
- package/__tests__/firmware-update/firmware-upload-source.test.ts +70 -0
- package/__tests__/get-device-state.test.ts +578 -0
- package/__tests__/homescreen.test.ts +81 -0
- package/__tests__/kaspa-public-types.type-test.ts +16 -0
- package/__tests__/kaspa-use-tweak-pro2.test.ts +20 -0
- package/__tests__/kaspaSignTransaction.test.ts +578 -0
- package/__tests__/known-device-public-types.type-test.ts +3 -0
- package/__tests__/logBlockEvent.test.ts +128 -1
- package/__tests__/method-protocol-support.test.ts +144 -0
- package/__tests__/networkUtils.test.ts +406 -0
- package/__tests__/open-wallet-session-error-response.test.ts +43 -0
- package/__tests__/open-wallet-session.test.ts +1899 -0
- package/__tests__/pro2Nft.test.ts +108 -0
- package/__tests__/pro2Wallpaper.test.ts +39 -0
- package/__tests__/protocol-binding.test.ts +89 -0
- package/__tests__/protocol-v2-bootloader-mode.test.ts +37 -0
- package/__tests__/protocol-v2-firmware-targets.test.ts +66 -0
- package/__tests__/protocol-v2-ping.test.ts +28 -0
- package/__tests__/protocol-v2-resources.test.ts +270 -0
- package/__tests__/protocol-v2-ui-lifecycle.test.ts +91 -0
- package/__tests__/protocol-v2-unlock-policy.test.ts +308 -0
- package/__tests__/protocol-v2.test.ts +6669 -1759
- package/__tests__/protocolV2FileWrite.test.ts +285 -0
- package/__tests__/protocolV2UiInteraction.test.ts +329 -0
- package/__tests__/public-device-state-api.test.ts +275 -0
- package/__tests__/public-pro2-api-boundary.test.ts +107 -0
- package/__tests__/refresh-device-state.test.ts +101 -0
- package/__tests__/request-context-logging.test.ts +16 -0
- package/__tests__/search-devices.test.ts +145 -0
- package/__tests__/tron-sign-message-init.test.ts +74 -0
- package/__tests__/uiProgressThrottle.test.ts +74 -0
- package/__tests__/uiPromiseRegistry.test.ts +115 -0
- package/dist/api/BaseMethod.d.ts +16 -1
- package/dist/api/BaseMethod.d.ts.map +1 -1
- package/dist/api/CheckAllFirmwareRelease.d.ts +25 -1
- package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
- package/dist/api/CheckBLEFirmwareRelease.d.ts +3 -2
- package/dist/api/CheckBLEFirmwareRelease.d.ts.map +1 -1
- package/dist/api/CheckBootloaderRelease.d.ts +2 -1
- package/dist/api/CheckBootloaderRelease.d.ts.map +1 -1
- package/dist/api/CheckFirmwareRelease.d.ts +3 -2
- package/dist/api/CheckFirmwareRelease.d.ts.map +1 -1
- package/dist/api/CipherKeyValue.d.ts +1 -0
- package/dist/api/CipherKeyValue.d.ts.map +1 -1
- package/dist/api/ClearSessionCache.d.ts.map +1 -1
- package/dist/api/DetectDeviceConnectProtocol.d.ts +7 -0
- package/dist/api/DetectDeviceConnectProtocol.d.ts.map +1 -0
- package/dist/api/DirList.d.ts +1 -0
- package/dist/api/DirList.d.ts.map +1 -1
- package/dist/api/DirMake.d.ts +1 -0
- package/dist/api/DirMake.d.ts.map +1 -1
- package/dist/api/DirRemove.d.ts +1 -0
- package/dist/api/DirRemove.d.ts.map +1 -1
- package/dist/api/FileDelete.d.ts +1 -0
- package/dist/api/FileDelete.d.ts.map +1 -1
- package/dist/api/FileRead.d.ts +1 -0
- package/dist/api/FileRead.d.ts.map +1 -1
- package/dist/api/FileWrite.d.ts +2 -0
- package/dist/api/FileWrite.d.ts.map +1 -1
- package/dist/api/FirmwareUpdate.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV2.d.ts +12 -2
- package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV3.d.ts +7 -3
- package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +46 -16
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/GetDeviceState.d.ts +8 -0
- package/dist/api/GetDeviceState.d.ts.map +1 -0
- package/dist/api/GetFeatures.d.ts +1 -1
- package/dist/api/GetFeatures.d.ts.map +1 -1
- package/dist/api/GetOnekeyFeatures.d.ts.map +1 -1
- package/dist/api/GetPassphraseState.d.ts +2 -6
- package/dist/api/GetPassphraseState.d.ts.map +1 -1
- package/dist/api/OpenWalletSession.d.ts +8 -0
- package/dist/api/OpenWalletSession.d.ts.map +1 -0
- package/dist/api/PathInfo.d.ts +1 -0
- package/dist/api/PathInfo.d.ts.map +1 -1
- package/dist/api/PromptWebDeviceAccess.d.ts.map +1 -1
- package/dist/api/SearchDevices.d.ts +3 -0
- package/dist/api/SearchDevices.d.ts.map +1 -1
- package/dist/api/UploadPortfolio.d.ts +17 -0
- package/dist/api/UploadPortfolio.d.ts.map +1 -0
- package/dist/api/alephium/AlephiumGetAddress.d.ts.map +1 -1
- package/dist/api/alephium/AlephiumSignMessage.d.ts.map +1 -1
- package/dist/api/alephium/AlephiumSignTransaction.d.ts.map +1 -1
- package/dist/api/algo/AlgoGetAddress.d.ts +1 -0
- package/dist/api/algo/AlgoGetAddress.d.ts.map +1 -1
- package/dist/api/algo/AlgoSignTransaction.d.ts +1 -0
- package/dist/api/algo/AlgoSignTransaction.d.ts.map +1 -1
- package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts +1 -0
- package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
- package/dist/api/aptos/AptosGetAddress.d.ts +1 -0
- package/dist/api/aptos/AptosGetAddress.d.ts.map +1 -1
- package/dist/api/aptos/AptosGetPublicKey.d.ts +1 -0
- package/dist/api/aptos/AptosGetPublicKey.d.ts.map +1 -1
- package/dist/api/aptos/AptosSignInMessage.d.ts +1 -0
- package/dist/api/aptos/AptosSignInMessage.d.ts.map +1 -1
- package/dist/api/aptos/AptosSignMessage.d.ts +1 -0
- package/dist/api/aptos/AptosSignMessage.d.ts.map +1 -1
- package/dist/api/aptos/AptosSignTransaction.d.ts +1 -0
- package/dist/api/aptos/AptosSignTransaction.d.ts.map +1 -1
- package/dist/api/benfen/BenfenGetAddress.d.ts.map +1 -1
- package/dist/api/benfen/BenfenGetPublicKey.d.ts.map +1 -1
- package/dist/api/benfen/BenfenSignMessage.d.ts.map +1 -1
- package/dist/api/benfen/BenfenSignTransaction.d.ts.map +1 -1
- package/dist/api/btc/BTCGetAddress.d.ts +1 -0
- package/dist/api/btc/BTCGetAddress.d.ts.map +1 -1
- package/dist/api/btc/BTCGetPublicKey.d.ts +1 -0
- package/dist/api/btc/BTCGetPublicKey.d.ts.map +1 -1
- package/dist/api/btc/BTCSignMessage.d.ts +1 -0
- package/dist/api/btc/BTCSignMessage.d.ts.map +1 -1
- package/dist/api/btc/BTCSignPsbt.d.ts +1 -0
- package/dist/api/btc/BTCSignPsbt.d.ts.map +1 -1
- package/dist/api/btc/BTCSignTransaction.d.ts +1 -0
- package/dist/api/btc/BTCSignTransaction.d.ts.map +1 -1
- package/dist/api/btc/BTCVerifyMessage.d.ts +1 -0
- package/dist/api/btc/BTCVerifyMessage.d.ts.map +1 -1
- package/dist/api/btc/helpers/versionLimit.d.ts +2 -0
- package/dist/api/btc/helpers/versionLimit.d.ts.map +1 -1
- package/dist/api/cardano/CardanoGetAddress.d.ts +1 -0
- package/dist/api/cardano/CardanoGetAddress.d.ts.map +1 -1
- package/dist/api/cardano/CardanoGetPublicKey.d.ts +1 -0
- package/dist/api/cardano/CardanoGetPublicKey.d.ts.map +1 -1
- package/dist/api/cardano/CardanoSignMessage.d.ts +1 -0
- package/dist/api/cardano/CardanoSignMessage.d.ts.map +1 -1
- package/dist/api/cardano/CardanoSignTransaction.d.ts +1 -0
- package/dist/api/cardano/CardanoSignTransaction.d.ts.map +1 -1
- package/dist/api/conflux/ConfluxGetAddress.d.ts +2 -0
- package/dist/api/conflux/ConfluxGetAddress.d.ts.map +1 -1
- package/dist/api/conflux/ConfluxSignMessage.d.ts +2 -0
- package/dist/api/conflux/ConfluxSignMessage.d.ts.map +1 -1
- package/dist/api/conflux/ConfluxSignMessageCIP23.d.ts +2 -0
- package/dist/api/conflux/ConfluxSignMessageCIP23.d.ts.map +1 -1
- package/dist/api/conflux/ConfluxSignTransaction.d.ts +2 -0
- package/dist/api/conflux/ConfluxSignTransaction.d.ts.map +1 -1
- package/dist/api/cosmos/CosmosGetAddress.d.ts +1 -0
- package/dist/api/cosmos/CosmosGetAddress.d.ts.map +1 -1
- package/dist/api/cosmos/CosmosGetPublicKey.d.ts +1 -0
- package/dist/api/cosmos/CosmosGetPublicKey.d.ts.map +1 -1
- package/dist/api/cosmos/CosmosSignTransaction.d.ts +1 -0
- package/dist/api/cosmos/CosmosSignTransaction.d.ts.map +1 -1
- package/dist/api/device/DeviceCancel.d.ts +1 -0
- package/dist/api/device/DeviceCancel.d.ts.map +1 -1
- package/dist/api/device/DeviceChangePin.d.ts +1 -0
- package/dist/api/device/DeviceChangePin.d.ts.map +1 -1
- package/dist/api/device/DeviceFullyUploadResource.d.ts +1 -2
- package/dist/api/device/DeviceFullyUploadResource.d.ts.map +1 -1
- package/dist/api/device/DeviceLock.d.ts +1 -0
- package/dist/api/device/DeviceLock.d.ts.map +1 -1
- package/dist/api/device/DeviceRebootToBoardloader.d.ts +1 -0
- package/dist/api/device/DeviceRebootToBoardloader.d.ts.map +1 -1
- package/dist/api/device/DeviceRebootToBootloader.d.ts +1 -0
- package/dist/api/device/DeviceRebootToBootloader.d.ts.map +1 -1
- package/dist/api/device/DeviceSettings.d.ts +1 -0
- package/dist/api/device/DeviceSettings.d.ts.map +1 -1
- package/dist/api/device/DeviceSupportFeatures.d.ts +1 -0
- package/dist/api/device/DeviceSupportFeatures.d.ts.map +1 -1
- package/dist/api/device/DeviceUnlock.d.ts +3 -2
- package/dist/api/device/DeviceUnlock.d.ts.map +1 -1
- package/dist/api/device/DeviceUpdateBootloader.d.ts +5 -1
- package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
- package/dist/api/device/DeviceWipe.d.ts +1 -0
- package/dist/api/device/DeviceWipe.d.ts.map +1 -1
- package/dist/api/device/PreInitialize.d.ts +1 -0
- package/dist/api/device/PreInitialize.d.ts.map +1 -1
- package/dist/api/dynex/DnxGetAddress.d.ts.map +1 -1
- package/dist/api/dynex/DnxSignTransaction.d.ts.map +1 -1
- package/dist/api/evm/EVMGetAddress.d.ts +1 -0
- package/dist/api/evm/EVMGetAddress.d.ts.map +1 -1
- package/dist/api/evm/EVMGetPublicKey.d.ts +1 -0
- package/dist/api/evm/EVMGetPublicKey.d.ts.map +1 -1
- package/dist/api/evm/EVMSignMessage.d.ts +1 -0
- package/dist/api/evm/EVMSignMessage.d.ts.map +1 -1
- package/dist/api/evm/EVMSignMessageEIP712.d.ts.map +1 -1
- package/dist/api/evm/EVMSignTransaction.d.ts +1 -0
- package/dist/api/evm/EVMSignTransaction.d.ts.map +1 -1
- package/dist/api/evm/EVMSignTypedData.d.ts +1 -0
- package/dist/api/evm/EVMSignTypedData.d.ts.map +1 -1
- package/dist/api/evm/EVMVerifyMessage.d.ts +1 -0
- package/dist/api/evm/EVMVerifyMessage.d.ts.map +1 -1
- package/dist/api/evm/protocol.d.ts +3 -0
- package/dist/api/evm/protocol.d.ts.map +1 -0
- package/dist/api/filecoin/FilecoinGetAddress.d.ts +1 -0
- package/dist/api/filecoin/FilecoinGetAddress.d.ts.map +1 -1
- package/dist/api/filecoin/FilecoinSignTransaction.d.ts +1 -0
- package/dist/api/filecoin/FilecoinSignTransaction.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareArtifactSource.d.ts +25 -0
- package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +1 -0
- package/dist/api/firmware/FirmwareHostBinding.d.ts +6 -0
- package/dist/api/firmware/FirmwareHostBinding.d.ts.map +1 -0
- package/dist/api/firmware/FirmwareMemoryHost.d.ts +22 -0
- package/dist/api/firmware/FirmwareMemoryHost.d.ts.map +1 -0
- package/dist/api/firmware/FirmwarePreparationError.d.ts +3 -0
- package/dist/api/firmware/FirmwarePreparationError.d.ts.map +1 -0
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +12 -3
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +3 -0
- package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +1 -0
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts +72 -0
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -0
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +32 -0
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -0
- package/dist/api/firmware/getBinary.d.ts +14 -4
- package/dist/api/firmware/getBinary.d.ts.map +1 -1
- package/dist/api/firmware/protocolV2Release.d.ts +33 -0
- package/dist/api/firmware/protocolV2Release.d.ts.map +1 -0
- package/dist/api/firmware/updateBootloader.d.ts +2 -0
- package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
- package/dist/api/firmware/uploadFirmware.d.ts +9 -1
- package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
- package/dist/api/helpers/filesystemValidation.d.ts +3 -0
- package/dist/api/helpers/filesystemValidation.d.ts.map +1 -1
- package/dist/api/helpers/paramsValidator.d.ts +1 -0
- package/dist/api/helpers/paramsValidator.d.ts.map +1 -1
- package/dist/api/helpers/protocolV2FileWrite.d.ts +43 -0
- package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -0
- package/dist/api/index.d.ts +8 -27
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/kaspa/KaspaGetAddress.d.ts +4 -0
- package/dist/api/kaspa/KaspaGetAddress.d.ts.map +1 -1
- package/dist/api/kaspa/KaspaSignTransaction.d.ts +9 -1
- package/dist/api/kaspa/KaspaSignTransaction.d.ts.map +1 -1
- package/dist/api/kaspa/helpers/TransferSerialize.d.ts.map +1 -1
- package/dist/api/lightning/LnurlAuth.d.ts +0 -4
- package/dist/api/lightning/LnurlAuth.d.ts.map +1 -1
- package/dist/api/near/NearGetAddress.d.ts +1 -0
- package/dist/api/near/NearGetAddress.d.ts.map +1 -1
- package/dist/api/near/NearSignTransaction.d.ts +1 -0
- package/dist/api/near/NearSignTransaction.d.ts.map +1 -1
- package/dist/api/nem/NEMGetAddress.d.ts +1 -0
- package/dist/api/nem/NEMGetAddress.d.ts.map +1 -1
- package/dist/api/nem/NEMSignTransaction.d.ts +1 -0
- package/dist/api/nem/NEMSignTransaction.d.ts.map +1 -1
- package/dist/api/neo/NeoGetAddress.d.ts.map +1 -1
- package/dist/api/neo/NeoSignTransaction.d.ts.map +1 -1
- package/dist/api/nervos/NervosGetAddress.d.ts.map +1 -1
- package/dist/api/nervos/NervosSignTransaction.d.ts.map +1 -1
- package/dist/api/nexa/NexaGetAddress.d.ts.map +1 -1
- package/dist/api/nexa/NexaSignTransaction.d.ts +1 -0
- package/dist/api/nexa/NexaSignTransaction.d.ts.map +1 -1
- package/dist/api/nostr/NostrDecryptMessage.d.ts +1 -0
- package/dist/api/nostr/NostrDecryptMessage.d.ts.map +1 -1
- package/dist/api/nostr/NostrEncryptMessage.d.ts +1 -0
- package/dist/api/nostr/NostrEncryptMessage.d.ts.map +1 -1
- package/dist/api/nostr/NostrGetPublicKey.d.ts +1 -0
- package/dist/api/nostr/NostrGetPublicKey.d.ts.map +1 -1
- package/dist/api/nostr/NostrSignEvent.d.ts +1 -0
- package/dist/api/nostr/NostrSignEvent.d.ts.map +1 -1
- package/dist/api/nostr/NostrSignSchnorr.d.ts +1 -0
- package/dist/api/nostr/NostrSignSchnorr.d.ts.map +1 -1
- package/dist/api/polkadot/PolkadotGetAddress.d.ts +2 -1
- package/dist/api/polkadot/PolkadotGetAddress.d.ts.map +1 -1
- package/dist/api/polkadot/PolkadotSignTransaction.d.ts +1 -0
- package/dist/api/polkadot/PolkadotSignTransaction.d.ts.map +1 -1
- package/dist/api/polkadot/networks.d.ts +1 -1
- package/dist/api/protocol-v2/DeviceFactoryInfoGet.d.ts +1 -0
- package/dist/api/protocol-v2/DeviceFactoryInfoGet.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceFactoryInfoSet.d.ts +1 -0
- package/dist/api/protocol-v2/DeviceFactoryInfoSet.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceFirmwareUpdate.d.ts +1 -0
- package/dist/api/protocol-v2/DeviceFirmwareUpdate.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts +1 -0
- package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts +7 -0
- package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceInfoGet.d.ts +1 -1
- package/dist/api/protocol-v2/DeviceInfoGet.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceReboot.d.ts +1 -0
- package/dist/api/protocol-v2/DeviceReboot.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceStatusGet.d.ts +1 -0
- package/dist/api/protocol-v2/DeviceStatusGet.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts +31 -0
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts +27 -0
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -0
- package/dist/api/protocol-v2/FilesystemFormat.d.ts +1 -0
- package/dist/api/protocol-v2/FilesystemFormat.d.ts.map +1 -1
- package/dist/api/protocol-v2/FilesystemPermissionFix.d.ts +1 -0
- package/dist/api/protocol-v2/FilesystemPermissionFix.d.ts.map +1 -1
- package/dist/api/protocol-v2/Ping.d.ts +3 -0
- package/dist/api/protocol-v2/Ping.d.ts.map +1 -1
- package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts +1 -0
- package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -1
- package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
- package/dist/api/scdo/ScdoGetAddress.d.ts.map +1 -1
- package/dist/api/scdo/ScdoSignMessage.d.ts.map +1 -1
- package/dist/api/scdo/ScdoSignTransaction.d.ts.map +1 -1
- package/dist/api/solana/SolGetAddress.d.ts +1 -0
- package/dist/api/solana/SolGetAddress.d.ts.map +1 -1
- package/dist/api/solana/SolSignMessage.d.ts +2 -1
- package/dist/api/solana/SolSignMessage.d.ts.map +1 -1
- package/dist/api/solana/SolSignOffchainMessage.d.ts +2 -1
- package/dist/api/solana/SolSignOffchainMessage.d.ts.map +1 -1
- package/dist/api/solana/SolSignTransaction.d.ts +3 -2
- package/dist/api/solana/SolSignTransaction.d.ts.map +1 -1
- package/dist/api/starcoin/StarcoinGetAddress.d.ts +1 -0
- package/dist/api/starcoin/StarcoinGetAddress.d.ts.map +1 -1
- package/dist/api/starcoin/StarcoinGetPublicKey.d.ts +1 -0
- package/dist/api/starcoin/StarcoinGetPublicKey.d.ts.map +1 -1
- package/dist/api/starcoin/StarcoinSignMessage.d.ts +1 -0
- package/dist/api/starcoin/StarcoinSignMessage.d.ts.map +1 -1
- package/dist/api/starcoin/StarcoinSignTransaction.d.ts +1 -0
- package/dist/api/starcoin/StarcoinSignTransaction.d.ts.map +1 -1
- package/dist/api/starcoin/StarcoinVerifyMessage.d.ts +1 -0
- package/dist/api/starcoin/StarcoinVerifyMessage.d.ts.map +1 -1
- package/dist/api/stellar/StellarGetAddress.d.ts +1 -2
- package/dist/api/stellar/StellarGetAddress.d.ts.map +1 -1
- package/dist/api/stellar/StellarSignTransaction.d.ts +2 -3
- package/dist/api/stellar/StellarSignTransaction.d.ts.map +1 -1
- package/dist/api/sui/SuiGetAddress.d.ts +2 -1
- package/dist/api/sui/SuiGetAddress.d.ts.map +1 -1
- package/dist/api/sui/SuiGetPublicKey.d.ts +2 -1
- package/dist/api/sui/SuiGetPublicKey.d.ts.map +1 -1
- package/dist/api/sui/SuiSignMessage.d.ts +2 -1
- package/dist/api/sui/SuiSignMessage.d.ts.map +1 -1
- package/dist/api/sui/SuiSignTransaction.d.ts +2 -1
- package/dist/api/sui/SuiSignTransaction.d.ts.map +1 -1
- package/dist/api/ton/TonGetAddress.d.ts +2 -1
- package/dist/api/ton/TonGetAddress.d.ts.map +1 -1
- package/dist/api/ton/TonSignData.d.ts +1 -0
- package/dist/api/ton/TonSignData.d.ts.map +1 -1
- package/dist/api/ton/TonSignMessage.d.ts +2 -1
- package/dist/api/ton/TonSignMessage.d.ts.map +1 -1
- package/dist/api/ton/TonSignProof.d.ts +2 -1
- package/dist/api/ton/TonSignProof.d.ts.map +1 -1
- package/dist/api/tron/TronGetAddress.d.ts +1 -0
- package/dist/api/tron/TronGetAddress.d.ts.map +1 -1
- package/dist/api/tron/TronSignMessage.d.ts +6 -1
- package/dist/api/tron/TronSignMessage.d.ts.map +1 -1
- package/dist/api/tron/TronSignTransaction.d.ts +2 -1
- package/dist/api/tron/TronSignTransaction.d.ts.map +1 -1
- package/dist/api/utils.d.ts +4 -1
- package/dist/api/utils.d.ts.map +1 -1
- package/dist/api/xrp/XrpGetAddress.d.ts +1 -0
- package/dist/api/xrp/XrpGetAddress.d.ts.map +1 -1
- package/dist/api/xrp/XrpSignTransaction.d.ts +1 -0
- package/dist/api/xrp/XrpSignTransaction.d.ts.map +1 -1
- package/dist/constants/index.d.ts +1 -1
- package/dist/constants/index.d.ts.map +1 -1
- package/dist/core/RequestQueue.d.ts.map +1 -1
- package/dist/core/deviceEventRegistration.d.ts +13 -0
- package/dist/core/deviceEventRegistration.d.ts.map +1 -0
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/uiPromiseRegistry.d.ts +6 -0
- package/dist/core/uiPromiseRegistry.d.ts.map +1 -0
- package/dist/data-manager/DataManager.d.ts +11 -3
- package/dist/data-manager/DataManager.d.ts.map +1 -1
- package/dist/data-manager/TransportManager.d.ts.map +1 -1
- package/dist/data-manager/connectSettings.d.ts.map +1 -1
- package/dist/device/Device.d.ts +71 -16
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts +8 -6
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/device/DeviceConnector.d.ts +4 -3
- package/dist/device/DeviceConnector.d.ts.map +1 -1
- package/dist/device/DeviceFeaturesState.d.ts +3 -0
- package/dist/device/DeviceFeaturesState.d.ts.map +1 -0
- package/dist/device/DevicePool.d.ts +6 -2
- package/dist/device/DevicePool.d.ts.map +1 -1
- package/dist/device/DeviceStateMapper.d.ts +13 -0
- package/dist/device/DeviceStateMapper.d.ts.map +1 -0
- package/dist/device/DeviceStateProjector.d.ts +7 -0
- package/dist/device/DeviceStateProjector.d.ts.map +1 -0
- package/dist/device/DeviceStateStore.d.ts +21 -0
- package/dist/device/DeviceStateStore.d.ts.map +1 -0
- package/dist/device/DeviceWalletSessionStore.d.ts +13 -1
- package/dist/device/DeviceWalletSessionStore.d.ts.map +1 -1
- package/dist/device/cloneDeviceState.d.ts +2 -0
- package/dist/device/cloneDeviceState.d.ts.map +1 -0
- package/dist/device/deviceIdentity.d.ts +5 -0
- package/dist/device/deviceIdentity.d.ts.map +1 -0
- package/dist/deviceProfile/buildDeviceFeatures.d.ts +9 -3
- package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
- package/dist/deviceProfile/index.d.ts +0 -1
- package/dist/deviceProfile/index.d.ts.map +1 -1
- package/dist/deviceProfile/protocolV2DeviceIdentity.d.ts +7 -0
- package/dist/deviceProfile/protocolV2DeviceIdentity.d.ts.map +1 -0
- package/dist/events/device.d.ts +16 -2
- package/dist/events/device.d.ts.map +1 -1
- package/dist/events/logBlockEvent.d.ts +2 -0
- package/dist/events/logBlockEvent.d.ts.map +1 -1
- package/dist/events/ui-promise.d.ts +4 -2
- package/dist/events/ui-promise.d.ts.map +1 -1
- package/dist/events/ui-request.d.ts +50 -4
- package/dist/events/ui-request.d.ts.map +1 -1
- package/dist/events/ui-response.d.ts +10 -2
- package/dist/events/ui-response.d.ts.map +1 -1
- package/dist/index.d.ts +1786 -776
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11556 -4498
- package/dist/inject.d.ts +6 -1
- package/dist/inject.d.ts.map +1 -1
- package/dist/lowLevelInject.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/deviceSession.d.ts +7 -0
- package/dist/protocols/protocol-v2/deviceSession.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/features.d.ts +21 -17
- package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/firmware.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/index.d.ts +2 -2
- package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/resources.d.ts +12 -0
- package/dist/protocols/protocol-v2/resources.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts +17 -0
- package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/uiInteraction.d.ts +42 -0
- package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/unlockPolicy.d.ts +2 -0
- package/dist/protocols/protocol-v2/unlockPolicy.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +22 -0
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/walletSession.d.ts +18 -4
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/topLevelInject.d.ts.map +1 -1
- package/dist/types/api/checkAllFirmwareRelease.d.ts +48 -9
- package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
- package/dist/types/api/checkBLEFirmwareRelease.d.ts +2 -13
- package/dist/types/api/checkBLEFirmwareRelease.d.ts.map +1 -1
- package/dist/types/api/checkBootloaderRelease.d.ts +2 -6
- package/dist/types/api/checkBootloaderRelease.d.ts.map +1 -1
- package/dist/types/api/checkFirmwareRelease.d.ts +2 -13
- package/dist/types/api/checkFirmwareRelease.d.ts.map +1 -1
- package/dist/types/api/checkFirmwareTypeAvailable.d.ts +2 -2
- package/dist/types/api/checkFirmwareTypeAvailable.d.ts.map +1 -1
- package/dist/types/api/detectDeviceConnectProtocol.d.ts +4 -0
- package/dist/types/api/detectDeviceConnectProtocol.d.ts.map +1 -0
- package/dist/types/api/deviceSettings.d.ts +11 -2
- package/dist/types/api/deviceSettings.d.ts.map +1 -1
- package/dist/types/api/deviceUnlock.d.ts +6 -2
- package/dist/types/api/deviceUnlock.d.ts.map +1 -1
- package/dist/types/api/deviceUpdateBootloader.d.ts +6 -0
- package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
- package/dist/types/api/export.d.ts +8 -3
- package/dist/types/api/export.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdate.d.ts +76 -2
- package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdateCapabilities.d.ts +9 -0
- package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +1 -0
- package/dist/types/api/firmwareUpdatePlan.d.ts +28 -0
- package/dist/types/api/firmwareUpdatePlan.d.ts.map +1 -0
- package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +42 -0
- package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +1 -0
- package/dist/types/api/getDeviceState.d.ts +12 -0
- package/dist/types/api/getDeviceState.d.ts.map +1 -0
- package/dist/types/api/getFeatures.d.ts.map +1 -1
- package/dist/types/api/getOnekeyFeatures.d.ts.map +1 -1
- package/dist/types/api/getPassphraseState.d.ts +2 -10
- package/dist/types/api/getPassphraseState.d.ts.map +1 -1
- package/dist/types/api/index.d.ts +29 -33
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/api/kaspaSignTransaction.d.ts +42 -4
- package/dist/types/api/kaspaSignTransaction.d.ts.map +1 -1
- package/dist/types/api/openWalletSession.d.ts +35 -0
- package/dist/types/api/openWalletSession.d.ts.map +1 -0
- package/dist/types/api/protocolV2.d.ts +18 -88
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/api/sessionCache.d.ts +4 -1
- package/dist/types/api/sessionCache.d.ts.map +1 -1
- package/dist/types/device.d.ts +142 -10
- package/dist/types/device.d.ts.map +1 -1
- package/dist/types/params.d.ts +1 -0
- package/dist/types/params.d.ts.map +1 -1
- package/dist/types/settings.d.ts +59 -16
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/assets.d.ts +2 -1
- package/dist/utils/assets.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesCompat.d.ts +14 -0
- package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -0
- package/dist/utils/deviceFeaturesUtils.d.ts +10 -3
- package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
- package/dist/utils/deviceInfoUtils.d.ts +9 -9
- package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
- package/dist/utils/deviceSettings.d.ts +38 -7
- package/dist/utils/deviceSettings.d.ts.map +1 -1
- package/dist/utils/deviceVersionUtils.d.ts +7 -5
- package/dist/utils/deviceVersionUtils.d.ts.map +1 -1
- package/dist/utils/homescreen.d.ts +4 -0
- package/dist/utils/homescreen.d.ts.map +1 -1
- package/dist/utils/index.d.ts +6 -3
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/networkUtils.d.ts +10 -1
- package/dist/utils/networkUtils.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/patch.d.ts.map +1 -1
- package/dist/utils/pro2Nft.d.ts +31 -0
- package/dist/utils/pro2Nft.d.ts.map +1 -0
- package/dist/utils/pro2Wallpaper.d.ts +22 -0
- package/dist/utils/pro2Wallpaper.d.ts.map +1 -0
- package/dist/utils/tracing.d.ts.map +1 -1
- package/dist/utils/uiProgressThrottle.d.ts +3 -0
- package/dist/utils/uiProgressThrottle.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/api/BaseMethod.ts +53 -20
- package/src/api/CheckAllFirmwareRelease.ts +437 -4
- package/src/api/CheckBLEFirmwareRelease.ts +37 -5
- package/src/api/CheckBootloaderRelease.ts +36 -5
- package/src/api/CheckFirmwareRelease.ts +35 -8
- package/src/api/CipherKeyValue.ts +4 -0
- package/src/api/ClearSessionCache.ts +4 -0
- package/src/api/DetectDeviceConnectProtocol.ts +18 -0
- package/src/api/DirList.ts +9 -4
- package/src/api/DirMake.ts +7 -4
- package/src/api/DirRemove.ts +7 -4
- package/src/api/FileDelete.ts +7 -4
- package/src/api/FileRead.ts +13 -7
- package/src/api/FileWrite.ts +26 -181
- package/src/api/FirmwareUpdate.ts +30 -15
- package/src/api/FirmwareUpdateV2.ts +390 -115
- package/src/api/FirmwareUpdateV3.ts +281 -71
- package/src/api/FirmwareUpdateV4.ts +2005 -732
- package/src/api/GetDeviceState.ts +59 -0
- package/src/api/GetFeatures.ts +6 -4
- package/src/api/GetOnekeyFeatures.ts +1 -75
- package/src/api/GetPassphraseState.ts +36 -21
- package/src/api/OpenWalletSession.ts +268 -0
- package/src/api/PathInfo.ts +9 -4
- package/src/api/PromptWebDeviceAccess.ts +2 -7
- package/src/api/SearchDevices.ts +31 -4
- package/src/api/UploadPortfolio.ts +58 -0
- package/src/api/alephium/AlephiumGetAddress.ts +0 -4
- package/src/api/alephium/AlephiumSignMessage.ts +0 -4
- package/src/api/alephium/AlephiumSignTransaction.ts +2 -6
- package/src/api/algo/AlgoGetAddress.ts +4 -0
- package/src/api/algo/AlgoSignTransaction.ts +4 -0
- package/src/api/allnetwork/AllNetworkGetAddress.ts +21 -21
- package/src/api/allnetwork/AllNetworkGetAddressBase.ts +45 -32
- package/src/api/aptos/AptosGetAddress.ts +4 -0
- package/src/api/aptos/AptosGetPublicKey.ts +4 -0
- package/src/api/aptos/AptosSignInMessage.ts +4 -0
- package/src/api/aptos/AptosSignMessage.ts +4 -0
- package/src/api/aptos/AptosSignTransaction.ts +4 -0
- package/src/api/benfen/BenfenGetAddress.ts +11 -7
- package/src/api/benfen/BenfenGetPublicKey.ts +0 -4
- package/src/api/benfen/BenfenSignMessage.ts +0 -4
- package/src/api/benfen/BenfenSignTransaction.ts +0 -4
- package/src/api/btc/BTCGetAddress.ts +8 -1
- package/src/api/btc/BTCGetPublicKey.ts +9 -2
- package/src/api/btc/BTCSignMessage.ts +8 -1
- package/src/api/btc/BTCSignPsbt.ts +4 -0
- package/src/api/btc/BTCSignTransaction.ts +8 -1
- package/src/api/btc/BTCVerifyMessage.ts +8 -1
- package/src/api/btc/helpers/versionLimit.ts +7 -4
- package/src/api/cardano/CardanoGetAddress.ts +4 -0
- package/src/api/cardano/CardanoGetPublicKey.ts +4 -0
- package/src/api/cardano/CardanoSignMessage.ts +5 -1
- package/src/api/cardano/CardanoSignTransaction.ts +5 -1
- package/src/api/conflux/ConfluxGetAddress.ts +6 -0
- package/src/api/conflux/ConfluxSignMessage.ts +6 -0
- package/src/api/conflux/ConfluxSignMessageCIP23.ts +6 -0
- package/src/api/conflux/ConfluxSignTransaction.ts +8 -5
- package/src/api/cosmos/CosmosGetAddress.ts +4 -0
- package/src/api/cosmos/CosmosGetPublicKey.ts +4 -0
- package/src/api/cosmos/CosmosSignTransaction.ts +4 -0
- package/src/api/device/DeviceCancel.ts +5 -0
- package/src/api/device/DeviceChangePin.ts +28 -0
- package/src/api/device/DeviceFullyUploadResource.ts +4 -5
- package/src/api/device/DeviceLock.ts +5 -0
- package/src/api/device/DeviceRebootToBoardloader.ts +5 -0
- package/src/api/device/DeviceRebootToBootloader.ts +5 -0
- package/src/api/device/DeviceSettings.ts +195 -1
- package/src/api/device/DeviceSupportFeatures.ts +4 -0
- package/src/api/device/DeviceUnlock.ts +21 -3
- package/src/api/device/DeviceUpdateBootloader.ts +133 -8
- package/src/api/device/DeviceWipe.ts +25 -0
- package/src/api/device/PreInitialize.ts +4 -0
- package/src/api/dynex/DnxGetAddress.ts +0 -6
- package/src/api/dynex/DnxSignTransaction.ts +0 -6
- package/src/api/evm/EVMGetAddress.ts +6 -2
- package/src/api/evm/EVMGetPublicKey.ts +6 -2
- package/src/api/evm/EVMSignMessage.ts +6 -2
- package/src/api/evm/EVMSignMessageEIP712.ts +1 -11
- package/src/api/evm/EVMSignTransaction.ts +6 -2
- package/src/api/evm/EVMSignTypedData.ts +42 -50
- package/src/api/evm/EVMVerifyMessage.ts +6 -2
- package/src/api/evm/legacyV1/getAddress.ts +2 -2
- package/src/api/evm/legacyV1/getPublicKey.ts +2 -2
- package/src/api/evm/legacyV1/signMessage.ts +2 -2
- package/src/api/evm/legacyV1/signTypedData.ts +2 -2
- package/src/api/evm/legacyV1/signTypedHash.ts +2 -2
- package/src/api/evm/legacyV1/verifyMessage.ts +2 -2
- package/src/api/evm/protocol.ts +6 -0
- package/src/api/filecoin/FilecoinGetAddress.ts +4 -0
- package/src/api/filecoin/FilecoinSignTransaction.ts +4 -0
- package/src/api/firmware/FirmwareArtifactSource.ts +278 -0
- package/src/api/firmware/FirmwareHostBinding.ts +113 -0
- package/src/api/firmware/FirmwareMemoryHost.ts +142 -0
- package/src/api/firmware/FirmwarePreparationError.ts +12 -0
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +102 -16
- package/src/api/firmware/FirmwareUpdateCapabilities.ts +9 -0
- package/src/api/firmware/FirmwareUpdatePlan.ts +910 -0
- package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +456 -0
- package/src/api/firmware/getBinary.ts +8 -3
- package/src/api/firmware/protocolV2Release.ts +168 -0
- package/src/api/firmware/updateBootloader.ts +19 -4
- package/src/api/firmware/uploadFirmware.ts +157 -26
- package/src/api/helpers/batchGetPublickeys.ts +2 -2
- package/src/api/helpers/filesystemValidation.ts +61 -0
- package/src/api/helpers/paramsValidator.ts +1 -1
- package/src/api/helpers/protocolV2FileWrite.ts +392 -0
- package/src/api/index.ts +9 -28
- package/src/api/kaspa/KaspaGetAddress.ts +7 -0
- package/src/api/kaspa/KaspaSignTransaction.ts +369 -29
- package/src/api/kaspa/helpers/TransferSerialize.ts +17 -7
- package/src/api/lightning/LnurlAuth.ts +0 -4
- package/src/api/near/NearGetAddress.ts +4 -0
- package/src/api/near/NearSignTransaction.ts +4 -0
- package/src/api/nem/NEMGetAddress.ts +4 -0
- package/src/api/nem/NEMSignTransaction.ts +4 -0
- package/src/api/neo/NeoGetAddress.ts +0 -4
- package/src/api/neo/NeoSignTransaction.ts +0 -4
- package/src/api/nervos/NervosGetAddress.ts +0 -4
- package/src/api/nervos/NervosSignTransaction.ts +2 -6
- package/src/api/nexa/NexaGetAddress.ts +0 -4
- package/src/api/nexa/NexaSignTransaction.ts +3 -5
- package/src/api/nostr/NostrDecryptMessage.ts +4 -0
- package/src/api/nostr/NostrEncryptMessage.ts +4 -0
- package/src/api/nostr/NostrGetPublicKey.ts +4 -0
- package/src/api/nostr/NostrSignEvent.ts +4 -0
- package/src/api/nostr/NostrSignSchnorr.ts +4 -0
- package/src/api/polkadot/PolkadotGetAddress.ts +4 -0
- package/src/api/polkadot/PolkadotSignTransaction.ts +4 -0
- package/src/api/polkadot/networks.ts +3 -3
- package/src/api/protocol-v2/DeviceFactoryInfoGet.ts +5 -2
- package/src/api/protocol-v2/DeviceFactoryInfoSet.ts +5 -2
- package/src/api/protocol-v2/DeviceFirmwareUpdate.ts +8 -5
- package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +10 -6
- package/src/api/protocol-v2/DeviceGetOnboardingStatus.ts +21 -0
- package/src/api/protocol-v2/DeviceInfoGet.ts +8 -15
- package/src/api/protocol-v2/DeviceReboot.ts +7 -3
- package/src/api/protocol-v2/DeviceStatusGet.ts +6 -1
- package/src/api/protocol-v2/DeviceUploadNft.ts +197 -0
- package/src/api/protocol-v2/DeviceUploadWallpaper.ts +134 -0
- package/src/api/protocol-v2/FilesystemFormat.ts +5 -2
- package/src/api/protocol-v2/FilesystemPermissionFix.ts +5 -2
- package/src/api/protocol-v2/Ping.ts +37 -3
- package/src/api/protocol-v2/ProtocolInfoRequest.ts +8 -3
- package/src/api/protocol-v2/helpers.ts +1 -4
- package/src/api/scdo/ScdoGetAddress.ts +0 -4
- package/src/api/scdo/ScdoSignMessage.ts +0 -4
- package/src/api/scdo/ScdoSignTransaction.ts +0 -4
- package/src/api/solana/SolGetAddress.ts +4 -0
- package/src/api/solana/SolSignMessage.ts +5 -1
- package/src/api/solana/SolSignOffchainMessage.ts +5 -1
- package/src/api/solana/SolSignTransaction.ts +6 -2
- package/src/api/starcoin/StarcoinGetAddress.ts +4 -0
- package/src/api/starcoin/StarcoinGetPublicKey.ts +4 -0
- package/src/api/starcoin/StarcoinSignMessage.ts +4 -0
- package/src/api/starcoin/StarcoinSignTransaction.ts +4 -0
- package/src/api/starcoin/StarcoinVerifyMessage.ts +4 -0
- package/src/api/stellar/StellarGetAddress.ts +1 -10
- package/src/api/stellar/StellarSignTransaction.ts +1 -14
- package/src/api/sui/SuiGetAddress.ts +5 -1
- package/src/api/sui/SuiGetPublicKey.ts +5 -1
- package/src/api/sui/SuiSignMessage.ts +5 -1
- package/src/api/sui/SuiSignTransaction.ts +5 -1
- package/src/api/ton/TonGetAddress.ts +5 -1
- package/src/api/ton/TonSignData.ts +5 -1
- package/src/api/ton/TonSignMessage.ts +5 -1
- package/src/api/ton/TonSignProof.ts +5 -1
- package/src/api/tron/TronGetAddress.ts +4 -0
- package/src/api/tron/TronSignMessage.ts +19 -8
- package/src/api/tron/TronSignTransaction.ts +5 -1
- package/src/api/utils.ts +32 -3
- package/src/api/xrp/XrpGetAddress.ts +4 -0
- package/src/api/xrp/XrpSignTransaction.ts +5 -1
- package/src/constants/index.ts +1 -4
- package/src/core/RequestQueue.ts +1 -0
- package/src/core/deviceEventRegistration.ts +27 -0
- package/src/core/index.ts +381 -152
- package/src/core/uiPromiseRegistry.ts +63 -0
- package/src/data/messages/messages-protocol-v2.json +1113 -547
- package/src/data/messages/messages.json +286 -9
- package/src/data-manager/DataManager.ts +154 -43
- package/src/data-manager/TransportManager.ts +7 -1
- package/src/data-manager/connectSettings.ts +11 -0
- package/src/device/Device.ts +858 -264
- package/src/device/DeviceCommands.ts +193 -158
- package/src/device/DeviceConnector.ts +44 -21
- package/src/device/DeviceFeaturesState.ts +25 -0
- package/src/device/DevicePool.ts +101 -23
- package/src/device/DeviceStateMapper.ts +416 -0
- package/src/device/DeviceStateProjector.ts +152 -0
- package/src/device/DeviceStateStore.ts +232 -0
- package/src/device/DeviceWalletSessionStore.ts +72 -6
- package/src/device/cloneDeviceState.ts +39 -0
- package/src/device/deviceIdentity.ts +18 -0
- package/src/deviceProfile/buildDeviceFeatures.ts +223 -189
- package/src/deviceProfile/index.ts +0 -1
- package/src/deviceProfile/protocolV2DeviceIdentity.ts +35 -0
- package/src/events/device.ts +22 -1
- package/src/events/logBlockEvent.ts +80 -2
- package/src/events/ui-promise.ts +4 -2
- package/src/events/ui-request.ts +61 -4
- package/src/events/ui-response.ts +12 -2
- package/src/index.ts +17 -0
- package/src/inject.ts +94 -47
- package/src/lowLevelInject.ts +11 -4
- package/src/protocols/protocol-v2/deviceSession.ts +24 -0
- package/src/protocols/protocol-v2/features.ts +100 -33
- package/src/protocols/protocol-v2/firmware.ts +3 -1
- package/src/protocols/protocol-v2/index.ts +7 -1
- package/src/protocols/protocol-v2/resources.ts +210 -0
- package/src/protocols/protocol-v2/settingsUnlockPolicy.ts +65 -0
- package/src/protocols/protocol-v2/uiInteraction.ts +250 -0
- package/src/protocols/protocol-v2/unlockPolicy.ts +1 -0
- package/src/protocols/protocol-v2/unlockPolicyRunner.ts +131 -0
- package/src/protocols/protocol-v2/walletSession.ts +422 -42
- package/src/topLevelInject.ts +11 -4
- package/src/types/api/checkAllFirmwareRelease.ts +72 -9
- package/src/types/api/checkBLEFirmwareRelease.ts +4 -13
- package/src/types/api/checkBootloaderRelease.ts +2 -6
- package/src/types/api/checkFirmwareRelease.ts +2 -13
- package/src/types/api/checkFirmwareTypeAvailable.ts +2 -2
- package/src/types/api/detectDeviceConnectProtocol.ts +6 -0
- package/src/types/api/deviceSettings.ts +26 -2
- package/src/types/api/deviceUnlock.ts +13 -2
- package/src/types/api/deviceUpdateBootloader.ts +6 -0
- package/src/types/api/export.ts +33 -1
- package/src/types/api/firmwareUpdate.ts +105 -9
- package/src/types/api/firmwareUpdateCapabilities.ts +9 -0
- package/src/types/api/firmwareUpdatePlan.ts +38 -0
- package/src/types/api/firmwareUpdatePreparedPlan.ts +53 -0
- package/src/types/api/getDeviceState.ts +19 -0
- package/src/types/api/getFeatures.ts +3 -0
- package/src/types/api/getOnekeyFeatures.ts +3 -0
- package/src/types/api/getPassphraseState.ts +9 -11
- package/src/types/api/index.ts +49 -68
- package/src/types/api/kaspaSignTransaction.ts +60 -4
- package/src/types/api/openWalletSession.ts +54 -0
- package/src/types/api/protocolV2.ts +33 -155
- package/src/types/api/sessionCache.ts +9 -4
- package/src/types/device.ts +216 -10
- package/src/types/params.ts +9 -2
- package/src/types/settings.ts +67 -25
- package/src/utils/assets.ts +4 -1
- package/src/utils/deviceFeaturesCompat.ts +191 -0
- package/src/utils/deviceFeaturesUtils.ts +45 -17
- package/src/utils/deviceInfoUtils.ts +29 -69
- package/src/utils/deviceSettings.ts +174 -39
- package/src/utils/deviceVersionUtils.ts +25 -44
- package/src/utils/findDefectiveBatchDevice.ts +3 -3
- package/src/utils/homescreen.ts +40 -0
- package/src/utils/index.ts +27 -3
- package/src/utils/networkUtils.ts +274 -16
- package/src/utils/pro2Nft.ts +142 -0
- package/src/utils/pro2Wallpaper.ts +130 -0
- package/src/utils/tracing.ts +3 -1
- package/src/utils/uiProgressThrottle.ts +63 -0
- package/tsconfig.type-tests.json +12 -0
- package/dist/api/GetDeviceInfo.d.ts +0 -9
- package/dist/api/GetDeviceInfo.d.ts.map +0 -1
- package/dist/api/firmware/progressThrottle.d.ts +0 -3
- package/dist/api/firmware/progressThrottle.d.ts.map +0 -1
- package/dist/api/protocol-v2/DeviceSessionGet.d.ts +0 -9
- package/dist/api/protocol-v2/DeviceSessionGet.d.ts.map +0 -1
- package/dist/api/protocol-v2/FilesystemDiskControl.d.ts +0 -13
- package/dist/api/protocol-v2/FilesystemDiskControl.d.ts.map +0 -1
- package/dist/deviceProfile/buildDeviceProfile.d.ts +0 -21
- package/dist/deviceProfile/buildDeviceProfile.d.ts.map +0 -1
- package/dist/types/api/getDeviceInfo.d.ts +0 -87
- package/dist/types/api/getDeviceInfo.d.ts.map +0 -1
- package/src/api/GetDeviceInfo.ts +0 -145
- package/src/api/firmware/progressThrottle.ts +0 -44
- package/src/api/protocol-v2/DeviceSessionGet.ts +0 -26
- package/src/api/protocol-v2/FilesystemDiskControl.ts +0 -50
- package/src/deviceProfile/buildDeviceProfile.ts +0 -352
- package/src/types/api/getDeviceInfo.ts +0 -101
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
import { ERRORS, HardwareError, HardwareErrorCode, wait } from '@onekeyfe/hd-shared';
|
|
1
|
+
import { EDeviceType, ERRORS, HardwareError, HardwareErrorCode, wait } from '@onekeyfe/hd-shared';
|
|
2
|
+
import JSZip from 'jszip';
|
|
2
3
|
import {
|
|
3
4
|
DeviceRebootType,
|
|
4
5
|
PROTOCOL_V2_BLE_FILE_CHUNK_SIZE,
|
|
6
|
+
PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE,
|
|
7
|
+
PROTOCOL_V2_BLE_FIRMWARE_FILE_CHUNK_SIZE,
|
|
5
8
|
PROTOCOL_V2_WEBUSB_FILE_CHUNK_SIZE,
|
|
6
9
|
} from '@onekeyfe/hd-transport';
|
|
10
|
+
import { sha256 } from '@noble/hashes/sha256';
|
|
7
11
|
|
|
8
12
|
import { FirmwareUpdateTipMessage, UI_REQUEST } from '../events/ui-request';
|
|
9
13
|
import { validateParams } from './helpers/paramsValidator';
|
|
@@ -12,10 +16,12 @@ import {
|
|
|
12
16
|
getDeviceBLEFirmwareVersion,
|
|
13
17
|
getDeviceBootloaderVersion,
|
|
14
18
|
getDeviceFirmwareVersion,
|
|
19
|
+
getDeviceType,
|
|
15
20
|
getFirmwareType,
|
|
16
21
|
getLogger,
|
|
17
22
|
} from '../utils';
|
|
18
23
|
import { getSysResourceBinary } from './firmware/getBinary';
|
|
24
|
+
import { normalizeFirmwarePreparationError } from './firmware/FirmwarePreparationError';
|
|
19
25
|
import { DataManager } from '../data-manager';
|
|
20
26
|
import { FirmwareUpdateBaseMethod } from './firmware/FirmwareUpdateBaseMethod';
|
|
21
27
|
import { DevicePool } from '../device/DevicePool';
|
|
@@ -25,14 +31,39 @@ import {
|
|
|
25
31
|
} from '../protocols/protocol-v2';
|
|
26
32
|
import { requestProtocolV2DeviceInfo } from '../protocols/protocol-v2/features';
|
|
27
33
|
import {
|
|
28
|
-
|
|
34
|
+
parseProtocolV2ResourceManifest,
|
|
35
|
+
selectProtocolV2ResourceManifestFiles,
|
|
36
|
+
} from '../protocols/protocol-v2/resources';
|
|
37
|
+
import {
|
|
29
38
|
getProtocolV2UnknownErrorText,
|
|
30
39
|
isProtocolV2DeviceDisconnectedError,
|
|
31
40
|
} from './protocol-v2/helpers';
|
|
41
|
+
import {
|
|
42
|
+
openFirmwareByteSource,
|
|
43
|
+
readFirmwareByteSourceFully,
|
|
44
|
+
writeFirmwareByteSource,
|
|
45
|
+
} from './firmware/FirmwareArtifactSource';
|
|
46
|
+
import {
|
|
47
|
+
registerFirmwareUpdateHostBinding,
|
|
48
|
+
resolveFirmwareUpdateHostBinding,
|
|
49
|
+
unregisterFirmwareUpdateHostBinding,
|
|
50
|
+
} from './firmware/FirmwareHostBinding';
|
|
51
|
+
import {
|
|
52
|
+
assertFirmwareUpdatePreparedPlanBinding,
|
|
53
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity,
|
|
54
|
+
prepareFirmwareUpdatePlan,
|
|
55
|
+
validateFirmwareUpdatePreparedPlan,
|
|
56
|
+
} from './firmware/FirmwareUpdatePreparedPlan';
|
|
57
|
+
import { prepareFirmwareUpdateV4MemoryHost } from './firmware/FirmwareMemoryHost';
|
|
58
|
+
import { buildProtocolV2LocalFirmwareUpdatePlan } from './firmware/FirmwareUpdatePlan';
|
|
32
59
|
|
|
33
|
-
import type {
|
|
60
|
+
import type {
|
|
61
|
+
FirmwareArtifactReference,
|
|
62
|
+
FirmwareUpdateV4Params,
|
|
63
|
+
FirmwareUpdateV4Target,
|
|
64
|
+
} from '../types/api/firmwareUpdate';
|
|
34
65
|
import type { EFirmwareType } from '@onekeyfe/hd-shared';
|
|
35
|
-
import type {
|
|
66
|
+
import type { ProtocolV2DeviceInfo } from '@onekeyfe/hd-transport';
|
|
36
67
|
import type { TypedResponseMessage } from '../device/DeviceCommands';
|
|
37
68
|
import type {
|
|
38
69
|
Features,
|
|
@@ -40,14 +71,26 @@ import type {
|
|
|
40
71
|
IProtocolV2FirmwareComponent,
|
|
41
72
|
IVersionArray,
|
|
42
73
|
} from '../types';
|
|
74
|
+
import type { FirmwareByteSource } from './firmware/FirmwareArtifactSource';
|
|
75
|
+
import type {
|
|
76
|
+
FirmwareMemoryArtifact,
|
|
77
|
+
FirmwareMemoryArtifactEntry,
|
|
78
|
+
FirmwareUpdateV4MemoryHost,
|
|
79
|
+
} from './firmware/FirmwareMemoryHost';
|
|
43
80
|
|
|
44
81
|
const Log = getLogger(LoggerNames.Method);
|
|
45
82
|
|
|
83
|
+
// Restored after a rebase dropped the declaration while keeping its use in
|
|
84
|
+
// fileWriteChunk; without it that error branch throws ReferenceError instead of
|
|
85
|
+
// the intended typed session error.
|
|
46
86
|
const SESSION_ERROR = 'session not found';
|
|
47
|
-
const PROTOCOL_V2_BOOTLOADER_RECONNECT_TIMEOUT =
|
|
87
|
+
const PROTOCOL_V2_BOOTLOADER_RECONNECT_TIMEOUT = 90 * 1000;
|
|
88
|
+
const PROTOCOL_V2_FINAL_RECONNECT_TIMEOUT = 3 * 60 * 1000;
|
|
48
89
|
const PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT = 5 * 1000;
|
|
90
|
+
const PROTOCOL_V2_FIRMWARE_STATUS_RESPONSE_TIMEOUT = 15 * 1000;
|
|
49
91
|
const PROTOCOL_V2_START_UPDATE_TIMEOUT = 3 * 60 * 1000;
|
|
50
|
-
const PROTOCOL_V2_INSTALL_TIMEOUT =
|
|
92
|
+
const PROTOCOL_V2_INSTALL_TIMEOUT = 8 * 60 * 1000;
|
|
93
|
+
const PROTOCOL_V2_MISSING_TARGET_STATUS_GRACE_TIMEOUT = 30 * 1000;
|
|
51
94
|
const PROTOCOL_V2_TARGET_STATUS_PENDING = 0;
|
|
52
95
|
const PROTOCOL_V2_TARGET_STATUS_IN_PROGRESS = 1;
|
|
53
96
|
const PROTOCOL_V2_TARGET_STATUS_FINISHED = 2;
|
|
@@ -58,12 +101,60 @@ const PROTOCOL_V2_MIN_FILE_CHUNK_SIZE = 64;
|
|
|
58
101
|
const PROTOCOL_V2_CONNECT_RETRY_COUNT = 10;
|
|
59
102
|
const PROTOCOL_V2_CONNECT_POLL_INTERVAL = 500;
|
|
60
103
|
const PROTOCOL_V2_CONNECT_SINGLE_TIMEOUT = 75 * 1000;
|
|
61
|
-
const PROTOCOL_V2_DEVICE_INFO_READY_TIMEOUT =
|
|
104
|
+
const PROTOCOL_V2_DEVICE_INFO_READY_TIMEOUT = 30 * 1000;
|
|
62
105
|
const PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT = 3;
|
|
106
|
+
const PROTOCOL_V2_INSTALL_STATUS_CONFLICT_CODE = 'FirmwareInstallStatusConflict';
|
|
63
107
|
const PROTOCOL_V2_OKPP_HEADER_SIZE = 0x52a0;
|
|
64
108
|
const PROTOCOL_V2_OKPP_PAYLOAD_HASH_OFFSET = 0x200;
|
|
65
109
|
const PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET = 0x240;
|
|
66
110
|
const PROTOCOL_V2_OKPP_HASH_SIZE = 64;
|
|
111
|
+
const PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES = 1024 * 1024;
|
|
112
|
+
const PROTOCOL_V2_RESOURCE_FILE_MAX_COUNT = 512;
|
|
113
|
+
const PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES = 256 * 1024 * 1024;
|
|
114
|
+
|
|
115
|
+
const PROTOCOL_V2_NEO_UNSUPPORTED_TARGETS = new Set<FirmwareUpdateV4Target>(['se03', 'se04']);
|
|
116
|
+
|
|
117
|
+
const getProtocolV2ZipEntrySizes = (entry: JSZip.JSZipObject) => {
|
|
118
|
+
// loadAsync records central-directory sizes on JSZip's documented private data object.
|
|
119
|
+
// Validate those bounds before calling async(), which allocates the decompressed entry.
|
|
120
|
+
const { compressedSize, uncompressedSize } = (entry as JSZipSizedEntry)._data ?? {};
|
|
121
|
+
if (
|
|
122
|
+
!Number.isSafeInteger(compressedSize) ||
|
|
123
|
+
Number(compressedSize) < 0 ||
|
|
124
|
+
!Number.isSafeInteger(uncompressedSize) ||
|
|
125
|
+
Number(uncompressedSize) <= 0
|
|
126
|
+
) {
|
|
127
|
+
throw ERRORS.TypedError(
|
|
128
|
+
HardwareErrorCode.RuntimeError,
|
|
129
|
+
`Protocol V2 local resource ZIP entry size is invalid: ${entry.name}`,
|
|
130
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
compressedSize: Number(compressedSize),
|
|
135
|
+
uncompressedSize: Number(uncompressedSize),
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export function assertProtocolV2FirmwareTargetsSupported(
|
|
140
|
+
deviceType: EDeviceType | string | undefined,
|
|
141
|
+
params: FirmwareUpdateV4Params
|
|
142
|
+
) {
|
|
143
|
+
const unsupportedTargets = new Set(
|
|
144
|
+
(params.targetsToUpdate ?? []).filter(target => PROTOCOL_V2_NEO_UNSUPPORTED_TARGETS.has(target))
|
|
145
|
+
);
|
|
146
|
+
if (params.se03Binary) unsupportedTargets.add('se03');
|
|
147
|
+
if (params.se04Binary) unsupportedTargets.add('se04');
|
|
148
|
+
|
|
149
|
+
if (!unsupportedTargets.size || deviceType === EDeviceType.Pro2) return;
|
|
150
|
+
|
|
151
|
+
const targetList = Array.from(unsupportedTargets).join(', ');
|
|
152
|
+
const message =
|
|
153
|
+
deviceType === EDeviceType.Neo
|
|
154
|
+
? `Neo only supports SE01 and SE02; unsupported firmware targets: ${targetList}`
|
|
155
|
+
: `Cannot safely update ${targetList} without a confirmed Pro2 device type`;
|
|
156
|
+
throw ERRORS.TypedError(HardwareErrorCode.DeviceNotSupportMethod, message);
|
|
157
|
+
}
|
|
67
158
|
|
|
68
159
|
const getProtocolV2DeviceTransferProgress = (
|
|
69
160
|
bytesBeforeChunk: number,
|
|
@@ -82,11 +173,6 @@ const getProtocolV2DeviceTransferProgress = (
|
|
|
82
173
|
return Math.min(Math.max(Math.ceil((bytesAfterChunk / totalBytes) * 100), 1), 99);
|
|
83
174
|
};
|
|
84
175
|
|
|
85
|
-
const formatProtocolV2TransferSpeed = (bytes: number, elapsedMs: number) => {
|
|
86
|
-
const safeElapsedMs = Math.max(elapsedMs, 1);
|
|
87
|
-
return (bytes / 1024 / (safeElapsedMs / 1000)).toFixed(2);
|
|
88
|
-
};
|
|
89
|
-
|
|
90
176
|
type ProtocolV2FirmwareUpdateStatusTarget = {
|
|
91
177
|
target_id: number | string;
|
|
92
178
|
status?: number | string;
|
|
@@ -94,46 +180,66 @@ type ProtocolV2FirmwareUpdateStatusTarget = {
|
|
|
94
180
|
path?: string;
|
|
95
181
|
};
|
|
96
182
|
|
|
97
|
-
type ProtocolV2FirmwareUpdateStartResponse =
|
|
98
|
-
| TypedResponseMessage<'Success'>
|
|
99
|
-
| TypedResponseMessage<'DeviceFirmwareUpdateStatus'>
|
|
100
|
-
| undefined;
|
|
183
|
+
type ProtocolV2FirmwareUpdateStartResponse = TypedResponseMessage<'Success'>;
|
|
101
184
|
|
|
102
185
|
type ProtocolV2TargetBinary = { fileName: string; binary: ArrayBuffer; targetId: number };
|
|
103
186
|
type ProtocolV2InstallItem = ProtocolV2TargetBinary & {
|
|
104
187
|
kind: ProtocolV2RemoteComponentTarget['kind'];
|
|
105
188
|
};
|
|
106
|
-
|
|
107
|
-
|
|
189
|
+
|
|
190
|
+
type ProtocolV2RemoteComponentBinary = ProtocolV2RemoteComponentTarget & {
|
|
191
|
+
binary: ArrayBuffer;
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
type ProtocolV2RemoteComponentTarget = {
|
|
195
|
+
fileName: string;
|
|
196
|
+
targetId: number;
|
|
197
|
+
kind: 'bootloader' | 'firmware';
|
|
108
198
|
};
|
|
109
199
|
|
|
110
|
-
type
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
200
|
+
type ProtocolV2InstallSource = {
|
|
201
|
+
fileName: string;
|
|
202
|
+
source: FirmwareByteSource;
|
|
203
|
+
targetId: number;
|
|
204
|
+
kind: ProtocolV2RemoteComponentTarget['kind'];
|
|
115
205
|
};
|
|
116
206
|
|
|
117
|
-
|
|
118
|
-
type ProtocolV2ResourceBundleBinary = {
|
|
207
|
+
type ProtocolV2ResourceBundleSource = {
|
|
119
208
|
name: string;
|
|
120
|
-
|
|
209
|
+
source: FirmwareByteSource;
|
|
121
210
|
devicePath: string;
|
|
122
|
-
/** 远端配置模式下的下载 URL(手动模式不填) */
|
|
123
|
-
url?: string;
|
|
124
211
|
version?: IVersionArray;
|
|
125
212
|
payloadHash?: string;
|
|
126
213
|
headerHash?: string;
|
|
127
214
|
};
|
|
128
215
|
|
|
129
|
-
type
|
|
216
|
+
type ProtocolV2ResourceBundleBinary = Omit<ProtocolV2ResourceBundleSource, 'source'> & {
|
|
130
217
|
binary: ArrayBuffer;
|
|
131
218
|
};
|
|
132
219
|
|
|
133
|
-
type
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
220
|
+
type ProtocolV2LocalResourceArchive = {
|
|
221
|
+
binary: ArrayBuffer;
|
|
222
|
+
materializedEntries: FirmwareMemoryArtifactEntry[];
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
type JSZipSizedEntry = JSZip.JSZipObject & {
|
|
226
|
+
_data?: {
|
|
227
|
+
compressedSize?: unknown;
|
|
228
|
+
uncompressedSize?: unknown;
|
|
229
|
+
};
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
type ProtocolV2ExecutionPhaseKind =
|
|
233
|
+
| 'resource-sync'
|
|
234
|
+
| 'bootloader-install'
|
|
235
|
+
| 'bootloader-verify'
|
|
236
|
+
| 'component-install'
|
|
237
|
+
| 'final-verify';
|
|
238
|
+
|
|
239
|
+
type ProtocolV2ExecutionPhase = {
|
|
240
|
+
kind: ProtocolV2ExecutionPhaseKind;
|
|
241
|
+
installSources: ProtocolV2InstallSource[];
|
|
242
|
+
resourceSources: ProtocolV2ResourceBundleSource[];
|
|
137
243
|
};
|
|
138
244
|
|
|
139
245
|
type ProtocolV2OkppHeader = {
|
|
@@ -188,11 +294,54 @@ const PROTOCOL_V2_REMOTE_COMPONENT_TARGETS: Readonly<
|
|
|
188
294
|
},
|
|
189
295
|
};
|
|
190
296
|
|
|
297
|
+
const PROTOCOL_V2_FIRMWARE_STAGING_PATHS = new Set(
|
|
298
|
+
Object.values(PROTOCOL_V2_REMOTE_COMPONENT_TARGETS).map(
|
|
299
|
+
target => `${PROTOCOL_V2_FIRMWARE_STAGING_VOLUME}${target.fileName}`
|
|
300
|
+
)
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
const PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH = 'vol0:/loaders/bootloader/boot_resource.okpkg';
|
|
304
|
+
const PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_STAGING_PATH = `${PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH}.staging`;
|
|
305
|
+
|
|
306
|
+
const resolveProtocolV2ResourceWritePath = (devicePath: string) => {
|
|
307
|
+
const normalizedPath = devicePath.replace(/^vol0:(?!\/)/i, 'vol0:/').toLowerCase();
|
|
308
|
+
return normalizedPath === PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH
|
|
309
|
+
? PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_STAGING_PATH
|
|
310
|
+
: devicePath;
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
const PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID = new Map<
|
|
314
|
+
number,
|
|
315
|
+
Exclude<FirmwareUpdateV4Target, 'boot_resources'>
|
|
316
|
+
>([
|
|
317
|
+
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER, 'boot'],
|
|
318
|
+
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1, 'app_v1'],
|
|
319
|
+
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P2, 'app_v2'],
|
|
320
|
+
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_COPROCESSOR, 'coprocessor'],
|
|
321
|
+
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE01, 'se01'],
|
|
322
|
+
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE02, 'se02'],
|
|
323
|
+
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE03, 'se03'],
|
|
324
|
+
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE04, 'se04'],
|
|
325
|
+
]);
|
|
326
|
+
|
|
327
|
+
const PROTOCOL_V2_INSTALL_TARGET_BY_UPDATE_TARGET = new Map<
|
|
328
|
+
Exclude<FirmwareUpdateV4Target, 'resource'>,
|
|
329
|
+
ProtocolV2RemoteComponentTarget
|
|
330
|
+
>(
|
|
331
|
+
Object.values(PROTOCOL_V2_REMOTE_COMPONENT_TARGETS).map(target => [
|
|
332
|
+
PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(target.targetId) as Exclude<
|
|
333
|
+
FirmwareUpdateV4Target,
|
|
334
|
+
'resource'
|
|
335
|
+
>,
|
|
336
|
+
target,
|
|
337
|
+
])
|
|
338
|
+
);
|
|
339
|
+
|
|
191
340
|
const PROTOCOL_V2_ROMLOADER_UNSUPPORTED_MESSAGE =
|
|
192
341
|
'FW_MGMT_TARGET_ROMLOADER is not accepted by the current Pro2 bootloader update request. Flash romloader with the loader-specific flow instead of firmwareUpdateV4.';
|
|
193
342
|
|
|
194
|
-
// hd-transport
|
|
195
|
-
//
|
|
343
|
+
// hd-transport historically decodes scalar enums as enum-name strings.
|
|
344
|
+
// Map them back to firmware protocol values before internal comparisons.
|
|
196
345
|
const PROTOCOL_V2_TARGET_ID_BY_DECODED_NAME = new Map<string, number>(
|
|
197
346
|
Object.entries(ProtocolV2FirmwareTargetType).map(([key, value]) => [key, value])
|
|
198
347
|
);
|
|
@@ -218,27 +367,20 @@ const isProtocolV2ReconnectProbeError = (error: unknown) => {
|
|
|
218
367
|
);
|
|
219
368
|
};
|
|
220
369
|
|
|
221
|
-
const
|
|
370
|
+
const isProtocolV2FirmwareStatusEndpointUnavailable = (error: unknown) => {
|
|
222
371
|
const message = getProtocolV2UnknownErrorText(error).toLowerCase();
|
|
223
372
|
return (
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
message.includes('
|
|
227
|
-
(message.includes('response timeout') && message.includes('devicefirmwareupdatestatusget')) ||
|
|
228
|
-
message.includes('device not found') ||
|
|
229
|
-
message.includes('transportnotfound')
|
|
373
|
+
message.includes('handler not registered') ||
|
|
374
|
+
message.includes('message handler not found') ||
|
|
375
|
+
message.includes('unsupported message')
|
|
230
376
|
);
|
|
231
377
|
};
|
|
232
378
|
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
message.includes('libusb_transfer_timed_out') ||
|
|
239
|
-
(message.includes('response timeout') && message.includes('devicefirmwareupdaterequest'))
|
|
240
|
-
);
|
|
241
|
-
};
|
|
379
|
+
const isProtocolV2TerminalInstallStatusError = (error: unknown) =>
|
|
380
|
+
error instanceof HardwareError &&
|
|
381
|
+
(error.errorCode === HardwareErrorCode.FirmwareError ||
|
|
382
|
+
error.errorCode === HardwareErrorCode.FirmwareVerificationFailed ||
|
|
383
|
+
error.params?.firmwareUpdateCode === PROTOCOL_V2_INSTALL_STATUS_CONFLICT_CODE);
|
|
242
384
|
|
|
243
385
|
const isProtocolV2TargetStatusFinished = (status: ProtocolV2FirmwareUpdateStatusTarget['status']) =>
|
|
244
386
|
normalizeProtocolV2TargetStatus(status) === PROTOCOL_V2_TARGET_STATUS_FINISHED;
|
|
@@ -282,13 +424,6 @@ const versionArrayToNumber = (version?: IVersionArray) => {
|
|
|
282
424
|
return version[0] * 0x10000 + version[1] * 0x100 + version[2];
|
|
283
425
|
};
|
|
284
426
|
|
|
285
|
-
const versionStringToArray = (version?: string | null): IVersionArray | undefined => {
|
|
286
|
-
if (!version) return undefined;
|
|
287
|
-
const parts = version.split('.').map(part => Number(part));
|
|
288
|
-
if (parts.length !== 3 || parts.some(part => !Number.isFinite(part))) return undefined;
|
|
289
|
-
return parts as IVersionArray;
|
|
290
|
-
};
|
|
291
|
-
|
|
292
427
|
const compareProtocolV2Versions = (current?: IVersionArray, target?: IVersionArray) => {
|
|
293
428
|
const currentNumber = versionArrayToNumber(current);
|
|
294
429
|
const targetNumber = versionArrayToNumber(target);
|
|
@@ -375,6 +510,43 @@ const parseProtocolV2OkppHeader = (bytes: Uint8Array): ProtocolV2OkppHeader | nu
|
|
|
375
510
|
};
|
|
376
511
|
};
|
|
377
512
|
|
|
513
|
+
export const isProtocolV2FirmwareFingerprintValid = (
|
|
514
|
+
binary: ArrayBuffer | Uint8Array,
|
|
515
|
+
fingerprint: string | undefined
|
|
516
|
+
) => {
|
|
517
|
+
const expectedFingerprint = normalizeProtocolV2Hex(fingerprint);
|
|
518
|
+
if (!expectedFingerprint) return true;
|
|
519
|
+
return bytesToHex(sha256(toProtocolV2Bytes(binary))) === expectedFingerprint;
|
|
520
|
+
};
|
|
521
|
+
|
|
522
|
+
export const assertProtocolV2ReconnectIdentity = (
|
|
523
|
+
expectedSerialNumber?: string,
|
|
524
|
+
actualSerialNumber?: string,
|
|
525
|
+
expectedPath?: string,
|
|
526
|
+
actualPath?: string
|
|
527
|
+
) => {
|
|
528
|
+
if (expectedSerialNumber && actualSerialNumber) {
|
|
529
|
+
if (actualSerialNumber !== expectedSerialNumber) {
|
|
530
|
+
throw ERRORS.TypedError(
|
|
531
|
+
HardwareErrorCode.DeviceNotFound,
|
|
532
|
+
`Protocol V2 reconnect physical identity mismatch: expected ${expectedSerialNumber}, received ${actualSerialNumber}`
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
if (expectedPath && actualPath && expectedPath === actualPath) {
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
throw ERRORS.TypedError(
|
|
543
|
+
HardwareErrorCode.DeviceNotFound,
|
|
544
|
+
`Protocol V2 reconnect physical identity unavailable: expected path ${
|
|
545
|
+
expectedPath ?? 'unknown'
|
|
546
|
+
}, received ${actualPath ?? 'unknown'}`
|
|
547
|
+
);
|
|
548
|
+
};
|
|
549
|
+
|
|
378
550
|
/**
|
|
379
551
|
* FirmwareUpdateV4 is the complete Protocol V2 firmware update flow.
|
|
380
552
|
*
|
|
@@ -384,9 +556,28 @@ const parseProtocolV2OkppHeader = (bytes: Uint8Array): ProtocolV2OkppHeader | nu
|
|
|
384
556
|
* - completion waits for target status to finish, reboots to normal, then polls DeviceInfo
|
|
385
557
|
*/
|
|
386
558
|
export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareUpdateV4Params> {
|
|
559
|
+
private protocolV2ExpectedSerialNumber?: string;
|
|
560
|
+
|
|
561
|
+
private protocolV2ExpectedPath?: string;
|
|
562
|
+
|
|
563
|
+
getSupportedProtocols() {
|
|
564
|
+
return ['V2'] as const;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
private protocolV2PreparedSources: FirmwareByteSource[] = [];
|
|
568
|
+
|
|
569
|
+
private protocolV2ExecutionInLoader = false;
|
|
570
|
+
|
|
571
|
+
private protocolV2CompletedTargetVersions = new Map<number, number>();
|
|
572
|
+
|
|
573
|
+
private protocolV2LatestFinalFeatures?: Features;
|
|
574
|
+
|
|
575
|
+
private protocolV2FinalStatusVerified = false;
|
|
576
|
+
|
|
387
577
|
init() {
|
|
388
578
|
this.allowDeviceMode = [UI_REQUEST.BOOTLOADER, UI_REQUEST.NOT_INITIALIZE];
|
|
389
579
|
this.requireDeviceMode = [];
|
|
580
|
+
this.unlockPolicy = 'unlock-before-run';
|
|
390
581
|
this.useDevicePassphraseState = false;
|
|
391
582
|
this.skipForceUpdateCheck = true;
|
|
392
583
|
|
|
@@ -417,12 +608,107 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
417
608
|
{ name: 'se02Binary', type: 'buffer' },
|
|
418
609
|
{ name: 'se03Binary', type: 'buffer' },
|
|
419
610
|
{ name: 'se04Binary', type: 'buffer' },
|
|
611
|
+
{ name: 'resourceArchiveBinary', type: 'buffer' },
|
|
612
|
+
{ name: 'resourceFiles', type: 'array', allowEmpty: true },
|
|
613
|
+
{ name: 'resourceBundleArtifacts', type: 'array', allowEmpty: true },
|
|
420
614
|
{ name: 'firmwareType', type: 'string' },
|
|
615
|
+
{ name: 'targetsToUpdate', type: 'array', allowEmpty: true },
|
|
421
616
|
{ name: 'platform', type: 'string' },
|
|
422
|
-
{ name: '
|
|
617
|
+
{ name: 'expectedDeviceId', type: 'string' },
|
|
423
618
|
]);
|
|
619
|
+
if (
|
|
620
|
+
payload.expectedDeviceId !== undefined &&
|
|
621
|
+
(payload.expectedDeviceId.length === 0 || payload.expectedDeviceId.length > 160)
|
|
622
|
+
) {
|
|
623
|
+
throw ERRORS.TypedError(
|
|
624
|
+
HardwareErrorCode.CallMethodInvalidParameter,
|
|
625
|
+
'Protocol V2 expected device identity is invalid'
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
if (payload.resourceFiles?.length || payload.resourceBundleArtifacts?.length) {
|
|
629
|
+
throw ERRORS.TypedError(
|
|
630
|
+
HardwareErrorCode.CallMethodInvalidParameter,
|
|
631
|
+
'Protocol V2 resourceFiles and resourceBundleArtifacts are deprecated; provide a complete signed resource ZIP through resourceArchiveBinary or preparedPlan'
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
const preparedPlan = payload.preparedPlan
|
|
635
|
+
? validateFirmwareUpdatePreparedPlan(payload.preparedPlan)
|
|
636
|
+
: undefined;
|
|
637
|
+
const hasLocalArtifacts = [
|
|
638
|
+
payload.bootloaderBinary,
|
|
639
|
+
payload.romloaderBinary,
|
|
640
|
+
payload.applicationP1Binary,
|
|
641
|
+
payload.applicationP2Binary,
|
|
642
|
+
payload.coprocessorBinary,
|
|
643
|
+
payload.se01Binary,
|
|
644
|
+
payload.se02Binary,
|
|
645
|
+
payload.se03Binary,
|
|
646
|
+
payload.se04Binary,
|
|
647
|
+
payload.resourceArchiveBinary,
|
|
648
|
+
].some(Boolean);
|
|
649
|
+
if (preparedPlan && hasLocalArtifacts) {
|
|
650
|
+
throw ERRORS.TypedError(
|
|
651
|
+
HardwareErrorCode.CallMethodInvalidParameter,
|
|
652
|
+
'Prepared firmware plans cannot be combined with local firmware or resource binaries'
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
const hostBinding =
|
|
656
|
+
payload.hostBindingGeneration !== undefined
|
|
657
|
+
? resolveFirmwareUpdateHostBinding(
|
|
658
|
+
payload.hostBindingGeneration,
|
|
659
|
+
preparedPlan?.preparedPlanDigest
|
|
660
|
+
)
|
|
661
|
+
: undefined;
|
|
662
|
+
if (preparedPlan) {
|
|
663
|
+
assertFirmwareUpdatePreparedPlanBinding({
|
|
664
|
+
preparedPlan,
|
|
665
|
+
executor: 'v4',
|
|
666
|
+
platform: payload.platform,
|
|
667
|
+
scopeTargets: [],
|
|
668
|
+
bindings: [],
|
|
669
|
+
});
|
|
670
|
+
if (payload.componentArtifacts) {
|
|
671
|
+
const componentBindings: Array<{
|
|
672
|
+
target: Exclude<FirmwareUpdateV4Target, 'resource'>;
|
|
673
|
+
artifact: FirmwareArtifactReference;
|
|
674
|
+
}> = Object.entries(payload.componentArtifacts).flatMap(([target, artifact]) =>
|
|
675
|
+
artifact
|
|
676
|
+
? [
|
|
677
|
+
{
|
|
678
|
+
target: target as Exclude<FirmwareUpdateV4Target, 'resource'>,
|
|
679
|
+
artifact: artifact as FirmwareArtifactReference,
|
|
680
|
+
},
|
|
681
|
+
]
|
|
682
|
+
: []
|
|
683
|
+
);
|
|
684
|
+
assertFirmwareUpdatePreparedPlanBinding({
|
|
685
|
+
preparedPlan,
|
|
686
|
+
executor: 'v4',
|
|
687
|
+
platform: payload.platform,
|
|
688
|
+
scopeTargets: componentBindings.map(binding => binding.target),
|
|
689
|
+
bindings: componentBindings,
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
const preparedExpectedTargetVersions = preparedPlan?.artifacts.reduce<
|
|
695
|
+
NonNullable<FirmwareUpdateV4Params['expectedTargetVersions']>
|
|
696
|
+
>((result, artifact) => {
|
|
697
|
+
if (
|
|
698
|
+
artifact.role === 'component' &&
|
|
699
|
+
artifact.target !== 'resource' &&
|
|
700
|
+
artifact.targetVersion &&
|
|
701
|
+
PROTOCOL_V2_INSTALL_TARGET_BY_UPDATE_TARGET.has(
|
|
702
|
+
artifact.target as Exclude<FirmwareUpdateV4Target, 'resource'>
|
|
703
|
+
)
|
|
704
|
+
) {
|
|
705
|
+
result[artifact.target as FirmwareUpdateV4Target] = artifact.targetVersion;
|
|
706
|
+
}
|
|
707
|
+
return result;
|
|
708
|
+
}, {});
|
|
424
709
|
|
|
425
710
|
this.params = {
|
|
711
|
+
preparedPlan,
|
|
426
712
|
chunkSize: payload.chunkSize,
|
|
427
713
|
forcedUpdateRes: payload.forcedUpdateRes,
|
|
428
714
|
bootloaderBinary: payload.bootloaderBinary,
|
|
@@ -434,19 +720,38 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
434
720
|
se02Binary: payload.se02Binary,
|
|
435
721
|
se03Binary: payload.se03Binary,
|
|
436
722
|
se04Binary: payload.se04Binary,
|
|
437
|
-
|
|
438
|
-
firmwareType: payload.firmwareType,
|
|
723
|
+
resourceArchiveBinary: payload.resourceArchiveBinary,
|
|
724
|
+
firmwareType: preparedPlan?.firmwareType ?? payload.firmwareType,
|
|
725
|
+
targetsToUpdate: preparedPlan
|
|
726
|
+
? ([...preparedPlan.targetsToUpdate] as FirmwareUpdateV4Target[])
|
|
727
|
+
: payload.targetsToUpdate?.map((target: FirmwareUpdateV4Target) =>
|
|
728
|
+
target === 'boot_resources' ? 'resource' : target
|
|
729
|
+
),
|
|
730
|
+
expectedTargetVersions: preparedPlan
|
|
731
|
+
? preparedExpectedTargetVersions
|
|
732
|
+
: payload.expectedTargetVersions,
|
|
439
733
|
platform: payload.platform,
|
|
734
|
+
expectedDeviceId: payload.expectedDeviceId,
|
|
735
|
+
artifactReader: hostBinding?.artifactReader ?? payload.artifactReader,
|
|
736
|
+
componentArtifacts: preparedPlan ? undefined : payload.componentArtifacts,
|
|
440
737
|
};
|
|
441
738
|
}
|
|
442
739
|
|
|
443
|
-
private getProtocolV2FirmwareChunkSize() {
|
|
740
|
+
private getProtocolV2FirmwareChunkSize(direction: 'read' | 'write', filePath?: string) {
|
|
444
741
|
const payloadChunkSize = Number(this.params?.chunkSize);
|
|
445
742
|
const env = DataManager.getSettings('env');
|
|
446
|
-
const
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
743
|
+
const isBle = this.params?.platform === 'native' || (env && DataManager.isBleConnect(env));
|
|
744
|
+
let maxChunkSize = PROTOCOL_V2_WEBUSB_FILE_CHUNK_SIZE;
|
|
745
|
+
if (isBle) {
|
|
746
|
+
if (direction === 'read') {
|
|
747
|
+
maxChunkSize = PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE;
|
|
748
|
+
} else {
|
|
749
|
+
// Firmware staging writes tolerate a larger BLE chunk than generic filesystem writes.
|
|
750
|
+
maxChunkSize = PROTOCOL_V2_FIRMWARE_STAGING_PATHS.has(filePath ?? '')
|
|
751
|
+
? PROTOCOL_V2_BLE_FIRMWARE_FILE_CHUNK_SIZE
|
|
752
|
+
: PROTOCOL_V2_BLE_FILE_CHUNK_SIZE;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
450
755
|
if (!Number.isFinite(payloadChunkSize) || payloadChunkSize <= 0) {
|
|
451
756
|
return maxChunkSize;
|
|
452
757
|
}
|
|
@@ -457,21 +762,46 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
457
762
|
}
|
|
458
763
|
|
|
459
764
|
async run() {
|
|
460
|
-
if (!this.device.isProtocolV2()) {
|
|
461
|
-
throw ERRORS.TypedError(
|
|
462
|
-
HardwareErrorCode.RuntimeError,
|
|
463
|
-
'firmwareUpdateV4 requires a Protocol V2 device'
|
|
464
|
-
);
|
|
465
|
-
}
|
|
466
|
-
|
|
467
765
|
Log.debug('FirmwareUpdateV4 strategy: Protocol V2');
|
|
468
766
|
return this.runProtocolV2();
|
|
469
767
|
}
|
|
470
768
|
|
|
471
769
|
private async runProtocolV2() {
|
|
770
|
+
await this.captureProtocolV2PhysicalIdentity();
|
|
472
771
|
const deviceFeatures = await this.getProtocolV2DeviceFeatures();
|
|
772
|
+
const currentDeviceType = this.device.getCurrentDeviceType();
|
|
773
|
+
const capabilityDeviceType =
|
|
774
|
+
currentDeviceType === EDeviceType.Pro2 || currentDeviceType === EDeviceType.Neo
|
|
775
|
+
? currentDeviceType
|
|
776
|
+
: getDeviceType(deviceFeatures);
|
|
777
|
+
assertProtocolV2FirmwareTargetsSupported(capabilityDeviceType, this.params);
|
|
473
778
|
const deviceFirmwareType = getFirmwareType(deviceFeatures);
|
|
474
779
|
const firmwareType = this.params.firmwareType ?? deviceFirmwareType;
|
|
780
|
+
this.validateExpectedTargetVersions();
|
|
781
|
+
|
|
782
|
+
if (
|
|
783
|
+
!this.params.preparedPlan &&
|
|
784
|
+
this.params.resourceArchiveBinary &&
|
|
785
|
+
this.params.targetsToUpdate?.includes('resource')
|
|
786
|
+
) {
|
|
787
|
+
const localMemoryHost = await this.prepareProtocolV2LocalMemoryHost({
|
|
788
|
+
features: deviceFeatures,
|
|
789
|
+
firmwareType,
|
|
790
|
+
});
|
|
791
|
+
try {
|
|
792
|
+
return await this.runProtocolV2PreparedArtifacts(deviceFeatures, firmwareType);
|
|
793
|
+
} finally {
|
|
794
|
+
localMemoryHost.release();
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
const hasPreparedComponentArtifacts = Object.values(this.params.componentArtifacts ?? {}).some(
|
|
799
|
+
Boolean
|
|
800
|
+
);
|
|
801
|
+
if (this.params.preparedPlan || hasPreparedComponentArtifacts) {
|
|
802
|
+
return this.runProtocolV2PreparedArtifacts(deviceFeatures, firmwareType);
|
|
803
|
+
}
|
|
804
|
+
const wantsResources = !!this.params.targetsToUpdate?.includes('resource');
|
|
475
805
|
|
|
476
806
|
let fwBinaryMap: ProtocolV2TargetBinary[] = [];
|
|
477
807
|
let bootloaderBinary: ArrayBuffer | null = null;
|
|
@@ -480,10 +810,45 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
480
810
|
this.postTipMessage(FirmwareUpdateTipMessage.StartDownloadFirmware);
|
|
481
811
|
fwBinaryMap = this.collectExplicitTargetBinaries();
|
|
482
812
|
bootloaderBinary = this.prepareBootloaderBinary();
|
|
483
|
-
|
|
813
|
+
const explicitInstallItems = this.buildProtocolV2InstallItems({
|
|
814
|
+
bootloaderBinary,
|
|
815
|
+
fwBinaryMap,
|
|
816
|
+
});
|
|
817
|
+
const missingFirmwareTargets = this.getMissingProtocolV2FirmwareTargets(explicitInstallItems);
|
|
818
|
+
const needsRemoteFirmware = this.params.targetsToUpdate?.length
|
|
819
|
+
? missingFirmwareTargets.length > 0
|
|
820
|
+
: explicitInstallItems.length === 0;
|
|
821
|
+
if (wantsResources) {
|
|
822
|
+
throw ERRORS.TypedError(
|
|
823
|
+
HardwareErrorCode.RuntimeError,
|
|
824
|
+
'Protocol V2 resource archive must be provided through a local or external PreparedPlan',
|
|
825
|
+
{
|
|
826
|
+
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
827
|
+
}
|
|
828
|
+
);
|
|
829
|
+
}
|
|
830
|
+
if (
|
|
831
|
+
needsRemoteFirmware &&
|
|
832
|
+
(this.params.artifactReader ||
|
|
833
|
+
DataManager.getSettings('firmwareManifestMode') === 'external-only')
|
|
834
|
+
) {
|
|
835
|
+
throw ERRORS.TypedError(
|
|
836
|
+
HardwareErrorCode.RuntimeError,
|
|
837
|
+
'Protocol V2 firmware artifacts must be prepared by the external firmware host',
|
|
838
|
+
{
|
|
839
|
+
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
840
|
+
}
|
|
841
|
+
);
|
|
842
|
+
}
|
|
843
|
+
if (needsRemoteFirmware) {
|
|
844
|
+
// Remote updates must use a freshly fetched config before any reboot or file write.
|
|
845
|
+
await DataManager.forceReloadData();
|
|
846
|
+
}
|
|
847
|
+
if (needsRemoteFirmware) {
|
|
484
848
|
const remoteBinaries = await this.prepareRemoteProtocolV2Binaries(
|
|
485
849
|
firmwareType,
|
|
486
|
-
deviceFeatures
|
|
850
|
+
deviceFeatures,
|
|
851
|
+
explicitInstallItems
|
|
487
852
|
);
|
|
488
853
|
bootloaderBinary = remoteBinaries.bootloaderBinary;
|
|
489
854
|
fwBinaryMap = remoteBinaries.fwBinaryMap;
|
|
@@ -491,241 +856,844 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
491
856
|
}
|
|
492
857
|
this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
493
858
|
} catch (err) {
|
|
494
|
-
|
|
859
|
+
if (
|
|
860
|
+
typeof err === 'object' &&
|
|
861
|
+
err !== null &&
|
|
862
|
+
'params' in err &&
|
|
863
|
+
(err as HardwareError).params?.firmwareUpdateCode === 'FirmwareArtifactsNotPrepared'
|
|
864
|
+
) {
|
|
865
|
+
throw err;
|
|
866
|
+
}
|
|
867
|
+
if (err instanceof HardwareError && err.errorCode === HardwareErrorCode.NetworkError) {
|
|
868
|
+
throw err;
|
|
869
|
+
}
|
|
870
|
+
throw normalizeFirmwarePreparationError(err);
|
|
495
871
|
}
|
|
496
872
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
if (!bootloaderBinary && fwBinaryMap.length === 0 && !resourceBundles?.length) {
|
|
873
|
+
if (!bootloaderBinary && fwBinaryMap.length === 0 && !installItems?.length) {
|
|
500
874
|
throw ERRORS.TypedError(
|
|
501
875
|
HardwareErrorCode.FirmwareUpdateDownloadFailed,
|
|
502
876
|
'No firmware to update'
|
|
503
877
|
);
|
|
504
878
|
}
|
|
505
879
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
await this.executeProtocolV2Update({
|
|
880
|
+
return this.executeProtocolV2Update({
|
|
509
881
|
fwBinaryMap,
|
|
510
882
|
bootloaderBinary,
|
|
511
883
|
...(installItems ? { installItems } : undefined),
|
|
512
|
-
...(resourceBundles?.length ? { resourceBundles } : undefined),
|
|
513
884
|
});
|
|
514
|
-
|
|
515
|
-
await this.exitProtocolV2BootloaderToNormal();
|
|
516
|
-
|
|
517
|
-
const versions = await this.waitForProtocolV2FinalFeatures();
|
|
518
|
-
this.postTipMessage(FirmwareUpdateTipMessage.FirmwareUpdateCompleted);
|
|
519
|
-
DevicePool.resetState();
|
|
520
|
-
|
|
521
|
-
return versions;
|
|
522
885
|
}
|
|
523
886
|
|
|
524
|
-
private async
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
887
|
+
private async openProtocolV2PreparedSource(artifact: FirmwareArtifactReference) {
|
|
888
|
+
const source = await openFirmwareByteSource({
|
|
889
|
+
artifact,
|
|
890
|
+
reader: this.params.artifactReader,
|
|
891
|
+
});
|
|
892
|
+
if (!source) {
|
|
893
|
+
throw ERRORS.TypedError(
|
|
894
|
+
HardwareErrorCode.RuntimeError,
|
|
895
|
+
`Firmware artifact ${artifact.artifactRef} is not prepared`,
|
|
896
|
+
{
|
|
897
|
+
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
898
|
+
}
|
|
899
|
+
);
|
|
531
900
|
}
|
|
532
|
-
|
|
901
|
+
this.protocolV2PreparedSources.push(source);
|
|
902
|
+
return source;
|
|
533
903
|
}
|
|
534
904
|
|
|
535
|
-
private
|
|
536
|
-
|
|
905
|
+
private async openProtocolV2MemorySource(binary: ArrayBuffer) {
|
|
906
|
+
const source = await openFirmwareByteSource({ binary });
|
|
907
|
+
if (!source) {
|
|
908
|
+
throw ERRORS.TypedError(
|
|
909
|
+
HardwareErrorCode.RuntimeError,
|
|
910
|
+
'Protocol V2 firmware binary is empty'
|
|
911
|
+
);
|
|
912
|
+
}
|
|
913
|
+
this.protocolV2PreparedSources.push(source);
|
|
914
|
+
return source;
|
|
537
915
|
}
|
|
538
916
|
|
|
539
|
-
private
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
!!this.params.bootloaderBinary ||
|
|
543
|
-
fwBinaryMap.length > 0
|
|
544
|
-
);
|
|
917
|
+
private async closeProtocolV2PreparedSources() {
|
|
918
|
+
const sources = this.protocolV2PreparedSources.splice(0);
|
|
919
|
+
await Promise.all(sources.map(source => source.close().catch(() => undefined)));
|
|
545
920
|
}
|
|
546
921
|
|
|
547
|
-
private
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
922
|
+
private async prepareProtocolV2InstallSources(
|
|
923
|
+
firmwareType: EFirmwareType,
|
|
924
|
+
features: Features
|
|
925
|
+
): Promise<ProtocolV2InstallSource[]> {
|
|
926
|
+
if (this.params.preparedPlan) {
|
|
927
|
+
const requestedTargets = new Set(
|
|
928
|
+
this.params.preparedPlan.targetsToUpdate.filter(
|
|
929
|
+
(target): target is Exclude<FirmwareUpdateV4Target, 'resource'> => target !== 'resource'
|
|
930
|
+
)
|
|
931
|
+
);
|
|
932
|
+
const installSources: ProtocolV2InstallSource[] = [];
|
|
933
|
+
const preparedTargets = new Set<Exclude<FirmwareUpdateV4Target, 'resource'>>();
|
|
934
|
+
for (const artifact of this.params.preparedPlan.artifacts) {
|
|
935
|
+
if (artifact.target !== 'resource') {
|
|
936
|
+
const target = artifact.target as Exclude<FirmwareUpdateV4Target, 'resource'>;
|
|
937
|
+
const installTarget = PROTOCOL_V2_INSTALL_TARGET_BY_UPDATE_TARGET.get(target);
|
|
938
|
+
if (
|
|
939
|
+
!requestedTargets.has(target) ||
|
|
940
|
+
artifact.role !== 'component' ||
|
|
941
|
+
artifact.container !== 'raw' ||
|
|
942
|
+
!installTarget ||
|
|
943
|
+
preparedTargets.has(target)
|
|
944
|
+
) {
|
|
945
|
+
throw ERRORS.TypedError(
|
|
946
|
+
HardwareErrorCode.RuntimeError,
|
|
947
|
+
`Protocol V2 prepared component artifact is invalid: ${artifact.artifactId}`,
|
|
948
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
949
|
+
);
|
|
950
|
+
}
|
|
951
|
+
installSources.push({
|
|
952
|
+
...installTarget,
|
|
953
|
+
source: await this.openProtocolV2PreparedSource(artifact.artifact),
|
|
954
|
+
});
|
|
955
|
+
preparedTargets.add(target);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
const missingTarget = Array.from(requestedTargets).find(
|
|
959
|
+
target => !preparedTargets.has(target)
|
|
960
|
+
);
|
|
961
|
+
if (missingTarget) {
|
|
962
|
+
throw ERRORS.TypedError(
|
|
963
|
+
HardwareErrorCode.RuntimeError,
|
|
964
|
+
`Protocol V2 ${missingTarget} artifact is not prepared`,
|
|
965
|
+
{
|
|
966
|
+
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
967
|
+
artifactName: missingTarget,
|
|
968
|
+
}
|
|
969
|
+
);
|
|
970
|
+
}
|
|
971
|
+
return installSources;
|
|
563
972
|
}
|
|
564
973
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
private getRemoteComponentEntries(release: IFirmwareReleaseInfo) {
|
|
570
|
-
const { components } = release;
|
|
571
|
-
if (!components) return [];
|
|
572
|
-
|
|
573
|
-
const orderedKeys = [
|
|
574
|
-
...(release.installOrder ?? []),
|
|
575
|
-
...Object.keys(components).filter(key => !release.installOrder?.includes(key)),
|
|
576
|
-
];
|
|
577
|
-
|
|
578
|
-
return orderedKeys
|
|
579
|
-
.map(key => {
|
|
580
|
-
const component = components[key];
|
|
581
|
-
return component ? ([key, component] as const) : undefined;
|
|
582
|
-
})
|
|
583
|
-
.filter((entry): entry is readonly [string, IProtocolV2FirmwareComponent] => !!entry);
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
private getRemoteComponentTarget(key: string, component: IProtocolV2FirmwareComponent) {
|
|
587
|
-
const targetName = component.target?.toUpperCase();
|
|
588
|
-
if (targetName === 'ROMLOADER') {
|
|
974
|
+
const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
|
|
975
|
+
if (!release) {
|
|
589
976
|
throw ERRORS.TypedError(
|
|
590
977
|
HardwareErrorCode.RuntimeError,
|
|
591
|
-
|
|
978
|
+
'Protocol V2 firmware release is unavailable'
|
|
592
979
|
);
|
|
593
980
|
}
|
|
594
|
-
const
|
|
595
|
-
|
|
981
|
+
const componentArtifacts = this.params.componentArtifacts ?? {};
|
|
982
|
+
const requestedTargets = new Set(
|
|
983
|
+
this.params.targetsToUpdate ?? (Object.keys(componentArtifacts) as FirmwareUpdateV4Target[])
|
|
984
|
+
);
|
|
985
|
+
const installSources: ProtocolV2InstallSource[] = [];
|
|
986
|
+
const preparedTargets = new Set<FirmwareUpdateV4Target>();
|
|
987
|
+
|
|
988
|
+
for (const [key, component] of this.getRemoteComponentEntries(release)) {
|
|
989
|
+
const target = this.getRemoteComponentTarget(key, component);
|
|
990
|
+
const updateTarget = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(target.targetId);
|
|
991
|
+
if (updateTarget && updateTarget !== 'resource' && requestedTargets.has(updateTarget)) {
|
|
992
|
+
const artifact = componentArtifacts[updateTarget];
|
|
993
|
+
if (!artifact) {
|
|
994
|
+
throw ERRORS.TypedError(
|
|
995
|
+
HardwareErrorCode.RuntimeError,
|
|
996
|
+
`Protocol V2 ${updateTarget} artifact is not prepared`,
|
|
997
|
+
{
|
|
998
|
+
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
999
|
+
artifactName: updateTarget,
|
|
1000
|
+
}
|
|
1001
|
+
);
|
|
1002
|
+
}
|
|
1003
|
+
installSources.push({
|
|
1004
|
+
...target,
|
|
1005
|
+
source: await this.openProtocolV2PreparedSource(artifact),
|
|
1006
|
+
});
|
|
1007
|
+
preparedTargets.add(updateTarget);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
const unknownTarget = Array.from(requestedTargets).find(
|
|
1012
|
+
target => target !== 'resource' && !preparedTargets.has(target)
|
|
1013
|
+
);
|
|
1014
|
+
if (unknownTarget) {
|
|
596
1015
|
throw ERRORS.TypedError(
|
|
597
1016
|
HardwareErrorCode.RuntimeError,
|
|
598
|
-
`
|
|
1017
|
+
`Protocol V2 release does not contain requested target ${unknownTarget}`
|
|
599
1018
|
);
|
|
600
1019
|
}
|
|
601
|
-
return
|
|
1020
|
+
return installSources;
|
|
602
1021
|
}
|
|
603
1022
|
|
|
604
|
-
private
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
1023
|
+
private async prepareProtocolV2LocalMemoryHost({
|
|
1024
|
+
features,
|
|
1025
|
+
firmwareType,
|
|
1026
|
+
}: {
|
|
1027
|
+
features: Features;
|
|
1028
|
+
firmwareType: EFirmwareType;
|
|
1029
|
+
}): Promise<FirmwareUpdateV4MemoryHost> {
|
|
1030
|
+
const installItems = this.buildProtocolV2InstallItems({
|
|
1031
|
+
bootloaderBinary: this.prepareBootloaderBinary(),
|
|
1032
|
+
fwBinaryMap: this.collectExplicitTargetBinaries(),
|
|
1033
|
+
});
|
|
1034
|
+
const requestedComponentTargets = new Set(
|
|
1035
|
+
(this.params.targetsToUpdate ?? []).filter(
|
|
1036
|
+
(target): target is Exclude<FirmwareUpdateV4Target, 'resource' | 'boot_resources'> =>
|
|
1037
|
+
target !== 'resource' && target !== 'boot_resources'
|
|
1038
|
+
)
|
|
1039
|
+
);
|
|
1040
|
+
const localComponentTargets = new Set(
|
|
1041
|
+
installItems.flatMap(item => {
|
|
1042
|
+
const target = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(item.targetId);
|
|
1043
|
+
return target ? [target] : [];
|
|
1044
|
+
})
|
|
1045
|
+
);
|
|
1046
|
+
const missingTarget = Array.from(requestedComponentTargets).find(
|
|
1047
|
+
target => !localComponentTargets.has(target)
|
|
1048
|
+
);
|
|
1049
|
+
if (missingTarget) {
|
|
1050
|
+
throw ERRORS.TypedError(
|
|
1051
|
+
HardwareErrorCode.RuntimeError,
|
|
1052
|
+
`Protocol V2 local update has no binary for requested target ${missingTarget}`,
|
|
1053
|
+
{
|
|
1054
|
+
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
1055
|
+
artifactName: missingTarget,
|
|
1056
|
+
}
|
|
1057
|
+
);
|
|
618
1058
|
}
|
|
619
|
-
return undefined;
|
|
620
|
-
}
|
|
621
1059
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
release: IFirmwareReleaseInfo,
|
|
649
|
-
component: IProtocolV2FirmwareComponent,
|
|
650
|
-
target: ProtocolV2RemoteComponentTarget,
|
|
651
|
-
features: Features
|
|
652
|
-
) {
|
|
653
|
-
const targetVersion = this.getProtocolV2ComponentTargetVersion(release, component, target);
|
|
654
|
-
if (!targetVersion) return false;
|
|
1060
|
+
const planArtifacts: Parameters<typeof buildProtocolV2LocalFirmwareUpdatePlan>[0]['artifacts'] =
|
|
1061
|
+
[];
|
|
1062
|
+
const memoryArtifacts: FirmwareMemoryArtifact[] = [];
|
|
1063
|
+
for (const item of installItems) {
|
|
1064
|
+
const target = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(item.targetId);
|
|
1065
|
+
if (!target || item.binary.byteLength <= 0) {
|
|
1066
|
+
throw ERRORS.TypedError(
|
|
1067
|
+
HardwareErrorCode.RuntimeError,
|
|
1068
|
+
`Protocol V2 local firmware artifact is invalid: ${item.fileName}`,
|
|
1069
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1070
|
+
);
|
|
1071
|
+
}
|
|
1072
|
+
const artifactId = `component:${target}`;
|
|
1073
|
+
planArtifacts.push({
|
|
1074
|
+
artifactId,
|
|
1075
|
+
target,
|
|
1076
|
+
container: 'raw',
|
|
1077
|
+
logicalName: item.fileName,
|
|
1078
|
+
expectedSize: item.binary.byteLength,
|
|
1079
|
+
expectedSha256: bytesToHex(sha256(new Uint8Array(item.binary))),
|
|
1080
|
+
...(this.params.expectedTargetVersions?.[target]
|
|
1081
|
+
? { targetVersion: this.params.expectedTargetVersions[target] }
|
|
1082
|
+
: {}),
|
|
1083
|
+
});
|
|
1084
|
+
memoryArtifacts.push({ artifactId, binary: item.binary });
|
|
1085
|
+
}
|
|
655
1086
|
|
|
656
|
-
const
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
1087
|
+
const resourceArchive = await this.prepareProtocolV2LocalResourceArchive(
|
|
1088
|
+
this.params.resourceArchiveBinary as ArrayBuffer
|
|
1089
|
+
);
|
|
1090
|
+
const resourceArtifactId = 'resource:archive';
|
|
1091
|
+
planArtifacts.push({
|
|
1092
|
+
artifactId: resourceArtifactId,
|
|
1093
|
+
target: 'resource',
|
|
1094
|
+
container: 'zip',
|
|
1095
|
+
logicalName: 'protocol-v2-local-resource-archive',
|
|
1096
|
+
expectedSize: resourceArchive.binary.byteLength,
|
|
1097
|
+
expectedSha256: bytesToHex(sha256(new Uint8Array(resourceArchive.binary))),
|
|
1098
|
+
});
|
|
1099
|
+
memoryArtifacts.push({
|
|
1100
|
+
artifactId: resourceArtifactId,
|
|
1101
|
+
binary: resourceArchive.binary,
|
|
1102
|
+
materializedEntries: resourceArchive.materializedEntries,
|
|
1103
|
+
});
|
|
660
1104
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
1105
|
+
const plan = buildProtocolV2LocalFirmwareUpdatePlan({
|
|
1106
|
+
features,
|
|
1107
|
+
firmwareType,
|
|
1108
|
+
platform: this.params.platform,
|
|
1109
|
+
artifacts: planArtifacts,
|
|
1110
|
+
});
|
|
1111
|
+
let memoryHost: FirmwareUpdateV4MemoryHost | undefined;
|
|
1112
|
+
try {
|
|
1113
|
+
memoryHost = prepareFirmwareUpdateV4MemoryHost({
|
|
1114
|
+
sdk: {
|
|
1115
|
+
prepareFirmwareUpdatePlan,
|
|
1116
|
+
registerFirmwareUpdateHostBinding,
|
|
1117
|
+
unregisterFirmwareUpdateHostBinding,
|
|
1118
|
+
},
|
|
1119
|
+
plan,
|
|
1120
|
+
artifacts: memoryArtifacts,
|
|
1121
|
+
});
|
|
1122
|
+
const preparedPlan = validateFirmwareUpdatePreparedPlan(memoryHost.preparedPlan);
|
|
1123
|
+
assertFirmwareUpdatePreparedPlanBinding({
|
|
1124
|
+
preparedPlan,
|
|
1125
|
+
executor: 'v4',
|
|
1126
|
+
platform: this.params.platform,
|
|
1127
|
+
scopeTargets: [],
|
|
1128
|
+
bindings: [],
|
|
1129
|
+
});
|
|
1130
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
1131
|
+
preparedPlan,
|
|
1132
|
+
deviceIdentity: this.protocolV2ExpectedSerialNumber,
|
|
1133
|
+
});
|
|
1134
|
+
const hostBinding = resolveFirmwareUpdateHostBinding(
|
|
1135
|
+
memoryHost.hostBindingGeneration,
|
|
1136
|
+
preparedPlan.preparedPlanDigest
|
|
1137
|
+
);
|
|
1138
|
+
this.params.preparedPlan = preparedPlan;
|
|
1139
|
+
this.params.targetsToUpdate = [...preparedPlan.targetsToUpdate] as FirmwareUpdateV4Target[];
|
|
1140
|
+
this.params.artifactReader = hostBinding.artifactReader;
|
|
1141
|
+
this.params.componentArtifacts = undefined;
|
|
1142
|
+
return memoryHost;
|
|
1143
|
+
} catch (error) {
|
|
1144
|
+
memoryHost?.release();
|
|
1145
|
+
throw error;
|
|
1146
|
+
}
|
|
665
1147
|
}
|
|
666
1148
|
|
|
667
|
-
private async
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
1149
|
+
private async prepareProtocolV2LocalResourceArchive(
|
|
1150
|
+
binary: ArrayBuffer
|
|
1151
|
+
): Promise<ProtocolV2LocalResourceArchive> {
|
|
1152
|
+
if (binary.byteLength <= 0 || binary.byteLength > PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES) {
|
|
1153
|
+
throw ERRORS.TypedError(
|
|
1154
|
+
HardwareErrorCode.RuntimeError,
|
|
1155
|
+
'Protocol V2 local resource ZIP archive size is invalid',
|
|
1156
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1157
|
+
);
|
|
1158
|
+
}
|
|
1159
|
+
const zip = await JSZip.loadAsync(binary);
|
|
1160
|
+
const zipEntries = Object.values(zip.files);
|
|
1161
|
+
if (
|
|
1162
|
+
zipEntries.some(entry => entry.unsafeOriginalName && entry.unsafeOriginalName !== entry.name)
|
|
1163
|
+
) {
|
|
1164
|
+
throw ERRORS.TypedError(
|
|
1165
|
+
HardwareErrorCode.RuntimeError,
|
|
1166
|
+
'Protocol V2 local resource ZIP contains an unsafe entry path',
|
|
1167
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1168
|
+
);
|
|
1169
|
+
}
|
|
1170
|
+
const entries = zipEntries.filter(entry => !entry.dir);
|
|
1171
|
+
if (entries.length === 0 || entries.length > PROTOCOL_V2_RESOURCE_FILE_MAX_COUNT + 1) {
|
|
1172
|
+
throw ERRORS.TypedError(
|
|
1173
|
+
HardwareErrorCode.RuntimeError,
|
|
1174
|
+
'Protocol V2 local resource ZIP entry set is invalid',
|
|
1175
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1176
|
+
);
|
|
1177
|
+
}
|
|
1178
|
+
let declaredUncompressedSize = 0;
|
|
1179
|
+
let declaredCompressedSize = 0;
|
|
1180
|
+
for (const entry of entries) {
|
|
1181
|
+
const sizes = getProtocolV2ZipEntrySizes(entry);
|
|
1182
|
+
declaredCompressedSize += sizes.compressedSize;
|
|
1183
|
+
declaredUncompressedSize += sizes.uncompressedSize;
|
|
1184
|
+
const entryLimit =
|
|
1185
|
+
entry.name === 'manifest.json'
|
|
1186
|
+
? PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES
|
|
1187
|
+
: PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES;
|
|
1188
|
+
if (
|
|
1189
|
+
sizes.uncompressedSize > entryLimit ||
|
|
1190
|
+
declaredCompressedSize > binary.byteLength ||
|
|
1191
|
+
declaredUncompressedSize >
|
|
1192
|
+
PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES + PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES
|
|
1193
|
+
) {
|
|
1194
|
+
throw ERRORS.TypedError(
|
|
1195
|
+
HardwareErrorCode.RuntimeError,
|
|
1196
|
+
'Protocol V2 local resource ZIP declared size exceeds the allowed limit',
|
|
1197
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1198
|
+
);
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
const manifestEntry = zip.file('manifest.json');
|
|
1202
|
+
if (!manifestEntry) {
|
|
1203
|
+
throw ERRORS.TypedError(
|
|
1204
|
+
HardwareErrorCode.RuntimeError,
|
|
1205
|
+
'Protocol V2 local resource ZIP has no manifest.json',
|
|
1206
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1207
|
+
);
|
|
1208
|
+
}
|
|
1209
|
+
const manifestBinary = await manifestEntry.async('arraybuffer');
|
|
1210
|
+
if (
|
|
1211
|
+
manifestBinary.byteLength <= 0 ||
|
|
1212
|
+
manifestBinary.byteLength > PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES
|
|
1213
|
+
) {
|
|
1214
|
+
throw ERRORS.TypedError(
|
|
1215
|
+
HardwareErrorCode.RuntimeError,
|
|
1216
|
+
'Protocol V2 local resource manifest size is invalid',
|
|
1217
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1218
|
+
);
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
let manifestValue: unknown;
|
|
1222
|
+
try {
|
|
1223
|
+
manifestValue = JSON.parse(new TextDecoder().decode(manifestBinary));
|
|
1224
|
+
} catch (error) {
|
|
1225
|
+
throw ERRORS.TypedError(
|
|
1226
|
+
HardwareErrorCode.RuntimeError,
|
|
1227
|
+
`Protocol V2 local resource manifest is invalid: ${String(error)}`,
|
|
1228
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1229
|
+
);
|
|
1230
|
+
}
|
|
1231
|
+
const manifest = parseProtocolV2ResourceManifest(manifestValue);
|
|
1232
|
+
const selectedFiles = selectProtocolV2ResourceManifestFiles({
|
|
1233
|
+
manifest,
|
|
1234
|
+
targetsToUpdate: this.params.targetsToUpdate ?? [],
|
|
672
1235
|
});
|
|
673
|
-
const
|
|
1236
|
+
const expectedEntryNames = new Set([
|
|
1237
|
+
'manifest.json',
|
|
1238
|
+
...selectedFiles.map(file => file.archive_path),
|
|
1239
|
+
]);
|
|
674
1240
|
if (
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
fileSize === undefined ||
|
|
678
|
-
fileSize < PROTOCOL_V2_OKPP_HEADER_SIZE
|
|
1241
|
+
entries.length !== expectedEntryNames.size ||
|
|
1242
|
+
entries.some(entry => !expectedEntryNames.has(entry.name))
|
|
679
1243
|
) {
|
|
680
|
-
|
|
1244
|
+
throw ERRORS.TypedError(
|
|
1245
|
+
HardwareErrorCode.RuntimeError,
|
|
1246
|
+
'Protocol V2 local resource ZIP contains an unexpected entry',
|
|
1247
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1248
|
+
);
|
|
681
1249
|
}
|
|
682
1250
|
|
|
683
|
-
|
|
684
|
-
const
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
const
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
1251
|
+
let totalSize = 0;
|
|
1252
|
+
const materializedEntries: FirmwareMemoryArtifactEntry[] = [
|
|
1253
|
+
{ entryName: 'manifest.json', binary: manifestBinary },
|
|
1254
|
+
];
|
|
1255
|
+
for (const file of selectedFiles) {
|
|
1256
|
+
const entry = zip.file(file.archive_path);
|
|
1257
|
+
if (!entry) {
|
|
1258
|
+
throw ERRORS.TypedError(
|
|
1259
|
+
HardwareErrorCode.RuntimeError,
|
|
1260
|
+
`Protocol V2 local resource ZIP is missing ${file.archive_path}`,
|
|
1261
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1262
|
+
);
|
|
1263
|
+
}
|
|
1264
|
+
const { uncompressedSize } = getProtocolV2ZipEntrySizes(entry);
|
|
1265
|
+
totalSize += uncompressedSize;
|
|
1266
|
+
if (uncompressedSize !== file.size || totalSize > PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES) {
|
|
1267
|
+
throw ERRORS.TypedError(
|
|
1268
|
+
HardwareErrorCode.RuntimeError,
|
|
1269
|
+
`Protocol V2 local resource file declared size is invalid: ${file.archive_path}`,
|
|
1270
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1271
|
+
);
|
|
1272
|
+
}
|
|
1273
|
+
const fileBinary = await entry.async('arraybuffer');
|
|
1274
|
+
const digest = bytesToHex(sha256(new Uint8Array(fileBinary)));
|
|
1275
|
+
if (fileBinary.byteLength !== file.size || digest !== file.sha256.toLowerCase()) {
|
|
1276
|
+
throw ERRORS.TypedError(
|
|
1277
|
+
HardwareErrorCode.RuntimeError,
|
|
1278
|
+
`Protocol V2 local resource file does not match manifest: ${file.archive_path}`,
|
|
1279
|
+
{ firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch' }
|
|
1280
|
+
);
|
|
1281
|
+
}
|
|
1282
|
+
materializedEntries.push({ entryName: file.archive_path, binary: fileBinary });
|
|
1283
|
+
}
|
|
1284
|
+
return { binary, materializedEntries };
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
private async prepareProtocolV2ResourceSources(): Promise<ProtocolV2ResourceBundleSource[]> {
|
|
1288
|
+
const resourceRequested = this.params.targetsToUpdate?.includes('resource') ?? false;
|
|
1289
|
+
if (!resourceRequested) {
|
|
1290
|
+
return [];
|
|
1291
|
+
}
|
|
1292
|
+
const archiveSources = await this.prepareProtocolV2ResourceArchiveSources();
|
|
1293
|
+
if (archiveSources) {
|
|
1294
|
+
return archiveSources;
|
|
1295
|
+
}
|
|
1296
|
+
throw ERRORS.TypedError(
|
|
1297
|
+
HardwareErrorCode.RuntimeError,
|
|
1298
|
+
'Protocol V2 resource archive is not prepared',
|
|
1299
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1300
|
+
);
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
private async prepareProtocolV2ResourceArchiveSources(): Promise<
|
|
1304
|
+
ProtocolV2ResourceBundleSource[] | undefined
|
|
1305
|
+
> {
|
|
1306
|
+
const archiveArtifacts =
|
|
1307
|
+
this.params.preparedPlan?.artifacts.filter(
|
|
1308
|
+
artifact =>
|
|
1309
|
+
artifact.role === 'resourceBundle' &&
|
|
1310
|
+
artifact.target === 'resource' &&
|
|
1311
|
+
artifact.container === 'zip'
|
|
1312
|
+
) ?? [];
|
|
1313
|
+
if (archiveArtifacts.length === 0) {
|
|
1314
|
+
return undefined;
|
|
1315
|
+
}
|
|
1316
|
+
if (archiveArtifacts.length !== 1) {
|
|
1317
|
+
throw ERRORS.TypedError(
|
|
1318
|
+
HardwareErrorCode.RuntimeError,
|
|
1319
|
+
'Protocol V2 prepared plan must contain exactly one resource archive',
|
|
1320
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1321
|
+
);
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
const entries = archiveArtifacts[0].materializedEntries ?? [];
|
|
1325
|
+
const manifestEntry = entries.find(entry => entry.entryName === 'manifest.json');
|
|
1326
|
+
if (
|
|
1327
|
+
!manifestEntry ||
|
|
1328
|
+
manifestEntry.artifact.size <= 0 ||
|
|
1329
|
+
manifestEntry.artifact.size > PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES
|
|
1330
|
+
) {
|
|
1331
|
+
throw ERRORS.TypedError(
|
|
1332
|
+
HardwareErrorCode.RuntimeError,
|
|
1333
|
+
'Protocol V2 prepared resource archive has no valid manifest.json',
|
|
1334
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1335
|
+
);
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
const manifestSource = await this.openProtocolV2PreparedSource(manifestEntry.artifact);
|
|
1339
|
+
let manifestValue: unknown;
|
|
1340
|
+
try {
|
|
1341
|
+
manifestValue = JSON.parse(
|
|
1342
|
+
new TextDecoder().decode(await readFirmwareByteSourceFully(manifestSource))
|
|
1343
|
+
);
|
|
1344
|
+
} catch (error) {
|
|
1345
|
+
throw ERRORS.TypedError(
|
|
1346
|
+
HardwareErrorCode.RuntimeError,
|
|
1347
|
+
`Protocol V2 prepared resource manifest is invalid: ${String(error)}`,
|
|
1348
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1349
|
+
);
|
|
1350
|
+
}
|
|
1351
|
+
const manifest = parseProtocolV2ResourceManifest(manifestValue);
|
|
1352
|
+
const selectedFiles = selectProtocolV2ResourceManifestFiles({
|
|
1353
|
+
manifest,
|
|
1354
|
+
targetsToUpdate: this.params.targetsToUpdate ?? [],
|
|
1355
|
+
});
|
|
1356
|
+
if (selectedFiles.length > PROTOCOL_V2_RESOURCE_FILE_MAX_COUNT) {
|
|
1357
|
+
throw ERRORS.TypedError(
|
|
1358
|
+
HardwareErrorCode.RuntimeError,
|
|
1359
|
+
'Protocol V2 prepared resource archive contains too many files',
|
|
1360
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1361
|
+
);
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
const entriesByName = new Map(entries.map(entry => [entry.entryName, entry] as const));
|
|
1365
|
+
const expectedEntryNames = new Set([
|
|
1366
|
+
'manifest.json',
|
|
1367
|
+
...selectedFiles.map(file => file.archive_path),
|
|
1368
|
+
]);
|
|
1369
|
+
if (entries.some(entry => !expectedEntryNames.has(entry.entryName))) {
|
|
1370
|
+
throw ERRORS.TypedError(
|
|
1371
|
+
HardwareErrorCode.RuntimeError,
|
|
1372
|
+
'Protocol V2 prepared resource archive contains an unexpected entry',
|
|
1373
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1374
|
+
);
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
let totalSize = 0;
|
|
1378
|
+
const sources: ProtocolV2ResourceBundleSource[] = [];
|
|
1379
|
+
for (const [index, file] of selectedFiles.entries()) {
|
|
1380
|
+
const entry = entriesByName.get(file.archive_path);
|
|
1381
|
+
if (
|
|
1382
|
+
!entry ||
|
|
1383
|
+
entry.artifact.size !== file.size ||
|
|
1384
|
+
entry.artifact.sha256.toLowerCase() !== file.sha256.toLowerCase()
|
|
1385
|
+
) {
|
|
1386
|
+
throw ERRORS.TypedError(
|
|
1387
|
+
HardwareErrorCode.RuntimeError,
|
|
1388
|
+
`Protocol V2 prepared resource file does not match manifest: ${file.archive_path}`,
|
|
1389
|
+
{ firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch' }
|
|
1390
|
+
);
|
|
1391
|
+
}
|
|
1392
|
+
totalSize += entry.artifact.size;
|
|
1393
|
+
if (totalSize > PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES) {
|
|
1394
|
+
throw ERRORS.TypedError(
|
|
1395
|
+
HardwareErrorCode.RuntimeError,
|
|
1396
|
+
'Protocol V2 prepared resource archive exceeds the total size limit',
|
|
1397
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1398
|
+
);
|
|
1399
|
+
}
|
|
1400
|
+
const source = await this.openProtocolV2PreparedSource(entry.artifact);
|
|
1401
|
+
const header =
|
|
1402
|
+
source.size >= PROTOCOL_V2_OKPP_HEADER_SIZE
|
|
1403
|
+
? parseProtocolV2OkppHeader(
|
|
1404
|
+
new Uint8Array(await source.readAt(0, PROTOCOL_V2_OKPP_HEADER_SIZE))
|
|
1405
|
+
)
|
|
1406
|
+
: null;
|
|
1407
|
+
sources.push({
|
|
1408
|
+
name: file.original_name || `resource-${index}`,
|
|
1409
|
+
source,
|
|
1410
|
+
devicePath: file.device_path,
|
|
1411
|
+
...(header
|
|
1412
|
+
? {
|
|
1413
|
+
version: header.version,
|
|
1414
|
+
payloadHash: header.payloadHash,
|
|
1415
|
+
headerHash: header.headerHash,
|
|
1416
|
+
}
|
|
1417
|
+
: {}),
|
|
696
1418
|
});
|
|
697
|
-
const data = toProtocolV2Bytes(res.message?.data);
|
|
698
|
-
if (data.byteLength === 0) return null;
|
|
699
|
-
chunks.push(data);
|
|
700
|
-
offset += data.byteLength;
|
|
701
1419
|
}
|
|
1420
|
+
return sources;
|
|
1421
|
+
}
|
|
702
1422
|
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
1423
|
+
private async runProtocolV2PreparedArtifacts(features: Features, firmwareType: EFirmwareType) {
|
|
1424
|
+
try {
|
|
1425
|
+
this.postTipMessage(FirmwareUpdateTipMessage.StartDownloadFirmware);
|
|
1426
|
+
const installSources = await this.prepareProtocolV2InstallSources(firmwareType, features);
|
|
1427
|
+
const resourceSources = await this.prepareProtocolV2ResourceSources();
|
|
1428
|
+
if (installSources.length === 0 && resourceSources.length === 0) {
|
|
1429
|
+
throw ERRORS.TypedError(
|
|
1430
|
+
HardwareErrorCode.FirmwareUpdateDownloadFailed,
|
|
1431
|
+
'No firmware to update'
|
|
1432
|
+
);
|
|
1433
|
+
}
|
|
1434
|
+
this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
1435
|
+
|
|
1436
|
+
return await this.executeProtocolV2SourceUpdate({
|
|
1437
|
+
installSources,
|
|
1438
|
+
resourceSources,
|
|
1439
|
+
});
|
|
1440
|
+
} finally {
|
|
1441
|
+
await this.closeProtocolV2PreparedSources();
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
private validateExpectedTargetVersions() {
|
|
1446
|
+
const expected = this.params.expectedTargetVersions;
|
|
1447
|
+
if (expected === undefined) return;
|
|
1448
|
+
if (typeof expected !== 'object' || expected === null || Array.isArray(expected)) {
|
|
1449
|
+
throw ERRORS.TypedError(
|
|
1450
|
+
HardwareErrorCode.RuntimeError,
|
|
1451
|
+
'Protocol V2 expected target versions are invalid'
|
|
1452
|
+
);
|
|
1453
|
+
}
|
|
1454
|
+
for (const [target, version] of Object.entries(expected)) {
|
|
1455
|
+
if (
|
|
1456
|
+
!Array.from(PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.values()).includes(
|
|
1457
|
+
target as Exclude<FirmwareUpdateV4Target, 'boot_resources'>
|
|
1458
|
+
) ||
|
|
1459
|
+
typeof version !== 'string' ||
|
|
1460
|
+
!/^\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.-]+)?$/u.test(version) ||
|
|
1461
|
+
version.length > 64
|
|
1462
|
+
) {
|
|
1463
|
+
throw ERRORS.TypedError(
|
|
1464
|
+
HardwareErrorCode.RuntimeError,
|
|
1465
|
+
'Protocol V2 expected target version is invalid'
|
|
1466
|
+
);
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
private getExpectedProtocolV2TargetVersion(targetId: number) {
|
|
1472
|
+
const target = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(targetId);
|
|
1473
|
+
const version = target ? this.params?.expectedTargetVersions?.[target] : undefined;
|
|
1474
|
+
if (!version) return undefined;
|
|
1475
|
+
const parts = version.split(/[+-]/u, 1)[0].split('.').map(Number);
|
|
1476
|
+
if (
|
|
1477
|
+
parts.length !== 3 ||
|
|
1478
|
+
parts.some(part => !Number.isSafeInteger(part) || part < 0 || part > 0xff)
|
|
1479
|
+
) {
|
|
1480
|
+
return undefined;
|
|
1481
|
+
}
|
|
1482
|
+
return parts[0] * 0x10000 + parts[1] * 0x100 + parts[2];
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
private assertExpectedProtocolV2Versions(targets?: readonly FirmwareUpdateV4Target[]) {
|
|
1486
|
+
const expected = this.params?.expectedTargetVersions;
|
|
1487
|
+
if (!expected) return;
|
|
1488
|
+
const features = this.protocolV2LatestFinalFeatures;
|
|
1489
|
+
const requestedTargets = this.getProtocolV2RequestedTargets();
|
|
1490
|
+
const applicationTargets = requestedTargets.filter(
|
|
1491
|
+
target => target === 'app_v1' || target === 'app_v2'
|
|
1492
|
+
);
|
|
1493
|
+
const visibleVersions: Partial<Record<FirmwareUpdateV4Target, string>> = {
|
|
1494
|
+
boot: features ? getDeviceBootloaderVersion(features).join('.') : undefined,
|
|
1495
|
+
coprocessor: features ? getDeviceBLEFirmwareVersion(features).join('.') : undefined,
|
|
1496
|
+
se01: features?.se01Version ?? undefined,
|
|
1497
|
+
se02: features?.se02Version ?? undefined,
|
|
1498
|
+
se03: features?.se03Version ?? undefined,
|
|
1499
|
+
se04: features?.se04Version ?? undefined,
|
|
1500
|
+
};
|
|
1501
|
+
if (features && applicationTargets.length === 1) {
|
|
1502
|
+
visibleVersions[applicationTargets[0]] = getDeviceFirmwareVersion(features).join('.');
|
|
1503
|
+
}
|
|
1504
|
+
const expectedEntries = (
|
|
1505
|
+
Object.entries(expected) as Array<[FirmwareUpdateV4Target, string]>
|
|
1506
|
+
).filter(([target]) => !targets || targets.includes(target));
|
|
1507
|
+
for (const [target, expectedVersion] of expectedEntries) {
|
|
1508
|
+
const targetId = Array.from(PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.entries()).find(
|
|
1509
|
+
([, mappedTarget]) => mappedTarget === target
|
|
1510
|
+
)?.[0];
|
|
1511
|
+
const statusVersion =
|
|
1512
|
+
targetId === undefined ? undefined : this.protocolV2CompletedTargetVersions.get(targetId);
|
|
1513
|
+
const observedVersion =
|
|
1514
|
+
statusVersion === undefined
|
|
1515
|
+
? visibleVersions[target]
|
|
1516
|
+
: `${Math.floor(statusVersion / 0x10000) % 0x100}.${
|
|
1517
|
+
Math.floor(statusVersion / 0x100) % 0x100
|
|
1518
|
+
}.${statusVersion % 0x100}`;
|
|
1519
|
+
if (!observedVersion) {
|
|
1520
|
+
if (this.protocolV2FinalStatusVerified) {
|
|
1521
|
+
Log.warn(
|
|
1522
|
+
`Protocol V2 target ${target} has no observable final version; completed target status is authoritative`
|
|
1523
|
+
);
|
|
1524
|
+
} else {
|
|
1525
|
+
throw ERRORS.TypedError(
|
|
1526
|
+
HardwareErrorCode.FirmwareVerificationFailed,
|
|
1527
|
+
`Protocol V2 target ${target} has no observable final version after status fallback`
|
|
1528
|
+
);
|
|
1529
|
+
}
|
|
1530
|
+
} else if (observedVersion !== expectedVersion) {
|
|
1531
|
+
throw ERRORS.TypedError(
|
|
1532
|
+
HardwareErrorCode.FirmwareVerificationFailed,
|
|
1533
|
+
`Protocol V2 target ${target} reached ${observedVersion}, expected ${expectedVersion}`
|
|
1534
|
+
);
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
private getProtocolV2RequestedTargets(): FirmwareUpdateV4Target[] {
|
|
1540
|
+
if (this.params.targetsToUpdate?.length) {
|
|
1541
|
+
return [...new Set(this.params.targetsToUpdate)];
|
|
1542
|
+
}
|
|
1543
|
+
if (this.params.preparedPlan?.targetsToUpdate.length) {
|
|
1544
|
+
return [...new Set(this.params.preparedPlan.targetsToUpdate)] as FirmwareUpdateV4Target[];
|
|
1545
|
+
}
|
|
1546
|
+
const targets = new Set<FirmwareUpdateV4Target>();
|
|
1547
|
+
Object.keys(this.params.componentArtifacts ?? {}).forEach(target =>
|
|
1548
|
+
targets.add(target as FirmwareUpdateV4Target)
|
|
1549
|
+
);
|
|
1550
|
+
if (this.params.bootloaderBinary) targets.add('boot');
|
|
1551
|
+
if (this.params.applicationP1Binary) targets.add('app_v1');
|
|
1552
|
+
if (this.params.applicationP2Binary) targets.add('app_v2');
|
|
1553
|
+
if (this.params.coprocessorBinary) targets.add('coprocessor');
|
|
1554
|
+
if (this.params.se01Binary) targets.add('se01');
|
|
1555
|
+
if (this.params.se02Binary) targets.add('se02');
|
|
1556
|
+
if (this.params.se03Binary) targets.add('se03');
|
|
1557
|
+
if (this.params.se04Binary) targets.add('se04');
|
|
1558
|
+
return Array.from(targets);
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
private async getProtocolV2DeviceFeatures(): Promise<Features> {
|
|
1562
|
+
if (this.device.features) {
|
|
1563
|
+
return this.device.features;
|
|
1564
|
+
}
|
|
1565
|
+
if (typeof this.device.getFeatures === 'function') {
|
|
1566
|
+
const features = await this.device.getFeatures();
|
|
1567
|
+
if (features) return features;
|
|
1568
|
+
}
|
|
1569
|
+
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'Device features not available');
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
private getProtocolV2SerialNumber(deviceInfo: ProtocolV2DeviceInfo) {
|
|
1573
|
+
const serialNumber = deviceInfo.hw?.serial_no?.trim();
|
|
1574
|
+
return serialNumber || undefined;
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
private assertProtocolV2DeviceInfoIdentity(deviceInfo: ProtocolV2DeviceInfo) {
|
|
1578
|
+
assertProtocolV2ReconnectIdentity(
|
|
1579
|
+
this.protocolV2ExpectedSerialNumber,
|
|
1580
|
+
this.getProtocolV2SerialNumber(deviceInfo),
|
|
1581
|
+
this.protocolV2ExpectedPath,
|
|
1582
|
+
this.device.originalDescriptor.path
|
|
1583
|
+
);
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
private getProtocolV2DeviceInfoTimeout() {
|
|
1587
|
+
return this.isBleReconnect()
|
|
1588
|
+
? this.payload.protocolV2DeviceInfoTimeoutMs ?? PROTOCOL_V2_DEVICE_INFO_READY_TIMEOUT
|
|
1589
|
+
: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT;
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
private async requestProtocolV2PhysicalIdentity() {
|
|
1593
|
+
return requestProtocolV2DeviceInfo({
|
|
1594
|
+
commands: this.device.getCommands(),
|
|
1595
|
+
timeoutMs: this.getProtocolV2DeviceInfoTimeout(),
|
|
708
1596
|
});
|
|
709
|
-
return parseProtocolV2OkppHeader(headerBytes);
|
|
710
1597
|
}
|
|
711
1598
|
|
|
712
|
-
private
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
1599
|
+
private async captureProtocolV2PhysicalIdentity() {
|
|
1600
|
+
const deviceInfo = await this.requestProtocolV2PhysicalIdentity();
|
|
1601
|
+
const serialNumber = this.getProtocolV2SerialNumber(deviceInfo);
|
|
1602
|
+
const path = this.device.originalDescriptor?.path?.trim() || undefined;
|
|
1603
|
+
if (this.params?.preparedPlan) {
|
|
1604
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
1605
|
+
preparedPlan: this.params.preparedPlan,
|
|
1606
|
+
deviceIdentity: serialNumber,
|
|
1607
|
+
});
|
|
1608
|
+
} else if (this.params?.expectedDeviceId) {
|
|
1609
|
+
assertProtocolV2ReconnectIdentity(this.params?.expectedDeviceId, serialNumber);
|
|
1610
|
+
} else {
|
|
1611
|
+
assertProtocolV2ReconnectIdentity(serialNumber, serialNumber, path, path);
|
|
1612
|
+
}
|
|
1613
|
+
this.protocolV2ExpectedSerialNumber = serialNumber;
|
|
1614
|
+
this.protocolV2ExpectedPath = path;
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
private async verifyProtocolV2ReconnectIdentity() {
|
|
1618
|
+
const deviceInfo = await this.requestProtocolV2PhysicalIdentity();
|
|
1619
|
+
this.assertProtocolV2DeviceInfoIdentity(deviceInfo);
|
|
1620
|
+
return deviceInfo;
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
private prepareBootloaderBinary(): ArrayBuffer | null {
|
|
1624
|
+
return this.params.bootloaderBinary ?? null;
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
private getMissingProtocolV2FirmwareTargets(installItems: ProtocolV2InstallItem[]) {
|
|
1628
|
+
const preparedTargets = new Set(
|
|
1629
|
+
installItems.flatMap(item => {
|
|
1630
|
+
const target = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(item.targetId);
|
|
1631
|
+
return target ? [target] : [];
|
|
1632
|
+
})
|
|
724
1633
|
);
|
|
725
|
-
|
|
726
|
-
|
|
1634
|
+
return (this.params.targetsToUpdate ?? [])
|
|
1635
|
+
.filter(
|
|
1636
|
+
(target): target is Exclude<FirmwareUpdateV4Target, 'resource' | 'boot_resources'> =>
|
|
1637
|
+
target !== 'resource' && target !== 'boot_resources'
|
|
1638
|
+
)
|
|
1639
|
+
.filter(target => !preparedTargets.has(target));
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
private buildProtocolV2InstallItems({
|
|
1643
|
+
bootloaderBinary,
|
|
1644
|
+
fwBinaryMap,
|
|
1645
|
+
}: {
|
|
1646
|
+
bootloaderBinary: ArrayBuffer | null;
|
|
1647
|
+
fwBinaryMap: ProtocolV2TargetBinary[];
|
|
1648
|
+
}): ProtocolV2InstallItem[] {
|
|
1649
|
+
const installItems: ProtocolV2InstallItem[] = [];
|
|
1650
|
+
|
|
1651
|
+
if (bootloaderBinary) {
|
|
1652
|
+
installItems.push({
|
|
1653
|
+
fileName: 'bootloader.bin',
|
|
1654
|
+
binary: bootloaderBinary,
|
|
1655
|
+
targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER,
|
|
1656
|
+
kind: 'bootloader',
|
|
1657
|
+
});
|
|
727
1658
|
}
|
|
728
|
-
|
|
1659
|
+
|
|
1660
|
+
installItems.push(...fwBinaryMap.map(item => ({ ...item, kind: 'firmware' as const })));
|
|
1661
|
+
return installItems;
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
private getRemoteComponentEntries(release: IFirmwareReleaseInfo) {
|
|
1665
|
+
const { components } = release;
|
|
1666
|
+
if (!components) return [];
|
|
1667
|
+
|
|
1668
|
+
const orderedKeys = [
|
|
1669
|
+
...(release.installOrder ?? []),
|
|
1670
|
+
...Object.keys(components).filter(key => !release.installOrder?.includes(key)),
|
|
1671
|
+
];
|
|
1672
|
+
|
|
1673
|
+
return orderedKeys
|
|
1674
|
+
.map(key => {
|
|
1675
|
+
const component = components[key];
|
|
1676
|
+
return component ? ([key, component] as const) : undefined;
|
|
1677
|
+
})
|
|
1678
|
+
.filter((entry): entry is readonly [string, IProtocolV2FirmwareComponent] => !!entry);
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
private getRemoteComponentTarget(key: string, component: IProtocolV2FirmwareComponent) {
|
|
1682
|
+
const targetName = component.target?.toUpperCase();
|
|
1683
|
+
if (targetName === 'ROMLOADER') {
|
|
1684
|
+
throw ERRORS.TypedError(
|
|
1685
|
+
HardwareErrorCode.RuntimeError,
|
|
1686
|
+
PROTOCOL_V2_ROMLOADER_UNSUPPORTED_MESSAGE
|
|
1687
|
+
);
|
|
1688
|
+
}
|
|
1689
|
+
const target = PROTOCOL_V2_REMOTE_COMPONENT_TARGETS[targetName];
|
|
1690
|
+
if (!target) {
|
|
1691
|
+
throw ERRORS.TypedError(
|
|
1692
|
+
HardwareErrorCode.RuntimeError,
|
|
1693
|
+
`Unsupported Protocol V2 firmware component target: ${key}/${component.target}`
|
|
1694
|
+
);
|
|
1695
|
+
}
|
|
1696
|
+
return target;
|
|
729
1697
|
}
|
|
730
1698
|
|
|
731
1699
|
private async downloadRemoteProtocolV2Component(
|
|
@@ -739,15 +1707,55 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
739
1707
|
`Missing Protocol V2 firmware component url: ${key}/${component.target}`
|
|
740
1708
|
);
|
|
741
1709
|
}
|
|
1710
|
+
const expectedFingerprint = normalizeProtocolV2Hex(component.fingerprint);
|
|
1711
|
+
if (
|
|
1712
|
+
!Number.isSafeInteger(component.expectedSize) ||
|
|
1713
|
+
Number(component.expectedSize) <= 0 ||
|
|
1714
|
+
!expectedFingerprint ||
|
|
1715
|
+
!/^[0-9a-f]{64}$/u.test(expectedFingerprint)
|
|
1716
|
+
) {
|
|
1717
|
+
throw ERRORS.TypedError(
|
|
1718
|
+
HardwareErrorCode.RuntimeError,
|
|
1719
|
+
`Protocol V2 firmware component integrity metadata is invalid: ${key}/${component.target}`,
|
|
1720
|
+
{ firmwareUpdateCode: 'FirmwarePlanInvalid' }
|
|
1721
|
+
);
|
|
1722
|
+
}
|
|
742
1723
|
|
|
743
1724
|
const { binary } = await getSysResourceBinary(component.url);
|
|
1725
|
+
if (binary.byteLength !== component.expectedSize) {
|
|
1726
|
+
throw ERRORS.TypedError(
|
|
1727
|
+
HardwareErrorCode.RuntimeError,
|
|
1728
|
+
`Protocol V2 firmware size mismatch: ${key}/${component.target}`,
|
|
1729
|
+
{ firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch' }
|
|
1730
|
+
);
|
|
1731
|
+
}
|
|
1732
|
+
if (!isProtocolV2FirmwareFingerprintValid(binary, component.fingerprint)) {
|
|
1733
|
+
throw ERRORS.TypedError(
|
|
1734
|
+
HardwareErrorCode.RuntimeError,
|
|
1735
|
+
`Protocol V2 firmware fingerprint mismatch: ${key}/${component.target}`
|
|
1736
|
+
);
|
|
1737
|
+
}
|
|
1738
|
+
const expectedPayloadHash = normalizeProtocolV2Hex(component.payloadHash);
|
|
1739
|
+
if (expectedPayloadHash) {
|
|
1740
|
+
const header = parseProtocolV2OkppHeader(toProtocolV2Bytes(binary));
|
|
1741
|
+
if (!header || header.payloadHash !== expectedPayloadHash) {
|
|
1742
|
+
throw ERRORS.TypedError(
|
|
1743
|
+
HardwareErrorCode.RuntimeError,
|
|
1744
|
+
`Protocol V2 firmware payload hash mismatch: ${key}/${component.target}`
|
|
1745
|
+
);
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
744
1748
|
return {
|
|
745
1749
|
...target,
|
|
746
1750
|
binary,
|
|
747
1751
|
};
|
|
748
1752
|
}
|
|
749
1753
|
|
|
750
|
-
private async prepareRemoteProtocolV2Binaries(
|
|
1754
|
+
private async prepareRemoteProtocolV2Binaries(
|
|
1755
|
+
firmwareType: EFirmwareType,
|
|
1756
|
+
features: Features,
|
|
1757
|
+
explicitInstallItems: ProtocolV2InstallItem[] = []
|
|
1758
|
+
) {
|
|
751
1759
|
const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
|
|
752
1760
|
|
|
753
1761
|
let bootloaderBinary: ArrayBuffer | null = null;
|
|
@@ -755,6 +1763,15 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
755
1763
|
const installItems: ProtocolV2InstallItem[] = [];
|
|
756
1764
|
|
|
757
1765
|
if (!release) {
|
|
1766
|
+
const missingFirmwareTargets = this.getMissingProtocolV2FirmwareTargets(explicitInstallItems);
|
|
1767
|
+
if (missingFirmwareTargets.length > 0) {
|
|
1768
|
+
throw ERRORS.TypedError(
|
|
1769
|
+
HardwareErrorCode.RuntimeError,
|
|
1770
|
+
`Protocol V2 firmware release is unavailable for requested targets: ${missingFirmwareTargets.join(
|
|
1771
|
+
', '
|
|
1772
|
+
)}`
|
|
1773
|
+
);
|
|
1774
|
+
}
|
|
758
1775
|
return {
|
|
759
1776
|
bootloaderBinary,
|
|
760
1777
|
fwBinaryMap,
|
|
@@ -763,38 +1780,47 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
763
1780
|
}
|
|
764
1781
|
|
|
765
1782
|
const entries = this.getRemoteComponentEntries(release);
|
|
1783
|
+
const targetsToUpdate = new Set(this.params.targetsToUpdate ?? []);
|
|
1784
|
+
const explicitInstallItemByTargetId = new Map(
|
|
1785
|
+
explicitInstallItems.map(item => [item.targetId, item] as const)
|
|
1786
|
+
);
|
|
1787
|
+
const preparedTargets = new Set<FirmwareUpdateV4Target>();
|
|
766
1788
|
|
|
767
1789
|
for (const [key, component] of entries) {
|
|
768
|
-
const
|
|
769
|
-
const
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
bootloaderBinary = remoteBinary.binary;
|
|
780
|
-
installItems.push({
|
|
781
|
-
fileName: remoteBinary.fileName,
|
|
782
|
-
binary: remoteBinary.binary,
|
|
783
|
-
targetId: remoteBinary.targetId,
|
|
784
|
-
kind: remoteBinary.kind,
|
|
785
|
-
});
|
|
1790
|
+
const targetName = component.target?.toUpperCase();
|
|
1791
|
+
const target = PROTOCOL_V2_REMOTE_COMPONENT_TARGETS[targetName];
|
|
1792
|
+
const updateTarget = target
|
|
1793
|
+
? PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(target.targetId)
|
|
1794
|
+
: undefined;
|
|
1795
|
+
if (updateTarget && targetsToUpdate.has(updateTarget)) {
|
|
1796
|
+
const explicitInstallItem = explicitInstallItemByTargetId.get(target.targetId);
|
|
1797
|
+
const installItem =
|
|
1798
|
+
explicitInstallItem ?? (await this.downloadRemoteProtocolV2Component(key, component));
|
|
1799
|
+
if (installItem.kind === 'bootloader') {
|
|
1800
|
+
bootloaderBinary = installItem.binary;
|
|
786
1801
|
} else {
|
|
787
1802
|
const binaryEntry = {
|
|
788
|
-
fileName:
|
|
789
|
-
binary:
|
|
790
|
-
targetId:
|
|
1803
|
+
fileName: installItem.fileName,
|
|
1804
|
+
binary: installItem.binary,
|
|
1805
|
+
targetId: installItem.targetId,
|
|
791
1806
|
};
|
|
792
1807
|
fwBinaryMap.push(binaryEntry);
|
|
793
|
-
installItems.push({ ...binaryEntry, kind: remoteBinary.kind });
|
|
794
1808
|
}
|
|
1809
|
+
installItems.push({ ...installItem });
|
|
1810
|
+
preparedTargets.add(updateTarget);
|
|
795
1811
|
}
|
|
796
1812
|
}
|
|
797
1813
|
|
|
1814
|
+
const missingTarget = Array.from(targetsToUpdate).find(
|
|
1815
|
+
target => target !== 'resource' && !preparedTargets.has(target)
|
|
1816
|
+
);
|
|
1817
|
+
if (missingTarget) {
|
|
1818
|
+
throw ERRORS.TypedError(
|
|
1819
|
+
HardwareErrorCode.RuntimeError,
|
|
1820
|
+
`Protocol V2 release does not contain requested target ${missingTarget}`
|
|
1821
|
+
);
|
|
1822
|
+
}
|
|
1823
|
+
|
|
798
1824
|
return {
|
|
799
1825
|
bootloaderBinary,
|
|
800
1826
|
fwBinaryMap,
|
|
@@ -802,140 +1828,83 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
802
1828
|
};
|
|
803
1829
|
}
|
|
804
1830
|
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
/**
|
|
810
|
-
* 准备 RESC bundle 列表。
|
|
811
|
-
*
|
|
812
|
-
* 两种模式(同 FirmwareUpdateV3 的 binary vs version 模式):
|
|
813
|
-
* - 用户传了 resourceBundleFiles(binary 数组):直接用,不做版本比对。
|
|
814
|
-
* - 用户没传:从远端 config.json 的 release.resourceBundles 拉取,
|
|
815
|
-
* 此时 syncProtocolV2ResourceBundles 会按需下载 + 比对设备已有 header 跳过。
|
|
816
|
-
*/
|
|
817
|
-
private prepareProtocolV2ResourceBundles(
|
|
818
|
-
firmwareType: EFirmwareType,
|
|
819
|
-
features: Features
|
|
820
|
-
): ProtocolV2ResourceBundleBinary[] | undefined {
|
|
821
|
-
// 模式1:用户手动传入 binary,直接安装,不比对
|
|
822
|
-
if (this.params.resourceBundleFiles?.length) {
|
|
823
|
-
return this.params.resourceBundleFiles.map(file => ({
|
|
824
|
-
name: file.devicePath.split('/').pop() ?? file.devicePath,
|
|
825
|
-
binary: file.binary,
|
|
826
|
-
devicePath: file.devicePath,
|
|
827
|
-
}));
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
// 模式2:远端配置模式,后续按需下载 + 比对
|
|
831
|
-
const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
|
|
832
|
-
if (!release?.resourceBundles?.length) return undefined;
|
|
833
|
-
|
|
834
|
-
return release.resourceBundles.map(bundle => ({
|
|
835
|
-
name: bundle.name,
|
|
836
|
-
binary: new ArrayBuffer(0),
|
|
837
|
-
devicePath: bundle.devicePath,
|
|
838
|
-
url: bundle.url,
|
|
839
|
-
version: bundle.version,
|
|
840
|
-
payloadHash: bundle.payloadHash,
|
|
841
|
-
headerHash: bundle.headerHash,
|
|
842
|
-
})) as ProtocolV2ResourceBundleBinary[];
|
|
1831
|
+
private getProtocolV2ResourceFilePath(path: string) {
|
|
1832
|
+
if (path.startsWith('vol')) return path;
|
|
1833
|
+
if (path.startsWith('/')) return `vol0:${path}`;
|
|
1834
|
+
return `vol0:/${path}`;
|
|
843
1835
|
}
|
|
844
1836
|
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
// 远端配置模式:按需下载 + 比对跳过
|
|
862
|
-
if (!isManualMode) {
|
|
863
|
-
const filtered: ProtocolV2ResourceBundleBinary[] = [];
|
|
864
|
-
for (const bundle of bundles) {
|
|
865
|
-
// 下载 binary
|
|
866
|
-
if (bundle.binary.byteLength === 0 && bundle.url) {
|
|
867
|
-
Log.log(`[FirmwareUpdateV4] downloading RESC bundle ${bundle.name} from ${bundle.url}`);
|
|
868
|
-
const { binary } = await getSysResourceBinary(bundle.url);
|
|
869
|
-
bundle.binary = binary;
|
|
870
|
-
}
|
|
871
|
-
// 比对设备上已有 header
|
|
872
|
-
const upToDate = await this.isProtocolV2ResourceBundleUpToDate(bundle);
|
|
873
|
-
if (upToDate) {
|
|
874
|
-
Log.log(`[FirmwareUpdateV4] skip RESC bundle ${bundle.name}; already up to date`);
|
|
875
|
-
} else {
|
|
876
|
-
filtered.push(bundle);
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
bundlesToSync = filtered;
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
if (bundlesToSync.length === 0) {
|
|
883
|
-
Log.log('[FirmwareUpdateV4] all RESC bundles up to date, nothing to sync');
|
|
884
|
-
return { processedSize: 0, totalSize: firmwareSize };
|
|
1837
|
+
private async readProtocolV2DeviceFileHeader(path: string, expectedSize?: number) {
|
|
1838
|
+
const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
|
|
1839
|
+
const filePath = this.getProtocolV2ResourceFilePath(path);
|
|
1840
|
+
const pathInfoRes = await typedCall('FilesystemPathInfoQuery', 'FilesystemPathInfo', {
|
|
1841
|
+
path: filePath,
|
|
1842
|
+
});
|
|
1843
|
+
const fileSize = toProtocolV2FiniteNumber(pathInfoRes.message?.size);
|
|
1844
|
+
if (
|
|
1845
|
+
!pathInfoRes.message?.exist ||
|
|
1846
|
+
pathInfoRes.message?.directory ||
|
|
1847
|
+
fileSize === undefined ||
|
|
1848
|
+
fileSize < PROTOCOL_V2_OKPP_HEADER_SIZE ||
|
|
1849
|
+
(expectedSize !== undefined && fileSize !== expectedSize)
|
|
1850
|
+
) {
|
|
1851
|
+
return null;
|
|
885
1852
|
}
|
|
886
1853
|
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
processedSize,
|
|
901
|
-
totalSize: transferTotalSize,
|
|
1854
|
+
const chunkSize = this.getProtocolV2FirmwareChunkSize('read');
|
|
1855
|
+
const chunks: Uint8Array[] = [];
|
|
1856
|
+
let offset = 0;
|
|
1857
|
+
while (offset < PROTOCOL_V2_OKPP_HEADER_SIZE) {
|
|
1858
|
+
const readLen = Math.min(chunkSize, PROTOCOL_V2_OKPP_HEADER_SIZE - offset);
|
|
1859
|
+
const res = await typedCall('FilesystemFileRead', 'FilesystemFile', {
|
|
1860
|
+
file: {
|
|
1861
|
+
path: filePath,
|
|
1862
|
+
offset,
|
|
1863
|
+
total_size: 0,
|
|
1864
|
+
},
|
|
1865
|
+
chunk_len: readLen,
|
|
1866
|
+
ui_percentage: undefined,
|
|
902
1867
|
});
|
|
1868
|
+
const data = toProtocolV2Bytes(res.message?.data);
|
|
1869
|
+
if (data.byteLength === 0) return null;
|
|
1870
|
+
chunks.push(data);
|
|
1871
|
+
offset += data.byteLength;
|
|
903
1872
|
}
|
|
904
1873
|
|
|
905
|
-
const
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
);
|
|
911
|
-
return
|
|
1874
|
+
const headerBytes = new Uint8Array(offset);
|
|
1875
|
+
let cursor = 0;
|
|
1876
|
+
chunks.forEach(chunk => {
|
|
1877
|
+
headerBytes.set(chunk, cursor);
|
|
1878
|
+
cursor += chunk.byteLength;
|
|
1879
|
+
});
|
|
1880
|
+
return parseProtocolV2OkppHeader(headerBytes);
|
|
912
1881
|
}
|
|
913
1882
|
|
|
914
|
-
/**
|
|
915
|
-
* 比对设备上已有 okpkg 的 OKPP header(仅远端配置模式使用)。
|
|
916
|
-
*/
|
|
1883
|
+
/** Compare the downloaded and installed okpkg headers before transferring a resource. */
|
|
917
1884
|
private async isProtocolV2ResourceBundleUpToDate(
|
|
918
|
-
bundle:
|
|
1885
|
+
bundle: Pick<
|
|
1886
|
+
ProtocolV2ResourceBundleSource,
|
|
1887
|
+
'name' | 'source' | 'devicePath' | 'version' | 'payloadHash' | 'headerHash'
|
|
1888
|
+
>
|
|
919
1889
|
): Promise<boolean> {
|
|
920
|
-
if (this.params
|
|
921
|
-
if (!bundle.
|
|
1890
|
+
if (this.params?.forcedUpdateRes) return false;
|
|
1891
|
+
if (!bundle.payloadHash || !bundle.headerHash) return false;
|
|
922
1892
|
|
|
923
1893
|
try {
|
|
924
|
-
const header = await this.readProtocolV2DeviceFileHeader(
|
|
1894
|
+
const header = await this.readProtocolV2DeviceFileHeader(
|
|
1895
|
+
bundle.devicePath,
|
|
1896
|
+
bundle.source.size
|
|
1897
|
+
);
|
|
925
1898
|
if (!header) return false;
|
|
926
1899
|
|
|
927
1900
|
if (bundle.version) {
|
|
928
1901
|
const cmp = compareProtocolV2Versions(header.version, bundle.version);
|
|
929
1902
|
if (cmp === undefined || cmp !== 0) return false;
|
|
930
1903
|
}
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
if (bundle.headerHash) {
|
|
936
|
-
const expected = normalizeProtocolV2Hex(bundle.headerHash);
|
|
937
|
-
if (expected && header.headerHash !== expected) return false;
|
|
938
|
-
}
|
|
1904
|
+
const expectedPayloadHash = normalizeProtocolV2Hex(bundle.payloadHash);
|
|
1905
|
+
const expectedHeaderHash = normalizeProtocolV2Hex(bundle.headerHash);
|
|
1906
|
+
if (!expectedPayloadHash || header.payloadHash !== expectedPayloadHash) return false;
|
|
1907
|
+
if (!expectedHeaderHash || header.headerHash !== expectedHeaderHash) return false;
|
|
939
1908
|
return true;
|
|
940
1909
|
} catch (error) {
|
|
941
1910
|
Log.log(`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check failed: `, error);
|
|
@@ -945,23 +1914,50 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
945
1914
|
|
|
946
1915
|
private isProtocolV2BootloaderMode() {
|
|
947
1916
|
if (typeof this.device.isBootloader === 'function') {
|
|
948
|
-
return this.device.isBootloader();
|
|
1917
|
+
return !!this.device.isBootloader();
|
|
1918
|
+
}
|
|
1919
|
+
return (
|
|
1920
|
+
this.device.features?.mode === 'bootloader' ||
|
|
1921
|
+
(this.device.features?.mode == null && !!this.device.features?.bootloaderMode)
|
|
1922
|
+
);
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
private isProtocolV2RomloaderMode() {
|
|
1926
|
+
const deviceType = this.device.getCurrentDeviceType();
|
|
1927
|
+
if (
|
|
1928
|
+
!this.device.isProtocolV2() ||
|
|
1929
|
+
(deviceType !== EDeviceType.Pro2 && deviceType !== EDeviceType.Neo)
|
|
1930
|
+
) {
|
|
1931
|
+
return false;
|
|
1932
|
+
}
|
|
1933
|
+
if (typeof this.device.isRomloader === 'function') {
|
|
1934
|
+
return this.device.isRomloader();
|
|
949
1935
|
}
|
|
950
|
-
return
|
|
1936
|
+
return this.device.features?.mode === 'romloader';
|
|
951
1937
|
}
|
|
952
1938
|
|
|
953
1939
|
async enterProtocolV2BootloaderMode() {
|
|
1940
|
+
// romloader is the first update environment and forwards targets to bootloader.
|
|
1941
|
+
// It rejects DeviceRebootType.Bootloader, so reuse the current connection.
|
|
1942
|
+
if (this.isProtocolV2RomloaderMode()) {
|
|
1943
|
+
Log.debug('Protocol V2 device is in romloader mode; start firmware update directly');
|
|
1944
|
+
this.protocolV2ExecutionInLoader = true;
|
|
1945
|
+
return false;
|
|
1946
|
+
}
|
|
954
1947
|
if (this.isProtocolV2BootloaderMode()) {
|
|
955
|
-
Log.debug('Protocol V2 device is already in bootloader mode, skip reboot
|
|
1948
|
+
Log.debug('Protocol V2 device is already in bootloader mode, skip reboot');
|
|
1949
|
+
this.protocolV2ExecutionInLoader = true;
|
|
1950
|
+
this.postTipMessage(FirmwareUpdateTipMessage.GoToBootloaderSuccess);
|
|
956
1951
|
return false;
|
|
957
1952
|
}
|
|
958
1953
|
|
|
959
1954
|
try {
|
|
960
1955
|
this.postTipMessage(FirmwareUpdateTipMessage.AutoRebootToBootloader);
|
|
961
1956
|
await this.protocolV2Reboot(DeviceRebootType.Bootloader);
|
|
962
|
-
this.postTipMessage(FirmwareUpdateTipMessage.GoToBootloaderSuccess);
|
|
963
1957
|
await wait(1000);
|
|
964
1958
|
await this.waitForProtocolV2BootloaderMode();
|
|
1959
|
+
this.protocolV2ExecutionInLoader = true;
|
|
1960
|
+
this.postTipMessage(FirmwareUpdateTipMessage.GoToBootloaderSuccess);
|
|
965
1961
|
return true;
|
|
966
1962
|
} catch (error) {
|
|
967
1963
|
if (error instanceof HardwareError) {
|
|
@@ -978,20 +1974,32 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
978
1974
|
) {
|
|
979
1975
|
const startTime = Date.now();
|
|
980
1976
|
let lastError: unknown;
|
|
1977
|
+
let shouldReconnect = true;
|
|
981
1978
|
|
|
982
1979
|
while (Date.now() - startTime < timeout) {
|
|
983
1980
|
try {
|
|
984
|
-
|
|
1981
|
+
if (shouldReconnect) {
|
|
1982
|
+
await this.reconnectProtocolV2Device();
|
|
1983
|
+
shouldReconnect = false;
|
|
1984
|
+
}
|
|
985
1985
|
const deviceInfo = await requestProtocolV2DeviceInfo({
|
|
986
1986
|
commands: this.device.getCommands(),
|
|
987
|
-
timeoutMs:
|
|
1987
|
+
timeoutMs: this.getProtocolV2DeviceInfoTimeout(),
|
|
988
1988
|
});
|
|
989
|
-
|
|
990
|
-
|
|
1989
|
+
this.assertProtocolV2DeviceInfoIdentity(deviceInfo);
|
|
1990
|
+
const features = await this.device.probeProtocolV2RuntimeState(
|
|
1991
|
+
deviceInfo,
|
|
1992
|
+
PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT
|
|
1993
|
+
);
|
|
1994
|
+
if (features?.mode === 'bootloader') {
|
|
991
1995
|
return features;
|
|
992
1996
|
}
|
|
993
1997
|
lastError = new Error('Protocol V2 device is reachable but is not in bootloader mode');
|
|
994
1998
|
} catch (error) {
|
|
1999
|
+
if (this.isProtocolV2ReconnectIdentityError(error)) {
|
|
2000
|
+
throw error;
|
|
2001
|
+
}
|
|
2002
|
+
shouldReconnect = true;
|
|
995
2003
|
lastError = error;
|
|
996
2004
|
Log.log('Protocol V2 bootloader mode not ready, polling reconnect: ', error);
|
|
997
2005
|
}
|
|
@@ -1007,8 +2015,8 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1007
2015
|
}
|
|
1008
2016
|
|
|
1009
2017
|
/**
|
|
1010
|
-
*
|
|
1011
|
-
*
|
|
2018
|
+
* Collect explicit target binaries grouped by DeviceFirmwareTargetType.
|
|
2019
|
+
* Filenames are display-only staging paths because target_id is explicit.
|
|
1012
2020
|
*/
|
|
1013
2021
|
private collectExplicitTargetBinaries() {
|
|
1014
2022
|
const entries: ProtocolV2TargetBinary[] = [];
|
|
@@ -1044,115 +2052,277 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1044
2052
|
return entries;
|
|
1045
2053
|
}
|
|
1046
2054
|
|
|
2055
|
+
private buildProtocolV2ExecutionPhases({
|
|
2056
|
+
installSources,
|
|
2057
|
+
resourceSources,
|
|
2058
|
+
}: {
|
|
2059
|
+
installSources: ProtocolV2InstallSource[];
|
|
2060
|
+
resourceSources: ProtocolV2ResourceBundleSource[];
|
|
2061
|
+
}): ProtocolV2ExecutionPhase[] {
|
|
2062
|
+
const phases: ProtocolV2ExecutionPhase[] = [];
|
|
2063
|
+
const bootloaderSources = installSources.filter(source => source.kind === 'bootloader');
|
|
2064
|
+
if (bootloaderSources.length > 0) {
|
|
2065
|
+
phases.push(
|
|
2066
|
+
{
|
|
2067
|
+
kind: 'bootloader-install',
|
|
2068
|
+
installSources: bootloaderSources,
|
|
2069
|
+
resourceSources: [],
|
|
2070
|
+
},
|
|
2071
|
+
{
|
|
2072
|
+
kind: 'bootloader-verify',
|
|
2073
|
+
installSources: [],
|
|
2074
|
+
resourceSources: [],
|
|
2075
|
+
}
|
|
2076
|
+
);
|
|
2077
|
+
}
|
|
2078
|
+
if (resourceSources.length > 0) {
|
|
2079
|
+
phases.push({
|
|
2080
|
+
kind: 'resource-sync',
|
|
2081
|
+
installSources: [],
|
|
2082
|
+
resourceSources,
|
|
2083
|
+
});
|
|
2084
|
+
}
|
|
2085
|
+
const componentSources = installSources.filter(source => source.kind !== 'bootloader');
|
|
2086
|
+
if (componentSources.length > 0) {
|
|
2087
|
+
phases.push({
|
|
2088
|
+
kind: 'component-install',
|
|
2089
|
+
installSources: componentSources,
|
|
2090
|
+
resourceSources: [],
|
|
2091
|
+
});
|
|
2092
|
+
}
|
|
2093
|
+
phases.push({
|
|
2094
|
+
kind: 'final-verify',
|
|
2095
|
+
installSources: [],
|
|
2096
|
+
resourceSources: [],
|
|
2097
|
+
});
|
|
2098
|
+
return phases;
|
|
2099
|
+
}
|
|
2100
|
+
|
|
2101
|
+
private async executeProtocolV2Phases({
|
|
2102
|
+
installSources,
|
|
2103
|
+
resourceSources,
|
|
2104
|
+
}: {
|
|
2105
|
+
installSources: ProtocolV2InstallSource[];
|
|
2106
|
+
resourceSources: ProtocolV2ResourceBundleSource[];
|
|
2107
|
+
}) {
|
|
2108
|
+
const phases = this.buildProtocolV2ExecutionPhases({
|
|
2109
|
+
installSources,
|
|
2110
|
+
resourceSources,
|
|
2111
|
+
});
|
|
2112
|
+
for (const phase of phases) {
|
|
2113
|
+
if (phase.kind === 'final-verify') {
|
|
2114
|
+
return this.completeProtocolV2FinalVerification();
|
|
2115
|
+
}
|
|
2116
|
+
if (phase.kind === 'resource-sync') {
|
|
2117
|
+
await this.enterProtocolV2BootloaderMode();
|
|
2118
|
+
await this.executeProtocolV2TransferPhase(phase);
|
|
2119
|
+
} else if (phase.kind === 'bootloader-install' || phase.kind === 'component-install') {
|
|
2120
|
+
await this.enterProtocolV2BootloaderMode();
|
|
2121
|
+
await this.executeProtocolV2TransferPhase(phase);
|
|
2122
|
+
await this.exitProtocolV2BootloaderToNormal();
|
|
2123
|
+
} else if (phase.kind === 'bootloader-verify') {
|
|
2124
|
+
await this.waitForProtocolV2FinalFeatures();
|
|
2125
|
+
this.assertExpectedProtocolV2Versions(['boot']);
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
throw ERRORS.TypedError(
|
|
2129
|
+
HardwareErrorCode.RuntimeError,
|
|
2130
|
+
'Protocol V2 execution has no final verification phase'
|
|
2131
|
+
);
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
private async executeProtocolV2SourceUpdate({
|
|
2135
|
+
installSources,
|
|
2136
|
+
resourceSources,
|
|
2137
|
+
}: {
|
|
2138
|
+
installSources: ProtocolV2InstallSource[];
|
|
2139
|
+
resourceSources: ProtocolV2ResourceBundleSource[];
|
|
2140
|
+
}) {
|
|
2141
|
+
return this.executeProtocolV2Phases({
|
|
2142
|
+
installSources,
|
|
2143
|
+
resourceSources,
|
|
2144
|
+
});
|
|
2145
|
+
}
|
|
2146
|
+
|
|
1047
2147
|
private async executeProtocolV2Update({
|
|
1048
2148
|
fwBinaryMap,
|
|
1049
2149
|
bootloaderBinary,
|
|
1050
2150
|
installItems,
|
|
1051
|
-
resourceBundles,
|
|
2151
|
+
resourceBundles = [],
|
|
1052
2152
|
}: {
|
|
1053
2153
|
fwBinaryMap?: ProtocolV2TargetBinary[];
|
|
1054
2154
|
bootloaderBinary?: ArrayBuffer | null;
|
|
1055
2155
|
installItems?: ProtocolV2InstallItem[];
|
|
1056
2156
|
resourceBundles?: ProtocolV2ResourceBundleBinary[];
|
|
1057
2157
|
}) {
|
|
1058
|
-
const
|
|
2158
|
+
const memoryInstallItems =
|
|
1059
2159
|
installItems ??
|
|
1060
2160
|
this.buildProtocolV2InstallItems({
|
|
1061
|
-
bootloaderBinary: bootloaderBinary ?? null,
|
|
1062
2161
|
fwBinaryMap: fwBinaryMap ?? [],
|
|
2162
|
+
bootloaderBinary: bootloaderBinary ?? null,
|
|
1063
2163
|
});
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
if (resourceBundles?.length) {
|
|
1073
|
-
const resourceTransfer = await this.syncProtocolV2ResourceBundles(
|
|
1074
|
-
resourceBundles,
|
|
1075
|
-
firmwareSize
|
|
2164
|
+
try {
|
|
2165
|
+
const installSources = await Promise.all(
|
|
2166
|
+
memoryInstallItems.map(async item => ({
|
|
2167
|
+
fileName: item.fileName,
|
|
2168
|
+
source: await this.openProtocolV2MemorySource(item.binary),
|
|
2169
|
+
targetId: item.targetId,
|
|
2170
|
+
kind: item.kind,
|
|
2171
|
+
}))
|
|
1076
2172
|
);
|
|
1077
|
-
|
|
1078
|
-
|
|
2173
|
+
const resourceSources = await Promise.all(
|
|
2174
|
+
resourceBundles.map(async resource => ({
|
|
2175
|
+
name: resource.name,
|
|
2176
|
+
source: await this.openProtocolV2MemorySource(resource.binary),
|
|
2177
|
+
devicePath: resource.devicePath,
|
|
2178
|
+
...(resource.version ? { version: resource.version } : {}),
|
|
2179
|
+
...(resource.payloadHash ? { payloadHash: resource.payloadHash } : {}),
|
|
2180
|
+
...(resource.headerHash ? { headerHash: resource.headerHash } : {}),
|
|
2181
|
+
}))
|
|
2182
|
+
);
|
|
2183
|
+
return await this.executeProtocolV2Phases({
|
|
2184
|
+
installSources,
|
|
2185
|
+
resourceSources,
|
|
2186
|
+
});
|
|
2187
|
+
} finally {
|
|
2188
|
+
await this.closeProtocolV2PreparedSources();
|
|
1079
2189
|
}
|
|
2190
|
+
}
|
|
1080
2191
|
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
2192
|
+
private async executeProtocolV2TransferPhase({
|
|
2193
|
+
installSources,
|
|
2194
|
+
resourceSources,
|
|
2195
|
+
}: ProtocolV2ExecutionPhase) {
|
|
2196
|
+
let totalSize = installSources.reduce((total, item) => total + item.source.size, 0);
|
|
2197
|
+
const resourcesToSync: ProtocolV2ResourceBundleSource[] = [];
|
|
2198
|
+
for (const resource of resourceSources) {
|
|
2199
|
+
if (await this.isProtocolV2ResourceBundleUpToDate(resource)) {
|
|
2200
|
+
Log.log(`[FirmwareUpdateV4] skip RESC bundle ${resource.name}; already up to date`);
|
|
2201
|
+
} else {
|
|
2202
|
+
resourcesToSync.push(resource);
|
|
2203
|
+
totalSize += resource.source.size;
|
|
1086
2204
|
}
|
|
1087
|
-
return;
|
|
1088
2205
|
}
|
|
1089
|
-
let transferredSize = processedSize;
|
|
1090
|
-
const transferStartTime = Date.now();
|
|
1091
|
-
const transferTransport = this.getProtocolV2FirmwareTransferTransport();
|
|
1092
|
-
const chunkSize = this.getProtocolV2FirmwareChunkSize();
|
|
1093
|
-
const onTransferredBytes = (bytes: number) => {
|
|
1094
|
-
transferredSize = bytes;
|
|
1095
|
-
};
|
|
1096
|
-
Log.log(
|
|
1097
|
-
`[FirmwareUpdateV4] transfer started transport=${transferTransport} total=${totalSize} bytes chunk=${chunkSize} bytes`
|
|
1098
|
-
);
|
|
1099
|
-
|
|
1100
|
-
const stagedInstallTargets: ProtocolV2InstallTarget[] = [];
|
|
1101
|
-
|
|
1102
|
-
try {
|
|
1103
|
-
for (const item of orderedInstallItems) {
|
|
1104
|
-
const filePath = this.getProtocolV2InstallItemStagingPath(item);
|
|
1105
|
-
Log.log(
|
|
1106
|
-
`[FirmwareUpdateV4] staging ${item.kind} via FilesystemFileWrite target=${item.targetId} path=${filePath} source=${item.fileName} bytes=${item.binary.byteLength}`
|
|
1107
|
-
);
|
|
1108
|
-
processedSize = await this.protocolV2CommonUpdateProcess({
|
|
1109
|
-
payload: item.binary,
|
|
1110
|
-
filePath,
|
|
1111
|
-
processedSize,
|
|
1112
|
-
totalSize,
|
|
1113
|
-
onTransferredBytes,
|
|
1114
|
-
});
|
|
1115
|
-
transferredSize = processedSize;
|
|
1116
|
-
await this.verifyProtocolV2StagedFile(filePath, item.binary.byteLength);
|
|
1117
2206
|
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
2207
|
+
this.postTipMessage(FirmwareUpdateTipMessage.StartTransferData);
|
|
2208
|
+
let processedSize = 0;
|
|
2209
|
+
for (const resource of resourcesToSync) {
|
|
2210
|
+
// The bootloader keeps its live resource package mounted. FatFs rejects
|
|
2211
|
+
// replacing an open file, so early boot promotes this staging file before mounting it.
|
|
2212
|
+
const writePath = resolveProtocolV2ResourceWritePath(resource.devicePath);
|
|
2213
|
+
processedSize = await this.protocolV2SourceUpdateProcess({
|
|
2214
|
+
source: resource.source,
|
|
2215
|
+
filePath: writePath,
|
|
2216
|
+
processedSize,
|
|
2217
|
+
totalSize,
|
|
2218
|
+
});
|
|
2219
|
+
await this.verifyProtocolV2StagedFile(writePath, resource.source.size);
|
|
2220
|
+
}
|
|
1123
2221
|
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
2222
|
+
const stagedInstallTargets: Array<{ targetId: number; path: string }> = [];
|
|
2223
|
+
for (const item of installSources) {
|
|
2224
|
+
const filePath = this.getProtocolV2InstallItemStagingPath(item);
|
|
2225
|
+
processedSize = await this.protocolV2SourceUpdateProcess({
|
|
2226
|
+
source: item.source,
|
|
2227
|
+
filePath,
|
|
2228
|
+
processedSize,
|
|
2229
|
+
totalSize,
|
|
2230
|
+
});
|
|
2231
|
+
await this.verifyProtocolV2StagedFile(filePath, item.source.size);
|
|
2232
|
+
stagedInstallTargets.push({
|
|
2233
|
+
targetId: item.targetId,
|
|
2234
|
+
path: filePath,
|
|
2235
|
+
});
|
|
2236
|
+
}
|
|
1127
2237
|
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
);
|
|
1134
|
-
} catch (error) {
|
|
1135
|
-
const elapsedMs = Date.now() - transferStartTime;
|
|
1136
|
-
Log.warn(
|
|
1137
|
-
`[FirmwareUpdateV4] transfer failed transport=${transferTransport} bytes=${transferredSize}/${totalSize} elapsed=${(
|
|
1138
|
-
elapsedMs / 1000
|
|
1139
|
-
).toFixed(2)}s speed=${formatProtocolV2TransferSpeed(transferredSize, elapsedMs)} KB/s`
|
|
1140
|
-
);
|
|
1141
|
-
throw error;
|
|
2238
|
+
if (totalSize > 0) {
|
|
2239
|
+
this.postProgressMessage(100, 'transferData');
|
|
2240
|
+
}
|
|
2241
|
+
if (stagedInstallTargets.length === 0) {
|
|
2242
|
+
return;
|
|
1142
2243
|
}
|
|
1143
2244
|
|
|
1144
2245
|
this.postTipMessage(FirmwareUpdateTipMessage.ConfirmOnDevice);
|
|
1145
|
-
|
|
1146
|
-
const allTargets = stagedInstallTargets.map(item => ({
|
|
2246
|
+
const targets = stagedInstallTargets.map(item => ({
|
|
1147
2247
|
target_id: item.targetId,
|
|
1148
2248
|
path: item.path,
|
|
1149
2249
|
}));
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
2250
|
+
await this.protocolV2StartFirmwareUpdate({ targets });
|
|
2251
|
+
await this.waitForProtocolV2FirmwareUpdateComplete(targets);
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2254
|
+
private async protocolV2SourceUpdateProcess({
|
|
2255
|
+
source,
|
|
2256
|
+
filePath,
|
|
2257
|
+
processedSize,
|
|
2258
|
+
totalSize,
|
|
2259
|
+
}: {
|
|
2260
|
+
source: FirmwareByteSource;
|
|
2261
|
+
filePath: string;
|
|
2262
|
+
processedSize: number;
|
|
2263
|
+
totalSize: number;
|
|
2264
|
+
}) {
|
|
2265
|
+
let lastError: unknown;
|
|
2266
|
+
for (let attempt = 1; attempt <= PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT; attempt += 1) {
|
|
2267
|
+
try {
|
|
2268
|
+
const transferStartedAt = Date.now();
|
|
2269
|
+
await writeFirmwareByteSource({
|
|
2270
|
+
source,
|
|
2271
|
+
chunkSize: this.getProtocolV2FirmwareChunkSize('write', filePath),
|
|
2272
|
+
write: async ({ data, sourceOffset, length, first }) => {
|
|
2273
|
+
const chunkEnd = sourceOffset + length;
|
|
2274
|
+
const deviceProgress = getProtocolV2DeviceTransferProgress(
|
|
2275
|
+
processedSize + sourceOffset,
|
|
2276
|
+
processedSize + chunkEnd,
|
|
2277
|
+
totalSize
|
|
2278
|
+
);
|
|
2279
|
+
const response = await this.fileWriteChunk(
|
|
2280
|
+
filePath,
|
|
2281
|
+
source.size,
|
|
2282
|
+
sourceOffset,
|
|
2283
|
+
data,
|
|
2284
|
+
first,
|
|
2285
|
+
deviceProgress
|
|
2286
|
+
);
|
|
2287
|
+
const rawProcessedByte = response.message.processed_byte;
|
|
2288
|
+
const nextOffset = rawProcessedByte === undefined ? chunkEnd : Number(rawProcessedByte);
|
|
2289
|
+
if (!Number.isFinite(nextOffset) || nextOffset !== chunkEnd) {
|
|
2290
|
+
throw ERRORS.TypedError(
|
|
2291
|
+
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
2292
|
+
`invalid processed_byte ${rawProcessedByte} for offset ${sourceOffset}`
|
|
2293
|
+
);
|
|
2294
|
+
}
|
|
2295
|
+
const transferredBytes = processedSize + chunkEnd;
|
|
2296
|
+
const elapsedMs = Math.max(Date.now() - transferStartedAt, 0);
|
|
2297
|
+
this.postProgressMessage(
|
|
2298
|
+
Math.min(Math.ceil((transferredBytes / totalSize) * 100), 99),
|
|
2299
|
+
'transferData',
|
|
2300
|
+
{
|
|
2301
|
+
transferredBytes,
|
|
2302
|
+
totalBytes: totalSize,
|
|
2303
|
+
rateBytesPerSecond:
|
|
2304
|
+
elapsedMs > 0 ? Math.round((chunkEnd / elapsedMs) * 1000) : undefined,
|
|
2305
|
+
elapsedMs,
|
|
2306
|
+
}
|
|
2307
|
+
);
|
|
2308
|
+
return length;
|
|
2309
|
+
},
|
|
2310
|
+
});
|
|
2311
|
+
return processedSize + source.size;
|
|
2312
|
+
} catch (error) {
|
|
2313
|
+
lastError = error;
|
|
2314
|
+
if (attempt < PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT) {
|
|
2315
|
+
await this.recoverProtocolV2FileTransfer();
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
throw ERRORS.TypedError(
|
|
2320
|
+
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
2321
|
+
`transfer data error: ${getProtocolV2UnknownErrorText(lastError)}`
|
|
2322
|
+
);
|
|
1153
2323
|
}
|
|
1154
2324
|
|
|
1155
|
-
private getProtocolV2InstallItemStagingPath(item: ProtocolV2InstallItem) {
|
|
2325
|
+
private getProtocolV2InstallItemStagingPath(item: Pick<ProtocolV2InstallItem, 'fileName'>) {
|
|
1156
2326
|
return `${PROTOCOL_V2_FIRMWARE_STAGING_VOLUME}${item.fileName}`;
|
|
1157
2327
|
}
|
|
1158
2328
|
|
|
@@ -1169,168 +2339,315 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1169
2339
|
}
|
|
1170
2340
|
}
|
|
1171
2341
|
|
|
1172
|
-
private async queryProtocolV2FirmwareUpdateStatus() {
|
|
1173
|
-
const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
|
|
1174
|
-
return typedCall(
|
|
1175
|
-
'DeviceFirmwareUpdateStatusGet',
|
|
1176
|
-
'DeviceFirmwareUpdateStatus',
|
|
1177
|
-
{},
|
|
1178
|
-
{
|
|
1179
|
-
timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
|
|
1180
|
-
}
|
|
1181
|
-
);
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
private async pingProtocolV2Device() {
|
|
1185
|
-
const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
|
|
1186
|
-
await typedCall(
|
|
1187
|
-
'Ping',
|
|
1188
|
-
'Success',
|
|
1189
|
-
{ message: 'firmware-update' },
|
|
1190
|
-
{
|
|
1191
|
-
timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
|
|
1192
|
-
}
|
|
1193
|
-
);
|
|
1194
|
-
}
|
|
1195
|
-
|
|
1196
|
-
private isProtocolV2NormalModeFeatures(features?: Features | null) {
|
|
1197
|
-
return !!features && !features.bootloaderMode && features.mode !== 'bootloader';
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
|
-
private async probeProtocolV2NormalMode() {
|
|
1201
|
-
const deviceInfo = await requestProtocolV2DeviceInfo({
|
|
1202
|
-
commands: this.device.getCommands(),
|
|
1203
|
-
timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
|
|
1204
|
-
request: PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST,
|
|
1205
|
-
});
|
|
1206
|
-
const features = this.device.updateProtocolV2Features(deviceInfo);
|
|
1207
|
-
if (this.isProtocolV2NormalModeFeatures(features)) {
|
|
1208
|
-
Log.log('Protocol V2 firmware install finished; device is back in normal mode');
|
|
1209
|
-
return true;
|
|
1210
|
-
}
|
|
1211
|
-
return false;
|
|
1212
|
-
}
|
|
1213
|
-
|
|
1214
2342
|
private assertProtocolV2TargetStatus(
|
|
1215
2343
|
statusTargets: ProtocolV2FirmwareUpdateStatusTarget[],
|
|
1216
|
-
expectedTargetIds: Set<number
|
|
2344
|
+
expectedTargetIds: Set<number>,
|
|
2345
|
+
expectedPaths = new Map<number, string>()
|
|
1217
2346
|
) {
|
|
2347
|
+
Log.log(
|
|
2348
|
+
`[FirmwareUpdateV4] DeviceFirmwareUpdateStatus records=${JSON.stringify(statusTargets)}`
|
|
2349
|
+
);
|
|
1218
2350
|
const failedTarget = statusTargets.find(
|
|
1219
2351
|
target =>
|
|
1220
2352
|
expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1) &&
|
|
1221
2353
|
isProtocolV2TargetStatusFailed(target.status)
|
|
1222
2354
|
);
|
|
1223
2355
|
if (failedTarget) {
|
|
2356
|
+
const failedTargetDetails = JSON.stringify({
|
|
2357
|
+
targetId: failedTarget.target_id,
|
|
2358
|
+
status: failedTarget.status,
|
|
2359
|
+
payloadVersion: failedTarget.payload_version,
|
|
2360
|
+
path: failedTarget.path,
|
|
2361
|
+
});
|
|
2362
|
+
Log.error(`[FirmwareUpdateV4] firmware install failed target=${failedTargetDetails}`);
|
|
1224
2363
|
throw ERRORS.TypedError(
|
|
1225
2364
|
HardwareErrorCode.FirmwareError,
|
|
1226
|
-
`Protocol V2 firmware target
|
|
2365
|
+
`Protocol V2 firmware target failed: target=${failedTarget.target_id} status=${
|
|
2366
|
+
failedTarget.status ?? 'unknown'
|
|
2367
|
+
} payloadVersion=${failedTarget.payload_version ?? 'unknown'} path=${
|
|
2368
|
+
failedTarget.path ?? 'unknown'
|
|
2369
|
+
}`
|
|
1227
2370
|
);
|
|
1228
2371
|
}
|
|
1229
2372
|
|
|
1230
|
-
const
|
|
1231
|
-
target
|
|
1232
|
-
|
|
1233
|
-
|
|
2373
|
+
const matchingTargets = statusTargets.filter(target =>
|
|
2374
|
+
expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1)
|
|
2375
|
+
);
|
|
2376
|
+
const seenTargetIds = new Set<number>();
|
|
2377
|
+
for (const target of matchingTargets) {
|
|
2378
|
+
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
2379
|
+
if (
|
|
2380
|
+
targetId === undefined ||
|
|
2381
|
+
seenTargetIds.has(targetId) ||
|
|
2382
|
+
(target.path && expectedPaths.get(targetId) && target.path !== expectedPaths.get(targetId))
|
|
2383
|
+
) {
|
|
2384
|
+
throw ERRORS.TypedError(
|
|
2385
|
+
HardwareErrorCode.RuntimeError,
|
|
2386
|
+
`Protocol V2 install status conflicts with target ${target.target_id}`,
|
|
2387
|
+
{
|
|
2388
|
+
firmwareUpdateCode: PROTOCOL_V2_INSTALL_STATUS_CONFLICT_CODE,
|
|
2389
|
+
}
|
|
2390
|
+
);
|
|
2391
|
+
}
|
|
2392
|
+
seenTargetIds.add(targetId);
|
|
2393
|
+
}
|
|
2394
|
+
const completedTargets = matchingTargets.filter(target =>
|
|
2395
|
+
isProtocolV2TargetStatusFinished(target.status)
|
|
1234
2396
|
);
|
|
1235
|
-
|
|
2397
|
+
const completedTargetIds = new Set<number>();
|
|
2398
|
+
completedTargets.forEach(target => {
|
|
2399
|
+
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
2400
|
+
if (targetId !== undefined) {
|
|
2401
|
+
completedTargetIds.add(targetId);
|
|
2402
|
+
}
|
|
2403
|
+
});
|
|
2404
|
+
if (completedTargetIds.size === expectedTargetIds.size && expectedTargetIds.size > 0) {
|
|
2405
|
+
for (const target of completedTargets) {
|
|
2406
|
+
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
2407
|
+
const payloadVersion = toProtocolV2FiniteNumber(target.payload_version);
|
|
2408
|
+
if (targetId === undefined) {
|
|
2409
|
+
throw ERRORS.TypedError(
|
|
2410
|
+
HardwareErrorCode.RuntimeError,
|
|
2411
|
+
'Protocol V2 completed target identity is invalid'
|
|
2412
|
+
);
|
|
2413
|
+
}
|
|
2414
|
+
const expectedVersion = this.getExpectedProtocolV2TargetVersion(targetId);
|
|
2415
|
+
if (expectedVersion !== undefined && payloadVersion !== undefined) {
|
|
2416
|
+
if (payloadVersion !== expectedVersion) {
|
|
2417
|
+
throw ERRORS.TypedError(
|
|
2418
|
+
HardwareErrorCode.FirmwareVerificationFailed,
|
|
2419
|
+
`Protocol V2 target ${targetId} reached payload version ${payloadVersion}, expected ${expectedVersion}`
|
|
2420
|
+
);
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
if (payloadVersion !== undefined) {
|
|
2424
|
+
this.protocolV2CompletedTargetVersions.set(targetId, payloadVersion);
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
this.postProgressMessage(100, 'installingFirmware');
|
|
1236
2428
|
return true;
|
|
1237
2429
|
}
|
|
1238
2430
|
|
|
1239
|
-
|
|
1240
|
-
target =>
|
|
1241
|
-
expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1) &&
|
|
2431
|
+
if (expectedTargetIds.size > 0 && matchingTargets.length > 0) {
|
|
2432
|
+
const hasInProgressTarget = matchingTargets.some(target =>
|
|
1242
2433
|
isProtocolV2TargetStatusInProgress(target.status)
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
2434
|
+
);
|
|
2435
|
+
const completedProgress = Math.floor(
|
|
2436
|
+
(completedTargetIds.size / expectedTargetIds.size) * 100
|
|
2437
|
+
);
|
|
2438
|
+
// The protocol exposes no per-target percentage, so report coarse progress by
|
|
2439
|
+
// completed targets and use 1% once work starts to keep the UI responsive.
|
|
2440
|
+
const progress = Math.min(99, Math.max(completedProgress, hasInProgressTarget ? 1 : 0));
|
|
2441
|
+
this.postProgressMessage(progress, 'installingFirmware');
|
|
1246
2442
|
}
|
|
1247
2443
|
|
|
1248
2444
|
return false;
|
|
1249
2445
|
}
|
|
1250
2446
|
|
|
1251
|
-
private
|
|
1252
|
-
|
|
1253
|
-
|
|
2447
|
+
private getProtocolV2MissingTargetIds(
|
|
2448
|
+
statusTargets: ProtocolV2FirmwareUpdateStatusTarget[],
|
|
2449
|
+
expectedTargetIds: Set<number>
|
|
1254
2450
|
) {
|
|
1255
|
-
const
|
|
1256
|
-
|
|
1257
|
-
const
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
return;
|
|
2451
|
+
const reportedTargetIds = new Set<number>();
|
|
2452
|
+
statusTargets.forEach(target => {
|
|
2453
|
+
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
2454
|
+
if (targetId !== undefined) {
|
|
2455
|
+
reportedTargetIds.add(targetId);
|
|
1261
2456
|
}
|
|
1262
|
-
}
|
|
2457
|
+
});
|
|
2458
|
+
return Array.from(expectedTargetIds).filter(targetId => !reportedTargetIds.has(targetId));
|
|
2459
|
+
}
|
|
1263
2460
|
|
|
2461
|
+
private async waitForProtocolV2FirmwareUpdateComplete(
|
|
2462
|
+
targets: Array<{ target_id: number; path: string }>
|
|
2463
|
+
) {
|
|
2464
|
+
this.protocolV2FinalStatusVerified = false;
|
|
2465
|
+
const expectedTargetIds = new Set(targets.map(target => target.target_id));
|
|
2466
|
+
const expectedPaths = new Map(targets.map(target => [target.target_id, target.path]));
|
|
1264
2467
|
const startTime = Date.now();
|
|
1265
2468
|
let lastError: unknown;
|
|
2469
|
+
let shouldReconnect = true;
|
|
2470
|
+
let deviceInfo: ProtocolV2DeviceInfo | undefined;
|
|
2471
|
+
let missingTargetStatusSince: number | undefined;
|
|
2472
|
+
let missingTargetStatusKey: string | undefined;
|
|
2473
|
+
const resetMissingTargetStatusGrace = () => {
|
|
2474
|
+
missingTargetStatusSince = undefined;
|
|
2475
|
+
missingTargetStatusKey = undefined;
|
|
2476
|
+
};
|
|
1266
2477
|
|
|
1267
2478
|
while (Date.now() - startTime < PROTOCOL_V2_INSTALL_TIMEOUT) {
|
|
1268
2479
|
try {
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
return;
|
|
1274
|
-
}
|
|
1275
|
-
} catch (error) {
|
|
1276
|
-
lastError = error;
|
|
1277
|
-
if (error instanceof HardwareError && error.errorCode === HardwareErrorCode.FirmwareError) {
|
|
1278
|
-
throw error;
|
|
2480
|
+
if (shouldReconnect) {
|
|
2481
|
+
await this.reconnectProtocolV2Device();
|
|
2482
|
+
deviceInfo = await this.verifyProtocolV2ReconnectIdentity();
|
|
2483
|
+
shouldReconnect = false;
|
|
1279
2484
|
}
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
2485
|
+
const currentDeviceInfo = deviceInfo;
|
|
2486
|
+
try {
|
|
2487
|
+
const statusResponse = await this.device.getCommands().typedCall(
|
|
2488
|
+
'DeviceFirmwareUpdateStatusGet',
|
|
2489
|
+
'DeviceFirmwareUpdateStatus',
|
|
2490
|
+
{
|
|
2491
|
+
fields: {
|
|
2492
|
+
status: true,
|
|
2493
|
+
payload_version: true,
|
|
2494
|
+
path: true,
|
|
2495
|
+
},
|
|
2496
|
+
},
|
|
2497
|
+
{ timeoutMs: PROTOCOL_V2_FIRMWARE_STATUS_RESPONSE_TIMEOUT }
|
|
2498
|
+
);
|
|
2499
|
+
const statusTargets = (statusResponse.message.records ??
|
|
2500
|
+
[]) as ProtocolV2FirmwareUpdateStatusTarget[];
|
|
2501
|
+
if (this.assertProtocolV2TargetStatus(statusTargets, expectedTargetIds, expectedPaths)) {
|
|
2502
|
+
this.protocolV2FinalStatusVerified = true;
|
|
2503
|
+
return;
|
|
2504
|
+
}
|
|
2505
|
+
|
|
2506
|
+
if (
|
|
2507
|
+
statusTargets.length === 0 &&
|
|
2508
|
+
currentDeviceInfo &&
|
|
2509
|
+
(await this.probeProtocolV2NormalMode(currentDeviceInfo))
|
|
2510
|
+
) {
|
|
2511
|
+
Log.log(
|
|
2512
|
+
'[FirmwareUpdateV4] empty firmware status after confirmed App reboot; update complete'
|
|
2513
|
+
);
|
|
2514
|
+
this.postProgressMessage(100, 'installingFirmware');
|
|
2515
|
+
return;
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2518
|
+
const missingTargetIds = this.getProtocolV2MissingTargetIds(
|
|
2519
|
+
statusTargets,
|
|
2520
|
+
expectedTargetIds
|
|
2521
|
+
);
|
|
2522
|
+
if (missingTargetIds.length > 0) {
|
|
2523
|
+
const now = Date.now();
|
|
2524
|
+
const missingKey = missingTargetIds.join(',');
|
|
2525
|
+
if (missingTargetStatusSince === undefined || missingTargetStatusKey !== missingKey) {
|
|
2526
|
+
missingTargetStatusSince = now;
|
|
2527
|
+
missingTargetStatusKey = missingKey;
|
|
2528
|
+
} else if (
|
|
2529
|
+
now - missingTargetStatusSince >=
|
|
2530
|
+
PROTOCOL_V2_MISSING_TARGET_STATUS_GRACE_TIMEOUT
|
|
2531
|
+
) {
|
|
2532
|
+
const reportedTargetIds = statusTargets
|
|
2533
|
+
.map(target => normalizeProtocolV2TargetId(target.target_id))
|
|
2534
|
+
.filter((targetId): targetId is number => targetId !== undefined);
|
|
2535
|
+
throw ERRORS.TypedError(
|
|
2536
|
+
HardwareErrorCode.FirmwareError,
|
|
2537
|
+
`Protocol V2 firmware status is missing requested records: targetIds=${missingKey} reportedTargetIds=${reportedTargetIds.join(
|
|
2538
|
+
','
|
|
2539
|
+
)}`
|
|
2540
|
+
);
|
|
2541
|
+
}
|
|
2542
|
+
lastError = new Error(
|
|
2543
|
+
`Protocol V2 firmware status is temporarily missing targetIds=${missingKey}`
|
|
2544
|
+
);
|
|
2545
|
+
} else {
|
|
2546
|
+
resetMissingTargetStatusGrace();
|
|
2547
|
+
lastError = new Error('Protocol V2 firmware targets are still installing');
|
|
2548
|
+
}
|
|
2549
|
+
} catch (error) {
|
|
2550
|
+
if (isProtocolV2TerminalInstallStatusError(error)) {
|
|
2551
|
+
throw error;
|
|
2552
|
+
}
|
|
2553
|
+
// Only consecutive full status dumps can prove that a record is absent.
|
|
2554
|
+
// A reboot or transport interruption starts a fresh grace window.
|
|
2555
|
+
resetMissingTargetStatusGrace();
|
|
2556
|
+
// App firmware does not register DeviceFirmwareUpdateStatusGet. Treat the
|
|
2557
|
+
// missing endpoint as completion only after the runtime probe confirms App mode.
|
|
2558
|
+
if (isProtocolV2FirmwareStatusEndpointUnavailable(error)) {
|
|
2559
|
+
if (!currentDeviceInfo) {
|
|
2560
|
+
throw ERRORS.TypedError(
|
|
2561
|
+
HardwareErrorCode.RuntimeError,
|
|
2562
|
+
'Protocol V2 device identity is unavailable during install polling'
|
|
2563
|
+
);
|
|
2564
|
+
}
|
|
2565
|
+
if (await this.probeProtocolV2NormalMode(currentDeviceInfo)) {
|
|
2566
|
+
Log.log(
|
|
2567
|
+
'[FirmwareUpdateV4] firmware status endpoint unavailable after confirmed App reboot'
|
|
2568
|
+
);
|
|
2569
|
+
this.postProgressMessage(100, 'installingFirmware');
|
|
1285
2570
|
return;
|
|
1286
2571
|
}
|
|
1287
|
-
|
|
1288
|
-
|
|
2572
|
+
lastError = new Error(
|
|
2573
|
+
'Protocol V2 firmware status endpoint is unavailable while the device remains in loader mode'
|
|
2574
|
+
);
|
|
2575
|
+
} else {
|
|
2576
|
+
shouldReconnect = true;
|
|
2577
|
+
deviceInfo = undefined;
|
|
2578
|
+
lastError = error;
|
|
1289
2579
|
Log.log(
|
|
1290
|
-
'
|
|
1291
|
-
|
|
2580
|
+
'[FirmwareUpdateV4] DeviceFirmwareUpdateStatusGet unavailable during install: ',
|
|
2581
|
+
error
|
|
1292
2582
|
);
|
|
1293
2583
|
}
|
|
1294
|
-
try {
|
|
1295
|
-
await this.pingProtocolV2Device();
|
|
1296
|
-
Log.log('Protocol V2 firmware status unavailable, Ping is ready');
|
|
1297
|
-
} catch (pingError) {
|
|
1298
|
-
lastError = pingError;
|
|
1299
|
-
Log.log('Protocol V2 firmware install Ping polling failed: ', pingError);
|
|
1300
|
-
}
|
|
1301
2584
|
}
|
|
2585
|
+
} catch (error) {
|
|
2586
|
+
lastError = error;
|
|
2587
|
+
if (this.isProtocolV2ReconnectIdentityError(error)) {
|
|
2588
|
+
throw error;
|
|
2589
|
+
}
|
|
2590
|
+
if (isProtocolV2TerminalInstallStatusError(error)) {
|
|
2591
|
+
throw error;
|
|
2592
|
+
}
|
|
2593
|
+
if (
|
|
2594
|
+
error instanceof HardwareError &&
|
|
2595
|
+
error.params?.firmwareUpdateCode === 'FirmwareInstallStatusUnavailable'
|
|
2596
|
+
) {
|
|
2597
|
+
throw error;
|
|
2598
|
+
}
|
|
2599
|
+
shouldReconnect = true;
|
|
2600
|
+
deviceInfo = undefined;
|
|
2601
|
+
resetMissingTargetStatusGrace();
|
|
2602
|
+
Log.log('Protocol V2 firmware install device readiness probe failed: ', error);
|
|
1302
2603
|
}
|
|
1303
2604
|
await wait(1000);
|
|
1304
2605
|
}
|
|
1305
2606
|
|
|
1306
2607
|
throw ERRORS.TypedError(
|
|
1307
2608
|
HardwareErrorCode.RuntimeError,
|
|
1308
|
-
`Protocol V2 firmware update status
|
|
2609
|
+
`Protocol V2 firmware update status not complete within ${
|
|
2610
|
+
PROTOCOL_V2_INSTALL_TIMEOUT / 1000
|
|
2611
|
+
}s: ${this.normalizeErrorMessage(lastError)}`
|
|
1309
2612
|
);
|
|
1310
2613
|
}
|
|
1311
2614
|
|
|
1312
2615
|
private async exitProtocolV2BootloaderToNormal() {
|
|
1313
|
-
this.
|
|
2616
|
+
await this.reconnectProtocolV2Device();
|
|
2617
|
+
const deviceInfo = await this.verifyProtocolV2ReconnectIdentity();
|
|
1314
2618
|
try {
|
|
1315
|
-
await this.
|
|
1316
|
-
|
|
1317
|
-
Log.log('Protocol V2 device is already in normal mode, skip normal reboot');
|
|
2619
|
+
if (await this.probeProtocolV2NormalMode(deviceInfo)) {
|
|
2620
|
+
Log.log('[FirmwareUpdateV4] device already returned to App mode; skip Normal reboot');
|
|
1318
2621
|
return;
|
|
1319
2622
|
}
|
|
1320
2623
|
} catch (error) {
|
|
1321
|
-
Log.log('
|
|
2624
|
+
Log.log('[FirmwareUpdateV4] unable to confirm App mode before Normal reboot: ', error);
|
|
1322
2625
|
}
|
|
1323
2626
|
await this.protocolV2Reboot(DeviceRebootType.Normal);
|
|
2627
|
+
this.protocolV2ExecutionInLoader = false;
|
|
2628
|
+
}
|
|
2629
|
+
|
|
2630
|
+
private async probeProtocolV2NormalMode(deviceInfo: ProtocolV2DeviceInfo) {
|
|
2631
|
+
const features = await this.device.probeProtocolV2RuntimeState(
|
|
2632
|
+
deviceInfo,
|
|
2633
|
+
PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT
|
|
2634
|
+
);
|
|
2635
|
+
return features.mode === 'normal' && !features.bootloaderMode;
|
|
1324
2636
|
}
|
|
1325
2637
|
|
|
1326
2638
|
private async waitForProtocolV2FinalFeatures() {
|
|
1327
2639
|
const features = await this.waitForProtocolV2ReconnectAndFeatures(
|
|
1328
|
-
|
|
2640
|
+
PROTOCOL_V2_FINAL_RECONNECT_TIMEOUT
|
|
1329
2641
|
);
|
|
1330
2642
|
|
|
2643
|
+
return this.getProtocolV2VersionResult(features);
|
|
2644
|
+
}
|
|
2645
|
+
|
|
2646
|
+
private getProtocolV2VersionResult(features: Features) {
|
|
1331
2647
|
const bootloaderVersion = getDeviceBootloaderVersion(features).join('.');
|
|
1332
2648
|
const bleVersion = getDeviceBLEFirmwareVersion(features).join('.');
|
|
1333
2649
|
const firmwareVersion = getDeviceFirmwareVersion(features).join('.');
|
|
2650
|
+
this.protocolV2LatestFinalFeatures = features;
|
|
1334
2651
|
if (firmwareVersion === '0.0.0') {
|
|
1335
2652
|
Log.warn(
|
|
1336
2653
|
'Protocol V2 firmware update finished but app firmware version is still 0.0.0. This is allowed for Pro2 debug BLE-only update flows.'
|
|
@@ -1344,32 +2661,55 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1344
2661
|
};
|
|
1345
2662
|
}
|
|
1346
2663
|
|
|
2664
|
+
private async completeProtocolV2FinalVerification() {
|
|
2665
|
+
if (this.protocolV2ExecutionInLoader || this.isProtocolV2BootloaderMode()) {
|
|
2666
|
+
await this.exitProtocolV2BootloaderToNormal();
|
|
2667
|
+
}
|
|
2668
|
+
const versions = await this.waitForProtocolV2FinalFeatures();
|
|
2669
|
+
this.assertExpectedProtocolV2Versions();
|
|
2670
|
+
this.postTipMessage(FirmwareUpdateTipMessage.FirmwareUpdateCompleted);
|
|
2671
|
+
DevicePool.resetState();
|
|
2672
|
+
return versions;
|
|
2673
|
+
}
|
|
2674
|
+
|
|
1347
2675
|
private async waitForProtocolV2ReconnectAndFeatures(timeout: number) {
|
|
1348
2676
|
const startTime = Date.now();
|
|
1349
2677
|
let lastError: unknown;
|
|
2678
|
+
let shouldReconnect = true;
|
|
1350
2679
|
|
|
1351
2680
|
while (Date.now() - startTime < timeout) {
|
|
1352
2681
|
try {
|
|
1353
|
-
|
|
2682
|
+
if (shouldReconnect) {
|
|
2683
|
+
await this.reconnectProtocolV2Device();
|
|
2684
|
+
shouldReconnect = false;
|
|
2685
|
+
}
|
|
1354
2686
|
const deviceInfo = await requestProtocolV2DeviceInfo({
|
|
1355
2687
|
commands: this.device.getCommands(),
|
|
1356
|
-
timeoutMs:
|
|
1357
|
-
//
|
|
2688
|
+
timeoutMs: this.getProtocolV2DeviceInfoTimeout(),
|
|
2689
|
+
// Completion needs target versions only; keep scope aligned with the request.
|
|
1358
2690
|
request: PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST,
|
|
1359
2691
|
});
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
2692
|
+
this.assertProtocolV2DeviceInfoIdentity(deviceInfo);
|
|
2693
|
+
const features = await this.device.probeProtocolV2RuntimeState(
|
|
2694
|
+
deviceInfo,
|
|
2695
|
+
PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT
|
|
2696
|
+
);
|
|
2697
|
+
if (features.mode === 'normal' && !features.bootloaderMode) {
|
|
2698
|
+
return features;
|
|
1366
2699
|
}
|
|
1367
|
-
|
|
2700
|
+
lastError = ERRORS.TypedError(
|
|
2701
|
+
HardwareErrorCode.DeviceNotFound,
|
|
2702
|
+
'Protocol V2 device is still in bootloader mode'
|
|
2703
|
+
);
|
|
1368
2704
|
} catch (error) {
|
|
2705
|
+
if (this.isProtocolV2ReconnectIdentityError(error)) {
|
|
2706
|
+
throw error;
|
|
2707
|
+
}
|
|
2708
|
+
shouldReconnect = true;
|
|
1369
2709
|
lastError = error;
|
|
1370
2710
|
Log.log('Protocol V2 normal mode not ready, polling Ping: ', error);
|
|
1371
|
-
await wait(1000);
|
|
1372
2711
|
}
|
|
2712
|
+
await wait(1000);
|
|
1373
2713
|
}
|
|
1374
2714
|
|
|
1375
2715
|
throw ERRORS.TypedError(
|
|
@@ -1393,130 +2733,71 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1393
2733
|
DataManager.isBrowserWebUsb(DataManager.getSettings('env')) &&
|
|
1394
2734
|
devicesDescriptor.length === 1
|
|
1395
2735
|
) {
|
|
2736
|
+
const descriptor = devicesDescriptor[0];
|
|
2737
|
+
if (!this.protocolV2ExpectedSerialNumber && descriptor.path !== this.protocolV2ExpectedPath) {
|
|
2738
|
+
throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound);
|
|
2739
|
+
}
|
|
1396
2740
|
this.device.updateDescriptor(
|
|
1397
2741
|
{
|
|
1398
|
-
...
|
|
2742
|
+
...descriptor,
|
|
1399
2743
|
protocolType: PROTOCOL_V2_CONNECT_PROTOCOL,
|
|
1400
2744
|
},
|
|
1401
2745
|
true
|
|
1402
2746
|
);
|
|
1403
|
-
await this.
|
|
2747
|
+
await this.ensureProtocolV2DeviceAcquired();
|
|
1404
2748
|
this.device.commands.disposed = false;
|
|
1405
2749
|
this.device.getCommands().mainId = this.device.mainId ?? '';
|
|
1406
|
-
await this.device.initialize();
|
|
1407
2750
|
return;
|
|
1408
2751
|
}
|
|
1409
2752
|
|
|
1410
|
-
//
|
|
1411
|
-
//
|
|
2753
|
+
// Reinitialize every USB descriptor so DeviceInfo can provide the physical serial.
|
|
2754
|
+
// Enumeration order is not stable and must never decide the reconnect target.
|
|
1412
2755
|
const { deviceList } = await DevicePool.getDevices(devicesDescriptor, undefined, {
|
|
1413
2756
|
connectProtocol: PROTOCOL_V2_CONNECT_PROTOCOL,
|
|
1414
2757
|
});
|
|
1415
|
-
|
|
2758
|
+
const expectedSerialNumber = this.protocolV2ExpectedSerialNumber?.trim();
|
|
2759
|
+
const expectedPath = this.protocolV2ExpectedPath?.trim();
|
|
2760
|
+
const reconnectDevice = deviceList.find(candidate => {
|
|
2761
|
+
const candidateSerialNumber = candidate.getCurrentSerialNo?.().trim();
|
|
2762
|
+
if (expectedSerialNumber && candidateSerialNumber) {
|
|
2763
|
+
return candidateSerialNumber === expectedSerialNumber;
|
|
2764
|
+
}
|
|
2765
|
+
return !!expectedPath && candidate.getConnectId() === expectedPath;
|
|
2766
|
+
});
|
|
2767
|
+
if (!reconnectDevice) {
|
|
1416
2768
|
throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound);
|
|
1417
2769
|
}
|
|
1418
2770
|
|
|
1419
2771
|
Log.debug(
|
|
1420
|
-
'Protocol V2 firmware reconnect using
|
|
1421
|
-
|
|
2772
|
+
'Protocol V2 firmware reconnect using matched device:',
|
|
2773
|
+
reconnectDevice.getConnectId()
|
|
1422
2774
|
);
|
|
1423
|
-
this.device.updateFromCache(
|
|
1424
|
-
await this.
|
|
2775
|
+
this.device.updateFromCache(reconnectDevice);
|
|
2776
|
+
await this.ensureProtocolV2DeviceAcquired();
|
|
1425
2777
|
this.device.commands.disposed = false;
|
|
1426
2778
|
this.device.getCommands().mainId = this.device.mainId ?? '';
|
|
1427
|
-
await this.device.initialize();
|
|
1428
|
-
}
|
|
1429
|
-
|
|
1430
|
-
private async protocolV2CommonUpdateProcess(params: ProtocolV2FileTransferParams) {
|
|
1431
|
-
let lastError: unknown;
|
|
1432
|
-
for (let attempt = 1; attempt <= PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT; attempt += 1) {
|
|
1433
|
-
try {
|
|
1434
|
-
return await this.protocolV2WriteWholeFile(params);
|
|
1435
|
-
} catch (error) {
|
|
1436
|
-
lastError = error;
|
|
1437
|
-
Log.error(
|
|
1438
|
-
`Protocol V2 file transfer failed path=${params.filePath} attempt=${attempt}/${PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT}; restarting from offset 0`,
|
|
1439
|
-
error
|
|
1440
|
-
);
|
|
1441
|
-
if (attempt === PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT) {
|
|
1442
|
-
break;
|
|
1443
|
-
}
|
|
1444
|
-
await this.recoverProtocolV2FileTransfer();
|
|
1445
|
-
}
|
|
1446
|
-
}
|
|
1447
|
-
|
|
1448
|
-
throw ERRORS.TypedError(
|
|
1449
|
-
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
1450
|
-
`transfer data error: ${getProtocolV2UnknownErrorText(lastError)}`
|
|
1451
|
-
);
|
|
1452
2779
|
}
|
|
1453
2780
|
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
let offset = 0;
|
|
1463
|
-
const getUploadProgress = (fileOffset: number) => {
|
|
1464
|
-
if (totalSize !== undefined && processedSize !== undefined) {
|
|
1465
|
-
return Math.min(Math.ceil(((processedSize + fileOffset) / totalSize) * 100), 99);
|
|
1466
|
-
}
|
|
1467
|
-
return Math.min(Math.ceil((fileOffset / payload.byteLength) * 100), 99);
|
|
1468
|
-
};
|
|
1469
|
-
|
|
1470
|
-
while (offset < payload.byteLength) {
|
|
1471
|
-
const chunkEnd = Math.min(offset + chunkSize, payload.byteLength);
|
|
1472
|
-
const chunkLength = chunkEnd - offset;
|
|
1473
|
-
const chunk = payload.slice(offset, chunkEnd);
|
|
1474
|
-
const overwrite = offset === 0;
|
|
1475
|
-
const progress = getProtocolV2DeviceTransferProgress(
|
|
1476
|
-
(processedSize ?? 0) + offset,
|
|
1477
|
-
(processedSize ?? 0) + chunkEnd,
|
|
1478
|
-
totalSize ?? payload.byteLength
|
|
1479
|
-
);
|
|
1480
|
-
|
|
1481
|
-
const writeRes = await this.fileWriteChunk(
|
|
1482
|
-
filePath,
|
|
1483
|
-
payload.byteLength,
|
|
1484
|
-
offset,
|
|
1485
|
-
chunk,
|
|
1486
|
-
overwrite,
|
|
1487
|
-
progress
|
|
1488
|
-
);
|
|
1489
|
-
const processedByte = Number(writeRes.message.processed_byte);
|
|
1490
|
-
const nextOffset =
|
|
1491
|
-
Number.isFinite(processedByte) && processedByte > offset
|
|
1492
|
-
? processedByte
|
|
1493
|
-
: offset + chunkLength;
|
|
1494
|
-
if (nextOffset <= offset || nextOffset > payload.byteLength) {
|
|
1495
|
-
throw ERRORS.TypedError(
|
|
1496
|
-
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
1497
|
-
`invalid processed_byte ${writeRes.message.processed_byte} for offset ${offset}`
|
|
1498
|
-
);
|
|
1499
|
-
}
|
|
1500
|
-
offset = nextOffset;
|
|
1501
|
-
onTransferredBytes?.((processedSize ?? 0) + offset);
|
|
1502
|
-
this.postProgressMessage(getUploadProgress(offset), 'transferData');
|
|
2781
|
+
/**
|
|
2782
|
+
* After acquiring a USB session, polling reuses its command channel. Reacquire only
|
|
2783
|
+
* after reboot or enumeration changes cause hasDeviceAcquire() to become false.
|
|
2784
|
+
*/
|
|
2785
|
+
private async ensureProtocolV2DeviceAcquired() {
|
|
2786
|
+
const commands = this.device.getCommands();
|
|
2787
|
+
if (this.device.hasDeviceAcquire() && !commands.disposed) {
|
|
2788
|
+
return;
|
|
1503
2789
|
}
|
|
1504
|
-
|
|
1505
|
-
|
|
2790
|
+
await this.device.acquire(PROTOCOL_V2_CONNECT_PROTOCOL, {
|
|
2791
|
+
throwOnRunPromiseError: true,
|
|
2792
|
+
});
|
|
1506
2793
|
}
|
|
1507
2794
|
|
|
1508
|
-
private
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
env &&
|
|
1515
|
-
(DataManager.isBrowserWebUsb(env) || DataManager.isDesktopWebUsb(env) || env === 'web')
|
|
1516
|
-
) {
|
|
1517
|
-
return 'WebUSB';
|
|
1518
|
-
}
|
|
1519
|
-
return env ?? 'unknown';
|
|
2795
|
+
private isProtocolV2ReconnectIdentityError(error: unknown) {
|
|
2796
|
+
// A serial can be temporarily unavailable while Loader starts, and BLE has no stable path.
|
|
2797
|
+
// Keep polling unless the device explicitly reports a different serial.
|
|
2798
|
+
return this.normalizeErrorMessage(error).includes(
|
|
2799
|
+
'Protocol V2 reconnect physical identity mismatch'
|
|
2800
|
+
);
|
|
1520
2801
|
}
|
|
1521
2802
|
|
|
1522
2803
|
private async fileWriteChunk(
|
|
@@ -1528,17 +2809,25 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1528
2809
|
progress: number | null
|
|
1529
2810
|
): Promise<TypedResponseMessage<'FilesystemFile'>> {
|
|
1530
2811
|
const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
|
|
1531
|
-
const writeRes = await typedCall(
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
2812
|
+
const writeRes = await typedCall(
|
|
2813
|
+
'FilesystemFileWrite',
|
|
2814
|
+
'FilesystemFile',
|
|
2815
|
+
{
|
|
2816
|
+
file: {
|
|
2817
|
+
path: filePath,
|
|
2818
|
+
offset,
|
|
2819
|
+
total_size: totalFileSize,
|
|
2820
|
+
data: chunk,
|
|
2821
|
+
},
|
|
2822
|
+
overwrite,
|
|
2823
|
+
append: false,
|
|
2824
|
+
ui_percentage: progress ?? undefined,
|
|
1537
2825
|
},
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
2826
|
+
{
|
|
2827
|
+
writeWithResponse: false,
|
|
2828
|
+
onWriteCompleted: () => undefined,
|
|
2829
|
+
}
|
|
2830
|
+
);
|
|
1542
2831
|
if (writeRes.type !== 'FilesystemFile') {
|
|
1543
2832
|
if ((writeRes as any).type === 'CallMethodError') {
|
|
1544
2833
|
if (((writeRes as any).message.error ?? '').indexOf(SESSION_ERROR) > -1) {
|
|
@@ -1554,9 +2843,10 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1554
2843
|
const env = DataManager.getSettings('env');
|
|
1555
2844
|
if (DataManager.isBleConnect(env)) {
|
|
1556
2845
|
await wait(3000);
|
|
1557
|
-
await this.acquireProtocolV2BleDevice();
|
|
1558
|
-
await this.device.initialize();
|
|
1559
2846
|
}
|
|
2847
|
+
await this.reconnectProtocolV2Device();
|
|
2848
|
+
await this.verifyProtocolV2ReconnectIdentity();
|
|
2849
|
+
await this.device.initialize();
|
|
1560
2850
|
await wait(2000);
|
|
1561
2851
|
}
|
|
1562
2852
|
|
|
@@ -1575,35 +2865,16 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1575
2865
|
targets: Array<{ target_id: number; path: string }>;
|
|
1576
2866
|
}) {
|
|
1577
2867
|
const commands = this.device.getCommands();
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
{
|
|
1587
|
-
intermediateTypes: ['DeviceFirmwareUpdateStatus'],
|
|
1588
|
-
timeoutMs: PROTOCOL_V2_START_UPDATE_TIMEOUT,
|
|
1589
|
-
onIntermediateResponse: (response: { type?: string }) => {
|
|
1590
|
-
if (response.type === 'DeviceFirmwareUpdateStatus') {
|
|
1591
|
-
this.postProgressMessage(99, 'installingFirmware');
|
|
1592
|
-
}
|
|
1593
|
-
},
|
|
1594
|
-
}
|
|
1595
|
-
);
|
|
1596
|
-
} catch (error) {
|
|
1597
|
-
if (isProtocolV2StartUpdateTransientError(error)) {
|
|
1598
|
-
Log.log(
|
|
1599
|
-
'Protocol V2 firmware update request did not return; continue status polling',
|
|
1600
|
-
error
|
|
1601
|
-
);
|
|
1602
|
-
} else {
|
|
1603
|
-
throw error;
|
|
1604
|
-
}
|
|
1605
|
-
}
|
|
2868
|
+
const response: ProtocolV2FirmwareUpdateStartResponse = await commands.typedCall(
|
|
2869
|
+
'DeviceFirmwareUpdateRequest',
|
|
2870
|
+
'Success',
|
|
2871
|
+
{ targets },
|
|
2872
|
+
{ timeoutMs: PROTOCOL_V2_START_UPDATE_TIMEOUT }
|
|
2873
|
+
);
|
|
2874
|
+
// Success acknowledges that the device accepted installation. End confirmation
|
|
2875
|
+
// and begin status polling only after receiving this ACK.
|
|
1606
2876
|
this.postTipMessage(FirmwareUpdateTipMessage.FirmwareUpdating);
|
|
2877
|
+
this.postProgressMessage(0, 'installingFirmware');
|
|
1607
2878
|
return response;
|
|
1608
2879
|
}
|
|
1609
2880
|
|
|
@@ -1613,9 +2884,11 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1613
2884
|
const res = await typedCall('DeviceReboot', 'Success', {
|
|
1614
2885
|
reboot_type: rebootType,
|
|
1615
2886
|
});
|
|
2887
|
+
this.device.markProtocolV2Reboot(rebootType);
|
|
1616
2888
|
return res.message;
|
|
1617
2889
|
} catch (error) {
|
|
1618
2890
|
if (isProtocolV2DeviceDisconnectedError(error) || isProtocolV2ReconnectProbeError(error)) {
|
|
2891
|
+
this.device.markProtocolV2Reboot(rebootType);
|
|
1619
2892
|
return { message: 'Device rebooted successfully' };
|
|
1620
2893
|
}
|
|
1621
2894
|
throw error;
|