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