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