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