@onekeyfe/hd-core 1.2.0-alpha.13 → 1.2.0-alpha.130
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 +694 -0
- package/__tests__/DeviceEventRegistration.test.ts +49 -0
- package/__tests__/base64Data.test.ts +69 -0
- package/__tests__/chain-params-onekey-compatibility.test.ts +229 -0
- package/__tests__/check-all-firmware-release-protocol-v2.test.ts +830 -0
- package/__tests__/check-firmware-release-protocol-v2.test.ts +270 -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 +102 -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 +518 -0
- package/__tests__/device-pool-state.test.ts +191 -0
- package/__tests__/device-settings.test.ts +569 -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 +297 -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 +117 -0
- package/__tests__/deviceUploadNft.test.ts +345 -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 +126 -0
- package/__tests__/firmware-update/check-all-firmware-release.test.ts +175 -0
- package/__tests__/firmware-update/device-update-bootloader.test.ts +109 -0
- package/__tests__/firmware-update/firmware-artifact-source.test.ts +171 -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 +583 -0
- package/__tests__/firmware-update/firmware-prepared-resource-archive.test.ts +171 -0
- package/__tests__/firmware-update/firmware-update-bootloader-poll.test.ts +438 -0
- package/__tests__/firmware-update/firmware-update-capabilities.test.ts +13 -0
- package/__tests__/firmware-update/firmware-update-plan-bootloader-identity.test.ts +474 -0
- package/__tests__/firmware-update/firmware-update-plan.test.ts +819 -0
- package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +287 -0
- package/__tests__/firmware-update/firmware-update-prepared-resource-executors.test.ts +326 -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 +237 -0
- package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +130 -0
- package/__tests__/firmware-update/firmware-update-v4-parameters.test.ts +85 -0
- package/__tests__/firmware-update/firmware-upload-source.test.ts +70 -0
- package/__tests__/get-device-state.test.ts +617 -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 +140 -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 +8 -19
- package/__tests__/protocol-binding.test.ts +89 -0
- package/__tests__/protocol-v2-firmware-targets.test.ts +105 -0
- package/__tests__/protocol-v2-ping.test.ts +28 -0
- package/__tests__/protocol-v2-resources.test.ts +385 -0
- package/__tests__/protocol-v2-ui-lifecycle.test.ts +91 -0
- package/__tests__/protocol-v2-unlock-policy.test.ts +339 -0
- package/__tests__/protocol-v2.test.ts +8084 -2515
- package/__tests__/protocolV2FileWrite.test.ts +221 -3
- 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 +103 -0
- package/__tests__/request-context-logging.test.ts +16 -0
- package/__tests__/resourceBase64Boundary.test.ts +48 -0
- package/__tests__/search-devices.test.ts +191 -0
- package/__tests__/ton-sign-message.test.ts +84 -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 +14 -2
- 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 +13 -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 +57 -14
- 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 +1 -0
- 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 +1 -2
- package/dist/api/UploadPortfolio.d.ts.map +1 -1
- 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 +29 -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/FirmwarePreparedResourceArchive.d.ts +11 -0
- package/dist/api/firmware/FirmwarePreparedResourceArchive.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 +38 -0
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -0
- package/dist/api/firmware/getBinary.d.ts +15 -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/base64Data.d.ts +16 -0
- package/dist/api/helpers/base64Data.d.ts.map +1 -0
- 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 +11 -0
- package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
- package/dist/api/index.d.ts +5 -29
- 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 +4 -1
- 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 +2 -1
- package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceInfoGet.d.ts +2 -2
- 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 +30 -0
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts +3 -3
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
- 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 +0 -2
- 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 -2
- 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 +14 -5
- 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 +78 -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 +8 -2
- 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/call.d.ts +0 -7
- package/dist/events/call.d.ts.map +1 -1
- package/dist/events/core.d.ts +2 -2
- package/dist/events/core.d.ts.map +1 -1
- package/dist/events/device.d.ts +16 -2
- package/dist/events/device.d.ts.map +1 -1
- package/dist/events/iframe.d.ts +0 -1
- package/dist/events/iframe.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 +1764 -828
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11673 -4693
- package/dist/inject.d.ts +7 -3
- package/dist/inject.d.ts.map +1 -1
- package/dist/lowLevelInject.d.ts +1 -2
- 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 +33 -22
- 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 +67 -5
- 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.map +1 -1
- package/dist/types/api/index.d.ts +25 -35
- 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 +11 -93
- 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 +139 -8
- 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 +44 -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 -1
- 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 +39 -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 +5 -4
- 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 +38 -0
- package/dist/utils/pro2Nft.d.ts.map +1 -0
- package/dist/utils/pro2Wallpaper.d.ts +10 -0
- package/dist/utils/pro2Wallpaper.d.ts.map +1 -1
- 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 +6 -4
- package/src/api/BaseMethod.ts +45 -23
- package/src/api/CheckAllFirmwareRelease.ts +440 -4
- package/src/api/CheckBLEFirmwareRelease.ts +37 -5
- package/src/api/CheckBootloaderRelease.ts +37 -5
- package/src/api/CheckFirmwareRelease.ts +36 -9
- 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 +10 -5
- package/src/api/FirmwareUpdate.ts +30 -15
- package/src/api/FirmwareUpdateV2.ts +535 -169
- package/src/api/FirmwareUpdateV3.ts +349 -91
- package/src/api/FirmwareUpdateV4.ts +2300 -710
- 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 -12
- 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 +35 -5
- package/src/api/UploadPortfolio.ts +32 -4
- 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 +4 -0
- package/src/api/cardano/CardanoSignTransaction.ts +4 -0
- 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 +211 -3
- package/src/api/device/DeviceSupportFeatures.ts +4 -0
- package/src/api/device/DeviceUnlock.ts +21 -3
- package/src/api/device/DeviceUpdateBootloader.ts +152 -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 +305 -0
- package/src/api/firmware/FirmwareHostBinding.ts +123 -0
- package/src/api/firmware/FirmwareMemoryHost.ts +143 -0
- package/src/api/firmware/FirmwarePreparationError.ts +12 -0
- package/src/api/firmware/FirmwarePreparedResourceArchive.ts +196 -0
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +214 -77
- package/src/api/firmware/FirmwareUpdateCapabilities.ts +9 -0
- package/src/api/firmware/FirmwareUpdatePlan.ts +946 -0
- package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +486 -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/base64Data.ts +85 -0
- 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 +266 -42
- package/src/api/index.ts +6 -30
- 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 +6 -3
- package/src/api/protocol-v2/DeviceFirmwareUpdate.ts +13 -32
- package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +10 -6
- package/src/api/protocol-v2/DeviceGetOnboardingStatus.ts +6 -3
- package/src/api/protocol-v2/DeviceInfoGet.ts +11 -18
- 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 +245 -0
- package/src/api/protocol-v2/DeviceUploadWallpaper.ts +45 -21
- 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 -1
- 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 +10 -4
- 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 +0 -18
- package/src/core/deviceEventRegistration.ts +27 -0
- package/src/core/index.ts +419 -174
- package/src/core/uiPromiseRegistry.ts +63 -0
- package/src/data/messages/messages-protocol-v2.json +523 -449
- package/src/data/messages/messages.json +286 -1
- package/src/data-manager/DataManager.ts +162 -44
- package/src/data-manager/TransportManager.ts +7 -1
- package/src/data-manager/connectSettings.ts +11 -0
- package/src/device/Device.ts +909 -269
- package/src/device/DeviceCommands.ts +185 -160
- package/src/device/DeviceConnector.ts +51 -21
- package/src/device/DeviceFeaturesState.ts +25 -0
- package/src/device/DevicePool.ts +105 -23
- package/src/device/DeviceStateMapper.ts +418 -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 +209 -191
- package/src/deviceProfile/index.ts +0 -1
- package/src/deviceProfile/protocolV2DeviceIdentity.ts +35 -0
- package/src/events/call.ts +0 -6
- package/src/events/core.ts +0 -2
- package/src/events/device.ts +22 -1
- package/src/events/iframe.ts +0 -1
- package/src/events/logBlockEvent.ts +93 -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 -4
- package/src/inject.ts +89 -55
- package/src/lowLevelInject.ts +11 -7
- package/src/protocols/protocol-v2/deviceSession.ts +24 -0
- package/src/protocols/protocol-v2/features.ts +118 -52
- package/src/protocols/protocol-v2/firmware.ts +3 -1
- package/src/protocols/protocol-v2/index.ts +9 -3
- package/src/protocols/protocol-v2/resources.ts +171 -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 +136 -0
- package/src/protocols/protocol-v2/walletSession.ts +420 -49
- package/src/topLevelInject.ts +11 -6
- 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 +7 -0
- package/src/types/api/export.ts +33 -1
- package/src/types/api/firmwareUpdate.ts +85 -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 +7 -0
- package/src/types/api/index.ts +43 -73
- package/src/types/api/kaspaSignTransaction.ts +60 -4
- package/src/types/api/openWalletSession.ts +54 -0
- package/src/types/api/protocolV2.ts +23 -188
- package/src/types/api/sessionCache.ts +9 -4
- package/src/types/device.ts +211 -9
- package/src/types/params.ts +9 -2
- package/src/types/settings.ts +52 -25
- package/src/utils/assets.ts +4 -1
- package/src/utils/deviceFeaturesCompat.ts +191 -0
- package/src/utils/deviceFeaturesUtils.ts +42 -9
- package/src/utils/deviceInfoUtils.ts +29 -69
- package/src/utils/deviceSettings.ts +181 -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 +26 -7
- package/src/utils/networkUtils.ts +274 -16
- package/src/utils/pro2Nft.ts +165 -0
- package/src/utils/pro2Wallpaper.ts +44 -25
- package/src/utils/tracing.ts +3 -1
- package/src/utils/uiProgressThrottle.ts +63 -0
- package/tsconfig.type-tests.json +12 -0
- package/__tests__/RequestQueue.test.ts +0 -34
- 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 -6
- package/dist/api/protocol-v2/DeviceSessionGet.d.ts.map +0 -1
- package/dist/api/protocol-v2/DeviceSettingsGet.d.ts +0 -6
- package/dist/api/protocol-v2/DeviceSettingsGet.d.ts.map +0 -1
- package/dist/api/protocol-v2/DeviceSettingsPageShow.d.ts +0 -16
- package/dist/api/protocol-v2/DeviceSettingsPageShow.d.ts.map +0 -1
- package/dist/api/protocol-v2/DeviceSettingsSet.d.ts +0 -12
- package/dist/api/protocol-v2/DeviceSettingsSet.d.ts.map +0 -1
- package/dist/deviceProfile/buildDeviceProfile.d.ts +0 -21
- package/dist/deviceProfile/buildDeviceProfile.d.ts.map +0 -1
- package/dist/protocols/protocol-v2/unlockRetry.d.ts +0 -9
- package/dist/protocols/protocol-v2/unlockRetry.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 -15
- package/src/api/protocol-v2/DeviceSettingsGet.ts +0 -16
- package/src/api/protocol-v2/DeviceSettingsPageShow.ts +0 -59
- package/src/api/protocol-v2/DeviceSettingsSet.ts +0 -39
- package/src/deviceProfile/buildDeviceProfile.ts +0 -354
- package/src/protocols/protocol-v2/unlockRetry.ts +0 -46
- package/src/types/api/getDeviceInfo.ts +0 -101
|
@@ -1,21 +1,27 @@
|
|
|
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
|
-
import { FirmwareUpdateTipMessage, UI_REQUEST } from '../events/ui-request';
|
|
12
|
+
import { FirmwareUpdateTipMessage, UI_REQUEST, createUiMessage } from '../events/ui-request';
|
|
9
13
|
import { validateParams } from './helpers/paramsValidator';
|
|
10
14
|
import {
|
|
11
15
|
LoggerNames,
|
|
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,29 +31,68 @@ 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,
|
|
39
70
|
IFirmwareReleaseInfo,
|
|
40
71
|
IProtocolV2FirmwareComponent,
|
|
72
|
+
IProtocolV2ResourceManifestFile,
|
|
41
73
|
IVersionArray,
|
|
74
|
+
KnownDevice,
|
|
42
75
|
} from '../types';
|
|
76
|
+
import type { FirmwareByteSource } from './firmware/FirmwareArtifactSource';
|
|
77
|
+
import type {
|
|
78
|
+
FirmwareMemoryArtifact,
|
|
79
|
+
FirmwareMemoryArtifactEntry,
|
|
80
|
+
FirmwareUpdateV4MemoryHost,
|
|
81
|
+
} from './firmware/FirmwareMemoryHost';
|
|
43
82
|
|
|
44
83
|
const Log = getLogger(LoggerNames.Method);
|
|
45
84
|
|
|
85
|
+
// Restored after a rebase dropped the declaration while keeping its use in
|
|
86
|
+
// fileWriteChunk; without it that error branch throws ReferenceError instead of
|
|
87
|
+
// the intended typed session error.
|
|
46
88
|
const SESSION_ERROR = 'session not found';
|
|
47
|
-
const PROTOCOL_V2_BOOTLOADER_RECONNECT_TIMEOUT =
|
|
89
|
+
const PROTOCOL_V2_BOOTLOADER_RECONNECT_TIMEOUT = 90 * 1000;
|
|
90
|
+
const PROTOCOL_V2_FINAL_RECONNECT_TIMEOUT = 3 * 60 * 1000;
|
|
48
91
|
const PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT = 5 * 1000;
|
|
49
|
-
const
|
|
50
|
-
const PROTOCOL_V2_INSTALL_TIMEOUT =
|
|
92
|
+
const PROTOCOL_V2_FIRMWARE_STATUS_RESPONSE_TIMEOUT = 15 * 1000;
|
|
93
|
+
const PROTOCOL_V2_INSTALL_TIMEOUT = 8 * 60 * 1000;
|
|
94
|
+
const PROTOCOL_V2_INSTALL_STATUS_INITIAL_DELAY = 1000;
|
|
95
|
+
const PROTOCOL_V2_MISSING_TARGET_STATUS_GRACE_TIMEOUT = 30 * 1000;
|
|
51
96
|
const PROTOCOL_V2_TARGET_STATUS_PENDING = 0;
|
|
52
97
|
const PROTOCOL_V2_TARGET_STATUS_IN_PROGRESS = 1;
|
|
53
98
|
const PROTOCOL_V2_TARGET_STATUS_FINISHED = 2;
|
|
@@ -58,12 +103,85 @@ const PROTOCOL_V2_MIN_FILE_CHUNK_SIZE = 64;
|
|
|
58
103
|
const PROTOCOL_V2_CONNECT_RETRY_COUNT = 10;
|
|
59
104
|
const PROTOCOL_V2_CONNECT_POLL_INTERVAL = 500;
|
|
60
105
|
const PROTOCOL_V2_CONNECT_SINGLE_TIMEOUT = 75 * 1000;
|
|
61
|
-
const PROTOCOL_V2_DEVICE_INFO_READY_TIMEOUT =
|
|
106
|
+
const PROTOCOL_V2_DEVICE_INFO_READY_TIMEOUT = 30 * 1000;
|
|
62
107
|
const PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT = 3;
|
|
108
|
+
const PROTOCOL_V2_INSTALL_STATUS_CONFLICT_CODE = 'FirmwareInstallStatusConflict';
|
|
63
109
|
const PROTOCOL_V2_OKPP_HEADER_SIZE = 0x52a0;
|
|
64
110
|
const PROTOCOL_V2_OKPP_PAYLOAD_HASH_OFFSET = 0x200;
|
|
65
111
|
const PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET = 0x240;
|
|
66
112
|
const PROTOCOL_V2_OKPP_HASH_SIZE = 64;
|
|
113
|
+
const PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES = 1024 * 1024;
|
|
114
|
+
const PROTOCOL_V2_RESOURCE_FILE_MAX_COUNT = 512;
|
|
115
|
+
const PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES = 256 * 1024 * 1024;
|
|
116
|
+
|
|
117
|
+
const getProtocolV2LocalResourceArchivePath = (entryName: string) => {
|
|
118
|
+
const match = entryName.match(
|
|
119
|
+
/(?:^|\/)((?:bundles\/|loaders\/(?:bootloader|rom)\/).+\.okpkg)$/iu
|
|
120
|
+
);
|
|
121
|
+
return match?.[1];
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const PROTOCOL_V2_NEO_UNSUPPORTED_TARGETS = new Set<FirmwareUpdateV4Target>(['se03', 'se04']);
|
|
125
|
+
|
|
126
|
+
const getProtocolV2ZipEntrySizes = (entry: JSZip.JSZipObject) => {
|
|
127
|
+
// loadAsync records central-directory sizes on JSZip's documented private data object.
|
|
128
|
+
// Validate those bounds before calling async(), which allocates the decompressed entry.
|
|
129
|
+
const { compressedSize, uncompressedSize } = (entry as JSZipSizedEntry)._data ?? {};
|
|
130
|
+
if (
|
|
131
|
+
!Number.isSafeInteger(compressedSize) ||
|
|
132
|
+
Number(compressedSize) < 0 ||
|
|
133
|
+
!Number.isSafeInteger(uncompressedSize) ||
|
|
134
|
+
Number(uncompressedSize) <= 0
|
|
135
|
+
) {
|
|
136
|
+
throw ERRORS.TypedError(
|
|
137
|
+
HardwareErrorCode.RuntimeError,
|
|
138
|
+
`Protocol V2 local resource ZIP entry size is invalid: ${entry.name}`,
|
|
139
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
compressedSize: Number(compressedSize),
|
|
144
|
+
uncompressedSize: Number(uncompressedSize),
|
|
145
|
+
};
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
export function assertProtocolV2FirmwareTargetsSupported(
|
|
149
|
+
deviceType: EDeviceType | string | undefined,
|
|
150
|
+
params: FirmwareUpdateV4Params,
|
|
151
|
+
hasExplicitTargetSelection = false
|
|
152
|
+
) {
|
|
153
|
+
const requestedTargets = new Set(params.targetsToUpdate ?? []);
|
|
154
|
+
const unsupportedTargets = new Set(
|
|
155
|
+
Array.from(requestedTargets).filter(target => PROTOCOL_V2_NEO_UNSUPPORTED_TARGETS.has(target))
|
|
156
|
+
);
|
|
157
|
+
if (params.se03Binary && (!hasExplicitTargetSelection || requestedTargets.has('se03'))) {
|
|
158
|
+
unsupportedTargets.add('se03');
|
|
159
|
+
}
|
|
160
|
+
if (params.se04Binary && (!hasExplicitTargetSelection || requestedTargets.has('se04'))) {
|
|
161
|
+
unsupportedTargets.add('se04');
|
|
162
|
+
}
|
|
163
|
+
if (
|
|
164
|
+
params.componentArtifacts?.se03 &&
|
|
165
|
+
(!hasExplicitTargetSelection || requestedTargets.has('se03'))
|
|
166
|
+
) {
|
|
167
|
+
unsupportedTargets.add('se03');
|
|
168
|
+
}
|
|
169
|
+
if (
|
|
170
|
+
params.componentArtifacts?.se04 &&
|
|
171
|
+
(!hasExplicitTargetSelection || requestedTargets.has('se04'))
|
|
172
|
+
) {
|
|
173
|
+
unsupportedTargets.add('se04');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (!unsupportedTargets.size || deviceType === EDeviceType.Pro2) return;
|
|
177
|
+
|
|
178
|
+
const targetList = Array.from(unsupportedTargets).join(', ');
|
|
179
|
+
const message =
|
|
180
|
+
deviceType === EDeviceType.Neo
|
|
181
|
+
? `Neo only supports SE01 and SE02; unsupported firmware targets: ${targetList}`
|
|
182
|
+
: `Cannot safely update ${targetList} without a confirmed Pro2 device type`;
|
|
183
|
+
throw ERRORS.TypedError(HardwareErrorCode.DeviceNotSupportMethod, message);
|
|
184
|
+
}
|
|
67
185
|
|
|
68
186
|
const getProtocolV2DeviceTransferProgress = (
|
|
69
187
|
bytesBeforeChunk: number,
|
|
@@ -71,22 +189,14 @@ const getProtocolV2DeviceTransferProgress = (
|
|
|
71
189
|
totalBytes: number
|
|
72
190
|
) => {
|
|
73
191
|
if (!Number.isFinite(totalBytes) || totalBytes <= 0) {
|
|
74
|
-
return
|
|
192
|
+
return 0;
|
|
75
193
|
}
|
|
76
194
|
if (bytesBeforeChunk <= 0 && bytesAfterChunk < totalBytes) {
|
|
77
195
|
return 0;
|
|
78
196
|
}
|
|
79
|
-
if (bytesAfterChunk >= totalBytes) {
|
|
80
|
-
return 100;
|
|
81
|
-
}
|
|
82
197
|
return Math.min(Math.max(Math.ceil((bytesAfterChunk / totalBytes) * 100), 1), 99);
|
|
83
198
|
};
|
|
84
199
|
|
|
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
200
|
type ProtocolV2FirmwareUpdateStatusTarget = {
|
|
91
201
|
target_id: number | string;
|
|
92
202
|
status?: number | string;
|
|
@@ -94,46 +204,52 @@ type ProtocolV2FirmwareUpdateStatusTarget = {
|
|
|
94
204
|
path?: string;
|
|
95
205
|
};
|
|
96
206
|
|
|
97
|
-
type ProtocolV2FirmwareUpdateStartResponse =
|
|
98
|
-
| TypedResponseMessage<'Success'>
|
|
99
|
-
| TypedResponseMessage<'DeviceFirmwareUpdateStatus'>
|
|
100
|
-
| undefined;
|
|
101
|
-
|
|
102
207
|
type ProtocolV2TargetBinary = { fileName: string; binary: ArrayBuffer; targetId: number };
|
|
103
208
|
type ProtocolV2InstallItem = ProtocolV2TargetBinary & {
|
|
104
209
|
kind: ProtocolV2RemoteComponentTarget['kind'];
|
|
105
210
|
};
|
|
106
|
-
|
|
107
|
-
|
|
211
|
+
|
|
212
|
+
type ProtocolV2RemoteComponentBinary = ProtocolV2RemoteComponentTarget & {
|
|
213
|
+
binary: ArrayBuffer;
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
type ProtocolV2RemoteComponentTarget = {
|
|
217
|
+
fileName: string;
|
|
218
|
+
targetId: number;
|
|
219
|
+
kind: 'bootloader' | 'firmware';
|
|
108
220
|
};
|
|
109
221
|
|
|
110
|
-
type
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
222
|
+
type ProtocolV2InstallSource = {
|
|
223
|
+
fileName: string;
|
|
224
|
+
source: FirmwareByteSource;
|
|
225
|
+
targetId: number;
|
|
226
|
+
kind: ProtocolV2RemoteComponentTarget['kind'];
|
|
115
227
|
};
|
|
116
228
|
|
|
117
|
-
|
|
118
|
-
type ProtocolV2ResourceBundleBinary = {
|
|
229
|
+
type ProtocolV2ResourceBundleSource = {
|
|
119
230
|
name: string;
|
|
120
|
-
|
|
231
|
+
source: FirmwareByteSource;
|
|
121
232
|
devicePath: string;
|
|
122
|
-
/** 远端配置模式下的下载 URL(手动模式不填) */
|
|
123
|
-
url?: string;
|
|
124
233
|
version?: IVersionArray;
|
|
125
234
|
payloadHash?: string;
|
|
126
235
|
headerHash?: string;
|
|
127
236
|
};
|
|
128
237
|
|
|
129
|
-
type
|
|
238
|
+
type ProtocolV2LocalResourceArchive = {
|
|
130
239
|
binary: ArrayBuffer;
|
|
240
|
+
materializedEntries: FirmwareMemoryArtifactEntry[];
|
|
131
241
|
};
|
|
132
242
|
|
|
133
|
-
type
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
243
|
+
type JSZipSizedEntry = JSZip.JSZipObject & {
|
|
244
|
+
_data?: {
|
|
245
|
+
compressedSize?: unknown;
|
|
246
|
+
uncompressedSize?: unknown;
|
|
247
|
+
};
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
type ProtocolV2TransferBatch = {
|
|
251
|
+
installSources: ProtocolV2InstallSource[];
|
|
252
|
+
resourceSources: ProtocolV2ResourceBundleSource[];
|
|
137
253
|
};
|
|
138
254
|
|
|
139
255
|
type ProtocolV2OkppHeader = {
|
|
@@ -188,7 +304,29 @@ const PROTOCOL_V2_REMOTE_COMPONENT_TARGETS: Readonly<
|
|
|
188
304
|
},
|
|
189
305
|
};
|
|
190
306
|
|
|
191
|
-
const
|
|
307
|
+
const PROTOCOL_V2_FIRMWARE_STAGING_PATHS = new Set(
|
|
308
|
+
Object.values(PROTOCOL_V2_REMOTE_COMPONENT_TARGETS).map(
|
|
309
|
+
target => `${PROTOCOL_V2_FIRMWARE_STAGING_VOLUME}${target.fileName}`
|
|
310
|
+
)
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
const PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH = 'vol0:/loaders/bootloader/boot_resource.okpkg';
|
|
314
|
+
const PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_STAGING_PATH = `${PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH}.staging`;
|
|
315
|
+
|
|
316
|
+
const isProtocolV2BootResourcePackagePath = (devicePath: string) =>
|
|
317
|
+
typeof devicePath === 'string' &&
|
|
318
|
+
devicePath.replace(/^vol0:(?!\/)/i, 'vol0:/').toLowerCase() ===
|
|
319
|
+
PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH;
|
|
320
|
+
|
|
321
|
+
const resolveProtocolV2ResourceWritePath = (devicePath: string) =>
|
|
322
|
+
isProtocolV2BootResourcePackagePath(devicePath)
|
|
323
|
+
? PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_STAGING_PATH
|
|
324
|
+
: devicePath;
|
|
325
|
+
|
|
326
|
+
const PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID = new Map<
|
|
327
|
+
number,
|
|
328
|
+
Exclude<FirmwareUpdateV4Target, 'boot_resources'>
|
|
329
|
+
>([
|
|
192
330
|
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER, 'boot'],
|
|
193
331
|
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1, 'app_v1'],
|
|
194
332
|
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P2, 'app_v2'],
|
|
@@ -199,11 +337,24 @@ const PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID = new Map<number, FirmwareUpdateV4T
|
|
|
199
337
|
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE04, 'se04'],
|
|
200
338
|
]);
|
|
201
339
|
|
|
340
|
+
const PROTOCOL_V2_INSTALL_TARGET_BY_UPDATE_TARGET = new Map<
|
|
341
|
+
Exclude<FirmwareUpdateV4Target, 'resource'>,
|
|
342
|
+
ProtocolV2RemoteComponentTarget
|
|
343
|
+
>(
|
|
344
|
+
Object.values(PROTOCOL_V2_REMOTE_COMPONENT_TARGETS).map(target => [
|
|
345
|
+
PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(target.targetId) as Exclude<
|
|
346
|
+
FirmwareUpdateV4Target,
|
|
347
|
+
'resource'
|
|
348
|
+
>,
|
|
349
|
+
target,
|
|
350
|
+
])
|
|
351
|
+
);
|
|
352
|
+
|
|
202
353
|
const PROTOCOL_V2_ROMLOADER_UNSUPPORTED_MESSAGE =
|
|
203
354
|
'FW_MGMT_TARGET_ROMLOADER is not accepted by the current Pro2 bootloader update request. Flash romloader with the loader-specific flow instead of firmwareUpdateV4.';
|
|
204
355
|
|
|
205
|
-
// hd-transport
|
|
206
|
-
//
|
|
356
|
+
// hd-transport historically decodes scalar enums as enum-name strings.
|
|
357
|
+
// Map them back to firmware protocol values before internal comparisons.
|
|
207
358
|
const PROTOCOL_V2_TARGET_ID_BY_DECODED_NAME = new Map<string, number>(
|
|
208
359
|
Object.entries(ProtocolV2FirmwareTargetType).map(([key, value]) => [key, value])
|
|
209
360
|
);
|
|
@@ -229,27 +380,20 @@ const isProtocolV2ReconnectProbeError = (error: unknown) => {
|
|
|
229
380
|
);
|
|
230
381
|
};
|
|
231
382
|
|
|
232
|
-
const
|
|
383
|
+
const isProtocolV2FirmwareStatusEndpointUnavailable = (error: unknown) => {
|
|
233
384
|
const message = getProtocolV2UnknownErrorText(error).toLowerCase();
|
|
234
385
|
return (
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
message.includes('
|
|
238
|
-
(message.includes('response timeout') && message.includes('devicefirmwareupdatestatusget')) ||
|
|
239
|
-
message.includes('device not found') ||
|
|
240
|
-
message.includes('transportnotfound')
|
|
386
|
+
message.includes('handler not registered') ||
|
|
387
|
+
message.includes('message handler not found') ||
|
|
388
|
+
message.includes('unsupported message')
|
|
241
389
|
);
|
|
242
390
|
};
|
|
243
391
|
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
message.includes('libusb_transfer_timed_out') ||
|
|
250
|
-
(message.includes('response timeout') && message.includes('devicefirmwareupdaterequest'))
|
|
251
|
-
);
|
|
252
|
-
};
|
|
392
|
+
const isProtocolV2TerminalInstallStatusError = (error: unknown) =>
|
|
393
|
+
error instanceof HardwareError &&
|
|
394
|
+
(error.errorCode === HardwareErrorCode.FirmwareError ||
|
|
395
|
+
error.errorCode === HardwareErrorCode.FirmwareVerificationFailed ||
|
|
396
|
+
error.params?.firmwareUpdateCode === PROTOCOL_V2_INSTALL_STATUS_CONFLICT_CODE);
|
|
253
397
|
|
|
254
398
|
const isProtocolV2TargetStatusFinished = (status: ProtocolV2FirmwareUpdateStatusTarget['status']) =>
|
|
255
399
|
normalizeProtocolV2TargetStatus(status) === PROTOCOL_V2_TARGET_STATUS_FINISHED;
|
|
@@ -379,23 +523,41 @@ const parseProtocolV2OkppHeader = (bytes: Uint8Array): ProtocolV2OkppHeader | nu
|
|
|
379
523
|
};
|
|
380
524
|
};
|
|
381
525
|
|
|
526
|
+
export const isProtocolV2FirmwareFingerprintValid = (
|
|
527
|
+
binary: ArrayBuffer | Uint8Array,
|
|
528
|
+
fingerprint: string | undefined
|
|
529
|
+
) => {
|
|
530
|
+
const expectedFingerprint = normalizeProtocolV2Hex(fingerprint);
|
|
531
|
+
if (!expectedFingerprint) return true;
|
|
532
|
+
return bytesToHex(sha256(toProtocolV2Bytes(binary))) === expectedFingerprint;
|
|
533
|
+
};
|
|
534
|
+
|
|
382
535
|
export const assertProtocolV2ReconnectIdentity = (
|
|
383
|
-
|
|
384
|
-
|
|
536
|
+
expectedSerialNumber?: string,
|
|
537
|
+
actualSerialNumber?: string,
|
|
538
|
+
expectedPath?: string,
|
|
539
|
+
actualPath?: string
|
|
385
540
|
) => {
|
|
386
|
-
if (
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
541
|
+
if (expectedSerialNumber && actualSerialNumber) {
|
|
542
|
+
if (actualSerialNumber !== expectedSerialNumber) {
|
|
543
|
+
throw ERRORS.TypedError(
|
|
544
|
+
HardwareErrorCode.DeviceNotFound,
|
|
545
|
+
`Protocol V2 reconnect physical identity mismatch: expected ${expectedSerialNumber}, received ${actualSerialNumber}`
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
return;
|
|
392
549
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
`Protocol V2 reconnect identity mismatch: expected ${expectedDeviceId}, received ${actualDeviceId}`
|
|
397
|
-
);
|
|
550
|
+
|
|
551
|
+
if (expectedPath && actualPath && expectedPath === actualPath) {
|
|
552
|
+
return;
|
|
398
553
|
}
|
|
554
|
+
|
|
555
|
+
throw ERRORS.TypedError(
|
|
556
|
+
HardwareErrorCode.DeviceNotFound,
|
|
557
|
+
`Protocol V2 reconnect physical identity unavailable: expected path ${
|
|
558
|
+
expectedPath ?? 'unknown'
|
|
559
|
+
}, received ${actualPath ?? 'unknown'}`
|
|
560
|
+
);
|
|
399
561
|
};
|
|
400
562
|
|
|
401
563
|
/**
|
|
@@ -403,20 +565,65 @@ export const assertProtocolV2ReconnectIdentity = (
|
|
|
403
565
|
*
|
|
404
566
|
* It intentionally does not fall back to FirmwareUpdateV3/V1 behavior:
|
|
405
567
|
* - upload uses FilesystemFileWrite
|
|
406
|
-
* - install uses DeviceFirmwareUpdateRequest
|
|
568
|
+
* - install uses DeviceFirmwareUpdateStage followed by an empty DeviceFirmwareUpdateRequest
|
|
407
569
|
* - completion waits for target status to finish, reboots to normal, then polls DeviceInfo
|
|
408
570
|
*/
|
|
409
571
|
export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareUpdateV4Params> {
|
|
410
|
-
private
|
|
572
|
+
private protocolV2ExpectedSerialNumber?: string;
|
|
573
|
+
|
|
574
|
+
private protocolV2ExpectedPath?: string;
|
|
575
|
+
|
|
576
|
+
private protocolV2HasExplicitTargetSelection = false;
|
|
577
|
+
|
|
578
|
+
getSupportedProtocols() {
|
|
579
|
+
return ['V2'] as const;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
private protocolV2PreparedSources: FirmwareByteSource[] = [];
|
|
583
|
+
|
|
584
|
+
private protocolV2ExecutionInLoader = false;
|
|
585
|
+
|
|
586
|
+
private protocolV2BootResourceStagingSafe = false;
|
|
587
|
+
|
|
588
|
+
private protocolV2CompletedTargetVersions = new Map<number, number>();
|
|
589
|
+
|
|
590
|
+
private protocolV2LatestFinalFeatures?: Features;
|
|
591
|
+
|
|
592
|
+
private protocolV2FinalStatusVerified = false;
|
|
593
|
+
|
|
594
|
+
private protocolV2InstallBaselineVersions = new Map<number, string>();
|
|
595
|
+
|
|
596
|
+
private protocolV2LastRuntimeProbeFeatures?: Features;
|
|
411
597
|
|
|
412
598
|
init() {
|
|
413
599
|
this.allowDeviceMode = [UI_REQUEST.BOOTLOADER, UI_REQUEST.NOT_INITIALIZE];
|
|
414
600
|
this.requireDeviceMode = [];
|
|
601
|
+
this.unlockPolicy = 'unlock-before-run';
|
|
415
602
|
this.useDevicePassphraseState = false;
|
|
416
603
|
this.skipForceUpdateCheck = true;
|
|
417
604
|
|
|
418
605
|
const { payload } = this;
|
|
419
606
|
|
|
607
|
+
const removedResourceInputs = ['resourceFiles', 'resourceBundleArtifacts'].filter(input =>
|
|
608
|
+
Object.prototype.hasOwnProperty.call(payload, input)
|
|
609
|
+
);
|
|
610
|
+
if (removedResourceInputs.length > 0) {
|
|
611
|
+
throw ERRORS.TypedError(
|
|
612
|
+
HardwareErrorCode.CallMethodInvalidParameter,
|
|
613
|
+
`Protocol V2 ${removedResourceInputs.join(
|
|
614
|
+
' and '
|
|
615
|
+
)} inputs are no longer supported; use resourceArchiveBinary for local updates or preparedPlan with hostBindingGeneration for remote updates`
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
if (payload.hostBindingGeneration !== undefined && !payload.preparedPlan) {
|
|
619
|
+
throw ERRORS.TypedError(
|
|
620
|
+
HardwareErrorCode.CallMethodInvalidParameter,
|
|
621
|
+
'Protocol V2 hostBindingGeneration requires preparedPlan for remote updates; use direct binaries or resourceArchiveBinary for local updates'
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
this.protocolV2HasExplicitTargetSelection = payload.targetsToUpdate !== undefined;
|
|
626
|
+
|
|
420
627
|
if (typeof payload.retryCount !== 'number') {
|
|
421
628
|
payload.retryCount = PROTOCOL_V2_CONNECT_RETRY_COUNT;
|
|
422
629
|
}
|
|
@@ -442,13 +649,112 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
442
649
|
{ name: 'se02Binary', type: 'buffer' },
|
|
443
650
|
{ name: 'se03Binary', type: 'buffer' },
|
|
444
651
|
{ name: 'se04Binary', type: 'buffer' },
|
|
652
|
+
{ name: 'resourceArchiveBinary', type: 'buffer' },
|
|
445
653
|
{ name: 'firmwareType', type: 'string' },
|
|
446
654
|
{ name: 'targetsToUpdate', type: 'array', allowEmpty: true },
|
|
447
655
|
{ name: 'platform', type: 'string' },
|
|
448
|
-
{ name: '
|
|
656
|
+
{ name: 'expectedDeviceId', type: 'string' },
|
|
449
657
|
]);
|
|
658
|
+
if (
|
|
659
|
+
payload.expectedDeviceId !== undefined &&
|
|
660
|
+
(payload.expectedDeviceId.length === 0 || payload.expectedDeviceId.length > 160)
|
|
661
|
+
) {
|
|
662
|
+
throw ERRORS.TypedError(
|
|
663
|
+
HardwareErrorCode.CallMethodInvalidParameter,
|
|
664
|
+
'Protocol V2 expected device identity is invalid'
|
|
665
|
+
);
|
|
666
|
+
}
|
|
667
|
+
const preparedPlan = payload.preparedPlan
|
|
668
|
+
? validateFirmwareUpdatePreparedPlan(payload.preparedPlan)
|
|
669
|
+
: undefined;
|
|
670
|
+
const hasLocalArtifacts = [
|
|
671
|
+
payload.bootloaderBinary,
|
|
672
|
+
payload.romloaderBinary,
|
|
673
|
+
payload.applicationP1Binary,
|
|
674
|
+
payload.applicationP2Binary,
|
|
675
|
+
payload.coprocessorBinary,
|
|
676
|
+
payload.se01Binary,
|
|
677
|
+
payload.se02Binary,
|
|
678
|
+
payload.se03Binary,
|
|
679
|
+
payload.se04Binary,
|
|
680
|
+
payload.resourceArchiveBinary,
|
|
681
|
+
].some(Boolean);
|
|
682
|
+
if (preparedPlan && hasLocalArtifacts) {
|
|
683
|
+
throw ERRORS.TypedError(
|
|
684
|
+
HardwareErrorCode.CallMethodInvalidParameter,
|
|
685
|
+
'Prepared firmware plans cannot be combined with legacy or local firmware inputs'
|
|
686
|
+
);
|
|
687
|
+
}
|
|
688
|
+
let { artifactReader } = payload;
|
|
689
|
+
if (preparedPlan) {
|
|
690
|
+
artifactReader = resolveFirmwareUpdateHostBinding(
|
|
691
|
+
payload.hostBindingGeneration,
|
|
692
|
+
preparedPlan.preparedPlanDigest
|
|
693
|
+
).artifactReader;
|
|
694
|
+
}
|
|
695
|
+
if (preparedPlan) {
|
|
696
|
+
assertFirmwareUpdatePreparedPlanBinding({
|
|
697
|
+
preparedPlan,
|
|
698
|
+
executor: 'v4',
|
|
699
|
+
platform: payload.platform,
|
|
700
|
+
scopeTargets: [],
|
|
701
|
+
bindings: [],
|
|
702
|
+
});
|
|
703
|
+
if (payload.componentArtifacts) {
|
|
704
|
+
const componentBindings: Array<{
|
|
705
|
+
target: Exclude<FirmwareUpdateV4Target, 'resource'>;
|
|
706
|
+
artifact: FirmwareArtifactReference;
|
|
707
|
+
}> = Object.entries(payload.componentArtifacts).flatMap(([target, artifact]) =>
|
|
708
|
+
artifact
|
|
709
|
+
? [
|
|
710
|
+
{
|
|
711
|
+
target: target as Exclude<FirmwareUpdateV4Target, 'resource'>,
|
|
712
|
+
artifact: artifact as FirmwareArtifactReference,
|
|
713
|
+
},
|
|
714
|
+
]
|
|
715
|
+
: []
|
|
716
|
+
);
|
|
717
|
+
assertFirmwareUpdatePreparedPlanBinding({
|
|
718
|
+
preparedPlan,
|
|
719
|
+
executor: 'v4',
|
|
720
|
+
platform: payload.platform,
|
|
721
|
+
scopeTargets: componentBindings.map(binding => binding.target),
|
|
722
|
+
bindings: componentBindings,
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
const preparedExpectedTargetVersions = preparedPlan?.artifacts.reduce<
|
|
728
|
+
NonNullable<FirmwareUpdateV4Params['expectedTargetVersions']>
|
|
729
|
+
>((result, artifact) => {
|
|
730
|
+
if (
|
|
731
|
+
artifact.role === 'component' &&
|
|
732
|
+
artifact.target !== 'resource' &&
|
|
733
|
+
artifact.targetVersion &&
|
|
734
|
+
PROTOCOL_V2_INSTALL_TARGET_BY_UPDATE_TARGET.has(
|
|
735
|
+
artifact.target as Exclude<FirmwareUpdateV4Target, 'resource'>
|
|
736
|
+
)
|
|
737
|
+
) {
|
|
738
|
+
result[artifact.target as FirmwareUpdateV4Target] = artifact.targetVersion;
|
|
739
|
+
}
|
|
740
|
+
return result;
|
|
741
|
+
}, {});
|
|
742
|
+
const localTargetsToUpdate = payload.targetsToUpdate?.map((target: FirmwareUpdateV4Target) =>
|
|
743
|
+
target === 'boot_resources' ? 'resource' : target
|
|
744
|
+
);
|
|
745
|
+
if (
|
|
746
|
+
payload.resourceArchiveBinary &&
|
|
747
|
+
localTargetsToUpdate &&
|
|
748
|
+
!localTargetsToUpdate.includes('resource')
|
|
749
|
+
) {
|
|
750
|
+
localTargetsToUpdate.push('resource');
|
|
751
|
+
}
|
|
752
|
+
// 本地 ZIP 本身就是明确的资源升级请求,不要求调用方重复声明 target。
|
|
753
|
+
const resolvedLocalTargetsToUpdate =
|
|
754
|
+
localTargetsToUpdate ?? (payload.resourceArchiveBinary ? ['resource'] : undefined);
|
|
450
755
|
|
|
451
756
|
this.params = {
|
|
757
|
+
preparedPlan,
|
|
452
758
|
chunkSize: payload.chunkSize,
|
|
453
759
|
forcedUpdateRes: payload.forcedUpdateRes,
|
|
454
760
|
bootloaderBinary: payload.bootloaderBinary,
|
|
@@ -460,20 +766,36 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
460
766
|
se02Binary: payload.se02Binary,
|
|
461
767
|
se03Binary: payload.se03Binary,
|
|
462
768
|
se04Binary: payload.se04Binary,
|
|
463
|
-
|
|
464
|
-
firmwareType: payload.firmwareType,
|
|
465
|
-
targetsToUpdate:
|
|
769
|
+
resourceArchiveBinary: payload.resourceArchiveBinary,
|
|
770
|
+
firmwareType: preparedPlan?.firmwareType ?? payload.firmwareType,
|
|
771
|
+
targetsToUpdate: preparedPlan
|
|
772
|
+
? ([...preparedPlan.targetsToUpdate] as FirmwareUpdateV4Target[])
|
|
773
|
+
: resolvedLocalTargetsToUpdate,
|
|
774
|
+
expectedTargetVersions: preparedPlan
|
|
775
|
+
? preparedExpectedTargetVersions
|
|
776
|
+
: payload.expectedTargetVersions,
|
|
466
777
|
platform: payload.platform,
|
|
778
|
+
expectedDeviceId: payload.expectedDeviceId,
|
|
779
|
+
artifactReader,
|
|
780
|
+
componentArtifacts: preparedPlan ? undefined : payload.componentArtifacts,
|
|
467
781
|
};
|
|
468
782
|
}
|
|
469
783
|
|
|
470
|
-
private getProtocolV2FirmwareChunkSize() {
|
|
784
|
+
private getProtocolV2FirmwareChunkSize(direction: 'read' | 'write', filePath?: string) {
|
|
471
785
|
const payloadChunkSize = Number(this.params?.chunkSize);
|
|
472
786
|
const env = DataManager.getSettings('env');
|
|
473
|
-
const
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
787
|
+
const isBle = this.params?.platform === 'native' || (env && DataManager.isBleConnect(env));
|
|
788
|
+
let maxChunkSize = PROTOCOL_V2_WEBUSB_FILE_CHUNK_SIZE;
|
|
789
|
+
if (isBle) {
|
|
790
|
+
if (direction === 'read') {
|
|
791
|
+
maxChunkSize = PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE;
|
|
792
|
+
} else {
|
|
793
|
+
// Firmware staging writes tolerate a larger BLE chunk than generic filesystem writes.
|
|
794
|
+
maxChunkSize = PROTOCOL_V2_FIRMWARE_STAGING_PATHS.has(filePath ?? '')
|
|
795
|
+
? PROTOCOL_V2_BLE_FIRMWARE_FILE_CHUNK_SIZE
|
|
796
|
+
: PROTOCOL_V2_BLE_FILE_CHUNK_SIZE;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
477
799
|
if (!Number.isFinite(payloadChunkSize) || payloadChunkSize <= 0) {
|
|
478
800
|
return maxChunkSize;
|
|
479
801
|
}
|
|
@@ -484,200 +806,1034 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
484
806
|
}
|
|
485
807
|
|
|
486
808
|
async run() {
|
|
487
|
-
if (!this.device.isProtocolV2()) {
|
|
488
|
-
throw ERRORS.TypedError(
|
|
489
|
-
HardwareErrorCode.RuntimeError,
|
|
490
|
-
'firmwareUpdateV4 requires a Protocol V2 device'
|
|
491
|
-
);
|
|
492
|
-
}
|
|
493
|
-
|
|
494
809
|
Log.debug('FirmwareUpdateV4 strategy: Protocol V2');
|
|
495
810
|
return this.runProtocolV2();
|
|
496
811
|
}
|
|
497
812
|
|
|
498
813
|
private async runProtocolV2() {
|
|
814
|
+
await this.captureProtocolV2PhysicalIdentity();
|
|
499
815
|
const deviceFeatures = await this.getProtocolV2DeviceFeatures();
|
|
500
|
-
this.
|
|
816
|
+
this.protocolV2InstallBaselineVersions =
|
|
817
|
+
this.getProtocolV2ObservableTargetVersions(deviceFeatures);
|
|
818
|
+
this.protocolV2LastRuntimeProbeFeatures = undefined;
|
|
819
|
+
const currentDeviceType = this.device.getCurrentDeviceType();
|
|
820
|
+
const capabilityDeviceType =
|
|
821
|
+
currentDeviceType === EDeviceType.Pro2 || currentDeviceType === EDeviceType.Neo
|
|
822
|
+
? currentDeviceType
|
|
823
|
+
: getDeviceType(deviceFeatures);
|
|
824
|
+
assertProtocolV2FirmwareTargetsSupported(
|
|
825
|
+
capabilityDeviceType,
|
|
826
|
+
this.params,
|
|
827
|
+
this.protocolV2HasExplicitTargetSelection
|
|
828
|
+
);
|
|
501
829
|
const deviceFirmwareType = getFirmwareType(deviceFeatures);
|
|
502
830
|
const firmwareType = this.params.firmwareType ?? deviceFirmwareType;
|
|
831
|
+
this.validateExpectedTargetVersions();
|
|
832
|
+
const wantsResources = !!this.params.targetsToUpdate?.includes('resource');
|
|
833
|
+
|
|
834
|
+
if (
|
|
835
|
+
!this.params.preparedPlan &&
|
|
836
|
+
this.params.resourceArchiveBinary &&
|
|
837
|
+
this.params.targetsToUpdate?.includes('resource')
|
|
838
|
+
) {
|
|
839
|
+
const localMemoryHost = await this.prepareProtocolV2LocalMemoryHost({
|
|
840
|
+
features: deviceFeatures,
|
|
841
|
+
firmwareType,
|
|
842
|
+
});
|
|
843
|
+
try {
|
|
844
|
+
return await this.runProtocolV2PreparedArtifacts(deviceFeatures, firmwareType);
|
|
845
|
+
} finally {
|
|
846
|
+
localMemoryHost.release();
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
const hasPreparedComponentArtifacts = Object.values(this.params.componentArtifacts ?? {}).some(
|
|
851
|
+
Boolean
|
|
852
|
+
);
|
|
853
|
+
if (this.params.preparedPlan || hasPreparedComponentArtifacts) {
|
|
854
|
+
return this.runProtocolV2PreparedArtifacts(deviceFeatures, firmwareType);
|
|
855
|
+
}
|
|
503
856
|
|
|
504
857
|
let fwBinaryMap: ProtocolV2TargetBinary[] = [];
|
|
505
858
|
let bootloaderBinary: ArrayBuffer | null = null;
|
|
506
859
|
let installItems: ProtocolV2InstallItem[] | undefined;
|
|
860
|
+
let resourceMemoryHost: FirmwareUpdateV4MemoryHost | undefined;
|
|
507
861
|
try {
|
|
508
862
|
this.postTipMessage(FirmwareUpdateTipMessage.StartDownloadFirmware);
|
|
509
863
|
fwBinaryMap = this.collectExplicitTargetBinaries();
|
|
510
864
|
bootloaderBinary = this.prepareBootloaderBinary();
|
|
511
|
-
|
|
865
|
+
const explicitInstallItems = this.buildProtocolV2InstallItems({
|
|
866
|
+
bootloaderBinary,
|
|
867
|
+
fwBinaryMap,
|
|
868
|
+
});
|
|
869
|
+
const missingFirmwareTargets = this.getMissingProtocolV2FirmwareTargets(explicitInstallItems);
|
|
870
|
+
const needsRemoteFirmware = this.params.targetsToUpdate?.length
|
|
871
|
+
? missingFirmwareTargets.length > 0
|
|
872
|
+
: explicitInstallItems.length === 0;
|
|
873
|
+
const needsSdkManagedArtifacts = needsRemoteFirmware || wantsResources;
|
|
874
|
+
if (
|
|
875
|
+
needsSdkManagedArtifacts &&
|
|
876
|
+
(this.params.artifactReader ||
|
|
877
|
+
DataManager.getSettings('firmwareManifestMode') === 'external-only')
|
|
878
|
+
) {
|
|
879
|
+
throw ERRORS.TypedError(
|
|
880
|
+
HardwareErrorCode.RuntimeError,
|
|
881
|
+
'Protocol V2 firmware artifacts must be prepared by the external firmware host',
|
|
882
|
+
{
|
|
883
|
+
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
884
|
+
}
|
|
885
|
+
);
|
|
886
|
+
}
|
|
887
|
+
if (needsSdkManagedArtifacts) {
|
|
888
|
+
// Remote updates must use a freshly fetched config before any reboot or file write.
|
|
889
|
+
await DataManager.forceReloadData({
|
|
890
|
+
requireResources: wantsResources,
|
|
891
|
+
resourceDeviceType:
|
|
892
|
+
capabilityDeviceType === EDeviceType.Neo ? EDeviceType.Neo : EDeviceType.Pro2,
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
if (needsRemoteFirmware) {
|
|
512
896
|
const remoteBinaries = await this.prepareRemoteProtocolV2Binaries(
|
|
513
897
|
firmwareType,
|
|
514
|
-
deviceFeatures
|
|
898
|
+
deviceFeatures,
|
|
899
|
+
explicitInstallItems
|
|
515
900
|
);
|
|
516
901
|
bootloaderBinary = remoteBinaries.bootloaderBinary;
|
|
517
902
|
fwBinaryMap = remoteBinaries.fwBinaryMap;
|
|
518
903
|
installItems = remoteBinaries.installItems;
|
|
904
|
+
} else {
|
|
905
|
+
const selectedInstallItems = this.filterProtocolV2LocalInstallItems(explicitInstallItems);
|
|
906
|
+
bootloaderBinary =
|
|
907
|
+
selectedInstallItems.find(item => item.kind === 'bootloader')?.binary ?? null;
|
|
908
|
+
fwBinaryMap = selectedInstallItems
|
|
909
|
+
.filter(item => item.kind === 'firmware')
|
|
910
|
+
.map(item => ({
|
|
911
|
+
fileName: item.fileName,
|
|
912
|
+
binary: item.binary,
|
|
913
|
+
targetId: item.targetId,
|
|
914
|
+
}));
|
|
915
|
+
}
|
|
916
|
+
if (wantsResources) {
|
|
917
|
+
this.params.resourceArchiveBinary = await this.downloadRemoteProtocolV2ResourceArchive(
|
|
918
|
+
deviceFeatures,
|
|
919
|
+
firmwareType
|
|
920
|
+
);
|
|
921
|
+
resourceMemoryHost = await this.prepareProtocolV2LocalMemoryHost({
|
|
922
|
+
features: deviceFeatures,
|
|
923
|
+
firmwareType,
|
|
924
|
+
availableInstallItems: installItems ?? explicitInstallItems,
|
|
925
|
+
});
|
|
519
926
|
}
|
|
520
927
|
this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
521
928
|
} catch (err) {
|
|
522
|
-
|
|
929
|
+
resourceMemoryHost?.release();
|
|
930
|
+
if (
|
|
931
|
+
typeof err === 'object' &&
|
|
932
|
+
err !== null &&
|
|
933
|
+
'params' in err &&
|
|
934
|
+
(err as HardwareError).params?.firmwareUpdateCode === 'FirmwareArtifactsNotPrepared'
|
|
935
|
+
) {
|
|
936
|
+
throw err;
|
|
937
|
+
}
|
|
938
|
+
if (err instanceof HardwareError && err.errorCode === HardwareErrorCode.NetworkError) {
|
|
939
|
+
throw err;
|
|
940
|
+
}
|
|
941
|
+
throw normalizeFirmwarePreparationError(err);
|
|
523
942
|
}
|
|
524
943
|
|
|
525
|
-
|
|
944
|
+
if (resourceMemoryHost) {
|
|
945
|
+
try {
|
|
946
|
+
return await this.runProtocolV2PreparedArtifacts(deviceFeatures, firmwareType, false);
|
|
947
|
+
} finally {
|
|
948
|
+
resourceMemoryHost.release();
|
|
949
|
+
}
|
|
950
|
+
}
|
|
526
951
|
|
|
527
|
-
if (!bootloaderBinary && fwBinaryMap.length === 0 && !
|
|
952
|
+
if (!bootloaderBinary && fwBinaryMap.length === 0 && !installItems?.length) {
|
|
528
953
|
throw ERRORS.TypedError(
|
|
529
954
|
HardwareErrorCode.FirmwareUpdateDownloadFailed,
|
|
530
955
|
'No firmware to update'
|
|
531
956
|
);
|
|
532
957
|
}
|
|
533
958
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
await this.executeProtocolV2Update({
|
|
959
|
+
return this.executeProtocolV2Update({
|
|
537
960
|
fwBinaryMap,
|
|
538
961
|
bootloaderBinary,
|
|
539
962
|
...(installItems ? { installItems } : undefined),
|
|
540
|
-
...(resourceBundles?.length ? { resourceBundles } : undefined),
|
|
541
963
|
});
|
|
542
|
-
|
|
543
|
-
await this.exitProtocolV2BootloaderToNormal();
|
|
544
|
-
|
|
545
|
-
const versions = await this.waitForProtocolV2FinalFeatures();
|
|
546
|
-
this.postTipMessage(FirmwareUpdateTipMessage.FirmwareUpdateCompleted);
|
|
547
|
-
DevicePool.resetState();
|
|
548
|
-
|
|
549
|
-
return versions;
|
|
550
964
|
}
|
|
551
965
|
|
|
552
|
-
private async
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
966
|
+
private async openProtocolV2PreparedSource(artifact: FirmwareArtifactReference) {
|
|
967
|
+
const source = await openFirmwareByteSource({
|
|
968
|
+
artifact,
|
|
969
|
+
reader: this.params.artifactReader,
|
|
970
|
+
});
|
|
971
|
+
if (!source) {
|
|
972
|
+
throw ERRORS.TypedError(
|
|
973
|
+
HardwareErrorCode.RuntimeError,
|
|
974
|
+
`Firmware artifact ${artifact.artifactRef} is not prepared`,
|
|
975
|
+
{
|
|
976
|
+
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
977
|
+
}
|
|
978
|
+
);
|
|
559
979
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
private prepareBootloaderBinary(): ArrayBuffer | null {
|
|
564
|
-
return this.params.bootloaderBinary ?? null;
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
private hasExplicitProtocolV2Payload(fwBinaryMap: ProtocolV2TargetBinary[]) {
|
|
568
|
-
return (
|
|
569
|
-
!!this.params.resourceBundleFiles?.length ||
|
|
570
|
-
!!this.params.bootloaderBinary ||
|
|
571
|
-
fwBinaryMap.length > 0
|
|
572
|
-
);
|
|
980
|
+
this.protocolV2PreparedSources.push(source);
|
|
981
|
+
return source;
|
|
573
982
|
}
|
|
574
983
|
|
|
575
|
-
private
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
const installItems: ProtocolV2InstallItem[] = [];
|
|
583
|
-
|
|
584
|
-
if (bootloaderBinary) {
|
|
585
|
-
installItems.push({
|
|
586
|
-
fileName: 'bootloader.bin',
|
|
587
|
-
binary: bootloaderBinary,
|
|
588
|
-
targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER,
|
|
589
|
-
kind: 'bootloader',
|
|
590
|
-
});
|
|
984
|
+
private async openProtocolV2MemorySource(binary: ArrayBuffer) {
|
|
985
|
+
const source = await openFirmwareByteSource({ binary });
|
|
986
|
+
if (!source) {
|
|
987
|
+
throw ERRORS.TypedError(
|
|
988
|
+
HardwareErrorCode.RuntimeError,
|
|
989
|
+
'Protocol V2 firmware binary is empty'
|
|
990
|
+
);
|
|
591
991
|
}
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
return installItems;
|
|
992
|
+
this.protocolV2PreparedSources.push(source);
|
|
993
|
+
return source;
|
|
595
994
|
}
|
|
596
995
|
|
|
597
|
-
private
|
|
598
|
-
const
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
const orderedKeys = [
|
|
602
|
-
...(release.installOrder ?? []),
|
|
603
|
-
...Object.keys(components).filter(key => !release.installOrder?.includes(key)),
|
|
604
|
-
];
|
|
605
|
-
|
|
606
|
-
return orderedKeys
|
|
607
|
-
.map(key => {
|
|
608
|
-
const component = components[key];
|
|
609
|
-
return component ? ([key, component] as const) : undefined;
|
|
610
|
-
})
|
|
611
|
-
.filter((entry): entry is readonly [string, IProtocolV2FirmwareComponent] => !!entry);
|
|
996
|
+
private async closeProtocolV2PreparedSources() {
|
|
997
|
+
const sources = this.protocolV2PreparedSources.splice(0);
|
|
998
|
+
await Promise.all(sources.map(source => source.close().catch(() => undefined)));
|
|
612
999
|
}
|
|
613
1000
|
|
|
614
|
-
private
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
1001
|
+
private async prepareProtocolV2InstallSources(
|
|
1002
|
+
firmwareType: EFirmwareType,
|
|
1003
|
+
features: Features
|
|
1004
|
+
): Promise<ProtocolV2InstallSource[]> {
|
|
1005
|
+
if (this.params.preparedPlan) {
|
|
1006
|
+
const requestedTargets = new Set(
|
|
1007
|
+
this.params.preparedPlan.targetsToUpdate.filter(
|
|
1008
|
+
(target): target is Exclude<FirmwareUpdateV4Target, 'resource'> => target !== 'resource'
|
|
1009
|
+
)
|
|
1010
|
+
);
|
|
1011
|
+
const installSources: ProtocolV2InstallSource[] = [];
|
|
1012
|
+
const preparedTargets = new Set<Exclude<FirmwareUpdateV4Target, 'resource'>>();
|
|
1013
|
+
for (const artifact of this.params.preparedPlan.artifacts) {
|
|
1014
|
+
if (artifact.target !== 'resource') {
|
|
1015
|
+
const target = artifact.target as Exclude<FirmwareUpdateV4Target, 'resource'>;
|
|
1016
|
+
const installTarget = PROTOCOL_V2_INSTALL_TARGET_BY_UPDATE_TARGET.get(target);
|
|
1017
|
+
if (
|
|
1018
|
+
!requestedTargets.has(target) ||
|
|
1019
|
+
artifact.role !== 'component' ||
|
|
1020
|
+
artifact.container !== 'raw' ||
|
|
1021
|
+
!installTarget ||
|
|
1022
|
+
preparedTargets.has(target)
|
|
1023
|
+
) {
|
|
1024
|
+
throw ERRORS.TypedError(
|
|
1025
|
+
HardwareErrorCode.RuntimeError,
|
|
1026
|
+
`Protocol V2 prepared component artifact is invalid: ${artifact.artifactId}`,
|
|
1027
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1028
|
+
);
|
|
1029
|
+
}
|
|
1030
|
+
installSources.push({
|
|
1031
|
+
...installTarget,
|
|
1032
|
+
source: await this.openProtocolV2PreparedSource(artifact.artifact),
|
|
1033
|
+
});
|
|
1034
|
+
preparedTargets.add(target);
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
const missingTarget = Array.from(requestedTargets).find(
|
|
1038
|
+
target => !preparedTargets.has(target)
|
|
620
1039
|
);
|
|
1040
|
+
if (missingTarget) {
|
|
1041
|
+
throw ERRORS.TypedError(
|
|
1042
|
+
HardwareErrorCode.RuntimeError,
|
|
1043
|
+
`Protocol V2 ${missingTarget} artifact is not prepared`,
|
|
1044
|
+
{
|
|
1045
|
+
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
1046
|
+
artifactName: missingTarget,
|
|
1047
|
+
}
|
|
1048
|
+
);
|
|
1049
|
+
}
|
|
1050
|
+
return installSources;
|
|
621
1051
|
}
|
|
622
|
-
|
|
623
|
-
|
|
1052
|
+
|
|
1053
|
+
const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
|
|
1054
|
+
if (!release) {
|
|
624
1055
|
throw ERRORS.TypedError(
|
|
625
1056
|
HardwareErrorCode.RuntimeError,
|
|
626
|
-
|
|
1057
|
+
'Protocol V2 firmware release is unavailable'
|
|
627
1058
|
);
|
|
628
1059
|
}
|
|
629
|
-
|
|
630
|
-
|
|
1060
|
+
const componentArtifacts = this.params.componentArtifacts ?? {};
|
|
1061
|
+
const requestedTargets = new Set(
|
|
1062
|
+
this.params.targetsToUpdate ?? (Object.keys(componentArtifacts) as FirmwareUpdateV4Target[])
|
|
1063
|
+
);
|
|
1064
|
+
const installSources: ProtocolV2InstallSource[] = [];
|
|
1065
|
+
const preparedTargets = new Set<FirmwareUpdateV4Target>();
|
|
631
1066
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
1067
|
+
for (const [key, component] of this.getRemoteComponentEntries(release)) {
|
|
1068
|
+
const target = this.getRemoteComponentTarget(key, component);
|
|
1069
|
+
const updateTarget = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(target.targetId);
|
|
1070
|
+
if (updateTarget && updateTarget !== 'resource' && requestedTargets.has(updateTarget)) {
|
|
1071
|
+
const artifact = componentArtifacts[updateTarget];
|
|
1072
|
+
if (!artifact) {
|
|
1073
|
+
throw ERRORS.TypedError(
|
|
1074
|
+
HardwareErrorCode.RuntimeError,
|
|
1075
|
+
`Protocol V2 ${updateTarget} artifact is not prepared`,
|
|
1076
|
+
{
|
|
1077
|
+
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
1078
|
+
artifactName: updateTarget,
|
|
1079
|
+
}
|
|
1080
|
+
);
|
|
1081
|
+
}
|
|
1082
|
+
installSources.push({
|
|
1083
|
+
...target,
|
|
1084
|
+
source: await this.openProtocolV2PreparedSource(artifact),
|
|
1085
|
+
});
|
|
1086
|
+
preparedTargets.add(updateTarget);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
const unknownTarget = Array.from(requestedTargets).find(
|
|
1091
|
+
target => target !== 'resource' && !preparedTargets.has(target)
|
|
1092
|
+
);
|
|
1093
|
+
if (unknownTarget) {
|
|
1094
|
+
throw ERRORS.TypedError(
|
|
1095
|
+
HardwareErrorCode.RuntimeError,
|
|
1096
|
+
`Protocol V2 release does not contain requested target ${unknownTarget}`
|
|
1097
|
+
);
|
|
1098
|
+
}
|
|
1099
|
+
return installSources;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
private async prepareProtocolV2LocalMemoryHost({
|
|
1103
|
+
features,
|
|
1104
|
+
firmwareType,
|
|
1105
|
+
availableInstallItems = this.buildProtocolV2InstallItems({
|
|
1106
|
+
bootloaderBinary: this.prepareBootloaderBinary(),
|
|
1107
|
+
fwBinaryMap: this.collectExplicitTargetBinaries(),
|
|
1108
|
+
}),
|
|
1109
|
+
}: {
|
|
1110
|
+
features: Features;
|
|
1111
|
+
firmwareType: EFirmwareType;
|
|
1112
|
+
availableInstallItems?: ProtocolV2InstallItem[];
|
|
1113
|
+
}): Promise<FirmwareUpdateV4MemoryHost> {
|
|
1114
|
+
const requestedComponentTargets = new Set(
|
|
1115
|
+
(this.params.targetsToUpdate ?? []).filter(
|
|
1116
|
+
(target): target is Exclude<FirmwareUpdateV4Target, 'resource' | 'boot_resources'> =>
|
|
1117
|
+
target !== 'resource' && target !== 'boot_resources'
|
|
1118
|
+
)
|
|
1119
|
+
);
|
|
1120
|
+
const localComponentTargets = new Set(
|
|
1121
|
+
availableInstallItems.flatMap(item => {
|
|
1122
|
+
const target = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(item.targetId);
|
|
1123
|
+
return target ? [target] : [];
|
|
1124
|
+
})
|
|
1125
|
+
);
|
|
1126
|
+
const missingTarget = Array.from(requestedComponentTargets).find(
|
|
1127
|
+
target => !localComponentTargets.has(target)
|
|
1128
|
+
);
|
|
1129
|
+
if (missingTarget) {
|
|
1130
|
+
throw ERRORS.TypedError(
|
|
1131
|
+
HardwareErrorCode.RuntimeError,
|
|
1132
|
+
`Protocol V2 local update has no binary for requested target ${missingTarget}`,
|
|
1133
|
+
{
|
|
1134
|
+
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
1135
|
+
artifactName: missingTarget,
|
|
1136
|
+
}
|
|
1137
|
+
);
|
|
1138
|
+
}
|
|
1139
|
+
const installItems = this.filterProtocolV2LocalInstallItems(availableInstallItems);
|
|
1140
|
+
|
|
1141
|
+
const planArtifacts: Parameters<typeof buildProtocolV2LocalFirmwareUpdatePlan>[0]['artifacts'] =
|
|
1142
|
+
[];
|
|
1143
|
+
const memoryArtifacts: FirmwareMemoryArtifact[] = [];
|
|
1144
|
+
for (const item of installItems) {
|
|
1145
|
+
const target = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(item.targetId);
|
|
1146
|
+
if (!target || item.binary.byteLength <= 0) {
|
|
1147
|
+
throw ERRORS.TypedError(
|
|
1148
|
+
HardwareErrorCode.RuntimeError,
|
|
1149
|
+
`Protocol V2 local firmware artifact is invalid: ${item.fileName}`,
|
|
1150
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1151
|
+
);
|
|
1152
|
+
}
|
|
1153
|
+
const artifactId = `component:${target}`;
|
|
1154
|
+
planArtifacts.push({
|
|
1155
|
+
artifactId,
|
|
1156
|
+
target,
|
|
1157
|
+
container: 'raw',
|
|
1158
|
+
logicalName: item.fileName,
|
|
1159
|
+
expectedSize: item.binary.byteLength,
|
|
1160
|
+
expectedSha256: bytesToHex(sha256(new Uint8Array(item.binary))),
|
|
1161
|
+
...(this.params.expectedTargetVersions?.[target]
|
|
1162
|
+
? { targetVersion: this.params.expectedTargetVersions[target] }
|
|
1163
|
+
: {}),
|
|
1164
|
+
});
|
|
1165
|
+
memoryArtifacts.push({ artifactId, binary: item.binary });
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
const resourceArchive = await this.prepareProtocolV2LocalResourceArchive(
|
|
1169
|
+
this.params.resourceArchiveBinary as ArrayBuffer
|
|
1170
|
+
);
|
|
1171
|
+
const resourceArtifactId = 'resource:archive';
|
|
1172
|
+
planArtifacts.push({
|
|
1173
|
+
artifactId: resourceArtifactId,
|
|
1174
|
+
target: 'resource',
|
|
1175
|
+
container: 'zip',
|
|
1176
|
+
logicalName: 'protocol-v2-local-resource-archive',
|
|
1177
|
+
expectedSize: resourceArchive.binary.byteLength,
|
|
1178
|
+
expectedSha256: bytesToHex(sha256(new Uint8Array(resourceArchive.binary))),
|
|
1179
|
+
});
|
|
1180
|
+
memoryArtifacts.push({
|
|
1181
|
+
artifactId: resourceArtifactId,
|
|
1182
|
+
binary: resourceArchive.binary,
|
|
1183
|
+
materializedEntries: resourceArchive.materializedEntries,
|
|
1184
|
+
});
|
|
1185
|
+
|
|
1186
|
+
const plan = buildProtocolV2LocalFirmwareUpdatePlan({
|
|
1187
|
+
features,
|
|
1188
|
+
firmwareType,
|
|
1189
|
+
platform: this.params.platform,
|
|
1190
|
+
artifacts: planArtifacts,
|
|
1191
|
+
});
|
|
1192
|
+
let memoryHost: FirmwareUpdateV4MemoryHost | undefined;
|
|
1193
|
+
try {
|
|
1194
|
+
memoryHost = prepareFirmwareUpdateV4MemoryHost({
|
|
1195
|
+
sdk: {
|
|
1196
|
+
prepareFirmwareUpdatePlan,
|
|
1197
|
+
registerFirmwareUpdateHostBinding,
|
|
1198
|
+
unregisterFirmwareUpdateHostBinding,
|
|
1199
|
+
},
|
|
1200
|
+
plan,
|
|
1201
|
+
artifacts: memoryArtifacts,
|
|
1202
|
+
});
|
|
1203
|
+
const preparedPlan = validateFirmwareUpdatePreparedPlan(memoryHost.preparedPlan);
|
|
1204
|
+
assertFirmwareUpdatePreparedPlanBinding({
|
|
1205
|
+
preparedPlan,
|
|
1206
|
+
executor: 'v4',
|
|
1207
|
+
platform: this.params.platform,
|
|
1208
|
+
scopeTargets: [],
|
|
1209
|
+
bindings: [],
|
|
1210
|
+
});
|
|
1211
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
1212
|
+
preparedPlan,
|
|
1213
|
+
deviceIdentity: this.protocolV2ExpectedSerialNumber,
|
|
1214
|
+
deviceModel: this.getProtocolV2PreparedPlanDeviceModel(features),
|
|
1215
|
+
});
|
|
1216
|
+
const hostBinding = resolveFirmwareUpdateHostBinding(
|
|
1217
|
+
memoryHost.hostBindingGeneration,
|
|
1218
|
+
preparedPlan.preparedPlanDigest
|
|
1219
|
+
);
|
|
1220
|
+
this.params.preparedPlan = preparedPlan;
|
|
1221
|
+
this.params.targetsToUpdate = [...preparedPlan.targetsToUpdate] as FirmwareUpdateV4Target[];
|
|
1222
|
+
this.params.artifactReader = hostBinding.artifactReader;
|
|
1223
|
+
this.params.componentArtifacts = undefined;
|
|
1224
|
+
return memoryHost;
|
|
1225
|
+
} catch (error) {
|
|
1226
|
+
memoryHost?.release();
|
|
1227
|
+
throw error;
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
private async prepareProtocolV2LocalResourceArchive(
|
|
1232
|
+
binary: ArrayBuffer
|
|
1233
|
+
): Promise<ProtocolV2LocalResourceArchive> {
|
|
1234
|
+
if (binary.byteLength <= 0 || binary.byteLength > PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES) {
|
|
1235
|
+
throw ERRORS.TypedError(
|
|
1236
|
+
HardwareErrorCode.RuntimeError,
|
|
1237
|
+
'Protocol V2 local resource ZIP archive size is invalid',
|
|
1238
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1239
|
+
);
|
|
1240
|
+
}
|
|
1241
|
+
let zip: JSZip;
|
|
1242
|
+
try {
|
|
1243
|
+
zip = await JSZip.loadAsync(binary);
|
|
1244
|
+
} catch {
|
|
1245
|
+
throw ERRORS.TypedError(
|
|
1246
|
+
HardwareErrorCode.RuntimeError,
|
|
1247
|
+
'Protocol V2 resource ZIP cannot be parsed',
|
|
1248
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1249
|
+
);
|
|
1250
|
+
}
|
|
1251
|
+
const zipEntries = Object.values(zip.files);
|
|
1252
|
+
if (
|
|
1253
|
+
zipEntries.some(entry => entry.unsafeOriginalName && entry.unsafeOriginalName !== entry.name)
|
|
1254
|
+
) {
|
|
1255
|
+
throw ERRORS.TypedError(
|
|
1256
|
+
HardwareErrorCode.RuntimeError,
|
|
1257
|
+
'Protocol V2 local resource ZIP contains an unsafe entry path',
|
|
1258
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1259
|
+
);
|
|
1260
|
+
}
|
|
1261
|
+
const entries = zipEntries.filter(entry => !entry.dir);
|
|
1262
|
+
if (entries.length === 0) {
|
|
1263
|
+
throw ERRORS.TypedError(
|
|
1264
|
+
HardwareErrorCode.RuntimeError,
|
|
1265
|
+
'Protocol V2 local resource ZIP entry set is invalid',
|
|
1266
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1267
|
+
);
|
|
1268
|
+
}
|
|
1269
|
+
const manifestEntry = entries.find(entry => entry.name.split('/').pop() === 'manifest.json');
|
|
1270
|
+
let manifestBinary: ArrayBuffer | undefined;
|
|
1271
|
+
let manifestDirectory = '';
|
|
1272
|
+
let selectedFiles: IProtocolV2ResourceManifestFile[];
|
|
1273
|
+
if (manifestEntry) {
|
|
1274
|
+
if (
|
|
1275
|
+
getProtocolV2ZipEntrySizes(manifestEntry).uncompressedSize >
|
|
1276
|
+
PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES
|
|
1277
|
+
) {
|
|
1278
|
+
throw ERRORS.TypedError(
|
|
1279
|
+
HardwareErrorCode.RuntimeError,
|
|
1280
|
+
'Protocol V2 local resource manifest size is invalid',
|
|
1281
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1282
|
+
);
|
|
1283
|
+
}
|
|
1284
|
+
manifestBinary = await manifestEntry.async('arraybuffer');
|
|
1285
|
+
if (
|
|
1286
|
+
manifestBinary.byteLength <= 0 ||
|
|
1287
|
+
manifestBinary.byteLength > PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES
|
|
1288
|
+
) {
|
|
1289
|
+
throw ERRORS.TypedError(
|
|
1290
|
+
HardwareErrorCode.RuntimeError,
|
|
1291
|
+
'Protocol V2 local resource manifest size is invalid',
|
|
1292
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1293
|
+
);
|
|
1294
|
+
}
|
|
1295
|
+
let manifestValue: unknown;
|
|
1296
|
+
try {
|
|
1297
|
+
manifestValue = JSON.parse(new TextDecoder().decode(manifestBinary));
|
|
1298
|
+
} catch (error) {
|
|
1299
|
+
throw ERRORS.TypedError(
|
|
1300
|
+
HardwareErrorCode.RuntimeError,
|
|
1301
|
+
`Protocol V2 local resource manifest is invalid: ${String(error)}`,
|
|
1302
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1303
|
+
);
|
|
1304
|
+
}
|
|
1305
|
+
selectedFiles = selectProtocolV2ResourceManifestFiles({
|
|
1306
|
+
manifest: parseProtocolV2ResourceManifest(manifestValue),
|
|
1307
|
+
targetsToUpdate: this.params.targetsToUpdate ?? [],
|
|
1308
|
+
});
|
|
1309
|
+
manifestDirectory = manifestEntry.name.slice(0, -'manifest.json'.length);
|
|
1310
|
+
} else {
|
|
1311
|
+
selectedFiles = entries.flatMap(entry => {
|
|
1312
|
+
const archivePath = getProtocolV2LocalResourceArchivePath(entry.name);
|
|
1313
|
+
if (!archivePath) return [];
|
|
1314
|
+
return [
|
|
1315
|
+
{
|
|
1316
|
+
archive_path: archivePath,
|
|
1317
|
+
original_name: archivePath.split('/').pop() ?? archivePath,
|
|
1318
|
+
device_path: `vol0:/${archivePath}`,
|
|
1319
|
+
size: getProtocolV2ZipEntrySizes(entry).uncompressedSize,
|
|
1320
|
+
sha256: '',
|
|
1321
|
+
},
|
|
1322
|
+
];
|
|
1323
|
+
});
|
|
1324
|
+
}
|
|
1325
|
+
if (selectedFiles.length === 0 || selectedFiles.length > PROTOCOL_V2_RESOURCE_FILE_MAX_COUNT) {
|
|
1326
|
+
throw ERRORS.TypedError(
|
|
1327
|
+
HardwareErrorCode.RuntimeError,
|
|
1328
|
+
'Protocol V2 local resource ZIP has no resource packages',
|
|
1329
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1330
|
+
);
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
let totalSize = 0;
|
|
1334
|
+
const materializedEntries: FirmwareMemoryArtifactEntry[] = [];
|
|
1335
|
+
const normalizedFiles: IProtocolV2ResourceManifestFile[] = [];
|
|
1336
|
+
for (const file of selectedFiles) {
|
|
1337
|
+
const entry = manifestEntry
|
|
1338
|
+
? zip.file(`${manifestDirectory}${file.archive_path}`)
|
|
1339
|
+
: entries.find(
|
|
1340
|
+
candidate => getProtocolV2LocalResourceArchivePath(candidate.name) === file.archive_path
|
|
1341
|
+
);
|
|
1342
|
+
if (!entry) {
|
|
1343
|
+
throw ERRORS.TypedError(
|
|
1344
|
+
HardwareErrorCode.RuntimeError,
|
|
1345
|
+
`Protocol V2 local resource ZIP is missing ${file.archive_path}`,
|
|
1346
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1347
|
+
);
|
|
1348
|
+
}
|
|
1349
|
+
const { uncompressedSize } = getProtocolV2ZipEntrySizes(entry);
|
|
1350
|
+
totalSize += uncompressedSize;
|
|
1351
|
+
if (uncompressedSize !== file.size || totalSize > PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES) {
|
|
1352
|
+
throw ERRORS.TypedError(
|
|
1353
|
+
HardwareErrorCode.RuntimeError,
|
|
1354
|
+
`Protocol V2 local resource file declared size is invalid: ${file.archive_path}`,
|
|
1355
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1356
|
+
);
|
|
1357
|
+
}
|
|
1358
|
+
const fileBinary = await entry.async('arraybuffer');
|
|
1359
|
+
const digest = bytesToHex(sha256(new Uint8Array(fileBinary)));
|
|
1360
|
+
if (
|
|
1361
|
+
fileBinary.byteLength !== file.size ||
|
|
1362
|
+
(file.sha256 && digest !== file.sha256.toLowerCase())
|
|
1363
|
+
) {
|
|
1364
|
+
throw ERRORS.TypedError(
|
|
1365
|
+
HardwareErrorCode.RuntimeError,
|
|
1366
|
+
`Protocol V2 local resource file does not match manifest: ${file.archive_path}`,
|
|
1367
|
+
{ firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch' }
|
|
1368
|
+
);
|
|
1369
|
+
}
|
|
1370
|
+
normalizedFiles.push({ ...file, sha256: digest });
|
|
1371
|
+
materializedEntries.push({ entryName: file.archive_path, binary: fileBinary });
|
|
1372
|
+
}
|
|
1373
|
+
manifestBinary ??= new TextEncoder().encode(JSON.stringify({ files: normalizedFiles })).buffer;
|
|
1374
|
+
materializedEntries.unshift({ entryName: 'manifest.json', binary: manifestBinary });
|
|
1375
|
+
return { binary, materializedEntries };
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
private async prepareProtocolV2ResourceSources(): Promise<ProtocolV2ResourceBundleSource[]> {
|
|
1379
|
+
const resourceRequested = this.params.targetsToUpdate?.includes('resource') ?? false;
|
|
1380
|
+
if (!resourceRequested) {
|
|
1381
|
+
return [];
|
|
1382
|
+
}
|
|
1383
|
+
const archiveSources = await this.prepareProtocolV2ResourceArchiveSources();
|
|
1384
|
+
if (archiveSources) {
|
|
1385
|
+
return archiveSources;
|
|
1386
|
+
}
|
|
1387
|
+
throw ERRORS.TypedError(
|
|
1388
|
+
HardwareErrorCode.RuntimeError,
|
|
1389
|
+
'Protocol V2 resource archive is not prepared',
|
|
1390
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1391
|
+
);
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
private async prepareProtocolV2ResourceArchiveSources(): Promise<
|
|
1395
|
+
ProtocolV2ResourceBundleSource[] | undefined
|
|
1396
|
+
> {
|
|
1397
|
+
const archiveArtifacts =
|
|
1398
|
+
this.params.preparedPlan?.artifacts.filter(
|
|
1399
|
+
artifact =>
|
|
1400
|
+
artifact.role === 'resourceBundle' &&
|
|
1401
|
+
artifact.target === 'resource' &&
|
|
1402
|
+
artifact.container === 'zip'
|
|
1403
|
+
) ?? [];
|
|
1404
|
+
if (archiveArtifacts.length === 0) {
|
|
1405
|
+
return undefined;
|
|
1406
|
+
}
|
|
1407
|
+
if (archiveArtifacts.length !== 1) {
|
|
1408
|
+
throw ERRORS.TypedError(
|
|
1409
|
+
HardwareErrorCode.RuntimeError,
|
|
1410
|
+
'Protocol V2 prepared plan must contain exactly one resource archive',
|
|
1411
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1412
|
+
);
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
const archiveArtifact = archiveArtifacts[0];
|
|
1416
|
+
const archiveSource = await this.openProtocolV2PreparedSource(archiveArtifact.artifact);
|
|
1417
|
+
if (archiveSource.size > PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES) {
|
|
1418
|
+
throw ERRORS.TypedError(
|
|
1419
|
+
HardwareErrorCode.RuntimeError,
|
|
1420
|
+
'Protocol V2 prepared resource archive exceeds the total size limit',
|
|
1421
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1422
|
+
);
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
let archiveBinary: ArrayBuffer;
|
|
1426
|
+
try {
|
|
1427
|
+
archiveBinary = await readFirmwareByteSourceFully(archiveSource);
|
|
1428
|
+
} finally {
|
|
1429
|
+
await archiveSource.close();
|
|
1430
|
+
}
|
|
1431
|
+
const archiveDigest = bytesToHex(sha256(new Uint8Array(archiveBinary)));
|
|
1432
|
+
if (archiveDigest !== archiveArtifact.artifact.sha256.toLowerCase()) {
|
|
1433
|
+
throw ERRORS.TypedError(
|
|
1434
|
+
HardwareErrorCode.RuntimeError,
|
|
1435
|
+
'Protocol V2 prepared resource archive does not match its approved receipt',
|
|
1436
|
+
{ firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch' }
|
|
1437
|
+
);
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
// The verified archive bytes are authoritative. Host materialization is an
|
|
1441
|
+
// implementation detail and may preserve wrapper paths or extra metadata files.
|
|
1442
|
+
const verifiedArchive = await this.prepareProtocolV2LocalResourceArchive(archiveBinary);
|
|
1443
|
+
const verifiedEntriesByName = new Map(
|
|
1444
|
+
verifiedArchive.materializedEntries.map(entry => [entry.entryName, entry.binary] as const)
|
|
1445
|
+
);
|
|
1446
|
+
const manifestBinary = verifiedEntriesByName.get('manifest.json');
|
|
1447
|
+
if (!manifestBinary) {
|
|
1448
|
+
throw ERRORS.TypedError(
|
|
1449
|
+
HardwareErrorCode.RuntimeError,
|
|
1450
|
+
'Protocol V2 prepared resource archive has no valid manifest.json',
|
|
1451
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1452
|
+
);
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
let manifestValue: unknown;
|
|
1456
|
+
try {
|
|
1457
|
+
manifestValue = JSON.parse(new TextDecoder().decode(manifestBinary));
|
|
1458
|
+
} catch (error) {
|
|
1459
|
+
throw ERRORS.TypedError(
|
|
1460
|
+
HardwareErrorCode.RuntimeError,
|
|
1461
|
+
`Protocol V2 prepared resource manifest is invalid: ${String(error)}`,
|
|
1462
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1463
|
+
);
|
|
1464
|
+
}
|
|
1465
|
+
const manifest = parseProtocolV2ResourceManifest(manifestValue);
|
|
1466
|
+
const selectedFiles = selectProtocolV2ResourceManifestFiles({
|
|
1467
|
+
manifest,
|
|
1468
|
+
targetsToUpdate: this.params.targetsToUpdate ?? [],
|
|
1469
|
+
});
|
|
1470
|
+
if (selectedFiles.length > PROTOCOL_V2_RESOURCE_FILE_MAX_COUNT) {
|
|
1471
|
+
throw ERRORS.TypedError(
|
|
1472
|
+
HardwareErrorCode.RuntimeError,
|
|
1473
|
+
'Protocol V2 prepared resource archive contains too many files',
|
|
1474
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1475
|
+
);
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
let totalSize = 0;
|
|
1479
|
+
const sources: ProtocolV2ResourceBundleSource[] = [];
|
|
1480
|
+
for (const [index, file] of selectedFiles.entries()) {
|
|
1481
|
+
const binary = verifiedEntriesByName.get(file.archive_path);
|
|
1482
|
+
if (!binary || binary.byteLength !== file.size) {
|
|
1483
|
+
throw ERRORS.TypedError(
|
|
1484
|
+
HardwareErrorCode.RuntimeError,
|
|
1485
|
+
`Protocol V2 prepared resource file does not match manifest: ${file.archive_path}`,
|
|
1486
|
+
{ firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch' }
|
|
1487
|
+
);
|
|
1488
|
+
}
|
|
1489
|
+
totalSize += binary.byteLength;
|
|
1490
|
+
if (totalSize > PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES) {
|
|
1491
|
+
throw ERRORS.TypedError(
|
|
1492
|
+
HardwareErrorCode.RuntimeError,
|
|
1493
|
+
'Protocol V2 prepared resource archive exceeds the total size limit',
|
|
1494
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1495
|
+
);
|
|
1496
|
+
}
|
|
1497
|
+
// 使用从获批 ZIP 中提取的规范字节,避免宿主在校验后替换 entry reader 内容。
|
|
1498
|
+
const source = await this.openProtocolV2MemorySource(binary);
|
|
1499
|
+
const header =
|
|
1500
|
+
source.size >= PROTOCOL_V2_OKPP_HEADER_SIZE
|
|
1501
|
+
? parseProtocolV2OkppHeader(
|
|
1502
|
+
new Uint8Array(await source.readAt(0, PROTOCOL_V2_OKPP_HEADER_SIZE))
|
|
1503
|
+
)
|
|
1504
|
+
: null;
|
|
1505
|
+
sources.push({
|
|
1506
|
+
name: file.original_name || `resource-${index}`,
|
|
1507
|
+
source,
|
|
1508
|
+
devicePath: file.device_path,
|
|
1509
|
+
...(header
|
|
1510
|
+
? {
|
|
1511
|
+
version: header.version,
|
|
1512
|
+
payloadHash: header.payloadHash,
|
|
1513
|
+
headerHash: header.headerHash,
|
|
1514
|
+
}
|
|
1515
|
+
: {}),
|
|
1516
|
+
});
|
|
1517
|
+
}
|
|
1518
|
+
return sources;
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
private async runProtocolV2PreparedArtifacts(
|
|
1522
|
+
features: Features,
|
|
1523
|
+
firmwareType: EFirmwareType,
|
|
1524
|
+
announceDownload = true
|
|
1525
|
+
) {
|
|
1526
|
+
try {
|
|
1527
|
+
if (announceDownload) {
|
|
1528
|
+
this.postTipMessage(FirmwareUpdateTipMessage.StartDownloadFirmware);
|
|
1529
|
+
}
|
|
1530
|
+
const installSources = await this.prepareProtocolV2InstallSources(firmwareType, features);
|
|
1531
|
+
const resourceSources = await this.prepareProtocolV2ResourceSources();
|
|
1532
|
+
if (installSources.length === 0 && resourceSources.length === 0) {
|
|
1533
|
+
throw ERRORS.TypedError(
|
|
1534
|
+
HardwareErrorCode.FirmwareUpdateDownloadFailed,
|
|
1535
|
+
'No firmware to update'
|
|
1536
|
+
);
|
|
1537
|
+
}
|
|
1538
|
+
if (announceDownload) {
|
|
1539
|
+
this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
return await this.executeProtocolV2SourceUpdate({
|
|
1543
|
+
installSources,
|
|
1544
|
+
resourceSources,
|
|
1545
|
+
});
|
|
1546
|
+
} finally {
|
|
1547
|
+
await this.closeProtocolV2PreparedSources();
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
private validateExpectedTargetVersions() {
|
|
1552
|
+
const expected = this.params.expectedTargetVersions;
|
|
1553
|
+
if (expected === undefined) return;
|
|
1554
|
+
if (typeof expected !== 'object' || expected === null || Array.isArray(expected)) {
|
|
1555
|
+
throw ERRORS.TypedError(
|
|
1556
|
+
HardwareErrorCode.RuntimeError,
|
|
1557
|
+
'Protocol V2 expected target versions are invalid'
|
|
1558
|
+
);
|
|
1559
|
+
}
|
|
1560
|
+
for (const [target, version] of Object.entries(expected)) {
|
|
1561
|
+
if (
|
|
1562
|
+
!Array.from(PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.values()).includes(
|
|
1563
|
+
target as Exclude<FirmwareUpdateV4Target, 'boot_resources'>
|
|
1564
|
+
) ||
|
|
1565
|
+
typeof version !== 'string' ||
|
|
1566
|
+
!/^\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.-]+)?$/u.test(version) ||
|
|
1567
|
+
version.length > 64
|
|
1568
|
+
) {
|
|
1569
|
+
throw ERRORS.TypedError(
|
|
1570
|
+
HardwareErrorCode.RuntimeError,
|
|
1571
|
+
'Protocol V2 expected target version is invalid'
|
|
1572
|
+
);
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
private getExpectedProtocolV2TargetVersion(targetId: number) {
|
|
1578
|
+
const target = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(targetId);
|
|
1579
|
+
const version = target ? this.params?.expectedTargetVersions?.[target] : undefined;
|
|
1580
|
+
if (!version) return undefined;
|
|
1581
|
+
const parts = version.split(/[+-]/u, 1)[0].split('.').map(Number);
|
|
1582
|
+
if (
|
|
1583
|
+
parts.length !== 3 ||
|
|
1584
|
+
parts.some(part => !Number.isSafeInteger(part) || part < 0 || part > 0xff)
|
|
1585
|
+
) {
|
|
1586
|
+
return undefined;
|
|
1587
|
+
}
|
|
1588
|
+
return parts[0] * 0x10000 + parts[1] * 0x100 + parts[2];
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
private assertExpectedProtocolV2Versions(targets?: readonly FirmwareUpdateV4Target[]) {
|
|
1592
|
+
const expected = this.params?.expectedTargetVersions;
|
|
1593
|
+
if (!expected) return;
|
|
1594
|
+
const features = this.protocolV2LatestFinalFeatures;
|
|
1595
|
+
const requestedTargets = this.getProtocolV2RequestedTargets();
|
|
1596
|
+
const applicationTargets = requestedTargets.filter(
|
|
1597
|
+
target => target === 'app_v1' || target === 'app_v2'
|
|
1598
|
+
);
|
|
1599
|
+
const visibleVersions: Partial<Record<FirmwareUpdateV4Target, string>> = {
|
|
1600
|
+
boot: features ? getDeviceBootloaderVersion(features).join('.') : undefined,
|
|
1601
|
+
coprocessor: features ? getDeviceBLEFirmwareVersion(features).join('.') : undefined,
|
|
1602
|
+
se01: features?.se01Version ?? undefined,
|
|
1603
|
+
se02: features?.se02Version ?? undefined,
|
|
1604
|
+
se03: features?.se03Version ?? undefined,
|
|
1605
|
+
se04: features?.se04Version ?? undefined,
|
|
1606
|
+
};
|
|
1607
|
+
if (features && applicationTargets.length === 1) {
|
|
1608
|
+
visibleVersions[applicationTargets[0]] = getDeviceFirmwareVersion(features).join('.');
|
|
1609
|
+
}
|
|
1610
|
+
const expectedEntries = (
|
|
1611
|
+
Object.entries(expected) as Array<[FirmwareUpdateV4Target, string]>
|
|
1612
|
+
).filter(([target]) => !targets || targets.includes(target));
|
|
1613
|
+
for (const [target, expectedVersion] of expectedEntries) {
|
|
1614
|
+
const targetId = Array.from(PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.entries()).find(
|
|
1615
|
+
([, mappedTarget]) => mappedTarget === target
|
|
1616
|
+
)?.[0];
|
|
1617
|
+
const statusVersion =
|
|
1618
|
+
targetId === undefined ? undefined : this.protocolV2CompletedTargetVersions.get(targetId);
|
|
1619
|
+
const observedVersion =
|
|
1620
|
+
statusVersion === undefined
|
|
1621
|
+
? visibleVersions[target]
|
|
1622
|
+
: `${Math.floor(statusVersion / 0x10000) % 0x100}.${
|
|
1623
|
+
Math.floor(statusVersion / 0x100) % 0x100
|
|
1624
|
+
}.${statusVersion % 0x100}`;
|
|
1625
|
+
if (!observedVersion) {
|
|
1626
|
+
if (this.protocolV2FinalStatusVerified) {
|
|
1627
|
+
Log.warn(
|
|
1628
|
+
`Protocol V2 target ${target} has no observable final version; completed target status is authoritative`
|
|
1629
|
+
);
|
|
1630
|
+
} else {
|
|
1631
|
+
throw ERRORS.TypedError(
|
|
1632
|
+
HardwareErrorCode.FirmwareVerificationFailed,
|
|
1633
|
+
`Protocol V2 target ${target} has no observable final version after status fallback`
|
|
1634
|
+
);
|
|
1635
|
+
}
|
|
1636
|
+
} else if (observedVersion !== expectedVersion) {
|
|
1637
|
+
throw ERRORS.TypedError(
|
|
1638
|
+
HardwareErrorCode.FirmwareVerificationFailed,
|
|
1639
|
+
`Protocol V2 target ${target} reached ${observedVersion}, expected ${expectedVersion}`
|
|
1640
|
+
);
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
private getProtocolV2RequestedTargets(): FirmwareUpdateV4Target[] {
|
|
1646
|
+
if (this.params.targetsToUpdate?.length) {
|
|
1647
|
+
return [...new Set(this.params.targetsToUpdate)];
|
|
1648
|
+
}
|
|
1649
|
+
if (this.params.preparedPlan?.targetsToUpdate.length) {
|
|
1650
|
+
return [...new Set(this.params.preparedPlan.targetsToUpdate)] as FirmwareUpdateV4Target[];
|
|
1651
|
+
}
|
|
1652
|
+
const targets = new Set<FirmwareUpdateV4Target>();
|
|
1653
|
+
Object.keys(this.params.componentArtifacts ?? {}).forEach(target =>
|
|
1654
|
+
targets.add(target as FirmwareUpdateV4Target)
|
|
1655
|
+
);
|
|
1656
|
+
if (this.params.bootloaderBinary) targets.add('boot');
|
|
1657
|
+
if (this.params.applicationP1Binary) targets.add('app_v1');
|
|
1658
|
+
if (this.params.applicationP2Binary) targets.add('app_v2');
|
|
1659
|
+
if (this.params.coprocessorBinary) targets.add('coprocessor');
|
|
1660
|
+
if (this.params.se01Binary) targets.add('se01');
|
|
1661
|
+
if (this.params.se02Binary) targets.add('se02');
|
|
1662
|
+
if (this.params.se03Binary) targets.add('se03');
|
|
1663
|
+
if (this.params.se04Binary) targets.add('se04');
|
|
1664
|
+
return Array.from(targets);
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
private async getProtocolV2DeviceFeatures(): Promise<Features> {
|
|
1668
|
+
if (this.device.features) {
|
|
1669
|
+
return this.device.features;
|
|
1670
|
+
}
|
|
1671
|
+
if (typeof this.device.getFeatures === 'function') {
|
|
1672
|
+
const features = await this.device.getFeatures();
|
|
1673
|
+
if (features) return features;
|
|
1674
|
+
}
|
|
1675
|
+
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'Device features not available');
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
private getProtocolV2SerialNumber(deviceInfo: ProtocolV2DeviceInfo) {
|
|
1679
|
+
const serialNumber = deviceInfo.hw?.serial_no?.trim();
|
|
1680
|
+
return serialNumber || undefined;
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
private getProtocolV2PreparedPlanDeviceModel(features?: Features) {
|
|
1684
|
+
const currentDeviceType = this.device.getCurrentDeviceType();
|
|
1685
|
+
const featureDeviceType = features ? getDeviceType(features) : undefined;
|
|
1686
|
+
const deviceType =
|
|
1687
|
+
currentDeviceType === EDeviceType.Pro2 || currentDeviceType === EDeviceType.Neo
|
|
1688
|
+
? currentDeviceType
|
|
1689
|
+
: featureDeviceType;
|
|
1690
|
+
return deviceType === EDeviceType.Pro2 || deviceType === EDeviceType.Neo
|
|
1691
|
+
? String(deviceType)
|
|
1692
|
+
: undefined;
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
private getProtocolV2ConnectionRoute() {
|
|
1696
|
+
const descriptor = this.device.originalDescriptor;
|
|
1697
|
+
return (
|
|
1698
|
+
descriptor?.path?.trim() ||
|
|
1699
|
+
this.device.getConnectId?.()?.trim() ||
|
|
1700
|
+
descriptor?.id?.trim() ||
|
|
1701
|
+
undefined
|
|
1702
|
+
);
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
private assertProtocolV2DeviceInfoIdentity(deviceInfo: ProtocolV2DeviceInfo) {
|
|
1706
|
+
assertProtocolV2ReconnectIdentity(
|
|
1707
|
+
this.protocolV2ExpectedSerialNumber,
|
|
1708
|
+
this.getProtocolV2SerialNumber(deviceInfo),
|
|
1709
|
+
this.protocolV2ExpectedPath,
|
|
1710
|
+
this.getProtocolV2ConnectionRoute()
|
|
1711
|
+
);
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
private getProtocolV2DeviceInfoTimeout() {
|
|
1715
|
+
return this.isBleReconnect()
|
|
1716
|
+
? this.payload.protocolV2DeviceInfoTimeoutMs ?? PROTOCOL_V2_DEVICE_INFO_READY_TIMEOUT
|
|
1717
|
+
: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT;
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
private async requestProtocolV2PhysicalIdentity() {
|
|
1721
|
+
return requestProtocolV2DeviceInfo({
|
|
1722
|
+
commands: this.device.getCommands(),
|
|
1723
|
+
timeoutMs: this.getProtocolV2DeviceInfoTimeout(),
|
|
1724
|
+
});
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
private async captureProtocolV2PhysicalIdentity() {
|
|
1728
|
+
const deviceInfo = await this.requestProtocolV2PhysicalIdentity();
|
|
1729
|
+
const serialNumber = this.getProtocolV2SerialNumber(deviceInfo);
|
|
1730
|
+
const path = this.getProtocolV2ConnectionRoute();
|
|
1731
|
+
if (this.params?.preparedPlan) {
|
|
1732
|
+
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
1733
|
+
preparedPlan: this.params.preparedPlan,
|
|
1734
|
+
deviceIdentity: serialNumber,
|
|
1735
|
+
deviceModel: this.getProtocolV2PreparedPlanDeviceModel(this.device.features),
|
|
1736
|
+
});
|
|
1737
|
+
} else if (this.params?.expectedDeviceId) {
|
|
1738
|
+
assertProtocolV2ReconnectIdentity(this.params?.expectedDeviceId, serialNumber);
|
|
1739
|
+
} else {
|
|
1740
|
+
assertProtocolV2ReconnectIdentity(serialNumber, serialNumber, path, path);
|
|
1741
|
+
}
|
|
1742
|
+
this.protocolV2ExpectedSerialNumber = serialNumber;
|
|
1743
|
+
this.protocolV2ExpectedPath = path;
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
private async verifyProtocolV2ReconnectIdentity() {
|
|
1747
|
+
const deviceInfo = await this.requestProtocolV2PhysicalIdentity();
|
|
1748
|
+
this.assertProtocolV2DeviceInfoIdentity(deviceInfo);
|
|
1749
|
+
return deviceInfo;
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
private prepareBootloaderBinary(): ArrayBuffer | null {
|
|
1753
|
+
return this.params.bootloaderBinary ?? null;
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
private getMissingProtocolV2FirmwareTargets(installItems: ProtocolV2InstallItem[]) {
|
|
1757
|
+
const preparedTargets = new Set(
|
|
1758
|
+
installItems.flatMap(item => {
|
|
1759
|
+
const target = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(item.targetId);
|
|
1760
|
+
return target ? [target] : [];
|
|
1761
|
+
})
|
|
1762
|
+
);
|
|
1763
|
+
return (this.params.targetsToUpdate ?? [])
|
|
1764
|
+
.filter(
|
|
1765
|
+
(target): target is Exclude<FirmwareUpdateV4Target, 'resource' | 'boot_resources'> =>
|
|
1766
|
+
target !== 'resource' && target !== 'boot_resources'
|
|
1767
|
+
)
|
|
1768
|
+
.filter(target => !preparedTargets.has(target));
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
private filterProtocolV2LocalInstallItems(installItems: ProtocolV2InstallItem[]) {
|
|
1772
|
+
if (!this.protocolV2HasExplicitTargetSelection) {
|
|
1773
|
+
return installItems;
|
|
1774
|
+
}
|
|
1775
|
+
const requestedTargets = new Set(this.params.targetsToUpdate ?? []);
|
|
1776
|
+
return installItems.filter(item => {
|
|
1777
|
+
const target = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(item.targetId);
|
|
1778
|
+
return target !== undefined && requestedTargets.has(target);
|
|
1779
|
+
});
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
private buildProtocolV2InstallItems({
|
|
1783
|
+
bootloaderBinary,
|
|
1784
|
+
fwBinaryMap,
|
|
1785
|
+
}: {
|
|
1786
|
+
bootloaderBinary: ArrayBuffer | null;
|
|
1787
|
+
fwBinaryMap: ProtocolV2TargetBinary[];
|
|
1788
|
+
}): ProtocolV2InstallItem[] {
|
|
1789
|
+
const installItems: ProtocolV2InstallItem[] = [];
|
|
1790
|
+
|
|
1791
|
+
if (bootloaderBinary) {
|
|
1792
|
+
installItems.push({
|
|
1793
|
+
fileName: 'bootloader.bin',
|
|
1794
|
+
binary: bootloaderBinary,
|
|
1795
|
+
targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER,
|
|
1796
|
+
kind: 'bootloader',
|
|
1797
|
+
});
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
installItems.push(...fwBinaryMap.map(item => ({ ...item, kind: 'firmware' as const })));
|
|
1801
|
+
return installItems;
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
private getRemoteComponentEntries(release: IFirmwareReleaseInfo) {
|
|
1805
|
+
const { components } = release;
|
|
1806
|
+
if (!components) return [];
|
|
1807
|
+
|
|
1808
|
+
const orderedKeys = [
|
|
1809
|
+
...(release.installOrder ?? []),
|
|
1810
|
+
...Object.keys(components).filter(key => !release.installOrder?.includes(key)),
|
|
1811
|
+
];
|
|
1812
|
+
|
|
1813
|
+
return orderedKeys
|
|
1814
|
+
.map(key => {
|
|
1815
|
+
const component = components[key];
|
|
1816
|
+
return component ? ([key, component] as const) : undefined;
|
|
1817
|
+
})
|
|
1818
|
+
.filter((entry): entry is readonly [string, IProtocolV2FirmwareComponent] => !!entry);
|
|
636
1819
|
}
|
|
637
1820
|
|
|
638
|
-
private
|
|
639
|
-
const
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
if (
|
|
646
|
-
!pathInfoRes.message?.exist ||
|
|
647
|
-
pathInfoRes.message?.directory ||
|
|
648
|
-
fileSize === undefined ||
|
|
649
|
-
fileSize < PROTOCOL_V2_OKPP_HEADER_SIZE
|
|
650
|
-
) {
|
|
651
|
-
return null;
|
|
1821
|
+
private getRemoteComponentTarget(key: string, component: IProtocolV2FirmwareComponent) {
|
|
1822
|
+
const targetName = component.target?.toUpperCase();
|
|
1823
|
+
if (targetName === 'ROMLOADER') {
|
|
1824
|
+
throw ERRORS.TypedError(
|
|
1825
|
+
HardwareErrorCode.RuntimeError,
|
|
1826
|
+
PROTOCOL_V2_ROMLOADER_UNSUPPORTED_MESSAGE
|
|
1827
|
+
);
|
|
652
1828
|
}
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
const res = await typedCall('FilesystemFileRead', 'FilesystemFile', {
|
|
660
|
-
file: {
|
|
661
|
-
path: filePath,
|
|
662
|
-
offset,
|
|
663
|
-
total_size: 0,
|
|
664
|
-
},
|
|
665
|
-
chunk_len: readLen,
|
|
666
|
-
ui_percentage: undefined,
|
|
667
|
-
});
|
|
668
|
-
const data = toProtocolV2Bytes(res.message?.data);
|
|
669
|
-
if (data.byteLength === 0) return null;
|
|
670
|
-
chunks.push(data);
|
|
671
|
-
offset += data.byteLength;
|
|
1829
|
+
const target = PROTOCOL_V2_REMOTE_COMPONENT_TARGETS[targetName];
|
|
1830
|
+
if (!target) {
|
|
1831
|
+
throw ERRORS.TypedError(
|
|
1832
|
+
HardwareErrorCode.RuntimeError,
|
|
1833
|
+
`Unsupported Protocol V2 firmware component target: ${key}/${component.target}`
|
|
1834
|
+
);
|
|
672
1835
|
}
|
|
673
|
-
|
|
674
|
-
const headerBytes = new Uint8Array(offset);
|
|
675
|
-
let cursor = 0;
|
|
676
|
-
chunks.forEach(chunk => {
|
|
677
|
-
headerBytes.set(chunk, cursor);
|
|
678
|
-
cursor += chunk.byteLength;
|
|
679
|
-
});
|
|
680
|
-
return parseProtocolV2OkppHeader(headerBytes);
|
|
1836
|
+
return target;
|
|
681
1837
|
}
|
|
682
1838
|
|
|
683
1839
|
private async downloadRemoteProtocolV2Component(
|
|
@@ -691,15 +1847,87 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
691
1847
|
`Missing Protocol V2 firmware component url: ${key}/${component.target}`
|
|
692
1848
|
);
|
|
693
1849
|
}
|
|
1850
|
+
const expectedFingerprint = normalizeProtocolV2Hex(component.fingerprint);
|
|
1851
|
+
if (
|
|
1852
|
+
!Number.isSafeInteger(component.expectedSize) ||
|
|
1853
|
+
Number(component.expectedSize) <= 0 ||
|
|
1854
|
+
!expectedFingerprint ||
|
|
1855
|
+
!/^[0-9a-f]{64}$/u.test(expectedFingerprint)
|
|
1856
|
+
) {
|
|
1857
|
+
throw ERRORS.TypedError(
|
|
1858
|
+
HardwareErrorCode.RuntimeError,
|
|
1859
|
+
`Protocol V2 firmware component integrity metadata is invalid: ${key}/${component.target}`,
|
|
1860
|
+
{ firmwareUpdateCode: 'FirmwarePlanInvalid' }
|
|
1861
|
+
);
|
|
1862
|
+
}
|
|
694
1863
|
|
|
695
1864
|
const { binary } = await getSysResourceBinary(component.url);
|
|
1865
|
+
if (binary.byteLength !== component.expectedSize) {
|
|
1866
|
+
throw ERRORS.TypedError(
|
|
1867
|
+
HardwareErrorCode.RuntimeError,
|
|
1868
|
+
`Protocol V2 firmware size mismatch: ${key}/${component.target}`,
|
|
1869
|
+
{ firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch' }
|
|
1870
|
+
);
|
|
1871
|
+
}
|
|
1872
|
+
if (!isProtocolV2FirmwareFingerprintValid(binary, component.fingerprint)) {
|
|
1873
|
+
throw ERRORS.TypedError(
|
|
1874
|
+
HardwareErrorCode.RuntimeError,
|
|
1875
|
+
`Protocol V2 firmware fingerprint mismatch: ${key}/${component.target}`
|
|
1876
|
+
);
|
|
1877
|
+
}
|
|
696
1878
|
return {
|
|
697
1879
|
...target,
|
|
698
1880
|
binary,
|
|
699
1881
|
};
|
|
700
1882
|
}
|
|
701
1883
|
|
|
702
|
-
private async
|
|
1884
|
+
private async downloadRemoteProtocolV2ResourceArchive(
|
|
1885
|
+
features: Features,
|
|
1886
|
+
firmwareType: EFirmwareType
|
|
1887
|
+
): Promise<ArrayBuffer> {
|
|
1888
|
+
const deviceType = getDeviceType(features);
|
|
1889
|
+
if (deviceType !== EDeviceType.Pro2 && deviceType !== EDeviceType.Neo) {
|
|
1890
|
+
throw ERRORS.TypedError(
|
|
1891
|
+
HardwareErrorCode.RuntimeError,
|
|
1892
|
+
'Protocol V2 resource archive requires a Pro2 or Neo device'
|
|
1893
|
+
);
|
|
1894
|
+
}
|
|
1895
|
+
const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
|
|
1896
|
+
const source = DataManager.getProtocolV2ResourceSource(release);
|
|
1897
|
+
const expectedSha256 = normalizeProtocolV2Hex(source?.archiveSha256);
|
|
1898
|
+
if (
|
|
1899
|
+
!source?.archiveUrl ||
|
|
1900
|
+
!Number.isSafeInteger(source.archiveSize) ||
|
|
1901
|
+
source.archiveSize <= 0 ||
|
|
1902
|
+
source.archiveSize > PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES ||
|
|
1903
|
+
!expectedSha256 ||
|
|
1904
|
+
!/^[0-9a-f]{64}$/u.test(expectedSha256)
|
|
1905
|
+
) {
|
|
1906
|
+
throw ERRORS.TypedError(
|
|
1907
|
+
HardwareErrorCode.RuntimeError,
|
|
1908
|
+
'Protocol V2 resource archive integrity metadata is invalid',
|
|
1909
|
+
{ firmwareUpdateCode: 'FirmwarePlanInvalid' }
|
|
1910
|
+
);
|
|
1911
|
+
}
|
|
1912
|
+
const { binary } = await getSysResourceBinary(source.archiveUrl);
|
|
1913
|
+
if (
|
|
1914
|
+
binary.byteLength !== source.archiveSize ||
|
|
1915
|
+
bytesToHex(sha256(new Uint8Array(binary))) !== expectedSha256
|
|
1916
|
+
) {
|
|
1917
|
+
throw ERRORS.TypedError(
|
|
1918
|
+
HardwareErrorCode.RuntimeError,
|
|
1919
|
+
'Protocol V2 resource archive does not match the remote config',
|
|
1920
|
+
{ firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch' }
|
|
1921
|
+
);
|
|
1922
|
+
}
|
|
1923
|
+
return binary;
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
private async prepareRemoteProtocolV2Binaries(
|
|
1927
|
+
firmwareType: EFirmwareType,
|
|
1928
|
+
features: Features,
|
|
1929
|
+
explicitInstallItems: ProtocolV2InstallItem[] = []
|
|
1930
|
+
) {
|
|
703
1931
|
const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
|
|
704
1932
|
|
|
705
1933
|
let bootloaderBinary: ArrayBuffer | null = null;
|
|
@@ -707,6 +1935,15 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
707
1935
|
const installItems: ProtocolV2InstallItem[] = [];
|
|
708
1936
|
|
|
709
1937
|
if (!release) {
|
|
1938
|
+
const missingFirmwareTargets = this.getMissingProtocolV2FirmwareTargets(explicitInstallItems);
|
|
1939
|
+
if (missingFirmwareTargets.length > 0) {
|
|
1940
|
+
throw ERRORS.TypedError(
|
|
1941
|
+
HardwareErrorCode.RuntimeError,
|
|
1942
|
+
`Protocol V2 firmware release is unavailable for requested targets: ${missingFirmwareTargets.join(
|
|
1943
|
+
', '
|
|
1944
|
+
)}`
|
|
1945
|
+
);
|
|
1946
|
+
}
|
|
710
1947
|
return {
|
|
711
1948
|
bootloaderBinary,
|
|
712
1949
|
fwBinaryMap,
|
|
@@ -716,33 +1953,46 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
716
1953
|
|
|
717
1954
|
const entries = this.getRemoteComponentEntries(release);
|
|
718
1955
|
const targetsToUpdate = new Set(this.params.targetsToUpdate ?? []);
|
|
1956
|
+
const explicitInstallItemByTargetId = new Map(
|
|
1957
|
+
explicitInstallItems.map(item => [item.targetId, item] as const)
|
|
1958
|
+
);
|
|
1959
|
+
const preparedTargets = new Set<FirmwareUpdateV4Target>();
|
|
719
1960
|
|
|
720
1961
|
for (const [key, component] of entries) {
|
|
721
|
-
const
|
|
722
|
-
const
|
|
723
|
-
const
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
kind: remoteBinary.kind,
|
|
733
|
-
});
|
|
1962
|
+
const targetName = component.target?.toUpperCase();
|
|
1963
|
+
const target = PROTOCOL_V2_REMOTE_COMPONENT_TARGETS[targetName];
|
|
1964
|
+
const updateTarget = target
|
|
1965
|
+
? PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(target.targetId)
|
|
1966
|
+
: undefined;
|
|
1967
|
+
if (updateTarget && targetsToUpdate.has(updateTarget)) {
|
|
1968
|
+
const explicitInstallItem = explicitInstallItemByTargetId.get(target.targetId);
|
|
1969
|
+
const installItem =
|
|
1970
|
+
explicitInstallItem ?? (await this.downloadRemoteProtocolV2Component(key, component));
|
|
1971
|
+
if (installItem.kind === 'bootloader') {
|
|
1972
|
+
bootloaderBinary = installItem.binary;
|
|
734
1973
|
} else {
|
|
735
1974
|
const binaryEntry = {
|
|
736
|
-
fileName:
|
|
737
|
-
binary:
|
|
738
|
-
targetId:
|
|
1975
|
+
fileName: installItem.fileName,
|
|
1976
|
+
binary: installItem.binary,
|
|
1977
|
+
targetId: installItem.targetId,
|
|
739
1978
|
};
|
|
740
1979
|
fwBinaryMap.push(binaryEntry);
|
|
741
|
-
installItems.push({ ...binaryEntry, kind: remoteBinary.kind });
|
|
742
1980
|
}
|
|
1981
|
+
installItems.push({ ...installItem });
|
|
1982
|
+
preparedTargets.add(updateTarget);
|
|
743
1983
|
}
|
|
744
1984
|
}
|
|
745
1985
|
|
|
1986
|
+
const missingTarget = Array.from(targetsToUpdate).find(
|
|
1987
|
+
target => target !== 'resource' && !preparedTargets.has(target)
|
|
1988
|
+
);
|
|
1989
|
+
if (missingTarget) {
|
|
1990
|
+
throw ERRORS.TypedError(
|
|
1991
|
+
HardwareErrorCode.RuntimeError,
|
|
1992
|
+
`Protocol V2 release does not contain requested target ${missingTarget}`
|
|
1993
|
+
);
|
|
1994
|
+
}
|
|
1995
|
+
|
|
746
1996
|
return {
|
|
747
1997
|
bootloaderBinary,
|
|
748
1998
|
fwBinaryMap,
|
|
@@ -750,144 +2000,83 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
750
2000
|
};
|
|
751
2001
|
}
|
|
752
2002
|
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
/**
|
|
758
|
-
* 准备 RESC bundle 列表。
|
|
759
|
-
*
|
|
760
|
-
* 两种模式(同 FirmwareUpdateV3 的 binary vs version 模式):
|
|
761
|
-
* - 用户传了 resourceBundleFiles(binary 数组):直接用,不做版本比对。
|
|
762
|
-
* - 用户没传:从远端 config.json 的 release.resourceBundles 拉取,
|
|
763
|
-
* 此时 syncProtocolV2ResourceBundles 会按需下载 + 比对设备已有 header 跳过。
|
|
764
|
-
*/
|
|
765
|
-
private prepareProtocolV2ResourceBundles(
|
|
766
|
-
firmwareType: EFirmwareType,
|
|
767
|
-
features: Features
|
|
768
|
-
): ProtocolV2ResourceBundleBinary[] | undefined {
|
|
769
|
-
// 模式1:用户手动传入 binary,直接安装,不比对
|
|
770
|
-
if (this.params.resourceBundleFiles?.length) {
|
|
771
|
-
return this.params.resourceBundleFiles.map(file => ({
|
|
772
|
-
name: file.devicePath.split('/').pop() ?? file.devicePath,
|
|
773
|
-
binary: file.binary,
|
|
774
|
-
devicePath: file.devicePath,
|
|
775
|
-
}));
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
if (!this.params.targetsToUpdate?.includes('resource')) {
|
|
779
|
-
return undefined;
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
// 模式2:远端配置模式,后续按需下载 + 比对
|
|
783
|
-
const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
|
|
784
|
-
if (!release?.resourceBundles?.length) return undefined;
|
|
785
|
-
|
|
786
|
-
return release.resourceBundles.map(bundle => ({
|
|
787
|
-
name: bundle.name,
|
|
788
|
-
binary: new ArrayBuffer(0),
|
|
789
|
-
devicePath: bundle.devicePath,
|
|
790
|
-
url: bundle.url,
|
|
791
|
-
version: bundle.version,
|
|
792
|
-
payloadHash: bundle.payloadHash,
|
|
793
|
-
headerHash: bundle.headerHash,
|
|
794
|
-
})) as ProtocolV2ResourceBundleBinary[];
|
|
2003
|
+
private getProtocolV2ResourceFilePath(path: string) {
|
|
2004
|
+
if (path.startsWith('vol')) return path;
|
|
2005
|
+
if (path.startsWith('/')) return `vol0:${path}`;
|
|
2006
|
+
return `vol0:/${path}`;
|
|
795
2007
|
}
|
|
796
2008
|
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
// 远端配置模式:按需下载 + 比对跳过
|
|
814
|
-
if (!isManualMode) {
|
|
815
|
-
const filtered: ProtocolV2ResourceBundleBinary[] = [];
|
|
816
|
-
for (const bundle of bundles) {
|
|
817
|
-
// 下载 binary
|
|
818
|
-
if (bundle.binary.byteLength === 0 && bundle.url) {
|
|
819
|
-
Log.log(`[FirmwareUpdateV4] downloading RESC bundle ${bundle.name} from ${bundle.url}`);
|
|
820
|
-
const { binary } = await getSysResourceBinary(bundle.url);
|
|
821
|
-
bundle.binary = binary;
|
|
822
|
-
}
|
|
823
|
-
// 比对设备上已有 header
|
|
824
|
-
const upToDate = await this.isProtocolV2ResourceBundleUpToDate(bundle);
|
|
825
|
-
if (upToDate) {
|
|
826
|
-
Log.log(`[FirmwareUpdateV4] skip RESC bundle ${bundle.name}; already up to date`);
|
|
827
|
-
} else {
|
|
828
|
-
filtered.push(bundle);
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
bundlesToSync = filtered;
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
if (bundlesToSync.length === 0) {
|
|
835
|
-
Log.log('[FirmwareUpdateV4] all RESC bundles up to date, nothing to sync');
|
|
836
|
-
return { processedSize: 0, totalSize: firmwareSize };
|
|
2009
|
+
private async readProtocolV2DeviceFileHeader(path: string, expectedSize?: number) {
|
|
2010
|
+
const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
|
|
2011
|
+
const filePath = this.getProtocolV2ResourceFilePath(path);
|
|
2012
|
+
const pathInfoRes = await typedCall('FilesystemPathInfoQuery', 'FilesystemPathInfo', {
|
|
2013
|
+
path: filePath,
|
|
2014
|
+
});
|
|
2015
|
+
const fileSize = toProtocolV2FiniteNumber(pathInfoRes.message?.size);
|
|
2016
|
+
if (
|
|
2017
|
+
!pathInfoRes.message?.exist ||
|
|
2018
|
+
pathInfoRes.message?.directory ||
|
|
2019
|
+
fileSize === undefined ||
|
|
2020
|
+
fileSize < PROTOCOL_V2_OKPP_HEADER_SIZE ||
|
|
2021
|
+
(expectedSize !== undefined && fileSize !== expectedSize)
|
|
2022
|
+
) {
|
|
2023
|
+
return null;
|
|
837
2024
|
}
|
|
838
2025
|
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
processedSize,
|
|
853
|
-
totalSize: transferTotalSize,
|
|
2026
|
+
const chunkSize = this.getProtocolV2FirmwareChunkSize('read');
|
|
2027
|
+
const chunks: Uint8Array[] = [];
|
|
2028
|
+
let offset = 0;
|
|
2029
|
+
while (offset < PROTOCOL_V2_OKPP_HEADER_SIZE) {
|
|
2030
|
+
const readLen = Math.min(chunkSize, PROTOCOL_V2_OKPP_HEADER_SIZE - offset);
|
|
2031
|
+
const res = await typedCall('FilesystemFileRead', 'FilesystemFile', {
|
|
2032
|
+
file: {
|
|
2033
|
+
path: filePath,
|
|
2034
|
+
offset,
|
|
2035
|
+
total_size: 0,
|
|
2036
|
+
},
|
|
2037
|
+
chunk_len: readLen,
|
|
2038
|
+
ui_percentage: undefined,
|
|
854
2039
|
});
|
|
2040
|
+
const data = toProtocolV2Bytes(res.message?.data);
|
|
2041
|
+
if (data.byteLength === 0) return null;
|
|
2042
|
+
chunks.push(data);
|
|
2043
|
+
offset += data.byteLength;
|
|
855
2044
|
}
|
|
856
2045
|
|
|
857
|
-
const
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
);
|
|
863
|
-
return
|
|
2046
|
+
const headerBytes = new Uint8Array(offset);
|
|
2047
|
+
let cursor = 0;
|
|
2048
|
+
chunks.forEach(chunk => {
|
|
2049
|
+
headerBytes.set(chunk, cursor);
|
|
2050
|
+
cursor += chunk.byteLength;
|
|
2051
|
+
});
|
|
2052
|
+
return parseProtocolV2OkppHeader(headerBytes);
|
|
864
2053
|
}
|
|
865
2054
|
|
|
866
|
-
/**
|
|
867
|
-
* 比对设备上已有 okpkg 的 OKPP header(仅远端配置模式使用)。
|
|
868
|
-
*/
|
|
2055
|
+
/** Compare the downloaded and installed okpkg headers before transferring a resource. */
|
|
869
2056
|
private async isProtocolV2ResourceBundleUpToDate(
|
|
870
|
-
bundle:
|
|
2057
|
+
bundle: Pick<
|
|
2058
|
+
ProtocolV2ResourceBundleSource,
|
|
2059
|
+
'name' | 'source' | 'devicePath' | 'version' | 'payloadHash' | 'headerHash'
|
|
2060
|
+
>
|
|
871
2061
|
): Promise<boolean> {
|
|
872
|
-
if (this.params
|
|
873
|
-
if (!bundle.
|
|
2062
|
+
if (this.params?.forcedUpdateRes) return false;
|
|
2063
|
+
if (!bundle.payloadHash || !bundle.headerHash) return false;
|
|
874
2064
|
|
|
875
2065
|
try {
|
|
876
|
-
const header = await this.readProtocolV2DeviceFileHeader(
|
|
2066
|
+
const header = await this.readProtocolV2DeviceFileHeader(
|
|
2067
|
+
bundle.devicePath,
|
|
2068
|
+
bundle.source.size
|
|
2069
|
+
);
|
|
877
2070
|
if (!header) return false;
|
|
878
2071
|
|
|
879
2072
|
if (bundle.version) {
|
|
880
2073
|
const cmp = compareProtocolV2Versions(header.version, bundle.version);
|
|
881
2074
|
if (cmp === undefined || cmp !== 0) return false;
|
|
882
2075
|
}
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
if (bundle.headerHash) {
|
|
888
|
-
const expected = normalizeProtocolV2Hex(bundle.headerHash);
|
|
889
|
-
if (expected && header.headerHash !== expected) return false;
|
|
890
|
-
}
|
|
2076
|
+
const expectedPayloadHash = normalizeProtocolV2Hex(bundle.payloadHash);
|
|
2077
|
+
const expectedHeaderHash = normalizeProtocolV2Hex(bundle.headerHash);
|
|
2078
|
+
if (!expectedPayloadHash || header.payloadHash !== expectedPayloadHash) return false;
|
|
2079
|
+
if (!expectedHeaderHash || header.headerHash !== expectedHeaderHash) return false;
|
|
891
2080
|
return true;
|
|
892
2081
|
} catch (error) {
|
|
893
2082
|
Log.log(`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check failed: `, error);
|
|
@@ -897,23 +2086,36 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
897
2086
|
|
|
898
2087
|
private isProtocolV2BootloaderMode() {
|
|
899
2088
|
if (typeof this.device.isBootloader === 'function') {
|
|
900
|
-
return this.device.isBootloader();
|
|
2089
|
+
return !!this.device.isBootloader();
|
|
901
2090
|
}
|
|
902
|
-
return
|
|
2091
|
+
return (
|
|
2092
|
+
this.device.features?.mode === 'bootloader' ||
|
|
2093
|
+
(this.device.features?.mode == null && !!this.device.features?.bootloaderMode)
|
|
2094
|
+
);
|
|
903
2095
|
}
|
|
904
2096
|
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
2097
|
+
private isProtocolV2RomloaderMode() {
|
|
2098
|
+
const deviceType = this.device.getCurrentDeviceType();
|
|
2099
|
+
if (
|
|
2100
|
+
!this.device.isProtocolV2() ||
|
|
2101
|
+
(deviceType !== EDeviceType.Pro2 && deviceType !== EDeviceType.Neo)
|
|
2102
|
+
) {
|
|
908
2103
|
return false;
|
|
909
2104
|
}
|
|
2105
|
+
if (typeof this.device.isRomloader === 'function') {
|
|
2106
|
+
return this.device.isRomloader();
|
|
2107
|
+
}
|
|
2108
|
+
return this.device.features?.mode === 'romloader';
|
|
2109
|
+
}
|
|
910
2110
|
|
|
2111
|
+
private async rebootProtocolV2ToBootloader() {
|
|
911
2112
|
try {
|
|
912
2113
|
this.postTipMessage(FirmwareUpdateTipMessage.AutoRebootToBootloader);
|
|
913
2114
|
await this.protocolV2Reboot(DeviceRebootType.Bootloader);
|
|
914
|
-
this.postTipMessage(FirmwareUpdateTipMessage.GoToBootloaderSuccess);
|
|
915
2115
|
await wait(1000);
|
|
916
2116
|
await this.waitForProtocolV2BootloaderMode();
|
|
2117
|
+
this.protocolV2ExecutionInLoader = true;
|
|
2118
|
+
this.postTipMessage(FirmwareUpdateTipMessage.GoToBootloaderSuccess);
|
|
917
2119
|
return true;
|
|
918
2120
|
} catch (error) {
|
|
919
2121
|
if (error instanceof HardwareError) {
|
|
@@ -924,30 +2126,56 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
924
2126
|
}
|
|
925
2127
|
}
|
|
926
2128
|
|
|
2129
|
+
async enterProtocolV2BootloaderMode() {
|
|
2130
|
+
// romloader is the first update environment and forwards targets to bootloader.
|
|
2131
|
+
// It rejects DeviceRebootType.Bootloader, so reuse the current connection.
|
|
2132
|
+
if (this.isProtocolV2RomloaderMode()) {
|
|
2133
|
+
Log.debug('Protocol V2 device is in romloader mode; start firmware update directly');
|
|
2134
|
+
this.protocolV2ExecutionInLoader = true;
|
|
2135
|
+
return false;
|
|
2136
|
+
}
|
|
2137
|
+
if (this.isProtocolV2BootloaderMode()) {
|
|
2138
|
+
Log.debug('Protocol V2 device is already in bootloader mode, skip reboot');
|
|
2139
|
+
this.protocolV2ExecutionInLoader = true;
|
|
2140
|
+
this.postTipMessage(FirmwareUpdateTipMessage.GoToBootloaderSuccess);
|
|
2141
|
+
return false;
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
return this.rebootProtocolV2ToBootloader();
|
|
2145
|
+
}
|
|
2146
|
+
|
|
927
2147
|
private async waitForProtocolV2BootloaderMode(
|
|
928
2148
|
timeout = PROTOCOL_V2_BOOTLOADER_RECONNECT_TIMEOUT,
|
|
929
2149
|
retryInterval = 1000
|
|
930
2150
|
) {
|
|
931
2151
|
const startTime = Date.now();
|
|
932
2152
|
let lastError: unknown;
|
|
2153
|
+
let shouldReconnect = true;
|
|
933
2154
|
|
|
934
2155
|
while (Date.now() - startTime < timeout) {
|
|
935
2156
|
try {
|
|
936
|
-
|
|
2157
|
+
if (shouldReconnect) {
|
|
2158
|
+
await this.reconnectProtocolV2Device();
|
|
2159
|
+
shouldReconnect = false;
|
|
2160
|
+
}
|
|
937
2161
|
const deviceInfo = await requestProtocolV2DeviceInfo({
|
|
938
2162
|
commands: this.device.getCommands(),
|
|
939
|
-
timeoutMs:
|
|
2163
|
+
timeoutMs: this.getProtocolV2DeviceInfoTimeout(),
|
|
940
2164
|
});
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
2165
|
+
this.assertProtocolV2DeviceInfoIdentity(deviceInfo);
|
|
2166
|
+
const features = await this.device.probeProtocolV2RuntimeState(
|
|
2167
|
+
deviceInfo,
|
|
2168
|
+
PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT
|
|
945
2169
|
);
|
|
946
|
-
if (features?.
|
|
2170
|
+
if (features?.mode === 'bootloader') {
|
|
947
2171
|
return features;
|
|
948
2172
|
}
|
|
949
2173
|
lastError = new Error('Protocol V2 device is reachable but is not in bootloader mode');
|
|
950
2174
|
} catch (error) {
|
|
2175
|
+
if (this.isProtocolV2ReconnectIdentityError(error)) {
|
|
2176
|
+
throw error;
|
|
2177
|
+
}
|
|
2178
|
+
shouldReconnect = true;
|
|
951
2179
|
lastError = error;
|
|
952
2180
|
Log.log('Protocol V2 bootloader mode not ready, polling reconnect: ', error);
|
|
953
2181
|
}
|
|
@@ -963,8 +2191,8 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
963
2191
|
}
|
|
964
2192
|
|
|
965
2193
|
/**
|
|
966
|
-
*
|
|
967
|
-
*
|
|
2194
|
+
* Collect explicit target binaries grouped by DeviceFirmwareTargetType.
|
|
2195
|
+
* Filenames are display-only staging paths because target_id is explicit.
|
|
968
2196
|
*/
|
|
969
2197
|
private collectExplicitTargetBinaries() {
|
|
970
2198
|
const entries: ProtocolV2TargetBinary[] = [];
|
|
@@ -1000,115 +2228,240 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1000
2228
|
return entries;
|
|
1001
2229
|
}
|
|
1002
2230
|
|
|
2231
|
+
private async executeProtocolV2SourceUpdate({
|
|
2232
|
+
installSources,
|
|
2233
|
+
resourceSources,
|
|
2234
|
+
}: {
|
|
2235
|
+
installSources: ProtocolV2InstallSource[];
|
|
2236
|
+
resourceSources: ProtocolV2ResourceBundleSource[];
|
|
2237
|
+
}) {
|
|
2238
|
+
this.protocolV2BootResourceStagingSafe = false;
|
|
2239
|
+
if (installSources.length > 0 || resourceSources.length > 0) {
|
|
2240
|
+
await this.enterProtocolV2BootloaderMode();
|
|
2241
|
+
// Clear stale boot-resource staging before writing any artifacts. The new
|
|
2242
|
+
// boot resource, resources and firmware then share one transfer session,
|
|
2243
|
+
// one global progress range and one multi-target install request.
|
|
2244
|
+
// Preserve the approved source order instead of synthesizing a boot-first
|
|
2245
|
+
// phase. Loader firmware owns the cross-stage handoff: bootloader runs its
|
|
2246
|
+
// component targets and leaves boot pending for romloader, while pending
|
|
2247
|
+
// runner-backed records always resume before the application boots.
|
|
2248
|
+
await this.ensureProtocolV2BootResourceStagingIsEmpty();
|
|
2249
|
+
await this.executeProtocolV2TransferPhase({
|
|
2250
|
+
installSources,
|
|
2251
|
+
resourceSources,
|
|
2252
|
+
});
|
|
2253
|
+
}
|
|
2254
|
+
return this.completeProtocolV2FinalVerification();
|
|
2255
|
+
}
|
|
2256
|
+
|
|
2257
|
+
private async ensureProtocolV2BootResourceStagingIsEmpty() {
|
|
2258
|
+
if (this.protocolV2BootResourceStagingSafe) return;
|
|
2259
|
+
const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
|
|
2260
|
+
const query = () =>
|
|
2261
|
+
typedCall('FilesystemPathInfoQuery', 'FilesystemPathInfo', {
|
|
2262
|
+
path: PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_STAGING_PATH,
|
|
2263
|
+
});
|
|
2264
|
+
const current = await query();
|
|
2265
|
+
if (current.message?.exist) {
|
|
2266
|
+
if (current.message.directory) {
|
|
2267
|
+
throw ERRORS.TypedError(
|
|
2268
|
+
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
2269
|
+
'Protocol V2 boot resource staging path is not a file'
|
|
2270
|
+
);
|
|
2271
|
+
}
|
|
2272
|
+
await typedCall('FilesystemFileDelete', 'Success', {
|
|
2273
|
+
path: PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_STAGING_PATH,
|
|
2274
|
+
});
|
|
2275
|
+
const remaining = await query();
|
|
2276
|
+
if (remaining.message?.exist) {
|
|
2277
|
+
throw ERRORS.TypedError(
|
|
2278
|
+
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
2279
|
+
'Protocol V2 stale boot resource staging file could not be removed'
|
|
2280
|
+
);
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
this.protocolV2BootResourceStagingSafe = true;
|
|
2284
|
+
}
|
|
2285
|
+
|
|
1003
2286
|
private async executeProtocolV2Update({
|
|
1004
2287
|
fwBinaryMap,
|
|
1005
2288
|
bootloaderBinary,
|
|
1006
2289
|
installItems,
|
|
1007
|
-
resourceBundles,
|
|
1008
2290
|
}: {
|
|
1009
2291
|
fwBinaryMap?: ProtocolV2TargetBinary[];
|
|
1010
2292
|
bootloaderBinary?: ArrayBuffer | null;
|
|
1011
2293
|
installItems?: ProtocolV2InstallItem[];
|
|
1012
|
-
resourceBundles?: ProtocolV2ResourceBundleBinary[];
|
|
1013
2294
|
}) {
|
|
1014
|
-
const
|
|
2295
|
+
const memoryInstallItems =
|
|
1015
2296
|
installItems ??
|
|
1016
2297
|
this.buildProtocolV2InstallItems({
|
|
1017
|
-
bootloaderBinary: bootloaderBinary ?? null,
|
|
1018
2298
|
fwBinaryMap: fwBinaryMap ?? [],
|
|
2299
|
+
bootloaderBinary: bootloaderBinary ?? null,
|
|
1019
2300
|
});
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
if (resourceBundles?.length) {
|
|
1029
|
-
const resourceTransfer = await this.syncProtocolV2ResourceBundles(
|
|
1030
|
-
resourceBundles,
|
|
1031
|
-
firmwareSize
|
|
2301
|
+
try {
|
|
2302
|
+
const installSources = await Promise.all(
|
|
2303
|
+
memoryInstallItems.map(async item => ({
|
|
2304
|
+
fileName: item.fileName,
|
|
2305
|
+
source: await this.openProtocolV2MemorySource(item.binary),
|
|
2306
|
+
targetId: item.targetId,
|
|
2307
|
+
kind: item.kind,
|
|
2308
|
+
}))
|
|
1032
2309
|
);
|
|
1033
|
-
|
|
1034
|
-
|
|
2310
|
+
return await this.executeProtocolV2SourceUpdate({
|
|
2311
|
+
installSources,
|
|
2312
|
+
resourceSources: [],
|
|
2313
|
+
});
|
|
2314
|
+
} finally {
|
|
2315
|
+
await this.closeProtocolV2PreparedSources();
|
|
1035
2316
|
}
|
|
2317
|
+
}
|
|
1036
2318
|
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
2319
|
+
private async executeProtocolV2TransferPhase({
|
|
2320
|
+
installSources,
|
|
2321
|
+
resourceSources,
|
|
2322
|
+
}: ProtocolV2TransferBatch) {
|
|
2323
|
+
let totalSize = installSources.reduce((total, item) => total + item.source.size, 0);
|
|
2324
|
+
const resourcesToSync: ProtocolV2ResourceBundleSource[] = [];
|
|
2325
|
+
for (const resource of resourceSources) {
|
|
2326
|
+
// Early boot promotes this mounted package's staging file. Always replace any
|
|
2327
|
+
// stale staging bytes with the artifact approved by the current PreparedPlan,
|
|
2328
|
+
// even when the mounted final file itself is already current.
|
|
2329
|
+
const requiresFreshStaging = isProtocolV2BootResourcePackagePath(resource.devicePath);
|
|
2330
|
+
if (!requiresFreshStaging && (await this.isProtocolV2ResourceBundleUpToDate(resource))) {
|
|
2331
|
+
Log.log(`[FirmwareUpdateV4] skip RESC bundle ${resource.name}; already up to date`);
|
|
2332
|
+
} else {
|
|
2333
|
+
resourcesToSync.push(resource);
|
|
2334
|
+
totalSize += resource.source.size;
|
|
1042
2335
|
}
|
|
1043
|
-
return;
|
|
1044
2336
|
}
|
|
1045
|
-
let transferredSize = processedSize;
|
|
1046
|
-
const transferStartTime = Date.now();
|
|
1047
|
-
const transferTransport = this.getProtocolV2FirmwareTransferTransport();
|
|
1048
|
-
const chunkSize = this.getProtocolV2FirmwareChunkSize();
|
|
1049
|
-
const onTransferredBytes = (bytes: number) => {
|
|
1050
|
-
transferredSize = bytes;
|
|
1051
|
-
};
|
|
1052
|
-
Log.log(
|
|
1053
|
-
`[FirmwareUpdateV4] transfer started transport=${transferTransport} total=${totalSize} bytes chunk=${chunkSize} bytes`
|
|
1054
|
-
);
|
|
1055
|
-
|
|
1056
|
-
const stagedInstallTargets: ProtocolV2InstallTarget[] = [];
|
|
1057
|
-
|
|
1058
|
-
try {
|
|
1059
|
-
for (const item of orderedInstallItems) {
|
|
1060
|
-
const filePath = this.getProtocolV2InstallItemStagingPath(item);
|
|
1061
|
-
Log.log(
|
|
1062
|
-
`[FirmwareUpdateV4] staging ${item.kind} via FilesystemFileWrite target=${item.targetId} path=${filePath} source=${item.fileName} bytes=${item.binary.byteLength}`
|
|
1063
|
-
);
|
|
1064
|
-
processedSize = await this.protocolV2CommonUpdateProcess({
|
|
1065
|
-
payload: item.binary,
|
|
1066
|
-
filePath,
|
|
1067
|
-
processedSize,
|
|
1068
|
-
totalSize,
|
|
1069
|
-
onTransferredBytes,
|
|
1070
|
-
});
|
|
1071
|
-
transferredSize = processedSize;
|
|
1072
|
-
await this.verifyProtocolV2StagedFile(filePath, item.binary.byteLength);
|
|
1073
2337
|
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
2338
|
+
this.postTipMessage(FirmwareUpdateTipMessage.StartTransferData);
|
|
2339
|
+
let processedSize = 0;
|
|
2340
|
+
for (const resource of resourcesToSync) {
|
|
2341
|
+
// The bootloader keeps its live resource package mounted. FatFs rejects
|
|
2342
|
+
// replacing an open file, so early boot promotes this staging file before mounting it.
|
|
2343
|
+
const writePath = resolveProtocolV2ResourceWritePath(resource.devicePath);
|
|
2344
|
+
processedSize = await this.protocolV2SourceUpdateProcess({
|
|
2345
|
+
source: resource.source,
|
|
2346
|
+
filePath: writePath,
|
|
2347
|
+
processedSize,
|
|
2348
|
+
totalSize,
|
|
2349
|
+
});
|
|
2350
|
+
await this.verifyProtocolV2StagedFile(writePath, resource.source.size);
|
|
2351
|
+
if (isProtocolV2BootResourcePackagePath(resource.devicePath)) {
|
|
2352
|
+
this.protocolV2BootResourceStagingSafe = true;
|
|
1078
2353
|
}
|
|
2354
|
+
}
|
|
1079
2355
|
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
2356
|
+
const stagedInstallTargets: Array<{ targetId: number; path: string }> = [];
|
|
2357
|
+
for (const item of installSources) {
|
|
2358
|
+
const filePath = this.getProtocolV2InstallItemStagingPath(item);
|
|
2359
|
+
processedSize = await this.protocolV2SourceUpdateProcess({
|
|
2360
|
+
source: item.source,
|
|
2361
|
+
filePath,
|
|
2362
|
+
processedSize,
|
|
2363
|
+
totalSize,
|
|
2364
|
+
});
|
|
2365
|
+
await this.verifyProtocolV2StagedFile(filePath, item.source.size);
|
|
2366
|
+
stagedInstallTargets.push({
|
|
2367
|
+
targetId: item.targetId,
|
|
2368
|
+
path: filePath,
|
|
2369
|
+
});
|
|
2370
|
+
}
|
|
1083
2371
|
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
);
|
|
1090
|
-
} catch (error) {
|
|
1091
|
-
const elapsedMs = Date.now() - transferStartTime;
|
|
1092
|
-
Log.warn(
|
|
1093
|
-
`[FirmwareUpdateV4] transfer failed transport=${transferTransport} bytes=${transferredSize}/${totalSize} elapsed=${(
|
|
1094
|
-
elapsedMs / 1000
|
|
1095
|
-
).toFixed(2)}s speed=${formatProtocolV2TransferSpeed(transferredSize, elapsedMs)} KB/s`
|
|
1096
|
-
);
|
|
1097
|
-
throw error;
|
|
2372
|
+
if (totalSize > 0) {
|
|
2373
|
+
this.postProgressMessage(100, 'transferData');
|
|
2374
|
+
}
|
|
2375
|
+
if (stagedInstallTargets.length === 0) {
|
|
2376
|
+
return;
|
|
1098
2377
|
}
|
|
1099
2378
|
|
|
1100
2379
|
this.postTipMessage(FirmwareUpdateTipMessage.ConfirmOnDevice);
|
|
1101
|
-
|
|
1102
|
-
const allTargets = stagedInstallTargets.map(item => ({
|
|
2380
|
+
const targets = stagedInstallTargets.map(item => ({
|
|
1103
2381
|
target_id: item.targetId,
|
|
1104
2382
|
path: item.path,
|
|
1105
2383
|
}));
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
2384
|
+
try {
|
|
2385
|
+
await this.protocolV2StartFirmwareUpdate({ targets });
|
|
2386
|
+
await wait(PROTOCOL_V2_INSTALL_STATUS_INITIAL_DELAY);
|
|
2387
|
+
await this.waitForProtocolV2FirmwareUpdateComplete(targets, true);
|
|
2388
|
+
} finally {
|
|
2389
|
+
this.device?.clearCancelableAction();
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
|
|
2393
|
+
private async protocolV2SourceUpdateProcess({
|
|
2394
|
+
source,
|
|
2395
|
+
filePath,
|
|
2396
|
+
processedSize,
|
|
2397
|
+
totalSize,
|
|
2398
|
+
}: {
|
|
2399
|
+
source: FirmwareByteSource;
|
|
2400
|
+
filePath: string;
|
|
2401
|
+
processedSize: number;
|
|
2402
|
+
totalSize: number;
|
|
2403
|
+
}) {
|
|
2404
|
+
let lastError: unknown;
|
|
2405
|
+
for (let attempt = 1; attempt <= PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT; attempt += 1) {
|
|
2406
|
+
try {
|
|
2407
|
+
const transferStartedAt = Date.now();
|
|
2408
|
+
await writeFirmwareByteSource({
|
|
2409
|
+
source,
|
|
2410
|
+
chunkSize: this.getProtocolV2FirmwareChunkSize('write', filePath),
|
|
2411
|
+
write: async ({ data, sourceOffset, length, first }) => {
|
|
2412
|
+
const chunkEnd = sourceOffset + length;
|
|
2413
|
+
const deviceProgress = getProtocolV2DeviceTransferProgress(
|
|
2414
|
+
processedSize + sourceOffset,
|
|
2415
|
+
processedSize + chunkEnd,
|
|
2416
|
+
totalSize
|
|
2417
|
+
);
|
|
2418
|
+
const response = await this.fileWriteChunk(
|
|
2419
|
+
filePath,
|
|
2420
|
+
source.size,
|
|
2421
|
+
sourceOffset,
|
|
2422
|
+
data,
|
|
2423
|
+
first,
|
|
2424
|
+
deviceProgress
|
|
2425
|
+
);
|
|
2426
|
+
const rawProcessedByte = response.message.processed_byte;
|
|
2427
|
+
const nextOffset = rawProcessedByte === undefined ? chunkEnd : Number(rawProcessedByte);
|
|
2428
|
+
if (!Number.isFinite(nextOffset) || nextOffset !== chunkEnd) {
|
|
2429
|
+
throw ERRORS.TypedError(
|
|
2430
|
+
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
2431
|
+
`invalid processed_byte ${rawProcessedByte} for offset ${sourceOffset}`
|
|
2432
|
+
);
|
|
2433
|
+
}
|
|
2434
|
+
const transferredBytes = processedSize + chunkEnd;
|
|
2435
|
+
const elapsedMs = Math.max(Date.now() - transferStartedAt, 0);
|
|
2436
|
+
this.postProgressMessage(
|
|
2437
|
+
Math.min(Math.ceil((transferredBytes / totalSize) * 100), 99),
|
|
2438
|
+
'transferData',
|
|
2439
|
+
{
|
|
2440
|
+
transferredBytes,
|
|
2441
|
+
totalBytes: totalSize,
|
|
2442
|
+
rateBytesPerSecond:
|
|
2443
|
+
elapsedMs > 0 ? Math.round((chunkEnd / elapsedMs) * 1000) : undefined,
|
|
2444
|
+
elapsedMs,
|
|
2445
|
+
}
|
|
2446
|
+
);
|
|
2447
|
+
return length;
|
|
2448
|
+
},
|
|
2449
|
+
});
|
|
2450
|
+
return processedSize + source.size;
|
|
2451
|
+
} catch (error) {
|
|
2452
|
+
lastError = error;
|
|
2453
|
+
if (attempt < PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT) {
|
|
2454
|
+
await this.recoverProtocolV2FileTransfer();
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
throw ERRORS.TypedError(
|
|
2459
|
+
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
2460
|
+
`transfer data error: ${getProtocolV2UnknownErrorText(lastError)}`
|
|
2461
|
+
);
|
|
1109
2462
|
}
|
|
1110
2463
|
|
|
1111
|
-
private getProtocolV2InstallItemStagingPath(item: ProtocolV2InstallItem) {
|
|
2464
|
+
private getProtocolV2InstallItemStagingPath(item: Pick<ProtocolV2InstallItem, 'fileName'>) {
|
|
1112
2465
|
return `${PROTOCOL_V2_FIRMWARE_STAGING_VOLUME}${item.fileName}`;
|
|
1113
2466
|
}
|
|
1114
2467
|
|
|
@@ -1125,172 +2478,457 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1125
2478
|
}
|
|
1126
2479
|
}
|
|
1127
2480
|
|
|
1128
|
-
private async queryProtocolV2FirmwareUpdateStatus() {
|
|
1129
|
-
const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
|
|
1130
|
-
return typedCall(
|
|
1131
|
-
'DeviceFirmwareUpdateStatusGet',
|
|
1132
|
-
'DeviceFirmwareUpdateStatus',
|
|
1133
|
-
{},
|
|
1134
|
-
{
|
|
1135
|
-
timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
|
|
1136
|
-
}
|
|
1137
|
-
);
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
private async pingProtocolV2Device() {
|
|
1141
|
-
const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
|
|
1142
|
-
await typedCall(
|
|
1143
|
-
'Ping',
|
|
1144
|
-
'Success',
|
|
1145
|
-
{ message: 'firmware-update' },
|
|
1146
|
-
{
|
|
1147
|
-
timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
|
|
1148
|
-
}
|
|
1149
|
-
);
|
|
1150
|
-
}
|
|
1151
|
-
|
|
1152
|
-
private isProtocolV2NormalModeFeatures(features?: Features | null) {
|
|
1153
|
-
return !!features && !features.bootloaderMode && features.mode !== 'bootloader';
|
|
1154
|
-
}
|
|
1155
|
-
|
|
1156
|
-
private async probeProtocolV2NormalMode() {
|
|
1157
|
-
const deviceInfo = await requestProtocolV2DeviceInfo({
|
|
1158
|
-
commands: this.device.getCommands(),
|
|
1159
|
-
timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
|
|
1160
|
-
request: PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST,
|
|
1161
|
-
});
|
|
1162
|
-
const features = this.device.updateProtocolV2Features(deviceInfo);
|
|
1163
|
-
assertProtocolV2ReconnectIdentity(
|
|
1164
|
-
this.protocolV2ExpectedDeviceId,
|
|
1165
|
-
features.deviceId ?? undefined
|
|
1166
|
-
);
|
|
1167
|
-
if (this.isProtocolV2NormalModeFeatures(features)) {
|
|
1168
|
-
Log.log('Protocol V2 firmware install finished; device is back in normal mode');
|
|
1169
|
-
return true;
|
|
1170
|
-
}
|
|
1171
|
-
return false;
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
2481
|
private assertProtocolV2TargetStatus(
|
|
1175
2482
|
statusTargets: ProtocolV2FirmwareUpdateStatusTarget[],
|
|
1176
|
-
expectedTargetIds: Set<number
|
|
2483
|
+
expectedTargetIds: Set<number>,
|
|
2484
|
+
expectedPaths = new Map<number, string>()
|
|
1177
2485
|
) {
|
|
2486
|
+
Log.log(
|
|
2487
|
+
`[FirmwareUpdateV4] DeviceFirmwareUpdateStatus records=${JSON.stringify(statusTargets)}`
|
|
2488
|
+
);
|
|
1178
2489
|
const failedTarget = statusTargets.find(
|
|
1179
2490
|
target =>
|
|
1180
2491
|
expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1) &&
|
|
1181
2492
|
isProtocolV2TargetStatusFailed(target.status)
|
|
1182
2493
|
);
|
|
1183
2494
|
if (failedTarget) {
|
|
2495
|
+
const failedTargetDetails = JSON.stringify({
|
|
2496
|
+
targetId: failedTarget.target_id,
|
|
2497
|
+
status: failedTarget.status,
|
|
2498
|
+
payloadVersion: failedTarget.payload_version,
|
|
2499
|
+
path: failedTarget.path,
|
|
2500
|
+
});
|
|
2501
|
+
Log.error(`[FirmwareUpdateV4] firmware install failed target=${failedTargetDetails}`);
|
|
1184
2502
|
throw ERRORS.TypedError(
|
|
1185
2503
|
HardwareErrorCode.FirmwareError,
|
|
1186
|
-
`Protocol V2 firmware target
|
|
2504
|
+
`Protocol V2 firmware target failed: target=${failedTarget.target_id} status=${
|
|
2505
|
+
failedTarget.status ?? 'unknown'
|
|
2506
|
+
} payloadVersion=${failedTarget.payload_version ?? 'unknown'} path=${
|
|
2507
|
+
failedTarget.path ?? 'unknown'
|
|
2508
|
+
}`
|
|
1187
2509
|
);
|
|
1188
2510
|
}
|
|
1189
2511
|
|
|
1190
|
-
const
|
|
1191
|
-
target
|
|
1192
|
-
|
|
1193
|
-
|
|
2512
|
+
const matchingTargets = statusTargets.filter(target =>
|
|
2513
|
+
expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1)
|
|
2514
|
+
);
|
|
2515
|
+
const seenTargetIds = new Set<number>();
|
|
2516
|
+
for (const target of matchingTargets) {
|
|
2517
|
+
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
2518
|
+
if (
|
|
2519
|
+
targetId === undefined ||
|
|
2520
|
+
seenTargetIds.has(targetId) ||
|
|
2521
|
+
(target.path && expectedPaths.get(targetId) && target.path !== expectedPaths.get(targetId))
|
|
2522
|
+
) {
|
|
2523
|
+
throw ERRORS.TypedError(
|
|
2524
|
+
HardwareErrorCode.RuntimeError,
|
|
2525
|
+
`Protocol V2 install status conflicts with target ${target.target_id}`,
|
|
2526
|
+
{
|
|
2527
|
+
firmwareUpdateCode: PROTOCOL_V2_INSTALL_STATUS_CONFLICT_CODE,
|
|
2528
|
+
}
|
|
2529
|
+
);
|
|
2530
|
+
}
|
|
2531
|
+
seenTargetIds.add(targetId);
|
|
2532
|
+
}
|
|
2533
|
+
const completedTargets = matchingTargets.filter(target =>
|
|
2534
|
+
isProtocolV2TargetStatusFinished(target.status)
|
|
1194
2535
|
);
|
|
1195
|
-
|
|
2536
|
+
const completedTargetIds = new Set<number>();
|
|
2537
|
+
completedTargets.forEach(target => {
|
|
2538
|
+
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
2539
|
+
if (targetId !== undefined) {
|
|
2540
|
+
completedTargetIds.add(targetId);
|
|
2541
|
+
}
|
|
2542
|
+
});
|
|
2543
|
+
if (completedTargetIds.size === expectedTargetIds.size && expectedTargetIds.size > 0) {
|
|
2544
|
+
for (const target of completedTargets) {
|
|
2545
|
+
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
2546
|
+
const payloadVersion = toProtocolV2FiniteNumber(target.payload_version);
|
|
2547
|
+
if (targetId === undefined) {
|
|
2548
|
+
throw ERRORS.TypedError(
|
|
2549
|
+
HardwareErrorCode.RuntimeError,
|
|
2550
|
+
'Protocol V2 completed target identity is invalid'
|
|
2551
|
+
);
|
|
2552
|
+
}
|
|
2553
|
+
const expectedVersion = this.getExpectedProtocolV2TargetVersion(targetId);
|
|
2554
|
+
if (expectedVersion !== undefined && payloadVersion !== undefined) {
|
|
2555
|
+
if (payloadVersion !== expectedVersion) {
|
|
2556
|
+
throw ERRORS.TypedError(
|
|
2557
|
+
HardwareErrorCode.FirmwareVerificationFailed,
|
|
2558
|
+
`Protocol V2 target ${targetId} reached payload version ${payloadVersion}, expected ${expectedVersion}`
|
|
2559
|
+
);
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
if (payloadVersion !== undefined) {
|
|
2563
|
+
this.protocolV2CompletedTargetVersions.set(targetId, payloadVersion);
|
|
2564
|
+
}
|
|
2565
|
+
}
|
|
2566
|
+
this.postProgressMessage(100, 'installingFirmware');
|
|
1196
2567
|
return true;
|
|
1197
2568
|
}
|
|
1198
2569
|
|
|
1199
|
-
|
|
1200
|
-
target =>
|
|
1201
|
-
expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1) &&
|
|
2570
|
+
if (expectedTargetIds.size > 0 && matchingTargets.length > 0) {
|
|
2571
|
+
const hasInProgressTarget = matchingTargets.some(target =>
|
|
1202
2572
|
isProtocolV2TargetStatusInProgress(target.status)
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
2573
|
+
);
|
|
2574
|
+
const completedProgress = Math.floor(
|
|
2575
|
+
(completedTargetIds.size / expectedTargetIds.size) * 100
|
|
2576
|
+
);
|
|
2577
|
+
// The protocol exposes no per-target percentage, so report coarse progress by
|
|
2578
|
+
// completed targets and use 1% once work starts to keep the UI responsive.
|
|
2579
|
+
const progress = Math.min(99, Math.max(completedProgress, hasInProgressTarget ? 1 : 0));
|
|
2580
|
+
this.postProgressMessage(progress, 'installingFirmware');
|
|
1206
2581
|
}
|
|
1207
2582
|
|
|
1208
2583
|
return false;
|
|
1209
2584
|
}
|
|
1210
2585
|
|
|
2586
|
+
private getProtocolV2MissingTargetIds(
|
|
2587
|
+
statusTargets: ProtocolV2FirmwareUpdateStatusTarget[],
|
|
2588
|
+
expectedTargetIds: Set<number>
|
|
2589
|
+
) {
|
|
2590
|
+
const reportedTargetIds = new Set<number>();
|
|
2591
|
+
statusTargets.forEach(target => {
|
|
2592
|
+
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
2593
|
+
if (targetId !== undefined) {
|
|
2594
|
+
reportedTargetIds.add(targetId);
|
|
2595
|
+
}
|
|
2596
|
+
});
|
|
2597
|
+
return Array.from(expectedTargetIds).filter(targetId => !reportedTargetIds.has(targetId));
|
|
2598
|
+
}
|
|
2599
|
+
|
|
2600
|
+
private getProtocolV2ObservableTargetVersions(features: Features) {
|
|
2601
|
+
const versions = new Map<number, string>();
|
|
2602
|
+
versions.set(
|
|
2603
|
+
ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER,
|
|
2604
|
+
getDeviceBootloaderVersion(features).join('.')
|
|
2605
|
+
);
|
|
2606
|
+
const applicationVersion = getDeviceFirmwareVersion(features).join('.');
|
|
2607
|
+
versions.set(ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1, applicationVersion);
|
|
2608
|
+
versions.set(ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P2, applicationVersion);
|
|
2609
|
+
versions.set(
|
|
2610
|
+
ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_COPROCESSOR,
|
|
2611
|
+
getDeviceBLEFirmwareVersion(features).join('.')
|
|
2612
|
+
);
|
|
2613
|
+
const secureElementVersions: Array<[number, string | null | undefined]> = [
|
|
2614
|
+
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE01, features.se01Version],
|
|
2615
|
+
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE02, features.se02Version],
|
|
2616
|
+
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE03, features.se03Version],
|
|
2617
|
+
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE04, features.se04Version],
|
|
2618
|
+
];
|
|
2619
|
+
secureElementVersions.forEach(([targetId, version]) => {
|
|
2620
|
+
if (version) versions.set(targetId, version);
|
|
2621
|
+
});
|
|
2622
|
+
return versions;
|
|
2623
|
+
}
|
|
2624
|
+
|
|
2625
|
+
private hasProtocolV2InstallVersionChanged(expectedTargetIds: Set<number>) {
|
|
2626
|
+
if (!this.protocolV2LastRuntimeProbeFeatures) return false;
|
|
2627
|
+
const currentVersions = this.getProtocolV2ObservableTargetVersions(
|
|
2628
|
+
this.protocolV2LastRuntimeProbeFeatures
|
|
2629
|
+
);
|
|
2630
|
+
return Array.from(expectedTargetIds).some(targetId => {
|
|
2631
|
+
const previousVersion = this.protocolV2InstallBaselineVersions.get(targetId);
|
|
2632
|
+
const currentVersion = currentVersions.get(targetId);
|
|
2633
|
+
return (
|
|
2634
|
+
previousVersion !== undefined &&
|
|
2635
|
+
currentVersion !== undefined &&
|
|
2636
|
+
previousVersion !== currentVersion
|
|
2637
|
+
);
|
|
2638
|
+
});
|
|
2639
|
+
}
|
|
2640
|
+
|
|
1211
2641
|
private async waitForProtocolV2FirmwareUpdateComplete(
|
|
1212
2642
|
targets: Array<{ target_id: number; path: string }>,
|
|
1213
|
-
|
|
2643
|
+
requireCurrentInstallStatus = false
|
|
1214
2644
|
) {
|
|
2645
|
+
this.protocolV2FinalStatusVerified = false;
|
|
1215
2646
|
const expectedTargetIds = new Set(targets.map(target => target.target_id));
|
|
1216
|
-
|
|
1217
|
-
const statusTargets = (startResponse.message.records ??
|
|
1218
|
-
[]) as ProtocolV2FirmwareUpdateStatusTarget[];
|
|
1219
|
-
if (this.assertProtocolV2TargetStatus(statusTargets, expectedTargetIds)) {
|
|
1220
|
-
return;
|
|
1221
|
-
}
|
|
1222
|
-
}
|
|
1223
|
-
|
|
2647
|
+
const expectedPaths = new Map(targets.map(target => [target.target_id, target.path]));
|
|
1224
2648
|
const startTime = Date.now();
|
|
1225
2649
|
let lastError: unknown;
|
|
2650
|
+
// DeviceFirmwareUpdateRequest leaves the current loader link usable for status polling.
|
|
2651
|
+
// Reconnecting here probes DeviceInfo, which loaders reject while installation is active.
|
|
2652
|
+
let shouldReconnect = false;
|
|
2653
|
+
let deviceInfo: ProtocolV2DeviceInfo | undefined;
|
|
2654
|
+
let missingTargetStatusSince: number | undefined;
|
|
2655
|
+
let missingTargetStatusKey: string | undefined;
|
|
2656
|
+
let normalModeWithoutInstallEvidenceSince: number | undefined;
|
|
2657
|
+
let installEvidenceObserved = false;
|
|
2658
|
+
let currentInstallStatusObserved = false;
|
|
2659
|
+
const resetMissingTargetStatusGrace = () => {
|
|
2660
|
+
missingTargetStatusSince = undefined;
|
|
2661
|
+
missingTargetStatusKey = undefined;
|
|
2662
|
+
};
|
|
1226
2663
|
|
|
1227
2664
|
while (Date.now() - startTime < PROTOCOL_V2_INSTALL_TIMEOUT) {
|
|
2665
|
+
// A transport release caused by an explicit workflow cancellation must not
|
|
2666
|
+
// be mistaken for the expected device reboot during installation.
|
|
2667
|
+
this.throwIfAborted();
|
|
1228
2668
|
try {
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
return;
|
|
1234
|
-
}
|
|
1235
|
-
} catch (error) {
|
|
1236
|
-
lastError = error;
|
|
1237
|
-
if (error instanceof HardwareError && error.errorCode === HardwareErrorCode.FirmwareError) {
|
|
1238
|
-
throw error;
|
|
2669
|
+
if (shouldReconnect) {
|
|
2670
|
+
await this.reconnectProtocolV2Device();
|
|
2671
|
+
deviceInfo = await this.verifyProtocolV2ReconnectIdentity();
|
|
2672
|
+
shouldReconnect = false;
|
|
1239
2673
|
}
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
2674
|
+
const currentDeviceInfo = deviceInfo;
|
|
2675
|
+
try {
|
|
2676
|
+
const statusResponse = await this.device.getCommands().typedCall(
|
|
2677
|
+
'DeviceFirmwareUpdateStatusGet',
|
|
2678
|
+
['DeviceFirmwareUpdateStatus', 'Success'],
|
|
2679
|
+
{
|
|
2680
|
+
fields: {
|
|
2681
|
+
status: true,
|
|
2682
|
+
payload_version: true,
|
|
2683
|
+
path: true,
|
|
2684
|
+
},
|
|
2685
|
+
},
|
|
2686
|
+
{ timeoutMs: PROTOCOL_V2_FIRMWARE_STATUS_RESPONSE_TIMEOUT }
|
|
2687
|
+
);
|
|
2688
|
+
if (statusResponse.type === 'Success') {
|
|
2689
|
+
installEvidenceObserved = true;
|
|
2690
|
+
resetMissingTargetStatusGrace();
|
|
2691
|
+
lastError = new Error(
|
|
2692
|
+
'Protocol V2 firmware install acknowledged; waiting for target status'
|
|
2693
|
+
);
|
|
2694
|
+
}
|
|
2695
|
+
const statusTargets =
|
|
2696
|
+
statusResponse.type === 'DeviceFirmwareUpdateStatus'
|
|
2697
|
+
? ((statusResponse.message.records ?? []) as ProtocolV2FirmwareUpdateStatusTarget[])
|
|
2698
|
+
: [];
|
|
2699
|
+
const hasInProgressTarget = statusTargets.some(target => {
|
|
2700
|
+
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
2701
|
+
return (
|
|
2702
|
+
targetId !== undefined &&
|
|
2703
|
+
expectedTargetIds.has(targetId) &&
|
|
2704
|
+
isProtocolV2TargetStatusInProgress(target.status)
|
|
2705
|
+
);
|
|
2706
|
+
});
|
|
2707
|
+
if (hasInProgressTarget) {
|
|
2708
|
+
currentInstallStatusObserved = true;
|
|
2709
|
+
}
|
|
2710
|
+
const hasMatchingTargetStatus = statusTargets.some(target => {
|
|
2711
|
+
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
2712
|
+
return targetId !== undefined && expectedTargetIds.has(targetId);
|
|
2713
|
+
});
|
|
2714
|
+
if (
|
|
2715
|
+
hasMatchingTargetStatus &&
|
|
2716
|
+
(!requireCurrentInstallStatus || currentInstallStatusObserved)
|
|
2717
|
+
) {
|
|
2718
|
+
installEvidenceObserved = true;
|
|
2719
|
+
normalModeWithoutInstallEvidenceSince = undefined;
|
|
2720
|
+
}
|
|
2721
|
+
const matchingStatusTargets = statusTargets.filter(target => {
|
|
2722
|
+
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
2723
|
+
return targetId !== undefined && expectedTargetIds.has(targetId);
|
|
2724
|
+
});
|
|
2725
|
+
const shouldVerifyTargetCompletion =
|
|
2726
|
+
!requireCurrentInstallStatus || currentInstallStatusObserved;
|
|
2727
|
+
if (
|
|
2728
|
+
shouldVerifyTargetCompletion &&
|
|
2729
|
+
this.assertProtocolV2TargetStatus(statusTargets, expectedTargetIds, expectedPaths)
|
|
2730
|
+
) {
|
|
2731
|
+
this.protocolV2FinalStatusVerified = true;
|
|
2732
|
+
return;
|
|
2733
|
+
}
|
|
2734
|
+
|
|
2735
|
+
if (
|
|
2736
|
+
requireCurrentInstallStatus &&
|
|
2737
|
+
!currentInstallStatusObserved &&
|
|
2738
|
+
matchingStatusTargets.length > 0
|
|
2739
|
+
) {
|
|
2740
|
+
lastError = new Error(
|
|
2741
|
+
'Protocol V2 firmware status is stale; waiting for the current install to start'
|
|
2742
|
+
);
|
|
2743
|
+
}
|
|
2744
|
+
|
|
2745
|
+
if (statusTargets.length === 0 && currentDeviceInfo) {
|
|
2746
|
+
const isNormalMode = await this.probeProtocolV2NormalMode(currentDeviceInfo);
|
|
2747
|
+
if (
|
|
2748
|
+
isNormalMode &&
|
|
2749
|
+
(installEvidenceObserved ||
|
|
2750
|
+
this.hasProtocolV2InstallVersionChanged(expectedTargetIds))
|
|
2751
|
+
) {
|
|
2752
|
+
Log.log(
|
|
2753
|
+
'[FirmwareUpdateV4] empty firmware status after confirmed App reboot; update complete'
|
|
2754
|
+
);
|
|
2755
|
+
this.postProgressMessage(100, 'installingFirmware');
|
|
1245
2756
|
return;
|
|
1246
2757
|
}
|
|
1247
|
-
}
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2760
|
+
if (statusTargets.length === 0) {
|
|
2761
|
+
resetMissingTargetStatusGrace();
|
|
2762
|
+
if (statusResponse.type !== 'Success') {
|
|
2763
|
+
lastError = new Error(
|
|
2764
|
+
'Protocol V2 firmware update is waiting for user confirmation or target status'
|
|
2765
|
+
);
|
|
2766
|
+
}
|
|
2767
|
+
} else {
|
|
2768
|
+
const missingTargetIds = this.getProtocolV2MissingTargetIds(
|
|
2769
|
+
statusTargets,
|
|
2770
|
+
expectedTargetIds
|
|
1252
2771
|
);
|
|
2772
|
+
if (missingTargetIds.length > 0) {
|
|
2773
|
+
const now = Date.now();
|
|
2774
|
+
const missingKey = missingTargetIds.join(',');
|
|
2775
|
+
if (missingTargetStatusSince === undefined || missingTargetStatusKey !== missingKey) {
|
|
2776
|
+
missingTargetStatusSince = now;
|
|
2777
|
+
missingTargetStatusKey = missingKey;
|
|
2778
|
+
} else if (
|
|
2779
|
+
now - missingTargetStatusSince >=
|
|
2780
|
+
PROTOCOL_V2_MISSING_TARGET_STATUS_GRACE_TIMEOUT
|
|
2781
|
+
) {
|
|
2782
|
+
const reportedTargetIds = statusTargets
|
|
2783
|
+
.map(target => normalizeProtocolV2TargetId(target.target_id))
|
|
2784
|
+
.filter((targetId): targetId is number => targetId !== undefined);
|
|
2785
|
+
throw ERRORS.TypedError(
|
|
2786
|
+
HardwareErrorCode.FirmwareError,
|
|
2787
|
+
`Protocol V2 firmware status is missing requested records: targetIds=${missingKey} reportedTargetIds=${reportedTargetIds.join(
|
|
2788
|
+
','
|
|
2789
|
+
)}`
|
|
2790
|
+
);
|
|
2791
|
+
}
|
|
2792
|
+
lastError = new Error(
|
|
2793
|
+
`Protocol V2 firmware status is temporarily missing targetIds=${missingKey}`
|
|
2794
|
+
);
|
|
2795
|
+
} else {
|
|
2796
|
+
resetMissingTargetStatusGrace();
|
|
2797
|
+
lastError = new Error('Protocol V2 firmware targets are still installing');
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
} catch (error) {
|
|
2801
|
+
if (isProtocolV2TerminalInstallStatusError(error)) {
|
|
2802
|
+
throw error;
|
|
1253
2803
|
}
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
2804
|
+
// Only consecutive full status dumps can prove that a record is absent.
|
|
2805
|
+
// A reboot or transport interruption starts a fresh grace window.
|
|
2806
|
+
resetMissingTargetStatusGrace();
|
|
2807
|
+
// App firmware does not register DeviceFirmwareUpdateStatusGet. Treat the
|
|
2808
|
+
// missing endpoint as completion only after the runtime probe confirms App mode.
|
|
2809
|
+
if (isProtocolV2FirmwareStatusEndpointUnavailable(error)) {
|
|
2810
|
+
if (!currentDeviceInfo) {
|
|
2811
|
+
throw ERRORS.TypedError(
|
|
2812
|
+
HardwareErrorCode.RuntimeError,
|
|
2813
|
+
'Protocol V2 device identity is unavailable during install polling'
|
|
2814
|
+
);
|
|
2815
|
+
}
|
|
2816
|
+
const isNormalMode = await this.probeProtocolV2NormalMode(currentDeviceInfo);
|
|
2817
|
+
if (
|
|
2818
|
+
isNormalMode &&
|
|
2819
|
+
(installEvidenceObserved ||
|
|
2820
|
+
this.hasProtocolV2InstallVersionChanged(expectedTargetIds))
|
|
2821
|
+
) {
|
|
2822
|
+
Log.log(
|
|
2823
|
+
'[FirmwareUpdateV4] firmware status endpoint unavailable after confirmed App reboot'
|
|
2824
|
+
);
|
|
2825
|
+
this.postProgressMessage(100, 'installingFirmware');
|
|
2826
|
+
return;
|
|
2827
|
+
}
|
|
2828
|
+
if (isNormalMode) {
|
|
2829
|
+
const now = Date.now();
|
|
2830
|
+
normalModeWithoutInstallEvidenceSince ??= now;
|
|
2831
|
+
if (
|
|
2832
|
+
now - normalModeWithoutInstallEvidenceSince >=
|
|
2833
|
+
PROTOCOL_V2_MISSING_TARGET_STATUS_GRACE_TIMEOUT
|
|
2834
|
+
) {
|
|
2835
|
+
throw ERRORS.TypedError(
|
|
2836
|
+
HardwareErrorCode.FirmwareError,
|
|
2837
|
+
'Protocol V2 device returned to normal mode without install ACK, target status, or version change'
|
|
2838
|
+
);
|
|
2839
|
+
}
|
|
2840
|
+
lastError = new Error(
|
|
2841
|
+
'Protocol V2 device is in normal mode but installation is not yet confirmed'
|
|
2842
|
+
);
|
|
2843
|
+
} else {
|
|
2844
|
+
normalModeWithoutInstallEvidenceSince = undefined;
|
|
2845
|
+
lastError = new Error(
|
|
2846
|
+
'Protocol V2 firmware status endpoint is unavailable while the device remains in loader mode'
|
|
2847
|
+
);
|
|
2848
|
+
}
|
|
2849
|
+
} else {
|
|
2850
|
+
shouldReconnect = true;
|
|
2851
|
+
deviceInfo = undefined;
|
|
2852
|
+
lastError = error;
|
|
2853
|
+
Log.log(
|
|
2854
|
+
'[FirmwareUpdateV4] DeviceFirmwareUpdateStatusGet unavailable during install: ',
|
|
2855
|
+
error
|
|
2856
|
+
);
|
|
1260
2857
|
}
|
|
1261
2858
|
}
|
|
2859
|
+
} catch (error) {
|
|
2860
|
+
lastError = error;
|
|
2861
|
+
if (this.isProtocolV2ReconnectIdentityError(error)) {
|
|
2862
|
+
throw error;
|
|
2863
|
+
}
|
|
2864
|
+
if (isProtocolV2TerminalInstallStatusError(error)) {
|
|
2865
|
+
throw error;
|
|
2866
|
+
}
|
|
2867
|
+
if (
|
|
2868
|
+
error instanceof HardwareError &&
|
|
2869
|
+
error.params?.firmwareUpdateCode === 'FirmwareInstallStatusUnavailable'
|
|
2870
|
+
) {
|
|
2871
|
+
throw error;
|
|
2872
|
+
}
|
|
2873
|
+
shouldReconnect = true;
|
|
2874
|
+
deviceInfo = undefined;
|
|
2875
|
+
resetMissingTargetStatusGrace();
|
|
2876
|
+
Log.log('Protocol V2 firmware install device readiness probe failed: ', error);
|
|
1262
2877
|
}
|
|
1263
2878
|
await wait(1000);
|
|
1264
2879
|
}
|
|
1265
2880
|
|
|
1266
2881
|
throw ERRORS.TypedError(
|
|
1267
2882
|
HardwareErrorCode.RuntimeError,
|
|
1268
|
-
`Protocol V2 firmware update status
|
|
2883
|
+
`Protocol V2 firmware update status not complete within ${
|
|
2884
|
+
PROTOCOL_V2_INSTALL_TIMEOUT / 1000
|
|
2885
|
+
}s: ${this.normalizeErrorMessage(lastError)}`
|
|
1269
2886
|
);
|
|
1270
2887
|
}
|
|
1271
2888
|
|
|
1272
2889
|
private async exitProtocolV2BootloaderToNormal() {
|
|
1273
|
-
this.
|
|
2890
|
+
await this.reconnectProtocolV2Device();
|
|
2891
|
+
const deviceInfo = await this.verifyProtocolV2ReconnectIdentity();
|
|
1274
2892
|
try {
|
|
1275
|
-
await this.
|
|
1276
|
-
|
|
1277
|
-
Log.log('Protocol V2 device is already in normal mode, skip normal reboot');
|
|
2893
|
+
if (await this.probeProtocolV2NormalMode(deviceInfo)) {
|
|
2894
|
+
Log.log('[FirmwareUpdateV4] device already returned to App mode; skip Normal reboot');
|
|
1278
2895
|
return;
|
|
1279
2896
|
}
|
|
1280
2897
|
} catch (error) {
|
|
1281
|
-
Log.log('
|
|
2898
|
+
Log.log('[FirmwareUpdateV4] unable to confirm App mode before Normal reboot: ', error);
|
|
1282
2899
|
}
|
|
1283
2900
|
await this.protocolV2Reboot(DeviceRebootType.Normal);
|
|
2901
|
+
this.protocolV2ExecutionInLoader = false;
|
|
2902
|
+
}
|
|
2903
|
+
|
|
2904
|
+
private async probeProtocolV2NormalMode(deviceInfo: ProtocolV2DeviceInfo) {
|
|
2905
|
+
const features = await this.device.probeProtocolV2RuntimeState(
|
|
2906
|
+
deviceInfo,
|
|
2907
|
+
PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT
|
|
2908
|
+
);
|
|
2909
|
+
this.protocolV2LastRuntimeProbeFeatures = features;
|
|
2910
|
+
return this.isProtocolV2ApplicationMode(features);
|
|
2911
|
+
}
|
|
2912
|
+
|
|
2913
|
+
private isProtocolV2ApplicationMode(features: Features) {
|
|
2914
|
+
return (
|
|
2915
|
+
(features.mode === 'normal' || features.mode === 'notInitialized') && !features.bootloaderMode
|
|
2916
|
+
);
|
|
1284
2917
|
}
|
|
1285
2918
|
|
|
1286
2919
|
private async waitForProtocolV2FinalFeatures() {
|
|
1287
2920
|
const features = await this.waitForProtocolV2ReconnectAndFeatures(
|
|
1288
|
-
|
|
2921
|
+
PROTOCOL_V2_FINAL_RECONNECT_TIMEOUT
|
|
1289
2922
|
);
|
|
1290
2923
|
|
|
2924
|
+
return this.getProtocolV2VersionResult(features);
|
|
2925
|
+
}
|
|
2926
|
+
|
|
2927
|
+
private getProtocolV2VersionResult(features: Features) {
|
|
1291
2928
|
const bootloaderVersion = getDeviceBootloaderVersion(features).join('.');
|
|
1292
2929
|
const bleVersion = getDeviceBLEFirmwareVersion(features).join('.');
|
|
1293
2930
|
const firmwareVersion = getDeviceFirmwareVersion(features).join('.');
|
|
2931
|
+
this.protocolV2LatestFinalFeatures = features;
|
|
1294
2932
|
if (firmwareVersion === '0.0.0') {
|
|
1295
2933
|
Log.warn(
|
|
1296
2934
|
'Protocol V2 firmware update finished but app firmware version is still 0.0.0. This is allowed for Pro2 debug BLE-only update flows.'
|
|
@@ -1304,36 +2942,56 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1304
2942
|
};
|
|
1305
2943
|
}
|
|
1306
2944
|
|
|
2945
|
+
private async completeProtocolV2FinalVerification() {
|
|
2946
|
+
if (this.protocolV2ExecutionInLoader || this.isProtocolV2BootloaderMode()) {
|
|
2947
|
+
await this.exitProtocolV2BootloaderToNormal();
|
|
2948
|
+
}
|
|
2949
|
+
const versions = await this.waitForProtocolV2FinalFeatures();
|
|
2950
|
+
this.assertExpectedProtocolV2Versions();
|
|
2951
|
+
this.postTipMessage(FirmwareUpdateTipMessage.FirmwareUpdateCompleted);
|
|
2952
|
+
DevicePool.resetState();
|
|
2953
|
+
return versions;
|
|
2954
|
+
}
|
|
2955
|
+
|
|
1307
2956
|
private async waitForProtocolV2ReconnectAndFeatures(timeout: number) {
|
|
1308
2957
|
const startTime = Date.now();
|
|
1309
2958
|
let lastError: unknown;
|
|
2959
|
+
let shouldReconnect = true;
|
|
1310
2960
|
|
|
1311
2961
|
while (Date.now() - startTime < timeout) {
|
|
1312
2962
|
try {
|
|
1313
|
-
|
|
2963
|
+
if (shouldReconnect) {
|
|
2964
|
+
await this.reconnectProtocolV2Device();
|
|
2965
|
+
shouldReconnect = false;
|
|
2966
|
+
}
|
|
1314
2967
|
const deviceInfo = await requestProtocolV2DeviceInfo({
|
|
1315
2968
|
commands: this.device.getCommands(),
|
|
1316
|
-
timeoutMs:
|
|
1317
|
-
//
|
|
2969
|
+
timeoutMs: this.getProtocolV2DeviceInfoTimeout(),
|
|
2970
|
+
// Completion needs target versions only; keep scope aligned with the request.
|
|
1318
2971
|
request: PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST,
|
|
1319
2972
|
});
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
2973
|
+
this.assertProtocolV2DeviceInfoIdentity(deviceInfo);
|
|
2974
|
+
const features = await this.device.probeProtocolV2RuntimeState(
|
|
2975
|
+
deviceInfo,
|
|
2976
|
+
PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
|
|
2977
|
+
{ forceRuntimeContextRefresh: true }
|
|
1324
2978
|
);
|
|
1325
|
-
if (
|
|
1326
|
-
|
|
1327
|
-
HardwareErrorCode.DeviceNotFound,
|
|
1328
|
-
'Protocol V2 device is still in bootloader mode'
|
|
1329
|
-
);
|
|
2979
|
+
if (this.isProtocolV2ApplicationMode(features)) {
|
|
2980
|
+
return features;
|
|
1330
2981
|
}
|
|
1331
|
-
|
|
2982
|
+
lastError = ERRORS.TypedError(
|
|
2983
|
+
HardwareErrorCode.DeviceNotFound,
|
|
2984
|
+
'Protocol V2 device is still in bootloader mode'
|
|
2985
|
+
);
|
|
1332
2986
|
} catch (error) {
|
|
2987
|
+
if (this.isProtocolV2ReconnectIdentityError(error)) {
|
|
2988
|
+
throw error;
|
|
2989
|
+
}
|
|
2990
|
+
shouldReconnect = true;
|
|
1333
2991
|
lastError = error;
|
|
1334
2992
|
Log.log('Protocol V2 normal mode not ready, polling Ping: ', error);
|
|
1335
|
-
await wait(1000);
|
|
1336
2993
|
}
|
|
2994
|
+
await wait(1000);
|
|
1337
2995
|
}
|
|
1338
2996
|
|
|
1339
2997
|
throw ERRORS.TypedError(
|
|
@@ -1357,136 +3015,71 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1357
3015
|
DataManager.isBrowserWebUsb(DataManager.getSettings('env')) &&
|
|
1358
3016
|
devicesDescriptor.length === 1
|
|
1359
3017
|
) {
|
|
3018
|
+
const descriptor = devicesDescriptor[0];
|
|
3019
|
+
if (!this.protocolV2ExpectedSerialNumber && descriptor.path !== this.protocolV2ExpectedPath) {
|
|
3020
|
+
throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound);
|
|
3021
|
+
}
|
|
1360
3022
|
this.device.updateDescriptor(
|
|
1361
3023
|
{
|
|
1362
|
-
...
|
|
3024
|
+
...descriptor,
|
|
1363
3025
|
protocolType: PROTOCOL_V2_CONNECT_PROTOCOL,
|
|
1364
3026
|
},
|
|
1365
3027
|
true
|
|
1366
3028
|
);
|
|
1367
|
-
await this.
|
|
3029
|
+
await this.ensureProtocolV2DeviceAcquired();
|
|
1368
3030
|
this.device.commands.disposed = false;
|
|
1369
3031
|
this.device.getCommands().mainId = this.device.mainId ?? '';
|
|
1370
|
-
await this.device.initialize();
|
|
1371
|
-
assertProtocolV2ReconnectIdentity(
|
|
1372
|
-
this.protocolV2ExpectedDeviceId,
|
|
1373
|
-
this.device.getCurrentDeviceId()
|
|
1374
|
-
);
|
|
1375
3032
|
return;
|
|
1376
3033
|
}
|
|
1377
3034
|
|
|
1378
|
-
//
|
|
1379
|
-
//
|
|
3035
|
+
// Reinitialize every USB descriptor so DeviceInfo can provide the physical serial.
|
|
3036
|
+
// Enumeration order is not stable and must never decide the reconnect target.
|
|
1380
3037
|
const { deviceList } = await DevicePool.getDevices(devicesDescriptor, undefined, {
|
|
1381
3038
|
connectProtocol: PROTOCOL_V2_CONNECT_PROTOCOL,
|
|
1382
3039
|
});
|
|
1383
|
-
|
|
3040
|
+
const expectedSerialNumber = this.protocolV2ExpectedSerialNumber?.trim();
|
|
3041
|
+
const expectedPath = this.protocolV2ExpectedPath?.trim();
|
|
3042
|
+
const reconnectDevice = deviceList.find(candidate => {
|
|
3043
|
+
const candidateSerialNumber = candidate.getCurrentSerialNo?.().trim();
|
|
3044
|
+
if (expectedSerialNumber && candidateSerialNumber) {
|
|
3045
|
+
return candidateSerialNumber === expectedSerialNumber;
|
|
3046
|
+
}
|
|
3047
|
+
return !!expectedPath && candidate.getConnectId() === expectedPath;
|
|
3048
|
+
});
|
|
3049
|
+
if (!reconnectDevice) {
|
|
1384
3050
|
throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound);
|
|
1385
3051
|
}
|
|
1386
3052
|
|
|
1387
3053
|
Log.debug(
|
|
1388
|
-
'Protocol V2 firmware reconnect using
|
|
1389
|
-
|
|
3054
|
+
'Protocol V2 firmware reconnect using matched device:',
|
|
3055
|
+
reconnectDevice.getConnectId()
|
|
1390
3056
|
);
|
|
1391
|
-
this.device.updateFromCache(
|
|
1392
|
-
await this.
|
|
3057
|
+
this.device.updateFromCache(reconnectDevice);
|
|
3058
|
+
await this.ensureProtocolV2DeviceAcquired();
|
|
1393
3059
|
this.device.commands.disposed = false;
|
|
1394
3060
|
this.device.getCommands().mainId = this.device.mainId ?? '';
|
|
1395
|
-
await this.device.initialize();
|
|
1396
|
-
assertProtocolV2ReconnectIdentity(
|
|
1397
|
-
this.protocolV2ExpectedDeviceId,
|
|
1398
|
-
this.device.getCurrentDeviceId()
|
|
1399
|
-
);
|
|
1400
|
-
}
|
|
1401
|
-
|
|
1402
|
-
private async protocolV2CommonUpdateProcess(params: ProtocolV2FileTransferParams) {
|
|
1403
|
-
let lastError: unknown;
|
|
1404
|
-
for (let attempt = 1; attempt <= PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT; attempt += 1) {
|
|
1405
|
-
try {
|
|
1406
|
-
return await this.protocolV2WriteWholeFile(params);
|
|
1407
|
-
} catch (error) {
|
|
1408
|
-
lastError = error;
|
|
1409
|
-
Log.error(
|
|
1410
|
-
`Protocol V2 file transfer failed path=${params.filePath} attempt=${attempt}/${PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT}; restarting from offset 0`,
|
|
1411
|
-
error
|
|
1412
|
-
);
|
|
1413
|
-
if (attempt === PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT) {
|
|
1414
|
-
break;
|
|
1415
|
-
}
|
|
1416
|
-
await this.recoverProtocolV2FileTransfer();
|
|
1417
|
-
}
|
|
1418
|
-
}
|
|
1419
|
-
|
|
1420
|
-
throw ERRORS.TypedError(
|
|
1421
|
-
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
1422
|
-
`transfer data error: ${getProtocolV2UnknownErrorText(lastError)}`
|
|
1423
|
-
);
|
|
1424
3061
|
}
|
|
1425
3062
|
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
let offset = 0;
|
|
1435
|
-
const getUploadProgress = (fileOffset: number) => {
|
|
1436
|
-
if (totalSize !== undefined && processedSize !== undefined) {
|
|
1437
|
-
return Math.min(Math.ceil(((processedSize + fileOffset) / totalSize) * 100), 99);
|
|
1438
|
-
}
|
|
1439
|
-
return Math.min(Math.ceil((fileOffset / payload.byteLength) * 100), 99);
|
|
1440
|
-
};
|
|
1441
|
-
|
|
1442
|
-
while (offset < payload.byteLength) {
|
|
1443
|
-
const chunkEnd = Math.min(offset + chunkSize, payload.byteLength);
|
|
1444
|
-
const chunkLength = chunkEnd - offset;
|
|
1445
|
-
const chunk = payload.slice(offset, chunkEnd);
|
|
1446
|
-
const overwrite = offset === 0;
|
|
1447
|
-
const progress = getProtocolV2DeviceTransferProgress(
|
|
1448
|
-
(processedSize ?? 0) + offset,
|
|
1449
|
-
(processedSize ?? 0) + chunkEnd,
|
|
1450
|
-
totalSize ?? payload.byteLength
|
|
1451
|
-
);
|
|
1452
|
-
|
|
1453
|
-
const writeRes = await this.fileWriteChunk(
|
|
1454
|
-
filePath,
|
|
1455
|
-
payload.byteLength,
|
|
1456
|
-
offset,
|
|
1457
|
-
chunk,
|
|
1458
|
-
overwrite,
|
|
1459
|
-
progress
|
|
1460
|
-
);
|
|
1461
|
-
const rawProcessedByte = writeRes.message.processed_byte;
|
|
1462
|
-
const processedByte = Number(rawProcessedByte);
|
|
1463
|
-
const nextOffset = rawProcessedByte === undefined ? offset + chunkLength : processedByte;
|
|
1464
|
-
if (!Number.isFinite(nextOffset) || nextOffset <= offset || nextOffset > chunkEnd) {
|
|
1465
|
-
throw ERRORS.TypedError(
|
|
1466
|
-
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
1467
|
-
`invalid processed_byte ${writeRes.message.processed_byte} for offset ${offset}`
|
|
1468
|
-
);
|
|
1469
|
-
}
|
|
1470
|
-
offset = nextOffset;
|
|
1471
|
-
onTransferredBytes?.((processedSize ?? 0) + offset);
|
|
1472
|
-
this.postProgressMessage(getUploadProgress(offset), 'transferData');
|
|
3063
|
+
/**
|
|
3064
|
+
* After acquiring a USB session, polling reuses its command channel. Reacquire only
|
|
3065
|
+
* after reboot or enumeration changes cause hasDeviceAcquire() to become false.
|
|
3066
|
+
*/
|
|
3067
|
+
private async ensureProtocolV2DeviceAcquired() {
|
|
3068
|
+
const commands = this.device.getCommands();
|
|
3069
|
+
if (this.device.hasDeviceAcquire() && !commands.disposed) {
|
|
3070
|
+
return;
|
|
1473
3071
|
}
|
|
1474
|
-
|
|
1475
|
-
|
|
3072
|
+
await this.device.acquire(PROTOCOL_V2_CONNECT_PROTOCOL, {
|
|
3073
|
+
throwOnRunPromiseError: true,
|
|
3074
|
+
});
|
|
1476
3075
|
}
|
|
1477
3076
|
|
|
1478
|
-
private
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
env &&
|
|
1485
|
-
(DataManager.isBrowserWebUsb(env) || DataManager.isDesktopWebUsb(env) || env === 'web')
|
|
1486
|
-
) {
|
|
1487
|
-
return 'WebUSB';
|
|
1488
|
-
}
|
|
1489
|
-
return env ?? 'unknown';
|
|
3077
|
+
private isProtocolV2ReconnectIdentityError(error: unknown) {
|
|
3078
|
+
// A serial can be temporarily unavailable while Loader starts, and BLE has no stable path.
|
|
3079
|
+
// Keep polling unless the device explicitly reports a different serial.
|
|
3080
|
+
return this.normalizeErrorMessage(error).includes(
|
|
3081
|
+
'Protocol V2 reconnect physical identity mismatch'
|
|
3082
|
+
);
|
|
1490
3083
|
}
|
|
1491
3084
|
|
|
1492
3085
|
private async fileWriteChunk(
|
|
@@ -1498,17 +3091,25 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1498
3091
|
progress: number | null
|
|
1499
3092
|
): Promise<TypedResponseMessage<'FilesystemFile'>> {
|
|
1500
3093
|
const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
|
|
1501
|
-
const writeRes = await typedCall(
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
3094
|
+
const writeRes = await typedCall(
|
|
3095
|
+
'FilesystemFileWrite',
|
|
3096
|
+
'FilesystemFile',
|
|
3097
|
+
{
|
|
3098
|
+
file: {
|
|
3099
|
+
path: filePath,
|
|
3100
|
+
offset,
|
|
3101
|
+
total_size: totalFileSize,
|
|
3102
|
+
data: chunk,
|
|
3103
|
+
},
|
|
3104
|
+
overwrite,
|
|
3105
|
+
append: false,
|
|
3106
|
+
ui_percentage: progress ?? undefined,
|
|
1507
3107
|
},
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
3108
|
+
{
|
|
3109
|
+
writeWithResponse: false,
|
|
3110
|
+
onWriteCompleted: () => undefined,
|
|
3111
|
+
}
|
|
3112
|
+
);
|
|
1512
3113
|
if (writeRes.type !== 'FilesystemFile') {
|
|
1513
3114
|
if ((writeRes as any).type === 'CallMethodError') {
|
|
1514
3115
|
if (((writeRes as any).message.error ?? '').indexOf(SESSION_ERROR) > -1) {
|
|
@@ -1524,9 +3125,10 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1524
3125
|
const env = DataManager.getSettings('env');
|
|
1525
3126
|
if (DataManager.isBleConnect(env)) {
|
|
1526
3127
|
await wait(3000);
|
|
1527
|
-
await this.acquireProtocolV2BleDevice();
|
|
1528
|
-
await this.device.initialize();
|
|
1529
3128
|
}
|
|
3129
|
+
await this.reconnectProtocolV2Device();
|
|
3130
|
+
await this.verifyProtocolV2ReconnectIdentity();
|
|
3131
|
+
await this.device.initialize();
|
|
1530
3132
|
await wait(2000);
|
|
1531
3133
|
}
|
|
1532
3134
|
|
|
@@ -1544,37 +3146,23 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1544
3146
|
}: {
|
|
1545
3147
|
targets: Array<{ target_id: number; path: string }>;
|
|
1546
3148
|
}) {
|
|
3149
|
+
this.protocolV2LastRuntimeProbeFeatures = undefined;
|
|
1547
3150
|
const commands = this.device.getCommands();
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
},
|
|
1564
|
-
}
|
|
1565
|
-
);
|
|
1566
|
-
} catch (error) {
|
|
1567
|
-
if (isProtocolV2StartUpdateTransientError(error)) {
|
|
1568
|
-
Log.log(
|
|
1569
|
-
'Protocol V2 firmware update request did not return; continue status polling',
|
|
1570
|
-
error
|
|
1571
|
-
);
|
|
1572
|
-
} else {
|
|
1573
|
-
throw error;
|
|
1574
|
-
}
|
|
1575
|
-
}
|
|
1576
|
-
this.postTipMessage(FirmwareUpdateTipMessage.FirmwareUpdating);
|
|
1577
|
-
return response;
|
|
3151
|
+
await commands.typedCall('DeviceFirmwareUpdateStage', 'Success', { targets });
|
|
3152
|
+
this.device.setCancelableAction(() => commands.cancelDevice());
|
|
3153
|
+
const interaction = this.device.createProtocolV2UiPhaseMetadata('button', 'start');
|
|
3154
|
+
this.postMessage(
|
|
3155
|
+
createUiMessage(UI_REQUEST.REQUEST_BUTTON, {
|
|
3156
|
+
device: this.device.toMessageObject() as KnownDevice,
|
|
3157
|
+
source: 'method-lifecycle',
|
|
3158
|
+
reason: 'firmware-update',
|
|
3159
|
+
completion: 'operation-completed',
|
|
3160
|
+
deviceOnly: true,
|
|
3161
|
+
operation: this.name,
|
|
3162
|
+
...(interaction ? { interaction } : {}),
|
|
3163
|
+
})
|
|
3164
|
+
);
|
|
3165
|
+
await commands.call('DeviceFirmwareUpdateRequest', {}, { returnAfterWrite: true });
|
|
1578
3166
|
}
|
|
1579
3167
|
|
|
1580
3168
|
private async protocolV2Reboot(rebootType: DeviceRebootType) {
|
|
@@ -1583,9 +3171,11 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1583
3171
|
const res = await typedCall('DeviceReboot', 'Success', {
|
|
1584
3172
|
reboot_type: rebootType,
|
|
1585
3173
|
});
|
|
3174
|
+
this.device.markProtocolV2Reboot(rebootType);
|
|
1586
3175
|
return res.message;
|
|
1587
3176
|
} catch (error) {
|
|
1588
3177
|
if (isProtocolV2DeviceDisconnectedError(error) || isProtocolV2ReconnectProbeError(error)) {
|
|
3178
|
+
this.device.markProtocolV2Reboot(rebootType);
|
|
1589
3179
|
return { message: 'Device rebooted successfully' };
|
|
1590
3180
|
}
|
|
1591
3181
|
throw error;
|