@onekeyfe/hd-core 1.2.0-alpha.67 → 1.2.0-alpha.69
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 +313 -0
- package/__tests__/DeviceCommands.test.ts +59 -13
- package/__tests__/DeviceEventRegistration.test.ts +6 -0
- package/__tests__/check-all-firmware-release-protocol-v2.test.ts +226 -3
- package/__tests__/connectSettings.test.ts +5 -47
- package/__tests__/core-initialization.test.ts +23 -0
- package/__tests__/device-connector-protocol.test.ts +81 -0
- package/__tests__/device-lifecycle-events.test.ts +105 -3
- package/__tests__/device-pool-state.test.ts +22 -0
- package/__tests__/device-settings.test.ts +39 -18
- package/__tests__/device-state-contract.test.ts +1 -0
- package/__tests__/device-state-mapper.test.ts +13 -1
- package/__tests__/device-utils.test.ts +48 -1
- package/__tests__/deviceSettings.test.ts +11 -1
- package/__tests__/deviceUploadNft.test.ts +316 -0
- package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +31 -59
- package/__tests__/get-device-state.test.ts +132 -12
- package/__tests__/homescreen.test.ts +41 -1
- package/__tests__/kaspa-use-tweak-pro2.test.ts +2 -2
- package/__tests__/kaspaSignTransaction.test.ts +3 -26
- package/__tests__/logBlockEvent.test.ts +1 -1
- package/__tests__/method-protocol-support.test.ts +19 -0
- package/__tests__/open-wallet-session.test.ts +568 -37
- package/__tests__/pro2Nft.test.ts +108 -0
- package/__tests__/protocol-binding.test.ts +89 -0
- package/__tests__/protocol-v2-firmware-targets.test.ts +33 -1
- package/__tests__/protocol-v2-resources.test.ts +343 -0
- package/__tests__/protocol-v2-ui-lifecycle.test.ts +91 -0
- package/__tests__/protocol-v2-unlock-policy.test.ts +300 -37
- package/__tests__/protocol-v2.test.ts +1586 -1213
- package/__tests__/protocolV2FileWrite.test.ts +193 -0
- package/__tests__/protocolV2UiInteraction.test.ts +63 -0
- package/__tests__/public-pro2-api-boundary.test.ts +1 -0
- package/__tests__/search-devices.test.ts +12 -3
- package/__tests__/tron-sign-message-init.test.ts +2 -2
- package/__tests__/uiProgressThrottle.test.ts +74 -0
- package/__tests__/uiPromiseRegistry.test.ts +115 -0
- package/dist/api/BaseMethod.d.ts +3 -1
- package/dist/api/BaseMethod.d.ts.map +1 -1
- package/dist/api/CheckAllFirmwareRelease.d.ts +3 -1
- package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
- package/dist/api/DetectDeviceConnectProtocol.d.ts +7 -0
- package/dist/api/DetectDeviceConnectProtocol.d.ts.map +1 -0
- package/dist/api/FirmwareUpdate.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV2.d.ts +0 -10
- package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV3.d.ts +3 -7
- package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +15 -27
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/GetPassphraseState.d.ts.map +1 -1
- package/dist/api/OpenWalletSession.d.ts.map +1 -1
- package/dist/api/SearchDevices.d.ts.map +1 -1
- package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
- package/dist/api/device/DeviceSettings.d.ts.map +1 -1
- package/dist/api/device/DeviceUnlock.d.ts +2 -2
- package/dist/api/device/DeviceUnlock.d.ts.map +1 -1
- package/dist/api/device/DeviceUpdateBootloader.d.ts +1 -5
- package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +4 -11
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
- package/dist/api/firmware/getBinary.d.ts +0 -7
- package/dist/api/firmware/getBinary.d.ts.map +1 -1
- package/dist/api/firmware/updateBootloader.d.ts +0 -2
- package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
- package/dist/api/firmware/uploadFirmware.d.ts +1 -9
- package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
- package/dist/api/helpers/protocolV2FileWrite.d.ts +9 -0
- package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/kaspa/KaspaGetAddress.d.ts +1 -1
- package/dist/api/kaspa/KaspaSignTransaction.d.ts +1 -1
- package/dist/api/kaspa/KaspaSignTransaction.d.ts.map +1 -1
- package/dist/api/polkadot/PolkadotGetAddress.d.ts +1 -1
- package/dist/api/polkadot/networks.d.ts +1 -1
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts +31 -0
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -0
- package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -1
- package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
- package/dist/api/solana/SolSignMessage.d.ts +1 -1
- package/dist/api/solana/SolSignOffchainMessage.d.ts +1 -1
- package/dist/api/solana/SolSignTransaction.d.ts +2 -2
- package/dist/api/stellar/StellarSignTransaction.d.ts +1 -1
- package/dist/api/sui/SuiGetAddress.d.ts +1 -1
- package/dist/api/sui/SuiGetPublicKey.d.ts +1 -1
- package/dist/api/sui/SuiSignMessage.d.ts +1 -1
- package/dist/api/sui/SuiSignTransaction.d.ts +1 -1
- package/dist/api/ton/TonGetAddress.d.ts +1 -1
- package/dist/api/ton/TonSignMessage.d.ts +1 -1
- package/dist/api/ton/TonSignProof.d.ts +1 -1
- package/dist/api/tron/TronSignMessage.d.ts +2 -2
- package/dist/api/tron/TronSignTransaction.d.ts +1 -1
- package/dist/core/deviceEventRegistration.d.ts +2 -0
- package/dist/core/deviceEventRegistration.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/uiPromiseRegistry.d.ts +6 -0
- package/dist/core/uiPromiseRegistry.d.ts.map +1 -0
- package/dist/data-manager/DataManager.d.ts +9 -4
- package/dist/data-manager/DataManager.d.ts.map +1 -1
- package/dist/data-manager/TransportManager.d.ts.map +1 -1
- package/dist/data-manager/connectSettings.d.ts.map +1 -1
- package/dist/device/Device.d.ts +29 -6
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts +4 -4
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/device/DeviceConnector.d.ts +2 -1
- package/dist/device/DeviceConnector.d.ts.map +1 -1
- package/dist/device/DevicePool.d.ts.map +1 -1
- package/dist/deviceProfile/protocolV2DeviceIdentity.d.ts.map +1 -1
- package/dist/events/device.d.ts +4 -0
- package/dist/events/device.d.ts.map +1 -1
- package/dist/events/logBlockEvent.d.ts.map +1 -1
- package/dist/events/ui-promise.d.ts +4 -2
- package/dist/events/ui-promise.d.ts.map +1 -1
- package/dist/events/ui-request.d.ts +31 -2
- package/dist/events/ui-request.d.ts.map +1 -1
- package/dist/events/ui-response.d.ts +10 -2
- package/dist/events/ui-response.d.ts.map +1 -1
- package/dist/index.d.ts +243 -247
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3135 -3785
- package/dist/inject.d.ts +6 -1
- package/dist/inject.d.ts.map +1 -1
- package/dist/lowLevelInject.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/features.d.ts +0 -4
- package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/firmware.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/resources.d.ts +30 -0
- package/dist/protocols/protocol-v2/resources.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/uiInteraction.d.ts +7 -4
- package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockPolicy.d.ts +1 -3
- package/dist/protocols/protocol-v2/unlockPolicy.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +22 -0
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/walletSession.d.ts +6 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/topLevelInject.d.ts.map +1 -1
- package/dist/types/api/checkAllFirmwareRelease.d.ts +3 -5
- package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
- package/dist/types/api/detectDeviceConnectProtocol.d.ts +4 -0
- package/dist/types/api/detectDeviceConnectProtocol.d.ts.map +1 -0
- package/dist/types/api/deviceUnlock.d.ts +5 -1
- package/dist/types/api/deviceUnlock.d.ts.map +1 -1
- package/dist/types/api/deviceUpdateBootloader.d.ts +0 -6
- package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
- package/dist/types/api/export.d.ts +2 -4
- package/dist/types/api/export.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdate.d.ts +4 -70
- package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
- package/dist/types/api/index.d.ts +7 -10
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +3 -0
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/device.d.ts +2 -2
- 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 +38 -21
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesCompat.d.ts +0 -1
- package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts +4 -0
- package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
- package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
- package/dist/utils/deviceSettings.d.ts.map +1 -1
- package/dist/utils/homescreen.d.ts +4 -0
- package/dist/utils/homescreen.d.ts.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/patch.d.ts.map +1 -1
- package/dist/utils/pro2Nft.d.ts +31 -0
- package/dist/utils/pro2Nft.d.ts.map +1 -0
- package/dist/utils/pro2Wallpaper.d.ts +10 -0
- package/dist/utils/pro2Wallpaper.d.ts.map +1 -1
- package/dist/utils/uiProgressThrottle.d.ts +3 -0
- package/dist/utils/uiProgressThrottle.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/api/BaseMethod.ts +8 -10
- package/src/api/CheckAllFirmwareRelease.ts +78 -64
- package/src/api/DetectDeviceConnectProtocol.ts +18 -0
- package/src/api/FirmwareUpdate.ts +7 -15
- package/src/api/FirmwareUpdateV2.ts +126 -298
- package/src/api/FirmwareUpdateV3.ts +63 -265
- package/src/api/FirmwareUpdateV4.ts +645 -1018
- package/src/api/GetPassphraseState.ts +7 -1
- package/src/api/OpenWalletSession.ts +71 -17
- package/src/api/SearchDevices.ts +3 -1
- package/src/api/allnetwork/AllNetworkGetAddress.ts +21 -21
- package/src/api/allnetwork/AllNetworkGetAddressBase.ts +36 -11
- package/src/api/device/DeviceSettings.ts +5 -4
- package/src/api/device/DeviceUnlock.ts +8 -3
- package/src/api/device/DeviceUpdateBootloader.ts +6 -125
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +27 -83
- package/src/api/firmware/updateBootloader.ts +4 -19
- package/src/api/firmware/uploadFirmware.ts +39 -144
- package/src/api/helpers/protocolV2FileWrite.ts +252 -63
- package/src/api/index.ts +2 -0
- package/src/api/kaspa/KaspaGetAddress.ts +1 -1
- package/src/api/kaspa/KaspaSignTransaction.ts +2 -13
- package/src/api/polkadot/networks.ts +3 -3
- package/src/api/protocol-v2/DeviceUploadNft.ts +197 -0
- package/src/api/protocol-v2/ProtocolInfoRequest.ts +3 -1
- package/src/api/protocol-v2/helpers.ts +1 -1
- package/src/api/solana/SolSignMessage.ts +1 -1
- package/src/api/solana/SolSignOffchainMessage.ts +1 -1
- package/src/api/solana/SolSignTransaction.ts +2 -2
- package/src/api/sui/SuiGetAddress.ts +1 -1
- package/src/api/sui/SuiGetPublicKey.ts +1 -1
- package/src/api/sui/SuiSignMessage.ts +1 -1
- package/src/api/sui/SuiSignTransaction.ts +1 -1
- package/src/api/ton/TonGetAddress.ts +1 -1
- package/src/api/ton/TonSignMessage.ts +1 -1
- package/src/api/ton/TonSignProof.ts +1 -1
- package/src/api/tron/TronSignMessage.ts +2 -2
- package/src/api/tron/TronSignTransaction.ts +1 -1
- package/src/core/deviceEventRegistration.ts +4 -0
- package/src/core/index.ts +213 -111
- package/src/core/uiPromiseRegistry.ts +63 -0
- package/src/data/messages/messages-protocol-v2.json +25 -0
- package/src/data-manager/DataManager.ts +113 -41
- package/src/data-manager/TransportManager.ts +6 -0
- package/src/data-manager/connectSettings.ts +0 -11
- package/src/device/Device.ts +274 -49
- package/src/device/DeviceCommands.ts +31 -4
- package/src/device/DeviceConnector.ts +33 -13
- package/src/device/DevicePool.ts +21 -7
- package/src/deviceProfile/buildDeviceFeatures.ts +2 -2
- package/src/deviceProfile/protocolV2DeviceIdentity.ts +3 -0
- package/src/events/device.ts +4 -0
- package/src/events/logBlockEvent.ts +1 -0
- package/src/events/ui-promise.ts +4 -2
- package/src/events/ui-request.ts +36 -2
- package/src/events/ui-response.ts +12 -2
- package/src/index.ts +0 -5
- package/src/inject.ts +60 -24
- package/src/lowLevelInject.ts +7 -8
- package/src/protocols/protocol-v2/features.ts +10 -7
- package/src/protocols/protocol-v2/firmware.ts +2 -0
- package/src/protocols/protocol-v2/resources.ts +394 -0
- package/src/protocols/protocol-v2/uiInteraction.ts +46 -6
- package/src/protocols/protocol-v2/unlockPolicy.ts +1 -105
- package/src/protocols/protocol-v2/unlockPolicyRunner.ts +131 -0
- package/src/protocols/protocol-v2/walletSession.ts +214 -36
- package/src/topLevelInject.ts +7 -8
- package/src/types/api/checkAllFirmwareRelease.ts +3 -5
- package/src/types/api/detectDeviceConnectProtocol.ts +6 -0
- package/src/types/api/deviceUnlock.ts +12 -1
- package/src/types/api/deviceUpdateBootloader.ts +0 -6
- package/src/types/api/export.ts +1 -18
- package/src/types/api/firmwareUpdate.ts +6 -78
- package/src/types/api/index.ts +13 -14
- package/src/types/api/protocolV2.ts +13 -0
- package/src/types/device.ts +15 -3
- package/src/types/params.ts +7 -1
- package/src/types/settings.ts +61 -22
- package/src/utils/deviceFeaturesCompat.ts +5 -6
- package/src/utils/deviceFeaturesUtils.ts +14 -3
- package/src/utils/deviceInfoUtils.ts +2 -0
- package/src/utils/deviceSettings.ts +3 -0
- package/src/utils/homescreen.ts +32 -6
- package/src/utils/index.ts +6 -1
- package/src/utils/pro2Nft.ts +142 -0
- package/src/utils/pro2Wallpaper.ts +35 -8
- package/src/utils/uiProgressThrottle.ts +63 -0
- package/__tests__/device-initialize-timeout.test.ts +0 -56
- package/__tests__/firmware-update/check-all-firmware-release.test.ts +0 -175
- package/__tests__/firmware-update/device-update-bootloader.test.ts +0 -87
- package/__tests__/firmware-update/firmware-artifact-source.test.ts +0 -119
- package/__tests__/firmware-update/firmware-host-binding.test.ts +0 -70
- package/__tests__/firmware-update/firmware-preparation-error.test.ts +0 -27
- package/__tests__/firmware-update/firmware-update-bootloader-poll.test.ts +0 -200
- package/__tests__/firmware-update/firmware-update-capabilities.test.ts +0 -13
- package/__tests__/firmware-update/firmware-update-plan-bootloader-identity.test.ts +0 -394
- package/__tests__/firmware-update/firmware-update-plan.test.ts +0 -593
- package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +0 -231
- package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +0 -58
- package/__tests__/firmware-update/firmware-upload-source.test.ts +0 -70
- package/dist/api/firmware/FirmwareArtifactSource.d.ts +0 -25
- package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +0 -1
- package/dist/api/firmware/FirmwareHostBinding.d.ts +0 -6
- package/dist/api/firmware/FirmwareHostBinding.d.ts.map +0 -1
- package/dist/api/firmware/FirmwarePreparationError.d.ts +0 -3
- package/dist/api/firmware/FirmwarePreparationError.d.ts.map +0 -1
- package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +0 -3
- package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +0 -1
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts +0 -25
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +0 -1
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +0 -32
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +0 -1
- package/dist/api/firmware/progressThrottle.d.ts +0 -3
- package/dist/api/firmware/progressThrottle.d.ts.map +0 -1
- package/dist/protocols/protocol-v2/lockedError.d.ts +0 -2
- package/dist/protocols/protocol-v2/lockedError.d.ts.map +0 -1
- package/dist/protocols/protocol-v2/unlockRetry.d.ts +0 -10
- package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +0 -1
- package/dist/types/api/firmwareUpdateCapabilities.d.ts +0 -9
- package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +0 -1
- package/dist/types/api/firmwareUpdatePlan.d.ts +0 -28
- package/dist/types/api/firmwareUpdatePlan.d.ts.map +0 -1
- package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +0 -42
- package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +0 -1
- package/src/api/firmware/FirmwareArtifactSource.ts +0 -278
- package/src/api/firmware/FirmwareHostBinding.ts +0 -100
- package/src/api/firmware/FirmwarePreparationError.ts +0 -12
- package/src/api/firmware/FirmwareUpdateCapabilities.ts +0 -9
- package/src/api/firmware/FirmwareUpdatePlan.ts +0 -772
- package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +0 -449
- package/src/api/firmware/progressThrottle.ts +0 -44
- package/src/protocols/protocol-v2/lockedError.ts +0 -10
- package/src/protocols/protocol-v2/unlockRetry.ts +0 -109
- package/src/types/api/firmwareUpdateCapabilities.ts +0 -9
- package/src/types/api/firmwareUpdatePlan.ts +0 -38
- package/src/types/api/firmwareUpdatePreparedPlan.ts +0 -53
|
@@ -2,24 +2,24 @@ import { EDeviceType, ERRORS, HardwareError, HardwareErrorCode, wait } from '@on
|
|
|
2
2
|
import {
|
|
3
3
|
DeviceRebootType,
|
|
4
4
|
PROTOCOL_V2_BLE_FILE_CHUNK_SIZE,
|
|
5
|
-
PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE,
|
|
6
5
|
PROTOCOL_V2_WEBUSB_FILE_CHUNK_SIZE,
|
|
7
6
|
} from '@onekeyfe/hd-transport';
|
|
8
7
|
import { sha256 } from '@noble/hashes/sha256';
|
|
9
8
|
|
|
10
9
|
import { FirmwareUpdateTipMessage, UI_REQUEST } from '../events/ui-request';
|
|
11
10
|
import { validateProtocolV2FilesystemPath } from './helpers/filesystemValidation';
|
|
11
|
+
import { writeProtocolV2File } from './helpers/protocolV2FileWrite';
|
|
12
12
|
import { validateParams } from './helpers/paramsValidator';
|
|
13
13
|
import {
|
|
14
14
|
LoggerNames,
|
|
15
15
|
getDeviceBLEFirmwareVersion,
|
|
16
16
|
getDeviceBootloaderVersion,
|
|
17
17
|
getDeviceFirmwareVersion,
|
|
18
|
+
getDeviceType,
|
|
18
19
|
getFirmwareType,
|
|
19
20
|
getLogger,
|
|
20
21
|
} from '../utils';
|
|
21
22
|
import { getSysResourceBinary } from './firmware/getBinary';
|
|
22
|
-
import { normalizeFirmwarePreparationError } from './firmware/FirmwarePreparationError';
|
|
23
23
|
import { DataManager } from '../data-manager';
|
|
24
24
|
import { FirmwareUpdateBaseMethod } from './firmware/FirmwareUpdateBaseMethod';
|
|
25
25
|
import { DevicePool } from '../device/DevicePool';
|
|
@@ -28,41 +28,40 @@ import {
|
|
|
28
28
|
ProtocolV2FirmwareTargetType,
|
|
29
29
|
} from '../protocols/protocol-v2';
|
|
30
30
|
import { requestProtocolV2DeviceInfo } from '../protocols/protocol-v2/features';
|
|
31
|
+
import {
|
|
32
|
+
PROTOCOL_V2_RESOURCE_DEVICE_PATHS,
|
|
33
|
+
buildProtocolV2ResourceUpdatePlan,
|
|
34
|
+
isProtocolV2ResourceFileValid,
|
|
35
|
+
readProtocolV2ResourceInventory,
|
|
36
|
+
} from '../protocols/protocol-v2/resources';
|
|
31
37
|
import {
|
|
32
38
|
getProtocolV2UnknownErrorText,
|
|
33
39
|
isProtocolV2DeviceDisconnectedError,
|
|
34
40
|
} from './protocol-v2/helpers';
|
|
35
|
-
import { openFirmwareByteSource, writeFirmwareByteSource } from './firmware/FirmwareArtifactSource';
|
|
36
|
-
import { resolveFirmwareUpdateHostBinding } from './firmware/FirmwareHostBinding';
|
|
37
|
-
import {
|
|
38
|
-
assertFirmwareUpdatePreparedPlanBinding,
|
|
39
|
-
assertFirmwareUpdatePreparedPlanDeviceIdentity,
|
|
40
|
-
} from './firmware/FirmwareUpdatePreparedPlan';
|
|
41
41
|
|
|
42
|
-
import type {
|
|
43
|
-
FirmwareArtifactReference,
|
|
44
|
-
FirmwareUpdateV4Params,
|
|
45
|
-
FirmwareUpdateV4Target,
|
|
46
|
-
} from '../types/api/firmwareUpdate';
|
|
42
|
+
import type { FirmwareUpdateV4Params, FirmwareUpdateV4Target } from '../types/api/firmwareUpdate';
|
|
47
43
|
import type { EFirmwareType } from '@onekeyfe/hd-shared';
|
|
48
44
|
import type { ProtocolV2DeviceInfo } from '@onekeyfe/hd-transport';
|
|
45
|
+
import type { PROTO } from '../constants';
|
|
49
46
|
import type { TypedResponseMessage } from '../device/DeviceCommands';
|
|
50
47
|
import type {
|
|
51
48
|
Features,
|
|
52
49
|
IFirmwareReleaseInfo,
|
|
53
50
|
IProtocolV2FirmwareComponent,
|
|
51
|
+
IProtocolV2Resource,
|
|
52
|
+
IProtocolV2ResourceFile,
|
|
54
53
|
IVersionArray,
|
|
55
54
|
} from '../types';
|
|
56
|
-
import type { FirmwareByteSource } from './firmware/FirmwareArtifactSource';
|
|
57
55
|
|
|
58
56
|
const Log = getLogger(LoggerNames.Method);
|
|
59
57
|
|
|
60
|
-
const
|
|
61
|
-
const
|
|
62
|
-
const PROTOCOL_V2_FINAL_RECONNECT_TIMEOUT = 2 * 60 * 1000;
|
|
58
|
+
const PROTOCOL_V2_BOOTLOADER_RECONNECT_TIMEOUT = 90 * 1000;
|
|
59
|
+
const PROTOCOL_V2_FINAL_RECONNECT_TIMEOUT = 3 * 60 * 1000;
|
|
63
60
|
const PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT = 5 * 1000;
|
|
61
|
+
const PROTOCOL_V2_FIRMWARE_STATUS_RESPONSE_TIMEOUT = 15 * 1000;
|
|
64
62
|
const PROTOCOL_V2_START_UPDATE_TIMEOUT = 3 * 60 * 1000;
|
|
65
|
-
const PROTOCOL_V2_INSTALL_TIMEOUT =
|
|
63
|
+
const PROTOCOL_V2_INSTALL_TIMEOUT = 8 * 60 * 1000;
|
|
64
|
+
const PROTOCOL_V2_MISSING_TARGET_STATUS_GRACE_TIMEOUT = 30 * 1000;
|
|
66
65
|
const PROTOCOL_V2_TARGET_STATUS_PENDING = 0;
|
|
67
66
|
const PROTOCOL_V2_TARGET_STATUS_IN_PROGRESS = 1;
|
|
68
67
|
const PROTOCOL_V2_TARGET_STATUS_FINISHED = 2;
|
|
@@ -73,14 +72,37 @@ const PROTOCOL_V2_MIN_FILE_CHUNK_SIZE = 64;
|
|
|
73
72
|
const PROTOCOL_V2_CONNECT_RETRY_COUNT = 10;
|
|
74
73
|
const PROTOCOL_V2_CONNECT_POLL_INTERVAL = 500;
|
|
75
74
|
const PROTOCOL_V2_CONNECT_SINGLE_TIMEOUT = 75 * 1000;
|
|
76
|
-
const PROTOCOL_V2_DEVICE_INFO_READY_TIMEOUT =
|
|
75
|
+
const PROTOCOL_V2_DEVICE_INFO_READY_TIMEOUT = 30 * 1000;
|
|
77
76
|
const PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT = 3;
|
|
78
|
-
const PROTOCOL_V2_INSTALL_STATUS_CONFLICT_CODE = 'FirmwareInstallStatusConflict';
|
|
79
77
|
const PROTOCOL_V2_OKPP_HEADER_SIZE = 0x52a0;
|
|
80
78
|
const PROTOCOL_V2_OKPP_PAYLOAD_HASH_OFFSET = 0x200;
|
|
81
79
|
const PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET = 0x240;
|
|
82
80
|
const PROTOCOL_V2_OKPP_HASH_SIZE = 64;
|
|
83
81
|
|
|
82
|
+
const PROTOCOL_V2_NEO_UNSUPPORTED_TARGETS = new Set<FirmwareUpdateV4Target>(['se03', 'se04']);
|
|
83
|
+
|
|
84
|
+
export function assertProtocolV2FirmwareTargetsSupported(
|
|
85
|
+
deviceType: EDeviceType | string | undefined,
|
|
86
|
+
params: FirmwareUpdateV4Params
|
|
87
|
+
) {
|
|
88
|
+
if (deviceType !== EDeviceType.Neo) return;
|
|
89
|
+
|
|
90
|
+
const unsupportedTargets = new Set(
|
|
91
|
+
(params.targetsToUpdate ?? []).filter(target => PROTOCOL_V2_NEO_UNSUPPORTED_TARGETS.has(target))
|
|
92
|
+
);
|
|
93
|
+
if (params.se03Binary) unsupportedTargets.add('se03');
|
|
94
|
+
if (params.se04Binary) unsupportedTargets.add('se04');
|
|
95
|
+
|
|
96
|
+
if (unsupportedTargets.size) {
|
|
97
|
+
throw ERRORS.TypedError(
|
|
98
|
+
HardwareErrorCode.RuntimeError,
|
|
99
|
+
`Neo only supports SE01 and SE02; unsupported firmware targets: ${Array.from(
|
|
100
|
+
unsupportedTargets
|
|
101
|
+
).join(', ')}`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
84
106
|
const getProtocolV2DeviceTransferProgress = (
|
|
85
107
|
bytesBeforeChunk: number,
|
|
86
108
|
bytesAfterChunk: number,
|
|
@@ -98,6 +120,11 @@ const getProtocolV2DeviceTransferProgress = (
|
|
|
98
120
|
return Math.min(Math.max(Math.ceil((bytesAfterChunk / totalBytes) * 100), 1), 99);
|
|
99
121
|
};
|
|
100
122
|
|
|
123
|
+
const formatProtocolV2TransferSpeed = (bytes: number, elapsedMs: number) => {
|
|
124
|
+
const safeElapsedMs = Math.max(elapsedMs, 1);
|
|
125
|
+
return (bytes / 1024 / (safeElapsedMs / 1000)).toFixed(2);
|
|
126
|
+
};
|
|
127
|
+
|
|
101
128
|
type ProtocolV2FirmwareUpdateStatusTarget = {
|
|
102
129
|
target_id: number | string;
|
|
103
130
|
status?: number | string;
|
|
@@ -111,16 +138,22 @@ type ProtocolV2TargetBinary = { fileName: string; binary: ArrayBuffer; targetId:
|
|
|
111
138
|
type ProtocolV2InstallItem = ProtocolV2TargetBinary & {
|
|
112
139
|
kind: ProtocolV2RemoteComponentTarget['kind'];
|
|
113
140
|
};
|
|
114
|
-
|
|
141
|
+
type ProtocolV2InstallTarget = ProtocolV2InstallItem & {
|
|
142
|
+
path: string;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
type ProtocolV2FileTransferParams = PROTO.FirmwareUpload & {
|
|
146
|
+
filePath: string;
|
|
147
|
+
processedSize?: number;
|
|
148
|
+
totalSize?: number;
|
|
149
|
+
onTransferredBytes?: (transferredBytes: number) => void;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/** Protocol V2 resource file written independently to devicePath through FileWrite. */
|
|
115
153
|
type ProtocolV2ResourceBundleBinary = {
|
|
116
154
|
name: string;
|
|
117
155
|
binary: ArrayBuffer;
|
|
118
156
|
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
157
|
};
|
|
125
158
|
|
|
126
159
|
type ProtocolV2RemoteComponentBinary = ProtocolV2RemoteComponentTarget & {
|
|
@@ -133,35 +166,6 @@ type ProtocolV2RemoteComponentTarget = {
|
|
|
133
166
|
kind: 'bootloader' | 'firmware';
|
|
134
167
|
};
|
|
135
168
|
|
|
136
|
-
type ProtocolV2InstallSource = {
|
|
137
|
-
fileName: string;
|
|
138
|
-
source: FirmwareByteSource;
|
|
139
|
-
targetId: number;
|
|
140
|
-
kind: ProtocolV2RemoteComponentTarget['kind'];
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
type ProtocolV2ResourceBundleSource = {
|
|
144
|
-
name: string;
|
|
145
|
-
source: FirmwareByteSource;
|
|
146
|
-
devicePath: string;
|
|
147
|
-
version?: IVersionArray;
|
|
148
|
-
payloadHash?: string;
|
|
149
|
-
headerHash?: string;
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
type ProtocolV2ExecutionPhaseKind =
|
|
153
|
-
| 'resource-sync'
|
|
154
|
-
| 'bootloader-install'
|
|
155
|
-
| 'bootloader-verify'
|
|
156
|
-
| 'component-install'
|
|
157
|
-
| 'final-verify';
|
|
158
|
-
|
|
159
|
-
type ProtocolV2ExecutionPhase = {
|
|
160
|
-
kind: ProtocolV2ExecutionPhaseKind;
|
|
161
|
-
installSources: ProtocolV2InstallSource[];
|
|
162
|
-
resourceSources: ProtocolV2ResourceBundleSource[];
|
|
163
|
-
};
|
|
164
|
-
|
|
165
169
|
type ProtocolV2OkppHeader = {
|
|
166
170
|
type: string;
|
|
167
171
|
version: IVersionArray;
|
|
@@ -264,12 +268,6 @@ const isProtocolV2FirmwareStatusEndpointUnavailable = (error: unknown) => {
|
|
|
264
268
|
);
|
|
265
269
|
};
|
|
266
270
|
|
|
267
|
-
const isProtocolV2TerminalInstallStatusError = (error: unknown) =>
|
|
268
|
-
error instanceof HardwareError &&
|
|
269
|
-
(error.errorCode === HardwareErrorCode.FirmwareError ||
|
|
270
|
-
error.errorCode === HardwareErrorCode.FirmwareVerificationFailed ||
|
|
271
|
-
error.params?.firmwareUpdateCode === PROTOCOL_V2_INSTALL_STATUS_CONFLICT_CODE);
|
|
272
|
-
|
|
273
271
|
const isProtocolV2TargetStatusFinished = (status: ProtocolV2FirmwareUpdateStatusTarget['status']) =>
|
|
274
272
|
normalizeProtocolV2TargetStatus(status) === PROTOCOL_V2_TARGET_STATUS_FINISHED;
|
|
275
273
|
|
|
@@ -307,18 +305,6 @@ const normalizeProtocolV2TargetStatus = (
|
|
|
307
305
|
|
|
308
306
|
const normalizeProtocolV2Hex = (value?: string) => value?.replace(/^0x/i, '').toLowerCase();
|
|
309
307
|
|
|
310
|
-
const versionArrayToNumber = (version?: IVersionArray) => {
|
|
311
|
-
if (!version) return undefined;
|
|
312
|
-
return version[0] * 0x10000 + version[1] * 0x100 + version[2];
|
|
313
|
-
};
|
|
314
|
-
|
|
315
|
-
const compareProtocolV2Versions = (current?: IVersionArray, target?: IVersionArray) => {
|
|
316
|
-
const currentNumber = versionArrayToNumber(current);
|
|
317
|
-
const targetNumber = versionArrayToNumber(target);
|
|
318
|
-
if (currentNumber === undefined || targetNumber === undefined) return undefined;
|
|
319
|
-
return currentNumber - targetNumber;
|
|
320
|
-
};
|
|
321
|
-
|
|
322
308
|
const bytesToHex = (bytes: Uint8Array) =>
|
|
323
309
|
Array.from(bytes)
|
|
324
310
|
.map(byte => byte.toString(16).padStart(2, '0'))
|
|
@@ -409,20 +395,30 @@ export const isProtocolV2FirmwareFingerprintValid = (
|
|
|
409
395
|
|
|
410
396
|
export const assertProtocolV2ReconnectIdentity = (
|
|
411
397
|
expectedSerialNumber?: string,
|
|
412
|
-
actualSerialNumber?: string
|
|
398
|
+
actualSerialNumber?: string,
|
|
399
|
+
expectedPath?: string,
|
|
400
|
+
actualPath?: string
|
|
413
401
|
) => {
|
|
414
|
-
if (
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
402
|
+
if (expectedSerialNumber && actualSerialNumber) {
|
|
403
|
+
if (actualSerialNumber !== expectedSerialNumber) {
|
|
404
|
+
throw ERRORS.TypedError(
|
|
405
|
+
HardwareErrorCode.DeviceNotFound,
|
|
406
|
+
`Protocol V2 reconnect physical identity mismatch: expected ${expectedSerialNumber}, received ${actualSerialNumber}`
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
return;
|
|
419
410
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
`Protocol V2 reconnect physical identity mismatch: expected ${expectedSerialNumber}, received ${actualSerialNumber}`
|
|
424
|
-
);
|
|
411
|
+
|
|
412
|
+
if (expectedPath && actualPath && expectedPath === actualPath) {
|
|
413
|
+
return;
|
|
425
414
|
}
|
|
415
|
+
|
|
416
|
+
throw ERRORS.TypedError(
|
|
417
|
+
HardwareErrorCode.DeviceNotFound,
|
|
418
|
+
`Protocol V2 reconnect physical identity unavailable: expected path ${
|
|
419
|
+
expectedPath ?? 'unknown'
|
|
420
|
+
}, received ${actualPath ?? 'unknown'}`
|
|
421
|
+
);
|
|
426
422
|
};
|
|
427
423
|
|
|
428
424
|
/**
|
|
@@ -436,20 +432,12 @@ export const assertProtocolV2ReconnectIdentity = (
|
|
|
436
432
|
export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareUpdateV4Params> {
|
|
437
433
|
private protocolV2ExpectedSerialNumber?: string;
|
|
438
434
|
|
|
435
|
+
private protocolV2ExpectedPath?: string;
|
|
436
|
+
|
|
439
437
|
getSupportedProtocols() {
|
|
440
438
|
return ['V2'] as const;
|
|
441
439
|
}
|
|
442
440
|
|
|
443
|
-
private protocolV2PreparedSources: FirmwareByteSource[] = [];
|
|
444
|
-
|
|
445
|
-
private protocolV2ExecutionInLoader = false;
|
|
446
|
-
|
|
447
|
-
private protocolV2CompletedTargetVersions = new Map<number, number>();
|
|
448
|
-
|
|
449
|
-
private protocolV2LatestFinalFeatures?: Features;
|
|
450
|
-
|
|
451
|
-
private protocolV2FinalStatusVerified = false;
|
|
452
|
-
|
|
453
441
|
init() {
|
|
454
442
|
this.allowDeviceMode = [UI_REQUEST.BOOTLOADER, UI_REQUEST.NOT_INITIALIZE];
|
|
455
443
|
this.requireDeviceMode = [];
|
|
@@ -487,62 +475,10 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
487
475
|
{ name: 'firmwareType', type: 'string' },
|
|
488
476
|
{ name: 'targetsToUpdate', type: 'array', allowEmpty: true },
|
|
489
477
|
{ name: 'platform', type: 'string' },
|
|
490
|
-
{ name: '
|
|
491
|
-
{ name: 'resourceBundleFiles', type: 'array', allowEmpty: true },
|
|
478
|
+
{ name: 'resourceFiles', type: 'array', allowEmpty: true },
|
|
492
479
|
]);
|
|
493
|
-
if (
|
|
494
|
-
payload.expectedDeviceId !== undefined &&
|
|
495
|
-
(payload.expectedDeviceId.length === 0 || payload.expectedDeviceId.length > 160)
|
|
496
|
-
) {
|
|
497
|
-
throw ERRORS.TypedError(
|
|
498
|
-
HardwareErrorCode.CallMethodInvalidParameter,
|
|
499
|
-
'Protocol V2 expected device identity is invalid'
|
|
500
|
-
);
|
|
501
|
-
}
|
|
502
|
-
const hostBinding =
|
|
503
|
-
payload.preparedPlan || payload.componentArtifacts || payload.resourceBundleArtifacts
|
|
504
|
-
? resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration)
|
|
505
|
-
: undefined;
|
|
506
|
-
if (hostBinding) {
|
|
507
|
-
assertFirmwareUpdatePreparedPlanBinding({
|
|
508
|
-
preparedPlan: payload.preparedPlan,
|
|
509
|
-
executor: 'v4',
|
|
510
|
-
platform: payload.platform,
|
|
511
|
-
scopeTargets: [
|
|
512
|
-
'boot',
|
|
513
|
-
'app_v1',
|
|
514
|
-
'app_v2',
|
|
515
|
-
'coprocessor',
|
|
516
|
-
'resource',
|
|
517
|
-
'se01',
|
|
518
|
-
'se02',
|
|
519
|
-
'se03',
|
|
520
|
-
'se04',
|
|
521
|
-
],
|
|
522
|
-
bindings: [
|
|
523
|
-
...Object.entries(payload.componentArtifacts ?? {}).flatMap(([target, artifact]) =>
|
|
524
|
-
artifact
|
|
525
|
-
? [
|
|
526
|
-
{
|
|
527
|
-
target: target as Exclude<FirmwareUpdateV4Target, 'resource'>,
|
|
528
|
-
artifact,
|
|
529
|
-
},
|
|
530
|
-
]
|
|
531
|
-
: []
|
|
532
|
-
),
|
|
533
|
-
...(payload.resourceBundleArtifacts ?? []).map(
|
|
534
|
-
(entry: { name: string; artifact: FirmwareArtifactReference }) => ({
|
|
535
|
-
target: 'resource' as const,
|
|
536
|
-
logicalName: entry.name,
|
|
537
|
-
artifact: entry.artifact,
|
|
538
|
-
})
|
|
539
|
-
),
|
|
540
|
-
],
|
|
541
|
-
});
|
|
542
|
-
}
|
|
543
480
|
|
|
544
481
|
this.params = {
|
|
545
|
-
preparedPlan: payload.preparedPlan,
|
|
546
482
|
chunkSize: payload.chunkSize,
|
|
547
483
|
forcedUpdateRes: payload.forcedUpdateRes,
|
|
548
484
|
bootloaderBinary: payload.bootloaderBinary,
|
|
@@ -554,28 +490,20 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
554
490
|
se02Binary: payload.se02Binary,
|
|
555
491
|
se03Binary: payload.se03Binary,
|
|
556
492
|
se04Binary: payload.se04Binary,
|
|
557
|
-
|
|
493
|
+
resourceFiles: payload.resourceFiles,
|
|
558
494
|
firmwareType: payload.firmwareType,
|
|
559
495
|
targetsToUpdate: payload.targetsToUpdate,
|
|
560
|
-
expectedTargetVersions: payload.expectedTargetVersions,
|
|
561
496
|
platform: payload.platform,
|
|
562
|
-
expectedDeviceId: payload.expectedDeviceId,
|
|
563
|
-
artifactReader: hostBinding?.artifactReader ?? payload.artifactReader,
|
|
564
|
-
componentArtifacts: payload.componentArtifacts,
|
|
565
|
-
resourceBundleArtifacts: payload.resourceBundleArtifacts,
|
|
566
497
|
};
|
|
567
498
|
}
|
|
568
499
|
|
|
569
|
-
private getProtocolV2FirmwareChunkSize(
|
|
500
|
+
private getProtocolV2FirmwareChunkSize() {
|
|
570
501
|
const payloadChunkSize = Number(this.params?.chunkSize);
|
|
571
502
|
const env = DataManager.getSettings('env');
|
|
572
503
|
const isBle = this.params?.platform === 'native' || (env && DataManager.isBleConnect(env));
|
|
573
504
|
let maxChunkSize = PROTOCOL_V2_WEBUSB_FILE_CHUNK_SIZE;
|
|
574
505
|
if (isBle) {
|
|
575
|
-
maxChunkSize =
|
|
576
|
-
direction === 'read'
|
|
577
|
-
? PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE
|
|
578
|
-
: PROTOCOL_V2_BLE_FILE_CHUNK_SIZE;
|
|
506
|
+
maxChunkSize = PROTOCOL_V2_BLE_FILE_CHUNK_SIZE;
|
|
579
507
|
}
|
|
580
508
|
if (!Number.isFinite(payloadChunkSize) || payloadChunkSize <= 0) {
|
|
581
509
|
return maxChunkSize;
|
|
@@ -594,16 +522,16 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
594
522
|
private async runProtocolV2() {
|
|
595
523
|
await this.captureProtocolV2PhysicalIdentity();
|
|
596
524
|
const deviceFeatures = await this.getProtocolV2DeviceFeatures();
|
|
525
|
+
const currentDeviceType = this.device.getCurrentDeviceType();
|
|
526
|
+
const capabilityDeviceType =
|
|
527
|
+
currentDeviceType === EDeviceType.Pro2 || currentDeviceType === EDeviceType.Neo
|
|
528
|
+
? currentDeviceType
|
|
529
|
+
: getDeviceType(deviceFeatures);
|
|
530
|
+
assertProtocolV2FirmwareTargetsSupported(capabilityDeviceType, this.params);
|
|
597
531
|
const deviceFirmwareType = getFirmwareType(deviceFeatures);
|
|
598
532
|
const firmwareType = this.params.firmwareType ?? deviceFirmwareType;
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
if (
|
|
602
|
-
(this.params.componentArtifacts && Object.keys(this.params.componentArtifacts).length > 0) ||
|
|
603
|
-
this.params.resourceBundleArtifacts?.length
|
|
604
|
-
) {
|
|
605
|
-
return this.runProtocolV2PreparedArtifacts(deviceFeatures, firmwareType);
|
|
606
|
-
}
|
|
533
|
+
const needsRemoteResources =
|
|
534
|
+
!this.params.resourceFiles?.length && !!this.params.targetsToUpdate?.includes('resource');
|
|
607
535
|
|
|
608
536
|
let fwBinaryMap: ProtocolV2TargetBinary[] = [];
|
|
609
537
|
let bootloaderBinary: ArrayBuffer | null = null;
|
|
@@ -611,21 +539,20 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
611
539
|
let resourceBundles: ProtocolV2ResourceBundleBinary[] | undefined;
|
|
612
540
|
try {
|
|
613
541
|
this.postTipMessage(FirmwareUpdateTipMessage.StartDownloadFirmware);
|
|
542
|
+
resourceBundles = this.prepareExplicitProtocolV2ResourceFiles();
|
|
614
543
|
fwBinaryMap = this.collectExplicitTargetBinaries();
|
|
615
544
|
bootloaderBinary = this.prepareBootloaderBinary();
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
);
|
|
628
|
-
}
|
|
545
|
+
const needsRemoteFirmware = !this.hasExplicitProtocolV2Payload(fwBinaryMap);
|
|
546
|
+
const needsRemoteBootResources =
|
|
547
|
+
!!this.params.targetsToUpdate?.includes('resource') ||
|
|
548
|
+
!!this.params.targetsToUpdate?.includes('boot_resources');
|
|
549
|
+
if (needsRemoteFirmware || needsRemoteResources || needsRemoteBootResources) {
|
|
550
|
+
// Remote updates must use a freshly fetched config before any reboot or file write.
|
|
551
|
+
await DataManager.forceReloadData({
|
|
552
|
+
requireResources: needsRemoteResources || needsRemoteBootResources,
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
if (needsRemoteFirmware) {
|
|
629
556
|
const remoteBinaries = await this.prepareRemoteProtocolV2Binaries(
|
|
630
557
|
firmwareType,
|
|
631
558
|
deviceFeatures
|
|
@@ -634,308 +561,70 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
634
561
|
fwBinaryMap = remoteBinaries.fwBinaryMap;
|
|
635
562
|
installItems = remoteBinaries.installItems;
|
|
636
563
|
}
|
|
637
|
-
const
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
564
|
+
const bootResourceFiles = await this.prepareProtocolV2BootResources();
|
|
565
|
+
if (bootResourceFiles?.length) {
|
|
566
|
+
resourceBundles = [...(resourceBundles ?? []), ...bootResourceFiles];
|
|
567
|
+
}
|
|
568
|
+
if (!needsRemoteResources) {
|
|
569
|
+
this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
570
|
+
}
|
|
645
571
|
} catch (err) {
|
|
646
|
-
|
|
572
|
+
if (err instanceof HardwareError && err.errorCode === HardwareErrorCode.NetworkError) {
|
|
573
|
+
throw err;
|
|
574
|
+
}
|
|
575
|
+
throw ERRORS.TypedError(HardwareErrorCode.FirmwareUpdateDownloadFailed, err.message ?? err);
|
|
647
576
|
}
|
|
648
577
|
|
|
649
|
-
if (
|
|
578
|
+
if (
|
|
579
|
+
!bootloaderBinary &&
|
|
580
|
+
fwBinaryMap.length === 0 &&
|
|
581
|
+
!installItems?.length &&
|
|
582
|
+
!resourceBundles?.length &&
|
|
583
|
+
!needsRemoteResources
|
|
584
|
+
) {
|
|
650
585
|
throw ERRORS.TypedError(
|
|
651
586
|
HardwareErrorCode.FirmwareUpdateDownloadFailed,
|
|
652
587
|
'No firmware to update'
|
|
653
588
|
);
|
|
654
589
|
}
|
|
655
590
|
|
|
656
|
-
|
|
657
|
-
fwBinaryMap,
|
|
658
|
-
bootloaderBinary,
|
|
659
|
-
...(installItems ? { installItems } : undefined),
|
|
660
|
-
...(resourceBundles?.length ? { resourceBundles } : undefined),
|
|
661
|
-
});
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
private async openProtocolV2PreparedSource(artifact: FirmwareArtifactReference) {
|
|
665
|
-
const source = await openFirmwareByteSource({
|
|
666
|
-
artifact,
|
|
667
|
-
reader: this.params.artifactReader,
|
|
668
|
-
});
|
|
669
|
-
if (!source) {
|
|
670
|
-
throw ERRORS.TypedError(
|
|
671
|
-
HardwareErrorCode.RuntimeError,
|
|
672
|
-
`Firmware artifact ${artifact.artifactRef} is not prepared`,
|
|
673
|
-
{
|
|
674
|
-
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
675
|
-
}
|
|
676
|
-
);
|
|
677
|
-
}
|
|
678
|
-
this.protocolV2PreparedSources.push(source);
|
|
679
|
-
return source;
|
|
680
|
-
}
|
|
591
|
+
const enteredBootloader = await this.enterProtocolV2BootloaderMode();
|
|
681
592
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
await Promise.all(sources.map(source => source.close().catch(() => undefined)));
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
private async prepareProtocolV2InstallSources(
|
|
700
|
-
firmwareType: EFirmwareType,
|
|
701
|
-
features: Features
|
|
702
|
-
): Promise<ProtocolV2InstallSource[]> {
|
|
703
|
-
const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
|
|
704
|
-
if (!release) {
|
|
705
|
-
throw ERRORS.TypedError(
|
|
706
|
-
HardwareErrorCode.RuntimeError,
|
|
707
|
-
'Protocol V2 firmware release is unavailable'
|
|
708
|
-
);
|
|
709
|
-
}
|
|
710
|
-
const componentArtifacts = this.params.componentArtifacts ?? {};
|
|
711
|
-
const requestedTargets = new Set(
|
|
712
|
-
this.params.targetsToUpdate ?? (Object.keys(componentArtifacts) as FirmwareUpdateV4Target[])
|
|
713
|
-
);
|
|
714
|
-
const installSources: ProtocolV2InstallSource[] = [];
|
|
715
|
-
const preparedTargets = new Set<FirmwareUpdateV4Target>();
|
|
716
|
-
|
|
717
|
-
for (const [key, component] of this.getRemoteComponentEntries(release)) {
|
|
718
|
-
const target = this.getRemoteComponentTarget(key, component);
|
|
719
|
-
const updateTarget = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(target.targetId);
|
|
720
|
-
if (updateTarget && updateTarget !== 'resource' && requestedTargets.has(updateTarget)) {
|
|
721
|
-
const artifact = componentArtifacts[updateTarget];
|
|
722
|
-
if (!artifact) {
|
|
723
|
-
throw ERRORS.TypedError(
|
|
724
|
-
HardwareErrorCode.RuntimeError,
|
|
725
|
-
`Protocol V2 ${updateTarget} artifact is not prepared`,
|
|
726
|
-
{
|
|
727
|
-
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
728
|
-
artifactName: updateTarget,
|
|
729
|
-
}
|
|
730
|
-
);
|
|
731
|
-
}
|
|
732
|
-
installSources.push({
|
|
733
|
-
...target,
|
|
734
|
-
source: await this.openProtocolV2PreparedSource(artifact),
|
|
735
|
-
});
|
|
736
|
-
preparedTargets.add(updateTarget);
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
const unknownTarget = Array.from(requestedTargets).find(
|
|
741
|
-
target => target !== 'resource' && !preparedTargets.has(target)
|
|
742
|
-
);
|
|
743
|
-
if (unknownTarget) {
|
|
744
|
-
throw ERRORS.TypedError(
|
|
745
|
-
HardwareErrorCode.RuntimeError,
|
|
746
|
-
`Protocol V2 release does not contain requested target ${unknownTarget}`
|
|
747
|
-
);
|
|
748
|
-
}
|
|
749
|
-
return installSources;
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
private async prepareProtocolV2ResourceSources(
|
|
753
|
-
firmwareType: EFirmwareType,
|
|
754
|
-
features: Features
|
|
755
|
-
): Promise<ProtocolV2ResourceBundleSource[]> {
|
|
756
|
-
const preparedArtifacts = this.params.resourceBundleArtifacts ?? [];
|
|
757
|
-
const resourceRequested =
|
|
758
|
-
this.params.targetsToUpdate?.includes('resource') || preparedArtifacts.length > 0;
|
|
759
|
-
if (!resourceRequested) {
|
|
760
|
-
return [];
|
|
761
|
-
}
|
|
762
|
-
const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
|
|
763
|
-
const descriptors = release?.resourceBundles ?? [];
|
|
764
|
-
const artifactByName = new Map(
|
|
765
|
-
preparedArtifacts.map(item => [item.name, item.artifact] as const)
|
|
766
|
-
);
|
|
767
|
-
const sources: ProtocolV2ResourceBundleSource[] = [];
|
|
768
|
-
for (const descriptor of descriptors) {
|
|
769
|
-
const artifact = artifactByName.get(descriptor.name);
|
|
770
|
-
if (!artifact) {
|
|
771
|
-
throw ERRORS.TypedError(
|
|
772
|
-
HardwareErrorCode.RuntimeError,
|
|
773
|
-
`Protocol V2 resource bundle ${descriptor.name} is not prepared`,
|
|
774
|
-
{
|
|
775
|
-
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
776
|
-
artifactName: descriptor.name,
|
|
593
|
+
if (needsRemoteResources) {
|
|
594
|
+
try {
|
|
595
|
+
const stableResources = await this.prepareProtocolV2ResourceBundles();
|
|
596
|
+
resourceBundles = [...(resourceBundles ?? []), ...(stableResources ?? [])];
|
|
597
|
+
this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
598
|
+
} catch (err) {
|
|
599
|
+
if (enteredBootloader) {
|
|
600
|
+
try {
|
|
601
|
+
await this.exitProtocolV2BootloaderToNormal();
|
|
602
|
+
} catch (restoreError) {
|
|
603
|
+
Log.warn(
|
|
604
|
+
'[FirmwareUpdateV4] failed to restore App mode after resource preparation error:',
|
|
605
|
+
restoreError
|
|
606
|
+
);
|
|
777
607
|
}
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
const devicePath = validateProtocolV2FilesystemPath(
|
|
781
|
-
descriptor.devicePath,
|
|
782
|
-
'resourceBundles[].devicePath'
|
|
783
|
-
);
|
|
784
|
-
sources.push({
|
|
785
|
-
name: descriptor.name,
|
|
786
|
-
source: await this.openProtocolV2PreparedSource(artifact),
|
|
787
|
-
devicePath,
|
|
788
|
-
version: descriptor.version,
|
|
789
|
-
payloadHash: descriptor.payloadHash,
|
|
790
|
-
headerHash: descriptor.headerHash,
|
|
791
|
-
});
|
|
792
|
-
artifactByName.delete(descriptor.name);
|
|
793
|
-
}
|
|
794
|
-
if (artifactByName.size > 0) {
|
|
795
|
-
throw ERRORS.TypedError(
|
|
796
|
-
HardwareErrorCode.RuntimeError,
|
|
797
|
-
`Protocol V2 release does not contain resource bundle ${artifactByName.keys().next().value}`
|
|
798
|
-
);
|
|
799
|
-
}
|
|
800
|
-
return sources;
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
private async runProtocolV2PreparedArtifacts(features: Features, firmwareType: EFirmwareType) {
|
|
804
|
-
try {
|
|
805
|
-
this.postTipMessage(FirmwareUpdateTipMessage.StartDownloadFirmware);
|
|
806
|
-
const installSources = await this.prepareProtocolV2InstallSources(firmwareType, features);
|
|
807
|
-
const resourceSources = await this.prepareProtocolV2ResourceSources(firmwareType, features);
|
|
808
|
-
if (installSources.length === 0 && resourceSources.length === 0) {
|
|
809
|
-
throw ERRORS.TypedError(
|
|
810
|
-
HardwareErrorCode.FirmwareUpdateDownloadFailed,
|
|
811
|
-
'No firmware to update'
|
|
812
|
-
);
|
|
608
|
+
}
|
|
609
|
+
throw ERRORS.TypedError(HardwareErrorCode.FirmwareUpdateDownloadFailed, err.message ?? err);
|
|
813
610
|
}
|
|
814
|
-
this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
815
|
-
|
|
816
|
-
return await this.executeProtocolV2SourceUpdate({
|
|
817
|
-
installSources,
|
|
818
|
-
resourceSources,
|
|
819
|
-
});
|
|
820
|
-
} finally {
|
|
821
|
-
await this.closeProtocolV2PreparedSources();
|
|
822
611
|
}
|
|
823
|
-
}
|
|
824
612
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
'Protocol V2 expected target versions are invalid'
|
|
832
|
-
);
|
|
833
|
-
}
|
|
834
|
-
for (const [target, version] of Object.entries(expected)) {
|
|
835
|
-
if (
|
|
836
|
-
!Array.from(PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.values()).includes(
|
|
837
|
-
target as FirmwareUpdateV4Target
|
|
838
|
-
) ||
|
|
839
|
-
typeof version !== 'string' ||
|
|
840
|
-
!/^\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.-]+)?$/u.test(version) ||
|
|
841
|
-
version.length > 64
|
|
842
|
-
) {
|
|
843
|
-
throw ERRORS.TypedError(
|
|
844
|
-
HardwareErrorCode.RuntimeError,
|
|
845
|
-
'Protocol V2 expected target version is invalid'
|
|
846
|
-
);
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
}
|
|
613
|
+
await this.executeProtocolV2Update({
|
|
614
|
+
fwBinaryMap,
|
|
615
|
+
bootloaderBinary,
|
|
616
|
+
...(installItems ? { installItems } : undefined),
|
|
617
|
+
...(resourceBundles?.length ? { resourceBundles } : undefined),
|
|
618
|
+
});
|
|
850
619
|
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
const version = target ? this.params?.expectedTargetVersions?.[target] : undefined;
|
|
854
|
-
if (!version) return undefined;
|
|
855
|
-
const parts = version.split(/[+-]/u, 1)[0].split('.').map(Number);
|
|
856
|
-
if (
|
|
857
|
-
parts.length !== 3 ||
|
|
858
|
-
parts.some(part => !Number.isSafeInteger(part) || part < 0 || part > 0xff)
|
|
859
|
-
) {
|
|
860
|
-
return undefined;
|
|
861
|
-
}
|
|
862
|
-
return parts[0] * 0x10000 + parts[1] * 0x100 + parts[2];
|
|
863
|
-
}
|
|
620
|
+
this.postTipMessage(FirmwareUpdateTipMessage.SwitchFirmwareReconnectDevice);
|
|
621
|
+
await this.exitProtocolV2BootloaderToNormal();
|
|
864
622
|
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
const features = this.protocolV2LatestFinalFeatures;
|
|
869
|
-
const requestedTargets = this.getProtocolV2RequestedTargets();
|
|
870
|
-
const applicationTargets = requestedTargets.filter(
|
|
871
|
-
target => target === 'app_v1' || target === 'app_v2'
|
|
872
|
-
);
|
|
873
|
-
const visibleVersions: Partial<Record<FirmwareUpdateV4Target, string>> = {
|
|
874
|
-
boot: features ? getDeviceBootloaderVersion(features).join('.') : undefined,
|
|
875
|
-
coprocessor: features ? getDeviceBLEFirmwareVersion(features).join('.') : undefined,
|
|
876
|
-
se01: features?.se01Version ?? undefined,
|
|
877
|
-
se02: features?.se02Version ?? undefined,
|
|
878
|
-
se03: features?.se03Version ?? undefined,
|
|
879
|
-
se04: features?.se04Version ?? undefined,
|
|
880
|
-
};
|
|
881
|
-
if (features && applicationTargets.length === 1) {
|
|
882
|
-
visibleVersions[applicationTargets[0]] = getDeviceFirmwareVersion(features).join('.');
|
|
883
|
-
}
|
|
884
|
-
const expectedEntries = (
|
|
885
|
-
Object.entries(expected) as Array<[FirmwareUpdateV4Target, string]>
|
|
886
|
-
).filter(([target]) => !targets || targets.includes(target));
|
|
887
|
-
for (const [target, expectedVersion] of expectedEntries) {
|
|
888
|
-
const targetId = Array.from(PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.entries()).find(
|
|
889
|
-
([, mappedTarget]) => mappedTarget === target
|
|
890
|
-
)?.[0];
|
|
891
|
-
const statusVersion =
|
|
892
|
-
targetId === undefined ? undefined : this.protocolV2CompletedTargetVersions.get(targetId);
|
|
893
|
-
const observedVersion =
|
|
894
|
-
statusVersion === undefined
|
|
895
|
-
? visibleVersions[target]
|
|
896
|
-
: `${Math.floor(statusVersion / 0x10000) % 0x100}.${
|
|
897
|
-
Math.floor(statusVersion / 0x100) % 0x100
|
|
898
|
-
}.${statusVersion % 0x100}`;
|
|
899
|
-
if (!observedVersion) {
|
|
900
|
-
if (this.protocolV2FinalStatusVerified) {
|
|
901
|
-
Log.warn(
|
|
902
|
-
`Protocol V2 target ${target} has no observable final version; completed target status is authoritative`
|
|
903
|
-
);
|
|
904
|
-
} else {
|
|
905
|
-
throw ERRORS.TypedError(
|
|
906
|
-
HardwareErrorCode.FirmwareVerificationFailed,
|
|
907
|
-
`Protocol V2 target ${target} has no observable final version after status fallback`
|
|
908
|
-
);
|
|
909
|
-
}
|
|
910
|
-
} else if (observedVersion !== expectedVersion) {
|
|
911
|
-
throw ERRORS.TypedError(
|
|
912
|
-
HardwareErrorCode.FirmwareVerificationFailed,
|
|
913
|
-
`Protocol V2 target ${target} reached ${observedVersion}, expected ${expectedVersion}`
|
|
914
|
-
);
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
}
|
|
623
|
+
const versions = await this.waitForProtocolV2FinalFeatures();
|
|
624
|
+
this.postTipMessage(FirmwareUpdateTipMessage.FirmwareUpdateCompleted);
|
|
625
|
+
DevicePool.resetState();
|
|
918
626
|
|
|
919
|
-
|
|
920
|
-
if (this.params.targetsToUpdate?.length) {
|
|
921
|
-
return [...new Set(this.params.targetsToUpdate)];
|
|
922
|
-
}
|
|
923
|
-
const targets = new Set<FirmwareUpdateV4Target>();
|
|
924
|
-
Object.keys(this.params.componentArtifacts ?? {}).forEach(target =>
|
|
925
|
-
targets.add(target as FirmwareUpdateV4Target)
|
|
926
|
-
);
|
|
927
|
-
if (this.params.resourceBundleArtifacts?.length || this.params.resourceBundleFiles?.length) {
|
|
928
|
-
targets.add('resource');
|
|
929
|
-
}
|
|
930
|
-
if (this.params.bootloaderBinary) targets.add('boot');
|
|
931
|
-
if (this.params.applicationP1Binary) targets.add('app_v1');
|
|
932
|
-
if (this.params.applicationP2Binary) targets.add('app_v2');
|
|
933
|
-
if (this.params.coprocessorBinary) targets.add('coprocessor');
|
|
934
|
-
if (this.params.se01Binary) targets.add('se01');
|
|
935
|
-
if (this.params.se02Binary) targets.add('se02');
|
|
936
|
-
if (this.params.se03Binary) targets.add('se03');
|
|
937
|
-
if (this.params.se04Binary) targets.add('se04');
|
|
938
|
-
return Array.from(targets);
|
|
627
|
+
return versions;
|
|
939
628
|
}
|
|
940
629
|
|
|
941
630
|
private async getProtocolV2DeviceFeatures(): Promise<Features> {
|
|
@@ -957,31 +646,32 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
957
646
|
private assertProtocolV2DeviceInfoIdentity(deviceInfo: ProtocolV2DeviceInfo) {
|
|
958
647
|
assertProtocolV2ReconnectIdentity(
|
|
959
648
|
this.protocolV2ExpectedSerialNumber,
|
|
960
|
-
this.getProtocolV2SerialNumber(deviceInfo)
|
|
649
|
+
this.getProtocolV2SerialNumber(deviceInfo),
|
|
650
|
+
this.protocolV2ExpectedPath,
|
|
651
|
+
this.device.originalDescriptor.path
|
|
961
652
|
);
|
|
962
653
|
}
|
|
963
654
|
|
|
655
|
+
private getProtocolV2DeviceInfoTimeout() {
|
|
656
|
+
return this.isBleReconnect()
|
|
657
|
+
? this.payload.protocolV2DeviceInfoTimeoutMs ?? PROTOCOL_V2_DEVICE_INFO_READY_TIMEOUT
|
|
658
|
+
: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT;
|
|
659
|
+
}
|
|
660
|
+
|
|
964
661
|
private async requestProtocolV2PhysicalIdentity() {
|
|
965
662
|
return requestProtocolV2DeviceInfo({
|
|
966
663
|
commands: this.device.getCommands(),
|
|
967
|
-
timeoutMs:
|
|
664
|
+
timeoutMs: this.getProtocolV2DeviceInfoTimeout(),
|
|
968
665
|
});
|
|
969
666
|
}
|
|
970
667
|
|
|
971
668
|
private async captureProtocolV2PhysicalIdentity() {
|
|
972
669
|
const deviceInfo = await this.requestProtocolV2PhysicalIdentity();
|
|
973
670
|
const serialNumber = this.getProtocolV2SerialNumber(deviceInfo);
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
preparedPlan: this.params.preparedPlan,
|
|
977
|
-
deviceIdentity: serialNumber,
|
|
978
|
-
});
|
|
979
|
-
} else if (this.params?.expectedDeviceId) {
|
|
980
|
-
assertProtocolV2ReconnectIdentity(this.params?.expectedDeviceId, serialNumber);
|
|
981
|
-
} else {
|
|
982
|
-
assertProtocolV2ReconnectIdentity(serialNumber, serialNumber);
|
|
983
|
-
}
|
|
671
|
+
const path = this.device.originalDescriptor.path?.trim() || undefined;
|
|
672
|
+
assertProtocolV2ReconnectIdentity(serialNumber, serialNumber, path, path);
|
|
984
673
|
this.protocolV2ExpectedSerialNumber = serialNumber;
|
|
674
|
+
this.protocolV2ExpectedPath = path;
|
|
985
675
|
}
|
|
986
676
|
|
|
987
677
|
private async verifyProtocolV2ReconnectIdentity() {
|
|
@@ -996,7 +686,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
996
686
|
|
|
997
687
|
private hasExplicitProtocolV2Payload(fwBinaryMap: ProtocolV2TargetBinary[]) {
|
|
998
688
|
return (
|
|
999
|
-
!!this.params.
|
|
689
|
+
!!this.params.resourceFiles?.length ||
|
|
1000
690
|
!!this.params.bootloaderBinary ||
|
|
1001
691
|
fwBinaryMap.length > 0
|
|
1002
692
|
);
|
|
@@ -1059,57 +749,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1059
749
|
return target;
|
|
1060
750
|
}
|
|
1061
751
|
|
|
1062
|
-
private getProtocolV2ResourceFilePath(path: string) {
|
|
1063
|
-
if (path.startsWith('vol')) return path;
|
|
1064
|
-
if (path.startsWith('/')) return `vol0:${path}`;
|
|
1065
|
-
return `vol0:/${path}`;
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
private async readProtocolV2DeviceFileHeader(path: string) {
|
|
1069
|
-
const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
|
|
1070
|
-
const filePath = this.getProtocolV2ResourceFilePath(path);
|
|
1071
|
-
const pathInfoRes = await typedCall('FilesystemPathInfoQuery', 'FilesystemPathInfo', {
|
|
1072
|
-
path: filePath,
|
|
1073
|
-
});
|
|
1074
|
-
const fileSize = toProtocolV2FiniteNumber(pathInfoRes.message?.size);
|
|
1075
|
-
if (
|
|
1076
|
-
!pathInfoRes.message?.exist ||
|
|
1077
|
-
pathInfoRes.message?.directory ||
|
|
1078
|
-
fileSize === undefined ||
|
|
1079
|
-
fileSize < PROTOCOL_V2_OKPP_HEADER_SIZE
|
|
1080
|
-
) {
|
|
1081
|
-
return null;
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
|
-
const chunkSize = this.getProtocolV2FirmwareChunkSize('read');
|
|
1085
|
-
const chunks: Uint8Array[] = [];
|
|
1086
|
-
let offset = 0;
|
|
1087
|
-
while (offset < PROTOCOL_V2_OKPP_HEADER_SIZE) {
|
|
1088
|
-
const readLen = Math.min(chunkSize, PROTOCOL_V2_OKPP_HEADER_SIZE - offset);
|
|
1089
|
-
const res = await typedCall('FilesystemFileRead', 'FilesystemFile', {
|
|
1090
|
-
file: {
|
|
1091
|
-
path: filePath,
|
|
1092
|
-
offset,
|
|
1093
|
-
total_size: 0,
|
|
1094
|
-
},
|
|
1095
|
-
chunk_len: readLen,
|
|
1096
|
-
ui_percentage: undefined,
|
|
1097
|
-
});
|
|
1098
|
-
const data = toProtocolV2Bytes(res.message?.data);
|
|
1099
|
-
if (data.byteLength === 0) return null;
|
|
1100
|
-
chunks.push(data);
|
|
1101
|
-
offset += data.byteLength;
|
|
1102
|
-
}
|
|
1103
|
-
|
|
1104
|
-
const headerBytes = new Uint8Array(offset);
|
|
1105
|
-
let cursor = 0;
|
|
1106
|
-
chunks.forEach(chunk => {
|
|
1107
|
-
headerBytes.set(chunk, cursor);
|
|
1108
|
-
cursor += chunk.byteLength;
|
|
1109
|
-
});
|
|
1110
|
-
return parseProtocolV2OkppHeader(headerBytes);
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1113
752
|
private async downloadRemoteProtocolV2Component(
|
|
1114
753
|
key: string,
|
|
1115
754
|
component: IProtocolV2FirmwareComponent
|
|
@@ -1198,155 +837,164 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1198
837
|
};
|
|
1199
838
|
}
|
|
1200
839
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
840
|
+
private getProtocolV2DeviceType(): EDeviceType.Pro2 | EDeviceType.Neo {
|
|
841
|
+
const deviceType = this.device.getCurrentDeviceType();
|
|
842
|
+
if (deviceType === EDeviceType.Pro2 || deviceType === EDeviceType.Neo) return deviceType;
|
|
843
|
+
throw new Error(`Unsupported Protocol V2 device type: ${deviceType}`);
|
|
844
|
+
}
|
|
1204
845
|
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
};
|
|
846
|
+
private async prepareProtocolV2BootResources(): Promise<
|
|
847
|
+
ProtocolV2ResourceBundleBinary[] | undefined
|
|
848
|
+
> {
|
|
849
|
+
if (
|
|
850
|
+
!this.params.targetsToUpdate?.includes('resource') &&
|
|
851
|
+
!this.params.targetsToUpdate?.includes('boot_resources')
|
|
852
|
+
) {
|
|
853
|
+
return undefined;
|
|
854
|
+
}
|
|
855
|
+
const resource = DataManager.getProtocolV2BootResources(this.getProtocolV2DeviceType());
|
|
856
|
+
if (!resource) throw new Error('Missing Protocol V2 boot resources configuration');
|
|
857
|
+
|
|
858
|
+
const files: ProtocolV2ResourceBundleBinary[] = [];
|
|
859
|
+
for (const file of resource.files) {
|
|
860
|
+
Log.log(`[FirmwareUpdateV4] downloading boot resource ${file.devicePath}`);
|
|
861
|
+
const { binary } = await getSysResourceBinary(file.url);
|
|
862
|
+
if (!isProtocolV2ResourceFileValid(binary, file)) {
|
|
863
|
+
throw new Error(`Boot resource file verification failed: ${file.devicePath}`);
|
|
864
|
+
}
|
|
865
|
+
files.push({
|
|
866
|
+
name: file.name ?? file.devicePath.split('/').pop() ?? file.devicePath,
|
|
867
|
+
binary,
|
|
868
|
+
devicePath: file.devicePath,
|
|
1229
869
|
});
|
|
1230
870
|
}
|
|
871
|
+
return files;
|
|
872
|
+
}
|
|
1231
873
|
|
|
874
|
+
private prepareExplicitProtocolV2ResourceFiles(): ProtocolV2ResourceBundleBinary[] | undefined {
|
|
875
|
+
const files = this.params.resourceFiles ?? [];
|
|
876
|
+
if (!files?.length) return undefined;
|
|
877
|
+
|
|
878
|
+
const prepared = files.map((file, index) => {
|
|
879
|
+
const devicePath = validateProtocolV2FilesystemPath(
|
|
880
|
+
file.devicePath,
|
|
881
|
+
`resourceFiles[${index}].devicePath`
|
|
882
|
+
);
|
|
883
|
+
const descriptor = file as typeof file &
|
|
884
|
+
Pick<Partial<IProtocolV2ResourceFile>, 'size' | 'fileHash'>;
|
|
885
|
+
if (descriptor.size !== undefined && descriptor.size !== file.binary.byteLength) {
|
|
886
|
+
throw new Error(`resourceFiles[${index}] size mismatch`);
|
|
887
|
+
}
|
|
888
|
+
if (
|
|
889
|
+
descriptor.fileHash &&
|
|
890
|
+
!isProtocolV2ResourceFileValid(file.binary, {
|
|
891
|
+
size: file.binary.byteLength,
|
|
892
|
+
fileHash: descriptor.fileHash,
|
|
893
|
+
})
|
|
894
|
+
) {
|
|
895
|
+
throw new Error(`resourceFiles[${index}] SHA-256 mismatch`);
|
|
896
|
+
}
|
|
897
|
+
return {
|
|
898
|
+
name: devicePath.split('/').pop() ?? devicePath,
|
|
899
|
+
binary: file.binary,
|
|
900
|
+
devicePath,
|
|
901
|
+
};
|
|
902
|
+
});
|
|
903
|
+
if (new Set(prepared.map(file => file.devicePath)).size !== prepared.length) {
|
|
904
|
+
throw new Error('resourceFiles contain duplicate devicePath values');
|
|
905
|
+
}
|
|
906
|
+
return prepared;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
private async prepareProtocolV2ResourceBundles(): Promise<
|
|
910
|
+
ProtocolV2ResourceBundleBinary[] | undefined
|
|
911
|
+
> {
|
|
1232
912
|
if (!this.params.targetsToUpdate?.includes('resource')) {
|
|
1233
913
|
return undefined;
|
|
1234
914
|
}
|
|
1235
915
|
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
throw ERRORS.TypedError(
|
|
1263
|
-
HardwareErrorCode.FirmwareUpdateDownloadFailed,
|
|
1264
|
-
`Missing Protocol V2 RESC bundle binary: ${bundle.name}`
|
|
1265
|
-
);
|
|
1266
|
-
}
|
|
1267
|
-
if (
|
|
1268
|
-
this.params.artifactReader ||
|
|
1269
|
-
DataManager.getSettings('firmwareManifestMode') === 'external-only'
|
|
1270
|
-
) {
|
|
1271
|
-
throw ERRORS.TypedError(
|
|
1272
|
-
HardwareErrorCode.RuntimeError,
|
|
1273
|
-
`Firmware resource bundle ${bundle.name} is not prepared`,
|
|
1274
|
-
{
|
|
1275
|
-
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
1276
|
-
artifactName: bundle.name,
|
|
1277
|
-
}
|
|
1278
|
-
);
|
|
1279
|
-
}
|
|
1280
|
-
Log.log(`[FirmwareUpdateV4] downloading remote RESC bundle ${bundle.name}`);
|
|
1281
|
-
({ binary } = await getSysResourceBinary(bundle.url));
|
|
1282
|
-
bundle.binary = binary;
|
|
1283
|
-
downloadedFromRemote = true;
|
|
1284
|
-
}
|
|
1285
|
-
if (bundle.binary.byteLength === 0) {
|
|
1286
|
-
throw ERRORS.TypedError(
|
|
1287
|
-
HardwareErrorCode.FirmwareUpdateDownloadFailed,
|
|
1288
|
-
`Protocol V2 RESC bundle is empty: ${bundle.name}`
|
|
1289
|
-
);
|
|
1290
|
-
}
|
|
1291
|
-
if (downloadedFromRemote) {
|
|
1292
|
-
const header = parseProtocolV2OkppHeader(toProtocolV2Bytes(binary));
|
|
1293
|
-
if (!header || header.type !== 'RESC') {
|
|
1294
|
-
throw new Error(`Invalid Protocol V2 RESC bundle header: ${bundle.name}`);
|
|
1295
|
-
}
|
|
1296
|
-
if (bundle.version && compareProtocolV2Versions(header.version, bundle.version) !== 0) {
|
|
1297
|
-
throw new Error(`Protocol V2 RESC bundle version mismatch: ${bundle.name}`);
|
|
1298
|
-
}
|
|
1299
|
-
const expectedPayloadHash = normalizeProtocolV2Hex(bundle.payloadHash);
|
|
1300
|
-
if (expectedPayloadHash && header.payloadHash !== expectedPayloadHash) {
|
|
1301
|
-
throw new Error(`Protocol V2 RESC bundle payload hash mismatch: ${bundle.name}`);
|
|
1302
|
-
}
|
|
1303
|
-
const expectedHeaderHash = normalizeProtocolV2Hex(bundle.headerHash);
|
|
1304
|
-
if (expectedHeaderHash && header.headerHash !== expectedHeaderHash) {
|
|
1305
|
-
throw new Error(`Protocol V2 RESC bundle header hash mismatch: ${bundle.name}`);
|
|
1306
|
-
}
|
|
1307
|
-
}
|
|
916
|
+
const resources = DataManager.getProtocolV2Resources(this.getProtocolV2DeviceType());
|
|
917
|
+
if (!resources?.length) {
|
|
918
|
+
throw new Error('Missing Pro2 stable resource configuration');
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
const inventory = this.params.forcedUpdateRes
|
|
922
|
+
? undefined
|
|
923
|
+
: await readProtocolV2ResourceInventory({
|
|
924
|
+
commands: this.device.getCommands(),
|
|
925
|
+
resources,
|
|
926
|
+
chunkSize: this.getProtocolV2FirmwareChunkSize(),
|
|
927
|
+
timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
|
|
928
|
+
});
|
|
929
|
+
const plan = buildProtocolV2ResourceUpdatePlan({
|
|
930
|
+
resources,
|
|
931
|
+
inventory,
|
|
932
|
+
mode: 'bootloader-recovery',
|
|
933
|
+
forced: this.params.forcedUpdateRes,
|
|
934
|
+
});
|
|
935
|
+
Log.log(
|
|
936
|
+
`[FirmwareUpdateV4] Protocol V2 resource plan mode=bootloader-recovery status=${plan.status} count=${plan.resources.length}`
|
|
937
|
+
);
|
|
938
|
+
|
|
939
|
+
const bundles: ProtocolV2ResourceBundleBinary[] = [];
|
|
940
|
+
for (const resource of plan.resources) {
|
|
941
|
+
bundles.push(await this.downloadProtocolV2Resource(resource));
|
|
1308
942
|
}
|
|
1309
943
|
return bundles;
|
|
1310
944
|
}
|
|
1311
945
|
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
946
|
+
private async downloadProtocolV2Resource(
|
|
947
|
+
resource: IProtocolV2Resource
|
|
948
|
+
): Promise<ProtocolV2ResourceBundleBinary> {
|
|
949
|
+
Log.log(`[FirmwareUpdateV4] downloading Pro2 resource ${resource.type}`);
|
|
950
|
+
const { binary } = await getSysResourceBinary(resource.url);
|
|
951
|
+
if (!isProtocolV2ResourceFileValid(binary, resource)) {
|
|
952
|
+
throw new Error(`Pro2 resource file verification failed: ${resource.type}`);
|
|
953
|
+
}
|
|
954
|
+
return {
|
|
955
|
+
name: `${resource.type}.okpkg`,
|
|
956
|
+
binary,
|
|
957
|
+
devicePath: PROTOCOL_V2_RESOURCE_DEVICE_PATHS[resource.type],
|
|
958
|
+
};
|
|
959
|
+
}
|
|
1323
960
|
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
961
|
+
private async syncProtocolV2ResourceBundles(
|
|
962
|
+
bundles: ProtocolV2ResourceBundleBinary[],
|
|
963
|
+
firmwareSize: number
|
|
964
|
+
): Promise<{ processedSize: number; totalSize: number }> {
|
|
965
|
+
const transferStartTime = Date.now();
|
|
966
|
+
const transferTransport = this.getProtocolV2FirmwareTransferTransport();
|
|
1327
967
|
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
968
|
+
// Write directly to the device through FileWrite.
|
|
969
|
+
let totalSize = 0;
|
|
970
|
+
for (const b of bundles) totalSize += b.binary.byteLength;
|
|
971
|
+
|
|
972
|
+
const transferTotalSize = totalSize + firmwareSize;
|
|
973
|
+
let processedSize = 0;
|
|
974
|
+
for (const bundle of bundles) {
|
|
975
|
+
Log.log(
|
|
976
|
+
`[FirmwareUpdateV4] syncing resource ${bundle.name} -> ${bundle.devicePath} bytes=${bundle.binary.byteLength}`
|
|
977
|
+
);
|
|
978
|
+
processedSize = await this.protocolV2CommonUpdateProcess({
|
|
979
|
+
payload: bundle.binary,
|
|
980
|
+
filePath: bundle.devicePath,
|
|
981
|
+
processedSize,
|
|
982
|
+
totalSize: transferTotalSize,
|
|
983
|
+
});
|
|
1344
984
|
}
|
|
985
|
+
|
|
986
|
+
const elapsedMs = Date.now() - transferStartTime;
|
|
987
|
+
Log.log(
|
|
988
|
+
`[FirmwareUpdateV4] resource sync finished transport=${transferTransport} bytes=${totalSize} elapsed=${(
|
|
989
|
+
elapsedMs / 1000
|
|
990
|
+
).toFixed(2)}s speed=${formatProtocolV2TransferSpeed(totalSize, elapsedMs)} KB/s`
|
|
991
|
+
);
|
|
992
|
+
return { processedSize, totalSize: transferTotalSize };
|
|
1345
993
|
}
|
|
1346
994
|
|
|
1347
995
|
private isProtocolV2BootloaderMode() {
|
|
1348
996
|
if (typeof this.device.isBootloader === 'function') {
|
|
1349
|
-
return this.device.isBootloader();
|
|
997
|
+
return !!this.device.isBootloader();
|
|
1350
998
|
}
|
|
1351
999
|
return (
|
|
1352
1000
|
this.device.features?.mode === 'bootloader' ||
|
|
@@ -1355,7 +1003,11 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1355
1003
|
}
|
|
1356
1004
|
|
|
1357
1005
|
private isProtocolV2RomloaderMode() {
|
|
1358
|
-
|
|
1006
|
+
const deviceType = this.device.getCurrentDeviceType();
|
|
1007
|
+
if (
|
|
1008
|
+
!this.device.isProtocolV2() ||
|
|
1009
|
+
(deviceType !== EDeviceType.Pro2 && deviceType !== EDeviceType.Neo)
|
|
1010
|
+
) {
|
|
1359
1011
|
return false;
|
|
1360
1012
|
}
|
|
1361
1013
|
if (typeof this.device.isRomloader === 'function') {
|
|
@@ -1369,22 +1021,20 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1369
1021
|
// It rejects DeviceRebootType.Bootloader, so reuse the current connection.
|
|
1370
1022
|
if (this.isProtocolV2RomloaderMode()) {
|
|
1371
1023
|
Log.debug('Protocol V2 device is in romloader mode; start firmware update directly');
|
|
1372
|
-
this.protocolV2ExecutionInLoader = true;
|
|
1373
1024
|
return false;
|
|
1374
1025
|
}
|
|
1375
1026
|
if (this.isProtocolV2BootloaderMode()) {
|
|
1376
1027
|
Log.debug('Protocol V2 device is already in bootloader mode, skip reboot');
|
|
1377
|
-
this.
|
|
1028
|
+
this.postTipMessage(FirmwareUpdateTipMessage.GoToBootloaderSuccess);
|
|
1378
1029
|
return false;
|
|
1379
1030
|
}
|
|
1380
1031
|
|
|
1381
1032
|
try {
|
|
1382
1033
|
this.postTipMessage(FirmwareUpdateTipMessage.AutoRebootToBootloader);
|
|
1383
1034
|
await this.protocolV2Reboot(DeviceRebootType.Bootloader);
|
|
1384
|
-
this.postTipMessage(FirmwareUpdateTipMessage.GoToBootloaderSuccess);
|
|
1385
1035
|
await wait(1000);
|
|
1386
1036
|
await this.waitForProtocolV2BootloaderMode();
|
|
1387
|
-
this.
|
|
1037
|
+
this.postTipMessage(FirmwareUpdateTipMessage.GoToBootloaderSuccess);
|
|
1388
1038
|
return true;
|
|
1389
1039
|
} catch (error) {
|
|
1390
1040
|
if (error instanceof HardwareError) {
|
|
@@ -1401,13 +1051,17 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1401
1051
|
) {
|
|
1402
1052
|
const startTime = Date.now();
|
|
1403
1053
|
let lastError: unknown;
|
|
1054
|
+
let shouldReconnect = true;
|
|
1404
1055
|
|
|
1405
1056
|
while (Date.now() - startTime < timeout) {
|
|
1406
1057
|
try {
|
|
1407
|
-
|
|
1058
|
+
if (shouldReconnect) {
|
|
1059
|
+
await this.reconnectProtocolV2Device();
|
|
1060
|
+
shouldReconnect = false;
|
|
1061
|
+
}
|
|
1408
1062
|
const deviceInfo = await requestProtocolV2DeviceInfo({
|
|
1409
1063
|
commands: this.device.getCommands(),
|
|
1410
|
-
timeoutMs:
|
|
1064
|
+
timeoutMs: this.getProtocolV2DeviceInfoTimeout(),
|
|
1411
1065
|
});
|
|
1412
1066
|
this.assertProtocolV2DeviceInfoIdentity(deviceInfo);
|
|
1413
1067
|
const features = await this.device.probeProtocolV2RuntimeState(
|
|
@@ -1422,6 +1076,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1422
1076
|
if (this.isProtocolV2ReconnectIdentityError(error)) {
|
|
1423
1077
|
throw error;
|
|
1424
1078
|
}
|
|
1079
|
+
shouldReconnect = true;
|
|
1425
1080
|
lastError = error;
|
|
1426
1081
|
Log.log('Protocol V2 bootloader mode not ready, polling reconnect: ', error);
|
|
1427
1082
|
}
|
|
@@ -1474,98 +1129,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1474
1129
|
return entries;
|
|
1475
1130
|
}
|
|
1476
1131
|
|
|
1477
|
-
private buildProtocolV2ExecutionPhases({
|
|
1478
|
-
installSources,
|
|
1479
|
-
resourceSources,
|
|
1480
|
-
}: {
|
|
1481
|
-
installSources: ProtocolV2InstallSource[];
|
|
1482
|
-
resourceSources: ProtocolV2ResourceBundleSource[];
|
|
1483
|
-
}): ProtocolV2ExecutionPhase[] {
|
|
1484
|
-
const phases: ProtocolV2ExecutionPhase[] = [];
|
|
1485
|
-
if (resourceSources.length > 0) {
|
|
1486
|
-
phases.push({
|
|
1487
|
-
kind: 'resource-sync',
|
|
1488
|
-
installSources: [],
|
|
1489
|
-
resourceSources,
|
|
1490
|
-
});
|
|
1491
|
-
}
|
|
1492
|
-
const bootloaderSources = installSources.filter(source => source.kind === 'bootloader');
|
|
1493
|
-
if (bootloaderSources.length > 0) {
|
|
1494
|
-
phases.push(
|
|
1495
|
-
{
|
|
1496
|
-
kind: 'bootloader-install',
|
|
1497
|
-
installSources: bootloaderSources,
|
|
1498
|
-
resourceSources: [],
|
|
1499
|
-
},
|
|
1500
|
-
{
|
|
1501
|
-
kind: 'bootloader-verify',
|
|
1502
|
-
installSources: [],
|
|
1503
|
-
resourceSources: [],
|
|
1504
|
-
}
|
|
1505
|
-
);
|
|
1506
|
-
}
|
|
1507
|
-
const componentSources = installSources.filter(source => source.kind !== 'bootloader');
|
|
1508
|
-
if (componentSources.length > 0) {
|
|
1509
|
-
phases.push({
|
|
1510
|
-
kind: 'component-install',
|
|
1511
|
-
installSources: componentSources,
|
|
1512
|
-
resourceSources: [],
|
|
1513
|
-
});
|
|
1514
|
-
}
|
|
1515
|
-
phases.push({
|
|
1516
|
-
kind: 'final-verify',
|
|
1517
|
-
installSources: [],
|
|
1518
|
-
resourceSources: [],
|
|
1519
|
-
});
|
|
1520
|
-
return phases;
|
|
1521
|
-
}
|
|
1522
|
-
|
|
1523
|
-
private async executeProtocolV2Phases({
|
|
1524
|
-
installSources,
|
|
1525
|
-
resourceSources,
|
|
1526
|
-
}: {
|
|
1527
|
-
installSources: ProtocolV2InstallSource[];
|
|
1528
|
-
resourceSources: ProtocolV2ResourceBundleSource[];
|
|
1529
|
-
}) {
|
|
1530
|
-
const phases = this.buildProtocolV2ExecutionPhases({
|
|
1531
|
-
installSources,
|
|
1532
|
-
resourceSources,
|
|
1533
|
-
});
|
|
1534
|
-
for (const phase of phases) {
|
|
1535
|
-
if (phase.kind === 'final-verify') {
|
|
1536
|
-
return this.completeProtocolV2FinalVerification();
|
|
1537
|
-
}
|
|
1538
|
-
if (phase.kind === 'resource-sync') {
|
|
1539
|
-
await this.enterProtocolV2BootloaderMode();
|
|
1540
|
-
await this.executeProtocolV2TransferPhase(phase);
|
|
1541
|
-
} else if (phase.kind === 'bootloader-install' || phase.kind === 'component-install') {
|
|
1542
|
-
await this.enterProtocolV2BootloaderMode();
|
|
1543
|
-
await this.executeProtocolV2TransferPhase(phase);
|
|
1544
|
-
await this.exitProtocolV2BootloaderToNormal();
|
|
1545
|
-
} else if (phase.kind === 'bootloader-verify') {
|
|
1546
|
-
await this.waitForProtocolV2FinalFeatures();
|
|
1547
|
-
this.assertExpectedProtocolV2Versions(['boot']);
|
|
1548
|
-
}
|
|
1549
|
-
}
|
|
1550
|
-
throw ERRORS.TypedError(
|
|
1551
|
-
HardwareErrorCode.RuntimeError,
|
|
1552
|
-
'Protocol V2 execution has no final verification phase'
|
|
1553
|
-
);
|
|
1554
|
-
}
|
|
1555
|
-
|
|
1556
|
-
private async executeProtocolV2SourceUpdate({
|
|
1557
|
-
installSources,
|
|
1558
|
-
resourceSources,
|
|
1559
|
-
}: {
|
|
1560
|
-
installSources: ProtocolV2InstallSource[];
|
|
1561
|
-
resourceSources: ProtocolV2ResourceBundleSource[];
|
|
1562
|
-
}) {
|
|
1563
|
-
return this.executeProtocolV2Phases({
|
|
1564
|
-
installSources,
|
|
1565
|
-
resourceSources,
|
|
1566
|
-
});
|
|
1567
|
-
}
|
|
1568
|
-
|
|
1569
1132
|
private async executeProtocolV2Update({
|
|
1570
1133
|
fwBinaryMap,
|
|
1571
1134
|
bootloaderBinary,
|
|
@@ -1577,158 +1140,104 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1577
1140
|
installItems?: ProtocolV2InstallItem[];
|
|
1578
1141
|
resourceBundles?: ProtocolV2ResourceBundleBinary[];
|
|
1579
1142
|
}) {
|
|
1580
|
-
const
|
|
1143
|
+
const orderedInstallItems =
|
|
1581
1144
|
installItems ??
|
|
1582
1145
|
this.buildProtocolV2InstallItems({
|
|
1583
|
-
fwBinaryMap: fwBinaryMap ?? [],
|
|
1584
1146
|
bootloaderBinary: bootloaderBinary ?? null,
|
|
1147
|
+
fwBinaryMap: fwBinaryMap ?? [],
|
|
1585
1148
|
});
|
|
1586
|
-
try {
|
|
1587
|
-
const installSources = await Promise.all(
|
|
1588
|
-
memoryInstallItems.map(async item => ({
|
|
1589
|
-
fileName: item.fileName,
|
|
1590
|
-
source: await this.openProtocolV2MemorySource(item.binary),
|
|
1591
|
-
targetId: item.targetId,
|
|
1592
|
-
kind: item.kind,
|
|
1593
|
-
}))
|
|
1594
|
-
);
|
|
1595
|
-
const resourceSources = await Promise.all(
|
|
1596
|
-
(resourceBundles ?? []).map(async bundle => ({
|
|
1597
|
-
name: bundle.name,
|
|
1598
|
-
source: await this.openProtocolV2MemorySource(bundle.binary),
|
|
1599
|
-
devicePath: bundle.devicePath,
|
|
1600
|
-
version: bundle.version,
|
|
1601
|
-
payloadHash: bundle.payloadHash,
|
|
1602
|
-
headerHash: bundle.headerHash,
|
|
1603
|
-
}))
|
|
1604
|
-
);
|
|
1605
|
-
return await this.executeProtocolV2Phases({
|
|
1606
|
-
installSources,
|
|
1607
|
-
resourceSources,
|
|
1608
|
-
});
|
|
1609
|
-
} finally {
|
|
1610
|
-
await this.closeProtocolV2PreparedSources();
|
|
1611
|
-
}
|
|
1612
|
-
}
|
|
1613
1149
|
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
resourceSources,
|
|
1617
|
-
}: ProtocolV2ExecutionPhase) {
|
|
1618
|
-
let totalSize = installSources.reduce((total, item) => total + item.source.size, 0);
|
|
1619
|
-
const resourcesToSync: ProtocolV2ResourceBundleSource[] = [];
|
|
1620
|
-
for (const resource of resourceSources) {
|
|
1621
|
-
if (await this.isProtocolV2ResourceBundleUpToDate(resource)) {
|
|
1622
|
-
Log.log(`[FirmwareUpdateV4] skip RESC bundle ${resource.name}; already up to date`);
|
|
1623
|
-
} else {
|
|
1624
|
-
resourcesToSync.push(resource);
|
|
1625
|
-
totalSize += resource.source.size;
|
|
1626
|
-
}
|
|
1627
|
-
}
|
|
1150
|
+
let firmwareSize = 0;
|
|
1151
|
+
for (const item of orderedInstallItems) firmwareSize += item.binary.byteLength;
|
|
1628
1152
|
|
|
1629
1153
|
this.postTipMessage(FirmwareUpdateTipMessage.StartTransferData);
|
|
1154
|
+
|
|
1630
1155
|
let processedSize = 0;
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1156
|
+
let totalSize = firmwareSize;
|
|
1157
|
+
if (resourceBundles?.length) {
|
|
1158
|
+
const resourceTransfer = await this.syncProtocolV2ResourceBundles(
|
|
1159
|
+
resourceBundles,
|
|
1160
|
+
firmwareSize
|
|
1161
|
+
);
|
|
1162
|
+
processedSize = resourceTransfer.processedSize;
|
|
1163
|
+
totalSize = resourceTransfer.totalSize;
|
|
1639
1164
|
}
|
|
1640
1165
|
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
totalSize,
|
|
1649
|
-
});
|
|
1650
|
-
await this.verifyProtocolV2StagedFile(filePath, item.source.size);
|
|
1651
|
-
stagedInstallTargets.push({
|
|
1652
|
-
targetId: item.targetId,
|
|
1653
|
-
path: filePath,
|
|
1654
|
-
});
|
|
1166
|
+
// Skip staging and installation when the update contains resource files only.
|
|
1167
|
+
if (orderedInstallItems.length === 0) {
|
|
1168
|
+
Log.log('[FirmwareUpdateV4] no firmware targets to install (resource files only)');
|
|
1169
|
+
if (totalSize > 0) {
|
|
1170
|
+
this.postProgressMessage(100, 'transferData');
|
|
1171
|
+
}
|
|
1172
|
+
return;
|
|
1655
1173
|
}
|
|
1174
|
+
let transferredSize = processedSize;
|
|
1175
|
+
const transferStartTime = Date.now();
|
|
1176
|
+
const transferTransport = this.getProtocolV2FirmwareTransferTransport();
|
|
1177
|
+
const chunkSize = this.getProtocolV2FirmwareChunkSize();
|
|
1178
|
+
const onTransferredBytes = (bytes: number) => {
|
|
1179
|
+
transferredSize = bytes;
|
|
1180
|
+
};
|
|
1181
|
+
Log.log(
|
|
1182
|
+
`[FirmwareUpdateV4] transfer started transport=${transferTransport} total=${totalSize} bytes chunk=${chunkSize} bytes`
|
|
1183
|
+
);
|
|
1656
1184
|
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1185
|
+
const stagedInstallTargets: ProtocolV2InstallTarget[] = [];
|
|
1186
|
+
|
|
1187
|
+
try {
|
|
1188
|
+
for (const item of orderedInstallItems) {
|
|
1189
|
+
const filePath = this.getProtocolV2InstallItemStagingPath(item);
|
|
1190
|
+
Log.log(
|
|
1191
|
+
`[FirmwareUpdateV4] staging ${item.kind} via FilesystemFileWrite target=${item.targetId} path=${filePath} source=${item.fileName} bytes=${item.binary.byteLength}`
|
|
1192
|
+
);
|
|
1193
|
+
processedSize = await this.protocolV2CommonUpdateProcess({
|
|
1194
|
+
payload: item.binary,
|
|
1195
|
+
filePath,
|
|
1196
|
+
processedSize,
|
|
1197
|
+
totalSize,
|
|
1198
|
+
onTransferredBytes,
|
|
1199
|
+
});
|
|
1200
|
+
transferredSize = processedSize;
|
|
1201
|
+
await this.verifyProtocolV2StagedFile(filePath, item.binary.byteLength);
|
|
1202
|
+
|
|
1203
|
+
stagedInstallTargets.push({
|
|
1204
|
+
...item,
|
|
1205
|
+
path: filePath,
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
if (totalSize > 0) {
|
|
1210
|
+
this.postProgressMessage(100, 'transferData');
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
const elapsedMs = Date.now() - transferStartTime;
|
|
1214
|
+
Log.log(
|
|
1215
|
+
`[FirmwareUpdateV4] transfer finished transport=${transferTransport} bytes=${totalSize} elapsed=${(
|
|
1216
|
+
elapsedMs / 1000
|
|
1217
|
+
).toFixed(2)}s speed=${formatProtocolV2TransferSpeed(totalSize, elapsedMs)} KB/s`
|
|
1218
|
+
);
|
|
1219
|
+
} catch (error) {
|
|
1220
|
+
const elapsedMs = Date.now() - transferStartTime;
|
|
1221
|
+
Log.warn(
|
|
1222
|
+
`[FirmwareUpdateV4] transfer failed transport=${transferTransport} bytes=${transferredSize}/${totalSize} elapsed=${(
|
|
1223
|
+
elapsedMs / 1000
|
|
1224
|
+
).toFixed(2)}s speed=${formatProtocolV2TransferSpeed(transferredSize, elapsedMs)} KB/s`
|
|
1225
|
+
);
|
|
1226
|
+
throw error;
|
|
1662
1227
|
}
|
|
1663
1228
|
|
|
1664
1229
|
this.postTipMessage(FirmwareUpdateTipMessage.ConfirmOnDevice);
|
|
1665
|
-
|
|
1230
|
+
|
|
1231
|
+
const allTargets = stagedInstallTargets.map(item => ({
|
|
1666
1232
|
target_id: item.targetId,
|
|
1667
1233
|
path: item.path,
|
|
1668
1234
|
}));
|
|
1669
|
-
|
|
1670
|
-
await this.
|
|
1235
|
+
Log.log(`[FirmwareUpdateV4] DeviceFirmwareUpdateRequest targets=${JSON.stringify(allTargets)}`);
|
|
1236
|
+
await this.protocolV2StartFirmwareUpdate({ targets: allTargets });
|
|
1237
|
+
await this.waitForProtocolV2FirmwareUpdateComplete(allTargets);
|
|
1671
1238
|
}
|
|
1672
1239
|
|
|
1673
|
-
private
|
|
1674
|
-
source,
|
|
1675
|
-
filePath,
|
|
1676
|
-
processedSize,
|
|
1677
|
-
totalSize,
|
|
1678
|
-
}: {
|
|
1679
|
-
source: FirmwareByteSource;
|
|
1680
|
-
filePath: string;
|
|
1681
|
-
processedSize: number;
|
|
1682
|
-
totalSize: number;
|
|
1683
|
-
}) {
|
|
1684
|
-
let lastError: unknown;
|
|
1685
|
-
for (let attempt = 1; attempt <= PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT; attempt += 1) {
|
|
1686
|
-
try {
|
|
1687
|
-
await writeFirmwareByteSource({
|
|
1688
|
-
source,
|
|
1689
|
-
chunkSize: this.getProtocolV2FirmwareChunkSize(),
|
|
1690
|
-
write: async ({ data, sourceOffset, length, first }) => {
|
|
1691
|
-
const chunkEnd = sourceOffset + length;
|
|
1692
|
-
const progress = getProtocolV2DeviceTransferProgress(
|
|
1693
|
-
processedSize + sourceOffset,
|
|
1694
|
-
processedSize + chunkEnd,
|
|
1695
|
-
totalSize
|
|
1696
|
-
);
|
|
1697
|
-
const response = await this.fileWriteChunk(
|
|
1698
|
-
filePath,
|
|
1699
|
-
source.size,
|
|
1700
|
-
sourceOffset,
|
|
1701
|
-
data,
|
|
1702
|
-
first,
|
|
1703
|
-
progress
|
|
1704
|
-
);
|
|
1705
|
-
const rawProcessedByte = response.message.processed_byte;
|
|
1706
|
-
const nextOffset = rawProcessedByte === undefined ? chunkEnd : Number(rawProcessedByte);
|
|
1707
|
-
if (!Number.isFinite(nextOffset) || nextOffset !== chunkEnd) {
|
|
1708
|
-
throw ERRORS.TypedError(
|
|
1709
|
-
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
1710
|
-
`invalid processed_byte ${rawProcessedByte} for offset ${sourceOffset}`
|
|
1711
|
-
);
|
|
1712
|
-
}
|
|
1713
|
-
this.postProgressMessage(progress, 'transferData');
|
|
1714
|
-
return length;
|
|
1715
|
-
},
|
|
1716
|
-
});
|
|
1717
|
-
return processedSize + source.size;
|
|
1718
|
-
} catch (error) {
|
|
1719
|
-
lastError = error;
|
|
1720
|
-
if (attempt < PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT) {
|
|
1721
|
-
await this.recoverProtocolV2FileTransfer();
|
|
1722
|
-
}
|
|
1723
|
-
}
|
|
1724
|
-
}
|
|
1725
|
-
throw ERRORS.TypedError(
|
|
1726
|
-
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
1727
|
-
`transfer data error: ${getProtocolV2UnknownErrorText(lastError)}`
|
|
1728
|
-
);
|
|
1729
|
-
}
|
|
1730
|
-
|
|
1731
|
-
private getProtocolV2InstallItemStagingPath(item: Pick<ProtocolV2InstallItem, 'fileName'>) {
|
|
1240
|
+
private getProtocolV2InstallItemStagingPath(item: ProtocolV2InstallItem) {
|
|
1732
1241
|
return `${PROTOCOL_V2_FIRMWARE_STAGING_VOLUME}${item.fileName}`;
|
|
1733
1242
|
}
|
|
1734
1243
|
|
|
@@ -1747,8 +1256,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1747
1256
|
|
|
1748
1257
|
private assertProtocolV2TargetStatus(
|
|
1749
1258
|
statusTargets: ProtocolV2FirmwareUpdateStatusTarget[],
|
|
1750
|
-
expectedTargetIds: Set<number
|
|
1751
|
-
expectedPaths = new Map<number, string>()
|
|
1259
|
+
expectedTargetIds: Set<number>
|
|
1752
1260
|
) {
|
|
1753
1261
|
Log.log(
|
|
1754
1262
|
`[FirmwareUpdateV4] DeviceFirmwareUpdateStatus records=${JSON.stringify(statusTargets)}`
|
|
@@ -1779,57 +1287,17 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1779
1287
|
const matchingTargets = statusTargets.filter(target =>
|
|
1780
1288
|
expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1)
|
|
1781
1289
|
);
|
|
1782
|
-
const seenTargetIds = new Set<number>();
|
|
1783
|
-
for (const target of matchingTargets) {
|
|
1784
|
-
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
1785
|
-
if (
|
|
1786
|
-
targetId === undefined ||
|
|
1787
|
-
seenTargetIds.has(targetId) ||
|
|
1788
|
-
(target.path && expectedPaths.get(targetId) && target.path !== expectedPaths.get(targetId))
|
|
1789
|
-
) {
|
|
1790
|
-
throw ERRORS.TypedError(
|
|
1791
|
-
HardwareErrorCode.RuntimeError,
|
|
1792
|
-
`Protocol V2 install status conflicts with target ${target.target_id}`,
|
|
1793
|
-
{
|
|
1794
|
-
firmwareUpdateCode: PROTOCOL_V2_INSTALL_STATUS_CONFLICT_CODE,
|
|
1795
|
-
}
|
|
1796
|
-
);
|
|
1797
|
-
}
|
|
1798
|
-
seenTargetIds.add(targetId);
|
|
1799
|
-
}
|
|
1800
|
-
const completedTargets = matchingTargets.filter(target =>
|
|
1801
|
-
isProtocolV2TargetStatusFinished(target.status)
|
|
1802
|
-
);
|
|
1803
1290
|
const completedTargetIds = new Set<number>();
|
|
1804
|
-
|
|
1291
|
+
matchingTargets.forEach(target => {
|
|
1805
1292
|
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
1806
|
-
if (targetId !== undefined) {
|
|
1293
|
+
if (targetId !== undefined && isProtocolV2TargetStatusFinished(target.status)) {
|
|
1807
1294
|
completedTargetIds.add(targetId);
|
|
1808
1295
|
}
|
|
1809
1296
|
});
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
if (targetId === undefined) {
|
|
1815
|
-
throw ERRORS.TypedError(
|
|
1816
|
-
HardwareErrorCode.RuntimeError,
|
|
1817
|
-
'Protocol V2 completed target identity is invalid'
|
|
1818
|
-
);
|
|
1819
|
-
}
|
|
1820
|
-
const expectedVersion = this.getExpectedProtocolV2TargetVersion(targetId);
|
|
1821
|
-
if (expectedVersion !== undefined && payloadVersion !== undefined) {
|
|
1822
|
-
if (payloadVersion !== expectedVersion) {
|
|
1823
|
-
throw ERRORS.TypedError(
|
|
1824
|
-
HardwareErrorCode.FirmwareVerificationFailed,
|
|
1825
|
-
`Protocol V2 target ${targetId} reached payload version ${payloadVersion}, expected ${expectedVersion}`
|
|
1826
|
-
);
|
|
1827
|
-
}
|
|
1828
|
-
}
|
|
1829
|
-
if (payloadVersion !== undefined) {
|
|
1830
|
-
this.protocolV2CompletedTargetVersions.set(targetId, payloadVersion);
|
|
1831
|
-
}
|
|
1832
|
-
}
|
|
1297
|
+
const allExpectedTargetsCompleted =
|
|
1298
|
+
expectedTargetIds.size > 0 &&
|
|
1299
|
+
Array.from(expectedTargetIds).every(targetId => completedTargetIds.has(targetId));
|
|
1300
|
+
if (allExpectedTargetsCompleted) {
|
|
1833
1301
|
this.postProgressMessage(100, 'installingFirmware');
|
|
1834
1302
|
return true;
|
|
1835
1303
|
}
|
|
@@ -1850,19 +1318,43 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1850
1318
|
return false;
|
|
1851
1319
|
}
|
|
1852
1320
|
|
|
1321
|
+
private getProtocolV2MissingTargetIds(
|
|
1322
|
+
statusTargets: ProtocolV2FirmwareUpdateStatusTarget[],
|
|
1323
|
+
expectedTargetIds: Set<number>
|
|
1324
|
+
) {
|
|
1325
|
+
const reportedTargetIds = new Set<number>();
|
|
1326
|
+
statusTargets.forEach(target => {
|
|
1327
|
+
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
1328
|
+
if (targetId !== undefined) {
|
|
1329
|
+
reportedTargetIds.add(targetId);
|
|
1330
|
+
}
|
|
1331
|
+
});
|
|
1332
|
+
return Array.from(expectedTargetIds).filter(targetId => !reportedTargetIds.has(targetId));
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1853
1335
|
private async waitForProtocolV2FirmwareUpdateComplete(
|
|
1854
1336
|
targets: Array<{ target_id: number; path: string }>
|
|
1855
1337
|
) {
|
|
1856
|
-
this.protocolV2FinalStatusVerified = false;
|
|
1857
1338
|
const expectedTargetIds = new Set(targets.map(target => target.target_id));
|
|
1858
|
-
const expectedPaths = new Map(targets.map(target => [target.target_id, target.path]));
|
|
1859
1339
|
const startTime = Date.now();
|
|
1860
1340
|
let lastError: unknown;
|
|
1341
|
+
let shouldReconnect = true;
|
|
1342
|
+
let deviceInfo: ProtocolV2DeviceInfo | undefined;
|
|
1343
|
+
let missingTargetStatusSince: number | undefined;
|
|
1344
|
+
let missingTargetStatusKey: string | undefined;
|
|
1345
|
+
const resetMissingTargetStatusGrace = () => {
|
|
1346
|
+
missingTargetStatusSince = undefined;
|
|
1347
|
+
missingTargetStatusKey = undefined;
|
|
1348
|
+
};
|
|
1861
1349
|
|
|
1862
1350
|
while (Date.now() - startTime < PROTOCOL_V2_INSTALL_TIMEOUT) {
|
|
1863
1351
|
try {
|
|
1864
|
-
|
|
1865
|
-
|
|
1352
|
+
if (shouldReconnect) {
|
|
1353
|
+
await this.reconnectProtocolV2Device();
|
|
1354
|
+
deviceInfo = await this.verifyProtocolV2ReconnectIdentity();
|
|
1355
|
+
shouldReconnect = false;
|
|
1356
|
+
}
|
|
1357
|
+
const currentDeviceInfo = deviceInfo;
|
|
1866
1358
|
try {
|
|
1867
1359
|
const statusResponse = await this.device.getCommands().typedCall(
|
|
1868
1360
|
'DeviceFirmwareUpdateStatusGet',
|
|
@@ -1874,36 +1366,89 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1874
1366
|
path: true,
|
|
1875
1367
|
},
|
|
1876
1368
|
},
|
|
1877
|
-
{ timeoutMs:
|
|
1369
|
+
{ timeoutMs: PROTOCOL_V2_FIRMWARE_STATUS_RESPONSE_TIMEOUT }
|
|
1878
1370
|
);
|
|
1371
|
+
const statusTargets = (statusResponse.message.records ??
|
|
1372
|
+
[]) as ProtocolV2FirmwareUpdateStatusTarget[];
|
|
1373
|
+
if (this.assertProtocolV2TargetStatus(statusTargets, expectedTargetIds)) {
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1879
1377
|
if (
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
expectedPaths
|
|
1884
|
-
)
|
|
1378
|
+
statusTargets.length === 0 &&
|
|
1379
|
+
currentDeviceInfo &&
|
|
1380
|
+
(await this.probeProtocolV2NormalMode(currentDeviceInfo))
|
|
1885
1381
|
) {
|
|
1886
|
-
|
|
1382
|
+
Log.log(
|
|
1383
|
+
'[FirmwareUpdateV4] empty firmware status after confirmed App reboot; update complete'
|
|
1384
|
+
);
|
|
1385
|
+
this.postProgressMessage(100, 'installingFirmware');
|
|
1887
1386
|
return;
|
|
1888
1387
|
}
|
|
1889
|
-
|
|
1388
|
+
|
|
1389
|
+
const missingTargetIds = this.getProtocolV2MissingTargetIds(
|
|
1390
|
+
statusTargets,
|
|
1391
|
+
expectedTargetIds
|
|
1392
|
+
);
|
|
1393
|
+
if (missingTargetIds.length > 0) {
|
|
1394
|
+
const now = Date.now();
|
|
1395
|
+
const missingKey = missingTargetIds.join(',');
|
|
1396
|
+
if (missingTargetStatusSince === undefined || missingTargetStatusKey !== missingKey) {
|
|
1397
|
+
missingTargetStatusSince = now;
|
|
1398
|
+
missingTargetStatusKey = missingKey;
|
|
1399
|
+
} else if (
|
|
1400
|
+
now - missingTargetStatusSince >=
|
|
1401
|
+
PROTOCOL_V2_MISSING_TARGET_STATUS_GRACE_TIMEOUT
|
|
1402
|
+
) {
|
|
1403
|
+
const reportedTargetIds = statusTargets
|
|
1404
|
+
.map(target => normalizeProtocolV2TargetId(target.target_id))
|
|
1405
|
+
.filter((targetId): targetId is number => targetId !== undefined);
|
|
1406
|
+
throw ERRORS.TypedError(
|
|
1407
|
+
HardwareErrorCode.FirmwareError,
|
|
1408
|
+
`Protocol V2 firmware status is missing requested records: targetIds=${missingKey} reportedTargetIds=${reportedTargetIds.join(
|
|
1409
|
+
','
|
|
1410
|
+
)}`
|
|
1411
|
+
);
|
|
1412
|
+
}
|
|
1413
|
+
lastError = new Error(
|
|
1414
|
+
`Protocol V2 firmware status is temporarily missing targetIds=${missingKey}`
|
|
1415
|
+
);
|
|
1416
|
+
} else {
|
|
1417
|
+
resetMissingTargetStatusGrace();
|
|
1418
|
+
lastError = new Error('Protocol V2 firmware targets are still installing');
|
|
1419
|
+
}
|
|
1890
1420
|
} catch (error) {
|
|
1891
|
-
if (
|
|
1421
|
+
if (
|
|
1422
|
+
error instanceof HardwareError &&
|
|
1423
|
+
error.errorCode === HardwareErrorCode.FirmwareError
|
|
1424
|
+
) {
|
|
1892
1425
|
throw error;
|
|
1893
1426
|
}
|
|
1427
|
+
// Only consecutive full status dumps can prove that a record is absent.
|
|
1428
|
+
// A reboot or transport interruption starts a fresh grace window.
|
|
1429
|
+
resetMissingTargetStatusGrace();
|
|
1894
1430
|
// App firmware does not register DeviceFirmwareUpdateStatusGet. Treat the
|
|
1895
1431
|
// missing endpoint as completion only after the runtime probe confirms App mode.
|
|
1896
1432
|
if (isProtocolV2FirmwareStatusEndpointUnavailable(error)) {
|
|
1897
|
-
if (
|
|
1433
|
+
if (!currentDeviceInfo) {
|
|
1434
|
+
throw ERRORS.TypedError(
|
|
1435
|
+
HardwareErrorCode.RuntimeError,
|
|
1436
|
+
'Protocol V2 device identity is unavailable during install polling'
|
|
1437
|
+
);
|
|
1438
|
+
}
|
|
1439
|
+
if (await this.probeProtocolV2NormalMode(currentDeviceInfo)) {
|
|
1898
1440
|
Log.log(
|
|
1899
1441
|
'[FirmwareUpdateV4] firmware status endpoint unavailable after confirmed App reboot'
|
|
1900
1442
|
);
|
|
1443
|
+
this.postProgressMessage(100, 'installingFirmware');
|
|
1901
1444
|
return;
|
|
1902
1445
|
}
|
|
1903
1446
|
lastError = new Error(
|
|
1904
1447
|
'Protocol V2 firmware status endpoint is unavailable while the device remains in loader mode'
|
|
1905
1448
|
);
|
|
1906
1449
|
} else {
|
|
1450
|
+
shouldReconnect = true;
|
|
1451
|
+
deviceInfo = undefined;
|
|
1907
1452
|
lastError = error;
|
|
1908
1453
|
Log.log(
|
|
1909
1454
|
'[FirmwareUpdateV4] DeviceFirmwareUpdateStatusGet unavailable during install: ',
|
|
@@ -1916,15 +1461,12 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1916
1461
|
if (this.isProtocolV2ReconnectIdentityError(error)) {
|
|
1917
1462
|
throw error;
|
|
1918
1463
|
}
|
|
1919
|
-
if (
|
|
1920
|
-
throw error;
|
|
1921
|
-
}
|
|
1922
|
-
if (
|
|
1923
|
-
error instanceof HardwareError &&
|
|
1924
|
-
error.params?.firmwareUpdateCode === 'FirmwareInstallStatusUnavailable'
|
|
1925
|
-
) {
|
|
1464
|
+
if (error instanceof HardwareError && error.errorCode === HardwareErrorCode.FirmwareError) {
|
|
1926
1465
|
throw error;
|
|
1927
1466
|
}
|
|
1467
|
+
shouldReconnect = true;
|
|
1468
|
+
deviceInfo = undefined;
|
|
1469
|
+
resetMissingTargetStatusGrace();
|
|
1928
1470
|
Log.log('Protocol V2 firmware install device readiness probe failed: ', error);
|
|
1929
1471
|
}
|
|
1930
1472
|
await wait(1000);
|
|
@@ -1932,17 +1474,24 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1932
1474
|
|
|
1933
1475
|
throw ERRORS.TypedError(
|
|
1934
1476
|
HardwareErrorCode.RuntimeError,
|
|
1935
|
-
`Protocol V2 firmware update status
|
|
1477
|
+
`Protocol V2 firmware update status not complete within ${
|
|
1478
|
+
PROTOCOL_V2_INSTALL_TIMEOUT / 1000
|
|
1479
|
+
}s: ${this.normalizeErrorMessage(lastError)}`
|
|
1936
1480
|
);
|
|
1937
1481
|
}
|
|
1938
1482
|
|
|
1939
1483
|
private async exitProtocolV2BootloaderToNormal() {
|
|
1940
1484
|
await this.reconnectProtocolV2Device();
|
|
1941
|
-
await this.verifyProtocolV2ReconnectIdentity();
|
|
1942
|
-
|
|
1943
|
-
|
|
1485
|
+
const deviceInfo = await this.verifyProtocolV2ReconnectIdentity();
|
|
1486
|
+
try {
|
|
1487
|
+
if (await this.probeProtocolV2NormalMode(deviceInfo)) {
|
|
1488
|
+
Log.log('[FirmwareUpdateV4] device already returned to App mode; skip Normal reboot');
|
|
1489
|
+
return;
|
|
1490
|
+
}
|
|
1491
|
+
} catch (error) {
|
|
1492
|
+
Log.log('[FirmwareUpdateV4] unable to confirm App mode before Normal reboot: ', error);
|
|
1493
|
+
}
|
|
1944
1494
|
await this.protocolV2Reboot(DeviceRebootType.Normal);
|
|
1945
|
-
this.protocolV2ExecutionInLoader = false;
|
|
1946
1495
|
}
|
|
1947
1496
|
|
|
1948
1497
|
private async probeProtocolV2NormalMode(deviceInfo: ProtocolV2DeviceInfo) {
|
|
@@ -1958,10 +1507,13 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1958
1507
|
PROTOCOL_V2_FINAL_RECONNECT_TIMEOUT
|
|
1959
1508
|
);
|
|
1960
1509
|
|
|
1510
|
+
return this.getProtocolV2VersionResult(features);
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
private getProtocolV2VersionResult(features: Features) {
|
|
1961
1514
|
const bootloaderVersion = getDeviceBootloaderVersion(features).join('.');
|
|
1962
1515
|
const bleVersion = getDeviceBLEFirmwareVersion(features).join('.');
|
|
1963
1516
|
const firmwareVersion = getDeviceFirmwareVersion(features).join('.');
|
|
1964
|
-
this.protocolV2LatestFinalFeatures = features;
|
|
1965
1517
|
if (firmwareVersion === '0.0.0') {
|
|
1966
1518
|
Log.warn(
|
|
1967
1519
|
'Protocol V2 firmware update finished but app firmware version is still 0.0.0. This is allowed for Pro2 debug BLE-only update flows.'
|
|
@@ -1975,27 +1527,20 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1975
1527
|
};
|
|
1976
1528
|
}
|
|
1977
1529
|
|
|
1978
|
-
private async completeProtocolV2FinalVerification() {
|
|
1979
|
-
if (this.protocolV2ExecutionInLoader || this.isProtocolV2BootloaderMode()) {
|
|
1980
|
-
await this.exitProtocolV2BootloaderToNormal();
|
|
1981
|
-
}
|
|
1982
|
-
const versions = await this.waitForProtocolV2FinalFeatures();
|
|
1983
|
-
this.assertExpectedProtocolV2Versions();
|
|
1984
|
-
this.postTipMessage(FirmwareUpdateTipMessage.FirmwareUpdateCompleted);
|
|
1985
|
-
DevicePool.resetState();
|
|
1986
|
-
return versions;
|
|
1987
|
-
}
|
|
1988
|
-
|
|
1989
1530
|
private async waitForProtocolV2ReconnectAndFeatures(timeout: number) {
|
|
1990
1531
|
const startTime = Date.now();
|
|
1991
1532
|
let lastError: unknown;
|
|
1533
|
+
let shouldReconnect = true;
|
|
1992
1534
|
|
|
1993
1535
|
while (Date.now() - startTime < timeout) {
|
|
1994
1536
|
try {
|
|
1995
|
-
|
|
1537
|
+
if (shouldReconnect) {
|
|
1538
|
+
await this.reconnectProtocolV2Device();
|
|
1539
|
+
shouldReconnect = false;
|
|
1540
|
+
}
|
|
1996
1541
|
const deviceInfo = await requestProtocolV2DeviceInfo({
|
|
1997
1542
|
commands: this.device.getCommands(),
|
|
1998
|
-
timeoutMs:
|
|
1543
|
+
timeoutMs: this.getProtocolV2DeviceInfoTimeout(),
|
|
1999
1544
|
// Completion needs target versions only; keep scope aligned with the request.
|
|
2000
1545
|
request: PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST,
|
|
2001
1546
|
});
|
|
@@ -2004,21 +1549,22 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2004
1549
|
deviceInfo,
|
|
2005
1550
|
PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT
|
|
2006
1551
|
);
|
|
2007
|
-
if (features.mode
|
|
2008
|
-
|
|
2009
|
-
HardwareErrorCode.DeviceNotFound,
|
|
2010
|
-
'Protocol V2 device is still in bootloader mode'
|
|
2011
|
-
);
|
|
1552
|
+
if (features.mode === 'normal' && !features.bootloaderMode) {
|
|
1553
|
+
return features;
|
|
2012
1554
|
}
|
|
2013
|
-
|
|
1555
|
+
lastError = ERRORS.TypedError(
|
|
1556
|
+
HardwareErrorCode.DeviceNotFound,
|
|
1557
|
+
'Protocol V2 device is still in bootloader mode'
|
|
1558
|
+
);
|
|
2014
1559
|
} catch (error) {
|
|
2015
1560
|
if (this.isProtocolV2ReconnectIdentityError(error)) {
|
|
2016
1561
|
throw error;
|
|
2017
1562
|
}
|
|
1563
|
+
shouldReconnect = true;
|
|
2018
1564
|
lastError = error;
|
|
2019
1565
|
Log.log('Protocol V2 normal mode not ready, polling Ping: ', error);
|
|
2020
|
-
await wait(1000);
|
|
2021
1566
|
}
|
|
1567
|
+
await wait(1000);
|
|
2022
1568
|
}
|
|
2023
1569
|
|
|
2024
1570
|
throw ERRORS.TypedError(
|
|
@@ -2042,9 +1588,13 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2042
1588
|
DataManager.isBrowserWebUsb(DataManager.getSettings('env')) &&
|
|
2043
1589
|
devicesDescriptor.length === 1
|
|
2044
1590
|
) {
|
|
1591
|
+
const descriptor = devicesDescriptor[0];
|
|
1592
|
+
if (!this.protocolV2ExpectedSerialNumber && descriptor.path !== this.protocolV2ExpectedPath) {
|
|
1593
|
+
throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound);
|
|
1594
|
+
}
|
|
2045
1595
|
this.device.updateDescriptor(
|
|
2046
1596
|
{
|
|
2047
|
-
...
|
|
1597
|
+
...descriptor,
|
|
2048
1598
|
protocolType: PROTOCOL_V2_CONNECT_PROTOCOL,
|
|
2049
1599
|
},
|
|
2050
1600
|
true
|
|
@@ -2055,20 +1605,29 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2055
1605
|
return;
|
|
2056
1606
|
}
|
|
2057
1607
|
|
|
2058
|
-
//
|
|
2059
|
-
//
|
|
1608
|
+
// Reinitialize every USB descriptor so DeviceInfo can provide the physical serial.
|
|
1609
|
+
// Enumeration order is not stable and must never decide the reconnect target.
|
|
2060
1610
|
const { deviceList } = await DevicePool.getDevices(devicesDescriptor, undefined, {
|
|
2061
1611
|
connectProtocol: PROTOCOL_V2_CONNECT_PROTOCOL,
|
|
2062
1612
|
});
|
|
2063
|
-
|
|
1613
|
+
const expectedSerialNumber = this.protocolV2ExpectedSerialNumber?.trim();
|
|
1614
|
+
const expectedPath = this.protocolV2ExpectedPath?.trim();
|
|
1615
|
+
const reconnectDevice = deviceList.find(candidate => {
|
|
1616
|
+
const candidateSerialNumber = candidate.getCurrentSerialNo?.().trim();
|
|
1617
|
+
if (expectedSerialNumber && candidateSerialNumber) {
|
|
1618
|
+
return candidateSerialNumber === expectedSerialNumber;
|
|
1619
|
+
}
|
|
1620
|
+
return !!expectedPath && candidate.getConnectId() === expectedPath;
|
|
1621
|
+
});
|
|
1622
|
+
if (!reconnectDevice) {
|
|
2064
1623
|
throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound);
|
|
2065
1624
|
}
|
|
2066
1625
|
|
|
2067
1626
|
Log.debug(
|
|
2068
|
-
'Protocol V2 firmware reconnect using
|
|
2069
|
-
|
|
1627
|
+
'Protocol V2 firmware reconnect using matched device:',
|
|
1628
|
+
reconnectDevice.getConnectId()
|
|
2070
1629
|
);
|
|
2071
|
-
this.device.updateFromCache(
|
|
1630
|
+
this.device.updateFromCache(reconnectDevice);
|
|
2072
1631
|
await this.ensureProtocolV2DeviceAcquired();
|
|
2073
1632
|
this.device.commands.disposed = false;
|
|
2074
1633
|
this.device.getCommands().mainId = this.device.mainId ?? '';
|
|
@@ -2089,38 +1648,106 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2089
1648
|
}
|
|
2090
1649
|
|
|
2091
1650
|
private isProtocolV2ReconnectIdentityError(error: unknown) {
|
|
2092
|
-
|
|
1651
|
+
// 序列号在 Loader 刚启动时可能暂时不可用,BLE descriptor 也没有稳定的
|
|
1652
|
+
// path。此时应继续轮询,只有明确读到不同序列号时才立即终止更新。
|
|
1653
|
+
return this.normalizeErrorMessage(error).includes(
|
|
1654
|
+
'Protocol V2 reconnect physical identity mismatch'
|
|
1655
|
+
);
|
|
2093
1656
|
}
|
|
2094
1657
|
|
|
2095
|
-
private async
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
total_size: totalFileSize,
|
|
2109
|
-
data: chunk,
|
|
2110
|
-
},
|
|
2111
|
-
overwrite,
|
|
2112
|
-
append: false,
|
|
2113
|
-
ui_percentage: progress ?? undefined,
|
|
2114
|
-
});
|
|
2115
|
-
if (writeRes.type !== 'FilesystemFile') {
|
|
2116
|
-
if ((writeRes as any).type === 'CallMethodError') {
|
|
2117
|
-
if (((writeRes as any).message.error ?? '').indexOf(SESSION_ERROR) > -1) {
|
|
2118
|
-
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, SESSION_ERROR);
|
|
1658
|
+
private async protocolV2CommonUpdateProcess(params: ProtocolV2FileTransferParams) {
|
|
1659
|
+
let lastError: unknown;
|
|
1660
|
+
for (let attempt = 1; attempt <= PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT; attempt += 1) {
|
|
1661
|
+
try {
|
|
1662
|
+
return await this.protocolV2WriteWholeFile(params);
|
|
1663
|
+
} catch (error) {
|
|
1664
|
+
lastError = error;
|
|
1665
|
+
Log.error(
|
|
1666
|
+
`Protocol V2 file transfer failed path=${params.filePath} attempt=${attempt}/${PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT}; restarting from offset 0`,
|
|
1667
|
+
error
|
|
1668
|
+
);
|
|
1669
|
+
if (attempt === PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT) {
|
|
1670
|
+
break;
|
|
2119
1671
|
}
|
|
1672
|
+
await this.recoverProtocolV2FileTransfer();
|
|
2120
1673
|
}
|
|
2121
|
-
throw ERRORS.TypedError(HardwareErrorCode.EmmcFileWriteFirmwareError, 'transfer data error');
|
|
2122
1674
|
}
|
|
2123
|
-
|
|
1675
|
+
|
|
1676
|
+
throw ERRORS.TypedError(
|
|
1677
|
+
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
1678
|
+
`transfer data error: ${getProtocolV2UnknownErrorText(lastError)}`
|
|
1679
|
+
);
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
private async protocolV2WriteWholeFile({
|
|
1683
|
+
payload,
|
|
1684
|
+
filePath,
|
|
1685
|
+
processedSize,
|
|
1686
|
+
totalSize,
|
|
1687
|
+
onTransferredBytes,
|
|
1688
|
+
}: ProtocolV2FileTransferParams) {
|
|
1689
|
+
const chunkSize = this.getProtocolV2FirmwareChunkSize();
|
|
1690
|
+
const getUploadProgress = (fileOffset: number) => {
|
|
1691
|
+
if (totalSize !== undefined && processedSize !== undefined) {
|
|
1692
|
+
return Math.min(Math.ceil(((processedSize + fileOffset) / totalSize) * 100), 99);
|
|
1693
|
+
}
|
|
1694
|
+
return Math.min(Math.ceil((fileOffset / payload.byteLength) * 100), 99);
|
|
1695
|
+
};
|
|
1696
|
+
|
|
1697
|
+
try {
|
|
1698
|
+
await writeProtocolV2File({
|
|
1699
|
+
commands: this.device.getCommands(),
|
|
1700
|
+
path: filePath,
|
|
1701
|
+
data: payload,
|
|
1702
|
+
totalSize: payload.byteLength,
|
|
1703
|
+
chunkSize,
|
|
1704
|
+
overwrite: true,
|
|
1705
|
+
append: false,
|
|
1706
|
+
writeWithResponse: false,
|
|
1707
|
+
maxChunkRetries: 0,
|
|
1708
|
+
getUiPercentage: ({ offset, chunkLength }) =>
|
|
1709
|
+
getProtocolV2DeviceTransferProgress(
|
|
1710
|
+
(processedSize ?? 0) + offset,
|
|
1711
|
+
(processedSize ?? 0) + offset + chunkLength,
|
|
1712
|
+
totalSize ?? payload.byteLength
|
|
1713
|
+
),
|
|
1714
|
+
onProgress: progress => {
|
|
1715
|
+
const transferredBytes = (processedSize ?? 0) + progress.transferredBytes;
|
|
1716
|
+
onTransferredBytes?.(transferredBytes);
|
|
1717
|
+
this.postProgressMessage(getUploadProgress(progress.transferredBytes), 'transferData', {
|
|
1718
|
+
transferredBytes,
|
|
1719
|
+
totalBytes: totalSize ?? payload.byteLength,
|
|
1720
|
+
rateBytesPerSecond: progress.rateBytesPerSecond,
|
|
1721
|
+
elapsedMs: progress.elapsedMs,
|
|
1722
|
+
});
|
|
1723
|
+
},
|
|
1724
|
+
});
|
|
1725
|
+
} catch (error) {
|
|
1726
|
+
if (
|
|
1727
|
+
error instanceof HardwareError &&
|
|
1728
|
+
error.errorCode === HardwareErrorCode.RuntimeError &&
|
|
1729
|
+
error.message.includes('FilesystemFileWrite')
|
|
1730
|
+
) {
|
|
1731
|
+
throw ERRORS.TypedError(HardwareErrorCode.EmmcFileWriteFirmwareError, error.message);
|
|
1732
|
+
}
|
|
1733
|
+
throw error;
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
return totalSize !== undefined ? (processedSize ?? 0) + payload.byteLength : 0;
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1739
|
+
private getProtocolV2FirmwareTransferTransport() {
|
|
1740
|
+
const env = DataManager.getSettings('env');
|
|
1741
|
+
if (env && DataManager.isBleConnect(env)) {
|
|
1742
|
+
return 'BLE';
|
|
1743
|
+
}
|
|
1744
|
+
if (
|
|
1745
|
+
env &&
|
|
1746
|
+
(DataManager.isBrowserWebUsb(env) || DataManager.isDesktopWebUsb(env) || env === 'web')
|
|
1747
|
+
) {
|
|
1748
|
+
return 'WebUSB';
|
|
1749
|
+
}
|
|
1750
|
+
return env ?? 'unknown';
|
|
2124
1751
|
}
|
|
2125
1752
|
|
|
2126
1753
|
private async recoverProtocolV2FileTransfer() {
|