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