@onekeyfe/hd-core 1.2.0-alpha.41 → 1.2.0-alpha.43
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 +78 -0
- package/__tests__/DeviceEventRegistration.test.ts +6 -0
- package/__tests__/check-all-firmware-release-protocol-v2.test.ts +14 -1
- package/__tests__/connectSettings.test.ts +5 -47
- package/__tests__/deviceUploadNft.test.ts +272 -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__/logBlockEvent.test.ts +1 -1
- package/__tests__/open-wallet-session.test.ts +366 -31
- package/__tests__/pro2Nft.test.ts +108 -0
- package/__tests__/protocol-v2-resources.test.ts +204 -0
- package/__tests__/protocol-v2-ui-lifecycle.test.ts +91 -0
- package/__tests__/protocol-v2-unlock-policy.test.ts +196 -35
- package/__tests__/protocol-v2.test.ts +568 -957
- package/__tests__/protocolV2UiInteraction.test.ts +63 -0
- package/__tests__/public-pro2-api-boundary.test.ts +1 -0
- package/__tests__/uiProgressThrottle.test.ts +74 -0
- package/dist/api/BaseMethod.d.ts +3 -1
- package/dist/api/BaseMethod.d.ts.map +1 -1
- package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV2.d.ts +0 -11
- 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 +6 -25
- 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/allnetwork/AllNetworkGetAddressBase.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 +0 -8
- 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 +2 -0
- package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.d.ts.map +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/stellar/StellarSignTransaction.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/data-manager/DataManager.d.ts +3 -2
- package/dist/data-manager/DataManager.d.ts.map +1 -1
- package/dist/data-manager/connectSettings.d.ts.map +1 -1
- package/dist/device/Device.d.ts +24 -4
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts +4 -4
- 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-request.d.ts +28 -2
- package/dist/events/ui-request.d.ts.map +1 -1
- package/dist/index.d.ts +115 -184
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2051 -3349
- package/dist/inject.d.ts.map +1 -1
- package/dist/lowLevelInject.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/features.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 +4 -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 +1 -4
- package/dist/types/api/checkAllFirmwareRelease.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 +1 -4
- package/dist/types/api/export.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdate.d.ts +0 -69
- package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
- package/dist/types/api/index.d.ts +3 -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/settings.d.ts +12 -13
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts +2 -0
- package/dist/utils/deviceFeaturesUtils.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 +40 -53
- package/src/api/FirmwareUpdateV2.ts +120 -298
- package/src/api/FirmwareUpdateV3.ts +57 -263
- package/src/api/FirmwareUpdateV4.ts +307 -912
- package/src/api/GetPassphraseState.ts +7 -1
- package/src/api/OpenWalletSession.ts +54 -17
- package/src/api/allnetwork/AllNetworkGetAddressBase.ts +32 -11
- package/src/api/device/DeviceUnlock.ts +1 -1
- package/src/api/device/DeviceUpdateBootloader.ts +6 -122
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +0 -57
- package/src/api/firmware/updateBootloader.ts +4 -19
- package/src/api/firmware/uploadFirmware.ts +22 -140
- package/src/api/helpers/protocolV2FileWrite.ts +11 -4
- package/src/api/index.ts +1 -0
- package/src/api/protocol-v2/DeviceUploadNft.ts +189 -0
- package/src/api/protocol-v2/ProtocolInfoRequest.ts +3 -1
- package/src/core/deviceEventRegistration.ts +4 -0
- package/src/core/index.ts +136 -77
- package/src/data/messages/messages-protocol-v2.json +68 -0
- package/src/data-manager/DataManager.ts +63 -38
- package/src/data-manager/connectSettings.ts +0 -11
- package/src/device/Device.ts +203 -21
- package/src/events/device.ts +4 -0
- package/src/events/logBlockEvent.ts +1 -0
- package/src/events/ui-request.ts +33 -2
- package/src/index.ts +0 -5
- package/src/inject.ts +3 -22
- package/src/lowLevelInject.ts +1 -5
- package/src/protocols/protocol-v2/features.ts +9 -2
- package/src/protocols/protocol-v2/resources.ts +233 -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 +124 -0
- package/src/protocols/protocol-v2/walletSession.ts +154 -30
- package/src/topLevelInject.ts +1 -5
- package/src/types/api/checkAllFirmwareRelease.ts +1 -4
- package/src/types/api/deviceUpdateBootloader.ts +0 -6
- package/src/types/api/export.ts +0 -18
- package/src/types/api/firmwareUpdate.ts +0 -76
- package/src/types/api/index.ts +2 -14
- package/src/types/api/protocolV2.ts +13 -0
- package/src/types/settings.ts +26 -13
- package/src/utils/deviceFeaturesUtils.ts +4 -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__/firmware-update/check-all-firmware-release.test.ts +0 -175
- 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-capabilities.test.ts +0 -13
- package/__tests__/firmware-update/firmware-update-plan.test.ts +0 -593
- package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +0 -213
- 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 -30
- 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 -745
- package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +0 -392
- 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,7 +2,6 @@ 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';
|
|
@@ -19,7 +18,6 @@ import {
|
|
|
19
18
|
getLogger,
|
|
20
19
|
} from '../utils';
|
|
21
20
|
import { getSysResourceBinary } from './firmware/getBinary';
|
|
22
|
-
import { normalizeFirmwarePreparationError } from './firmware/FirmwarePreparationError';
|
|
23
21
|
import { DataManager } from '../data-manager';
|
|
24
22
|
import { FirmwareUpdateBaseMethod } from './firmware/FirmwareUpdateBaseMethod';
|
|
25
23
|
import { DevicePool } from '../device/DevicePool';
|
|
@@ -28,32 +26,29 @@ import {
|
|
|
28
26
|
ProtocolV2FirmwareTargetType,
|
|
29
27
|
} from '../protocols/protocol-v2';
|
|
30
28
|
import { requestProtocolV2DeviceInfo } from '../protocols/protocol-v2/features';
|
|
29
|
+
import {
|
|
30
|
+
PROTOCOL_V2_RESOURCE_DEVICE_PATHS,
|
|
31
|
+
buildProtocolV2ResourceUpdatePlan,
|
|
32
|
+
isProtocolV2ResourceFileValid,
|
|
33
|
+
requestProtocolV2ResourceInventory,
|
|
34
|
+
} from '../protocols/protocol-v2/resources';
|
|
31
35
|
import {
|
|
32
36
|
getProtocolV2UnknownErrorText,
|
|
33
37
|
isProtocolV2DeviceDisconnectedError,
|
|
34
38
|
} 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
39
|
|
|
42
|
-
import type {
|
|
43
|
-
FirmwareArtifactReference,
|
|
44
|
-
FirmwareUpdateV4Params,
|
|
45
|
-
FirmwareUpdateV4Target,
|
|
46
|
-
} from '../types/api/firmwareUpdate';
|
|
40
|
+
import type { FirmwareUpdateV4Params, FirmwareUpdateV4Target } from '../types/api/firmwareUpdate';
|
|
47
41
|
import type { EFirmwareType } from '@onekeyfe/hd-shared';
|
|
48
42
|
import type { ProtocolV2DeviceInfo } from '@onekeyfe/hd-transport';
|
|
43
|
+
import type { PROTO } from '../constants';
|
|
49
44
|
import type { TypedResponseMessage } from '../device/DeviceCommands';
|
|
50
45
|
import type {
|
|
51
46
|
Features,
|
|
52
47
|
IFirmwareReleaseInfo,
|
|
53
48
|
IProtocolV2FirmwareComponent,
|
|
49
|
+
IProtocolV2Resource,
|
|
54
50
|
IVersionArray,
|
|
55
51
|
} from '../types';
|
|
56
|
-
import type { FirmwareByteSource } from './firmware/FirmwareArtifactSource';
|
|
57
52
|
|
|
58
53
|
const Log = getLogger(LoggerNames.Method);
|
|
59
54
|
|
|
@@ -97,6 +92,11 @@ const getProtocolV2DeviceTransferProgress = (
|
|
|
97
92
|
return Math.min(Math.max(Math.ceil((bytesAfterChunk / totalBytes) * 100), 1), 99);
|
|
98
93
|
};
|
|
99
94
|
|
|
95
|
+
const formatProtocolV2TransferSpeed = (bytes: number, elapsedMs: number) => {
|
|
96
|
+
const safeElapsedMs = Math.max(elapsedMs, 1);
|
|
97
|
+
return (bytes / 1024 / (safeElapsedMs / 1000)).toFixed(2);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
100
|
type ProtocolV2FirmwareUpdateStatusTarget = {
|
|
101
101
|
target_id: number | string;
|
|
102
102
|
status?: number | string;
|
|
@@ -110,16 +110,22 @@ type ProtocolV2TargetBinary = { fileName: string; binary: ArrayBuffer; targetId:
|
|
|
110
110
|
type ProtocolV2InstallItem = ProtocolV2TargetBinary & {
|
|
111
111
|
kind: ProtocolV2RemoteComponentTarget['kind'];
|
|
112
112
|
};
|
|
113
|
+
type ProtocolV2InstallTarget = ProtocolV2InstallItem & {
|
|
114
|
+
path: string;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
type ProtocolV2FileTransferParams = PROTO.FirmwareUpload & {
|
|
118
|
+
filePath: string;
|
|
119
|
+
processedSize?: number;
|
|
120
|
+
totalSize?: number;
|
|
121
|
+
onTransferredBytes?: (transferredBytes: number) => void;
|
|
122
|
+
};
|
|
123
|
+
|
|
113
124
|
/** RESC bundle okpkg written independently to devicePath through FileWrite. */
|
|
114
125
|
type ProtocolV2ResourceBundleBinary = {
|
|
115
126
|
name: string;
|
|
116
127
|
binary: ArrayBuffer;
|
|
117
128
|
devicePath: string;
|
|
118
|
-
/** Download URL for remote-config mode; omitted in manual mode. */
|
|
119
|
-
url?: string;
|
|
120
|
-
version?: IVersionArray;
|
|
121
|
-
payloadHash?: string;
|
|
122
|
-
headerHash?: string;
|
|
123
129
|
};
|
|
124
130
|
|
|
125
131
|
type ProtocolV2RemoteComponentBinary = ProtocolV2RemoteComponentTarget & {
|
|
@@ -132,35 +138,6 @@ type ProtocolV2RemoteComponentTarget = {
|
|
|
132
138
|
kind: 'bootloader' | 'firmware';
|
|
133
139
|
};
|
|
134
140
|
|
|
135
|
-
type ProtocolV2InstallSource = {
|
|
136
|
-
fileName: string;
|
|
137
|
-
source: FirmwareByteSource;
|
|
138
|
-
targetId: number;
|
|
139
|
-
kind: ProtocolV2RemoteComponentTarget['kind'];
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
type ProtocolV2ResourceBundleSource = {
|
|
143
|
-
name: string;
|
|
144
|
-
source: FirmwareByteSource;
|
|
145
|
-
devicePath: string;
|
|
146
|
-
version?: IVersionArray;
|
|
147
|
-
payloadHash?: string;
|
|
148
|
-
headerHash?: string;
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
type ProtocolV2ExecutionPhaseKind =
|
|
152
|
-
| 'resource-sync'
|
|
153
|
-
| 'bootloader-install'
|
|
154
|
-
| 'bootloader-verify'
|
|
155
|
-
| 'component-install'
|
|
156
|
-
| 'final-verify';
|
|
157
|
-
|
|
158
|
-
type ProtocolV2ExecutionPhase = {
|
|
159
|
-
kind: ProtocolV2ExecutionPhaseKind;
|
|
160
|
-
installSources: ProtocolV2InstallSource[];
|
|
161
|
-
resourceSources: ProtocolV2ResourceBundleSource[];
|
|
162
|
-
};
|
|
163
|
-
|
|
164
141
|
type ProtocolV2OkppHeader = {
|
|
165
142
|
type: string;
|
|
166
143
|
version: IVersionArray;
|
|
@@ -300,18 +277,6 @@ const normalizeProtocolV2TargetStatus = (
|
|
|
300
277
|
|
|
301
278
|
const normalizeProtocolV2Hex = (value?: string) => value?.replace(/^0x/i, '').toLowerCase();
|
|
302
279
|
|
|
303
|
-
const versionArrayToNumber = (version?: IVersionArray) => {
|
|
304
|
-
if (!version) return undefined;
|
|
305
|
-
return version[0] * 0x10000 + version[1] * 0x100 + version[2];
|
|
306
|
-
};
|
|
307
|
-
|
|
308
|
-
const compareProtocolV2Versions = (current?: IVersionArray, target?: IVersionArray) => {
|
|
309
|
-
const currentNumber = versionArrayToNumber(current);
|
|
310
|
-
const targetNumber = versionArrayToNumber(target);
|
|
311
|
-
if (currentNumber === undefined || targetNumber === undefined) return undefined;
|
|
312
|
-
return currentNumber - targetNumber;
|
|
313
|
-
};
|
|
314
|
-
|
|
315
280
|
const bytesToHex = (bytes: Uint8Array) =>
|
|
316
281
|
Array.from(bytes)
|
|
317
282
|
.map(byte => byte.toString(16).padStart(2, '0'))
|
|
@@ -433,16 +398,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
433
398
|
return ['V2'] as const;
|
|
434
399
|
}
|
|
435
400
|
|
|
436
|
-
private protocolV2PreparedSources: FirmwareByteSource[] = [];
|
|
437
|
-
|
|
438
|
-
private protocolV2ExecutionInLoader = false;
|
|
439
|
-
|
|
440
|
-
private protocolV2CompletedTargetVersions = new Map<number, number>();
|
|
441
|
-
|
|
442
|
-
private protocolV2LatestFinalFeatures?: Features;
|
|
443
|
-
|
|
444
|
-
private protocolV2FinalStatusVerified = false;
|
|
445
|
-
|
|
446
401
|
init() {
|
|
447
402
|
this.allowDeviceMode = [UI_REQUEST.BOOTLOADER, UI_REQUEST.NOT_INITIALIZE];
|
|
448
403
|
this.requireDeviceMode = [];
|
|
@@ -480,62 +435,10 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
480
435
|
{ name: 'firmwareType', type: 'string' },
|
|
481
436
|
{ name: 'targetsToUpdate', type: 'array', allowEmpty: true },
|
|
482
437
|
{ name: 'platform', type: 'string' },
|
|
483
|
-
{ name: 'expectedDeviceId', type: 'string' },
|
|
484
438
|
{ name: 'resourceBundleFiles', type: 'array', allowEmpty: true },
|
|
485
439
|
]);
|
|
486
|
-
if (
|
|
487
|
-
payload.expectedDeviceId !== undefined &&
|
|
488
|
-
(payload.expectedDeviceId.length === 0 || payload.expectedDeviceId.length > 160)
|
|
489
|
-
) {
|
|
490
|
-
throw ERRORS.TypedError(
|
|
491
|
-
HardwareErrorCode.CallMethodInvalidParameter,
|
|
492
|
-
'Protocol V2 expected device identity is invalid'
|
|
493
|
-
);
|
|
494
|
-
}
|
|
495
|
-
const hostBinding =
|
|
496
|
-
payload.preparedPlan || payload.componentArtifacts || payload.resourceBundleArtifacts
|
|
497
|
-
? resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration)
|
|
498
|
-
: undefined;
|
|
499
|
-
if (hostBinding) {
|
|
500
|
-
assertFirmwareUpdatePreparedPlanBinding({
|
|
501
|
-
preparedPlan: payload.preparedPlan,
|
|
502
|
-
executor: 'v4',
|
|
503
|
-
platform: payload.platform,
|
|
504
|
-
scopeTargets: [
|
|
505
|
-
'boot',
|
|
506
|
-
'app_v1',
|
|
507
|
-
'app_v2',
|
|
508
|
-
'coprocessor',
|
|
509
|
-
'resource',
|
|
510
|
-
'se01',
|
|
511
|
-
'se02',
|
|
512
|
-
'se03',
|
|
513
|
-
'se04',
|
|
514
|
-
],
|
|
515
|
-
bindings: [
|
|
516
|
-
...Object.entries(payload.componentArtifacts ?? {}).flatMap(([target, artifact]) =>
|
|
517
|
-
artifact
|
|
518
|
-
? [
|
|
519
|
-
{
|
|
520
|
-
target: target as Exclude<FirmwareUpdateV4Target, 'resource'>,
|
|
521
|
-
artifact,
|
|
522
|
-
},
|
|
523
|
-
]
|
|
524
|
-
: []
|
|
525
|
-
),
|
|
526
|
-
...(payload.resourceBundleArtifacts ?? []).map(
|
|
527
|
-
(entry: { name: string; artifact: FirmwareArtifactReference }) => ({
|
|
528
|
-
target: 'resource' as const,
|
|
529
|
-
logicalName: entry.name,
|
|
530
|
-
artifact: entry.artifact,
|
|
531
|
-
})
|
|
532
|
-
),
|
|
533
|
-
],
|
|
534
|
-
});
|
|
535
|
-
}
|
|
536
440
|
|
|
537
441
|
this.params = {
|
|
538
|
-
preparedPlan: payload.preparedPlan,
|
|
539
442
|
chunkSize: payload.chunkSize,
|
|
540
443
|
forcedUpdateRes: payload.forcedUpdateRes,
|
|
541
444
|
bootloaderBinary: payload.bootloaderBinary,
|
|
@@ -550,25 +453,17 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
550
453
|
resourceBundleFiles: payload.resourceBundleFiles,
|
|
551
454
|
firmwareType: payload.firmwareType,
|
|
552
455
|
targetsToUpdate: payload.targetsToUpdate,
|
|
553
|
-
expectedTargetVersions: payload.expectedTargetVersions,
|
|
554
456
|
platform: payload.platform,
|
|
555
|
-
expectedDeviceId: payload.expectedDeviceId,
|
|
556
|
-
artifactReader: hostBinding?.artifactReader ?? payload.artifactReader,
|
|
557
|
-
componentArtifacts: payload.componentArtifacts,
|
|
558
|
-
resourceBundleArtifacts: payload.resourceBundleArtifacts,
|
|
559
457
|
};
|
|
560
458
|
}
|
|
561
459
|
|
|
562
|
-
private getProtocolV2FirmwareChunkSize(
|
|
460
|
+
private getProtocolV2FirmwareChunkSize() {
|
|
563
461
|
const payloadChunkSize = Number(this.params?.chunkSize);
|
|
564
462
|
const env = DataManager.getSettings('env');
|
|
565
463
|
const isBle = this.params?.platform === 'native' || (env && DataManager.isBleConnect(env));
|
|
566
464
|
let maxChunkSize = PROTOCOL_V2_WEBUSB_FILE_CHUNK_SIZE;
|
|
567
465
|
if (isBle) {
|
|
568
|
-
maxChunkSize =
|
|
569
|
-
direction === 'read'
|
|
570
|
-
? PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE
|
|
571
|
-
: PROTOCOL_V2_BLE_FILE_CHUNK_SIZE;
|
|
466
|
+
maxChunkSize = PROTOCOL_V2_BLE_FILE_CHUNK_SIZE;
|
|
572
467
|
}
|
|
573
468
|
if (!Number.isFinite(payloadChunkSize) || payloadChunkSize <= 0) {
|
|
574
469
|
return maxChunkSize;
|
|
@@ -589,14 +484,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
589
484
|
const deviceFeatures = await this.getProtocolV2DeviceFeatures();
|
|
590
485
|
const deviceFirmwareType = getFirmwareType(deviceFeatures);
|
|
591
486
|
const firmwareType = this.params.firmwareType ?? deviceFirmwareType;
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
(this.params.componentArtifacts && Object.keys(this.params.componentArtifacts).length > 0) ||
|
|
596
|
-
this.params.resourceBundleArtifacts?.length
|
|
597
|
-
) {
|
|
598
|
-
return this.runProtocolV2PreparedArtifacts(deviceFeatures, firmwareType);
|
|
599
|
-
}
|
|
487
|
+
const resourceRecoveryMode = Boolean(
|
|
488
|
+
this.isProtocolV2BootloaderMode() || this.isProtocolV2RomloaderMode()
|
|
489
|
+
);
|
|
600
490
|
|
|
601
491
|
let fwBinaryMap: ProtocolV2TargetBinary[] = [];
|
|
602
492
|
let bootloaderBinary: ArrayBuffer | null = null;
|
|
@@ -606,19 +496,15 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
606
496
|
this.postTipMessage(FirmwareUpdateTipMessage.StartDownloadFirmware);
|
|
607
497
|
fwBinaryMap = this.collectExplicitTargetBinaries();
|
|
608
498
|
bootloaderBinary = this.prepareBootloaderBinary();
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
619
|
-
}
|
|
620
|
-
);
|
|
621
|
-
}
|
|
499
|
+
const needsRemoteFirmware = !this.hasExplicitProtocolV2Payload(fwBinaryMap);
|
|
500
|
+
const needsRemoteResources =
|
|
501
|
+
!this.params.resourceBundleFiles?.length &&
|
|
502
|
+
!!this.params.targetsToUpdate?.includes('resource');
|
|
503
|
+
if (needsRemoteFirmware || needsRemoteResources) {
|
|
504
|
+
// Remote updates must use a freshly fetched config before any reboot or file write.
|
|
505
|
+
await DataManager.forceReloadData();
|
|
506
|
+
}
|
|
507
|
+
if (needsRemoteFirmware) {
|
|
622
508
|
const remoteBinaries = await this.prepareRemoteProtocolV2Binaries(
|
|
623
509
|
firmwareType,
|
|
624
510
|
deviceFeatures
|
|
@@ -627,308 +513,39 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
627
513
|
fwBinaryMap = remoteBinaries.fwBinaryMap;
|
|
628
514
|
installItems = remoteBinaries.installItems;
|
|
629
515
|
}
|
|
630
|
-
|
|
631
|
-
firmwareType,
|
|
632
|
-
deviceFeatures
|
|
633
|
-
);
|
|
634
|
-
resourceBundles = resourceDescriptors?.length
|
|
635
|
-
? await this.materializeProtocolV2ResourceBundles(resourceDescriptors)
|
|
636
|
-
: undefined;
|
|
516
|
+
resourceBundles = await this.prepareProtocolV2ResourceBundles(resourceRecoveryMode);
|
|
637
517
|
this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
638
518
|
} catch (err) {
|
|
639
|
-
throw
|
|
519
|
+
throw ERRORS.TypedError(HardwareErrorCode.FirmwareUpdateDownloadFailed, err.message ?? err);
|
|
640
520
|
}
|
|
641
521
|
|
|
642
522
|
if (!bootloaderBinary && fwBinaryMap.length === 0 && !resourceBundles?.length) {
|
|
523
|
+
if (resourceBundles !== undefined) {
|
|
524
|
+
this.postTipMessage(FirmwareUpdateTipMessage.FirmwareUpdateCompleted);
|
|
525
|
+
return this.getProtocolV2VersionResult(deviceFeatures);
|
|
526
|
+
}
|
|
643
527
|
throw ERRORS.TypedError(
|
|
644
528
|
HardwareErrorCode.FirmwareUpdateDownloadFailed,
|
|
645
529
|
'No firmware to update'
|
|
646
530
|
);
|
|
647
531
|
}
|
|
648
532
|
|
|
649
|
-
|
|
533
|
+
await this.enterProtocolV2BootloaderMode();
|
|
534
|
+
|
|
535
|
+
await this.executeProtocolV2Update({
|
|
650
536
|
fwBinaryMap,
|
|
651
537
|
bootloaderBinary,
|
|
652
538
|
...(installItems ? { installItems } : undefined),
|
|
653
539
|
...(resourceBundles?.length ? { resourceBundles } : undefined),
|
|
654
540
|
});
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
private async openProtocolV2PreparedSource(artifact: FirmwareArtifactReference) {
|
|
658
|
-
const source = await openFirmwareByteSource({
|
|
659
|
-
artifact,
|
|
660
|
-
reader: this.params.artifactReader,
|
|
661
|
-
});
|
|
662
|
-
if (!source) {
|
|
663
|
-
throw ERRORS.TypedError(
|
|
664
|
-
HardwareErrorCode.RuntimeError,
|
|
665
|
-
`Firmware artifact ${artifact.artifactRef} is not prepared`,
|
|
666
|
-
{
|
|
667
|
-
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
668
|
-
}
|
|
669
|
-
);
|
|
670
|
-
}
|
|
671
|
-
this.protocolV2PreparedSources.push(source);
|
|
672
|
-
return source;
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
private async openProtocolV2MemorySource(binary: ArrayBuffer) {
|
|
676
|
-
const source = await openFirmwareByteSource({ binary });
|
|
677
|
-
if (!source) {
|
|
678
|
-
throw ERRORS.TypedError(
|
|
679
|
-
HardwareErrorCode.RuntimeError,
|
|
680
|
-
'Protocol V2 firmware binary is empty'
|
|
681
|
-
);
|
|
682
|
-
}
|
|
683
|
-
this.protocolV2PreparedSources.push(source);
|
|
684
|
-
return source;
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
private async closeProtocolV2PreparedSources() {
|
|
688
|
-
const sources = this.protocolV2PreparedSources.splice(0);
|
|
689
|
-
await Promise.all(sources.map(source => source.close().catch(() => undefined)));
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
private async prepareProtocolV2InstallSources(
|
|
693
|
-
firmwareType: EFirmwareType,
|
|
694
|
-
features: Features
|
|
695
|
-
): Promise<ProtocolV2InstallSource[]> {
|
|
696
|
-
const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
|
|
697
|
-
if (!release) {
|
|
698
|
-
throw ERRORS.TypedError(
|
|
699
|
-
HardwareErrorCode.RuntimeError,
|
|
700
|
-
'Protocol V2 firmware release is unavailable'
|
|
701
|
-
);
|
|
702
|
-
}
|
|
703
|
-
const componentArtifacts = this.params.componentArtifacts ?? {};
|
|
704
|
-
const requestedTargets = new Set(
|
|
705
|
-
this.params.targetsToUpdate ?? (Object.keys(componentArtifacts) as FirmwareUpdateV4Target[])
|
|
706
|
-
);
|
|
707
|
-
const installSources: ProtocolV2InstallSource[] = [];
|
|
708
|
-
const preparedTargets = new Set<FirmwareUpdateV4Target>();
|
|
709
|
-
|
|
710
|
-
for (const [key, component] of this.getRemoteComponentEntries(release)) {
|
|
711
|
-
const target = this.getRemoteComponentTarget(key, component);
|
|
712
|
-
const updateTarget = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(target.targetId);
|
|
713
|
-
if (updateTarget && updateTarget !== 'resource' && requestedTargets.has(updateTarget)) {
|
|
714
|
-
const artifact = componentArtifacts[updateTarget];
|
|
715
|
-
if (!artifact) {
|
|
716
|
-
throw ERRORS.TypedError(
|
|
717
|
-
HardwareErrorCode.RuntimeError,
|
|
718
|
-
`Protocol V2 ${updateTarget} artifact is not prepared`,
|
|
719
|
-
{
|
|
720
|
-
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
721
|
-
artifactName: updateTarget,
|
|
722
|
-
}
|
|
723
|
-
);
|
|
724
|
-
}
|
|
725
|
-
installSources.push({
|
|
726
|
-
...target,
|
|
727
|
-
source: await this.openProtocolV2PreparedSource(artifact),
|
|
728
|
-
});
|
|
729
|
-
preparedTargets.add(updateTarget);
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
const unknownTarget = Array.from(requestedTargets).find(
|
|
734
|
-
target => target !== 'resource' && !preparedTargets.has(target)
|
|
735
|
-
);
|
|
736
|
-
if (unknownTarget) {
|
|
737
|
-
throw ERRORS.TypedError(
|
|
738
|
-
HardwareErrorCode.RuntimeError,
|
|
739
|
-
`Protocol V2 release does not contain requested target ${unknownTarget}`
|
|
740
|
-
);
|
|
741
|
-
}
|
|
742
|
-
return installSources;
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
private async prepareProtocolV2ResourceSources(
|
|
746
|
-
firmwareType: EFirmwareType,
|
|
747
|
-
features: Features
|
|
748
|
-
): Promise<ProtocolV2ResourceBundleSource[]> {
|
|
749
|
-
const preparedArtifacts = this.params.resourceBundleArtifacts ?? [];
|
|
750
|
-
const resourceRequested =
|
|
751
|
-
this.params.targetsToUpdate?.includes('resource') || preparedArtifacts.length > 0;
|
|
752
|
-
if (!resourceRequested) {
|
|
753
|
-
return [];
|
|
754
|
-
}
|
|
755
|
-
const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
|
|
756
|
-
const descriptors = release?.resourceBundles ?? [];
|
|
757
|
-
const artifactByName = new Map(
|
|
758
|
-
preparedArtifacts.map(item => [item.name, item.artifact] as const)
|
|
759
|
-
);
|
|
760
|
-
const sources: ProtocolV2ResourceBundleSource[] = [];
|
|
761
|
-
for (const descriptor of descriptors) {
|
|
762
|
-
const artifact = artifactByName.get(descriptor.name);
|
|
763
|
-
if (!artifact) {
|
|
764
|
-
throw ERRORS.TypedError(
|
|
765
|
-
HardwareErrorCode.RuntimeError,
|
|
766
|
-
`Protocol V2 resource bundle ${descriptor.name} is not prepared`,
|
|
767
|
-
{
|
|
768
|
-
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
769
|
-
artifactName: descriptor.name,
|
|
770
|
-
}
|
|
771
|
-
);
|
|
772
|
-
}
|
|
773
|
-
const devicePath = validateProtocolV2FilesystemPath(
|
|
774
|
-
descriptor.devicePath,
|
|
775
|
-
'resourceBundles[].devicePath'
|
|
776
|
-
);
|
|
777
|
-
sources.push({
|
|
778
|
-
name: descriptor.name,
|
|
779
|
-
source: await this.openProtocolV2PreparedSource(artifact),
|
|
780
|
-
devicePath,
|
|
781
|
-
version: descriptor.version,
|
|
782
|
-
payloadHash: descriptor.payloadHash,
|
|
783
|
-
headerHash: descriptor.headerHash,
|
|
784
|
-
});
|
|
785
|
-
artifactByName.delete(descriptor.name);
|
|
786
|
-
}
|
|
787
|
-
if (artifactByName.size > 0) {
|
|
788
|
-
throw ERRORS.TypedError(
|
|
789
|
-
HardwareErrorCode.RuntimeError,
|
|
790
|
-
`Protocol V2 release does not contain resource bundle ${artifactByName.keys().next().value}`
|
|
791
|
-
);
|
|
792
|
-
}
|
|
793
|
-
return sources;
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
private async runProtocolV2PreparedArtifacts(features: Features, firmwareType: EFirmwareType) {
|
|
797
|
-
try {
|
|
798
|
-
this.postTipMessage(FirmwareUpdateTipMessage.StartDownloadFirmware);
|
|
799
|
-
const installSources = await this.prepareProtocolV2InstallSources(firmwareType, features);
|
|
800
|
-
const resourceSources = await this.prepareProtocolV2ResourceSources(firmwareType, features);
|
|
801
|
-
if (installSources.length === 0 && resourceSources.length === 0) {
|
|
802
|
-
throw ERRORS.TypedError(
|
|
803
|
-
HardwareErrorCode.FirmwareUpdateDownloadFailed,
|
|
804
|
-
'No firmware to update'
|
|
805
|
-
);
|
|
806
|
-
}
|
|
807
|
-
this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
808
|
-
|
|
809
|
-
return await this.executeProtocolV2SourceUpdate({
|
|
810
|
-
installSources,
|
|
811
|
-
resourceSources,
|
|
812
|
-
});
|
|
813
|
-
} finally {
|
|
814
|
-
await this.closeProtocolV2PreparedSources();
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
private validateExpectedTargetVersions() {
|
|
819
|
-
const expected = this.params.expectedTargetVersions;
|
|
820
|
-
if (expected === undefined) return;
|
|
821
|
-
if (typeof expected !== 'object' || expected === null || Array.isArray(expected)) {
|
|
822
|
-
throw ERRORS.TypedError(
|
|
823
|
-
HardwareErrorCode.RuntimeError,
|
|
824
|
-
'Protocol V2 expected target versions are invalid'
|
|
825
|
-
);
|
|
826
|
-
}
|
|
827
|
-
for (const [target, version] of Object.entries(expected)) {
|
|
828
|
-
if (
|
|
829
|
-
!Array.from(PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.values()).includes(
|
|
830
|
-
target as FirmwareUpdateV4Target
|
|
831
|
-
) ||
|
|
832
|
-
typeof version !== 'string' ||
|
|
833
|
-
!/^\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.-]+)?$/u.test(version) ||
|
|
834
|
-
version.length > 64
|
|
835
|
-
) {
|
|
836
|
-
throw ERRORS.TypedError(
|
|
837
|
-
HardwareErrorCode.RuntimeError,
|
|
838
|
-
'Protocol V2 expected target version is invalid'
|
|
839
|
-
);
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
541
|
|
|
844
|
-
|
|
845
|
-
const target = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(targetId);
|
|
846
|
-
const version = target ? this.params?.expectedTargetVersions?.[target] : undefined;
|
|
847
|
-
if (!version) return undefined;
|
|
848
|
-
const parts = version.split(/[+-]/u, 1)[0].split('.').map(Number);
|
|
849
|
-
if (
|
|
850
|
-
parts.length !== 3 ||
|
|
851
|
-
parts.some(part => !Number.isSafeInteger(part) || part < 0 || part > 0xff)
|
|
852
|
-
) {
|
|
853
|
-
return undefined;
|
|
854
|
-
}
|
|
855
|
-
return parts[0] * 0x10000 + parts[1] * 0x100 + parts[2];
|
|
856
|
-
}
|
|
542
|
+
await this.exitProtocolV2BootloaderToNormal();
|
|
857
543
|
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
const features = this.protocolV2LatestFinalFeatures;
|
|
862
|
-
const requestedTargets = this.getProtocolV2RequestedTargets();
|
|
863
|
-
const applicationTargets = requestedTargets.filter(
|
|
864
|
-
target => target === 'app_v1' || target === 'app_v2'
|
|
865
|
-
);
|
|
866
|
-
const visibleVersions: Partial<Record<FirmwareUpdateV4Target, string>> = {
|
|
867
|
-
boot: features ? getDeviceBootloaderVersion(features).join('.') : undefined,
|
|
868
|
-
coprocessor: features ? getDeviceBLEFirmwareVersion(features).join('.') : undefined,
|
|
869
|
-
se01: features?.se01Version ?? undefined,
|
|
870
|
-
se02: features?.se02Version ?? undefined,
|
|
871
|
-
se03: features?.se03Version ?? undefined,
|
|
872
|
-
se04: features?.se04Version ?? undefined,
|
|
873
|
-
};
|
|
874
|
-
if (features && applicationTargets.length === 1) {
|
|
875
|
-
visibleVersions[applicationTargets[0]] = getDeviceFirmwareVersion(features).join('.');
|
|
876
|
-
}
|
|
877
|
-
const expectedEntries = (
|
|
878
|
-
Object.entries(expected) as Array<[FirmwareUpdateV4Target, string]>
|
|
879
|
-
).filter(([target]) => !targets || targets.includes(target));
|
|
880
|
-
for (const [target, expectedVersion] of expectedEntries) {
|
|
881
|
-
const targetId = Array.from(PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.entries()).find(
|
|
882
|
-
([, mappedTarget]) => mappedTarget === target
|
|
883
|
-
)?.[0];
|
|
884
|
-
const statusVersion =
|
|
885
|
-
targetId === undefined ? undefined : this.protocolV2CompletedTargetVersions.get(targetId);
|
|
886
|
-
const observedVersion =
|
|
887
|
-
statusVersion === undefined
|
|
888
|
-
? visibleVersions[target]
|
|
889
|
-
: `${Math.floor(statusVersion / 0x10000) % 0x100}.${
|
|
890
|
-
Math.floor(statusVersion / 0x100) % 0x100
|
|
891
|
-
}.${statusVersion % 0x100}`;
|
|
892
|
-
if (!observedVersion) {
|
|
893
|
-
if (this.protocolV2FinalStatusVerified) {
|
|
894
|
-
Log.warn(
|
|
895
|
-
`Protocol V2 target ${target} has no observable final version; completed target status is authoritative`
|
|
896
|
-
);
|
|
897
|
-
} else {
|
|
898
|
-
throw ERRORS.TypedError(
|
|
899
|
-
HardwareErrorCode.FirmwareVerificationFailed,
|
|
900
|
-
`Protocol V2 target ${target} has no observable final version after status fallback`
|
|
901
|
-
);
|
|
902
|
-
}
|
|
903
|
-
} else if (observedVersion !== expectedVersion) {
|
|
904
|
-
throw ERRORS.TypedError(
|
|
905
|
-
HardwareErrorCode.FirmwareVerificationFailed,
|
|
906
|
-
`Protocol V2 target ${target} reached ${observedVersion}, expected ${expectedVersion}`
|
|
907
|
-
);
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
}
|
|
544
|
+
const versions = await this.waitForProtocolV2FinalFeatures();
|
|
545
|
+
this.postTipMessage(FirmwareUpdateTipMessage.FirmwareUpdateCompleted);
|
|
546
|
+
DevicePool.resetState();
|
|
911
547
|
|
|
912
|
-
|
|
913
|
-
if (this.params.targetsToUpdate?.length) {
|
|
914
|
-
return [...new Set(this.params.targetsToUpdate)];
|
|
915
|
-
}
|
|
916
|
-
const targets = new Set<FirmwareUpdateV4Target>();
|
|
917
|
-
Object.keys(this.params.componentArtifacts ?? {}).forEach(target =>
|
|
918
|
-
targets.add(target as FirmwareUpdateV4Target)
|
|
919
|
-
);
|
|
920
|
-
if (this.params.resourceBundleArtifacts?.length || this.params.resourceBundleFiles?.length) {
|
|
921
|
-
targets.add('resource');
|
|
922
|
-
}
|
|
923
|
-
if (this.params.bootloaderBinary) targets.add('boot');
|
|
924
|
-
if (this.params.applicationP1Binary) targets.add('app_v1');
|
|
925
|
-
if (this.params.applicationP2Binary) targets.add('app_v2');
|
|
926
|
-
if (this.params.coprocessorBinary) targets.add('coprocessor');
|
|
927
|
-
if (this.params.se01Binary) targets.add('se01');
|
|
928
|
-
if (this.params.se02Binary) targets.add('se02');
|
|
929
|
-
if (this.params.se03Binary) targets.add('se03');
|
|
930
|
-
if (this.params.se04Binary) targets.add('se04');
|
|
931
|
-
return Array.from(targets);
|
|
548
|
+
return versions;
|
|
932
549
|
}
|
|
933
550
|
|
|
934
551
|
private async getProtocolV2DeviceFeatures(): Promise<Features> {
|
|
@@ -964,16 +581,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
964
581
|
private async captureProtocolV2PhysicalIdentity() {
|
|
965
582
|
const deviceInfo = await this.requestProtocolV2PhysicalIdentity();
|
|
966
583
|
const serialNumber = this.getProtocolV2SerialNumber(deviceInfo);
|
|
967
|
-
|
|
968
|
-
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
969
|
-
preparedPlan: this.params.preparedPlan,
|
|
970
|
-
deviceIdentity: serialNumber,
|
|
971
|
-
});
|
|
972
|
-
} else if (this.params?.expectedDeviceId) {
|
|
973
|
-
assertProtocolV2ReconnectIdentity(this.params?.expectedDeviceId, serialNumber);
|
|
974
|
-
} else {
|
|
975
|
-
assertProtocolV2ReconnectIdentity(serialNumber, serialNumber);
|
|
976
|
-
}
|
|
584
|
+
assertProtocolV2ReconnectIdentity(serialNumber, serialNumber);
|
|
977
585
|
this.protocolV2ExpectedSerialNumber = serialNumber;
|
|
978
586
|
}
|
|
979
587
|
|
|
@@ -1052,57 +660,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1052
660
|
return target;
|
|
1053
661
|
}
|
|
1054
662
|
|
|
1055
|
-
private getProtocolV2ResourceFilePath(path: string) {
|
|
1056
|
-
if (path.startsWith('vol')) return path;
|
|
1057
|
-
if (path.startsWith('/')) return `vol0:${path}`;
|
|
1058
|
-
return `vol0:/${path}`;
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
private async readProtocolV2DeviceFileHeader(path: string) {
|
|
1062
|
-
const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
|
|
1063
|
-
const filePath = this.getProtocolV2ResourceFilePath(path);
|
|
1064
|
-
const pathInfoRes = await typedCall('FilesystemPathInfoQuery', 'FilesystemPathInfo', {
|
|
1065
|
-
path: filePath,
|
|
1066
|
-
});
|
|
1067
|
-
const fileSize = toProtocolV2FiniteNumber(pathInfoRes.message?.size);
|
|
1068
|
-
if (
|
|
1069
|
-
!pathInfoRes.message?.exist ||
|
|
1070
|
-
pathInfoRes.message?.directory ||
|
|
1071
|
-
fileSize === undefined ||
|
|
1072
|
-
fileSize < PROTOCOL_V2_OKPP_HEADER_SIZE
|
|
1073
|
-
) {
|
|
1074
|
-
return null;
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
const chunkSize = this.getProtocolV2FirmwareChunkSize('read');
|
|
1078
|
-
const chunks: Uint8Array[] = [];
|
|
1079
|
-
let offset = 0;
|
|
1080
|
-
while (offset < PROTOCOL_V2_OKPP_HEADER_SIZE) {
|
|
1081
|
-
const readLen = Math.min(chunkSize, PROTOCOL_V2_OKPP_HEADER_SIZE - offset);
|
|
1082
|
-
const res = await typedCall('FilesystemFileRead', 'FilesystemFile', {
|
|
1083
|
-
file: {
|
|
1084
|
-
path: filePath,
|
|
1085
|
-
offset,
|
|
1086
|
-
total_size: 0,
|
|
1087
|
-
},
|
|
1088
|
-
chunk_len: readLen,
|
|
1089
|
-
ui_percentage: undefined,
|
|
1090
|
-
});
|
|
1091
|
-
const data = toProtocolV2Bytes(res.message?.data);
|
|
1092
|
-
if (data.byteLength === 0) return null;
|
|
1093
|
-
chunks.push(data);
|
|
1094
|
-
offset += data.byteLength;
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
const headerBytes = new Uint8Array(offset);
|
|
1098
|
-
let cursor = 0;
|
|
1099
|
-
chunks.forEach(chunk => {
|
|
1100
|
-
headerBytes.set(chunk, cursor);
|
|
1101
|
-
cursor += chunk.byteLength;
|
|
1102
|
-
});
|
|
1103
|
-
return parseProtocolV2OkppHeader(headerBytes);
|
|
1104
|
-
}
|
|
1105
|
-
|
|
1106
663
|
private async downloadRemoteProtocolV2Component(
|
|
1107
664
|
key: string,
|
|
1108
665
|
component: IProtocolV2FirmwareComponent
|
|
@@ -1191,23 +748,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1191
748
|
};
|
|
1192
749
|
}
|
|
1193
750
|
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
/**
|
|
1199
|
-
* Prepare the RESC bundle list.
|
|
1200
|
-
*
|
|
1201
|
-
* Two modes, matching FirmwareUpdateV3 binary-versus-version behavior:
|
|
1202
|
-
* - resourceBundleFiles supplied: use binaries directly without version checks.
|
|
1203
|
-
* - Otherwise load release.resourceBundles from remote config.json; the binaries
|
|
1204
|
-
* are prefetched before reboot and synchronization skips matching device headers.
|
|
1205
|
-
*/
|
|
1206
|
-
private prepareProtocolV2ResourceBundles(
|
|
1207
|
-
firmwareType: EFirmwareType,
|
|
1208
|
-
features: Features
|
|
1209
|
-
): ProtocolV2ResourceBundleBinary[] | undefined {
|
|
1210
|
-
// Manual binaries are installed directly without comparison.
|
|
751
|
+
private async prepareProtocolV2ResourceBundles(
|
|
752
|
+
recoveryMode: boolean
|
|
753
|
+
): Promise<ProtocolV2ResourceBundleBinary[] | undefined> {
|
|
1211
754
|
if (this.params.resourceBundleFiles?.length) {
|
|
1212
755
|
return this.params.resourceBundleFiles.map((file, index) => {
|
|
1213
756
|
const devicePath = validateProtocolV2FilesystemPath(
|
|
@@ -1226,120 +769,88 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1226
769
|
return undefined;
|
|
1227
770
|
}
|
|
1228
771
|
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
return release.resourceBundles.map((bundle, index) => ({
|
|
1234
|
-
name: bundle.name,
|
|
1235
|
-
binary: new ArrayBuffer(0),
|
|
1236
|
-
devicePath: validateProtocolV2FilesystemPath(
|
|
1237
|
-
bundle.devicePath,
|
|
1238
|
-
`resourceBundles[${index}].devicePath`
|
|
1239
|
-
),
|
|
1240
|
-
url: bundle.url,
|
|
1241
|
-
version: bundle.version,
|
|
1242
|
-
payloadHash: bundle.payloadHash,
|
|
1243
|
-
headerHash: bundle.headerHash,
|
|
1244
|
-
})) as ProtocolV2ResourceBundleBinary[];
|
|
1245
|
-
}
|
|
772
|
+
const resources = DataManager.getProtocolV2Resources();
|
|
773
|
+
if (!resources?.length) {
|
|
774
|
+
throw new Error('Missing Pro2 stable resource configuration');
|
|
775
|
+
}
|
|
1246
776
|
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
1269
|
-
artifactName: bundle.name,
|
|
1270
|
-
}
|
|
1271
|
-
);
|
|
1272
|
-
}
|
|
1273
|
-
Log.log(`[FirmwareUpdateV4] downloading remote RESC bundle ${bundle.name}`);
|
|
1274
|
-
({ binary } = await getSysResourceBinary(bundle.url));
|
|
1275
|
-
bundle.binary = binary;
|
|
1276
|
-
downloadedFromRemote = true;
|
|
1277
|
-
}
|
|
1278
|
-
if (bundle.binary.byteLength === 0) {
|
|
1279
|
-
throw ERRORS.TypedError(
|
|
1280
|
-
HardwareErrorCode.FirmwareUpdateDownloadFailed,
|
|
1281
|
-
`Protocol V2 RESC bundle is empty: ${bundle.name}`
|
|
1282
|
-
);
|
|
1283
|
-
}
|
|
1284
|
-
if (downloadedFromRemote) {
|
|
1285
|
-
const header = parseProtocolV2OkppHeader(toProtocolV2Bytes(binary));
|
|
1286
|
-
if (!header || header.type !== 'RESC') {
|
|
1287
|
-
throw new Error(`Invalid Protocol V2 RESC bundle header: ${bundle.name}`);
|
|
1288
|
-
}
|
|
1289
|
-
if (bundle.version && compareProtocolV2Versions(header.version, bundle.version) !== 0) {
|
|
1290
|
-
throw new Error(`Protocol V2 RESC bundle version mismatch: ${bundle.name}`);
|
|
1291
|
-
}
|
|
1292
|
-
const expectedPayloadHash = normalizeProtocolV2Hex(bundle.payloadHash);
|
|
1293
|
-
if (expectedPayloadHash && header.payloadHash !== expectedPayloadHash) {
|
|
1294
|
-
throw new Error(`Protocol V2 RESC bundle payload hash mismatch: ${bundle.name}`);
|
|
1295
|
-
}
|
|
1296
|
-
const expectedHeaderHash = normalizeProtocolV2Hex(bundle.headerHash);
|
|
1297
|
-
if (expectedHeaderHash && header.headerHash !== expectedHeaderHash) {
|
|
1298
|
-
throw new Error(`Protocol V2 RESC bundle header hash mismatch: ${bundle.name}`);
|
|
1299
|
-
}
|
|
1300
|
-
}
|
|
777
|
+
const inventory = recoveryMode
|
|
778
|
+
? undefined
|
|
779
|
+
: await requestProtocolV2ResourceInventory({
|
|
780
|
+
commands: this.device.getCommands(),
|
|
781
|
+
timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
|
|
782
|
+
});
|
|
783
|
+
const plan = buildProtocolV2ResourceUpdatePlan({
|
|
784
|
+
resources,
|
|
785
|
+
inventory,
|
|
786
|
+
mode: recoveryMode ? 'bootloader-recovery' : 'application',
|
|
787
|
+
forced: this.params.forcedUpdateRes,
|
|
788
|
+
});
|
|
789
|
+
Log.log(
|
|
790
|
+
`[FirmwareUpdateV4] Pro2 resource plan mode=${
|
|
791
|
+
recoveryMode ? 'bootloader-recovery' : 'application'
|
|
792
|
+
} status=${plan.status} count=${plan.resources.length}`
|
|
793
|
+
);
|
|
794
|
+
|
|
795
|
+
const bundles: ProtocolV2ResourceBundleBinary[] = [];
|
|
796
|
+
for (const resource of plan.resources) {
|
|
797
|
+
bundles.push(await this.downloadProtocolV2Resource(resource));
|
|
1301
798
|
}
|
|
1302
799
|
return bundles;
|
|
1303
800
|
}
|
|
1304
801
|
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
802
|
+
private async downloadProtocolV2Resource(
|
|
803
|
+
resource: IProtocolV2Resource
|
|
804
|
+
): Promise<ProtocolV2ResourceBundleBinary> {
|
|
805
|
+
Log.log(`[FirmwareUpdateV4] downloading Pro2 resource ${resource.type}`);
|
|
806
|
+
const { binary } = await getSysResourceBinary(resource.url);
|
|
807
|
+
if (!isProtocolV2ResourceFileValid(binary, resource)) {
|
|
808
|
+
throw new Error(`Pro2 resource file verification failed: ${resource.type}`);
|
|
809
|
+
}
|
|
810
|
+
return {
|
|
811
|
+
name: `${resource.type}.okpkg`,
|
|
812
|
+
binary,
|
|
813
|
+
devicePath: PROTOCOL_V2_RESOURCE_DEVICE_PATHS[resource.type],
|
|
814
|
+
};
|
|
815
|
+
}
|
|
1316
816
|
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
817
|
+
private async syncProtocolV2ResourceBundles(
|
|
818
|
+
bundles: ProtocolV2ResourceBundleBinary[],
|
|
819
|
+
firmwareSize: number
|
|
820
|
+
): Promise<{ processedSize: number; totalSize: number }> {
|
|
821
|
+
const transferStartTime = Date.now();
|
|
822
|
+
const transferTransport = this.getProtocolV2FirmwareTransferTransport();
|
|
1320
823
|
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
824
|
+
// Write directly to the device through FileWrite.
|
|
825
|
+
let totalSize = 0;
|
|
826
|
+
for (const b of bundles) totalSize += b.binary.byteLength;
|
|
827
|
+
|
|
828
|
+
const transferTotalSize = totalSize + firmwareSize;
|
|
829
|
+
let processedSize = 0;
|
|
830
|
+
for (const bundle of bundles) {
|
|
831
|
+
Log.log(
|
|
832
|
+
`[FirmwareUpdateV4] syncing RESC bundle ${bundle.name} -> ${bundle.devicePath} bytes=${bundle.binary.byteLength}`
|
|
833
|
+
);
|
|
834
|
+
processedSize = await this.protocolV2CommonUpdateProcess({
|
|
835
|
+
payload: bundle.binary,
|
|
836
|
+
filePath: bundle.devicePath,
|
|
837
|
+
processedSize,
|
|
838
|
+
totalSize: transferTotalSize,
|
|
839
|
+
});
|
|
1337
840
|
}
|
|
841
|
+
|
|
842
|
+
const elapsedMs = Date.now() - transferStartTime;
|
|
843
|
+
Log.log(
|
|
844
|
+
`[FirmwareUpdateV4] RESC bundle sync finished transport=${transferTransport} bytes=${totalSize} elapsed=${(
|
|
845
|
+
elapsedMs / 1000
|
|
846
|
+
).toFixed(2)}s speed=${formatProtocolV2TransferSpeed(totalSize, elapsedMs)} KB/s`
|
|
847
|
+
);
|
|
848
|
+
return { processedSize, totalSize: transferTotalSize };
|
|
1338
849
|
}
|
|
1339
850
|
|
|
1340
851
|
private isProtocolV2BootloaderMode() {
|
|
1341
852
|
if (typeof this.device.isBootloader === 'function') {
|
|
1342
|
-
return this.device.isBootloader();
|
|
853
|
+
return !!this.device.isBootloader();
|
|
1343
854
|
}
|
|
1344
855
|
return (
|
|
1345
856
|
this.device.features?.mode === 'bootloader' ||
|
|
@@ -1362,12 +873,10 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1362
873
|
// It rejects DeviceRebootType.Bootloader, so reuse the current connection.
|
|
1363
874
|
if (this.isProtocolV2RomloaderMode()) {
|
|
1364
875
|
Log.debug('Protocol V2 device is in romloader mode; start firmware update directly');
|
|
1365
|
-
this.protocolV2ExecutionInLoader = true;
|
|
1366
876
|
return false;
|
|
1367
877
|
}
|
|
1368
878
|
if (this.isProtocolV2BootloaderMode()) {
|
|
1369
879
|
Log.debug('Protocol V2 device is already in bootloader mode, skip reboot');
|
|
1370
|
-
this.protocolV2ExecutionInLoader = true;
|
|
1371
880
|
return false;
|
|
1372
881
|
}
|
|
1373
882
|
|
|
@@ -1377,7 +886,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1377
886
|
this.postTipMessage(FirmwareUpdateTipMessage.GoToBootloaderSuccess);
|
|
1378
887
|
await wait(1000);
|
|
1379
888
|
await this.waitForProtocolV2BootloaderMode();
|
|
1380
|
-
this.protocolV2ExecutionInLoader = true;
|
|
1381
889
|
return true;
|
|
1382
890
|
} catch (error) {
|
|
1383
891
|
if (error instanceof HardwareError) {
|
|
@@ -1467,98 +975,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1467
975
|
return entries;
|
|
1468
976
|
}
|
|
1469
977
|
|
|
1470
|
-
private buildProtocolV2ExecutionPhases({
|
|
1471
|
-
installSources,
|
|
1472
|
-
resourceSources,
|
|
1473
|
-
}: {
|
|
1474
|
-
installSources: ProtocolV2InstallSource[];
|
|
1475
|
-
resourceSources: ProtocolV2ResourceBundleSource[];
|
|
1476
|
-
}): ProtocolV2ExecutionPhase[] {
|
|
1477
|
-
const phases: ProtocolV2ExecutionPhase[] = [];
|
|
1478
|
-
if (resourceSources.length > 0) {
|
|
1479
|
-
phases.push({
|
|
1480
|
-
kind: 'resource-sync',
|
|
1481
|
-
installSources: [],
|
|
1482
|
-
resourceSources,
|
|
1483
|
-
});
|
|
1484
|
-
}
|
|
1485
|
-
const bootloaderSources = installSources.filter(source => source.kind === 'bootloader');
|
|
1486
|
-
if (bootloaderSources.length > 0) {
|
|
1487
|
-
phases.push(
|
|
1488
|
-
{
|
|
1489
|
-
kind: 'bootloader-install',
|
|
1490
|
-
installSources: bootloaderSources,
|
|
1491
|
-
resourceSources: [],
|
|
1492
|
-
},
|
|
1493
|
-
{
|
|
1494
|
-
kind: 'bootloader-verify',
|
|
1495
|
-
installSources: [],
|
|
1496
|
-
resourceSources: [],
|
|
1497
|
-
}
|
|
1498
|
-
);
|
|
1499
|
-
}
|
|
1500
|
-
const componentSources = installSources.filter(source => source.kind !== 'bootloader');
|
|
1501
|
-
if (componentSources.length > 0) {
|
|
1502
|
-
phases.push({
|
|
1503
|
-
kind: 'component-install',
|
|
1504
|
-
installSources: componentSources,
|
|
1505
|
-
resourceSources: [],
|
|
1506
|
-
});
|
|
1507
|
-
}
|
|
1508
|
-
phases.push({
|
|
1509
|
-
kind: 'final-verify',
|
|
1510
|
-
installSources: [],
|
|
1511
|
-
resourceSources: [],
|
|
1512
|
-
});
|
|
1513
|
-
return phases;
|
|
1514
|
-
}
|
|
1515
|
-
|
|
1516
|
-
private async executeProtocolV2Phases({
|
|
1517
|
-
installSources,
|
|
1518
|
-
resourceSources,
|
|
1519
|
-
}: {
|
|
1520
|
-
installSources: ProtocolV2InstallSource[];
|
|
1521
|
-
resourceSources: ProtocolV2ResourceBundleSource[];
|
|
1522
|
-
}) {
|
|
1523
|
-
const phases = this.buildProtocolV2ExecutionPhases({
|
|
1524
|
-
installSources,
|
|
1525
|
-
resourceSources,
|
|
1526
|
-
});
|
|
1527
|
-
for (const phase of phases) {
|
|
1528
|
-
if (phase.kind === 'final-verify') {
|
|
1529
|
-
return this.completeProtocolV2FinalVerification();
|
|
1530
|
-
}
|
|
1531
|
-
if (phase.kind === 'resource-sync') {
|
|
1532
|
-
await this.enterProtocolV2BootloaderMode();
|
|
1533
|
-
await this.executeProtocolV2TransferPhase(phase);
|
|
1534
|
-
} else if (phase.kind === 'bootloader-install' || phase.kind === 'component-install') {
|
|
1535
|
-
await this.enterProtocolV2BootloaderMode();
|
|
1536
|
-
await this.executeProtocolV2TransferPhase(phase);
|
|
1537
|
-
await this.exitProtocolV2BootloaderToNormal();
|
|
1538
|
-
} else if (phase.kind === 'bootloader-verify') {
|
|
1539
|
-
await this.waitForProtocolV2FinalFeatures();
|
|
1540
|
-
this.assertExpectedProtocolV2Versions(['boot']);
|
|
1541
|
-
}
|
|
1542
|
-
}
|
|
1543
|
-
throw ERRORS.TypedError(
|
|
1544
|
-
HardwareErrorCode.RuntimeError,
|
|
1545
|
-
'Protocol V2 execution has no final verification phase'
|
|
1546
|
-
);
|
|
1547
|
-
}
|
|
1548
|
-
|
|
1549
|
-
private async executeProtocolV2SourceUpdate({
|
|
1550
|
-
installSources,
|
|
1551
|
-
resourceSources,
|
|
1552
|
-
}: {
|
|
1553
|
-
installSources: ProtocolV2InstallSource[];
|
|
1554
|
-
resourceSources: ProtocolV2ResourceBundleSource[];
|
|
1555
|
-
}) {
|
|
1556
|
-
return this.executeProtocolV2Phases({
|
|
1557
|
-
installSources,
|
|
1558
|
-
resourceSources,
|
|
1559
|
-
});
|
|
1560
|
-
}
|
|
1561
|
-
|
|
1562
978
|
private async executeProtocolV2Update({
|
|
1563
979
|
fwBinaryMap,
|
|
1564
980
|
bootloaderBinary,
|
|
@@ -1570,158 +986,104 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1570
986
|
installItems?: ProtocolV2InstallItem[];
|
|
1571
987
|
resourceBundles?: ProtocolV2ResourceBundleBinary[];
|
|
1572
988
|
}) {
|
|
1573
|
-
const
|
|
989
|
+
const orderedInstallItems =
|
|
1574
990
|
installItems ??
|
|
1575
991
|
this.buildProtocolV2InstallItems({
|
|
1576
|
-
fwBinaryMap: fwBinaryMap ?? [],
|
|
1577
992
|
bootloaderBinary: bootloaderBinary ?? null,
|
|
993
|
+
fwBinaryMap: fwBinaryMap ?? [],
|
|
1578
994
|
});
|
|
1579
|
-
try {
|
|
1580
|
-
const installSources = await Promise.all(
|
|
1581
|
-
memoryInstallItems.map(async item => ({
|
|
1582
|
-
fileName: item.fileName,
|
|
1583
|
-
source: await this.openProtocolV2MemorySource(item.binary),
|
|
1584
|
-
targetId: item.targetId,
|
|
1585
|
-
kind: item.kind,
|
|
1586
|
-
}))
|
|
1587
|
-
);
|
|
1588
|
-
const resourceSources = await Promise.all(
|
|
1589
|
-
(resourceBundles ?? []).map(async bundle => ({
|
|
1590
|
-
name: bundle.name,
|
|
1591
|
-
source: await this.openProtocolV2MemorySource(bundle.binary),
|
|
1592
|
-
devicePath: bundle.devicePath,
|
|
1593
|
-
version: bundle.version,
|
|
1594
|
-
payloadHash: bundle.payloadHash,
|
|
1595
|
-
headerHash: bundle.headerHash,
|
|
1596
|
-
}))
|
|
1597
|
-
);
|
|
1598
|
-
return await this.executeProtocolV2Phases({
|
|
1599
|
-
installSources,
|
|
1600
|
-
resourceSources,
|
|
1601
|
-
});
|
|
1602
|
-
} finally {
|
|
1603
|
-
await this.closeProtocolV2PreparedSources();
|
|
1604
|
-
}
|
|
1605
|
-
}
|
|
1606
995
|
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
resourceSources,
|
|
1610
|
-
}: ProtocolV2ExecutionPhase) {
|
|
1611
|
-
let totalSize = installSources.reduce((total, item) => total + item.source.size, 0);
|
|
1612
|
-
const resourcesToSync: ProtocolV2ResourceBundleSource[] = [];
|
|
1613
|
-
for (const resource of resourceSources) {
|
|
1614
|
-
if (await this.isProtocolV2ResourceBundleUpToDate(resource)) {
|
|
1615
|
-
Log.log(`[FirmwareUpdateV4] skip RESC bundle ${resource.name}; already up to date`);
|
|
1616
|
-
} else {
|
|
1617
|
-
resourcesToSync.push(resource);
|
|
1618
|
-
totalSize += resource.source.size;
|
|
1619
|
-
}
|
|
1620
|
-
}
|
|
996
|
+
let firmwareSize = 0;
|
|
997
|
+
for (const item of orderedInstallItems) firmwareSize += item.binary.byteLength;
|
|
1621
998
|
|
|
1622
999
|
this.postTipMessage(FirmwareUpdateTipMessage.StartTransferData);
|
|
1000
|
+
|
|
1623
1001
|
let processedSize = 0;
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1002
|
+
let totalSize = firmwareSize;
|
|
1003
|
+
if (resourceBundles?.length) {
|
|
1004
|
+
const resourceTransfer = await this.syncProtocolV2ResourceBundles(
|
|
1005
|
+
resourceBundles,
|
|
1006
|
+
firmwareSize
|
|
1007
|
+
);
|
|
1008
|
+
processedSize = resourceTransfer.processedSize;
|
|
1009
|
+
totalSize = resourceTransfer.totalSize;
|
|
1632
1010
|
}
|
|
1633
1011
|
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
totalSize,
|
|
1642
|
-
});
|
|
1643
|
-
await this.verifyProtocolV2StagedFile(filePath, item.source.size);
|
|
1644
|
-
stagedInstallTargets.push({
|
|
1645
|
-
targetId: item.targetId,
|
|
1646
|
-
path: filePath,
|
|
1647
|
-
});
|
|
1012
|
+
// Skip staging and installation when the update contains RESC bundles only.
|
|
1013
|
+
if (orderedInstallItems.length === 0) {
|
|
1014
|
+
Log.log('[FirmwareUpdateV4] no firmware targets to install (RESC bundles only)');
|
|
1015
|
+
if (totalSize > 0) {
|
|
1016
|
+
this.postProgressMessage(100, 'transferData');
|
|
1017
|
+
}
|
|
1018
|
+
return;
|
|
1648
1019
|
}
|
|
1020
|
+
let transferredSize = processedSize;
|
|
1021
|
+
const transferStartTime = Date.now();
|
|
1022
|
+
const transferTransport = this.getProtocolV2FirmwareTransferTransport();
|
|
1023
|
+
const chunkSize = this.getProtocolV2FirmwareChunkSize();
|
|
1024
|
+
const onTransferredBytes = (bytes: number) => {
|
|
1025
|
+
transferredSize = bytes;
|
|
1026
|
+
};
|
|
1027
|
+
Log.log(
|
|
1028
|
+
`[FirmwareUpdateV4] transfer started transport=${transferTransport} total=${totalSize} bytes chunk=${chunkSize} bytes`
|
|
1029
|
+
);
|
|
1649
1030
|
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1031
|
+
const stagedInstallTargets: ProtocolV2InstallTarget[] = [];
|
|
1032
|
+
|
|
1033
|
+
try {
|
|
1034
|
+
for (const item of orderedInstallItems) {
|
|
1035
|
+
const filePath = this.getProtocolV2InstallItemStagingPath(item);
|
|
1036
|
+
Log.log(
|
|
1037
|
+
`[FirmwareUpdateV4] staging ${item.kind} via FilesystemFileWrite target=${item.targetId} path=${filePath} source=${item.fileName} bytes=${item.binary.byteLength}`
|
|
1038
|
+
);
|
|
1039
|
+
processedSize = await this.protocolV2CommonUpdateProcess({
|
|
1040
|
+
payload: item.binary,
|
|
1041
|
+
filePath,
|
|
1042
|
+
processedSize,
|
|
1043
|
+
totalSize,
|
|
1044
|
+
onTransferredBytes,
|
|
1045
|
+
});
|
|
1046
|
+
transferredSize = processedSize;
|
|
1047
|
+
await this.verifyProtocolV2StagedFile(filePath, item.binary.byteLength);
|
|
1048
|
+
|
|
1049
|
+
stagedInstallTargets.push({
|
|
1050
|
+
...item,
|
|
1051
|
+
path: filePath,
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
if (totalSize > 0) {
|
|
1056
|
+
this.postProgressMessage(100, 'transferData');
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
const elapsedMs = Date.now() - transferStartTime;
|
|
1060
|
+
Log.log(
|
|
1061
|
+
`[FirmwareUpdateV4] transfer finished transport=${transferTransport} bytes=${totalSize} elapsed=${(
|
|
1062
|
+
elapsedMs / 1000
|
|
1063
|
+
).toFixed(2)}s speed=${formatProtocolV2TransferSpeed(totalSize, elapsedMs)} KB/s`
|
|
1064
|
+
);
|
|
1065
|
+
} catch (error) {
|
|
1066
|
+
const elapsedMs = Date.now() - transferStartTime;
|
|
1067
|
+
Log.warn(
|
|
1068
|
+
`[FirmwareUpdateV4] transfer failed transport=${transferTransport} bytes=${transferredSize}/${totalSize} elapsed=${(
|
|
1069
|
+
elapsedMs / 1000
|
|
1070
|
+
).toFixed(2)}s speed=${formatProtocolV2TransferSpeed(transferredSize, elapsedMs)} KB/s`
|
|
1071
|
+
);
|
|
1072
|
+
throw error;
|
|
1655
1073
|
}
|
|
1656
1074
|
|
|
1657
1075
|
this.postTipMessage(FirmwareUpdateTipMessage.ConfirmOnDevice);
|
|
1658
|
-
|
|
1076
|
+
|
|
1077
|
+
const allTargets = stagedInstallTargets.map(item => ({
|
|
1659
1078
|
target_id: item.targetId,
|
|
1660
1079
|
path: item.path,
|
|
1661
1080
|
}));
|
|
1662
|
-
|
|
1663
|
-
await this.
|
|
1081
|
+
Log.log(`[FirmwareUpdateV4] DeviceFirmwareUpdateRequest targets=${JSON.stringify(allTargets)}`);
|
|
1082
|
+
await this.protocolV2StartFirmwareUpdate({ targets: allTargets });
|
|
1083
|
+
await this.waitForProtocolV2FirmwareUpdateComplete(allTargets);
|
|
1664
1084
|
}
|
|
1665
1085
|
|
|
1666
|
-
private
|
|
1667
|
-
source,
|
|
1668
|
-
filePath,
|
|
1669
|
-
processedSize,
|
|
1670
|
-
totalSize,
|
|
1671
|
-
}: {
|
|
1672
|
-
source: FirmwareByteSource;
|
|
1673
|
-
filePath: string;
|
|
1674
|
-
processedSize: number;
|
|
1675
|
-
totalSize: number;
|
|
1676
|
-
}) {
|
|
1677
|
-
let lastError: unknown;
|
|
1678
|
-
for (let attempt = 1; attempt <= PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT; attempt += 1) {
|
|
1679
|
-
try {
|
|
1680
|
-
await writeFirmwareByteSource({
|
|
1681
|
-
source,
|
|
1682
|
-
chunkSize: this.getProtocolV2FirmwareChunkSize(),
|
|
1683
|
-
write: async ({ data, sourceOffset, length, first }) => {
|
|
1684
|
-
const chunkEnd = sourceOffset + length;
|
|
1685
|
-
const progress = getProtocolV2DeviceTransferProgress(
|
|
1686
|
-
processedSize + sourceOffset,
|
|
1687
|
-
processedSize + chunkEnd,
|
|
1688
|
-
totalSize
|
|
1689
|
-
);
|
|
1690
|
-
const response = await this.fileWriteChunk(
|
|
1691
|
-
filePath,
|
|
1692
|
-
source.size,
|
|
1693
|
-
sourceOffset,
|
|
1694
|
-
data,
|
|
1695
|
-
first,
|
|
1696
|
-
progress
|
|
1697
|
-
);
|
|
1698
|
-
const rawProcessedByte = response.message.processed_byte;
|
|
1699
|
-
const nextOffset = rawProcessedByte === undefined ? chunkEnd : Number(rawProcessedByte);
|
|
1700
|
-
if (!Number.isFinite(nextOffset) || nextOffset !== chunkEnd) {
|
|
1701
|
-
throw ERRORS.TypedError(
|
|
1702
|
-
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
1703
|
-
`invalid processed_byte ${rawProcessedByte} for offset ${sourceOffset}`
|
|
1704
|
-
);
|
|
1705
|
-
}
|
|
1706
|
-
this.postProgressMessage(progress, 'transferData');
|
|
1707
|
-
return length;
|
|
1708
|
-
},
|
|
1709
|
-
});
|
|
1710
|
-
return processedSize + source.size;
|
|
1711
|
-
} catch (error) {
|
|
1712
|
-
lastError = error;
|
|
1713
|
-
if (attempt < PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT) {
|
|
1714
|
-
await this.recoverProtocolV2FileTransfer();
|
|
1715
|
-
}
|
|
1716
|
-
}
|
|
1717
|
-
}
|
|
1718
|
-
throw ERRORS.TypedError(
|
|
1719
|
-
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
1720
|
-
`transfer data error: ${getProtocolV2UnknownErrorText(lastError)}`
|
|
1721
|
-
);
|
|
1722
|
-
}
|
|
1723
|
-
|
|
1724
|
-
private getProtocolV2InstallItemStagingPath(item: Pick<ProtocolV2InstallItem, 'fileName'>) {
|
|
1086
|
+
private getProtocolV2InstallItemStagingPath(item: ProtocolV2InstallItem) {
|
|
1725
1087
|
return `${PROTOCOL_V2_FIRMWARE_STAGING_VOLUME}${item.fileName}`;
|
|
1726
1088
|
}
|
|
1727
1089
|
|
|
@@ -1740,8 +1102,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1740
1102
|
|
|
1741
1103
|
private assertProtocolV2TargetStatus(
|
|
1742
1104
|
statusTargets: ProtocolV2FirmwareUpdateStatusTarget[],
|
|
1743
|
-
expectedTargetIds: Set<number
|
|
1744
|
-
expectedPaths = new Map<number, string>()
|
|
1105
|
+
expectedTargetIds: Set<number>
|
|
1745
1106
|
) {
|
|
1746
1107
|
Log.log(
|
|
1747
1108
|
`[FirmwareUpdateV4] DeviceFirmwareUpdateStatus records=${JSON.stringify(statusTargets)}`
|
|
@@ -1772,54 +1133,17 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1772
1133
|
const matchingTargets = statusTargets.filter(target =>
|
|
1773
1134
|
expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1)
|
|
1774
1135
|
);
|
|
1775
|
-
const seenTargetIds = new Set<number>();
|
|
1776
|
-
for (const target of matchingTargets) {
|
|
1777
|
-
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
1778
|
-
if (
|
|
1779
|
-
targetId === undefined ||
|
|
1780
|
-
seenTargetIds.has(targetId) ||
|
|
1781
|
-
(target.path && expectedPaths.get(targetId) && target.path !== expectedPaths.get(targetId))
|
|
1782
|
-
) {
|
|
1783
|
-
throw ERRORS.TypedError(
|
|
1784
|
-
HardwareErrorCode.RuntimeError,
|
|
1785
|
-
`Protocol V2 install status conflicts with target ${target.target_id}`
|
|
1786
|
-
);
|
|
1787
|
-
}
|
|
1788
|
-
seenTargetIds.add(targetId);
|
|
1789
|
-
}
|
|
1790
|
-
const completedTargets = matchingTargets.filter(target =>
|
|
1791
|
-
isProtocolV2TargetStatusFinished(target.status)
|
|
1792
|
-
);
|
|
1793
1136
|
const completedTargetIds = new Set<number>();
|
|
1794
|
-
|
|
1137
|
+
matchingTargets.forEach(target => {
|
|
1795
1138
|
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
1796
|
-
if (targetId !== undefined) {
|
|
1139
|
+
if (targetId !== undefined && isProtocolV2TargetStatusFinished(target.status)) {
|
|
1797
1140
|
completedTargetIds.add(targetId);
|
|
1798
1141
|
}
|
|
1799
1142
|
});
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
if (targetId === undefined) {
|
|
1805
|
-
throw ERRORS.TypedError(
|
|
1806
|
-
HardwareErrorCode.RuntimeError,
|
|
1807
|
-
'Protocol V2 completed target identity is invalid'
|
|
1808
|
-
);
|
|
1809
|
-
}
|
|
1810
|
-
const expectedVersion = this.getExpectedProtocolV2TargetVersion(targetId);
|
|
1811
|
-
if (expectedVersion !== undefined && payloadVersion !== undefined) {
|
|
1812
|
-
if (payloadVersion !== expectedVersion) {
|
|
1813
|
-
throw ERRORS.TypedError(
|
|
1814
|
-
HardwareErrorCode.FirmwareVerificationFailed,
|
|
1815
|
-
`Protocol V2 target ${targetId} reached payload version ${payloadVersion}, expected ${expectedVersion}`
|
|
1816
|
-
);
|
|
1817
|
-
}
|
|
1818
|
-
}
|
|
1819
|
-
if (payloadVersion !== undefined) {
|
|
1820
|
-
this.protocolV2CompletedTargetVersions.set(targetId, payloadVersion);
|
|
1821
|
-
}
|
|
1822
|
-
}
|
|
1143
|
+
const allExpectedTargetsCompleted =
|
|
1144
|
+
expectedTargetIds.size > 0 &&
|
|
1145
|
+
Array.from(expectedTargetIds).every(targetId => completedTargetIds.has(targetId));
|
|
1146
|
+
if (allExpectedTargetsCompleted) {
|
|
1823
1147
|
this.postProgressMessage(100, 'installingFirmware');
|
|
1824
1148
|
return true;
|
|
1825
1149
|
}
|
|
@@ -1843,9 +1167,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1843
1167
|
private async waitForProtocolV2FirmwareUpdateComplete(
|
|
1844
1168
|
targets: Array<{ target_id: number; path: string }>
|
|
1845
1169
|
) {
|
|
1846
|
-
this.protocolV2FinalStatusVerified = false;
|
|
1847
1170
|
const expectedTargetIds = new Set(targets.map(target => target.target_id));
|
|
1848
|
-
const expectedPaths = new Map(targets.map(target => [target.target_id, target.path]));
|
|
1849
1171
|
const startTime = Date.now();
|
|
1850
1172
|
let lastError: unknown;
|
|
1851
1173
|
|
|
@@ -1869,11 +1191,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1869
1191
|
if (
|
|
1870
1192
|
this.assertProtocolV2TargetStatus(
|
|
1871
1193
|
(statusResponse.message.records ?? []) as ProtocolV2FirmwareUpdateStatusTarget[],
|
|
1872
|
-
expectedTargetIds
|
|
1873
|
-
expectedPaths
|
|
1194
|
+
expectedTargetIds
|
|
1874
1195
|
)
|
|
1875
1196
|
) {
|
|
1876
|
-
this.protocolV2FinalStatusVerified = true;
|
|
1877
1197
|
return;
|
|
1878
1198
|
}
|
|
1879
1199
|
lastError = new Error('Protocol V2 firmware targets are still installing');
|
|
@@ -1912,12 +1232,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1912
1232
|
if (error instanceof HardwareError && error.errorCode === HardwareErrorCode.FirmwareError) {
|
|
1913
1233
|
throw error;
|
|
1914
1234
|
}
|
|
1915
|
-
if (
|
|
1916
|
-
error instanceof HardwareError &&
|
|
1917
|
-
error.params?.firmwareUpdateCode === 'FirmwareInstallStatusUnavailable'
|
|
1918
|
-
) {
|
|
1919
|
-
throw error;
|
|
1920
|
-
}
|
|
1921
1235
|
Log.log('Protocol V2 firmware install device readiness probe failed: ', error);
|
|
1922
1236
|
}
|
|
1923
1237
|
await wait(1000);
|
|
@@ -1935,7 +1249,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1935
1249
|
// The connection may still be in bootloader. Request a Normal reboot directly;
|
|
1936
1250
|
// repeating it after an automatic App reboot is idempotent.
|
|
1937
1251
|
await this.protocolV2Reboot(DeviceRebootType.Normal);
|
|
1938
|
-
this.protocolV2ExecutionInLoader = false;
|
|
1939
1252
|
}
|
|
1940
1253
|
|
|
1941
1254
|
private async probeProtocolV2NormalMode(deviceInfo: ProtocolV2DeviceInfo) {
|
|
@@ -1951,10 +1264,13 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1951
1264
|
PROTOCOL_V2_FINAL_RECONNECT_TIMEOUT
|
|
1952
1265
|
);
|
|
1953
1266
|
|
|
1267
|
+
return this.getProtocolV2VersionResult(features);
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
private getProtocolV2VersionResult(features: Features) {
|
|
1954
1271
|
const bootloaderVersion = getDeviceBootloaderVersion(features).join('.');
|
|
1955
1272
|
const bleVersion = getDeviceBLEFirmwareVersion(features).join('.');
|
|
1956
1273
|
const firmwareVersion = getDeviceFirmwareVersion(features).join('.');
|
|
1957
|
-
this.protocolV2LatestFinalFeatures = features;
|
|
1958
1274
|
if (firmwareVersion === '0.0.0') {
|
|
1959
1275
|
Log.warn(
|
|
1960
1276
|
'Protocol V2 firmware update finished but app firmware version is still 0.0.0. This is allowed for Pro2 debug BLE-only update flows.'
|
|
@@ -1968,17 +1284,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1968
1284
|
};
|
|
1969
1285
|
}
|
|
1970
1286
|
|
|
1971
|
-
private async completeProtocolV2FinalVerification() {
|
|
1972
|
-
if (this.protocolV2ExecutionInLoader || this.isProtocolV2BootloaderMode()) {
|
|
1973
|
-
await this.exitProtocolV2BootloaderToNormal();
|
|
1974
|
-
}
|
|
1975
|
-
const versions = await this.waitForProtocolV2FinalFeatures();
|
|
1976
|
-
this.assertExpectedProtocolV2Versions();
|
|
1977
|
-
this.postTipMessage(FirmwareUpdateTipMessage.FirmwareUpdateCompleted);
|
|
1978
|
-
DevicePool.resetState();
|
|
1979
|
-
return versions;
|
|
1980
|
-
}
|
|
1981
|
-
|
|
1982
1287
|
private async waitForProtocolV2ReconnectAndFeatures(timeout: number) {
|
|
1983
1288
|
const startTime = Date.now();
|
|
1984
1289
|
let lastError: unknown;
|
|
@@ -2085,6 +1390,96 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2085
1390
|
return this.normalizeErrorMessage(error).includes('Protocol V2 reconnect physical identity');
|
|
2086
1391
|
}
|
|
2087
1392
|
|
|
1393
|
+
private async protocolV2CommonUpdateProcess(params: ProtocolV2FileTransferParams) {
|
|
1394
|
+
let lastError: unknown;
|
|
1395
|
+
for (let attempt = 1; attempt <= PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT; attempt += 1) {
|
|
1396
|
+
try {
|
|
1397
|
+
return await this.protocolV2WriteWholeFile(params);
|
|
1398
|
+
} catch (error) {
|
|
1399
|
+
lastError = error;
|
|
1400
|
+
Log.error(
|
|
1401
|
+
`Protocol V2 file transfer failed path=${params.filePath} attempt=${attempt}/${PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT}; restarting from offset 0`,
|
|
1402
|
+
error
|
|
1403
|
+
);
|
|
1404
|
+
if (attempt === PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT) {
|
|
1405
|
+
break;
|
|
1406
|
+
}
|
|
1407
|
+
await this.recoverProtocolV2FileTransfer();
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
throw ERRORS.TypedError(
|
|
1412
|
+
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
1413
|
+
`transfer data error: ${getProtocolV2UnknownErrorText(lastError)}`
|
|
1414
|
+
);
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
private async protocolV2WriteWholeFile({
|
|
1418
|
+
payload,
|
|
1419
|
+
filePath,
|
|
1420
|
+
processedSize,
|
|
1421
|
+
totalSize,
|
|
1422
|
+
onTransferredBytes,
|
|
1423
|
+
}: ProtocolV2FileTransferParams) {
|
|
1424
|
+
const chunkSize = this.getProtocolV2FirmwareChunkSize();
|
|
1425
|
+
let offset = 0;
|
|
1426
|
+
const getUploadProgress = (fileOffset: number) => {
|
|
1427
|
+
if (totalSize !== undefined && processedSize !== undefined) {
|
|
1428
|
+
return Math.min(Math.ceil(((processedSize + fileOffset) / totalSize) * 100), 99);
|
|
1429
|
+
}
|
|
1430
|
+
return Math.min(Math.ceil((fileOffset / payload.byteLength) * 100), 99);
|
|
1431
|
+
};
|
|
1432
|
+
|
|
1433
|
+
while (offset < payload.byteLength) {
|
|
1434
|
+
const chunkEnd = Math.min(offset + chunkSize, payload.byteLength);
|
|
1435
|
+
const chunkLength = chunkEnd - offset;
|
|
1436
|
+
const chunk = payload.slice(offset, chunkEnd);
|
|
1437
|
+
const overwrite = offset === 0;
|
|
1438
|
+
const progress = getProtocolV2DeviceTransferProgress(
|
|
1439
|
+
(processedSize ?? 0) + offset,
|
|
1440
|
+
(processedSize ?? 0) + chunkEnd,
|
|
1441
|
+
totalSize ?? payload.byteLength
|
|
1442
|
+
);
|
|
1443
|
+
|
|
1444
|
+
const writeRes = await this.fileWriteChunk(
|
|
1445
|
+
filePath,
|
|
1446
|
+
payload.byteLength,
|
|
1447
|
+
offset,
|
|
1448
|
+
chunk,
|
|
1449
|
+
overwrite,
|
|
1450
|
+
progress
|
|
1451
|
+
);
|
|
1452
|
+
const rawProcessedByte = writeRes.message.processed_byte;
|
|
1453
|
+
const processedByte = Number(rawProcessedByte);
|
|
1454
|
+
const nextOffset = rawProcessedByte === undefined ? offset + chunkLength : processedByte;
|
|
1455
|
+
if (!Number.isFinite(nextOffset) || nextOffset <= offset || nextOffset > chunkEnd) {
|
|
1456
|
+
throw ERRORS.TypedError(
|
|
1457
|
+
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
1458
|
+
`invalid processed_byte ${writeRes.message.processed_byte} for offset ${offset}`
|
|
1459
|
+
);
|
|
1460
|
+
}
|
|
1461
|
+
offset = nextOffset;
|
|
1462
|
+
onTransferredBytes?.((processedSize ?? 0) + offset);
|
|
1463
|
+
this.postProgressMessage(getUploadProgress(offset), 'transferData');
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
return totalSize !== undefined ? (processedSize ?? 0) + payload.byteLength : 0;
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
private getProtocolV2FirmwareTransferTransport() {
|
|
1470
|
+
const env = DataManager.getSettings('env');
|
|
1471
|
+
if (env && DataManager.isBleConnect(env)) {
|
|
1472
|
+
return 'BLE';
|
|
1473
|
+
}
|
|
1474
|
+
if (
|
|
1475
|
+
env &&
|
|
1476
|
+
(DataManager.isBrowserWebUsb(env) || DataManager.isDesktopWebUsb(env) || env === 'web')
|
|
1477
|
+
) {
|
|
1478
|
+
return 'WebUSB';
|
|
1479
|
+
}
|
|
1480
|
+
return env ?? 'unknown';
|
|
1481
|
+
}
|
|
1482
|
+
|
|
2088
1483
|
private async fileWriteChunk(
|
|
2089
1484
|
filePath: string,
|
|
2090
1485
|
totalFileSize: number,
|