@onekeyfe/hd-core 1.2.0-alpha.9 → 1.2.0-alpha.91
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 +6670 -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 +1776 -778
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11563 -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 +68 -4
- 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 +2014 -730
- 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 +95 -11
- 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,120 @@ 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
|
+
}, {});
|
|
709
|
+
const localTargetsToUpdate = payload.targetsToUpdate?.map((target: FirmwareUpdateV4Target) =>
|
|
710
|
+
target === 'boot_resources' ? 'resource' : target
|
|
711
|
+
);
|
|
712
|
+
if (
|
|
713
|
+
payload.resourceArchiveBinary &&
|
|
714
|
+
localTargetsToUpdate &&
|
|
715
|
+
!localTargetsToUpdate.includes('resource')
|
|
716
|
+
) {
|
|
717
|
+
localTargetsToUpdate.push('resource');
|
|
718
|
+
}
|
|
719
|
+
// 本地 ZIP 本身就是明确的资源升级请求,不要求调用方重复声明 target。
|
|
720
|
+
const resolvedLocalTargetsToUpdate =
|
|
721
|
+
localTargetsToUpdate ?? (payload.resourceArchiveBinary ? ['resource'] : undefined);
|
|
424
722
|
|
|
425
723
|
this.params = {
|
|
724
|
+
preparedPlan,
|
|
426
725
|
chunkSize: payload.chunkSize,
|
|
427
726
|
forcedUpdateRes: payload.forcedUpdateRes,
|
|
428
727
|
bootloaderBinary: payload.bootloaderBinary,
|
|
@@ -434,19 +733,36 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
434
733
|
se02Binary: payload.se02Binary,
|
|
435
734
|
se03Binary: payload.se03Binary,
|
|
436
735
|
se04Binary: payload.se04Binary,
|
|
437
|
-
|
|
438
|
-
firmwareType: payload.firmwareType,
|
|
736
|
+
resourceArchiveBinary: payload.resourceArchiveBinary,
|
|
737
|
+
firmwareType: preparedPlan?.firmwareType ?? payload.firmwareType,
|
|
738
|
+
targetsToUpdate: preparedPlan
|
|
739
|
+
? ([...preparedPlan.targetsToUpdate] as FirmwareUpdateV4Target[])
|
|
740
|
+
: resolvedLocalTargetsToUpdate,
|
|
741
|
+
expectedTargetVersions: preparedPlan
|
|
742
|
+
? preparedExpectedTargetVersions
|
|
743
|
+
: payload.expectedTargetVersions,
|
|
439
744
|
platform: payload.platform,
|
|
745
|
+
expectedDeviceId: payload.expectedDeviceId,
|
|
746
|
+
artifactReader: hostBinding?.artifactReader ?? payload.artifactReader,
|
|
747
|
+
componentArtifacts: preparedPlan ? undefined : payload.componentArtifacts,
|
|
440
748
|
};
|
|
441
749
|
}
|
|
442
750
|
|
|
443
|
-
private getProtocolV2FirmwareChunkSize() {
|
|
751
|
+
private getProtocolV2FirmwareChunkSize(direction: 'read' | 'write', filePath?: string) {
|
|
444
752
|
const payloadChunkSize = Number(this.params?.chunkSize);
|
|
445
753
|
const env = DataManager.getSettings('env');
|
|
446
|
-
const
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
754
|
+
const isBle = this.params?.platform === 'native' || (env && DataManager.isBleConnect(env));
|
|
755
|
+
let maxChunkSize = PROTOCOL_V2_WEBUSB_FILE_CHUNK_SIZE;
|
|
756
|
+
if (isBle) {
|
|
757
|
+
if (direction === 'read') {
|
|
758
|
+
maxChunkSize = PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE;
|
|
759
|
+
} else {
|
|
760
|
+
// Firmware staging writes tolerate a larger BLE chunk than generic filesystem writes.
|
|
761
|
+
maxChunkSize = PROTOCOL_V2_FIRMWARE_STAGING_PATHS.has(filePath ?? '')
|
|
762
|
+
? PROTOCOL_V2_BLE_FIRMWARE_FILE_CHUNK_SIZE
|
|
763
|
+
: PROTOCOL_V2_BLE_FILE_CHUNK_SIZE;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
450
766
|
if (!Number.isFinite(payloadChunkSize) || payloadChunkSize <= 0) {
|
|
451
767
|
return maxChunkSize;
|
|
452
768
|
}
|
|
@@ -457,21 +773,46 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
457
773
|
}
|
|
458
774
|
|
|
459
775
|
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
776
|
Log.debug('FirmwareUpdateV4 strategy: Protocol V2');
|
|
468
777
|
return this.runProtocolV2();
|
|
469
778
|
}
|
|
470
779
|
|
|
471
780
|
private async runProtocolV2() {
|
|
781
|
+
await this.captureProtocolV2PhysicalIdentity();
|
|
472
782
|
const deviceFeatures = await this.getProtocolV2DeviceFeatures();
|
|
783
|
+
const currentDeviceType = this.device.getCurrentDeviceType();
|
|
784
|
+
const capabilityDeviceType =
|
|
785
|
+
currentDeviceType === EDeviceType.Pro2 || currentDeviceType === EDeviceType.Neo
|
|
786
|
+
? currentDeviceType
|
|
787
|
+
: getDeviceType(deviceFeatures);
|
|
788
|
+
assertProtocolV2FirmwareTargetsSupported(capabilityDeviceType, this.params);
|
|
473
789
|
const deviceFirmwareType = getFirmwareType(deviceFeatures);
|
|
474
790
|
const firmwareType = this.params.firmwareType ?? deviceFirmwareType;
|
|
791
|
+
this.validateExpectedTargetVersions();
|
|
792
|
+
|
|
793
|
+
if (
|
|
794
|
+
!this.params.preparedPlan &&
|
|
795
|
+
this.params.resourceArchiveBinary &&
|
|
796
|
+
this.params.targetsToUpdate?.includes('resource')
|
|
797
|
+
) {
|
|
798
|
+
const localMemoryHost = await this.prepareProtocolV2LocalMemoryHost({
|
|
799
|
+
features: deviceFeatures,
|
|
800
|
+
firmwareType,
|
|
801
|
+
});
|
|
802
|
+
try {
|
|
803
|
+
return await this.runProtocolV2PreparedArtifacts(deviceFeatures, firmwareType);
|
|
804
|
+
} finally {
|
|
805
|
+
localMemoryHost.release();
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
const hasPreparedComponentArtifacts = Object.values(this.params.componentArtifacts ?? {}).some(
|
|
810
|
+
Boolean
|
|
811
|
+
);
|
|
812
|
+
if (this.params.preparedPlan || hasPreparedComponentArtifacts) {
|
|
813
|
+
return this.runProtocolV2PreparedArtifacts(deviceFeatures, firmwareType);
|
|
814
|
+
}
|
|
815
|
+
const wantsResources = !!this.params.targetsToUpdate?.includes('resource');
|
|
475
816
|
|
|
476
817
|
let fwBinaryMap: ProtocolV2TargetBinary[] = [];
|
|
477
818
|
let bootloaderBinary: ArrayBuffer | null = null;
|
|
@@ -480,10 +821,45 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
480
821
|
this.postTipMessage(FirmwareUpdateTipMessage.StartDownloadFirmware);
|
|
481
822
|
fwBinaryMap = this.collectExplicitTargetBinaries();
|
|
482
823
|
bootloaderBinary = this.prepareBootloaderBinary();
|
|
483
|
-
|
|
824
|
+
const explicitInstallItems = this.buildProtocolV2InstallItems({
|
|
825
|
+
bootloaderBinary,
|
|
826
|
+
fwBinaryMap,
|
|
827
|
+
});
|
|
828
|
+
const missingFirmwareTargets = this.getMissingProtocolV2FirmwareTargets(explicitInstallItems);
|
|
829
|
+
const needsRemoteFirmware = this.params.targetsToUpdate?.length
|
|
830
|
+
? missingFirmwareTargets.length > 0
|
|
831
|
+
: explicitInstallItems.length === 0;
|
|
832
|
+
if (wantsResources) {
|
|
833
|
+
throw ERRORS.TypedError(
|
|
834
|
+
HardwareErrorCode.RuntimeError,
|
|
835
|
+
'Protocol V2 resource archive must be provided through a local or external PreparedPlan',
|
|
836
|
+
{
|
|
837
|
+
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
838
|
+
}
|
|
839
|
+
);
|
|
840
|
+
}
|
|
841
|
+
if (
|
|
842
|
+
needsRemoteFirmware &&
|
|
843
|
+
(this.params.artifactReader ||
|
|
844
|
+
DataManager.getSettings('firmwareManifestMode') === 'external-only')
|
|
845
|
+
) {
|
|
846
|
+
throw ERRORS.TypedError(
|
|
847
|
+
HardwareErrorCode.RuntimeError,
|
|
848
|
+
'Protocol V2 firmware artifacts must be prepared by the external firmware host',
|
|
849
|
+
{
|
|
850
|
+
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
851
|
+
}
|
|
852
|
+
);
|
|
853
|
+
}
|
|
854
|
+
if (needsRemoteFirmware) {
|
|
855
|
+
// Remote updates must use a freshly fetched config before any reboot or file write.
|
|
856
|
+
await DataManager.forceReloadData();
|
|
857
|
+
}
|
|
858
|
+
if (needsRemoteFirmware) {
|
|
484
859
|
const remoteBinaries = await this.prepareRemoteProtocolV2Binaries(
|
|
485
860
|
firmwareType,
|
|
486
|
-
deviceFeatures
|
|
861
|
+
deviceFeatures,
|
|
862
|
+
explicitInstallItems
|
|
487
863
|
);
|
|
488
864
|
bootloaderBinary = remoteBinaries.bootloaderBinary;
|
|
489
865
|
fwBinaryMap = remoteBinaries.fwBinaryMap;
|
|
@@ -491,241 +867,844 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
491
867
|
}
|
|
492
868
|
this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
493
869
|
} catch (err) {
|
|
494
|
-
|
|
870
|
+
if (
|
|
871
|
+
typeof err === 'object' &&
|
|
872
|
+
err !== null &&
|
|
873
|
+
'params' in err &&
|
|
874
|
+
(err as HardwareError).params?.firmwareUpdateCode === 'FirmwareArtifactsNotPrepared'
|
|
875
|
+
) {
|
|
876
|
+
throw err;
|
|
877
|
+
}
|
|
878
|
+
if (err instanceof HardwareError && err.errorCode === HardwareErrorCode.NetworkError) {
|
|
879
|
+
throw err;
|
|
880
|
+
}
|
|
881
|
+
throw normalizeFirmwarePreparationError(err);
|
|
495
882
|
}
|
|
496
883
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
if (!bootloaderBinary && fwBinaryMap.length === 0 && !resourceBundles?.length) {
|
|
884
|
+
if (!bootloaderBinary && fwBinaryMap.length === 0 && !installItems?.length) {
|
|
500
885
|
throw ERRORS.TypedError(
|
|
501
886
|
HardwareErrorCode.FirmwareUpdateDownloadFailed,
|
|
502
887
|
'No firmware to update'
|
|
503
888
|
);
|
|
504
889
|
}
|
|
505
890
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
await this.executeProtocolV2Update({
|
|
891
|
+
return this.executeProtocolV2Update({
|
|
509
892
|
fwBinaryMap,
|
|
510
893
|
bootloaderBinary,
|
|
511
894
|
...(installItems ? { installItems } : undefined),
|
|
512
|
-
...(resourceBundles?.length ? { resourceBundles } : undefined),
|
|
513
895
|
});
|
|
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
896
|
}
|
|
523
897
|
|
|
524
|
-
private async
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
898
|
+
private async openProtocolV2PreparedSource(artifact: FirmwareArtifactReference) {
|
|
899
|
+
const source = await openFirmwareByteSource({
|
|
900
|
+
artifact,
|
|
901
|
+
reader: this.params.artifactReader,
|
|
902
|
+
});
|
|
903
|
+
if (!source) {
|
|
904
|
+
throw ERRORS.TypedError(
|
|
905
|
+
HardwareErrorCode.RuntimeError,
|
|
906
|
+
`Firmware artifact ${artifact.artifactRef} is not prepared`,
|
|
907
|
+
{
|
|
908
|
+
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
909
|
+
}
|
|
910
|
+
);
|
|
531
911
|
}
|
|
532
|
-
|
|
912
|
+
this.protocolV2PreparedSources.push(source);
|
|
913
|
+
return source;
|
|
533
914
|
}
|
|
534
915
|
|
|
535
|
-
private
|
|
536
|
-
|
|
916
|
+
private async openProtocolV2MemorySource(binary: ArrayBuffer) {
|
|
917
|
+
const source = await openFirmwareByteSource({ binary });
|
|
918
|
+
if (!source) {
|
|
919
|
+
throw ERRORS.TypedError(
|
|
920
|
+
HardwareErrorCode.RuntimeError,
|
|
921
|
+
'Protocol V2 firmware binary is empty'
|
|
922
|
+
);
|
|
923
|
+
}
|
|
924
|
+
this.protocolV2PreparedSources.push(source);
|
|
925
|
+
return source;
|
|
537
926
|
}
|
|
538
927
|
|
|
539
|
-
private
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
!!this.params.bootloaderBinary ||
|
|
543
|
-
fwBinaryMap.length > 0
|
|
544
|
-
);
|
|
928
|
+
private async closeProtocolV2PreparedSources() {
|
|
929
|
+
const sources = this.protocolV2PreparedSources.splice(0);
|
|
930
|
+
await Promise.all(sources.map(source => source.close().catch(() => undefined)));
|
|
545
931
|
}
|
|
546
932
|
|
|
547
|
-
private
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
933
|
+
private async prepareProtocolV2InstallSources(
|
|
934
|
+
firmwareType: EFirmwareType,
|
|
935
|
+
features: Features
|
|
936
|
+
): Promise<ProtocolV2InstallSource[]> {
|
|
937
|
+
if (this.params.preparedPlan) {
|
|
938
|
+
const requestedTargets = new Set(
|
|
939
|
+
this.params.preparedPlan.targetsToUpdate.filter(
|
|
940
|
+
(target): target is Exclude<FirmwareUpdateV4Target, 'resource'> => target !== 'resource'
|
|
941
|
+
)
|
|
942
|
+
);
|
|
943
|
+
const installSources: ProtocolV2InstallSource[] = [];
|
|
944
|
+
const preparedTargets = new Set<Exclude<FirmwareUpdateV4Target, 'resource'>>();
|
|
945
|
+
for (const artifact of this.params.preparedPlan.artifacts) {
|
|
946
|
+
if (artifact.target !== 'resource') {
|
|
947
|
+
const target = artifact.target as Exclude<FirmwareUpdateV4Target, 'resource'>;
|
|
948
|
+
const installTarget = PROTOCOL_V2_INSTALL_TARGET_BY_UPDATE_TARGET.get(target);
|
|
949
|
+
if (
|
|
950
|
+
!requestedTargets.has(target) ||
|
|
951
|
+
artifact.role !== 'component' ||
|
|
952
|
+
artifact.container !== 'raw' ||
|
|
953
|
+
!installTarget ||
|
|
954
|
+
preparedTargets.has(target)
|
|
955
|
+
) {
|
|
956
|
+
throw ERRORS.TypedError(
|
|
957
|
+
HardwareErrorCode.RuntimeError,
|
|
958
|
+
`Protocol V2 prepared component artifact is invalid: ${artifact.artifactId}`,
|
|
959
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
960
|
+
);
|
|
961
|
+
}
|
|
962
|
+
installSources.push({
|
|
963
|
+
...installTarget,
|
|
964
|
+
source: await this.openProtocolV2PreparedSource(artifact.artifact),
|
|
965
|
+
});
|
|
966
|
+
preparedTargets.add(target);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
const missingTarget = Array.from(requestedTargets).find(
|
|
970
|
+
target => !preparedTargets.has(target)
|
|
971
|
+
);
|
|
972
|
+
if (missingTarget) {
|
|
973
|
+
throw ERRORS.TypedError(
|
|
974
|
+
HardwareErrorCode.RuntimeError,
|
|
975
|
+
`Protocol V2 ${missingTarget} artifact is not prepared`,
|
|
976
|
+
{
|
|
977
|
+
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
978
|
+
artifactName: missingTarget,
|
|
979
|
+
}
|
|
980
|
+
);
|
|
981
|
+
}
|
|
982
|
+
return installSources;
|
|
563
983
|
}
|
|
564
984
|
|
|
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') {
|
|
985
|
+
const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
|
|
986
|
+
if (!release) {
|
|
589
987
|
throw ERRORS.TypedError(
|
|
590
988
|
HardwareErrorCode.RuntimeError,
|
|
591
|
-
|
|
989
|
+
'Protocol V2 firmware release is unavailable'
|
|
592
990
|
);
|
|
593
991
|
}
|
|
594
|
-
const
|
|
595
|
-
|
|
992
|
+
const componentArtifacts = this.params.componentArtifacts ?? {};
|
|
993
|
+
const requestedTargets = new Set(
|
|
994
|
+
this.params.targetsToUpdate ?? (Object.keys(componentArtifacts) as FirmwareUpdateV4Target[])
|
|
995
|
+
);
|
|
996
|
+
const installSources: ProtocolV2InstallSource[] = [];
|
|
997
|
+
const preparedTargets = new Set<FirmwareUpdateV4Target>();
|
|
998
|
+
|
|
999
|
+
for (const [key, component] of this.getRemoteComponentEntries(release)) {
|
|
1000
|
+
const target = this.getRemoteComponentTarget(key, component);
|
|
1001
|
+
const updateTarget = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(target.targetId);
|
|
1002
|
+
if (updateTarget && updateTarget !== 'resource' && requestedTargets.has(updateTarget)) {
|
|
1003
|
+
const artifact = componentArtifacts[updateTarget];
|
|
1004
|
+
if (!artifact) {
|
|
1005
|
+
throw ERRORS.TypedError(
|
|
1006
|
+
HardwareErrorCode.RuntimeError,
|
|
1007
|
+
`Protocol V2 ${updateTarget} artifact is not prepared`,
|
|
1008
|
+
{
|
|
1009
|
+
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
1010
|
+
artifactName: updateTarget,
|
|
1011
|
+
}
|
|
1012
|
+
);
|
|
1013
|
+
}
|
|
1014
|
+
installSources.push({
|
|
1015
|
+
...target,
|
|
1016
|
+
source: await this.openProtocolV2PreparedSource(artifact),
|
|
1017
|
+
});
|
|
1018
|
+
preparedTargets.add(updateTarget);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
const unknownTarget = Array.from(requestedTargets).find(
|
|
1023
|
+
target => target !== 'resource' && !preparedTargets.has(target)
|
|
1024
|
+
);
|
|
1025
|
+
if (unknownTarget) {
|
|
596
1026
|
throw ERRORS.TypedError(
|
|
597
1027
|
HardwareErrorCode.RuntimeError,
|
|
598
|
-
`
|
|
1028
|
+
`Protocol V2 release does not contain requested target ${unknownTarget}`
|
|
599
1029
|
);
|
|
600
1030
|
}
|
|
601
|
-
return
|
|
1031
|
+
return installSources;
|
|
602
1032
|
}
|
|
603
1033
|
|
|
604
|
-
private
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
1034
|
+
private async prepareProtocolV2LocalMemoryHost({
|
|
1035
|
+
features,
|
|
1036
|
+
firmwareType,
|
|
1037
|
+
}: {
|
|
1038
|
+
features: Features;
|
|
1039
|
+
firmwareType: EFirmwareType;
|
|
1040
|
+
}): Promise<FirmwareUpdateV4MemoryHost> {
|
|
1041
|
+
const installItems = this.buildProtocolV2InstallItems({
|
|
1042
|
+
bootloaderBinary: this.prepareBootloaderBinary(),
|
|
1043
|
+
fwBinaryMap: this.collectExplicitTargetBinaries(),
|
|
1044
|
+
});
|
|
1045
|
+
const requestedComponentTargets = new Set(
|
|
1046
|
+
(this.params.targetsToUpdate ?? []).filter(
|
|
1047
|
+
(target): target is Exclude<FirmwareUpdateV4Target, 'resource' | 'boot_resources'> =>
|
|
1048
|
+
target !== 'resource' && target !== 'boot_resources'
|
|
1049
|
+
)
|
|
1050
|
+
);
|
|
1051
|
+
const localComponentTargets = new Set(
|
|
1052
|
+
installItems.flatMap(item => {
|
|
1053
|
+
const target = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(item.targetId);
|
|
1054
|
+
return target ? [target] : [];
|
|
1055
|
+
})
|
|
1056
|
+
);
|
|
1057
|
+
const missingTarget = Array.from(requestedComponentTargets).find(
|
|
1058
|
+
target => !localComponentTargets.has(target)
|
|
1059
|
+
);
|
|
1060
|
+
if (missingTarget) {
|
|
1061
|
+
throw ERRORS.TypedError(
|
|
1062
|
+
HardwareErrorCode.RuntimeError,
|
|
1063
|
+
`Protocol V2 local update has no binary for requested target ${missingTarget}`,
|
|
1064
|
+
{
|
|
1065
|
+
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
1066
|
+
artifactName: missingTarget,
|
|
1067
|
+
}
|
|
1068
|
+
);
|
|
612
1069
|
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
1070
|
+
|
|
1071
|
+
const planArtifacts: Parameters<typeof buildProtocolV2LocalFirmwareUpdatePlan>[0]['artifacts'] =
|
|
1072
|
+
[];
|
|
1073
|
+
const memoryArtifacts: FirmwareMemoryArtifact[] = [];
|
|
1074
|
+
for (const item of installItems) {
|
|
1075
|
+
const target = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(item.targetId);
|
|
1076
|
+
if (!target || item.binary.byteLength <= 0) {
|
|
1077
|
+
throw ERRORS.TypedError(
|
|
1078
|
+
HardwareErrorCode.RuntimeError,
|
|
1079
|
+
`Protocol V2 local firmware artifact is invalid: ${item.fileName}`,
|
|
1080
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1081
|
+
);
|
|
1082
|
+
}
|
|
1083
|
+
const artifactId = `component:${target}`;
|
|
1084
|
+
planArtifacts.push({
|
|
1085
|
+
artifactId,
|
|
1086
|
+
target,
|
|
1087
|
+
container: 'raw',
|
|
1088
|
+
logicalName: item.fileName,
|
|
1089
|
+
expectedSize: item.binary.byteLength,
|
|
1090
|
+
expectedSha256: bytesToHex(sha256(new Uint8Array(item.binary))),
|
|
1091
|
+
...(this.params.expectedTargetVersions?.[target]
|
|
1092
|
+
? { targetVersion: this.params.expectedTargetVersions[target] }
|
|
1093
|
+
: {}),
|
|
1094
|
+
});
|
|
1095
|
+
memoryArtifacts.push({ artifactId, binary: item.binary });
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
const resourceArchive = await this.prepareProtocolV2LocalResourceArchive(
|
|
1099
|
+
this.params.resourceArchiveBinary as ArrayBuffer
|
|
1100
|
+
);
|
|
1101
|
+
const resourceArtifactId = 'resource:archive';
|
|
1102
|
+
planArtifacts.push({
|
|
1103
|
+
artifactId: resourceArtifactId,
|
|
1104
|
+
target: 'resource',
|
|
1105
|
+
container: 'zip',
|
|
1106
|
+
logicalName: 'protocol-v2-local-resource-archive',
|
|
1107
|
+
expectedSize: resourceArchive.binary.byteLength,
|
|
1108
|
+
expectedSha256: bytesToHex(sha256(new Uint8Array(resourceArchive.binary))),
|
|
1109
|
+
});
|
|
1110
|
+
memoryArtifacts.push({
|
|
1111
|
+
artifactId: resourceArtifactId,
|
|
1112
|
+
binary: resourceArchive.binary,
|
|
1113
|
+
materializedEntries: resourceArchive.materializedEntries,
|
|
1114
|
+
});
|
|
1115
|
+
|
|
1116
|
+
const plan = buildProtocolV2LocalFirmwareUpdatePlan({
|
|
1117
|
+
features,
|
|
1118
|
+
firmwareType,
|
|
1119
|
+
platform: this.params.platform,
|
|
1120
|
+
artifacts: planArtifacts,
|
|
1121
|
+
});
|
|
1122
|
+
let memoryHost: FirmwareUpdateV4MemoryHost | undefined;
|
|
1123
|
+
try {
|
|
1124
|
+
memoryHost = prepareFirmwareUpdateV4MemoryHost({
|
|
1125
|
+
sdk: {
|
|
1126
|
+
prepareFirmwareUpdatePlan,
|
|
1127
|
+
registerFirmwareUpdateHostBinding,
|
|
1128
|
+
unregisterFirmwareUpdateHostBinding,
|
|
1129
|
+
},
|
|
1130
|
+
plan,
|
|
1131
|
+
artifacts: memoryArtifacts,
|
|
1132
|
+
});
|
|
1133
|
+
const preparedPlan = validateFirmwareUpdatePreparedPlan(memoryHost.preparedPlan);
|
|
1134
|
+
assertFirmwareUpdatePreparedPlanBinding({
|
|
1135
|
+
preparedPlan,
|
|
1136
|
+
executor: 'v4',
|
|
1137
|
+
platform: this.params.platform,
|
|
1138
|
+
scopeTargets: [],
|
|
1139
|
+
bindings: [],
|
|
1140
|
+
});
|
|
1141
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
1142
|
+
preparedPlan,
|
|
1143
|
+
deviceIdentity: this.protocolV2ExpectedSerialNumber,
|
|
1144
|
+
});
|
|
1145
|
+
const hostBinding = resolveFirmwareUpdateHostBinding(
|
|
1146
|
+
memoryHost.hostBindingGeneration,
|
|
1147
|
+
preparedPlan.preparedPlanDigest
|
|
1148
|
+
);
|
|
1149
|
+
this.params.preparedPlan = preparedPlan;
|
|
1150
|
+
this.params.targetsToUpdate = [...preparedPlan.targetsToUpdate] as FirmwareUpdateV4Target[];
|
|
1151
|
+
this.params.artifactReader = hostBinding.artifactReader;
|
|
1152
|
+
this.params.componentArtifacts = undefined;
|
|
1153
|
+
return memoryHost;
|
|
1154
|
+
} catch (error) {
|
|
1155
|
+
memoryHost?.release();
|
|
1156
|
+
throw error;
|
|
618
1157
|
}
|
|
619
|
-
return undefined;
|
|
620
1158
|
}
|
|
621
1159
|
|
|
622
|
-
private
|
|
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
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
const
|
|
654
|
-
|
|
1160
|
+
private async prepareProtocolV2LocalResourceArchive(
|
|
1161
|
+
binary: ArrayBuffer
|
|
1162
|
+
): Promise<ProtocolV2LocalResourceArchive> {
|
|
1163
|
+
if (binary.byteLength <= 0 || binary.byteLength > PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES) {
|
|
1164
|
+
throw ERRORS.TypedError(
|
|
1165
|
+
HardwareErrorCode.RuntimeError,
|
|
1166
|
+
'Protocol V2 local resource ZIP archive size is invalid',
|
|
1167
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1168
|
+
);
|
|
1169
|
+
}
|
|
1170
|
+
const zip = await JSZip.loadAsync(binary);
|
|
1171
|
+
const zipEntries = Object.values(zip.files);
|
|
1172
|
+
if (
|
|
1173
|
+
zipEntries.some(entry => entry.unsafeOriginalName && entry.unsafeOriginalName !== entry.name)
|
|
1174
|
+
) {
|
|
1175
|
+
throw ERRORS.TypedError(
|
|
1176
|
+
HardwareErrorCode.RuntimeError,
|
|
1177
|
+
'Protocol V2 local resource ZIP contains an unsafe entry path',
|
|
1178
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1179
|
+
);
|
|
1180
|
+
}
|
|
1181
|
+
const entries = zipEntries.filter(entry => !entry.dir);
|
|
1182
|
+
if (entries.length === 0 || entries.length > PROTOCOL_V2_RESOURCE_FILE_MAX_COUNT + 1) {
|
|
1183
|
+
throw ERRORS.TypedError(
|
|
1184
|
+
HardwareErrorCode.RuntimeError,
|
|
1185
|
+
'Protocol V2 local resource ZIP entry set is invalid',
|
|
1186
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1187
|
+
);
|
|
1188
|
+
}
|
|
1189
|
+
let declaredUncompressedSize = 0;
|
|
1190
|
+
let declaredCompressedSize = 0;
|
|
1191
|
+
for (const entry of entries) {
|
|
1192
|
+
const sizes = getProtocolV2ZipEntrySizes(entry);
|
|
1193
|
+
declaredCompressedSize += sizes.compressedSize;
|
|
1194
|
+
declaredUncompressedSize += sizes.uncompressedSize;
|
|
1195
|
+
const entryLimit =
|
|
1196
|
+
entry.name === 'manifest.json'
|
|
1197
|
+
? PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES
|
|
1198
|
+
: PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES;
|
|
1199
|
+
if (
|
|
1200
|
+
sizes.uncompressedSize > entryLimit ||
|
|
1201
|
+
declaredCompressedSize > binary.byteLength ||
|
|
1202
|
+
declaredUncompressedSize >
|
|
1203
|
+
PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES + PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES
|
|
1204
|
+
) {
|
|
1205
|
+
throw ERRORS.TypedError(
|
|
1206
|
+
HardwareErrorCode.RuntimeError,
|
|
1207
|
+
'Protocol V2 local resource ZIP declared size exceeds the allowed limit',
|
|
1208
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1209
|
+
);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
const manifestEntry = zip.file('manifest.json');
|
|
1213
|
+
if (!manifestEntry) {
|
|
1214
|
+
throw ERRORS.TypedError(
|
|
1215
|
+
HardwareErrorCode.RuntimeError,
|
|
1216
|
+
'Protocol V2 local resource ZIP has no manifest.json',
|
|
1217
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1218
|
+
);
|
|
1219
|
+
}
|
|
1220
|
+
const manifestBinary = await manifestEntry.async('arraybuffer');
|
|
1221
|
+
if (
|
|
1222
|
+
manifestBinary.byteLength <= 0 ||
|
|
1223
|
+
manifestBinary.byteLength > PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES
|
|
1224
|
+
) {
|
|
1225
|
+
throw ERRORS.TypedError(
|
|
1226
|
+
HardwareErrorCode.RuntimeError,
|
|
1227
|
+
'Protocol V2 local resource manifest size is invalid',
|
|
1228
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1229
|
+
);
|
|
1230
|
+
}
|
|
655
1231
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
1232
|
+
let manifestValue: unknown;
|
|
1233
|
+
try {
|
|
1234
|
+
manifestValue = JSON.parse(new TextDecoder().decode(manifestBinary));
|
|
1235
|
+
} catch (error) {
|
|
1236
|
+
throw ERRORS.TypedError(
|
|
1237
|
+
HardwareErrorCode.RuntimeError,
|
|
1238
|
+
`Protocol V2 local resource manifest is invalid: ${String(error)}`,
|
|
1239
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1240
|
+
);
|
|
1241
|
+
}
|
|
1242
|
+
const manifest = parseProtocolV2ResourceManifest(manifestValue);
|
|
1243
|
+
const selectedFiles = selectProtocolV2ResourceManifestFiles({
|
|
1244
|
+
manifest,
|
|
1245
|
+
targetsToUpdate: this.params.targetsToUpdate ?? [],
|
|
1246
|
+
});
|
|
1247
|
+
const expectedEntryNames = new Set([
|
|
1248
|
+
'manifest.json',
|
|
1249
|
+
...selectedFiles.map(file => file.archive_path),
|
|
1250
|
+
]);
|
|
1251
|
+
if (
|
|
1252
|
+
entries.length !== expectedEntryNames.size ||
|
|
1253
|
+
entries.some(entry => !expectedEntryNames.has(entry.name))
|
|
1254
|
+
) {
|
|
1255
|
+
throw ERRORS.TypedError(
|
|
1256
|
+
HardwareErrorCode.RuntimeError,
|
|
1257
|
+
'Protocol V2 local resource ZIP contains an unexpected entry',
|
|
1258
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1259
|
+
);
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
let totalSize = 0;
|
|
1263
|
+
const materializedEntries: FirmwareMemoryArtifactEntry[] = [
|
|
1264
|
+
{ entryName: 'manifest.json', binary: manifestBinary },
|
|
1265
|
+
];
|
|
1266
|
+
for (const file of selectedFiles) {
|
|
1267
|
+
const entry = zip.file(file.archive_path);
|
|
1268
|
+
if (!entry) {
|
|
1269
|
+
throw ERRORS.TypedError(
|
|
1270
|
+
HardwareErrorCode.RuntimeError,
|
|
1271
|
+
`Protocol V2 local resource ZIP is missing ${file.archive_path}`,
|
|
1272
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1273
|
+
);
|
|
1274
|
+
}
|
|
1275
|
+
const { uncompressedSize } = getProtocolV2ZipEntrySizes(entry);
|
|
1276
|
+
totalSize += uncompressedSize;
|
|
1277
|
+
if (uncompressedSize !== file.size || totalSize > PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES) {
|
|
1278
|
+
throw ERRORS.TypedError(
|
|
1279
|
+
HardwareErrorCode.RuntimeError,
|
|
1280
|
+
`Protocol V2 local resource file declared size is invalid: ${file.archive_path}`,
|
|
1281
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1282
|
+
);
|
|
1283
|
+
}
|
|
1284
|
+
const fileBinary = await entry.async('arraybuffer');
|
|
1285
|
+
const digest = bytesToHex(sha256(new Uint8Array(fileBinary)));
|
|
1286
|
+
if (fileBinary.byteLength !== file.size || digest !== file.sha256.toLowerCase()) {
|
|
1287
|
+
throw ERRORS.TypedError(
|
|
1288
|
+
HardwareErrorCode.RuntimeError,
|
|
1289
|
+
`Protocol V2 local resource file does not match manifest: ${file.archive_path}`,
|
|
1290
|
+
{ firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch' }
|
|
1291
|
+
);
|
|
1292
|
+
}
|
|
1293
|
+
materializedEntries.push({ entryName: file.archive_path, binary: fileBinary });
|
|
1294
|
+
}
|
|
1295
|
+
return { binary, materializedEntries };
|
|
659
1296
|
}
|
|
660
1297
|
|
|
661
|
-
private
|
|
662
|
-
|
|
663
|
-
if (
|
|
664
|
-
|
|
1298
|
+
private async prepareProtocolV2ResourceSources(): Promise<ProtocolV2ResourceBundleSource[]> {
|
|
1299
|
+
const resourceRequested = this.params.targetsToUpdate?.includes('resource') ?? false;
|
|
1300
|
+
if (!resourceRequested) {
|
|
1301
|
+
return [];
|
|
1302
|
+
}
|
|
1303
|
+
const archiveSources = await this.prepareProtocolV2ResourceArchiveSources();
|
|
1304
|
+
if (archiveSources) {
|
|
1305
|
+
return archiveSources;
|
|
1306
|
+
}
|
|
1307
|
+
throw ERRORS.TypedError(
|
|
1308
|
+
HardwareErrorCode.RuntimeError,
|
|
1309
|
+
'Protocol V2 resource archive is not prepared',
|
|
1310
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1311
|
+
);
|
|
665
1312
|
}
|
|
666
1313
|
|
|
667
|
-
private async
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
const
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
1314
|
+
private async prepareProtocolV2ResourceArchiveSources(): Promise<
|
|
1315
|
+
ProtocolV2ResourceBundleSource[] | undefined
|
|
1316
|
+
> {
|
|
1317
|
+
const archiveArtifacts =
|
|
1318
|
+
this.params.preparedPlan?.artifacts.filter(
|
|
1319
|
+
artifact =>
|
|
1320
|
+
artifact.role === 'resourceBundle' &&
|
|
1321
|
+
artifact.target === 'resource' &&
|
|
1322
|
+
artifact.container === 'zip'
|
|
1323
|
+
) ?? [];
|
|
1324
|
+
if (archiveArtifacts.length === 0) {
|
|
1325
|
+
return undefined;
|
|
1326
|
+
}
|
|
1327
|
+
if (archiveArtifacts.length !== 1) {
|
|
1328
|
+
throw ERRORS.TypedError(
|
|
1329
|
+
HardwareErrorCode.RuntimeError,
|
|
1330
|
+
'Protocol V2 prepared plan must contain exactly one resource archive',
|
|
1331
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1332
|
+
);
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
const entries = archiveArtifacts[0].materializedEntries ?? [];
|
|
1336
|
+
const manifestEntry = entries.find(entry => entry.entryName === 'manifest.json');
|
|
674
1337
|
if (
|
|
675
|
-
!
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
fileSize < PROTOCOL_V2_OKPP_HEADER_SIZE
|
|
1338
|
+
!manifestEntry ||
|
|
1339
|
+
manifestEntry.artifact.size <= 0 ||
|
|
1340
|
+
manifestEntry.artifact.size > PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES
|
|
679
1341
|
) {
|
|
680
|
-
|
|
1342
|
+
throw ERRORS.TypedError(
|
|
1343
|
+
HardwareErrorCode.RuntimeError,
|
|
1344
|
+
'Protocol V2 prepared resource archive has no valid manifest.json',
|
|
1345
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1346
|
+
);
|
|
681
1347
|
}
|
|
682
1348
|
|
|
683
|
-
const
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
|
|
1349
|
+
const manifestSource = await this.openProtocolV2PreparedSource(manifestEntry.artifact);
|
|
1350
|
+
let manifestValue: unknown;
|
|
1351
|
+
try {
|
|
1352
|
+
manifestValue = JSON.parse(
|
|
1353
|
+
new TextDecoder().decode(await readFirmwareByteSourceFully(manifestSource))
|
|
1354
|
+
);
|
|
1355
|
+
} catch (error) {
|
|
1356
|
+
throw ERRORS.TypedError(
|
|
1357
|
+
HardwareErrorCode.RuntimeError,
|
|
1358
|
+
`Protocol V2 prepared resource manifest is invalid: ${String(error)}`,
|
|
1359
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1360
|
+
);
|
|
1361
|
+
}
|
|
1362
|
+
const manifest = parseProtocolV2ResourceManifest(manifestValue);
|
|
1363
|
+
const selectedFiles = selectProtocolV2ResourceManifestFiles({
|
|
1364
|
+
manifest,
|
|
1365
|
+
targetsToUpdate: this.params.targetsToUpdate ?? [],
|
|
1366
|
+
});
|
|
1367
|
+
if (selectedFiles.length > PROTOCOL_V2_RESOURCE_FILE_MAX_COUNT) {
|
|
1368
|
+
throw ERRORS.TypedError(
|
|
1369
|
+
HardwareErrorCode.RuntimeError,
|
|
1370
|
+
'Protocol V2 prepared resource archive contains too many files',
|
|
1371
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1372
|
+
);
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
const entriesByName = new Map(entries.map(entry => [entry.entryName, entry] as const));
|
|
1376
|
+
const expectedEntryNames = new Set([
|
|
1377
|
+
'manifest.json',
|
|
1378
|
+
...selectedFiles.map(file => file.archive_path),
|
|
1379
|
+
]);
|
|
1380
|
+
if (entries.some(entry => !expectedEntryNames.has(entry.entryName))) {
|
|
1381
|
+
throw ERRORS.TypedError(
|
|
1382
|
+
HardwareErrorCode.RuntimeError,
|
|
1383
|
+
'Protocol V2 prepared resource archive contains an unexpected entry',
|
|
1384
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1385
|
+
);
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
let totalSize = 0;
|
|
1389
|
+
const sources: ProtocolV2ResourceBundleSource[] = [];
|
|
1390
|
+
for (const [index, file] of selectedFiles.entries()) {
|
|
1391
|
+
const entry = entriesByName.get(file.archive_path);
|
|
1392
|
+
if (
|
|
1393
|
+
!entry ||
|
|
1394
|
+
entry.artifact.size !== file.size ||
|
|
1395
|
+
entry.artifact.sha256.toLowerCase() !== file.sha256.toLowerCase()
|
|
1396
|
+
) {
|
|
1397
|
+
throw ERRORS.TypedError(
|
|
1398
|
+
HardwareErrorCode.RuntimeError,
|
|
1399
|
+
`Protocol V2 prepared resource file does not match manifest: ${file.archive_path}`,
|
|
1400
|
+
{ firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch' }
|
|
1401
|
+
);
|
|
1402
|
+
}
|
|
1403
|
+
totalSize += entry.artifact.size;
|
|
1404
|
+
if (totalSize > PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES) {
|
|
1405
|
+
throw ERRORS.TypedError(
|
|
1406
|
+
HardwareErrorCode.RuntimeError,
|
|
1407
|
+
'Protocol V2 prepared resource archive exceeds the total size limit',
|
|
1408
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1409
|
+
);
|
|
1410
|
+
}
|
|
1411
|
+
const source = await this.openProtocolV2PreparedSource(entry.artifact);
|
|
1412
|
+
const header =
|
|
1413
|
+
source.size >= PROTOCOL_V2_OKPP_HEADER_SIZE
|
|
1414
|
+
? parseProtocolV2OkppHeader(
|
|
1415
|
+
new Uint8Array(await source.readAt(0, PROTOCOL_V2_OKPP_HEADER_SIZE))
|
|
1416
|
+
)
|
|
1417
|
+
: null;
|
|
1418
|
+
sources.push({
|
|
1419
|
+
name: file.original_name || `resource-${index}`,
|
|
1420
|
+
source,
|
|
1421
|
+
devicePath: file.device_path,
|
|
1422
|
+
...(header
|
|
1423
|
+
? {
|
|
1424
|
+
version: header.version,
|
|
1425
|
+
payloadHash: header.payloadHash,
|
|
1426
|
+
headerHash: header.headerHash,
|
|
1427
|
+
}
|
|
1428
|
+
: {}),
|
|
696
1429
|
});
|
|
697
|
-
const data = toProtocolV2Bytes(res.message?.data);
|
|
698
|
-
if (data.byteLength === 0) return null;
|
|
699
|
-
chunks.push(data);
|
|
700
|
-
offset += data.byteLength;
|
|
701
1430
|
}
|
|
1431
|
+
return sources;
|
|
1432
|
+
}
|
|
702
1433
|
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
1434
|
+
private async runProtocolV2PreparedArtifacts(features: Features, firmwareType: EFirmwareType) {
|
|
1435
|
+
try {
|
|
1436
|
+
this.postTipMessage(FirmwareUpdateTipMessage.StartDownloadFirmware);
|
|
1437
|
+
const installSources = await this.prepareProtocolV2InstallSources(firmwareType, features);
|
|
1438
|
+
const resourceSources = await this.prepareProtocolV2ResourceSources();
|
|
1439
|
+
if (installSources.length === 0 && resourceSources.length === 0) {
|
|
1440
|
+
throw ERRORS.TypedError(
|
|
1441
|
+
HardwareErrorCode.FirmwareUpdateDownloadFailed,
|
|
1442
|
+
'No firmware to update'
|
|
1443
|
+
);
|
|
1444
|
+
}
|
|
1445
|
+
this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
1446
|
+
|
|
1447
|
+
return await this.executeProtocolV2SourceUpdate({
|
|
1448
|
+
installSources,
|
|
1449
|
+
resourceSources,
|
|
1450
|
+
});
|
|
1451
|
+
} finally {
|
|
1452
|
+
await this.closeProtocolV2PreparedSources();
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
private validateExpectedTargetVersions() {
|
|
1457
|
+
const expected = this.params.expectedTargetVersions;
|
|
1458
|
+
if (expected === undefined) return;
|
|
1459
|
+
if (typeof expected !== 'object' || expected === null || Array.isArray(expected)) {
|
|
1460
|
+
throw ERRORS.TypedError(
|
|
1461
|
+
HardwareErrorCode.RuntimeError,
|
|
1462
|
+
'Protocol V2 expected target versions are invalid'
|
|
1463
|
+
);
|
|
1464
|
+
}
|
|
1465
|
+
for (const [target, version] of Object.entries(expected)) {
|
|
1466
|
+
if (
|
|
1467
|
+
!Array.from(PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.values()).includes(
|
|
1468
|
+
target as Exclude<FirmwareUpdateV4Target, 'boot_resources'>
|
|
1469
|
+
) ||
|
|
1470
|
+
typeof version !== 'string' ||
|
|
1471
|
+
!/^\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.-]+)?$/u.test(version) ||
|
|
1472
|
+
version.length > 64
|
|
1473
|
+
) {
|
|
1474
|
+
throw ERRORS.TypedError(
|
|
1475
|
+
HardwareErrorCode.RuntimeError,
|
|
1476
|
+
'Protocol V2 expected target version is invalid'
|
|
1477
|
+
);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
private getExpectedProtocolV2TargetVersion(targetId: number) {
|
|
1483
|
+
const target = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(targetId);
|
|
1484
|
+
const version = target ? this.params?.expectedTargetVersions?.[target] : undefined;
|
|
1485
|
+
if (!version) return undefined;
|
|
1486
|
+
const parts = version.split(/[+-]/u, 1)[0].split('.').map(Number);
|
|
1487
|
+
if (
|
|
1488
|
+
parts.length !== 3 ||
|
|
1489
|
+
parts.some(part => !Number.isSafeInteger(part) || part < 0 || part > 0xff)
|
|
1490
|
+
) {
|
|
1491
|
+
return undefined;
|
|
1492
|
+
}
|
|
1493
|
+
return parts[0] * 0x10000 + parts[1] * 0x100 + parts[2];
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
private assertExpectedProtocolV2Versions(targets?: readonly FirmwareUpdateV4Target[]) {
|
|
1497
|
+
const expected = this.params?.expectedTargetVersions;
|
|
1498
|
+
if (!expected) return;
|
|
1499
|
+
const features = this.protocolV2LatestFinalFeatures;
|
|
1500
|
+
const requestedTargets = this.getProtocolV2RequestedTargets();
|
|
1501
|
+
const applicationTargets = requestedTargets.filter(
|
|
1502
|
+
target => target === 'app_v1' || target === 'app_v2'
|
|
1503
|
+
);
|
|
1504
|
+
const visibleVersions: Partial<Record<FirmwareUpdateV4Target, string>> = {
|
|
1505
|
+
boot: features ? getDeviceBootloaderVersion(features).join('.') : undefined,
|
|
1506
|
+
coprocessor: features ? getDeviceBLEFirmwareVersion(features).join('.') : undefined,
|
|
1507
|
+
se01: features?.se01Version ?? undefined,
|
|
1508
|
+
se02: features?.se02Version ?? undefined,
|
|
1509
|
+
se03: features?.se03Version ?? undefined,
|
|
1510
|
+
se04: features?.se04Version ?? undefined,
|
|
1511
|
+
};
|
|
1512
|
+
if (features && applicationTargets.length === 1) {
|
|
1513
|
+
visibleVersions[applicationTargets[0]] = getDeviceFirmwareVersion(features).join('.');
|
|
1514
|
+
}
|
|
1515
|
+
const expectedEntries = (
|
|
1516
|
+
Object.entries(expected) as Array<[FirmwareUpdateV4Target, string]>
|
|
1517
|
+
).filter(([target]) => !targets || targets.includes(target));
|
|
1518
|
+
for (const [target, expectedVersion] of expectedEntries) {
|
|
1519
|
+
const targetId = Array.from(PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.entries()).find(
|
|
1520
|
+
([, mappedTarget]) => mappedTarget === target
|
|
1521
|
+
)?.[0];
|
|
1522
|
+
const statusVersion =
|
|
1523
|
+
targetId === undefined ? undefined : this.protocolV2CompletedTargetVersions.get(targetId);
|
|
1524
|
+
const observedVersion =
|
|
1525
|
+
statusVersion === undefined
|
|
1526
|
+
? visibleVersions[target]
|
|
1527
|
+
: `${Math.floor(statusVersion / 0x10000) % 0x100}.${
|
|
1528
|
+
Math.floor(statusVersion / 0x100) % 0x100
|
|
1529
|
+
}.${statusVersion % 0x100}`;
|
|
1530
|
+
if (!observedVersion) {
|
|
1531
|
+
if (this.protocolV2FinalStatusVerified) {
|
|
1532
|
+
Log.warn(
|
|
1533
|
+
`Protocol V2 target ${target} has no observable final version; completed target status is authoritative`
|
|
1534
|
+
);
|
|
1535
|
+
} else {
|
|
1536
|
+
throw ERRORS.TypedError(
|
|
1537
|
+
HardwareErrorCode.FirmwareVerificationFailed,
|
|
1538
|
+
`Protocol V2 target ${target} has no observable final version after status fallback`
|
|
1539
|
+
);
|
|
1540
|
+
}
|
|
1541
|
+
} else if (observedVersion !== expectedVersion) {
|
|
1542
|
+
throw ERRORS.TypedError(
|
|
1543
|
+
HardwareErrorCode.FirmwareVerificationFailed,
|
|
1544
|
+
`Protocol V2 target ${target} reached ${observedVersion}, expected ${expectedVersion}`
|
|
1545
|
+
);
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
private getProtocolV2RequestedTargets(): FirmwareUpdateV4Target[] {
|
|
1551
|
+
if (this.params.targetsToUpdate?.length) {
|
|
1552
|
+
return [...new Set(this.params.targetsToUpdate)];
|
|
1553
|
+
}
|
|
1554
|
+
if (this.params.preparedPlan?.targetsToUpdate.length) {
|
|
1555
|
+
return [...new Set(this.params.preparedPlan.targetsToUpdate)] as FirmwareUpdateV4Target[];
|
|
1556
|
+
}
|
|
1557
|
+
const targets = new Set<FirmwareUpdateV4Target>();
|
|
1558
|
+
Object.keys(this.params.componentArtifacts ?? {}).forEach(target =>
|
|
1559
|
+
targets.add(target as FirmwareUpdateV4Target)
|
|
1560
|
+
);
|
|
1561
|
+
if (this.params.bootloaderBinary) targets.add('boot');
|
|
1562
|
+
if (this.params.applicationP1Binary) targets.add('app_v1');
|
|
1563
|
+
if (this.params.applicationP2Binary) targets.add('app_v2');
|
|
1564
|
+
if (this.params.coprocessorBinary) targets.add('coprocessor');
|
|
1565
|
+
if (this.params.se01Binary) targets.add('se01');
|
|
1566
|
+
if (this.params.se02Binary) targets.add('se02');
|
|
1567
|
+
if (this.params.se03Binary) targets.add('se03');
|
|
1568
|
+
if (this.params.se04Binary) targets.add('se04');
|
|
1569
|
+
return Array.from(targets);
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
private async getProtocolV2DeviceFeatures(): Promise<Features> {
|
|
1573
|
+
if (this.device.features) {
|
|
1574
|
+
return this.device.features;
|
|
1575
|
+
}
|
|
1576
|
+
if (typeof this.device.getFeatures === 'function') {
|
|
1577
|
+
const features = await this.device.getFeatures();
|
|
1578
|
+
if (features) return features;
|
|
1579
|
+
}
|
|
1580
|
+
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'Device features not available');
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
private getProtocolV2SerialNumber(deviceInfo: ProtocolV2DeviceInfo) {
|
|
1584
|
+
const serialNumber = deviceInfo.hw?.serial_no?.trim();
|
|
1585
|
+
return serialNumber || undefined;
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
private assertProtocolV2DeviceInfoIdentity(deviceInfo: ProtocolV2DeviceInfo) {
|
|
1589
|
+
assertProtocolV2ReconnectIdentity(
|
|
1590
|
+
this.protocolV2ExpectedSerialNumber,
|
|
1591
|
+
this.getProtocolV2SerialNumber(deviceInfo),
|
|
1592
|
+
this.protocolV2ExpectedPath,
|
|
1593
|
+
this.device.originalDescriptor.path
|
|
1594
|
+
);
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
private getProtocolV2DeviceInfoTimeout() {
|
|
1598
|
+
return this.isBleReconnect()
|
|
1599
|
+
? this.payload.protocolV2DeviceInfoTimeoutMs ?? PROTOCOL_V2_DEVICE_INFO_READY_TIMEOUT
|
|
1600
|
+
: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT;
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
private async requestProtocolV2PhysicalIdentity() {
|
|
1604
|
+
return requestProtocolV2DeviceInfo({
|
|
1605
|
+
commands: this.device.getCommands(),
|
|
1606
|
+
timeoutMs: this.getProtocolV2DeviceInfoTimeout(),
|
|
708
1607
|
});
|
|
709
|
-
return parseProtocolV2OkppHeader(headerBytes);
|
|
710
1608
|
}
|
|
711
1609
|
|
|
712
|
-
private
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
1610
|
+
private async captureProtocolV2PhysicalIdentity() {
|
|
1611
|
+
const deviceInfo = await this.requestProtocolV2PhysicalIdentity();
|
|
1612
|
+
const serialNumber = this.getProtocolV2SerialNumber(deviceInfo);
|
|
1613
|
+
const path = this.device.originalDescriptor?.path?.trim() || undefined;
|
|
1614
|
+
if (this.params?.preparedPlan) {
|
|
1615
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
1616
|
+
preparedPlan: this.params.preparedPlan,
|
|
1617
|
+
deviceIdentity: serialNumber,
|
|
1618
|
+
});
|
|
1619
|
+
} else if (this.params?.expectedDeviceId) {
|
|
1620
|
+
assertProtocolV2ReconnectIdentity(this.params?.expectedDeviceId, serialNumber);
|
|
1621
|
+
} else {
|
|
1622
|
+
assertProtocolV2ReconnectIdentity(serialNumber, serialNumber, path, path);
|
|
1623
|
+
}
|
|
1624
|
+
this.protocolV2ExpectedSerialNumber = serialNumber;
|
|
1625
|
+
this.protocolV2ExpectedPath = path;
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
private async verifyProtocolV2ReconnectIdentity() {
|
|
1629
|
+
const deviceInfo = await this.requestProtocolV2PhysicalIdentity();
|
|
1630
|
+
this.assertProtocolV2DeviceInfoIdentity(deviceInfo);
|
|
1631
|
+
return deviceInfo;
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
private prepareBootloaderBinary(): ArrayBuffer | null {
|
|
1635
|
+
return this.params.bootloaderBinary ?? null;
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
private getMissingProtocolV2FirmwareTargets(installItems: ProtocolV2InstallItem[]) {
|
|
1639
|
+
const preparedTargets = new Set(
|
|
1640
|
+
installItems.flatMap(item => {
|
|
1641
|
+
const target = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(item.targetId);
|
|
1642
|
+
return target ? [target] : [];
|
|
1643
|
+
})
|
|
724
1644
|
);
|
|
725
|
-
|
|
726
|
-
|
|
1645
|
+
return (this.params.targetsToUpdate ?? [])
|
|
1646
|
+
.filter(
|
|
1647
|
+
(target): target is Exclude<FirmwareUpdateV4Target, 'resource' | 'boot_resources'> =>
|
|
1648
|
+
target !== 'resource' && target !== 'boot_resources'
|
|
1649
|
+
)
|
|
1650
|
+
.filter(target => !preparedTargets.has(target));
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
private buildProtocolV2InstallItems({
|
|
1654
|
+
bootloaderBinary,
|
|
1655
|
+
fwBinaryMap,
|
|
1656
|
+
}: {
|
|
1657
|
+
bootloaderBinary: ArrayBuffer | null;
|
|
1658
|
+
fwBinaryMap: ProtocolV2TargetBinary[];
|
|
1659
|
+
}): ProtocolV2InstallItem[] {
|
|
1660
|
+
const installItems: ProtocolV2InstallItem[] = [];
|
|
1661
|
+
|
|
1662
|
+
if (bootloaderBinary) {
|
|
1663
|
+
installItems.push({
|
|
1664
|
+
fileName: 'bootloader.bin',
|
|
1665
|
+
binary: bootloaderBinary,
|
|
1666
|
+
targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER,
|
|
1667
|
+
kind: 'bootloader',
|
|
1668
|
+
});
|
|
727
1669
|
}
|
|
728
|
-
|
|
1670
|
+
|
|
1671
|
+
installItems.push(...fwBinaryMap.map(item => ({ ...item, kind: 'firmware' as const })));
|
|
1672
|
+
return installItems;
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
private getRemoteComponentEntries(release: IFirmwareReleaseInfo) {
|
|
1676
|
+
const { components } = release;
|
|
1677
|
+
if (!components) return [];
|
|
1678
|
+
|
|
1679
|
+
const orderedKeys = [
|
|
1680
|
+
...(release.installOrder ?? []),
|
|
1681
|
+
...Object.keys(components).filter(key => !release.installOrder?.includes(key)),
|
|
1682
|
+
];
|
|
1683
|
+
|
|
1684
|
+
return orderedKeys
|
|
1685
|
+
.map(key => {
|
|
1686
|
+
const component = components[key];
|
|
1687
|
+
return component ? ([key, component] as const) : undefined;
|
|
1688
|
+
})
|
|
1689
|
+
.filter((entry): entry is readonly [string, IProtocolV2FirmwareComponent] => !!entry);
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
private getRemoteComponentTarget(key: string, component: IProtocolV2FirmwareComponent) {
|
|
1693
|
+
const targetName = component.target?.toUpperCase();
|
|
1694
|
+
if (targetName === 'ROMLOADER') {
|
|
1695
|
+
throw ERRORS.TypedError(
|
|
1696
|
+
HardwareErrorCode.RuntimeError,
|
|
1697
|
+
PROTOCOL_V2_ROMLOADER_UNSUPPORTED_MESSAGE
|
|
1698
|
+
);
|
|
1699
|
+
}
|
|
1700
|
+
const target = PROTOCOL_V2_REMOTE_COMPONENT_TARGETS[targetName];
|
|
1701
|
+
if (!target) {
|
|
1702
|
+
throw ERRORS.TypedError(
|
|
1703
|
+
HardwareErrorCode.RuntimeError,
|
|
1704
|
+
`Unsupported Protocol V2 firmware component target: ${key}/${component.target}`
|
|
1705
|
+
);
|
|
1706
|
+
}
|
|
1707
|
+
return target;
|
|
729
1708
|
}
|
|
730
1709
|
|
|
731
1710
|
private async downloadRemoteProtocolV2Component(
|
|
@@ -739,15 +1718,55 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
739
1718
|
`Missing Protocol V2 firmware component url: ${key}/${component.target}`
|
|
740
1719
|
);
|
|
741
1720
|
}
|
|
1721
|
+
const expectedFingerprint = normalizeProtocolV2Hex(component.fingerprint);
|
|
1722
|
+
if (
|
|
1723
|
+
!Number.isSafeInteger(component.expectedSize) ||
|
|
1724
|
+
Number(component.expectedSize) <= 0 ||
|
|
1725
|
+
!expectedFingerprint ||
|
|
1726
|
+
!/^[0-9a-f]{64}$/u.test(expectedFingerprint)
|
|
1727
|
+
) {
|
|
1728
|
+
throw ERRORS.TypedError(
|
|
1729
|
+
HardwareErrorCode.RuntimeError,
|
|
1730
|
+
`Protocol V2 firmware component integrity metadata is invalid: ${key}/${component.target}`,
|
|
1731
|
+
{ firmwareUpdateCode: 'FirmwarePlanInvalid' }
|
|
1732
|
+
);
|
|
1733
|
+
}
|
|
742
1734
|
|
|
743
1735
|
const { binary } = await getSysResourceBinary(component.url);
|
|
1736
|
+
if (binary.byteLength !== component.expectedSize) {
|
|
1737
|
+
throw ERRORS.TypedError(
|
|
1738
|
+
HardwareErrorCode.RuntimeError,
|
|
1739
|
+
`Protocol V2 firmware size mismatch: ${key}/${component.target}`,
|
|
1740
|
+
{ firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch' }
|
|
1741
|
+
);
|
|
1742
|
+
}
|
|
1743
|
+
if (!isProtocolV2FirmwareFingerprintValid(binary, component.fingerprint)) {
|
|
1744
|
+
throw ERRORS.TypedError(
|
|
1745
|
+
HardwareErrorCode.RuntimeError,
|
|
1746
|
+
`Protocol V2 firmware fingerprint mismatch: ${key}/${component.target}`
|
|
1747
|
+
);
|
|
1748
|
+
}
|
|
1749
|
+
const expectedPayloadHash = normalizeProtocolV2Hex(component.payloadHash);
|
|
1750
|
+
if (expectedPayloadHash) {
|
|
1751
|
+
const header = parseProtocolV2OkppHeader(toProtocolV2Bytes(binary));
|
|
1752
|
+
if (!header || header.payloadHash !== expectedPayloadHash) {
|
|
1753
|
+
throw ERRORS.TypedError(
|
|
1754
|
+
HardwareErrorCode.RuntimeError,
|
|
1755
|
+
`Protocol V2 firmware payload hash mismatch: ${key}/${component.target}`
|
|
1756
|
+
);
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
744
1759
|
return {
|
|
745
1760
|
...target,
|
|
746
1761
|
binary,
|
|
747
1762
|
};
|
|
748
1763
|
}
|
|
749
1764
|
|
|
750
|
-
private async prepareRemoteProtocolV2Binaries(
|
|
1765
|
+
private async prepareRemoteProtocolV2Binaries(
|
|
1766
|
+
firmwareType: EFirmwareType,
|
|
1767
|
+
features: Features,
|
|
1768
|
+
explicitInstallItems: ProtocolV2InstallItem[] = []
|
|
1769
|
+
) {
|
|
751
1770
|
const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
|
|
752
1771
|
|
|
753
1772
|
let bootloaderBinary: ArrayBuffer | null = null;
|
|
@@ -755,6 +1774,15 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
755
1774
|
const installItems: ProtocolV2InstallItem[] = [];
|
|
756
1775
|
|
|
757
1776
|
if (!release) {
|
|
1777
|
+
const missingFirmwareTargets = this.getMissingProtocolV2FirmwareTargets(explicitInstallItems);
|
|
1778
|
+
if (missingFirmwareTargets.length > 0) {
|
|
1779
|
+
throw ERRORS.TypedError(
|
|
1780
|
+
HardwareErrorCode.RuntimeError,
|
|
1781
|
+
`Protocol V2 firmware release is unavailable for requested targets: ${missingFirmwareTargets.join(
|
|
1782
|
+
', '
|
|
1783
|
+
)}`
|
|
1784
|
+
);
|
|
1785
|
+
}
|
|
758
1786
|
return {
|
|
759
1787
|
bootloaderBinary,
|
|
760
1788
|
fwBinaryMap,
|
|
@@ -763,38 +1791,47 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
763
1791
|
}
|
|
764
1792
|
|
|
765
1793
|
const entries = this.getRemoteComponentEntries(release);
|
|
1794
|
+
const targetsToUpdate = new Set(this.params.targetsToUpdate ?? []);
|
|
1795
|
+
const explicitInstallItemByTargetId = new Map(
|
|
1796
|
+
explicitInstallItems.map(item => [item.targetId, item] as const)
|
|
1797
|
+
);
|
|
1798
|
+
const preparedTargets = new Set<FirmwareUpdateV4Target>();
|
|
766
1799
|
|
|
767
1800
|
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
|
-
});
|
|
1801
|
+
const targetName = component.target?.toUpperCase();
|
|
1802
|
+
const target = PROTOCOL_V2_REMOTE_COMPONENT_TARGETS[targetName];
|
|
1803
|
+
const updateTarget = target
|
|
1804
|
+
? PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(target.targetId)
|
|
1805
|
+
: undefined;
|
|
1806
|
+
if (updateTarget && targetsToUpdate.has(updateTarget)) {
|
|
1807
|
+
const explicitInstallItem = explicitInstallItemByTargetId.get(target.targetId);
|
|
1808
|
+
const installItem =
|
|
1809
|
+
explicitInstallItem ?? (await this.downloadRemoteProtocolV2Component(key, component));
|
|
1810
|
+
if (installItem.kind === 'bootloader') {
|
|
1811
|
+
bootloaderBinary = installItem.binary;
|
|
786
1812
|
} else {
|
|
787
1813
|
const binaryEntry = {
|
|
788
|
-
fileName:
|
|
789
|
-
binary:
|
|
790
|
-
targetId:
|
|
1814
|
+
fileName: installItem.fileName,
|
|
1815
|
+
binary: installItem.binary,
|
|
1816
|
+
targetId: installItem.targetId,
|
|
791
1817
|
};
|
|
792
1818
|
fwBinaryMap.push(binaryEntry);
|
|
793
|
-
installItems.push({ ...binaryEntry, kind: remoteBinary.kind });
|
|
794
1819
|
}
|
|
1820
|
+
installItems.push({ ...installItem });
|
|
1821
|
+
preparedTargets.add(updateTarget);
|
|
795
1822
|
}
|
|
796
1823
|
}
|
|
797
1824
|
|
|
1825
|
+
const missingTarget = Array.from(targetsToUpdate).find(
|
|
1826
|
+
target => target !== 'resource' && !preparedTargets.has(target)
|
|
1827
|
+
);
|
|
1828
|
+
if (missingTarget) {
|
|
1829
|
+
throw ERRORS.TypedError(
|
|
1830
|
+
HardwareErrorCode.RuntimeError,
|
|
1831
|
+
`Protocol V2 release does not contain requested target ${missingTarget}`
|
|
1832
|
+
);
|
|
1833
|
+
}
|
|
1834
|
+
|
|
798
1835
|
return {
|
|
799
1836
|
bootloaderBinary,
|
|
800
1837
|
fwBinaryMap,
|
|
@@ -802,140 +1839,83 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
802
1839
|
};
|
|
803
1840
|
}
|
|
804
1841
|
|
|
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[];
|
|
1842
|
+
private getProtocolV2ResourceFilePath(path: string) {
|
|
1843
|
+
if (path.startsWith('vol')) return path;
|
|
1844
|
+
if (path.startsWith('/')) return `vol0:${path}`;
|
|
1845
|
+
return `vol0:/${path}`;
|
|
843
1846
|
}
|
|
844
1847
|
|
|
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 };
|
|
1848
|
+
private async readProtocolV2DeviceFileHeader(path: string, expectedSize?: number) {
|
|
1849
|
+
const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
|
|
1850
|
+
const filePath = this.getProtocolV2ResourceFilePath(path);
|
|
1851
|
+
const pathInfoRes = await typedCall('FilesystemPathInfoQuery', 'FilesystemPathInfo', {
|
|
1852
|
+
path: filePath,
|
|
1853
|
+
});
|
|
1854
|
+
const fileSize = toProtocolV2FiniteNumber(pathInfoRes.message?.size);
|
|
1855
|
+
if (
|
|
1856
|
+
!pathInfoRes.message?.exist ||
|
|
1857
|
+
pathInfoRes.message?.directory ||
|
|
1858
|
+
fileSize === undefined ||
|
|
1859
|
+
fileSize < PROTOCOL_V2_OKPP_HEADER_SIZE ||
|
|
1860
|
+
(expectedSize !== undefined && fileSize !== expectedSize)
|
|
1861
|
+
) {
|
|
1862
|
+
return null;
|
|
885
1863
|
}
|
|
886
1864
|
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
processedSize,
|
|
901
|
-
totalSize: transferTotalSize,
|
|
1865
|
+
const chunkSize = this.getProtocolV2FirmwareChunkSize('read');
|
|
1866
|
+
const chunks: Uint8Array[] = [];
|
|
1867
|
+
let offset = 0;
|
|
1868
|
+
while (offset < PROTOCOL_V2_OKPP_HEADER_SIZE) {
|
|
1869
|
+
const readLen = Math.min(chunkSize, PROTOCOL_V2_OKPP_HEADER_SIZE - offset);
|
|
1870
|
+
const res = await typedCall('FilesystemFileRead', 'FilesystemFile', {
|
|
1871
|
+
file: {
|
|
1872
|
+
path: filePath,
|
|
1873
|
+
offset,
|
|
1874
|
+
total_size: 0,
|
|
1875
|
+
},
|
|
1876
|
+
chunk_len: readLen,
|
|
1877
|
+
ui_percentage: undefined,
|
|
902
1878
|
});
|
|
1879
|
+
const data = toProtocolV2Bytes(res.message?.data);
|
|
1880
|
+
if (data.byteLength === 0) return null;
|
|
1881
|
+
chunks.push(data);
|
|
1882
|
+
offset += data.byteLength;
|
|
903
1883
|
}
|
|
904
1884
|
|
|
905
|
-
const
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
);
|
|
911
|
-
return
|
|
1885
|
+
const headerBytes = new Uint8Array(offset);
|
|
1886
|
+
let cursor = 0;
|
|
1887
|
+
chunks.forEach(chunk => {
|
|
1888
|
+
headerBytes.set(chunk, cursor);
|
|
1889
|
+
cursor += chunk.byteLength;
|
|
1890
|
+
});
|
|
1891
|
+
return parseProtocolV2OkppHeader(headerBytes);
|
|
912
1892
|
}
|
|
913
1893
|
|
|
914
|
-
/**
|
|
915
|
-
* 比对设备上已有 okpkg 的 OKPP header(仅远端配置模式使用)。
|
|
916
|
-
*/
|
|
1894
|
+
/** Compare the downloaded and installed okpkg headers before transferring a resource. */
|
|
917
1895
|
private async isProtocolV2ResourceBundleUpToDate(
|
|
918
|
-
bundle:
|
|
1896
|
+
bundle: Pick<
|
|
1897
|
+
ProtocolV2ResourceBundleSource,
|
|
1898
|
+
'name' | 'source' | 'devicePath' | 'version' | 'payloadHash' | 'headerHash'
|
|
1899
|
+
>
|
|
919
1900
|
): Promise<boolean> {
|
|
920
|
-
if (this.params
|
|
921
|
-
if (!bundle.
|
|
1901
|
+
if (this.params?.forcedUpdateRes) return false;
|
|
1902
|
+
if (!bundle.payloadHash || !bundle.headerHash) return false;
|
|
922
1903
|
|
|
923
1904
|
try {
|
|
924
|
-
const header = await this.readProtocolV2DeviceFileHeader(
|
|
1905
|
+
const header = await this.readProtocolV2DeviceFileHeader(
|
|
1906
|
+
bundle.devicePath,
|
|
1907
|
+
bundle.source.size
|
|
1908
|
+
);
|
|
925
1909
|
if (!header) return false;
|
|
926
1910
|
|
|
927
1911
|
if (bundle.version) {
|
|
928
1912
|
const cmp = compareProtocolV2Versions(header.version, bundle.version);
|
|
929
1913
|
if (cmp === undefined || cmp !== 0) return false;
|
|
930
1914
|
}
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
if (bundle.headerHash) {
|
|
936
|
-
const expected = normalizeProtocolV2Hex(bundle.headerHash);
|
|
937
|
-
if (expected && header.headerHash !== expected) return false;
|
|
938
|
-
}
|
|
1915
|
+
const expectedPayloadHash = normalizeProtocolV2Hex(bundle.payloadHash);
|
|
1916
|
+
const expectedHeaderHash = normalizeProtocolV2Hex(bundle.headerHash);
|
|
1917
|
+
if (!expectedPayloadHash || header.payloadHash !== expectedPayloadHash) return false;
|
|
1918
|
+
if (!expectedHeaderHash || header.headerHash !== expectedHeaderHash) return false;
|
|
939
1919
|
return true;
|
|
940
1920
|
} catch (error) {
|
|
941
1921
|
Log.log(`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check failed: `, error);
|
|
@@ -945,23 +1925,50 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
945
1925
|
|
|
946
1926
|
private isProtocolV2BootloaderMode() {
|
|
947
1927
|
if (typeof this.device.isBootloader === 'function') {
|
|
948
|
-
return this.device.isBootloader();
|
|
1928
|
+
return !!this.device.isBootloader();
|
|
1929
|
+
}
|
|
1930
|
+
return (
|
|
1931
|
+
this.device.features?.mode === 'bootloader' ||
|
|
1932
|
+
(this.device.features?.mode == null && !!this.device.features?.bootloaderMode)
|
|
1933
|
+
);
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
private isProtocolV2RomloaderMode() {
|
|
1937
|
+
const deviceType = this.device.getCurrentDeviceType();
|
|
1938
|
+
if (
|
|
1939
|
+
!this.device.isProtocolV2() ||
|
|
1940
|
+
(deviceType !== EDeviceType.Pro2 && deviceType !== EDeviceType.Neo)
|
|
1941
|
+
) {
|
|
1942
|
+
return false;
|
|
1943
|
+
}
|
|
1944
|
+
if (typeof this.device.isRomloader === 'function') {
|
|
1945
|
+
return this.device.isRomloader();
|
|
949
1946
|
}
|
|
950
|
-
return
|
|
1947
|
+
return this.device.features?.mode === 'romloader';
|
|
951
1948
|
}
|
|
952
1949
|
|
|
953
1950
|
async enterProtocolV2BootloaderMode() {
|
|
1951
|
+
// romloader is the first update environment and forwards targets to bootloader.
|
|
1952
|
+
// It rejects DeviceRebootType.Bootloader, so reuse the current connection.
|
|
1953
|
+
if (this.isProtocolV2RomloaderMode()) {
|
|
1954
|
+
Log.debug('Protocol V2 device is in romloader mode; start firmware update directly');
|
|
1955
|
+
this.protocolV2ExecutionInLoader = true;
|
|
1956
|
+
return false;
|
|
1957
|
+
}
|
|
954
1958
|
if (this.isProtocolV2BootloaderMode()) {
|
|
955
|
-
Log.debug('Protocol V2 device is already in bootloader mode, skip reboot
|
|
1959
|
+
Log.debug('Protocol V2 device is already in bootloader mode, skip reboot');
|
|
1960
|
+
this.protocolV2ExecutionInLoader = true;
|
|
1961
|
+
this.postTipMessage(FirmwareUpdateTipMessage.GoToBootloaderSuccess);
|
|
956
1962
|
return false;
|
|
957
1963
|
}
|
|
958
1964
|
|
|
959
1965
|
try {
|
|
960
1966
|
this.postTipMessage(FirmwareUpdateTipMessage.AutoRebootToBootloader);
|
|
961
1967
|
await this.protocolV2Reboot(DeviceRebootType.Bootloader);
|
|
962
|
-
this.postTipMessage(FirmwareUpdateTipMessage.GoToBootloaderSuccess);
|
|
963
1968
|
await wait(1000);
|
|
964
1969
|
await this.waitForProtocolV2BootloaderMode();
|
|
1970
|
+
this.protocolV2ExecutionInLoader = true;
|
|
1971
|
+
this.postTipMessage(FirmwareUpdateTipMessage.GoToBootloaderSuccess);
|
|
965
1972
|
return true;
|
|
966
1973
|
} catch (error) {
|
|
967
1974
|
if (error instanceof HardwareError) {
|
|
@@ -978,20 +1985,32 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
978
1985
|
) {
|
|
979
1986
|
const startTime = Date.now();
|
|
980
1987
|
let lastError: unknown;
|
|
1988
|
+
let shouldReconnect = true;
|
|
981
1989
|
|
|
982
1990
|
while (Date.now() - startTime < timeout) {
|
|
983
1991
|
try {
|
|
984
|
-
|
|
1992
|
+
if (shouldReconnect) {
|
|
1993
|
+
await this.reconnectProtocolV2Device();
|
|
1994
|
+
shouldReconnect = false;
|
|
1995
|
+
}
|
|
985
1996
|
const deviceInfo = await requestProtocolV2DeviceInfo({
|
|
986
1997
|
commands: this.device.getCommands(),
|
|
987
|
-
timeoutMs:
|
|
1998
|
+
timeoutMs: this.getProtocolV2DeviceInfoTimeout(),
|
|
988
1999
|
});
|
|
989
|
-
|
|
990
|
-
|
|
2000
|
+
this.assertProtocolV2DeviceInfoIdentity(deviceInfo);
|
|
2001
|
+
const features = await this.device.probeProtocolV2RuntimeState(
|
|
2002
|
+
deviceInfo,
|
|
2003
|
+
PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT
|
|
2004
|
+
);
|
|
2005
|
+
if (features?.mode === 'bootloader') {
|
|
991
2006
|
return features;
|
|
992
2007
|
}
|
|
993
2008
|
lastError = new Error('Protocol V2 device is reachable but is not in bootloader mode');
|
|
994
2009
|
} catch (error) {
|
|
2010
|
+
if (this.isProtocolV2ReconnectIdentityError(error)) {
|
|
2011
|
+
throw error;
|
|
2012
|
+
}
|
|
2013
|
+
shouldReconnect = true;
|
|
995
2014
|
lastError = error;
|
|
996
2015
|
Log.log('Protocol V2 bootloader mode not ready, polling reconnect: ', error);
|
|
997
2016
|
}
|
|
@@ -1007,8 +2026,8 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1007
2026
|
}
|
|
1008
2027
|
|
|
1009
2028
|
/**
|
|
1010
|
-
*
|
|
1011
|
-
*
|
|
2029
|
+
* Collect explicit target binaries grouped by DeviceFirmwareTargetType.
|
|
2030
|
+
* Filenames are display-only staging paths because target_id is explicit.
|
|
1012
2031
|
*/
|
|
1013
2032
|
private collectExplicitTargetBinaries() {
|
|
1014
2033
|
const entries: ProtocolV2TargetBinary[] = [];
|
|
@@ -1044,115 +2063,277 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1044
2063
|
return entries;
|
|
1045
2064
|
}
|
|
1046
2065
|
|
|
2066
|
+
private buildProtocolV2ExecutionPhases({
|
|
2067
|
+
installSources,
|
|
2068
|
+
resourceSources,
|
|
2069
|
+
}: {
|
|
2070
|
+
installSources: ProtocolV2InstallSource[];
|
|
2071
|
+
resourceSources: ProtocolV2ResourceBundleSource[];
|
|
2072
|
+
}): ProtocolV2ExecutionPhase[] {
|
|
2073
|
+
const phases: ProtocolV2ExecutionPhase[] = [];
|
|
2074
|
+
const bootloaderSources = installSources.filter(source => source.kind === 'bootloader');
|
|
2075
|
+
if (bootloaderSources.length > 0) {
|
|
2076
|
+
phases.push(
|
|
2077
|
+
{
|
|
2078
|
+
kind: 'bootloader-install',
|
|
2079
|
+
installSources: bootloaderSources,
|
|
2080
|
+
resourceSources: [],
|
|
2081
|
+
},
|
|
2082
|
+
{
|
|
2083
|
+
kind: 'bootloader-verify',
|
|
2084
|
+
installSources: [],
|
|
2085
|
+
resourceSources: [],
|
|
2086
|
+
}
|
|
2087
|
+
);
|
|
2088
|
+
}
|
|
2089
|
+
if (resourceSources.length > 0) {
|
|
2090
|
+
phases.push({
|
|
2091
|
+
kind: 'resource-sync',
|
|
2092
|
+
installSources: [],
|
|
2093
|
+
resourceSources,
|
|
2094
|
+
});
|
|
2095
|
+
}
|
|
2096
|
+
const componentSources = installSources.filter(source => source.kind !== 'bootloader');
|
|
2097
|
+
if (componentSources.length > 0) {
|
|
2098
|
+
phases.push({
|
|
2099
|
+
kind: 'component-install',
|
|
2100
|
+
installSources: componentSources,
|
|
2101
|
+
resourceSources: [],
|
|
2102
|
+
});
|
|
2103
|
+
}
|
|
2104
|
+
phases.push({
|
|
2105
|
+
kind: 'final-verify',
|
|
2106
|
+
installSources: [],
|
|
2107
|
+
resourceSources: [],
|
|
2108
|
+
});
|
|
2109
|
+
return phases;
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2112
|
+
private async executeProtocolV2Phases({
|
|
2113
|
+
installSources,
|
|
2114
|
+
resourceSources,
|
|
2115
|
+
}: {
|
|
2116
|
+
installSources: ProtocolV2InstallSource[];
|
|
2117
|
+
resourceSources: ProtocolV2ResourceBundleSource[];
|
|
2118
|
+
}) {
|
|
2119
|
+
const phases = this.buildProtocolV2ExecutionPhases({
|
|
2120
|
+
installSources,
|
|
2121
|
+
resourceSources,
|
|
2122
|
+
});
|
|
2123
|
+
for (const phase of phases) {
|
|
2124
|
+
if (phase.kind === 'final-verify') {
|
|
2125
|
+
return this.completeProtocolV2FinalVerification();
|
|
2126
|
+
}
|
|
2127
|
+
if (phase.kind === 'resource-sync') {
|
|
2128
|
+
await this.enterProtocolV2BootloaderMode();
|
|
2129
|
+
await this.executeProtocolV2TransferPhase(phase);
|
|
2130
|
+
} else if (phase.kind === 'bootloader-install' || phase.kind === 'component-install') {
|
|
2131
|
+
await this.enterProtocolV2BootloaderMode();
|
|
2132
|
+
await this.executeProtocolV2TransferPhase(phase);
|
|
2133
|
+
await this.exitProtocolV2BootloaderToNormal();
|
|
2134
|
+
} else if (phase.kind === 'bootloader-verify') {
|
|
2135
|
+
await this.waitForProtocolV2FinalFeatures();
|
|
2136
|
+
this.assertExpectedProtocolV2Versions(['boot']);
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
throw ERRORS.TypedError(
|
|
2140
|
+
HardwareErrorCode.RuntimeError,
|
|
2141
|
+
'Protocol V2 execution has no final verification phase'
|
|
2142
|
+
);
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
private async executeProtocolV2SourceUpdate({
|
|
2146
|
+
installSources,
|
|
2147
|
+
resourceSources,
|
|
2148
|
+
}: {
|
|
2149
|
+
installSources: ProtocolV2InstallSource[];
|
|
2150
|
+
resourceSources: ProtocolV2ResourceBundleSource[];
|
|
2151
|
+
}) {
|
|
2152
|
+
return this.executeProtocolV2Phases({
|
|
2153
|
+
installSources,
|
|
2154
|
+
resourceSources,
|
|
2155
|
+
});
|
|
2156
|
+
}
|
|
2157
|
+
|
|
1047
2158
|
private async executeProtocolV2Update({
|
|
1048
2159
|
fwBinaryMap,
|
|
1049
2160
|
bootloaderBinary,
|
|
1050
2161
|
installItems,
|
|
1051
|
-
resourceBundles,
|
|
2162
|
+
resourceBundles = [],
|
|
1052
2163
|
}: {
|
|
1053
2164
|
fwBinaryMap?: ProtocolV2TargetBinary[];
|
|
1054
2165
|
bootloaderBinary?: ArrayBuffer | null;
|
|
1055
2166
|
installItems?: ProtocolV2InstallItem[];
|
|
1056
2167
|
resourceBundles?: ProtocolV2ResourceBundleBinary[];
|
|
1057
2168
|
}) {
|
|
1058
|
-
const
|
|
2169
|
+
const memoryInstallItems =
|
|
1059
2170
|
installItems ??
|
|
1060
2171
|
this.buildProtocolV2InstallItems({
|
|
1061
|
-
bootloaderBinary: bootloaderBinary ?? null,
|
|
1062
2172
|
fwBinaryMap: fwBinaryMap ?? [],
|
|
2173
|
+
bootloaderBinary: bootloaderBinary ?? null,
|
|
1063
2174
|
});
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
if (resourceBundles?.length) {
|
|
1073
|
-
const resourceTransfer = await this.syncProtocolV2ResourceBundles(
|
|
1074
|
-
resourceBundles,
|
|
1075
|
-
firmwareSize
|
|
2175
|
+
try {
|
|
2176
|
+
const installSources = await Promise.all(
|
|
2177
|
+
memoryInstallItems.map(async item => ({
|
|
2178
|
+
fileName: item.fileName,
|
|
2179
|
+
source: await this.openProtocolV2MemorySource(item.binary),
|
|
2180
|
+
targetId: item.targetId,
|
|
2181
|
+
kind: item.kind,
|
|
2182
|
+
}))
|
|
1076
2183
|
);
|
|
1077
|
-
|
|
1078
|
-
|
|
2184
|
+
const resourceSources = await Promise.all(
|
|
2185
|
+
resourceBundles.map(async resource => ({
|
|
2186
|
+
name: resource.name,
|
|
2187
|
+
source: await this.openProtocolV2MemorySource(resource.binary),
|
|
2188
|
+
devicePath: resource.devicePath,
|
|
2189
|
+
...(resource.version ? { version: resource.version } : {}),
|
|
2190
|
+
...(resource.payloadHash ? { payloadHash: resource.payloadHash } : {}),
|
|
2191
|
+
...(resource.headerHash ? { headerHash: resource.headerHash } : {}),
|
|
2192
|
+
}))
|
|
2193
|
+
);
|
|
2194
|
+
return await this.executeProtocolV2Phases({
|
|
2195
|
+
installSources,
|
|
2196
|
+
resourceSources,
|
|
2197
|
+
});
|
|
2198
|
+
} finally {
|
|
2199
|
+
await this.closeProtocolV2PreparedSources();
|
|
1079
2200
|
}
|
|
2201
|
+
}
|
|
1080
2202
|
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
2203
|
+
private async executeProtocolV2TransferPhase({
|
|
2204
|
+
installSources,
|
|
2205
|
+
resourceSources,
|
|
2206
|
+
}: ProtocolV2ExecutionPhase) {
|
|
2207
|
+
let totalSize = installSources.reduce((total, item) => total + item.source.size, 0);
|
|
2208
|
+
const resourcesToSync: ProtocolV2ResourceBundleSource[] = [];
|
|
2209
|
+
for (const resource of resourceSources) {
|
|
2210
|
+
if (await this.isProtocolV2ResourceBundleUpToDate(resource)) {
|
|
2211
|
+
Log.log(`[FirmwareUpdateV4] skip RESC bundle ${resource.name}; already up to date`);
|
|
2212
|
+
} else {
|
|
2213
|
+
resourcesToSync.push(resource);
|
|
2214
|
+
totalSize += resource.source.size;
|
|
1086
2215
|
}
|
|
1087
|
-
return;
|
|
1088
2216
|
}
|
|
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
2217
|
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
2218
|
+
this.postTipMessage(FirmwareUpdateTipMessage.StartTransferData);
|
|
2219
|
+
let processedSize = 0;
|
|
2220
|
+
for (const resource of resourcesToSync) {
|
|
2221
|
+
// The bootloader keeps its live resource package mounted. FatFs rejects
|
|
2222
|
+
// replacing an open file, so early boot promotes this staging file before mounting it.
|
|
2223
|
+
const writePath = resolveProtocolV2ResourceWritePath(resource.devicePath);
|
|
2224
|
+
processedSize = await this.protocolV2SourceUpdateProcess({
|
|
2225
|
+
source: resource.source,
|
|
2226
|
+
filePath: writePath,
|
|
2227
|
+
processedSize,
|
|
2228
|
+
totalSize,
|
|
2229
|
+
});
|
|
2230
|
+
await this.verifyProtocolV2StagedFile(writePath, resource.source.size);
|
|
2231
|
+
}
|
|
1123
2232
|
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
2233
|
+
const stagedInstallTargets: Array<{ targetId: number; path: string }> = [];
|
|
2234
|
+
for (const item of installSources) {
|
|
2235
|
+
const filePath = this.getProtocolV2InstallItemStagingPath(item);
|
|
2236
|
+
processedSize = await this.protocolV2SourceUpdateProcess({
|
|
2237
|
+
source: item.source,
|
|
2238
|
+
filePath,
|
|
2239
|
+
processedSize,
|
|
2240
|
+
totalSize,
|
|
2241
|
+
});
|
|
2242
|
+
await this.verifyProtocolV2StagedFile(filePath, item.source.size);
|
|
2243
|
+
stagedInstallTargets.push({
|
|
2244
|
+
targetId: item.targetId,
|
|
2245
|
+
path: filePath,
|
|
2246
|
+
});
|
|
2247
|
+
}
|
|
1127
2248
|
|
|
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;
|
|
2249
|
+
if (totalSize > 0) {
|
|
2250
|
+
this.postProgressMessage(100, 'transferData');
|
|
2251
|
+
}
|
|
2252
|
+
if (stagedInstallTargets.length === 0) {
|
|
2253
|
+
return;
|
|
1142
2254
|
}
|
|
1143
2255
|
|
|
1144
2256
|
this.postTipMessage(FirmwareUpdateTipMessage.ConfirmOnDevice);
|
|
1145
|
-
|
|
1146
|
-
const allTargets = stagedInstallTargets.map(item => ({
|
|
2257
|
+
const targets = stagedInstallTargets.map(item => ({
|
|
1147
2258
|
target_id: item.targetId,
|
|
1148
2259
|
path: item.path,
|
|
1149
2260
|
}));
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
2261
|
+
await this.protocolV2StartFirmwareUpdate({ targets });
|
|
2262
|
+
await this.waitForProtocolV2FirmwareUpdateComplete(targets);
|
|
2263
|
+
}
|
|
2264
|
+
|
|
2265
|
+
private async protocolV2SourceUpdateProcess({
|
|
2266
|
+
source,
|
|
2267
|
+
filePath,
|
|
2268
|
+
processedSize,
|
|
2269
|
+
totalSize,
|
|
2270
|
+
}: {
|
|
2271
|
+
source: FirmwareByteSource;
|
|
2272
|
+
filePath: string;
|
|
2273
|
+
processedSize: number;
|
|
2274
|
+
totalSize: number;
|
|
2275
|
+
}) {
|
|
2276
|
+
let lastError: unknown;
|
|
2277
|
+
for (let attempt = 1; attempt <= PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT; attempt += 1) {
|
|
2278
|
+
try {
|
|
2279
|
+
const transferStartedAt = Date.now();
|
|
2280
|
+
await writeFirmwareByteSource({
|
|
2281
|
+
source,
|
|
2282
|
+
chunkSize: this.getProtocolV2FirmwareChunkSize('write', filePath),
|
|
2283
|
+
write: async ({ data, sourceOffset, length, first }) => {
|
|
2284
|
+
const chunkEnd = sourceOffset + length;
|
|
2285
|
+
const deviceProgress = getProtocolV2DeviceTransferProgress(
|
|
2286
|
+
processedSize + sourceOffset,
|
|
2287
|
+
processedSize + chunkEnd,
|
|
2288
|
+
totalSize
|
|
2289
|
+
);
|
|
2290
|
+
const response = await this.fileWriteChunk(
|
|
2291
|
+
filePath,
|
|
2292
|
+
source.size,
|
|
2293
|
+
sourceOffset,
|
|
2294
|
+
data,
|
|
2295
|
+
first,
|
|
2296
|
+
deviceProgress
|
|
2297
|
+
);
|
|
2298
|
+
const rawProcessedByte = response.message.processed_byte;
|
|
2299
|
+
const nextOffset = rawProcessedByte === undefined ? chunkEnd : Number(rawProcessedByte);
|
|
2300
|
+
if (!Number.isFinite(nextOffset) || nextOffset !== chunkEnd) {
|
|
2301
|
+
throw ERRORS.TypedError(
|
|
2302
|
+
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
2303
|
+
`invalid processed_byte ${rawProcessedByte} for offset ${sourceOffset}`
|
|
2304
|
+
);
|
|
2305
|
+
}
|
|
2306
|
+
const transferredBytes = processedSize + chunkEnd;
|
|
2307
|
+
const elapsedMs = Math.max(Date.now() - transferStartedAt, 0);
|
|
2308
|
+
this.postProgressMessage(
|
|
2309
|
+
Math.min(Math.ceil((transferredBytes / totalSize) * 100), 99),
|
|
2310
|
+
'transferData',
|
|
2311
|
+
{
|
|
2312
|
+
transferredBytes,
|
|
2313
|
+
totalBytes: totalSize,
|
|
2314
|
+
rateBytesPerSecond:
|
|
2315
|
+
elapsedMs > 0 ? Math.round((chunkEnd / elapsedMs) * 1000) : undefined,
|
|
2316
|
+
elapsedMs,
|
|
2317
|
+
}
|
|
2318
|
+
);
|
|
2319
|
+
return length;
|
|
2320
|
+
},
|
|
2321
|
+
});
|
|
2322
|
+
return processedSize + source.size;
|
|
2323
|
+
} catch (error) {
|
|
2324
|
+
lastError = error;
|
|
2325
|
+
if (attempt < PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT) {
|
|
2326
|
+
await this.recoverProtocolV2FileTransfer();
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
throw ERRORS.TypedError(
|
|
2331
|
+
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
2332
|
+
`transfer data error: ${getProtocolV2UnknownErrorText(lastError)}`
|
|
2333
|
+
);
|
|
1153
2334
|
}
|
|
1154
2335
|
|
|
1155
|
-
private getProtocolV2InstallItemStagingPath(item: ProtocolV2InstallItem) {
|
|
2336
|
+
private getProtocolV2InstallItemStagingPath(item: Pick<ProtocolV2InstallItem, 'fileName'>) {
|
|
1156
2337
|
return `${PROTOCOL_V2_FIRMWARE_STAGING_VOLUME}${item.fileName}`;
|
|
1157
2338
|
}
|
|
1158
2339
|
|
|
@@ -1169,168 +2350,315 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1169
2350
|
}
|
|
1170
2351
|
}
|
|
1171
2352
|
|
|
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
2353
|
private assertProtocolV2TargetStatus(
|
|
1215
2354
|
statusTargets: ProtocolV2FirmwareUpdateStatusTarget[],
|
|
1216
|
-
expectedTargetIds: Set<number
|
|
2355
|
+
expectedTargetIds: Set<number>,
|
|
2356
|
+
expectedPaths = new Map<number, string>()
|
|
1217
2357
|
) {
|
|
2358
|
+
Log.log(
|
|
2359
|
+
`[FirmwareUpdateV4] DeviceFirmwareUpdateStatus records=${JSON.stringify(statusTargets)}`
|
|
2360
|
+
);
|
|
1218
2361
|
const failedTarget = statusTargets.find(
|
|
1219
2362
|
target =>
|
|
1220
2363
|
expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1) &&
|
|
1221
2364
|
isProtocolV2TargetStatusFailed(target.status)
|
|
1222
2365
|
);
|
|
1223
2366
|
if (failedTarget) {
|
|
2367
|
+
const failedTargetDetails = JSON.stringify({
|
|
2368
|
+
targetId: failedTarget.target_id,
|
|
2369
|
+
status: failedTarget.status,
|
|
2370
|
+
payloadVersion: failedTarget.payload_version,
|
|
2371
|
+
path: failedTarget.path,
|
|
2372
|
+
});
|
|
2373
|
+
Log.error(`[FirmwareUpdateV4] firmware install failed target=${failedTargetDetails}`);
|
|
1224
2374
|
throw ERRORS.TypedError(
|
|
1225
2375
|
HardwareErrorCode.FirmwareError,
|
|
1226
|
-
`Protocol V2 firmware target
|
|
2376
|
+
`Protocol V2 firmware target failed: target=${failedTarget.target_id} status=${
|
|
2377
|
+
failedTarget.status ?? 'unknown'
|
|
2378
|
+
} payloadVersion=${failedTarget.payload_version ?? 'unknown'} path=${
|
|
2379
|
+
failedTarget.path ?? 'unknown'
|
|
2380
|
+
}`
|
|
1227
2381
|
);
|
|
1228
2382
|
}
|
|
1229
2383
|
|
|
1230
|
-
const
|
|
1231
|
-
target
|
|
1232
|
-
|
|
1233
|
-
|
|
2384
|
+
const matchingTargets = statusTargets.filter(target =>
|
|
2385
|
+
expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1)
|
|
2386
|
+
);
|
|
2387
|
+
const seenTargetIds = new Set<number>();
|
|
2388
|
+
for (const target of matchingTargets) {
|
|
2389
|
+
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
2390
|
+
if (
|
|
2391
|
+
targetId === undefined ||
|
|
2392
|
+
seenTargetIds.has(targetId) ||
|
|
2393
|
+
(target.path && expectedPaths.get(targetId) && target.path !== expectedPaths.get(targetId))
|
|
2394
|
+
) {
|
|
2395
|
+
throw ERRORS.TypedError(
|
|
2396
|
+
HardwareErrorCode.RuntimeError,
|
|
2397
|
+
`Protocol V2 install status conflicts with target ${target.target_id}`,
|
|
2398
|
+
{
|
|
2399
|
+
firmwareUpdateCode: PROTOCOL_V2_INSTALL_STATUS_CONFLICT_CODE,
|
|
2400
|
+
}
|
|
2401
|
+
);
|
|
2402
|
+
}
|
|
2403
|
+
seenTargetIds.add(targetId);
|
|
2404
|
+
}
|
|
2405
|
+
const completedTargets = matchingTargets.filter(target =>
|
|
2406
|
+
isProtocolV2TargetStatusFinished(target.status)
|
|
1234
2407
|
);
|
|
1235
|
-
|
|
2408
|
+
const completedTargetIds = new Set<number>();
|
|
2409
|
+
completedTargets.forEach(target => {
|
|
2410
|
+
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
2411
|
+
if (targetId !== undefined) {
|
|
2412
|
+
completedTargetIds.add(targetId);
|
|
2413
|
+
}
|
|
2414
|
+
});
|
|
2415
|
+
if (completedTargetIds.size === expectedTargetIds.size && expectedTargetIds.size > 0) {
|
|
2416
|
+
for (const target of completedTargets) {
|
|
2417
|
+
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
2418
|
+
const payloadVersion = toProtocolV2FiniteNumber(target.payload_version);
|
|
2419
|
+
if (targetId === undefined) {
|
|
2420
|
+
throw ERRORS.TypedError(
|
|
2421
|
+
HardwareErrorCode.RuntimeError,
|
|
2422
|
+
'Protocol V2 completed target identity is invalid'
|
|
2423
|
+
);
|
|
2424
|
+
}
|
|
2425
|
+
const expectedVersion = this.getExpectedProtocolV2TargetVersion(targetId);
|
|
2426
|
+
if (expectedVersion !== undefined && payloadVersion !== undefined) {
|
|
2427
|
+
if (payloadVersion !== expectedVersion) {
|
|
2428
|
+
throw ERRORS.TypedError(
|
|
2429
|
+
HardwareErrorCode.FirmwareVerificationFailed,
|
|
2430
|
+
`Protocol V2 target ${targetId} reached payload version ${payloadVersion}, expected ${expectedVersion}`
|
|
2431
|
+
);
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
if (payloadVersion !== undefined) {
|
|
2435
|
+
this.protocolV2CompletedTargetVersions.set(targetId, payloadVersion);
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
this.postProgressMessage(100, 'installingFirmware');
|
|
1236
2439
|
return true;
|
|
1237
2440
|
}
|
|
1238
2441
|
|
|
1239
|
-
|
|
1240
|
-
target =>
|
|
1241
|
-
expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1) &&
|
|
2442
|
+
if (expectedTargetIds.size > 0 && matchingTargets.length > 0) {
|
|
2443
|
+
const hasInProgressTarget = matchingTargets.some(target =>
|
|
1242
2444
|
isProtocolV2TargetStatusInProgress(target.status)
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
2445
|
+
);
|
|
2446
|
+
const completedProgress = Math.floor(
|
|
2447
|
+
(completedTargetIds.size / expectedTargetIds.size) * 100
|
|
2448
|
+
);
|
|
2449
|
+
// The protocol exposes no per-target percentage, so report coarse progress by
|
|
2450
|
+
// completed targets and use 1% once work starts to keep the UI responsive.
|
|
2451
|
+
const progress = Math.min(99, Math.max(completedProgress, hasInProgressTarget ? 1 : 0));
|
|
2452
|
+
this.postProgressMessage(progress, 'installingFirmware');
|
|
1246
2453
|
}
|
|
1247
2454
|
|
|
1248
2455
|
return false;
|
|
1249
2456
|
}
|
|
1250
2457
|
|
|
1251
|
-
private
|
|
1252
|
-
|
|
1253
|
-
|
|
2458
|
+
private getProtocolV2MissingTargetIds(
|
|
2459
|
+
statusTargets: ProtocolV2FirmwareUpdateStatusTarget[],
|
|
2460
|
+
expectedTargetIds: Set<number>
|
|
1254
2461
|
) {
|
|
1255
|
-
const
|
|
1256
|
-
|
|
1257
|
-
const
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
return;
|
|
2462
|
+
const reportedTargetIds = new Set<number>();
|
|
2463
|
+
statusTargets.forEach(target => {
|
|
2464
|
+
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
2465
|
+
if (targetId !== undefined) {
|
|
2466
|
+
reportedTargetIds.add(targetId);
|
|
1261
2467
|
}
|
|
1262
|
-
}
|
|
2468
|
+
});
|
|
2469
|
+
return Array.from(expectedTargetIds).filter(targetId => !reportedTargetIds.has(targetId));
|
|
2470
|
+
}
|
|
1263
2471
|
|
|
2472
|
+
private async waitForProtocolV2FirmwareUpdateComplete(
|
|
2473
|
+
targets: Array<{ target_id: number; path: string }>
|
|
2474
|
+
) {
|
|
2475
|
+
this.protocolV2FinalStatusVerified = false;
|
|
2476
|
+
const expectedTargetIds = new Set(targets.map(target => target.target_id));
|
|
2477
|
+
const expectedPaths = new Map(targets.map(target => [target.target_id, target.path]));
|
|
1264
2478
|
const startTime = Date.now();
|
|
1265
2479
|
let lastError: unknown;
|
|
2480
|
+
let shouldReconnect = true;
|
|
2481
|
+
let deviceInfo: ProtocolV2DeviceInfo | undefined;
|
|
2482
|
+
let missingTargetStatusSince: number | undefined;
|
|
2483
|
+
let missingTargetStatusKey: string | undefined;
|
|
2484
|
+
const resetMissingTargetStatusGrace = () => {
|
|
2485
|
+
missingTargetStatusSince = undefined;
|
|
2486
|
+
missingTargetStatusKey = undefined;
|
|
2487
|
+
};
|
|
1266
2488
|
|
|
1267
2489
|
while (Date.now() - startTime < PROTOCOL_V2_INSTALL_TIMEOUT) {
|
|
1268
2490
|
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;
|
|
2491
|
+
if (shouldReconnect) {
|
|
2492
|
+
await this.reconnectProtocolV2Device();
|
|
2493
|
+
deviceInfo = await this.verifyProtocolV2ReconnectIdentity();
|
|
2494
|
+
shouldReconnect = false;
|
|
1279
2495
|
}
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
2496
|
+
const currentDeviceInfo = deviceInfo;
|
|
2497
|
+
try {
|
|
2498
|
+
const statusResponse = await this.device.getCommands().typedCall(
|
|
2499
|
+
'DeviceFirmwareUpdateStatusGet',
|
|
2500
|
+
'DeviceFirmwareUpdateStatus',
|
|
2501
|
+
{
|
|
2502
|
+
fields: {
|
|
2503
|
+
status: true,
|
|
2504
|
+
payload_version: true,
|
|
2505
|
+
path: true,
|
|
2506
|
+
},
|
|
2507
|
+
},
|
|
2508
|
+
{ timeoutMs: PROTOCOL_V2_FIRMWARE_STATUS_RESPONSE_TIMEOUT }
|
|
2509
|
+
);
|
|
2510
|
+
const statusTargets = (statusResponse.message.records ??
|
|
2511
|
+
[]) as ProtocolV2FirmwareUpdateStatusTarget[];
|
|
2512
|
+
if (this.assertProtocolV2TargetStatus(statusTargets, expectedTargetIds, expectedPaths)) {
|
|
2513
|
+
this.protocolV2FinalStatusVerified = true;
|
|
2514
|
+
return;
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2517
|
+
if (
|
|
2518
|
+
statusTargets.length === 0 &&
|
|
2519
|
+
currentDeviceInfo &&
|
|
2520
|
+
(await this.probeProtocolV2NormalMode(currentDeviceInfo))
|
|
2521
|
+
) {
|
|
2522
|
+
Log.log(
|
|
2523
|
+
'[FirmwareUpdateV4] empty firmware status after confirmed App reboot; update complete'
|
|
2524
|
+
);
|
|
2525
|
+
this.postProgressMessage(100, 'installingFirmware');
|
|
2526
|
+
return;
|
|
2527
|
+
}
|
|
2528
|
+
|
|
2529
|
+
const missingTargetIds = this.getProtocolV2MissingTargetIds(
|
|
2530
|
+
statusTargets,
|
|
2531
|
+
expectedTargetIds
|
|
2532
|
+
);
|
|
2533
|
+
if (missingTargetIds.length > 0) {
|
|
2534
|
+
const now = Date.now();
|
|
2535
|
+
const missingKey = missingTargetIds.join(',');
|
|
2536
|
+
if (missingTargetStatusSince === undefined || missingTargetStatusKey !== missingKey) {
|
|
2537
|
+
missingTargetStatusSince = now;
|
|
2538
|
+
missingTargetStatusKey = missingKey;
|
|
2539
|
+
} else if (
|
|
2540
|
+
now - missingTargetStatusSince >=
|
|
2541
|
+
PROTOCOL_V2_MISSING_TARGET_STATUS_GRACE_TIMEOUT
|
|
2542
|
+
) {
|
|
2543
|
+
const reportedTargetIds = statusTargets
|
|
2544
|
+
.map(target => normalizeProtocolV2TargetId(target.target_id))
|
|
2545
|
+
.filter((targetId): targetId is number => targetId !== undefined);
|
|
2546
|
+
throw ERRORS.TypedError(
|
|
2547
|
+
HardwareErrorCode.FirmwareError,
|
|
2548
|
+
`Protocol V2 firmware status is missing requested records: targetIds=${missingKey} reportedTargetIds=${reportedTargetIds.join(
|
|
2549
|
+
','
|
|
2550
|
+
)}`
|
|
2551
|
+
);
|
|
2552
|
+
}
|
|
2553
|
+
lastError = new Error(
|
|
2554
|
+
`Protocol V2 firmware status is temporarily missing targetIds=${missingKey}`
|
|
2555
|
+
);
|
|
2556
|
+
} else {
|
|
2557
|
+
resetMissingTargetStatusGrace();
|
|
2558
|
+
lastError = new Error('Protocol V2 firmware targets are still installing');
|
|
2559
|
+
}
|
|
2560
|
+
} catch (error) {
|
|
2561
|
+
if (isProtocolV2TerminalInstallStatusError(error)) {
|
|
2562
|
+
throw error;
|
|
2563
|
+
}
|
|
2564
|
+
// Only consecutive full status dumps can prove that a record is absent.
|
|
2565
|
+
// A reboot or transport interruption starts a fresh grace window.
|
|
2566
|
+
resetMissingTargetStatusGrace();
|
|
2567
|
+
// App firmware does not register DeviceFirmwareUpdateStatusGet. Treat the
|
|
2568
|
+
// missing endpoint as completion only after the runtime probe confirms App mode.
|
|
2569
|
+
if (isProtocolV2FirmwareStatusEndpointUnavailable(error)) {
|
|
2570
|
+
if (!currentDeviceInfo) {
|
|
2571
|
+
throw ERRORS.TypedError(
|
|
2572
|
+
HardwareErrorCode.RuntimeError,
|
|
2573
|
+
'Protocol V2 device identity is unavailable during install polling'
|
|
2574
|
+
);
|
|
2575
|
+
}
|
|
2576
|
+
if (await this.probeProtocolV2NormalMode(currentDeviceInfo)) {
|
|
2577
|
+
Log.log(
|
|
2578
|
+
'[FirmwareUpdateV4] firmware status endpoint unavailable after confirmed App reboot'
|
|
2579
|
+
);
|
|
2580
|
+
this.postProgressMessage(100, 'installingFirmware');
|
|
1285
2581
|
return;
|
|
1286
2582
|
}
|
|
1287
|
-
|
|
1288
|
-
|
|
2583
|
+
lastError = new Error(
|
|
2584
|
+
'Protocol V2 firmware status endpoint is unavailable while the device remains in loader mode'
|
|
2585
|
+
);
|
|
2586
|
+
} else {
|
|
2587
|
+
shouldReconnect = true;
|
|
2588
|
+
deviceInfo = undefined;
|
|
2589
|
+
lastError = error;
|
|
1289
2590
|
Log.log(
|
|
1290
|
-
'
|
|
1291
|
-
|
|
2591
|
+
'[FirmwareUpdateV4] DeviceFirmwareUpdateStatusGet unavailable during install: ',
|
|
2592
|
+
error
|
|
1292
2593
|
);
|
|
1293
2594
|
}
|
|
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
2595
|
}
|
|
2596
|
+
} catch (error) {
|
|
2597
|
+
lastError = error;
|
|
2598
|
+
if (this.isProtocolV2ReconnectIdentityError(error)) {
|
|
2599
|
+
throw error;
|
|
2600
|
+
}
|
|
2601
|
+
if (isProtocolV2TerminalInstallStatusError(error)) {
|
|
2602
|
+
throw error;
|
|
2603
|
+
}
|
|
2604
|
+
if (
|
|
2605
|
+
error instanceof HardwareError &&
|
|
2606
|
+
error.params?.firmwareUpdateCode === 'FirmwareInstallStatusUnavailable'
|
|
2607
|
+
) {
|
|
2608
|
+
throw error;
|
|
2609
|
+
}
|
|
2610
|
+
shouldReconnect = true;
|
|
2611
|
+
deviceInfo = undefined;
|
|
2612
|
+
resetMissingTargetStatusGrace();
|
|
2613
|
+
Log.log('Protocol V2 firmware install device readiness probe failed: ', error);
|
|
1302
2614
|
}
|
|
1303
2615
|
await wait(1000);
|
|
1304
2616
|
}
|
|
1305
2617
|
|
|
1306
2618
|
throw ERRORS.TypedError(
|
|
1307
2619
|
HardwareErrorCode.RuntimeError,
|
|
1308
|
-
`Protocol V2 firmware update status
|
|
2620
|
+
`Protocol V2 firmware update status not complete within ${
|
|
2621
|
+
PROTOCOL_V2_INSTALL_TIMEOUT / 1000
|
|
2622
|
+
}s: ${this.normalizeErrorMessage(lastError)}`
|
|
1309
2623
|
);
|
|
1310
2624
|
}
|
|
1311
2625
|
|
|
1312
2626
|
private async exitProtocolV2BootloaderToNormal() {
|
|
1313
|
-
this.
|
|
2627
|
+
await this.reconnectProtocolV2Device();
|
|
2628
|
+
const deviceInfo = await this.verifyProtocolV2ReconnectIdentity();
|
|
1314
2629
|
try {
|
|
1315
|
-
await this.
|
|
1316
|
-
|
|
1317
|
-
Log.log('Protocol V2 device is already in normal mode, skip normal reboot');
|
|
2630
|
+
if (await this.probeProtocolV2NormalMode(deviceInfo)) {
|
|
2631
|
+
Log.log('[FirmwareUpdateV4] device already returned to App mode; skip Normal reboot');
|
|
1318
2632
|
return;
|
|
1319
2633
|
}
|
|
1320
2634
|
} catch (error) {
|
|
1321
|
-
Log.log('
|
|
2635
|
+
Log.log('[FirmwareUpdateV4] unable to confirm App mode before Normal reboot: ', error);
|
|
1322
2636
|
}
|
|
1323
2637
|
await this.protocolV2Reboot(DeviceRebootType.Normal);
|
|
2638
|
+
this.protocolV2ExecutionInLoader = false;
|
|
2639
|
+
}
|
|
2640
|
+
|
|
2641
|
+
private async probeProtocolV2NormalMode(deviceInfo: ProtocolV2DeviceInfo) {
|
|
2642
|
+
const features = await this.device.probeProtocolV2RuntimeState(
|
|
2643
|
+
deviceInfo,
|
|
2644
|
+
PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT
|
|
2645
|
+
);
|
|
2646
|
+
return features.mode === 'normal' && !features.bootloaderMode;
|
|
1324
2647
|
}
|
|
1325
2648
|
|
|
1326
2649
|
private async waitForProtocolV2FinalFeatures() {
|
|
1327
2650
|
const features = await this.waitForProtocolV2ReconnectAndFeatures(
|
|
1328
|
-
|
|
2651
|
+
PROTOCOL_V2_FINAL_RECONNECT_TIMEOUT
|
|
1329
2652
|
);
|
|
1330
2653
|
|
|
2654
|
+
return this.getProtocolV2VersionResult(features);
|
|
2655
|
+
}
|
|
2656
|
+
|
|
2657
|
+
private getProtocolV2VersionResult(features: Features) {
|
|
1331
2658
|
const bootloaderVersion = getDeviceBootloaderVersion(features).join('.');
|
|
1332
2659
|
const bleVersion = getDeviceBLEFirmwareVersion(features).join('.');
|
|
1333
2660
|
const firmwareVersion = getDeviceFirmwareVersion(features).join('.');
|
|
2661
|
+
this.protocolV2LatestFinalFeatures = features;
|
|
1334
2662
|
if (firmwareVersion === '0.0.0') {
|
|
1335
2663
|
Log.warn(
|
|
1336
2664
|
'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 +2672,55 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1344
2672
|
};
|
|
1345
2673
|
}
|
|
1346
2674
|
|
|
2675
|
+
private async completeProtocolV2FinalVerification() {
|
|
2676
|
+
if (this.protocolV2ExecutionInLoader || this.isProtocolV2BootloaderMode()) {
|
|
2677
|
+
await this.exitProtocolV2BootloaderToNormal();
|
|
2678
|
+
}
|
|
2679
|
+
const versions = await this.waitForProtocolV2FinalFeatures();
|
|
2680
|
+
this.assertExpectedProtocolV2Versions();
|
|
2681
|
+
this.postTipMessage(FirmwareUpdateTipMessage.FirmwareUpdateCompleted);
|
|
2682
|
+
DevicePool.resetState();
|
|
2683
|
+
return versions;
|
|
2684
|
+
}
|
|
2685
|
+
|
|
1347
2686
|
private async waitForProtocolV2ReconnectAndFeatures(timeout: number) {
|
|
1348
2687
|
const startTime = Date.now();
|
|
1349
2688
|
let lastError: unknown;
|
|
2689
|
+
let shouldReconnect = true;
|
|
1350
2690
|
|
|
1351
2691
|
while (Date.now() - startTime < timeout) {
|
|
1352
2692
|
try {
|
|
1353
|
-
|
|
2693
|
+
if (shouldReconnect) {
|
|
2694
|
+
await this.reconnectProtocolV2Device();
|
|
2695
|
+
shouldReconnect = false;
|
|
2696
|
+
}
|
|
1354
2697
|
const deviceInfo = await requestProtocolV2DeviceInfo({
|
|
1355
2698
|
commands: this.device.getCommands(),
|
|
1356
|
-
timeoutMs:
|
|
1357
|
-
//
|
|
2699
|
+
timeoutMs: this.getProtocolV2DeviceInfoTimeout(),
|
|
2700
|
+
// Completion needs target versions only; keep scope aligned with the request.
|
|
1358
2701
|
request: PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST,
|
|
1359
2702
|
});
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
2703
|
+
this.assertProtocolV2DeviceInfoIdentity(deviceInfo);
|
|
2704
|
+
const features = await this.device.probeProtocolV2RuntimeState(
|
|
2705
|
+
deviceInfo,
|
|
2706
|
+
PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT
|
|
2707
|
+
);
|
|
2708
|
+
if (features.mode === 'normal' && !features.bootloaderMode) {
|
|
2709
|
+
return features;
|
|
1366
2710
|
}
|
|
1367
|
-
|
|
2711
|
+
lastError = ERRORS.TypedError(
|
|
2712
|
+
HardwareErrorCode.DeviceNotFound,
|
|
2713
|
+
'Protocol V2 device is still in bootloader mode'
|
|
2714
|
+
);
|
|
1368
2715
|
} catch (error) {
|
|
2716
|
+
if (this.isProtocolV2ReconnectIdentityError(error)) {
|
|
2717
|
+
throw error;
|
|
2718
|
+
}
|
|
2719
|
+
shouldReconnect = true;
|
|
1369
2720
|
lastError = error;
|
|
1370
2721
|
Log.log('Protocol V2 normal mode not ready, polling Ping: ', error);
|
|
1371
|
-
await wait(1000);
|
|
1372
2722
|
}
|
|
2723
|
+
await wait(1000);
|
|
1373
2724
|
}
|
|
1374
2725
|
|
|
1375
2726
|
throw ERRORS.TypedError(
|
|
@@ -1393,130 +2744,71 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1393
2744
|
DataManager.isBrowserWebUsb(DataManager.getSettings('env')) &&
|
|
1394
2745
|
devicesDescriptor.length === 1
|
|
1395
2746
|
) {
|
|
2747
|
+
const descriptor = devicesDescriptor[0];
|
|
2748
|
+
if (!this.protocolV2ExpectedSerialNumber && descriptor.path !== this.protocolV2ExpectedPath) {
|
|
2749
|
+
throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound);
|
|
2750
|
+
}
|
|
1396
2751
|
this.device.updateDescriptor(
|
|
1397
2752
|
{
|
|
1398
|
-
...
|
|
2753
|
+
...descriptor,
|
|
1399
2754
|
protocolType: PROTOCOL_V2_CONNECT_PROTOCOL,
|
|
1400
2755
|
},
|
|
1401
2756
|
true
|
|
1402
2757
|
);
|
|
1403
|
-
await this.
|
|
2758
|
+
await this.ensureProtocolV2DeviceAcquired();
|
|
1404
2759
|
this.device.commands.disposed = false;
|
|
1405
2760
|
this.device.getCommands().mainId = this.device.mainId ?? '';
|
|
1406
|
-
await this.device.initialize();
|
|
1407
2761
|
return;
|
|
1408
2762
|
}
|
|
1409
2763
|
|
|
1410
|
-
//
|
|
1411
|
-
//
|
|
2764
|
+
// Reinitialize every USB descriptor so DeviceInfo can provide the physical serial.
|
|
2765
|
+
// Enumeration order is not stable and must never decide the reconnect target.
|
|
1412
2766
|
const { deviceList } = await DevicePool.getDevices(devicesDescriptor, undefined, {
|
|
1413
2767
|
connectProtocol: PROTOCOL_V2_CONNECT_PROTOCOL,
|
|
1414
2768
|
});
|
|
1415
|
-
|
|
2769
|
+
const expectedSerialNumber = this.protocolV2ExpectedSerialNumber?.trim();
|
|
2770
|
+
const expectedPath = this.protocolV2ExpectedPath?.trim();
|
|
2771
|
+
const reconnectDevice = deviceList.find(candidate => {
|
|
2772
|
+
const candidateSerialNumber = candidate.getCurrentSerialNo?.().trim();
|
|
2773
|
+
if (expectedSerialNumber && candidateSerialNumber) {
|
|
2774
|
+
return candidateSerialNumber === expectedSerialNumber;
|
|
2775
|
+
}
|
|
2776
|
+
return !!expectedPath && candidate.getConnectId() === expectedPath;
|
|
2777
|
+
});
|
|
2778
|
+
if (!reconnectDevice) {
|
|
1416
2779
|
throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound);
|
|
1417
2780
|
}
|
|
1418
2781
|
|
|
1419
2782
|
Log.debug(
|
|
1420
|
-
'Protocol V2 firmware reconnect using
|
|
1421
|
-
|
|
2783
|
+
'Protocol V2 firmware reconnect using matched device:',
|
|
2784
|
+
reconnectDevice.getConnectId()
|
|
1422
2785
|
);
|
|
1423
|
-
this.device.updateFromCache(
|
|
1424
|
-
await this.
|
|
2786
|
+
this.device.updateFromCache(reconnectDevice);
|
|
2787
|
+
await this.ensureProtocolV2DeviceAcquired();
|
|
1425
2788
|
this.device.commands.disposed = false;
|
|
1426
2789
|
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
2790
|
}
|
|
1453
2791
|
|
|
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');
|
|
2792
|
+
/**
|
|
2793
|
+
* After acquiring a USB session, polling reuses its command channel. Reacquire only
|
|
2794
|
+
* after reboot or enumeration changes cause hasDeviceAcquire() to become false.
|
|
2795
|
+
*/
|
|
2796
|
+
private async ensureProtocolV2DeviceAcquired() {
|
|
2797
|
+
const commands = this.device.getCommands();
|
|
2798
|
+
if (this.device.hasDeviceAcquire() && !commands.disposed) {
|
|
2799
|
+
return;
|
|
1503
2800
|
}
|
|
1504
|
-
|
|
1505
|
-
|
|
2801
|
+
await this.device.acquire(PROTOCOL_V2_CONNECT_PROTOCOL, {
|
|
2802
|
+
throwOnRunPromiseError: true,
|
|
2803
|
+
});
|
|
1506
2804
|
}
|
|
1507
2805
|
|
|
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';
|
|
2806
|
+
private isProtocolV2ReconnectIdentityError(error: unknown) {
|
|
2807
|
+
// A serial can be temporarily unavailable while Loader starts, and BLE has no stable path.
|
|
2808
|
+
// Keep polling unless the device explicitly reports a different serial.
|
|
2809
|
+
return this.normalizeErrorMessage(error).includes(
|
|
2810
|
+
'Protocol V2 reconnect physical identity mismatch'
|
|
2811
|
+
);
|
|
1520
2812
|
}
|
|
1521
2813
|
|
|
1522
2814
|
private async fileWriteChunk(
|
|
@@ -1528,17 +2820,25 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1528
2820
|
progress: number | null
|
|
1529
2821
|
): Promise<TypedResponseMessage<'FilesystemFile'>> {
|
|
1530
2822
|
const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
|
|
1531
|
-
const writeRes = await typedCall(
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
2823
|
+
const writeRes = await typedCall(
|
|
2824
|
+
'FilesystemFileWrite',
|
|
2825
|
+
'FilesystemFile',
|
|
2826
|
+
{
|
|
2827
|
+
file: {
|
|
2828
|
+
path: filePath,
|
|
2829
|
+
offset,
|
|
2830
|
+
total_size: totalFileSize,
|
|
2831
|
+
data: chunk,
|
|
2832
|
+
},
|
|
2833
|
+
overwrite,
|
|
2834
|
+
append: false,
|
|
2835
|
+
ui_percentage: progress ?? undefined,
|
|
1537
2836
|
},
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
2837
|
+
{
|
|
2838
|
+
writeWithResponse: false,
|
|
2839
|
+
onWriteCompleted: () => undefined,
|
|
2840
|
+
}
|
|
2841
|
+
);
|
|
1542
2842
|
if (writeRes.type !== 'FilesystemFile') {
|
|
1543
2843
|
if ((writeRes as any).type === 'CallMethodError') {
|
|
1544
2844
|
if (((writeRes as any).message.error ?? '').indexOf(SESSION_ERROR) > -1) {
|
|
@@ -1554,9 +2854,10 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1554
2854
|
const env = DataManager.getSettings('env');
|
|
1555
2855
|
if (DataManager.isBleConnect(env)) {
|
|
1556
2856
|
await wait(3000);
|
|
1557
|
-
await this.acquireProtocolV2BleDevice();
|
|
1558
|
-
await this.device.initialize();
|
|
1559
2857
|
}
|
|
2858
|
+
await this.reconnectProtocolV2Device();
|
|
2859
|
+
await this.verifyProtocolV2ReconnectIdentity();
|
|
2860
|
+
await this.device.initialize();
|
|
1560
2861
|
await wait(2000);
|
|
1561
2862
|
}
|
|
1562
2863
|
|
|
@@ -1575,35 +2876,16 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1575
2876
|
targets: Array<{ target_id: number; path: string }>;
|
|
1576
2877
|
}) {
|
|
1577
2878
|
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
|
-
}
|
|
2879
|
+
const response: ProtocolV2FirmwareUpdateStartResponse = await commands.typedCall(
|
|
2880
|
+
'DeviceFirmwareUpdateRequest',
|
|
2881
|
+
'Success',
|
|
2882
|
+
{ targets },
|
|
2883
|
+
{ timeoutMs: PROTOCOL_V2_START_UPDATE_TIMEOUT }
|
|
2884
|
+
);
|
|
2885
|
+
// Success acknowledges that the device accepted installation. End confirmation
|
|
2886
|
+
// and begin status polling only after receiving this ACK.
|
|
1606
2887
|
this.postTipMessage(FirmwareUpdateTipMessage.FirmwareUpdating);
|
|
2888
|
+
this.postProgressMessage(0, 'installingFirmware');
|
|
1607
2889
|
return response;
|
|
1608
2890
|
}
|
|
1609
2891
|
|
|
@@ -1613,9 +2895,11 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1613
2895
|
const res = await typedCall('DeviceReboot', 'Success', {
|
|
1614
2896
|
reboot_type: rebootType,
|
|
1615
2897
|
});
|
|
2898
|
+
this.device.markProtocolV2Reboot(rebootType);
|
|
1616
2899
|
return res.message;
|
|
1617
2900
|
} catch (error) {
|
|
1618
2901
|
if (isProtocolV2DeviceDisconnectedError(error) || isProtocolV2ReconnectProbeError(error)) {
|
|
2902
|
+
this.device.markProtocolV2Reboot(rebootType);
|
|
1619
2903
|
return { message: 'Device rebooted successfully' };
|
|
1620
2904
|
}
|
|
1621
2905
|
throw error;
|