@onekeyfe/hd-core 1.2.0-alpha.2 → 1.2.0-alpha.21
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 +75 -0
- package/__tests__/DeviceCommands.test.ts +285 -0
- package/__tests__/DeviceEventRegistration.test.ts +43 -0
- package/__tests__/connectSettings.test.ts +19 -0
- package/__tests__/core-dispose.test.ts +34 -0
- package/__tests__/core-error-output.test.ts +39 -0
- package/__tests__/device-features-state.test.ts +78 -0
- package/__tests__/device-identity.test.ts +30 -0
- package/__tests__/device-lifecycle-events.test.ts +117 -0
- package/__tests__/device-pool-state.test.ts +132 -0
- package/__tests__/device-settings.test.ts +391 -0
- package/__tests__/device-state-contract.test.ts +27 -0
- package/__tests__/device-state-events.test.ts +162 -0
- package/__tests__/device-state-mapper.test.ts +241 -0
- package/__tests__/device-state-projector.test.ts +94 -0
- package/__tests__/device-state-store.test.ts +82 -0
- package/__tests__/device-utils.test.ts +106 -0
- package/__tests__/device-wallet-session-store.test.ts +213 -0
- package/__tests__/deviceFeaturesUtils.test.ts +39 -0
- package/__tests__/deviceSettings.test.ts +103 -0
- package/__tests__/evmLedgerLegacySafety.test.ts +10 -6
- package/__tests__/evmSignTypedData.test.ts +80 -0
- package/__tests__/filesystemValidation.test.ts +53 -0
- package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +402 -0
- package/__tests__/get-device-state.test.ts +410 -0
- package/__tests__/homescreen.test.ts +24 -0
- package/__tests__/kaspaSignTransaction.test.ts +591 -0
- package/__tests__/logBlockEvent.test.ts +26 -0
- package/__tests__/networkUtils.test.ts +386 -0
- package/__tests__/open-wallet-session-error-response.test.ts +43 -0
- package/__tests__/open-wallet-session.test.ts +559 -0
- package/__tests__/preInitialize.test.ts +33 -0
- package/__tests__/pro2Wallpaper.test.ts +50 -0
- package/__tests__/protocol-v2-bootloader-mode.test.ts +37 -0
- package/__tests__/protocol-v2-firmware-targets.test.ts +19 -0
- package/__tests__/protocol-v2.test.ts +3954 -1191
- package/__tests__/protocolV2FileWrite.test.ts +92 -0
- package/__tests__/protocolV2UiInteraction.test.ts +266 -0
- package/__tests__/public-device-state-api.test.ts +257 -0
- package/__tests__/public-pro2-api-boundary.test.ts +110 -0
- package/__tests__/refresh-device-state.test.ts +101 -0
- package/__tests__/request-context-logging.test.ts +16 -0
- package/__tests__/search-devices.test.ts +136 -0
- package/dist/api/BaseMethod.d.ts +8 -0
- package/dist/api/BaseMethod.d.ts.map +1 -1
- package/dist/api/CheckBootloaderRelease.d.ts.map +1 -1
- package/dist/api/CheckFirmwareRelease.d.ts.map +1 -1
- package/dist/api/ClearSessionCache.d.ts +9 -0
- package/dist/api/ClearSessionCache.d.ts.map +1 -0
- package/dist/api/DirList.d.ts.map +1 -1
- 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/FirmwareUpdateV2.d.ts +2 -2
- package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +25 -5
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/GetDeviceState.d.ts +7 -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 -6
- package/dist/api/GetPassphraseState.d.ts.map +1 -1
- package/dist/api/OpenWalletSession.d.ts +14 -0
- package/dist/api/OpenWalletSession.d.ts.map +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 +17 -0
- package/dist/api/UploadPortfolio.d.ts.map +1 -0
- package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
- package/dist/api/device/DeviceCancel.d.ts.map +1 -1
- 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.map +1 -1
- package/dist/api/device/DeviceRebootToBoardloader.d.ts.map +1 -1
- package/dist/api/device/DeviceRebootToBootloader.d.ts.map +1 -1
- package/dist/api/device/DeviceSettings.d.ts.map +1 -1
- package/dist/api/device/DeviceUnlock.d.ts +1 -1
- package/dist/api/device/DeviceUnlock.d.ts.map +1 -1
- package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
- package/dist/api/device/DeviceWipe.d.ts.map +1 -1
- package/dist/api/evm/EVMSignTypedData.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +1 -0
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
- package/dist/api/firmware/getBinary.d.ts +11 -3
- package/dist/api/firmware/getBinary.d.ts.map +1 -1
- package/dist/api/firmware/progressThrottle.d.ts +3 -0
- package/dist/api/firmware/progressThrottle.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 +33 -0
- package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -0
- package/dist/api/index.d.ts +12 -19
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/kaspa/KaspaSignTransaction.d.ts +5 -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/protocol-v2/DeviceFactoryInfoGet.d.ts +6 -0
- package/dist/api/protocol-v2/DeviceFactoryInfoGet.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceFactoryInfoSet.d.ts +7 -0
- package/dist/api/protocol-v2/DeviceFactoryInfoSet.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceFirmwareUpdate.d.ts +1 -1
- package/dist/api/protocol-v2/DeviceFirmwareUpdate.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts +3 -2
- package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts +1 -1
- package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceInfoGet.d.ts +28 -0
- package/dist/api/protocol-v2/DeviceInfoGet.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceReboot.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceStatusGet.d.ts +6 -0
- package/dist/api/protocol-v2/DeviceStatusGet.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts +26 -0
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -0
- package/dist/api/protocol-v2/FilesystemFormat.d.ts.map +1 -1
- package/dist/api/protocol-v2/{FilesystemFixPermission.d.ts → FilesystemPermissionFix.d.ts} +2 -2
- package/dist/api/protocol-v2/FilesystemPermissionFix.d.ts.map +1 -0
- package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts +6 -0
- package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -0
- package/dist/api/protocol-v2/helpers.d.ts +27 -19
- package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
- package/dist/api/ton/TonSignData.d.ts +1 -5
- package/dist/api/ton/TonSignData.d.ts.map +1 -1
- package/dist/api/utils.d.ts +2 -1
- package/dist/api/utils.d.ts.map +1 -1
- package/dist/constants/index.d.ts +2 -1
- package/dist/constants/index.d.ts.map +1 -1
- package/dist/core/RequestQueue.d.ts.map +1 -1
- package/dist/core/deviceEventRegistration.d.ts +11 -0
- package/dist/core/deviceEventRegistration.d.ts.map +1 -0
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/data-manager/DataManager.d.ts +1 -1
- package/dist/data-manager/DataManager.d.ts.map +1 -1
- package/dist/data-manager/connectSettings.d.ts.map +1 -1
- package/dist/device/Device.d.ts +35 -11
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts +8 -6
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/device/DeviceFeaturesState.d.ts +3 -0
- package/dist/device/DeviceFeaturesState.d.ts.map +1 -0
- package/dist/device/DevicePool.d.ts +4 -2
- package/dist/device/DevicePool.d.ts.map +1 -1
- package/dist/device/DeviceStateMapper.d.ts +13 -0
- package/dist/device/DeviceStateMapper.d.ts.map +1 -0
- package/dist/device/DeviceStateProjector.d.ts +7 -0
- package/dist/device/DeviceStateProjector.d.ts.map +1 -0
- package/dist/device/DeviceStateStore.d.ts +21 -0
- package/dist/device/DeviceStateStore.d.ts.map +1 -0
- package/dist/device/DeviceWalletSessionStore.d.ts +20 -0
- package/dist/device/DeviceWalletSessionStore.d.ts.map +1 -0
- 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 +9 -3
- 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/events/device.d.ts +11 -2
- package/dist/events/device.d.ts.map +1 -1
- package/dist/events/logBlockEvent.d.ts.map +1 -1
- package/dist/events/ui-request.d.ts +18 -2
- package/dist/events/ui-request.d.ts.map +1 -1
- package/dist/index.d.ts +1361 -609
- package/dist/index.js +5898 -2860
- package/dist/inject.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 +23 -28
- package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/firmware.d.ts +11 -9
- package/dist/protocols/protocol-v2/firmware.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/index.d.ts +3 -2
- package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/lockedError.d.ts +2 -0
- package/dist/protocols/protocol-v2/lockedError.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts +16 -0
- package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/uiInteraction.d.ts +39 -0
- package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/unlockRetry.d.ts +11 -0
- package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/walletSession.d.ts +19 -0
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -0
- package/dist/types/api/checkAllFirmwareRelease.d.ts +1 -2
- package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
- 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 +2 -2
- package/dist/types/api/deviceUnlock.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdate.d.ts +6 -6
- package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
- 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 +2 -10
- package/dist/types/api/getPassphraseState.d.ts.map +1 -1
- package/dist/types/api/index.d.ts +22 -25
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/api/kaspaSignTransaction.d.ts +27 -2
- package/dist/types/api/kaspaSignTransaction.d.ts.map +1 -1
- package/dist/types/api/openWalletSession.d.ts +41 -0
- package/dist/types/api/openWalletSession.d.ts.map +1 -0
- package/dist/types/api/protocolV2.d.ts +23 -28
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/api/sessionCache.d.ts +10 -0
- package/dist/types/api/sessionCache.d.ts.map +1 -0
- package/dist/types/device.d.ts +121 -5
- 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 +21 -1
- 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 +13 -0
- package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -0
- package/dist/utils/deviceFeaturesUtils.d.ts +13 -3
- package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
- package/dist/utils/deviceInfoUtils.d.ts +9 -8
- package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
- package/dist/utils/deviceSettings.d.ts +38 -7
- package/dist/utils/deviceSettings.d.ts.map +1 -1
- package/dist/utils/deviceVersionUtils.d.ts +6 -5
- package/dist/utils/deviceVersionUtils.d.ts.map +1 -1
- package/dist/utils/homescreen.d.ts.map +1 -1
- package/dist/utils/index.d.ts +5 -2
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/networkUtils.d.ts +9 -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/pro2Wallpaper.d.ts +12 -0
- package/dist/utils/pro2Wallpaper.d.ts.map +1 -0
- package/dist/utils/tracing.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/BaseMethod.ts +36 -9
- package/src/api/CheckBootloaderRelease.ts +1 -2
- package/src/api/CheckFirmwareRelease.ts +1 -2
- package/src/api/ClearSessionCache.ts +28 -0
- package/src/api/DirList.ts +5 -3
- package/src/api/DirMake.ts +3 -3
- package/src/api/DirRemove.ts +3 -3
- package/src/api/FileDelete.ts +3 -3
- package/src/api/FileRead.ts +23 -7
- package/src/api/FileWrite.ts +23 -168
- package/src/api/FirmwareUpdate.ts +4 -4
- package/src/api/FirmwareUpdateV2.ts +133 -45
- package/src/api/FirmwareUpdateV3.ts +3 -5
- package/src/api/FirmwareUpdateV4.ts +1138 -357
- package/src/api/GetDeviceState.ts +55 -0
- package/src/api/GetFeatures.ts +8 -3
- package/src/api/GetOnekeyFeatures.ts +3 -72
- package/src/api/GetPassphraseState.ts +11 -18
- package/src/api/OpenWalletSession.ts +221 -0
- package/src/api/PathInfo.ts +5 -3
- package/src/api/PromptWebDeviceAccess.ts +2 -3
- package/src/api/SearchDevices.ts +29 -4
- package/src/api/UploadPortfolio.ts +40 -0
- package/src/api/alephium/AlephiumSignTransaction.ts +2 -2
- package/src/api/allnetwork/AllNetworkGetAddressBase.ts +4 -8
- package/src/api/benfen/BenfenGetAddress.ts +1 -1
- package/src/api/btc/BTCGetPublicKey.ts +1 -1
- package/src/api/cardano/CardanoSignMessage.ts +1 -1
- package/src/api/cardano/CardanoSignTransaction.ts +1 -1
- package/src/api/device/DeviceCancel.ts +1 -0
- package/src/api/device/DeviceChangePin.ts +24 -0
- package/src/api/device/DeviceFullyUploadResource.ts +4 -5
- package/src/api/device/DeviceLock.ts +1 -0
- package/src/api/device/DeviceRebootToBoardloader.ts +5 -4
- package/src/api/device/DeviceRebootToBootloader.ts +5 -4
- package/src/api/device/DeviceSettings.ts +183 -1
- package/src/api/device/DeviceUnlock.ts +11 -1
- package/src/api/device/DeviceUpdateBootloader.ts +1 -2
- package/src/api/device/DeviceWipe.ts +19 -0
- package/src/api/evm/EVMSignTypedData.ts +3 -5
- 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/firmware/FirmwareUpdateBaseMethod.ts +21 -7
- package/src/api/firmware/getBinary.ts +8 -3
- package/src/api/firmware/progressThrottle.ts +44 -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 +193 -0
- package/src/api/index.ts +14 -20
- package/src/api/kaspa/KaspaSignTransaction.ts +341 -22
- package/src/api/kaspa/helpers/TransferSerialize.ts +14 -0
- package/src/api/nervos/NervosSignTransaction.ts +2 -2
- package/src/api/nexa/NexaSignTransaction.ts +1 -1
- package/src/api/protocol-v2/{FactoryGetDeviceInfo.ts → DeviceFactoryInfoGet.ts} +4 -4
- package/src/api/protocol-v2/DeviceFactoryInfoSet.ts +32 -0
- package/src/api/protocol-v2/DeviceFirmwareUpdate.ts +7 -9
- package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +41 -6
- package/src/api/protocol-v2/DeviceGetOnboardingStatus.ts +6 -4
- package/src/api/protocol-v2/DeviceInfoGet.ts +145 -0
- package/src/api/protocol-v2/DeviceReboot.ts +5 -3
- package/src/api/protocol-v2/DeviceStatusGet.ts +16 -0
- package/src/api/protocol-v2/DeviceUploadWallpaper.ts +131 -0
- package/src/api/protocol-v2/FilesystemFormat.ts +5 -2
- package/src/api/protocol-v2/{FilesystemFixPermission.ts → FilesystemPermissionFix.ts} +3 -3
- package/src/api/protocol-v2/Ping.ts +1 -1
- package/src/api/protocol-v2/{GetProtoVersion.ts → ProtocolInfoRequest.ts} +3 -3
- package/src/api/protocol-v2/helpers.ts +58 -62
- package/src/api/ton/TonSignData.ts +1 -5
- package/src/api/utils.ts +7 -3
- package/src/constants/index.ts +7 -1
- package/src/core/RequestQueue.ts +1 -0
- package/src/core/deviceEventRegistration.ts +23 -0
- package/src/core/index.ts +140 -35
- package/src/data/messages/messages-protocol-v2.json +825 -709
- package/src/data/messages/messages.json +286 -1
- package/src/data-manager/DataManager.ts +13 -6
- package/src/data-manager/connectSettings.ts +4 -0
- package/src/device/Device.ts +480 -192
- package/src/device/DeviceCommands.ts +81 -25
- package/src/device/DeviceFeaturesState.ts +25 -0
- package/src/device/DevicePool.ts +67 -19
- package/src/device/DeviceStateMapper.ts +349 -0
- package/src/device/DeviceStateProjector.ts +95 -0
- package/src/device/DeviceStateStore.ts +201 -0
- package/src/device/DeviceWalletSessionStore.ts +105 -0
- package/src/device/cloneDeviceState.ts +39 -0
- package/src/device/deviceIdentity.ts +18 -0
- package/src/deviceProfile/buildDeviceFeatures.ts +235 -210
- package/src/deviceProfile/index.ts +0 -1
- package/src/events/device.ts +17 -1
- package/src/events/logBlockEvent.ts +8 -1
- package/src/events/ui-request.ts +24 -2
- package/src/inject.ts +23 -34
- package/src/protocols/protocol-v2/deviceSession.ts +24 -0
- package/src/protocols/protocol-v2/features.ts +68 -82
- package/src/protocols/protocol-v2/firmware.ts +12 -36
- package/src/protocols/protocol-v2/index.ts +3 -1
- package/src/protocols/protocol-v2/lockedError.ts +10 -0
- package/src/protocols/protocol-v2/settingsUnlockPolicy.ts +61 -0
- package/src/protocols/protocol-v2/uiInteraction.ts +210 -0
- package/src/protocols/protocol-v2/unlockRetry.ts +76 -0
- package/src/protocols/protocol-v2/walletSession.ts +107 -0
- package/src/types/api/checkAllFirmwareRelease.ts +1 -2
- package/src/types/api/deviceSettings.ts +26 -2
- package/src/types/api/deviceUnlock.ts +5 -2
- package/src/types/api/firmwareUpdate.ts +28 -20
- 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 +8 -11
- package/src/types/api/index.ts +30 -53
- package/src/types/api/kaspaSignTransaction.ts +40 -2
- package/src/types/api/openWalletSession.ts +64 -0
- package/src/types/api/protocolV2.ts +58 -55
- package/src/types/api/sessionCache.ts +14 -0
- package/src/types/device.ts +170 -4
- package/src/types/params.ts +4 -0
- package/src/types/settings.ts +42 -30
- package/src/utils/assets.ts +4 -1
- package/src/utils/deviceFeaturesCompat.ts +173 -0
- package/src/utils/deviceFeaturesUtils.ts +68 -24
- package/src/utils/deviceInfoUtils.ts +26 -64
- package/src/utils/deviceSettings.ts +161 -39
- package/src/utils/deviceVersionUtils.ts +25 -44
- package/src/utils/findDefectiveBatchDevice.ts +3 -3
- package/src/utils/homescreen.ts +10 -0
- package/src/utils/index.ts +21 -1
- package/src/utils/networkUtils.ts +270 -14
- package/src/utils/pro2Wallpaper.ts +109 -0
- package/src/utils/tracing.ts +3 -1
- package/dist/api/GetDeviceInfo.d.ts +0 -9
- package/dist/api/GetDeviceInfo.d.ts.map +0 -1
- package/dist/api/protocol-v2/DevReboot.d.ts +0 -7
- package/dist/api/protocol-v2/DevReboot.d.ts.map +0 -1
- package/dist/api/protocol-v2/DeviceGetDeviceInfo.d.ts +0 -29
- package/dist/api/protocol-v2/DeviceGetDeviceInfo.d.ts.map +0 -1
- package/dist/api/protocol-v2/FactoryDeviceInfoSettings.d.ts +0 -7
- package/dist/api/protocol-v2/FactoryDeviceInfoSettings.d.ts.map +0 -1
- package/dist/api/protocol-v2/FactoryGetDeviceInfo.d.ts +0 -6
- package/dist/api/protocol-v2/FactoryGetDeviceInfo.d.ts.map +0 -1
- package/dist/api/protocol-v2/FilesystemDiskControl.d.ts +0 -13
- package/dist/api/protocol-v2/FilesystemDiskControl.d.ts.map +0 -1
- package/dist/api/protocol-v2/FilesystemFixPermission.d.ts.map +0 -1
- package/dist/api/protocol-v2/GetProtoVersion.d.ts +0 -6
- package/dist/api/protocol-v2/GetProtoVersion.d.ts.map +0 -1
- package/dist/deviceProfile/buildDeviceProfile.d.ts +0 -21
- package/dist/deviceProfile/buildDeviceProfile.d.ts.map +0 -1
- package/dist/types/api/getDeviceInfo.d.ts +0 -85
- package/dist/types/api/getDeviceInfo.d.ts.map +0 -1
- package/src/api/GetDeviceInfo.ts +0 -145
- package/src/api/protocol-v2/DevReboot.ts +0 -24
- package/src/api/protocol-v2/DeviceGetDeviceInfo.ts +0 -118
- package/src/api/protocol-v2/FactoryDeviceInfoSettings.ts +0 -29
- package/src/api/protocol-v2/FilesystemDiskControl.ts +0 -50
- package/src/deviceProfile/buildDeviceProfile.ts +0 -334
- package/src/types/api/getDeviceInfo.ts +0 -99
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ERRORS, HardwareError, HardwareErrorCode, wait } from '@onekeyfe/hd-shared';
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
DeviceRebootType,
|
|
4
4
|
PROTOCOL_V2_BLE_FILE_CHUNK_SIZE,
|
|
5
5
|
PROTOCOL_V2_WEBUSB_FILE_CHUNK_SIZE,
|
|
6
6
|
} from '@onekeyfe/hd-transport';
|
|
@@ -15,50 +15,208 @@ import {
|
|
|
15
15
|
getFirmwareType,
|
|
16
16
|
getLogger,
|
|
17
17
|
} from '../utils';
|
|
18
|
-
import {
|
|
18
|
+
import { getSysResourceBinary } from './firmware/getBinary';
|
|
19
19
|
import { DataManager } from '../data-manager';
|
|
20
20
|
import { FirmwareUpdateBaseMethod } from './firmware/FirmwareUpdateBaseMethod';
|
|
21
21
|
import { DevicePool } from '../device/DevicePool';
|
|
22
22
|
import {
|
|
23
23
|
PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST,
|
|
24
24
|
ProtocolV2FirmwareTargetType,
|
|
25
|
-
protocolV2FileNameToTargetId,
|
|
26
25
|
} from '../protocols/protocol-v2';
|
|
27
26
|
import { requestProtocolV2DeviceInfo } from '../protocols/protocol-v2/features';
|
|
28
27
|
import {
|
|
29
|
-
PROTOCOL_V2_FIRMWARE_UPDATE_RESPONSE_TYPES,
|
|
30
28
|
getProtocolV2UnknownErrorText,
|
|
31
29
|
isProtocolV2DeviceDisconnectedError,
|
|
32
30
|
} from './protocol-v2/helpers';
|
|
33
31
|
|
|
34
|
-
import type { FirmwareUpdateV4Params } from '../types/api/firmwareUpdate';
|
|
32
|
+
import type { FirmwareUpdateV4Params, FirmwareUpdateV4Target } from '../types/api/firmwareUpdate';
|
|
35
33
|
import type { EFirmwareType } from '@onekeyfe/hd-shared';
|
|
36
34
|
import type { PROTO } from '../constants';
|
|
37
35
|
import type { TypedResponseMessage } from '../device/DeviceCommands';
|
|
38
|
-
import type {
|
|
36
|
+
import type {
|
|
37
|
+
Features,
|
|
38
|
+
IFirmwareReleaseInfo,
|
|
39
|
+
IProtocolV2FirmwareComponent,
|
|
40
|
+
IVersionArray,
|
|
41
|
+
} from '../types';
|
|
39
42
|
|
|
40
43
|
const Log = getLogger(LoggerNames.Method);
|
|
41
44
|
|
|
42
45
|
const SESSION_ERROR = 'session not found';
|
|
43
46
|
const PROTOCOL_V2_BOOTLOADER_RECONNECT_TIMEOUT = 60 * 1000;
|
|
47
|
+
const PROTOCOL_V2_FINAL_RECONNECT_TIMEOUT = 2 * 60 * 1000;
|
|
44
48
|
const PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT = 5 * 1000;
|
|
49
|
+
const PROTOCOL_V2_START_UPDATE_TIMEOUT = 3 * 60 * 1000;
|
|
45
50
|
const PROTOCOL_V2_INSTALL_TIMEOUT = 5 * 60 * 1000;
|
|
46
|
-
const
|
|
51
|
+
const PROTOCOL_V2_TARGET_STATUS_PENDING = 0;
|
|
47
52
|
const PROTOCOL_V2_TARGET_STATUS_IN_PROGRESS = 1;
|
|
48
|
-
const
|
|
53
|
+
const PROTOCOL_V2_TARGET_STATUS_FINISHED = 2;
|
|
54
|
+
const PROTOCOL_V2_TARGET_STATUS_FAILED_MIN = 3;
|
|
49
55
|
const PROTOCOL_V2_CONNECT_PROTOCOL = 'V2';
|
|
50
|
-
const PROTOCOL_V2_FIRMWARE_STAGING_VOLUME = '
|
|
56
|
+
const PROTOCOL_V2_FIRMWARE_STAGING_VOLUME = 'vol0:/';
|
|
51
57
|
const PROTOCOL_V2_MIN_FILE_CHUNK_SIZE = 64;
|
|
58
|
+
const PROTOCOL_V2_CONNECT_RETRY_COUNT = 10;
|
|
59
|
+
const PROTOCOL_V2_CONNECT_POLL_INTERVAL = 500;
|
|
60
|
+
const PROTOCOL_V2_CONNECT_SINGLE_TIMEOUT = 75 * 1000;
|
|
61
|
+
const PROTOCOL_V2_DEVICE_INFO_READY_TIMEOUT = 60 * 1000;
|
|
62
|
+
const PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT = 3;
|
|
63
|
+
const PROTOCOL_V2_OKPP_HEADER_SIZE = 0x52a0;
|
|
64
|
+
const PROTOCOL_V2_OKPP_PAYLOAD_HASH_OFFSET = 0x200;
|
|
65
|
+
const PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET = 0x240;
|
|
66
|
+
const PROTOCOL_V2_OKPP_HASH_SIZE = 64;
|
|
67
|
+
|
|
68
|
+
const getProtocolV2DeviceTransferProgress = (
|
|
69
|
+
bytesBeforeChunk: number,
|
|
70
|
+
bytesAfterChunk: number,
|
|
71
|
+
totalBytes: number
|
|
72
|
+
) => {
|
|
73
|
+
if (!Number.isFinite(totalBytes) || totalBytes <= 0) {
|
|
74
|
+
return 100;
|
|
75
|
+
}
|
|
76
|
+
if (bytesBeforeChunk <= 0 && bytesAfterChunk < totalBytes) {
|
|
77
|
+
return 0;
|
|
78
|
+
}
|
|
79
|
+
if (bytesAfterChunk >= totalBytes) {
|
|
80
|
+
return 100;
|
|
81
|
+
}
|
|
82
|
+
return Math.min(Math.max(Math.ceil((bytesAfterChunk / totalBytes) * 100), 1), 99);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const formatProtocolV2TransferSpeed = (bytes: number, elapsedMs: number) => {
|
|
86
|
+
const safeElapsedMs = Math.max(elapsedMs, 1);
|
|
87
|
+
return (bytes / 1024 / (safeElapsedMs / 1000)).toFixed(2);
|
|
88
|
+
};
|
|
52
89
|
|
|
53
90
|
type ProtocolV2FirmwareUpdateStatusTarget = {
|
|
54
|
-
target_id: number;
|
|
55
|
-
status
|
|
91
|
+
target_id: number | string;
|
|
92
|
+
status?: number | string;
|
|
93
|
+
payload_version?: number;
|
|
94
|
+
path?: string;
|
|
56
95
|
};
|
|
57
96
|
|
|
58
|
-
type ProtocolV2FirmwareUpdateStartResponse =
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
97
|
+
type ProtocolV2FirmwareUpdateStartResponse = TypedResponseMessage<'Success'>;
|
|
98
|
+
|
|
99
|
+
type ProtocolV2TargetBinary = { fileName: string; binary: ArrayBuffer; targetId: number };
|
|
100
|
+
type ProtocolV2InstallItem = ProtocolV2TargetBinary & {
|
|
101
|
+
kind: ProtocolV2RemoteComponentTarget['kind'];
|
|
102
|
+
};
|
|
103
|
+
type ProtocolV2InstallTarget = ProtocolV2InstallItem & {
|
|
104
|
+
path: string;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
type ProtocolV2FileTransferParams = PROTO.FirmwareUpload & {
|
|
108
|
+
filePath: string;
|
|
109
|
+
processedSize?: number;
|
|
110
|
+
totalSize?: number;
|
|
111
|
+
onTransferredBytes?: (transferredBytes: number) => void;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
/** RESC bundle okpkg written independently to devicePath through FileWrite. */
|
|
115
|
+
type ProtocolV2ResourceBundleBinary = {
|
|
116
|
+
name: string;
|
|
117
|
+
binary: ArrayBuffer;
|
|
118
|
+
devicePath: string;
|
|
119
|
+
/** Download URL for remote-config mode; omitted in manual mode. */
|
|
120
|
+
url?: string;
|
|
121
|
+
version?: IVersionArray;
|
|
122
|
+
payloadHash?: string;
|
|
123
|
+
headerHash?: string;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
type ProtocolV2RemoteComponentBinary = ProtocolV2RemoteComponentTarget & {
|
|
127
|
+
binary: ArrayBuffer;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
type ProtocolV2RemoteComponentTarget = {
|
|
131
|
+
fileName: string;
|
|
132
|
+
targetId: number;
|
|
133
|
+
kind: 'bootloader' | 'firmware';
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
type ProtocolV2OkppHeader = {
|
|
137
|
+
type: string;
|
|
138
|
+
version: IVersionArray;
|
|
139
|
+
payloadHash: string;
|
|
140
|
+
headerHash: string;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const PROTOCOL_V2_REMOTE_COMPONENT_TARGETS: Readonly<
|
|
144
|
+
Record<string, ProtocolV2RemoteComponentTarget>
|
|
145
|
+
> = {
|
|
146
|
+
BOOTLOADER: {
|
|
147
|
+
fileName: 'bootloader.bin',
|
|
148
|
+
targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER,
|
|
149
|
+
kind: 'bootloader',
|
|
150
|
+
},
|
|
151
|
+
APPLICATION_P1: {
|
|
152
|
+
fileName: 'application_p1.bin',
|
|
153
|
+
targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1,
|
|
154
|
+
kind: 'firmware',
|
|
155
|
+
},
|
|
156
|
+
APPLICATION_P2: {
|
|
157
|
+
fileName: 'application_p2.bin',
|
|
158
|
+
targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P2,
|
|
159
|
+
kind: 'firmware',
|
|
160
|
+
},
|
|
161
|
+
COPROCESSOR: {
|
|
162
|
+
fileName: 'coprocessor.bin',
|
|
163
|
+
targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_COPROCESSOR,
|
|
164
|
+
kind: 'firmware',
|
|
165
|
+
},
|
|
166
|
+
SE01: {
|
|
167
|
+
fileName: 'se01.bin',
|
|
168
|
+
targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE01,
|
|
169
|
+
kind: 'firmware',
|
|
170
|
+
},
|
|
171
|
+
SE02: {
|
|
172
|
+
fileName: 'se02.bin',
|
|
173
|
+
targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE02,
|
|
174
|
+
kind: 'firmware',
|
|
175
|
+
},
|
|
176
|
+
SE03: {
|
|
177
|
+
fileName: 'se03.bin',
|
|
178
|
+
targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE03,
|
|
179
|
+
kind: 'firmware',
|
|
180
|
+
},
|
|
181
|
+
SE04: {
|
|
182
|
+
fileName: 'se04.bin',
|
|
183
|
+
targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE04,
|
|
184
|
+
kind: 'firmware',
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID = new Map<number, FirmwareUpdateV4Target>([
|
|
189
|
+
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER, 'boot'],
|
|
190
|
+
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1, 'app_v1'],
|
|
191
|
+
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P2, 'app_v2'],
|
|
192
|
+
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_COPROCESSOR, 'coprocessor'],
|
|
193
|
+
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE01, 'se01'],
|
|
194
|
+
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE02, 'se02'],
|
|
195
|
+
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE03, 'se03'],
|
|
196
|
+
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE04, 'se04'],
|
|
197
|
+
]);
|
|
198
|
+
|
|
199
|
+
const PROTOCOL_V2_ROMLOADER_UNSUPPORTED_MESSAGE =
|
|
200
|
+
'FW_MGMT_TARGET_ROMLOADER is not accepted by the current Pro2 bootloader update request. Flash romloader with the loader-specific flow instead of firmwareUpdateV4.';
|
|
201
|
+
|
|
202
|
+
// hd-transport historically decodes scalar enums as enum-name strings.
|
|
203
|
+
// Map them back to firmware protocol values before internal comparisons.
|
|
204
|
+
const PROTOCOL_V2_TARGET_ID_BY_DECODED_NAME = new Map<string, number>(
|
|
205
|
+
Object.entries(ProtocolV2FirmwareTargetType).map(([key, value]) => [key, value])
|
|
206
|
+
);
|
|
207
|
+
const PROTOCOL_V2_TARGET_STATUS_BY_DECODED_NAME = new Map<string, number>([
|
|
208
|
+
['FW_MGMT_UPDATER_TASK_STATUS_PENDING', PROTOCOL_V2_TARGET_STATUS_PENDING],
|
|
209
|
+
['FW_MGMT_UPDATER_TASK_STATUS_IN_PROGRESS', PROTOCOL_V2_TARGET_STATUS_IN_PROGRESS],
|
|
210
|
+
['FW_MGMT_UPDATER_TASK_STATUS_FINISHED', PROTOCOL_V2_TARGET_STATUS_FINISHED],
|
|
211
|
+
['FW_MGMT_UPDATER_TASK_STATUS_FAILED_FILE_NOT_FOUND', 3],
|
|
212
|
+
['FW_MGMT_UPDATER_TASK_STATUS_FAILED_FILE_READ', 4],
|
|
213
|
+
['FW_MGMT_UPDATER_TASK_STATUS_FAILED_FILE_WRITE', 5],
|
|
214
|
+
['FW_MGMT_UPDATER_TASK_STATUS_FAILED_VERIFY', 6],
|
|
215
|
+
['FW_MGMT_UPDATER_TASK_STATUS_FAILED_INSTALL', 7],
|
|
216
|
+
['FW_MGMT_UPDATER_TASK_STATUS_FAILED_ABORT', 8],
|
|
217
|
+
['FW_MGMT_UPDATER_TASK_STATUS_FAILED_BUSY', 9],
|
|
218
|
+
['FW_MGMT_UPDATER_TASK_STATUS_FAILED_ENTRY_OUT_OF_BOUNDS', 10],
|
|
219
|
+
]);
|
|
62
220
|
|
|
63
221
|
const isProtocolV2ReconnectProbeError = (error: unknown) => {
|
|
64
222
|
const message = getProtocolV2UnknownErrorText(error).toLowerCase();
|
|
@@ -68,43 +226,200 @@ const isProtocolV2ReconnectProbeError = (error: unknown) => {
|
|
|
68
226
|
);
|
|
69
227
|
};
|
|
70
228
|
|
|
71
|
-
const
|
|
229
|
+
const isProtocolV2FirmwareStatusEndpointUnavailable = (error: unknown) => {
|
|
72
230
|
const message = getProtocolV2UnknownErrorText(error).toLowerCase();
|
|
73
231
|
return (
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
232
|
+
message.includes('handler not registered') ||
|
|
233
|
+
message.includes('message handler not found') ||
|
|
234
|
+
message.includes('unsupported message')
|
|
235
|
+
);
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const isProtocolV2TargetStatusFinished = (status: ProtocolV2FirmwareUpdateStatusTarget['status']) =>
|
|
239
|
+
normalizeProtocolV2TargetStatus(status) === PROTOCOL_V2_TARGET_STATUS_FINISHED;
|
|
240
|
+
|
|
241
|
+
const isProtocolV2TargetStatusInProgress = (
|
|
242
|
+
status: ProtocolV2FirmwareUpdateStatusTarget['status']
|
|
243
|
+
) =>
|
|
244
|
+
normalizeProtocolV2TargetStatus(status) === PROTOCOL_V2_TARGET_STATUS_PENDING ||
|
|
245
|
+
normalizeProtocolV2TargetStatus(status) === PROTOCOL_V2_TARGET_STATUS_IN_PROGRESS;
|
|
246
|
+
|
|
247
|
+
const isProtocolV2TargetStatusFailed = (status: ProtocolV2FirmwareUpdateStatusTarget['status']) => {
|
|
248
|
+
const normalizedStatus = normalizeProtocolV2TargetStatus(status);
|
|
249
|
+
return (
|
|
250
|
+
typeof normalizedStatus === 'number' && normalizedStatus >= PROTOCOL_V2_TARGET_STATUS_FAILED_MIN
|
|
79
251
|
);
|
|
80
252
|
};
|
|
81
253
|
|
|
254
|
+
const normalizeProtocolV2TargetId = (targetId: number | string) => {
|
|
255
|
+
if (typeof targetId === 'number') {
|
|
256
|
+
return targetId;
|
|
257
|
+
}
|
|
258
|
+
return PROTOCOL_V2_TARGET_ID_BY_DECODED_NAME.get(targetId);
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const normalizeProtocolV2TargetStatus = (
|
|
262
|
+
status: ProtocolV2FirmwareUpdateStatusTarget['status']
|
|
263
|
+
) => {
|
|
264
|
+
if (typeof status === 'number') {
|
|
265
|
+
return status;
|
|
266
|
+
}
|
|
267
|
+
if (typeof status === 'string') {
|
|
268
|
+
return PROTOCOL_V2_TARGET_STATUS_BY_DECODED_NAME.get(status);
|
|
269
|
+
}
|
|
270
|
+
return undefined;
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
const normalizeProtocolV2Hex = (value?: string) => value?.replace(/^0x/i, '').toLowerCase();
|
|
274
|
+
|
|
275
|
+
const versionArrayToNumber = (version?: IVersionArray) => {
|
|
276
|
+
if (!version) return undefined;
|
|
277
|
+
return version[0] * 0x10000 + version[1] * 0x100 + version[2];
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
const compareProtocolV2Versions = (current?: IVersionArray, target?: IVersionArray) => {
|
|
281
|
+
const currentNumber = versionArrayToNumber(current);
|
|
282
|
+
const targetNumber = versionArrayToNumber(target);
|
|
283
|
+
if (currentNumber === undefined || targetNumber === undefined) return undefined;
|
|
284
|
+
return currentNumber - targetNumber;
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
const bytesToHex = (bytes: Uint8Array) =>
|
|
288
|
+
Array.from(bytes)
|
|
289
|
+
.map(byte => byte.toString(16).padStart(2, '0'))
|
|
290
|
+
.join('');
|
|
291
|
+
|
|
292
|
+
const hexToProtocolV2Bytes = (hex: string) => {
|
|
293
|
+
const normalized = hex.replace(/^0x/i, '');
|
|
294
|
+
if (!normalized || normalized.length % 2 !== 0 || /[^0-9a-f]/i.test(normalized)) {
|
|
295
|
+
return new Uint8Array(0);
|
|
296
|
+
}
|
|
297
|
+
const bytes = new Uint8Array(normalized.length / 2);
|
|
298
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
299
|
+
bytes[i] = Number.parseInt(normalized.slice(i * 2, i * 2 + 2), 16);
|
|
300
|
+
}
|
|
301
|
+
return bytes;
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
const toProtocolV2Bytes = (value: unknown): Uint8Array => {
|
|
305
|
+
if (value instanceof Uint8Array) return value;
|
|
306
|
+
if (value instanceof ArrayBuffer) return new Uint8Array(value);
|
|
307
|
+
if (ArrayBuffer.isView(value)) {
|
|
308
|
+
return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
|
309
|
+
}
|
|
310
|
+
if (typeof value === 'string') return hexToProtocolV2Bytes(value);
|
|
311
|
+
return new Uint8Array(0);
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
const toProtocolV2FiniteNumber = (value: unknown) => {
|
|
315
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
|
316
|
+
if (typeof value === 'string') {
|
|
317
|
+
const numeric = Number(value);
|
|
318
|
+
return Number.isFinite(numeric) ? numeric : undefined;
|
|
319
|
+
}
|
|
320
|
+
if (value && typeof value === 'object') {
|
|
321
|
+
const longLike = value as { toNumber?: () => number };
|
|
322
|
+
if (typeof longLike.toNumber === 'function') {
|
|
323
|
+
const numeric = longLike.toNumber();
|
|
324
|
+
return Number.isFinite(numeric) ? numeric : undefined;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return undefined;
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
const readProtocolV2Ascii = (bytes: Uint8Array, offset: number, length: number) =>
|
|
331
|
+
Array.from(bytes.slice(offset, offset + length))
|
|
332
|
+
.map(byte => String.fromCharCode(byte))
|
|
333
|
+
.join('');
|
|
334
|
+
|
|
335
|
+
const parseProtocolV2OkppHeader = (bytes: Uint8Array): ProtocolV2OkppHeader | null => {
|
|
336
|
+
if (bytes.byteLength < PROTOCOL_V2_OKPP_HEADER_SIZE) return null;
|
|
337
|
+
if (readProtocolV2Ascii(bytes, 0, 4) !== 'OKPP') return null;
|
|
338
|
+
|
|
339
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
340
|
+
const headerLen = view.getUint32(0x0c, true);
|
|
341
|
+
if (headerLen !== PROTOCOL_V2_OKPP_HEADER_SIZE) return null;
|
|
342
|
+
|
|
343
|
+
const packedVersion = view.getUint32(0x10, true);
|
|
344
|
+
return {
|
|
345
|
+
type: readProtocolV2Ascii(bytes, 0x08, 4),
|
|
346
|
+
version: [
|
|
347
|
+
Math.floor(packedVersion / 0x10000) % 0x100,
|
|
348
|
+
Math.floor(packedVersion / 0x100) % 0x100,
|
|
349
|
+
packedVersion % 0x100,
|
|
350
|
+
],
|
|
351
|
+
payloadHash: bytesToHex(
|
|
352
|
+
bytes.slice(
|
|
353
|
+
PROTOCOL_V2_OKPP_PAYLOAD_HASH_OFFSET,
|
|
354
|
+
PROTOCOL_V2_OKPP_PAYLOAD_HASH_OFFSET + PROTOCOL_V2_OKPP_HASH_SIZE
|
|
355
|
+
)
|
|
356
|
+
),
|
|
357
|
+
headerHash: bytesToHex(
|
|
358
|
+
bytes.slice(
|
|
359
|
+
PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET,
|
|
360
|
+
PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET + PROTOCOL_V2_OKPP_HASH_SIZE
|
|
361
|
+
)
|
|
362
|
+
),
|
|
363
|
+
};
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
export const assertProtocolV2ReconnectIdentity = (
|
|
367
|
+
expectedDeviceId?: string,
|
|
368
|
+
actualDeviceId?: string,
|
|
369
|
+
options: { allowMissingActual?: boolean } = {}
|
|
370
|
+
) => {
|
|
371
|
+
if (!expectedDeviceId) return;
|
|
372
|
+
if (!actualDeviceId) {
|
|
373
|
+
if (options.allowMissingActual) return;
|
|
374
|
+
throw ERRORS.TypedError(
|
|
375
|
+
HardwareErrorCode.DeviceNotFound,
|
|
376
|
+
'Protocol V2 reconnect identity unavailable'
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
if (actualDeviceId !== expectedDeviceId) {
|
|
380
|
+
throw ERRORS.TypedError(
|
|
381
|
+
HardwareErrorCode.DeviceNotFound,
|
|
382
|
+
`Protocol V2 reconnect identity mismatch: expected ${expectedDeviceId}, received ${actualDeviceId}`
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
|
|
82
387
|
/**
|
|
83
388
|
* FirmwareUpdateV4 is the complete Protocol V2 firmware update flow.
|
|
84
389
|
*
|
|
85
390
|
* It intentionally does not fall back to FirmwareUpdateV3/V1 behavior:
|
|
86
391
|
* - upload uses FilesystemFileWrite
|
|
87
|
-
* - install uses
|
|
88
|
-
* - completion reboots to normal, then polls
|
|
392
|
+
* - install uses DeviceFirmwareUpdateRequest
|
|
393
|
+
* - completion waits for target status to finish, reboots to normal, then polls DeviceInfo
|
|
89
394
|
*/
|
|
90
395
|
export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareUpdateV4Params> {
|
|
396
|
+
private protocolV2ExpectedDeviceId?: string;
|
|
397
|
+
|
|
91
398
|
init() {
|
|
92
399
|
this.allowDeviceMode = [UI_REQUEST.BOOTLOADER, UI_REQUEST.NOT_INITIALIZE];
|
|
93
400
|
this.requireDeviceMode = [];
|
|
401
|
+
this.unlockPolicy = 'retry-on-locked';
|
|
94
402
|
this.useDevicePassphraseState = false;
|
|
95
403
|
this.skipForceUpdateCheck = true;
|
|
96
404
|
|
|
97
405
|
const { payload } = this;
|
|
98
406
|
|
|
407
|
+
if (typeof payload.retryCount !== 'number') {
|
|
408
|
+
payload.retryCount = PROTOCOL_V2_CONNECT_RETRY_COUNT;
|
|
409
|
+
}
|
|
410
|
+
if (typeof payload.pollIntervalTime !== 'number') {
|
|
411
|
+
payload.pollIntervalTime = PROTOCOL_V2_CONNECT_POLL_INTERVAL;
|
|
412
|
+
}
|
|
413
|
+
if (typeof payload.timeout !== 'number') {
|
|
414
|
+
payload.timeout = PROTOCOL_V2_CONNECT_SINGLE_TIMEOUT;
|
|
415
|
+
}
|
|
416
|
+
if (typeof payload.protocolV2DeviceInfoTimeoutMs !== 'number') {
|
|
417
|
+
payload.protocolV2DeviceInfoTimeoutMs = PROTOCOL_V2_DEVICE_INFO_READY_TIMEOUT;
|
|
418
|
+
}
|
|
419
|
+
|
|
99
420
|
validateParams(payload, [
|
|
100
|
-
{ name: 'bleVersion', type: 'array' },
|
|
101
|
-
{ name: 'bleBinary', type: 'buffer' },
|
|
102
421
|
{ name: 'chunkSize', type: 'number' },
|
|
103
|
-
{ name: 'firmwareVersion', type: 'array' },
|
|
104
|
-
{ name: 'firmwareBinary', type: 'buffer' },
|
|
105
|
-
{ name: 'resourceBinary', type: 'buffer' },
|
|
106
422
|
{ name: 'forcedUpdateRes', type: 'boolean' },
|
|
107
|
-
{ name: 'bootloaderVersion', type: 'array' },
|
|
108
423
|
{ name: 'bootloaderBinary', type: 'buffer' },
|
|
109
424
|
{ name: 'romloaderBinary', type: 'buffer' },
|
|
110
425
|
{ name: 'applicationP1Binary', type: 'buffer' },
|
|
@@ -115,16 +430,14 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
115
430
|
{ name: 'se03Binary', type: 'buffer' },
|
|
116
431
|
{ name: 'se04Binary', type: 'buffer' },
|
|
117
432
|
{ name: 'firmwareType', type: 'string' },
|
|
433
|
+
{ name: 'targetsToUpdate', type: 'array', allowEmpty: true },
|
|
118
434
|
{ name: 'platform', type: 'string' },
|
|
435
|
+
{ name: 'resourceBundleFiles', type: 'array', allowEmpty: true },
|
|
119
436
|
]);
|
|
120
437
|
|
|
121
438
|
this.params = {
|
|
122
|
-
bleBinary: payload.bleBinary,
|
|
123
439
|
chunkSize: payload.chunkSize,
|
|
124
|
-
firmwareBinary: payload.firmwareBinary,
|
|
125
440
|
forcedUpdateRes: payload.forcedUpdateRes,
|
|
126
|
-
bleVersion: payload.bleVersion,
|
|
127
|
-
bootloaderVersion: payload.bootloaderVersion,
|
|
128
441
|
bootloaderBinary: payload.bootloaderBinary,
|
|
129
442
|
romloaderBinary: payload.romloaderBinary,
|
|
130
443
|
applicationP1Binary: payload.applicationP1Binary,
|
|
@@ -134,9 +447,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
134
447
|
se02Binary: payload.se02Binary,
|
|
135
448
|
se03Binary: payload.se03Binary,
|
|
136
449
|
se04Binary: payload.se04Binary,
|
|
137
|
-
|
|
138
|
-
resourceBinary: payload.resourceBinary,
|
|
450
|
+
resourceBundleFiles: payload.resourceBundleFiles,
|
|
139
451
|
firmwareType: payload.firmwareType,
|
|
452
|
+
targetsToUpdate: payload.targetsToUpdate,
|
|
140
453
|
platform: payload.platform,
|
|
141
454
|
};
|
|
142
455
|
}
|
|
@@ -171,38 +484,50 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
171
484
|
|
|
172
485
|
private async runProtocolV2() {
|
|
173
486
|
const deviceFeatures = await this.getProtocolV2DeviceFeatures();
|
|
487
|
+
this.protocolV2ExpectedDeviceId = deviceFeatures.deviceId ?? undefined;
|
|
174
488
|
const deviceFirmwareType = getFirmwareType(deviceFeatures);
|
|
175
489
|
const firmwareType = this.params.firmwareType ?? deviceFirmwareType;
|
|
176
490
|
|
|
177
|
-
let
|
|
178
|
-
let fwBinaryMap: { fileName: string; binary: ArrayBuffer; targetId?: number }[] = [];
|
|
491
|
+
let fwBinaryMap: ProtocolV2TargetBinary[] = [];
|
|
179
492
|
let bootloaderBinary: ArrayBuffer | null = null;
|
|
493
|
+
let installItems: ProtocolV2InstallItem[] | undefined;
|
|
494
|
+
let resourceBundles: ProtocolV2ResourceBundleBinary[] | undefined;
|
|
180
495
|
try {
|
|
181
496
|
this.postTipMessage(FirmwareUpdateTipMessage.StartDownloadFirmware);
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
497
|
+
fwBinaryMap = this.collectExplicitTargetBinaries();
|
|
498
|
+
bootloaderBinary = this.prepareBootloaderBinary();
|
|
499
|
+
if (!this.hasExplicitProtocolV2Payload(fwBinaryMap)) {
|
|
500
|
+
const remoteBinaries = await this.prepareRemoteProtocolV2Binaries(
|
|
501
|
+
firmwareType,
|
|
502
|
+
deviceFeatures
|
|
503
|
+
);
|
|
504
|
+
bootloaderBinary = remoteBinaries.bootloaderBinary;
|
|
505
|
+
fwBinaryMap = remoteBinaries.fwBinaryMap;
|
|
506
|
+
installItems = remoteBinaries.installItems;
|
|
507
|
+
}
|
|
508
|
+
resourceBundles = this.prepareProtocolV2ResourceBundles(firmwareType, deviceFeatures);
|
|
509
|
+
if (resourceBundles?.length) {
|
|
510
|
+
resourceBundles = await this.prefetchProtocolV2ResourceBundles(resourceBundles);
|
|
511
|
+
}
|
|
187
512
|
this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
188
513
|
} catch (err) {
|
|
189
514
|
throw ERRORS.TypedError(HardwareErrorCode.FirmwareUpdateDownloadFailed, err.message ?? err);
|
|
190
515
|
}
|
|
191
516
|
|
|
192
|
-
if (!
|
|
517
|
+
if (!bootloaderBinary && fwBinaryMap.length === 0 && !resourceBundles?.length) {
|
|
193
518
|
throw ERRORS.TypedError(
|
|
194
519
|
HardwareErrorCode.FirmwareUpdateDownloadFailed,
|
|
195
520
|
'No firmware to update'
|
|
196
521
|
);
|
|
197
522
|
}
|
|
198
523
|
|
|
199
|
-
|
|
200
|
-
// await this.enterProtocolV2BootloaderMode();
|
|
524
|
+
await this.enterProtocolV2BootloaderMode();
|
|
201
525
|
|
|
202
526
|
await this.executeProtocolV2Update({
|
|
203
|
-
resourceBinary,
|
|
204
527
|
fwBinaryMap,
|
|
205
528
|
bootloaderBinary,
|
|
529
|
+
...(installItems ? { installItems } : undefined),
|
|
530
|
+
...(resourceBundles?.length ? { resourceBundles } : undefined),
|
|
206
531
|
});
|
|
207
532
|
|
|
208
533
|
await this.exitProtocolV2BootloaderToNormal();
|
|
@@ -215,50 +540,365 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
215
540
|
}
|
|
216
541
|
|
|
217
542
|
private async getProtocolV2DeviceFeatures(): Promise<Features> {
|
|
543
|
+
if (this.device.features) {
|
|
544
|
+
return this.device.features;
|
|
545
|
+
}
|
|
218
546
|
if (typeof this.device.getFeatures === 'function') {
|
|
219
547
|
const features = await this.device.getFeatures();
|
|
220
548
|
if (features) return features;
|
|
221
549
|
}
|
|
222
|
-
if (this.device.features) {
|
|
223
|
-
return this.device.features;
|
|
224
|
-
}
|
|
225
550
|
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'Device features not available');
|
|
226
551
|
}
|
|
227
552
|
|
|
228
|
-
private
|
|
229
|
-
|
|
230
|
-
|
|
553
|
+
private prepareBootloaderBinary(): ArrayBuffer | null {
|
|
554
|
+
return this.params.bootloaderBinary ?? null;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
private hasExplicitProtocolV2Payload(fwBinaryMap: ProtocolV2TargetBinary[]) {
|
|
558
|
+
return (
|
|
559
|
+
!!this.params.resourceBundleFiles?.length ||
|
|
560
|
+
!!this.params.bootloaderBinary ||
|
|
561
|
+
fwBinaryMap.length > 0
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
private buildProtocolV2InstallItems({
|
|
566
|
+
bootloaderBinary,
|
|
567
|
+
fwBinaryMap,
|
|
568
|
+
}: {
|
|
569
|
+
bootloaderBinary: ArrayBuffer | null;
|
|
570
|
+
fwBinaryMap: ProtocolV2TargetBinary[];
|
|
571
|
+
}): ProtocolV2InstallItem[] {
|
|
572
|
+
const installItems: ProtocolV2InstallItem[] = [];
|
|
573
|
+
|
|
574
|
+
if (bootloaderBinary) {
|
|
575
|
+
installItems.push({
|
|
576
|
+
fileName: 'bootloader.bin',
|
|
577
|
+
binary: bootloaderBinary,
|
|
578
|
+
targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER,
|
|
579
|
+
kind: 'bootloader',
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
installItems.push(...fwBinaryMap.map(item => ({ ...item, kind: 'firmware' as const })));
|
|
584
|
+
return installItems;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
private getRemoteComponentEntries(release: IFirmwareReleaseInfo) {
|
|
588
|
+
const { components } = release;
|
|
589
|
+
if (!components) return [];
|
|
590
|
+
|
|
591
|
+
const orderedKeys = [
|
|
592
|
+
...(release.installOrder ?? []),
|
|
593
|
+
...Object.keys(components).filter(key => !release.installOrder?.includes(key)),
|
|
594
|
+
];
|
|
595
|
+
|
|
596
|
+
return orderedKeys
|
|
597
|
+
.map(key => {
|
|
598
|
+
const component = components[key];
|
|
599
|
+
return component ? ([key, component] as const) : undefined;
|
|
600
|
+
})
|
|
601
|
+
.filter((entry): entry is readonly [string, IProtocolV2FirmwareComponent] => !!entry);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
private getRemoteComponentTarget(key: string, component: IProtocolV2FirmwareComponent) {
|
|
605
|
+
const targetName = component.target?.toUpperCase();
|
|
606
|
+
if (targetName === 'ROMLOADER') {
|
|
607
|
+
throw ERRORS.TypedError(
|
|
608
|
+
HardwareErrorCode.RuntimeError,
|
|
609
|
+
PROTOCOL_V2_ROMLOADER_UNSUPPORTED_MESSAGE
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
const target = PROTOCOL_V2_REMOTE_COMPONENT_TARGETS[targetName];
|
|
613
|
+
if (!target) {
|
|
614
|
+
throw ERRORS.TypedError(
|
|
615
|
+
HardwareErrorCode.RuntimeError,
|
|
616
|
+
`Unsupported Protocol V2 firmware component target: ${key}/${component.target}`
|
|
617
|
+
);
|
|
231
618
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
619
|
+
return target;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
private getProtocolV2ResourceFilePath(path: string) {
|
|
623
|
+
if (path.startsWith('vol')) return path;
|
|
624
|
+
if (path.startsWith('/')) return `vol0:${path}`;
|
|
625
|
+
return `vol0:/${path}`;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
private async readProtocolV2DeviceFileHeader(path: string) {
|
|
629
|
+
const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
|
|
630
|
+
const filePath = this.getProtocolV2ResourceFilePath(path);
|
|
631
|
+
const pathInfoRes = await typedCall('FilesystemPathInfoQuery', 'FilesystemPathInfo', {
|
|
632
|
+
path: filePath,
|
|
236
633
|
});
|
|
634
|
+
const fileSize = toProtocolV2FiniteNumber(pathInfoRes.message?.size);
|
|
635
|
+
if (
|
|
636
|
+
!pathInfoRes.message?.exist ||
|
|
637
|
+
pathInfoRes.message?.directory ||
|
|
638
|
+
fileSize === undefined ||
|
|
639
|
+
fileSize < PROTOCOL_V2_OKPP_HEADER_SIZE
|
|
640
|
+
) {
|
|
641
|
+
return null;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
const chunkSize = this.getProtocolV2FirmwareChunkSize();
|
|
645
|
+
const chunks: Uint8Array[] = [];
|
|
646
|
+
let offset = 0;
|
|
647
|
+
while (offset < PROTOCOL_V2_OKPP_HEADER_SIZE) {
|
|
648
|
+
const readLen = Math.min(chunkSize, PROTOCOL_V2_OKPP_HEADER_SIZE - offset);
|
|
649
|
+
const res = await typedCall('FilesystemFileRead', 'FilesystemFile', {
|
|
650
|
+
file: {
|
|
651
|
+
path: filePath,
|
|
652
|
+
offset,
|
|
653
|
+
total_size: 0,
|
|
654
|
+
},
|
|
655
|
+
chunk_len: readLen,
|
|
656
|
+
ui_percentage: undefined,
|
|
657
|
+
});
|
|
658
|
+
const data = toProtocolV2Bytes(res.message?.data);
|
|
659
|
+
if (data.byteLength === 0) return null;
|
|
660
|
+
chunks.push(data);
|
|
661
|
+
offset += data.byteLength;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
const headerBytes = new Uint8Array(offset);
|
|
665
|
+
let cursor = 0;
|
|
666
|
+
chunks.forEach(chunk => {
|
|
667
|
+
headerBytes.set(chunk, cursor);
|
|
668
|
+
cursor += chunk.byteLength;
|
|
669
|
+
});
|
|
670
|
+
return parseProtocolV2OkppHeader(headerBytes);
|
|
671
|
+
}
|
|
237
672
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
673
|
+
private async downloadRemoteProtocolV2Component(
|
|
674
|
+
key: string,
|
|
675
|
+
component: IProtocolV2FirmwareComponent
|
|
676
|
+
): Promise<ProtocolV2RemoteComponentBinary> {
|
|
677
|
+
const target = this.getRemoteComponentTarget(key, component);
|
|
678
|
+
if (!component.url) {
|
|
679
|
+
throw ERRORS.TypedError(
|
|
680
|
+
HardwareErrorCode.RuntimeError,
|
|
681
|
+
`Missing Protocol V2 firmware component url: ${key}/${component.target}`
|
|
682
|
+
);
|
|
241
683
|
}
|
|
242
|
-
|
|
243
|
-
|
|
684
|
+
|
|
685
|
+
const { binary } = await getSysResourceBinary(component.url);
|
|
686
|
+
return {
|
|
687
|
+
...target,
|
|
688
|
+
binary,
|
|
689
|
+
};
|
|
244
690
|
}
|
|
245
691
|
|
|
246
|
-
private async
|
|
692
|
+
private async prepareRemoteProtocolV2Binaries(firmwareType: EFirmwareType, features: Features) {
|
|
693
|
+
const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
|
|
694
|
+
|
|
695
|
+
let bootloaderBinary: ArrayBuffer | null = null;
|
|
696
|
+
const fwBinaryMap: ProtocolV2TargetBinary[] = [];
|
|
697
|
+
const installItems: ProtocolV2InstallItem[] = [];
|
|
698
|
+
|
|
699
|
+
if (!release) {
|
|
700
|
+
return {
|
|
701
|
+
bootloaderBinary,
|
|
702
|
+
fwBinaryMap,
|
|
703
|
+
installItems,
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
const entries = this.getRemoteComponentEntries(release);
|
|
708
|
+
const targetsToUpdate = new Set(this.params.targetsToUpdate ?? []);
|
|
709
|
+
|
|
710
|
+
for (const [key, component] of entries) {
|
|
711
|
+
const target = this.getRemoteComponentTarget(key, component);
|
|
712
|
+
const updateTarget = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(target.targetId);
|
|
713
|
+
const shouldInstall = updateTarget ? targetsToUpdate.has(updateTarget) : false;
|
|
714
|
+
if (shouldInstall) {
|
|
715
|
+
const remoteBinary = await this.downloadRemoteProtocolV2Component(key, component);
|
|
716
|
+
if (remoteBinary.kind === 'bootloader') {
|
|
717
|
+
bootloaderBinary = remoteBinary.binary;
|
|
718
|
+
installItems.push({
|
|
719
|
+
fileName: remoteBinary.fileName,
|
|
720
|
+
binary: remoteBinary.binary,
|
|
721
|
+
targetId: remoteBinary.targetId,
|
|
722
|
+
kind: remoteBinary.kind,
|
|
723
|
+
});
|
|
724
|
+
} else {
|
|
725
|
+
const binaryEntry = {
|
|
726
|
+
fileName: remoteBinary.fileName,
|
|
727
|
+
binary: remoteBinary.binary,
|
|
728
|
+
targetId: remoteBinary.targetId,
|
|
729
|
+
};
|
|
730
|
+
fwBinaryMap.push(binaryEntry);
|
|
731
|
+
installItems.push({ ...binaryEntry, kind: remoteBinary.kind });
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
return {
|
|
737
|
+
bootloaderBinary,
|
|
738
|
+
fwBinaryMap,
|
|
739
|
+
installItems,
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// ============================================================
|
|
744
|
+
// Incremental RESC bundle sync through FileRead and FilesystemFileWrite
|
|
745
|
+
// ============================================================
|
|
746
|
+
|
|
747
|
+
/**
|
|
748
|
+
* Prepare the RESC bundle list.
|
|
749
|
+
*
|
|
750
|
+
* Two modes, matching FirmwareUpdateV3 binary-versus-version behavior:
|
|
751
|
+
* - resourceBundleFiles supplied: use binaries directly without version checks.
|
|
752
|
+
* - Otherwise load release.resourceBundles from remote config.json; the binaries
|
|
753
|
+
* are prefetched before reboot and synchronization skips matching device headers.
|
|
754
|
+
*/
|
|
755
|
+
private prepareProtocolV2ResourceBundles(
|
|
247
756
|
firmwareType: EFirmwareType,
|
|
248
757
|
features: Features
|
|
249
|
-
):
|
|
250
|
-
|
|
251
|
-
|
|
758
|
+
): ProtocolV2ResourceBundleBinary[] | undefined {
|
|
759
|
+
// Manual binaries are installed directly without comparison.
|
|
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;
|
|
252
770
|
}
|
|
253
771
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
772
|
+
// Remote-config bundles are downloaded and compared later.
|
|
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[];
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
private async prefetchProtocolV2ResourceBundles(
|
|
788
|
+
bundles: ProtocolV2ResourceBundleBinary[]
|
|
789
|
+
): Promise<ProtocolV2ResourceBundleBinary[]> {
|
|
790
|
+
const prefetched: ProtocolV2ResourceBundleBinary[] = [];
|
|
791
|
+
for (const bundle of bundles) {
|
|
792
|
+
let { binary } = bundle;
|
|
793
|
+
if (binary.byteLength === 0) {
|
|
794
|
+
if (!bundle.url) {
|
|
795
|
+
throw new Error(`Missing Protocol V2 RESC bundle binary: ${bundle.name}`);
|
|
796
|
+
}
|
|
797
|
+
Log.log(`[FirmwareUpdateV4] downloading RESC bundle ${bundle.name} from ${bundle.url}`);
|
|
798
|
+
({ binary } = await getSysResourceBinary(bundle.url));
|
|
799
|
+
}
|
|
800
|
+
if (binary.byteLength === 0) {
|
|
801
|
+
throw new Error(`Protocol V2 RESC bundle is empty: ${bundle.name}`);
|
|
259
802
|
}
|
|
803
|
+
prefetched.push({ ...bundle, binary });
|
|
804
|
+
}
|
|
805
|
+
return prefetched;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
/**
|
|
809
|
+
* Synchronize RESC bundles to the device.
|
|
810
|
+
*
|
|
811
|
+
* Manual mode writes supplied binaries directly.
|
|
812
|
+
* Remote-config mode uses prefetched binaries and skips matching bundles after FileRead.
|
|
813
|
+
*/
|
|
814
|
+
private async syncProtocolV2ResourceBundles(
|
|
815
|
+
bundles: ProtocolV2ResourceBundleBinary[],
|
|
816
|
+
firmwareSize: number
|
|
817
|
+
): Promise<{ processedSize: number; totalSize: number }> {
|
|
818
|
+
const transferStartTime = Date.now();
|
|
819
|
+
const transferTransport = this.getProtocolV2FirmwareTransferTransport();
|
|
820
|
+
|
|
821
|
+
const isRemoteMode = bundles.some(bundle => !!bundle.url);
|
|
822
|
+
let bundlesToSync = bundles;
|
|
823
|
+
|
|
824
|
+
// Remote binaries were prefetched before bootloader entry. Only device-local
|
|
825
|
+
// header comparison and transfer are allowed in this phase.
|
|
826
|
+
if (isRemoteMode) {
|
|
827
|
+
const filtered: ProtocolV2ResourceBundleBinary[] = [];
|
|
828
|
+
for (const bundle of bundles) {
|
|
829
|
+
// Compare the existing device header.
|
|
830
|
+
const upToDate = await this.isProtocolV2ResourceBundleUpToDate(bundle);
|
|
831
|
+
if (upToDate) {
|
|
832
|
+
Log.log(`[FirmwareUpdateV4] skip RESC bundle ${bundle.name}; already up to date`);
|
|
833
|
+
} else {
|
|
834
|
+
filtered.push(bundle);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
bundlesToSync = filtered;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
if (bundlesToSync.length === 0) {
|
|
841
|
+
Log.log('[FirmwareUpdateV4] all RESC bundles up to date, nothing to sync');
|
|
842
|
+
return { processedSize: 0, totalSize: firmwareSize };
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
// Write directly to the device through FileWrite.
|
|
846
|
+
let totalSize = 0;
|
|
847
|
+
for (const b of bundlesToSync) totalSize += b.binary.byteLength;
|
|
848
|
+
|
|
849
|
+
const transferTotalSize = totalSize + firmwareSize;
|
|
850
|
+
let processedSize = 0;
|
|
851
|
+
for (const bundle of bundlesToSync) {
|
|
852
|
+
Log.log(
|
|
853
|
+
`[FirmwareUpdateV4] syncing RESC bundle ${bundle.name} -> ${bundle.devicePath} bytes=${bundle.binary.byteLength}`
|
|
854
|
+
);
|
|
855
|
+
processedSize = await this.protocolV2CommonUpdateProcess({
|
|
856
|
+
payload: bundle.binary,
|
|
857
|
+
filePath: bundle.devicePath,
|
|
858
|
+
processedSize,
|
|
859
|
+
totalSize: transferTotalSize,
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
const elapsedMs = Date.now() - transferStartTime;
|
|
864
|
+
Log.log(
|
|
865
|
+
`[FirmwareUpdateV4] RESC bundle sync finished transport=${transferTransport} bytes=${totalSize} elapsed=${(
|
|
866
|
+
elapsedMs / 1000
|
|
867
|
+
).toFixed(2)}s speed=${formatProtocolV2TransferSpeed(totalSize, elapsedMs)} KB/s`
|
|
868
|
+
);
|
|
869
|
+
return { processedSize, totalSize: transferTotalSize };
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
* Compare the existing okpkg OKPP header in remote-config mode.
|
|
874
|
+
*/
|
|
875
|
+
private async isProtocolV2ResourceBundleUpToDate(
|
|
876
|
+
bundle: ProtocolV2ResourceBundleBinary
|
|
877
|
+
): Promise<boolean> {
|
|
878
|
+
if (this.params.forcedUpdateRes) return false;
|
|
879
|
+
if (!bundle.version && !bundle.payloadHash) return false;
|
|
880
|
+
|
|
881
|
+
try {
|
|
882
|
+
const header = await this.readProtocolV2DeviceFileHeader(bundle.devicePath);
|
|
883
|
+
if (!header) return false;
|
|
884
|
+
|
|
885
|
+
if (bundle.version) {
|
|
886
|
+
const cmp = compareProtocolV2Versions(header.version, bundle.version);
|
|
887
|
+
if (cmp === undefined || cmp !== 0) return false;
|
|
888
|
+
}
|
|
889
|
+
if (bundle.payloadHash) {
|
|
890
|
+
const expected = normalizeProtocolV2Hex(bundle.payloadHash);
|
|
891
|
+
if (expected && header.payloadHash !== expected) return false;
|
|
892
|
+
}
|
|
893
|
+
if (bundle.headerHash) {
|
|
894
|
+
const expected = normalizeProtocolV2Hex(bundle.headerHash);
|
|
895
|
+
if (expected && header.headerHash !== expected) return false;
|
|
896
|
+
}
|
|
897
|
+
return true;
|
|
898
|
+
} catch (error) {
|
|
899
|
+
Log.log(`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check failed: `, error);
|
|
900
|
+
return false;
|
|
260
901
|
}
|
|
261
|
-
return null;
|
|
262
902
|
}
|
|
263
903
|
|
|
264
904
|
private isProtocolV2BootloaderMode() {
|
|
@@ -269,19 +909,19 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
269
909
|
}
|
|
270
910
|
|
|
271
911
|
async enterProtocolV2BootloaderMode() {
|
|
272
|
-
|
|
912
|
+
// romloader is the first update environment and forwards targets to bootloader.
|
|
913
|
+
// It rejects DeviceRebootType.Bootloader, so reuse the current connection.
|
|
914
|
+
if (this.isProtocolV2BootloaderMode() || this.device.features?.mode === 'romloader') {
|
|
915
|
+
Log.debug('Protocol V2 device is already in loader mode, skip reboot to bootloader');
|
|
273
916
|
return false;
|
|
274
917
|
}
|
|
275
918
|
|
|
276
919
|
try {
|
|
277
920
|
this.postTipMessage(FirmwareUpdateTipMessage.AutoRebootToBootloader);
|
|
278
|
-
await this.protocolV2Reboot(
|
|
921
|
+
await this.protocolV2Reboot(DeviceRebootType.Bootloader);
|
|
279
922
|
this.postTipMessage(FirmwareUpdateTipMessage.GoToBootloaderSuccess);
|
|
280
|
-
|
|
281
|
-
await this.
|
|
282
|
-
this.checkPromise = null;
|
|
283
|
-
await wait(1500);
|
|
284
|
-
await this.device.acquire?.();
|
|
923
|
+
await wait(1000);
|
|
924
|
+
await this.waitForProtocolV2BootloaderMode();
|
|
285
925
|
return true;
|
|
286
926
|
} catch (error) {
|
|
287
927
|
if (error instanceof HardwareError) {
|
|
@@ -292,268 +932,327 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
292
932
|
}
|
|
293
933
|
}
|
|
294
934
|
|
|
935
|
+
private async waitForProtocolV2BootloaderMode(
|
|
936
|
+
timeout = PROTOCOL_V2_BOOTLOADER_RECONNECT_TIMEOUT,
|
|
937
|
+
retryInterval = 1000
|
|
938
|
+
) {
|
|
939
|
+
const startTime = Date.now();
|
|
940
|
+
let lastError: unknown;
|
|
941
|
+
|
|
942
|
+
while (Date.now() - startTime < timeout) {
|
|
943
|
+
try {
|
|
944
|
+
await this.reconnectProtocolV2Device();
|
|
945
|
+
const deviceInfo = await requestProtocolV2DeviceInfo({
|
|
946
|
+
commands: this.device.getCommands(),
|
|
947
|
+
timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
|
|
948
|
+
});
|
|
949
|
+
// This is a reconnect probe after a Bootloader reboot. Bootloader does not
|
|
950
|
+
// support DeviceStatusGet, so the generic runtime-mode probe is invalid here.
|
|
951
|
+
const features = this.device.updateProtocolV2Features(deviceInfo, null, 'bootloader');
|
|
952
|
+
assertProtocolV2ReconnectIdentity(
|
|
953
|
+
this.protocolV2ExpectedDeviceId,
|
|
954
|
+
features.deviceId ?? undefined,
|
|
955
|
+
{ allowMissingActual: !!features.bootloaderMode }
|
|
956
|
+
);
|
|
957
|
+
if (features?.bootloaderMode) {
|
|
958
|
+
return features;
|
|
959
|
+
}
|
|
960
|
+
lastError = new Error('Protocol V2 device is reachable but is not in bootloader mode');
|
|
961
|
+
} catch (error) {
|
|
962
|
+
lastError = error;
|
|
963
|
+
Log.log('Protocol V2 bootloader mode not ready, polling reconnect: ', error);
|
|
964
|
+
}
|
|
965
|
+
await wait(retryInterval);
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
throw ERRORS.TypedError(
|
|
969
|
+
HardwareErrorCode.FirmwareUpdateAutoEnterBootFailure,
|
|
970
|
+
`Protocol V2 bootloader not ready within ${timeout / 1000}s: ${this.normalizeErrorMessage(
|
|
971
|
+
lastError
|
|
972
|
+
)}`
|
|
973
|
+
);
|
|
974
|
+
}
|
|
975
|
+
|
|
295
976
|
/**
|
|
296
|
-
*
|
|
297
|
-
*
|
|
977
|
+
* Collect explicit target binaries grouped by DeviceFirmwareTargetType.
|
|
978
|
+
* Filenames are display-only staging paths because target_id is explicit.
|
|
298
979
|
*/
|
|
299
980
|
private collectExplicitTargetBinaries() {
|
|
300
|
-
const entries:
|
|
981
|
+
const entries: ProtocolV2TargetBinary[] = [];
|
|
301
982
|
const push = (binary: ArrayBuffer | undefined, fileName: string, targetId: number) => {
|
|
302
983
|
if (binary) entries.push({ fileName, binary, targetId });
|
|
303
984
|
};
|
|
304
985
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
986
|
+
if (this.params.romloaderBinary) {
|
|
987
|
+
throw ERRORS.TypedError(
|
|
988
|
+
HardwareErrorCode.RuntimeError,
|
|
989
|
+
PROTOCOL_V2_ROMLOADER_UNSUPPORTED_MESSAGE
|
|
990
|
+
);
|
|
991
|
+
}
|
|
310
992
|
push(
|
|
311
993
|
this.params.applicationP1Binary,
|
|
312
994
|
'application_p1.bin',
|
|
313
|
-
ProtocolV2FirmwareTargetType.
|
|
995
|
+
ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1
|
|
314
996
|
);
|
|
315
997
|
push(
|
|
316
998
|
this.params.applicationP2Binary,
|
|
317
999
|
'application_p2.bin',
|
|
318
|
-
ProtocolV2FirmwareTargetType.
|
|
1000
|
+
ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P2
|
|
1001
|
+
);
|
|
1002
|
+
push(
|
|
1003
|
+
this.params.coprocessorBinary,
|
|
1004
|
+
'coprocessor.bin',
|
|
1005
|
+
ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_COPROCESSOR
|
|
319
1006
|
);
|
|
320
|
-
push(this.params.
|
|
321
|
-
push(this.params.
|
|
322
|
-
push(this.params.
|
|
323
|
-
push(this.params.
|
|
324
|
-
push(this.params.se04Binary, 'se04.bin', ProtocolV2FirmwareTargetType.TARGET_SE4);
|
|
1007
|
+
push(this.params.se01Binary, 'se01.bin', ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE01);
|
|
1008
|
+
push(this.params.se02Binary, 'se02.bin', ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE02);
|
|
1009
|
+
push(this.params.se03Binary, 'se03.bin', ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE03);
|
|
1010
|
+
push(this.params.se04Binary, 'se04.bin', ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE04);
|
|
325
1011
|
return entries;
|
|
326
1012
|
}
|
|
327
1013
|
|
|
328
|
-
private async prepareFirmwareAndBleBinary(firmwareType: EFirmwareType, features: Features) {
|
|
329
|
-
const fwBinaryMap: { fileName: string; binary: ArrayBuffer; targetId?: number }[] = [];
|
|
330
|
-
if (this.params.firmwareBinary) {
|
|
331
|
-
fwBinaryMap.push({
|
|
332
|
-
fileName: 'firmware.bin',
|
|
333
|
-
binary: this.params.firmwareBinary,
|
|
334
|
-
});
|
|
335
|
-
} else if (this.params.firmwareVersion) {
|
|
336
|
-
const firmwareBinary = (
|
|
337
|
-
await getBinary({
|
|
338
|
-
features,
|
|
339
|
-
version: this.params.firmwareVersion,
|
|
340
|
-
updateType: 'firmware',
|
|
341
|
-
isUpdateBootloader: false,
|
|
342
|
-
firmwareType,
|
|
343
|
-
})
|
|
344
|
-
).binary;
|
|
345
|
-
fwBinaryMap.push({
|
|
346
|
-
fileName: 'firmware.bin',
|
|
347
|
-
binary: firmwareBinary,
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
if (this.params.bleBinary) {
|
|
352
|
-
fwBinaryMap.push({
|
|
353
|
-
fileName: 'ble-firmware.bin',
|
|
354
|
-
binary: this.params.bleBinary,
|
|
355
|
-
});
|
|
356
|
-
} else if (this.params.bleVersion) {
|
|
357
|
-
const bleBinary = await getBinary({
|
|
358
|
-
features,
|
|
359
|
-
version: this.params.bleVersion,
|
|
360
|
-
updateType: 'ble',
|
|
361
|
-
firmwareType,
|
|
362
|
-
});
|
|
363
|
-
fwBinaryMap.push({
|
|
364
|
-
fileName: 'ble-firmware.bin',
|
|
365
|
-
binary: bleBinary.binary,
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
return fwBinaryMap;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
1014
|
private async executeProtocolV2Update({
|
|
373
|
-
resourceBinary,
|
|
374
1015
|
fwBinaryMap,
|
|
375
1016
|
bootloaderBinary,
|
|
1017
|
+
installItems,
|
|
1018
|
+
resourceBundles,
|
|
376
1019
|
}: {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
1020
|
+
fwBinaryMap?: ProtocolV2TargetBinary[];
|
|
1021
|
+
bootloaderBinary?: ArrayBuffer | null;
|
|
1022
|
+
installItems?: ProtocolV2InstallItem[];
|
|
1023
|
+
resourceBundles?: ProtocolV2ResourceBundleBinary[];
|
|
380
1024
|
}) {
|
|
381
|
-
|
|
382
|
-
|
|
1025
|
+
const orderedInstallItems =
|
|
1026
|
+
installItems ??
|
|
1027
|
+
this.buildProtocolV2InstallItems({
|
|
1028
|
+
bootloaderBinary: bootloaderBinary ?? null,
|
|
1029
|
+
fwBinaryMap: fwBinaryMap ?? [],
|
|
1030
|
+
});
|
|
383
1031
|
|
|
384
|
-
|
|
385
|
-
for (const
|
|
386
|
-
if (bootloaderBinary) totalSize += bootloaderBinary.byteLength;
|
|
1032
|
+
let firmwareSize = 0;
|
|
1033
|
+
for (const item of orderedInstallItems) firmwareSize += item.binary.byteLength;
|
|
387
1034
|
|
|
388
1035
|
this.postTipMessage(FirmwareUpdateTipMessage.StartTransferData);
|
|
389
1036
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
if (
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
totalSize,
|
|
400
|
-
});
|
|
401
|
-
targets.push({
|
|
402
|
-
target_id: ProtocolV2FirmwareTargetType.TARGET_RESOURCE,
|
|
403
|
-
path: resourceFilePath,
|
|
404
|
-
});
|
|
1037
|
+
let processedSize = 0;
|
|
1038
|
+
let totalSize = firmwareSize;
|
|
1039
|
+
if (resourceBundles?.length) {
|
|
1040
|
+
const resourceTransfer = await this.syncProtocolV2ResourceBundles(
|
|
1041
|
+
resourceBundles,
|
|
1042
|
+
firmwareSize
|
|
1043
|
+
);
|
|
1044
|
+
processedSize = resourceTransfer.processedSize;
|
|
1045
|
+
totalSize = resourceTransfer.totalSize;
|
|
405
1046
|
}
|
|
406
1047
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
});
|
|
415
|
-
targets.push({
|
|
416
|
-
target_id: ProtocolV2FirmwareTargetType.TARGET_MAIN_BOOT,
|
|
417
|
-
path: bootloaderPath,
|
|
418
|
-
});
|
|
1048
|
+
// Skip staging and installation when the update contains RESC bundles only.
|
|
1049
|
+
if (orderedInstallItems.length === 0) {
|
|
1050
|
+
Log.log('[FirmwareUpdateV4] no firmware targets to install (RESC bundles only)');
|
|
1051
|
+
if (totalSize > 0) {
|
|
1052
|
+
this.postProgressMessage(100, 'transferData');
|
|
1053
|
+
}
|
|
1054
|
+
return;
|
|
419
1055
|
}
|
|
1056
|
+
let transferredSize = processedSize;
|
|
1057
|
+
const transferStartTime = Date.now();
|
|
1058
|
+
const transferTransport = this.getProtocolV2FirmwareTransferTransport();
|
|
1059
|
+
const chunkSize = this.getProtocolV2FirmwareChunkSize();
|
|
1060
|
+
const onTransferredBytes = (bytes: number) => {
|
|
1061
|
+
transferredSize = bytes;
|
|
1062
|
+
};
|
|
1063
|
+
Log.log(
|
|
1064
|
+
`[FirmwareUpdateV4] transfer started transport=${transferTransport} total=${totalSize} bytes chunk=${chunkSize} bytes`
|
|
1065
|
+
);
|
|
420
1066
|
|
|
421
|
-
|
|
422
|
-
const firmwarePath = `${PROTOCOL_V2_FIRMWARE_STAGING_VOLUME}${fwbinary.fileName}`;
|
|
423
|
-
processedSize = await this.protocolV2CommonUpdateProcess({
|
|
424
|
-
payload: fwbinary.binary,
|
|
425
|
-
filePath: firmwarePath,
|
|
426
|
-
processedSize,
|
|
427
|
-
totalSize,
|
|
428
|
-
});
|
|
429
|
-
targets.push({
|
|
430
|
-
target_id: fwbinary.targetId ?? protocolV2FileNameToTargetId(fwbinary.fileName),
|
|
431
|
-
path: firmwarePath,
|
|
432
|
-
});
|
|
433
|
-
}
|
|
1067
|
+
const stagedInstallTargets: ProtocolV2InstallTarget[] = [];
|
|
434
1068
|
|
|
435
|
-
|
|
436
|
-
|
|
1069
|
+
try {
|
|
1070
|
+
for (const item of orderedInstallItems) {
|
|
1071
|
+
const filePath = this.getProtocolV2InstallItemStagingPath(item);
|
|
1072
|
+
Log.log(
|
|
1073
|
+
`[FirmwareUpdateV4] staging ${item.kind} via FilesystemFileWrite target=${item.targetId} path=${filePath} source=${item.fileName} bytes=${item.binary.byteLength}`
|
|
1074
|
+
);
|
|
1075
|
+
processedSize = await this.protocolV2CommonUpdateProcess({
|
|
1076
|
+
payload: item.binary,
|
|
1077
|
+
filePath,
|
|
1078
|
+
processedSize,
|
|
1079
|
+
totalSize,
|
|
1080
|
+
onTransferredBytes,
|
|
1081
|
+
});
|
|
1082
|
+
transferredSize = processedSize;
|
|
1083
|
+
await this.verifyProtocolV2StagedFile(filePath, item.binary.byteLength);
|
|
1084
|
+
|
|
1085
|
+
stagedInstallTargets.push({
|
|
1086
|
+
...item,
|
|
1087
|
+
path: filePath,
|
|
1088
|
+
});
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
if (totalSize > 0) {
|
|
1092
|
+
this.postProgressMessage(100, 'transferData');
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
const elapsedMs = Date.now() - transferStartTime;
|
|
1096
|
+
Log.log(
|
|
1097
|
+
`[FirmwareUpdateV4] transfer finished transport=${transferTransport} bytes=${totalSize} elapsed=${(
|
|
1098
|
+
elapsedMs / 1000
|
|
1099
|
+
).toFixed(2)}s speed=${formatProtocolV2TransferSpeed(totalSize, elapsedMs)} KB/s`
|
|
1100
|
+
);
|
|
1101
|
+
} catch (error) {
|
|
1102
|
+
const elapsedMs = Date.now() - transferStartTime;
|
|
1103
|
+
Log.warn(
|
|
1104
|
+
`[FirmwareUpdateV4] transfer failed transport=${transferTransport} bytes=${transferredSize}/${totalSize} elapsed=${(
|
|
1105
|
+
elapsedMs / 1000
|
|
1106
|
+
).toFixed(2)}s speed=${formatProtocolV2TransferSpeed(transferredSize, elapsedMs)} KB/s`
|
|
1107
|
+
);
|
|
1108
|
+
throw error;
|
|
437
1109
|
}
|
|
438
1110
|
|
|
439
1111
|
this.postTipMessage(FirmwareUpdateTipMessage.ConfirmOnDevice);
|
|
440
|
-
|
|
441
|
-
|
|
1112
|
+
|
|
1113
|
+
const allTargets = stagedInstallTargets.map(item => ({
|
|
1114
|
+
target_id: item.targetId,
|
|
1115
|
+
path: item.path,
|
|
1116
|
+
}));
|
|
1117
|
+
Log.log(`[FirmwareUpdateV4] DeviceFirmwareUpdateRequest targets=${JSON.stringify(allTargets)}`);
|
|
1118
|
+
await this.protocolV2StartFirmwareUpdate({ targets: allTargets });
|
|
1119
|
+
await this.waitForProtocolV2FirmwareUpdateComplete(allTargets);
|
|
442
1120
|
}
|
|
443
1121
|
|
|
444
|
-
private
|
|
445
|
-
|
|
446
|
-
return typedCall(
|
|
447
|
-
'DevGetFirmwareUpdateStatus',
|
|
448
|
-
'DevFirmwareUpdateStatus',
|
|
449
|
-
{},
|
|
450
|
-
{
|
|
451
|
-
timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
|
|
452
|
-
}
|
|
453
|
-
);
|
|
1122
|
+
private getProtocolV2InstallItemStagingPath(item: ProtocolV2InstallItem) {
|
|
1123
|
+
return `${PROTOCOL_V2_FIRMWARE_STAGING_VOLUME}${item.fileName}`;
|
|
454
1124
|
}
|
|
455
1125
|
|
|
456
|
-
private async
|
|
1126
|
+
private async verifyProtocolV2StagedFile(path: string, expectedSize: number) {
|
|
457
1127
|
const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
|
|
458
|
-
await typedCall(
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
1128
|
+
const response = await typedCall('FilesystemPathInfoQuery', 'FilesystemPathInfo', { path });
|
|
1129
|
+
const actualSize = toProtocolV2FiniteNumber(response.message?.size);
|
|
1130
|
+
if (!response.message?.exist || response.message?.directory || actualSize !== expectedSize) {
|
|
1131
|
+
throw ERRORS.TypedError(
|
|
1132
|
+
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
1133
|
+
`staged file verification failed: path=${path} exist=${!!response.message
|
|
1134
|
+
?.exist} expected=${expectedSize} actual=${actualSize ?? 'unknown'}`
|
|
1135
|
+
);
|
|
1136
|
+
}
|
|
466
1137
|
}
|
|
467
1138
|
|
|
468
1139
|
private assertProtocolV2TargetStatus(
|
|
469
1140
|
statusTargets: ProtocolV2FirmwareUpdateStatusTarget[],
|
|
470
1141
|
expectedTargetIds: Set<number>
|
|
471
1142
|
) {
|
|
1143
|
+
Log.log(
|
|
1144
|
+
`[FirmwareUpdateV4] DeviceFirmwareUpdateStatus records=${JSON.stringify(statusTargets)}`
|
|
1145
|
+
);
|
|
472
1146
|
const failedTarget = statusTargets.find(
|
|
473
1147
|
target =>
|
|
474
|
-
expectedTargetIds.has(target.target_id) &&
|
|
475
|
-
target.status
|
|
1148
|
+
expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1) &&
|
|
1149
|
+
isProtocolV2TargetStatusFailed(target.status)
|
|
476
1150
|
);
|
|
477
1151
|
if (failedTarget) {
|
|
1152
|
+
const failedTargetDetails = JSON.stringify({
|
|
1153
|
+
targetId: failedTarget.target_id,
|
|
1154
|
+
status: failedTarget.status,
|
|
1155
|
+
payloadVersion: failedTarget.payload_version,
|
|
1156
|
+
path: failedTarget.path,
|
|
1157
|
+
});
|
|
1158
|
+
Log.error(`[FirmwareUpdateV4] firmware install failed target=${failedTargetDetails}`);
|
|
478
1159
|
throw ERRORS.TypedError(
|
|
479
1160
|
HardwareErrorCode.FirmwareError,
|
|
480
|
-
`Protocol V2 firmware target
|
|
1161
|
+
`Protocol V2 firmware target failed: target=${failedTarget.target_id} status=${
|
|
1162
|
+
failedTarget.status ?? 'unknown'
|
|
1163
|
+
} payloadVersion=${failedTarget.payload_version ?? 'unknown'} path=${
|
|
1164
|
+
failedTarget.path ?? 'unknown'
|
|
1165
|
+
}`
|
|
481
1166
|
);
|
|
482
1167
|
}
|
|
483
1168
|
|
|
484
|
-
const
|
|
485
|
-
target
|
|
486
|
-
|
|
487
|
-
|
|
1169
|
+
const matchingTargets = statusTargets.filter(target =>
|
|
1170
|
+
expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1)
|
|
1171
|
+
);
|
|
1172
|
+
const completedTargets = matchingTargets.filter(target =>
|
|
1173
|
+
isProtocolV2TargetStatusFinished(target.status)
|
|
488
1174
|
);
|
|
489
1175
|
if (completedTargets.length === expectedTargetIds.size && expectedTargetIds.size > 0) {
|
|
1176
|
+
this.postProgressMessage(100, 'installingFirmware');
|
|
490
1177
|
return true;
|
|
491
1178
|
}
|
|
492
1179
|
|
|
493
|
-
|
|
494
|
-
target =>
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
1180
|
+
if (expectedTargetIds.size > 0 && matchingTargets.length > 0) {
|
|
1181
|
+
const hasInProgressTarget = matchingTargets.some(target =>
|
|
1182
|
+
isProtocolV2TargetStatusInProgress(target.status)
|
|
1183
|
+
);
|
|
1184
|
+
const completedProgress = Math.floor(
|
|
1185
|
+
(completedTargets.length / expectedTargetIds.size) * 100
|
|
1186
|
+
);
|
|
1187
|
+
// The protocol exposes no per-target percentage, so report coarse progress by
|
|
1188
|
+
// completed targets and use 1% once work starts to keep the UI responsive.
|
|
1189
|
+
const progress = Math.min(99, Math.max(completedProgress, hasInProgressTarget ? 1 : 0));
|
|
1190
|
+
this.postProgressMessage(progress, 'installingFirmware');
|
|
500
1191
|
}
|
|
501
1192
|
|
|
502
1193
|
return false;
|
|
503
1194
|
}
|
|
504
1195
|
|
|
505
1196
|
private async waitForProtocolV2FirmwareUpdateComplete(
|
|
506
|
-
targets: Array<{ target_id: number; path: string }
|
|
507
|
-
startResponse?: ProtocolV2FirmwareUpdateStartResponse
|
|
1197
|
+
targets: Array<{ target_id: number; path: string }>
|
|
508
1198
|
) {
|
|
509
1199
|
const expectedTargetIds = new Set(targets.map(target => target.target_id));
|
|
510
|
-
if (startResponse?.type === 'Success') {
|
|
511
|
-
return;
|
|
512
|
-
}
|
|
513
|
-
if (startResponse?.type === 'DevFirmwareUpdateStatus') {
|
|
514
|
-
const statusTargets = (startResponse.message.targets ??
|
|
515
|
-
[]) as ProtocolV2FirmwareUpdateStatusTarget[];
|
|
516
|
-
if (this.assertProtocolV2TargetStatus(statusTargets, expectedTargetIds)) {
|
|
517
|
-
return;
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
|
|
521
1200
|
const startTime = Date.now();
|
|
522
1201
|
let lastError: unknown;
|
|
523
1202
|
|
|
524
1203
|
while (Date.now() - startTime < PROTOCOL_V2_INSTALL_TIMEOUT) {
|
|
525
1204
|
try {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
1205
|
+
await this.reconnectProtocolV2Device();
|
|
1206
|
+
try {
|
|
1207
|
+
const statusResponse = await this.device.getCommands().typedCall(
|
|
1208
|
+
'DeviceFirmwareUpdateStatusGet',
|
|
1209
|
+
'DeviceFirmwareUpdateStatus',
|
|
1210
|
+
{
|
|
1211
|
+
fields: {
|
|
1212
|
+
status: true,
|
|
1213
|
+
payload_version: true,
|
|
1214
|
+
path: true,
|
|
1215
|
+
},
|
|
1216
|
+
},
|
|
1217
|
+
{ timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT }
|
|
1218
|
+
);
|
|
1219
|
+
if (
|
|
1220
|
+
this.assertProtocolV2TargetStatus(
|
|
1221
|
+
(statusResponse.message.records ?? []) as ProtocolV2FirmwareUpdateStatusTarget[],
|
|
1222
|
+
expectedTargetIds
|
|
1223
|
+
)
|
|
1224
|
+
) {
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
1227
|
+
lastError = new Error('Protocol V2 firmware targets are still installing');
|
|
1228
|
+
} catch (error) {
|
|
1229
|
+
if (
|
|
1230
|
+
error instanceof HardwareError &&
|
|
1231
|
+
error.errorCode === HardwareErrorCode.FirmwareError
|
|
1232
|
+
) {
|
|
1233
|
+
throw error;
|
|
1234
|
+
}
|
|
1235
|
+
// App firmware does not register DeviceFirmwareUpdateStatusGet. If the
|
|
1236
|
+
// device already rebooted into App, this endpoint error signals that the
|
|
1237
|
+
// install phase ended; the later phase performs the final readiness check.
|
|
1238
|
+
if (isProtocolV2FirmwareStatusEndpointUnavailable(error)) {
|
|
1239
|
+
Log.log(
|
|
1240
|
+
'[FirmwareUpdateV4] firmware status endpoint unavailable after reboot; continue with normal-mode verification'
|
|
1241
|
+
);
|
|
1242
|
+
return;
|
|
1243
|
+
}
|
|
1244
|
+
lastError = error;
|
|
1245
|
+
Log.log(
|
|
1246
|
+
'[FirmwareUpdateV4] DeviceFirmwareUpdateStatusGet unavailable during install: ',
|
|
1247
|
+
error
|
|
1248
|
+
);
|
|
531
1249
|
}
|
|
532
1250
|
} catch (error) {
|
|
533
1251
|
lastError = error;
|
|
534
1252
|
if (error instanceof HardwareError && error.errorCode === HardwareErrorCode.FirmwareError) {
|
|
535
1253
|
throw error;
|
|
536
1254
|
}
|
|
537
|
-
Log.log('Protocol V2 firmware install
|
|
538
|
-
if (isProtocolV2PollingTransientError(error)) {
|
|
539
|
-
try {
|
|
540
|
-
await this.reconnectProtocolV2Device();
|
|
541
|
-
} catch (reconnectError) {
|
|
542
|
-
lastError = reconnectError;
|
|
543
|
-
Log.log(
|
|
544
|
-
'Protocol V2 firmware install reconnect/status polling failed: ',
|
|
545
|
-
reconnectError
|
|
546
|
-
);
|
|
547
|
-
}
|
|
548
|
-
try {
|
|
549
|
-
await this.pingProtocolV2Device();
|
|
550
|
-
Log.log('Protocol V2 firmware status unavailable, Ping is ready');
|
|
551
|
-
return;
|
|
552
|
-
} catch (pingError) {
|
|
553
|
-
lastError = pingError;
|
|
554
|
-
Log.log('Protocol V2 firmware install Ping polling failed: ', pingError);
|
|
555
|
-
}
|
|
556
|
-
}
|
|
1255
|
+
Log.log('Protocol V2 firmware install device readiness probe failed: ', error);
|
|
557
1256
|
}
|
|
558
1257
|
await wait(1000);
|
|
559
1258
|
}
|
|
@@ -565,13 +1264,15 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
565
1264
|
}
|
|
566
1265
|
|
|
567
1266
|
private async exitProtocolV2BootloaderToNormal() {
|
|
568
|
-
this.
|
|
569
|
-
|
|
1267
|
+
await this.reconnectProtocolV2Device();
|
|
1268
|
+
// The connection may still be in bootloader. Request a Normal reboot directly;
|
|
1269
|
+
// repeating it after an automatic App reboot is idempotent.
|
|
1270
|
+
await this.protocolV2Reboot(DeviceRebootType.Normal);
|
|
570
1271
|
}
|
|
571
1272
|
|
|
572
1273
|
private async waitForProtocolV2FinalFeatures() {
|
|
573
1274
|
const features = await this.waitForProtocolV2ReconnectAndFeatures(
|
|
574
|
-
|
|
1275
|
+
PROTOCOL_V2_FINAL_RECONNECT_TIMEOUT
|
|
575
1276
|
);
|
|
576
1277
|
|
|
577
1278
|
const bootloaderVersion = getDeviceBootloaderVersion(features).join('.');
|
|
@@ -600,11 +1301,30 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
600
1301
|
const deviceInfo = await requestProtocolV2DeviceInfo({
|
|
601
1302
|
commands: this.device.getCommands(),
|
|
602
1303
|
timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
|
|
603
|
-
//
|
|
1304
|
+
// Completion needs target versions only; keep scope aligned with the request.
|
|
604
1305
|
request: PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST,
|
|
605
1306
|
});
|
|
606
|
-
|
|
1307
|
+
const features = await this.device.probeProtocolV2RuntimeState(
|
|
1308
|
+
deviceInfo,
|
|
1309
|
+
PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT
|
|
1310
|
+
);
|
|
1311
|
+
assertProtocolV2ReconnectIdentity(
|
|
1312
|
+
this.protocolV2ExpectedDeviceId,
|
|
1313
|
+
features.deviceId ?? undefined
|
|
1314
|
+
);
|
|
1315
|
+
if (features.mode !== 'normal' || features.bootloaderMode) {
|
|
1316
|
+
throw ERRORS.TypedError(
|
|
1317
|
+
HardwareErrorCode.DeviceNotFound,
|
|
1318
|
+
'Protocol V2 device is still in bootloader mode'
|
|
1319
|
+
);
|
|
1320
|
+
}
|
|
1321
|
+
return features;
|
|
607
1322
|
} catch (error) {
|
|
1323
|
+
// A confirmed deviceId change is a firmware contract violation and cannot be
|
|
1324
|
+
// fixed by polling. A temporarily missing identity may still become ready.
|
|
1325
|
+
if (this.isProtocolV2ReconnectIdentityMismatch(error)) {
|
|
1326
|
+
throw error;
|
|
1327
|
+
}
|
|
608
1328
|
lastError = error;
|
|
609
1329
|
Log.log('Protocol V2 normal mode not ready, polling Ping: ', error);
|
|
610
1330
|
await wait(1000);
|
|
@@ -628,27 +1348,105 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
628
1348
|
const deviceDiff = await this.device.deviceConnector?.enumerate();
|
|
629
1349
|
const devicesDescriptor = deviceDiff?.descriptors ?? [];
|
|
630
1350
|
|
|
631
|
-
|
|
1351
|
+
if (
|
|
1352
|
+
DataManager.isBrowserWebUsb(DataManager.getSettings('env')) &&
|
|
1353
|
+
devicesDescriptor.length === 1
|
|
1354
|
+
) {
|
|
1355
|
+
this.device.updateDescriptor(
|
|
1356
|
+
{
|
|
1357
|
+
...devicesDescriptor[0],
|
|
1358
|
+
protocolType: PROTOCOL_V2_CONNECT_PROTOCOL,
|
|
1359
|
+
},
|
|
1360
|
+
true
|
|
1361
|
+
);
|
|
1362
|
+
await this.ensureProtocolV2DeviceAcquired();
|
|
1363
|
+
this.device.commands.disposed = false;
|
|
1364
|
+
this.device.getCommands().mainId = this.device.mainId ?? '';
|
|
1365
|
+
assertProtocolV2ReconnectIdentity(
|
|
1366
|
+
this.protocolV2ExpectedDeviceId,
|
|
1367
|
+
this.device.getCurrentDeviceId(),
|
|
1368
|
+
// Cached identity may be absent immediately after acquire. Reject only an
|
|
1369
|
+
// existing mismatch; the final refresh performs strict identity validation.
|
|
1370
|
+
{ allowMissingActual: true }
|
|
1371
|
+
);
|
|
1372
|
+
return;
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
// App and bootloader serials may differ temporarily. During V4 reconnect, accept
|
|
1376
|
+
// only a uniquely enumerated device instead of repeatedly using the old App path.
|
|
1377
|
+
const { deviceList } = await DevicePool.getDevices(devicesDescriptor, undefined, {
|
|
1378
|
+
connectProtocol: PROTOCOL_V2_CONNECT_PROTOCOL,
|
|
1379
|
+
});
|
|
632
1380
|
if (deviceList.length !== 1) {
|
|
633
1381
|
throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound);
|
|
634
1382
|
}
|
|
635
1383
|
|
|
1384
|
+
Log.debug(
|
|
1385
|
+
'Protocol V2 firmware reconnect using single enumerated device:',
|
|
1386
|
+
deviceList[0].getConnectId()
|
|
1387
|
+
);
|
|
636
1388
|
this.device.updateFromCache(deviceList[0]);
|
|
637
|
-
await this.
|
|
1389
|
+
await this.ensureProtocolV2DeviceAcquired();
|
|
638
1390
|
this.device.commands.disposed = false;
|
|
639
1391
|
this.device.getCommands().mainId = this.device.mainId ?? '';
|
|
1392
|
+
assertProtocolV2ReconnectIdentity(
|
|
1393
|
+
this.protocolV2ExpectedDeviceId,
|
|
1394
|
+
this.device.getCurrentDeviceId(),
|
|
1395
|
+
// Cached identity may be absent immediately after acquire. Reject only an
|
|
1396
|
+
// existing mismatch; the final refresh performs strict identity validation.
|
|
1397
|
+
{ allowMissingActual: true }
|
|
1398
|
+
);
|
|
640
1399
|
}
|
|
641
1400
|
|
|
642
|
-
|
|
1401
|
+
/**
|
|
1402
|
+
* After acquiring a USB session, polling reuses its command channel. Reacquire only
|
|
1403
|
+
* after reboot or enumeration changes cause hasDeviceAcquire() to become false.
|
|
1404
|
+
*/
|
|
1405
|
+
private async ensureProtocolV2DeviceAcquired() {
|
|
1406
|
+
const commands = this.device.getCommands();
|
|
1407
|
+
if (this.device.hasDeviceAcquire() && !commands.disposed) {
|
|
1408
|
+
return;
|
|
1409
|
+
}
|
|
1410
|
+
await this.device.acquire(PROTOCOL_V2_CONNECT_PROTOCOL, {
|
|
1411
|
+
throwOnRunPromiseError: true,
|
|
1412
|
+
});
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
private isProtocolV2ReconnectIdentityMismatch(error: unknown) {
|
|
1416
|
+
return this.normalizeErrorMessage(error).includes('Protocol V2 reconnect identity mismatch');
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
private async protocolV2CommonUpdateProcess(params: ProtocolV2FileTransferParams) {
|
|
1420
|
+
let lastError: unknown;
|
|
1421
|
+
for (let attempt = 1; attempt <= PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT; attempt += 1) {
|
|
1422
|
+
try {
|
|
1423
|
+
return await this.protocolV2WriteWholeFile(params);
|
|
1424
|
+
} catch (error) {
|
|
1425
|
+
lastError = error;
|
|
1426
|
+
Log.error(
|
|
1427
|
+
`Protocol V2 file transfer failed path=${params.filePath} attempt=${attempt}/${PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT}; restarting from offset 0`,
|
|
1428
|
+
error
|
|
1429
|
+
);
|
|
1430
|
+
if (attempt === PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT) {
|
|
1431
|
+
break;
|
|
1432
|
+
}
|
|
1433
|
+
await this.recoverProtocolV2FileTransfer();
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
throw ERRORS.TypedError(
|
|
1438
|
+
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
1439
|
+
`transfer data error: ${getProtocolV2UnknownErrorText(lastError)}`
|
|
1440
|
+
);
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
private async protocolV2WriteWholeFile({
|
|
643
1444
|
payload,
|
|
644
1445
|
filePath,
|
|
645
1446
|
processedSize,
|
|
646
1447
|
totalSize,
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
processedSize?: number;
|
|
650
|
-
totalSize?: number;
|
|
651
|
-
}) {
|
|
1448
|
+
onTransferredBytes,
|
|
1449
|
+
}: ProtocolV2FileTransferParams) {
|
|
652
1450
|
const chunkSize = this.getProtocolV2FirmwareChunkSize();
|
|
653
1451
|
let offset = 0;
|
|
654
1452
|
const getUploadProgress = (fileOffset: number) => {
|
|
@@ -663,9 +1461,13 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
663
1461
|
const chunkLength = chunkEnd - offset;
|
|
664
1462
|
const chunk = payload.slice(offset, chunkEnd);
|
|
665
1463
|
const overwrite = offset === 0;
|
|
666
|
-
const progress =
|
|
1464
|
+
const progress = getProtocolV2DeviceTransferProgress(
|
|
1465
|
+
(processedSize ?? 0) + offset,
|
|
1466
|
+
(processedSize ?? 0) + chunkEnd,
|
|
1467
|
+
totalSize ?? payload.byteLength
|
|
1468
|
+
);
|
|
667
1469
|
|
|
668
|
-
const writeRes = await this.
|
|
1470
|
+
const writeRes = await this.fileWriteChunk(
|
|
669
1471
|
filePath,
|
|
670
1472
|
payload.byteLength,
|
|
671
1473
|
offset,
|
|
@@ -673,25 +1475,38 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
673
1475
|
overwrite,
|
|
674
1476
|
progress
|
|
675
1477
|
);
|
|
676
|
-
const
|
|
677
|
-
const
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
: offset + chunkLength;
|
|
681
|
-
if (nextOffset <= offset || nextOffset > payload.byteLength) {
|
|
1478
|
+
const rawProcessedByte = writeRes.message.processed_byte;
|
|
1479
|
+
const processedByte = Number(rawProcessedByte);
|
|
1480
|
+
const nextOffset = rawProcessedByte === undefined ? offset + chunkLength : processedByte;
|
|
1481
|
+
if (!Number.isFinite(nextOffset) || nextOffset <= offset || nextOffset > chunkEnd) {
|
|
682
1482
|
throw ERRORS.TypedError(
|
|
683
1483
|
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
684
1484
|
`invalid processed_byte ${writeRes.message.processed_byte} for offset ${offset}`
|
|
685
1485
|
);
|
|
686
1486
|
}
|
|
687
1487
|
offset = nextOffset;
|
|
1488
|
+
onTransferredBytes?.((processedSize ?? 0) + offset);
|
|
688
1489
|
this.postProgressMessage(getUploadProgress(offset), 'transferData');
|
|
689
1490
|
}
|
|
690
1491
|
|
|
691
1492
|
return totalSize !== undefined ? (processedSize ?? 0) + payload.byteLength : 0;
|
|
692
1493
|
}
|
|
693
1494
|
|
|
694
|
-
private
|
|
1495
|
+
private getProtocolV2FirmwareTransferTransport() {
|
|
1496
|
+
const env = DataManager.getSettings('env');
|
|
1497
|
+
if (env && DataManager.isBleConnect(env)) {
|
|
1498
|
+
return 'BLE';
|
|
1499
|
+
}
|
|
1500
|
+
if (
|
|
1501
|
+
env &&
|
|
1502
|
+
(DataManager.isBrowserWebUsb(env) || DataManager.isDesktopWebUsb(env) || env === 'web')
|
|
1503
|
+
) {
|
|
1504
|
+
return 'WebUSB';
|
|
1505
|
+
}
|
|
1506
|
+
return env ?? 'unknown';
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
private async fileWriteChunk(
|
|
695
1510
|
filePath: string,
|
|
696
1511
|
totalFileSize: number,
|
|
697
1512
|
offset: number,
|
|
@@ -699,57 +1514,37 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
699
1514
|
overwrite: boolean,
|
|
700
1515
|
progress: number | null
|
|
701
1516
|
): Promise<TypedResponseMessage<'FilesystemFile'>> {
|
|
702
|
-
const
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
if (writeRes.type
|
|
716
|
-
if ((writeRes as any).
|
|
717
|
-
|
|
718
|
-
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, SESSION_ERROR);
|
|
719
|
-
}
|
|
1517
|
+
const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
|
|
1518
|
+
const writeRes = await typedCall('FilesystemFileWrite', 'FilesystemFile', {
|
|
1519
|
+
file: {
|
|
1520
|
+
path: filePath,
|
|
1521
|
+
offset,
|
|
1522
|
+
total_size: totalFileSize,
|
|
1523
|
+
data: chunk,
|
|
1524
|
+
},
|
|
1525
|
+
overwrite,
|
|
1526
|
+
append: false,
|
|
1527
|
+
ui_percentage: progress ?? undefined,
|
|
1528
|
+
});
|
|
1529
|
+
if (writeRes.type !== 'FilesystemFile') {
|
|
1530
|
+
if ((writeRes as any).type === 'CallMethodError') {
|
|
1531
|
+
if (((writeRes as any).message.error ?? '').indexOf(SESSION_ERROR) > -1) {
|
|
1532
|
+
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, SESSION_ERROR);
|
|
720
1533
|
}
|
|
721
|
-
throw ERRORS.TypedError(
|
|
722
|
-
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
723
|
-
'transfer data error'
|
|
724
|
-
);
|
|
725
1534
|
}
|
|
726
|
-
|
|
727
|
-
}
|
|
1535
|
+
throw ERRORS.TypedError(HardwareErrorCode.EmmcFileWriteFirmwareError, 'transfer data error');
|
|
1536
|
+
}
|
|
1537
|
+
return writeRes;
|
|
1538
|
+
}
|
|
728
1539
|
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
Log.error(`fileWrite error: `, error);
|
|
736
|
-
retryCount--;
|
|
737
|
-
if (retryCount === 0) {
|
|
738
|
-
throw ERRORS.TypedError(
|
|
739
|
-
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
740
|
-
'transfer data error'
|
|
741
|
-
);
|
|
742
|
-
}
|
|
743
|
-
const env = DataManager.getSettings('env');
|
|
744
|
-
if (DataManager.isBleConnect(env)) {
|
|
745
|
-
await wait(3000);
|
|
746
|
-
await this.acquireProtocolV2BleDevice();
|
|
747
|
-
await this.device.initialize();
|
|
748
|
-
}
|
|
749
|
-
await wait(2000);
|
|
750
|
-
}
|
|
1540
|
+
private async recoverProtocolV2FileTransfer() {
|
|
1541
|
+
const env = DataManager.getSettings('env');
|
|
1542
|
+
if (DataManager.isBleConnect(env)) {
|
|
1543
|
+
await wait(3000);
|
|
1544
|
+
await this.acquireProtocolV2BleDevice();
|
|
1545
|
+
await this.device.initialize();
|
|
751
1546
|
}
|
|
752
|
-
|
|
1547
|
+
await wait(2000);
|
|
753
1548
|
}
|
|
754
1549
|
|
|
755
1550
|
private async acquireProtocolV2BleDevice() {
|
|
@@ -767,44 +1562,30 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
767
1562
|
targets: Array<{ target_id: number; path: string }>;
|
|
768
1563
|
}) {
|
|
769
1564
|
const commands = this.device.getCommands();
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
{
|
|
779
|
-
intermediateTypes: ['DevFirmwareInstallProgress'],
|
|
780
|
-
onIntermediateResponse: (response: { message?: { progress?: number } }) => {
|
|
781
|
-
const progress = Number(response.message?.progress);
|
|
782
|
-
if (Number.isFinite(progress)) {
|
|
783
|
-
this.postProgressMessage(Math.min(progress, 99), 'installingFirmware');
|
|
784
|
-
}
|
|
785
|
-
},
|
|
786
|
-
}
|
|
787
|
-
);
|
|
788
|
-
} catch (error) {
|
|
789
|
-
if (isProtocolV2DeviceDisconnectedError(error)) {
|
|
790
|
-
Log.log('Rebooting device');
|
|
791
|
-
} else {
|
|
792
|
-
throw error;
|
|
793
|
-
}
|
|
794
|
-
}
|
|
1565
|
+
const response: ProtocolV2FirmwareUpdateStartResponse = await commands.typedCall(
|
|
1566
|
+
'DeviceFirmwareUpdateRequest',
|
|
1567
|
+
'Success',
|
|
1568
|
+
{ targets },
|
|
1569
|
+
{ timeoutMs: PROTOCOL_V2_START_UPDATE_TIMEOUT }
|
|
1570
|
+
);
|
|
1571
|
+
// Success acknowledges that the device accepted installation. End confirmation
|
|
1572
|
+
// and begin status polling only after receiving this ACK.
|
|
795
1573
|
this.postTipMessage(FirmwareUpdateTipMessage.FirmwareUpdating);
|
|
1574
|
+
this.postProgressMessage(0, 'installingFirmware');
|
|
796
1575
|
return response;
|
|
797
1576
|
}
|
|
798
1577
|
|
|
799
|
-
private async protocolV2Reboot(rebootType:
|
|
1578
|
+
private async protocolV2Reboot(rebootType: DeviceRebootType) {
|
|
800
1579
|
const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
|
|
801
1580
|
try {
|
|
802
|
-
const res = await typedCall('
|
|
1581
|
+
const res = await typedCall('DeviceReboot', 'Success', {
|
|
803
1582
|
reboot_type: rebootType,
|
|
804
1583
|
});
|
|
1584
|
+
this.device.markProtocolV2Reboot(rebootType);
|
|
805
1585
|
return res.message;
|
|
806
1586
|
} catch (error) {
|
|
807
1587
|
if (isProtocolV2DeviceDisconnectedError(error) || isProtocolV2ReconnectProbeError(error)) {
|
|
1588
|
+
this.device.markProtocolV2Reboot(rebootType);
|
|
808
1589
|
return { message: 'Device rebooted successfully' };
|
|
809
1590
|
}
|
|
810
1591
|
throw error;
|