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