@onekeyfe/hd-core 1.2.0-alpha.68 → 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__/check-all-firmware-release-protocol-v2.test.ts +28 -0
- package/__tests__/connectSettings.test.ts +5 -47
- package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +31 -59
- package/__tests__/protocol-v2-firmware-targets.test.ts +33 -1
- package/__tests__/protocol-v2-resources.test.ts +9 -6
- package/__tests__/protocol-v2.test.ts +174 -383
- package/__tests__/protocolV2FileWrite.test.ts +48 -7
- package/dist/api/CheckAllFirmwareRelease.d.ts +2 -1
- package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
- 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 +6 -25
- package/dist/api/FirmwareUpdateV4.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.map +1 -1
- package/dist/api/stellar/StellarSignTransaction.d.ts +1 -1
- package/dist/data-manager/DataManager.d.ts +0 -1
- package/dist/data-manager/DataManager.d.ts.map +1 -1
- package/dist/data-manager/connectSettings.d.ts.map +1 -1
- package/dist/device/Device.d.ts +0 -1
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/index.d.ts +2 -180
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1041 -3037
- package/dist/inject.d.ts.map +1 -1
- package/dist/lowLevelInject.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/resources.d.ts +1 -1
- package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
- package/dist/topLevelInject.d.ts.map +1 -1
- package/dist/types/api/checkAllFirmwareRelease.d.ts +0 -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 -68
- package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
- package/dist/types/api/index.d.ts +1 -9
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/settings.d.ts +1 -14
- 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/package.json +4 -4
- package/src/api/CheckAllFirmwareRelease.ts +25 -61
- package/src/api/FirmwareUpdate.ts +1 -13
- package/src/api/FirmwareUpdateV2.ts +121 -296
- package/src/api/FirmwareUpdateV3.ts +57 -263
- package/src/api/FirmwareUpdateV4.ts +287 -865
- package/src/api/device/DeviceUpdateBootloader.ts +6 -125
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +1 -72
- package/src/api/firmware/updateBootloader.ts +4 -19
- package/src/api/firmware/uploadFirmware.ts +22 -140
- package/src/api/helpers/protocolV2FileWrite.ts +54 -4
- package/src/data-manager/DataManager.ts +54 -59
- package/src/data-manager/connectSettings.ts +0 -11
- package/src/device/Device.ts +1 -6
- package/src/index.ts +0 -5
- package/src/inject.ts +2 -22
- package/src/lowLevelInject.ts +1 -5
- package/src/protocols/protocol-v2/resources.ts +5 -1
- package/src/topLevelInject.ts +1 -5
- package/src/types/api/checkAllFirmwareRelease.ts +0 -4
- package/src/types/api/deviceUpdateBootloader.ts +0 -6
- package/src/types/api/export.ts +0 -18
- package/src/types/api/firmwareUpdate.ts +0 -75
- package/src/types/api/index.ts +0 -14
- package/src/types/settings.ts +2 -14
- package/src/utils/deviceFeaturesCompat.ts +0 -6
- 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 -63
- 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/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/types/api/firmwareUpdateCapabilities.ts +0 -9
- package/src/types/api/firmwareUpdatePlan.ts +0 -38
- package/src/types/api/firmwareUpdatePreparedPlan.ts +0 -53
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { EDeviceType, ERRORS, HardwareError, HardwareErrorCode, wait } from '@onekeyfe/hd-shared';
|
|
2
2
|
import {
|
|
3
3
|
DeviceRebootType,
|
|
4
|
-
PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE,
|
|
5
4
|
PROTOCOL_V2_BLE_FILE_CHUNK_SIZE,
|
|
6
5
|
PROTOCOL_V2_WEBUSB_FILE_CHUNK_SIZE,
|
|
7
6
|
} from '@onekeyfe/hd-transport';
|
|
@@ -9,17 +8,18 @@ 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';
|
|
@@ -38,20 +38,11 @@ import {
|
|
|
38
38
|
getProtocolV2UnknownErrorText,
|
|
39
39
|
isProtocolV2DeviceDisconnectedError,
|
|
40
40
|
} from './protocol-v2/helpers';
|
|
41
|
-
import { openFirmwareByteSource, writeFirmwareByteSource } from './firmware/FirmwareArtifactSource';
|
|
42
|
-
import { resolveFirmwareUpdateHostBinding } from './firmware/FirmwareHostBinding';
|
|
43
|
-
import {
|
|
44
|
-
assertFirmwareUpdatePreparedPlanBinding,
|
|
45
|
-
assertFirmwareUpdatePreparedPlanDeviceIdentity,
|
|
46
|
-
} from './firmware/FirmwareUpdatePreparedPlan';
|
|
47
41
|
|
|
48
|
-
import type {
|
|
49
|
-
FirmwareArtifactReference,
|
|
50
|
-
FirmwareUpdateV4Params,
|
|
51
|
-
FirmwareUpdateV4Target,
|
|
52
|
-
} from '../types/api/firmwareUpdate';
|
|
42
|
+
import type { FirmwareUpdateV4Params, FirmwareUpdateV4Target } from '../types/api/firmwareUpdate';
|
|
53
43
|
import type { EFirmwareType } from '@onekeyfe/hd-shared';
|
|
54
44
|
import type { ProtocolV2DeviceInfo } from '@onekeyfe/hd-transport';
|
|
45
|
+
import type { PROTO } from '../constants';
|
|
55
46
|
import type { TypedResponseMessage } from '../device/DeviceCommands';
|
|
56
47
|
import type {
|
|
57
48
|
Features,
|
|
@@ -61,7 +52,6 @@ import type {
|
|
|
61
52
|
IProtocolV2ResourceFile,
|
|
62
53
|
IVersionArray,
|
|
63
54
|
} from '../types';
|
|
64
|
-
import type { FirmwareByteSource } from './firmware/FirmwareArtifactSource';
|
|
65
55
|
|
|
66
56
|
const Log = getLogger(LoggerNames.Method);
|
|
67
57
|
|
|
@@ -84,12 +74,35 @@ const PROTOCOL_V2_CONNECT_POLL_INTERVAL = 500;
|
|
|
84
74
|
const PROTOCOL_V2_CONNECT_SINGLE_TIMEOUT = 75 * 1000;
|
|
85
75
|
const PROTOCOL_V2_DEVICE_INFO_READY_TIMEOUT = 30 * 1000;
|
|
86
76
|
const PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT = 3;
|
|
87
|
-
const PROTOCOL_V2_INSTALL_STATUS_CONFLICT_CODE = 'FirmwareInstallStatusConflict';
|
|
88
77
|
const PROTOCOL_V2_OKPP_HEADER_SIZE = 0x52a0;
|
|
89
78
|
const PROTOCOL_V2_OKPP_PAYLOAD_HASH_OFFSET = 0x200;
|
|
90
79
|
const PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET = 0x240;
|
|
91
80
|
const PROTOCOL_V2_OKPP_HASH_SIZE = 64;
|
|
92
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
|
+
|
|
93
106
|
const getProtocolV2DeviceTransferProgress = (
|
|
94
107
|
bytesBeforeChunk: number,
|
|
95
108
|
bytesAfterChunk: number,
|
|
@@ -107,6 +120,11 @@ const getProtocolV2DeviceTransferProgress = (
|
|
|
107
120
|
return Math.min(Math.max(Math.ceil((bytesAfterChunk / totalBytes) * 100), 1), 99);
|
|
108
121
|
};
|
|
109
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
|
+
|
|
110
128
|
type ProtocolV2FirmwareUpdateStatusTarget = {
|
|
111
129
|
target_id: number | string;
|
|
112
130
|
status?: number | string;
|
|
@@ -120,6 +138,17 @@ type ProtocolV2TargetBinary = { fileName: string; binary: ArrayBuffer; targetId:
|
|
|
120
138
|
type ProtocolV2InstallItem = ProtocolV2TargetBinary & {
|
|
121
139
|
kind: ProtocolV2RemoteComponentTarget['kind'];
|
|
122
140
|
};
|
|
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
|
+
|
|
123
152
|
/** Protocol V2 resource file written independently to devicePath through FileWrite. */
|
|
124
153
|
type ProtocolV2ResourceBundleBinary = {
|
|
125
154
|
name: string;
|
|
@@ -137,35 +166,6 @@ type ProtocolV2RemoteComponentTarget = {
|
|
|
137
166
|
kind: 'bootloader' | 'firmware';
|
|
138
167
|
};
|
|
139
168
|
|
|
140
|
-
type ProtocolV2InstallSource = {
|
|
141
|
-
fileName: string;
|
|
142
|
-
source: FirmwareByteSource;
|
|
143
|
-
targetId: number;
|
|
144
|
-
kind: ProtocolV2RemoteComponentTarget['kind'];
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
type ProtocolV2ResourceBundleSource = {
|
|
148
|
-
name: string;
|
|
149
|
-
source: FirmwareByteSource;
|
|
150
|
-
devicePath: string;
|
|
151
|
-
version?: IVersionArray;
|
|
152
|
-
payloadHash?: string;
|
|
153
|
-
headerHash?: string;
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
type ProtocolV2ExecutionPhaseKind =
|
|
157
|
-
| 'resource-sync'
|
|
158
|
-
| 'bootloader-install'
|
|
159
|
-
| 'bootloader-verify'
|
|
160
|
-
| 'component-install'
|
|
161
|
-
| 'final-verify';
|
|
162
|
-
|
|
163
|
-
type ProtocolV2ExecutionPhase = {
|
|
164
|
-
kind: ProtocolV2ExecutionPhaseKind;
|
|
165
|
-
installSources: ProtocolV2InstallSource[];
|
|
166
|
-
resourceSources: ProtocolV2ResourceBundleSource[];
|
|
167
|
-
};
|
|
168
|
-
|
|
169
169
|
type ProtocolV2OkppHeader = {
|
|
170
170
|
type: string;
|
|
171
171
|
version: IVersionArray;
|
|
@@ -268,12 +268,6 @@ const isProtocolV2FirmwareStatusEndpointUnavailable = (error: unknown) => {
|
|
|
268
268
|
);
|
|
269
269
|
};
|
|
270
270
|
|
|
271
|
-
const isProtocolV2TerminalInstallStatusError = (error: unknown) =>
|
|
272
|
-
error instanceof HardwareError &&
|
|
273
|
-
(error.errorCode === HardwareErrorCode.FirmwareError ||
|
|
274
|
-
error.errorCode === HardwareErrorCode.FirmwareVerificationFailed ||
|
|
275
|
-
error.params?.firmwareUpdateCode === PROTOCOL_V2_INSTALL_STATUS_CONFLICT_CODE);
|
|
276
|
-
|
|
277
271
|
const isProtocolV2TargetStatusFinished = (status: ProtocolV2FirmwareUpdateStatusTarget['status']) =>
|
|
278
272
|
normalizeProtocolV2TargetStatus(status) === PROTOCOL_V2_TARGET_STATUS_FINISHED;
|
|
279
273
|
|
|
@@ -311,18 +305,6 @@ const normalizeProtocolV2TargetStatus = (
|
|
|
311
305
|
|
|
312
306
|
const normalizeProtocolV2Hex = (value?: string) => value?.replace(/^0x/i, '').toLowerCase();
|
|
313
307
|
|
|
314
|
-
const versionArrayToNumber = (version?: IVersionArray) => {
|
|
315
|
-
if (!version) return undefined;
|
|
316
|
-
return version[0] * 0x10000 + version[1] * 0x100 + version[2];
|
|
317
|
-
};
|
|
318
|
-
|
|
319
|
-
const compareProtocolV2Versions = (current?: IVersionArray, target?: IVersionArray) => {
|
|
320
|
-
const currentNumber = versionArrayToNumber(current);
|
|
321
|
-
const targetNumber = versionArrayToNumber(target);
|
|
322
|
-
if (currentNumber === undefined || targetNumber === undefined) return undefined;
|
|
323
|
-
return currentNumber - targetNumber;
|
|
324
|
-
};
|
|
325
|
-
|
|
326
308
|
const bytesToHex = (bytes: Uint8Array) =>
|
|
327
309
|
Array.from(bytes)
|
|
328
310
|
.map(byte => byte.toString(16).padStart(2, '0'))
|
|
@@ -456,16 +438,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
456
438
|
return ['V2'] as const;
|
|
457
439
|
}
|
|
458
440
|
|
|
459
|
-
private protocolV2PreparedSources: FirmwareByteSource[] = [];
|
|
460
|
-
|
|
461
|
-
private protocolV2ExecutionInLoader = false;
|
|
462
|
-
|
|
463
|
-
private protocolV2CompletedTargetVersions = new Map<number, number>();
|
|
464
|
-
|
|
465
|
-
private protocolV2LatestFinalFeatures?: Features;
|
|
466
|
-
|
|
467
|
-
private protocolV2FinalStatusVerified = false;
|
|
468
|
-
|
|
469
441
|
init() {
|
|
470
442
|
this.allowDeviceMode = [UI_REQUEST.BOOTLOADER, UI_REQUEST.NOT_INITIALIZE];
|
|
471
443
|
this.requireDeviceMode = [];
|
|
@@ -503,62 +475,10 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
503
475
|
{ name: 'firmwareType', type: 'string' },
|
|
504
476
|
{ name: 'targetsToUpdate', type: 'array', allowEmpty: true },
|
|
505
477
|
{ name: 'platform', type: 'string' },
|
|
506
|
-
{ name: 'expectedDeviceId', type: 'string' },
|
|
507
478
|
{ name: 'resourceFiles', type: 'array', allowEmpty: true },
|
|
508
479
|
]);
|
|
509
|
-
if (
|
|
510
|
-
payload.expectedDeviceId !== undefined &&
|
|
511
|
-
(payload.expectedDeviceId.length === 0 || payload.expectedDeviceId.length > 160)
|
|
512
|
-
) {
|
|
513
|
-
throw ERRORS.TypedError(
|
|
514
|
-
HardwareErrorCode.CallMethodInvalidParameter,
|
|
515
|
-
'Protocol V2 expected device identity is invalid'
|
|
516
|
-
);
|
|
517
|
-
}
|
|
518
|
-
const hostBinding =
|
|
519
|
-
payload.preparedPlan || payload.componentArtifacts || payload.resourceBundleArtifacts
|
|
520
|
-
? resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration)
|
|
521
|
-
: undefined;
|
|
522
|
-
if (hostBinding) {
|
|
523
|
-
assertFirmwareUpdatePreparedPlanBinding({
|
|
524
|
-
preparedPlan: payload.preparedPlan,
|
|
525
|
-
executor: 'v4',
|
|
526
|
-
platform: payload.platform,
|
|
527
|
-
scopeTargets: [
|
|
528
|
-
'boot',
|
|
529
|
-
'app_v1',
|
|
530
|
-
'app_v2',
|
|
531
|
-
'coprocessor',
|
|
532
|
-
'resource',
|
|
533
|
-
'se01',
|
|
534
|
-
'se02',
|
|
535
|
-
'se03',
|
|
536
|
-
'se04',
|
|
537
|
-
],
|
|
538
|
-
bindings: [
|
|
539
|
-
...Object.entries(payload.componentArtifacts ?? {}).flatMap(([target, artifact]) =>
|
|
540
|
-
artifact
|
|
541
|
-
? [
|
|
542
|
-
{
|
|
543
|
-
target: target as Exclude<FirmwareUpdateV4Target, 'resource'>,
|
|
544
|
-
artifact,
|
|
545
|
-
},
|
|
546
|
-
]
|
|
547
|
-
: []
|
|
548
|
-
),
|
|
549
|
-
...(payload.resourceBundleArtifacts ?? []).map(
|
|
550
|
-
(entry: { name: string; artifact: FirmwareArtifactReference }) => ({
|
|
551
|
-
target: 'resource' as const,
|
|
552
|
-
logicalName: entry.name,
|
|
553
|
-
artifact: entry.artifact,
|
|
554
|
-
})
|
|
555
|
-
),
|
|
556
|
-
],
|
|
557
|
-
});
|
|
558
|
-
}
|
|
559
480
|
|
|
560
481
|
this.params = {
|
|
561
|
-
preparedPlan: payload.preparedPlan,
|
|
562
482
|
chunkSize: payload.chunkSize,
|
|
563
483
|
forcedUpdateRes: payload.forcedUpdateRes,
|
|
564
484
|
bootloaderBinary: payload.bootloaderBinary,
|
|
@@ -573,25 +493,17 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
573
493
|
resourceFiles: payload.resourceFiles,
|
|
574
494
|
firmwareType: payload.firmwareType,
|
|
575
495
|
targetsToUpdate: payload.targetsToUpdate,
|
|
576
|
-
expectedTargetVersions: payload.expectedTargetVersions,
|
|
577
496
|
platform: payload.platform,
|
|
578
|
-
expectedDeviceId: payload.expectedDeviceId,
|
|
579
|
-
artifactReader: hostBinding?.artifactReader ?? payload.artifactReader,
|
|
580
|
-
componentArtifacts: payload.componentArtifacts,
|
|
581
|
-
resourceBundleArtifacts: payload.resourceBundleArtifacts,
|
|
582
497
|
};
|
|
583
498
|
}
|
|
584
499
|
|
|
585
|
-
private getProtocolV2FirmwareChunkSize(
|
|
500
|
+
private getProtocolV2FirmwareChunkSize() {
|
|
586
501
|
const payloadChunkSize = Number(this.params?.chunkSize);
|
|
587
502
|
const env = DataManager.getSettings('env');
|
|
588
503
|
const isBle = this.params?.platform === 'native' || (env && DataManager.isBleConnect(env));
|
|
589
504
|
let maxChunkSize = PROTOCOL_V2_WEBUSB_FILE_CHUNK_SIZE;
|
|
590
505
|
if (isBle) {
|
|
591
|
-
maxChunkSize =
|
|
592
|
-
direction === 'read'
|
|
593
|
-
? PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE
|
|
594
|
-
: PROTOCOL_V2_BLE_FILE_CHUNK_SIZE;
|
|
506
|
+
maxChunkSize = PROTOCOL_V2_BLE_FILE_CHUNK_SIZE;
|
|
595
507
|
}
|
|
596
508
|
if (!Number.isFinite(payloadChunkSize) || payloadChunkSize <= 0) {
|
|
597
509
|
return maxChunkSize;
|
|
@@ -610,19 +522,16 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
610
522
|
private async runProtocolV2() {
|
|
611
523
|
await this.captureProtocolV2PhysicalIdentity();
|
|
612
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);
|
|
613
531
|
const deviceFirmwareType = getFirmwareType(deviceFeatures);
|
|
614
532
|
const firmwareType = this.params.firmwareType ?? deviceFirmwareType;
|
|
615
|
-
this.validateExpectedTargetVersions();
|
|
616
|
-
|
|
617
|
-
if (
|
|
618
|
-
(this.params.componentArtifacts && Object.keys(this.params.componentArtifacts).length > 0) ||
|
|
619
|
-
this.params.resourceBundleArtifacts?.length
|
|
620
|
-
) {
|
|
621
|
-
return this.runProtocolV2PreparedArtifacts(deviceFeatures, firmwareType);
|
|
622
|
-
}
|
|
623
533
|
const needsRemoteResources =
|
|
624
534
|
!this.params.resourceFiles?.length && !!this.params.targetsToUpdate?.includes('resource');
|
|
625
|
-
const needsRemoteBootResources = !!this.params.targetsToUpdate?.includes('boot_resources');
|
|
626
535
|
|
|
627
536
|
let fwBinaryMap: ProtocolV2TargetBinary[] = [];
|
|
628
537
|
let bootloaderBinary: ArrayBuffer | null = null;
|
|
@@ -634,19 +543,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
634
543
|
fwBinaryMap = this.collectExplicitTargetBinaries();
|
|
635
544
|
bootloaderBinary = this.prepareBootloaderBinary();
|
|
636
545
|
const needsRemoteFirmware = !this.hasExplicitProtocolV2Payload(fwBinaryMap);
|
|
637
|
-
|
|
638
|
-
(
|
|
639
|
-
|
|
640
|
-
DataManager.getSettings('firmwareManifestMode') === 'external-only')
|
|
641
|
-
) {
|
|
642
|
-
throw ERRORS.TypedError(
|
|
643
|
-
HardwareErrorCode.RuntimeError,
|
|
644
|
-
'Protocol V2 firmware artifacts must be prepared by the external firmware host',
|
|
645
|
-
{
|
|
646
|
-
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
647
|
-
}
|
|
648
|
-
);
|
|
649
|
-
}
|
|
546
|
+
const needsRemoteBootResources =
|
|
547
|
+
!!this.params.targetsToUpdate?.includes('resource') ||
|
|
548
|
+
!!this.params.targetsToUpdate?.includes('boot_resources');
|
|
650
549
|
if (needsRemoteFirmware || needsRemoteResources || needsRemoteBootResources) {
|
|
651
550
|
// Remote updates must use a freshly fetched config before any reboot or file write.
|
|
652
551
|
await DataManager.forceReloadData({
|
|
@@ -673,7 +572,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
673
572
|
if (err instanceof HardwareError && err.errorCode === HardwareErrorCode.NetworkError) {
|
|
674
573
|
throw err;
|
|
675
574
|
}
|
|
676
|
-
throw
|
|
575
|
+
throw ERRORS.TypedError(HardwareErrorCode.FirmwareUpdateDownloadFailed, err.message ?? err);
|
|
677
576
|
}
|
|
678
577
|
|
|
679
578
|
if (
|
|
@@ -689,8 +588,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
689
588
|
);
|
|
690
589
|
}
|
|
691
590
|
|
|
591
|
+
const enteredBootloader = await this.enterProtocolV2BootloaderMode();
|
|
592
|
+
|
|
692
593
|
if (needsRemoteResources) {
|
|
693
|
-
const enteredBootloader = await this.enterProtocolV2BootloaderMode();
|
|
694
594
|
try {
|
|
695
595
|
const stableResources = await this.prepareProtocolV2ResourceBundles();
|
|
696
596
|
resourceBundles = [...(resourceBundles ?? []), ...(stableResources ?? [])];
|
|
@@ -706,293 +606,25 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
706
606
|
);
|
|
707
607
|
}
|
|
708
608
|
}
|
|
709
|
-
throw
|
|
609
|
+
throw ERRORS.TypedError(HardwareErrorCode.FirmwareUpdateDownloadFailed, err.message ?? err);
|
|
710
610
|
}
|
|
711
611
|
}
|
|
712
612
|
|
|
713
|
-
|
|
613
|
+
await this.executeProtocolV2Update({
|
|
714
614
|
fwBinaryMap,
|
|
715
615
|
bootloaderBinary,
|
|
716
616
|
...(installItems ? { installItems } : undefined),
|
|
717
617
|
...(resourceBundles?.length ? { resourceBundles } : undefined),
|
|
718
618
|
});
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
private async openProtocolV2PreparedSource(artifact: FirmwareArtifactReference) {
|
|
722
|
-
const source = await openFirmwareByteSource({
|
|
723
|
-
artifact,
|
|
724
|
-
reader: this.params.artifactReader,
|
|
725
|
-
});
|
|
726
|
-
if (!source) {
|
|
727
|
-
throw ERRORS.TypedError(
|
|
728
|
-
HardwareErrorCode.RuntimeError,
|
|
729
|
-
`Firmware artifact ${artifact.artifactRef} is not prepared`,
|
|
730
|
-
{
|
|
731
|
-
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
732
|
-
}
|
|
733
|
-
);
|
|
734
|
-
}
|
|
735
|
-
this.protocolV2PreparedSources.push(source);
|
|
736
|
-
return source;
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
private async openProtocolV2MemorySource(binary: ArrayBuffer) {
|
|
740
|
-
const source = await openFirmwareByteSource({ binary });
|
|
741
|
-
if (!source) {
|
|
742
|
-
throw ERRORS.TypedError(
|
|
743
|
-
HardwareErrorCode.RuntimeError,
|
|
744
|
-
'Protocol V2 firmware binary is empty'
|
|
745
|
-
);
|
|
746
|
-
}
|
|
747
|
-
this.protocolV2PreparedSources.push(source);
|
|
748
|
-
return source;
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
private async closeProtocolV2PreparedSources() {
|
|
752
|
-
const sources = this.protocolV2PreparedSources.splice(0);
|
|
753
|
-
await Promise.all(sources.map(source => source.close().catch(() => undefined)));
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
private async prepareProtocolV2InstallSources(
|
|
757
|
-
firmwareType: EFirmwareType,
|
|
758
|
-
features: Features
|
|
759
|
-
): Promise<ProtocolV2InstallSource[]> {
|
|
760
|
-
const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
|
|
761
|
-
if (!release) {
|
|
762
|
-
throw ERRORS.TypedError(
|
|
763
|
-
HardwareErrorCode.RuntimeError,
|
|
764
|
-
'Protocol V2 firmware release is unavailable'
|
|
765
|
-
);
|
|
766
|
-
}
|
|
767
|
-
const componentArtifacts = this.params.componentArtifacts ?? {};
|
|
768
|
-
const requestedTargets = new Set(
|
|
769
|
-
this.params.targetsToUpdate ?? (Object.keys(componentArtifacts) as FirmwareUpdateV4Target[])
|
|
770
|
-
);
|
|
771
|
-
const installSources: ProtocolV2InstallSource[] = [];
|
|
772
|
-
const preparedTargets = new Set<FirmwareUpdateV4Target>();
|
|
773
|
-
|
|
774
|
-
for (const [key, component] of this.getRemoteComponentEntries(release)) {
|
|
775
|
-
const target = this.getRemoteComponentTarget(key, component);
|
|
776
|
-
const updateTarget = PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(target.targetId);
|
|
777
|
-
if (updateTarget && updateTarget !== 'resource' && requestedTargets.has(updateTarget)) {
|
|
778
|
-
const artifact = componentArtifacts[updateTarget];
|
|
779
|
-
if (!artifact) {
|
|
780
|
-
throw ERRORS.TypedError(
|
|
781
|
-
HardwareErrorCode.RuntimeError,
|
|
782
|
-
`Protocol V2 ${updateTarget} artifact is not prepared`,
|
|
783
|
-
{
|
|
784
|
-
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
785
|
-
artifactName: updateTarget,
|
|
786
|
-
}
|
|
787
|
-
);
|
|
788
|
-
}
|
|
789
|
-
installSources.push({
|
|
790
|
-
...target,
|
|
791
|
-
source: await this.openProtocolV2PreparedSource(artifact),
|
|
792
|
-
});
|
|
793
|
-
preparedTargets.add(updateTarget);
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
619
|
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
);
|
|
800
|
-
if (unknownTarget) {
|
|
801
|
-
throw ERRORS.TypedError(
|
|
802
|
-
HardwareErrorCode.RuntimeError,
|
|
803
|
-
`Protocol V2 release does not contain requested target ${unknownTarget}`
|
|
804
|
-
);
|
|
805
|
-
}
|
|
806
|
-
return installSources;
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
private async prepareProtocolV2ResourceSources(
|
|
810
|
-
firmwareType: EFirmwareType,
|
|
811
|
-
features: Features
|
|
812
|
-
): Promise<ProtocolV2ResourceBundleSource[]> {
|
|
813
|
-
const preparedArtifacts = this.params.resourceBundleArtifacts ?? [];
|
|
814
|
-
const resourceRequested =
|
|
815
|
-
this.params.targetsToUpdate?.includes('resource') || preparedArtifacts.length > 0;
|
|
816
|
-
if (!resourceRequested) {
|
|
817
|
-
return [];
|
|
818
|
-
}
|
|
819
|
-
const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
|
|
820
|
-
const descriptors = release?.resourceBundles ?? [];
|
|
821
|
-
const artifactByName = new Map(
|
|
822
|
-
preparedArtifacts.map(item => [item.name, item.artifact] as const)
|
|
823
|
-
);
|
|
824
|
-
const sources: ProtocolV2ResourceBundleSource[] = [];
|
|
825
|
-
for (const descriptor of descriptors) {
|
|
826
|
-
const artifact = artifactByName.get(descriptor.name);
|
|
827
|
-
if (!artifact) {
|
|
828
|
-
throw ERRORS.TypedError(
|
|
829
|
-
HardwareErrorCode.RuntimeError,
|
|
830
|
-
`Protocol V2 resource bundle ${descriptor.name} is not prepared`,
|
|
831
|
-
{
|
|
832
|
-
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
833
|
-
artifactName: descriptor.name,
|
|
834
|
-
}
|
|
835
|
-
);
|
|
836
|
-
}
|
|
837
|
-
const devicePath = validateProtocolV2FilesystemPath(
|
|
838
|
-
descriptor.devicePath,
|
|
839
|
-
'resourceBundles[].devicePath'
|
|
840
|
-
);
|
|
841
|
-
sources.push({
|
|
842
|
-
name: descriptor.name,
|
|
843
|
-
source: await this.openProtocolV2PreparedSource(artifact),
|
|
844
|
-
devicePath,
|
|
845
|
-
version: descriptor.version,
|
|
846
|
-
payloadHash: descriptor.payloadHash,
|
|
847
|
-
headerHash: descriptor.headerHash,
|
|
848
|
-
});
|
|
849
|
-
artifactByName.delete(descriptor.name);
|
|
850
|
-
}
|
|
851
|
-
if (artifactByName.size > 0) {
|
|
852
|
-
throw ERRORS.TypedError(
|
|
853
|
-
HardwareErrorCode.RuntimeError,
|
|
854
|
-
`Protocol V2 release does not contain resource bundle ${artifactByName.keys().next().value}`
|
|
855
|
-
);
|
|
856
|
-
}
|
|
857
|
-
return sources;
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
private async runProtocolV2PreparedArtifacts(features: Features, firmwareType: EFirmwareType) {
|
|
861
|
-
try {
|
|
862
|
-
this.postTipMessage(FirmwareUpdateTipMessage.StartDownloadFirmware);
|
|
863
|
-
const installSources = await this.prepareProtocolV2InstallSources(firmwareType, features);
|
|
864
|
-
const resourceSources = await this.prepareProtocolV2ResourceSources(firmwareType, features);
|
|
865
|
-
if (installSources.length === 0 && resourceSources.length === 0) {
|
|
866
|
-
throw ERRORS.TypedError(
|
|
867
|
-
HardwareErrorCode.FirmwareUpdateDownloadFailed,
|
|
868
|
-
'No firmware to update'
|
|
869
|
-
);
|
|
870
|
-
}
|
|
871
|
-
this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
872
|
-
|
|
873
|
-
return await this.executeProtocolV2SourceUpdate({
|
|
874
|
-
installSources,
|
|
875
|
-
resourceSources,
|
|
876
|
-
});
|
|
877
|
-
} finally {
|
|
878
|
-
await this.closeProtocolV2PreparedSources();
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
private validateExpectedTargetVersions() {
|
|
883
|
-
const expected = this.params.expectedTargetVersions;
|
|
884
|
-
if (expected === undefined) return;
|
|
885
|
-
if (typeof expected !== 'object' || expected === null || Array.isArray(expected)) {
|
|
886
|
-
throw ERRORS.TypedError(
|
|
887
|
-
HardwareErrorCode.RuntimeError,
|
|
888
|
-
'Protocol V2 expected target versions are invalid'
|
|
889
|
-
);
|
|
890
|
-
}
|
|
891
|
-
for (const [target, version] of Object.entries(expected)) {
|
|
892
|
-
if (
|
|
893
|
-
!Array.from(PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.values()).includes(
|
|
894
|
-
target as FirmwareUpdateV4Target
|
|
895
|
-
) ||
|
|
896
|
-
typeof version !== 'string' ||
|
|
897
|
-
!/^\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.-]+)?$/u.test(version) ||
|
|
898
|
-
version.length > 64
|
|
899
|
-
) {
|
|
900
|
-
throw ERRORS.TypedError(
|
|
901
|
-
HardwareErrorCode.RuntimeError,
|
|
902
|
-
'Protocol V2 expected target version is invalid'
|
|
903
|
-
);
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
}
|
|
620
|
+
this.postTipMessage(FirmwareUpdateTipMessage.SwitchFirmwareReconnectDevice);
|
|
621
|
+
await this.exitProtocolV2BootloaderToNormal();
|
|
907
622
|
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
if (!version) return undefined;
|
|
912
|
-
const parts = version.split(/[+-]/u, 1)[0].split('.').map(Number);
|
|
913
|
-
if (
|
|
914
|
-
parts.length !== 3 ||
|
|
915
|
-
parts.some(part => !Number.isSafeInteger(part) || part < 0 || part > 0xff)
|
|
916
|
-
) {
|
|
917
|
-
return undefined;
|
|
918
|
-
}
|
|
919
|
-
return parts[0] * 0x10000 + parts[1] * 0x100 + parts[2];
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
private assertExpectedProtocolV2Versions(targets?: readonly FirmwareUpdateV4Target[]) {
|
|
923
|
-
const expected = this.params?.expectedTargetVersions;
|
|
924
|
-
if (!expected) return;
|
|
925
|
-
const features = this.protocolV2LatestFinalFeatures;
|
|
926
|
-
const requestedTargets = this.getProtocolV2RequestedTargets();
|
|
927
|
-
const applicationTargets = requestedTargets.filter(
|
|
928
|
-
target => target === 'app_v1' || target === 'app_v2'
|
|
929
|
-
);
|
|
930
|
-
const visibleVersions: Partial<Record<FirmwareUpdateV4Target, string>> = {
|
|
931
|
-
boot: features ? getDeviceBootloaderVersion(features).join('.') : undefined,
|
|
932
|
-
coprocessor: features ? getDeviceBLEFirmwareVersion(features).join('.') : undefined,
|
|
933
|
-
se01: features?.se01Version ?? undefined,
|
|
934
|
-
se02: features?.se02Version ?? undefined,
|
|
935
|
-
se03: features?.se03Version ?? undefined,
|
|
936
|
-
se04: features?.se04Version ?? undefined,
|
|
937
|
-
};
|
|
938
|
-
if (features && applicationTargets.length === 1) {
|
|
939
|
-
visibleVersions[applicationTargets[0]] = getDeviceFirmwareVersion(features).join('.');
|
|
940
|
-
}
|
|
941
|
-
const expectedEntries = (
|
|
942
|
-
Object.entries(expected) as Array<[FirmwareUpdateV4Target, string]>
|
|
943
|
-
).filter(([target]) => !targets || targets.includes(target));
|
|
944
|
-
for (const [target, expectedVersion] of expectedEntries) {
|
|
945
|
-
const targetId = Array.from(PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.entries()).find(
|
|
946
|
-
([, mappedTarget]) => mappedTarget === target
|
|
947
|
-
)?.[0];
|
|
948
|
-
const statusVersion =
|
|
949
|
-
targetId === undefined ? undefined : this.protocolV2CompletedTargetVersions.get(targetId);
|
|
950
|
-
const observedVersion =
|
|
951
|
-
statusVersion === undefined
|
|
952
|
-
? visibleVersions[target]
|
|
953
|
-
: `${Math.floor(statusVersion / 0x10000) % 0x100}.${
|
|
954
|
-
Math.floor(statusVersion / 0x100) % 0x100
|
|
955
|
-
}.${statusVersion % 0x100}`;
|
|
956
|
-
if (!observedVersion) {
|
|
957
|
-
if (this.protocolV2FinalStatusVerified) {
|
|
958
|
-
Log.warn(
|
|
959
|
-
`Protocol V2 target ${target} has no observable final version; completed target status is authoritative`
|
|
960
|
-
);
|
|
961
|
-
} else {
|
|
962
|
-
throw ERRORS.TypedError(
|
|
963
|
-
HardwareErrorCode.FirmwareVerificationFailed,
|
|
964
|
-
`Protocol V2 target ${target} has no observable final version after status fallback`
|
|
965
|
-
);
|
|
966
|
-
}
|
|
967
|
-
} else if (observedVersion !== expectedVersion) {
|
|
968
|
-
throw ERRORS.TypedError(
|
|
969
|
-
HardwareErrorCode.FirmwareVerificationFailed,
|
|
970
|
-
`Protocol V2 target ${target} reached ${observedVersion}, expected ${expectedVersion}`
|
|
971
|
-
);
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
}
|
|
623
|
+
const versions = await this.waitForProtocolV2FinalFeatures();
|
|
624
|
+
this.postTipMessage(FirmwareUpdateTipMessage.FirmwareUpdateCompleted);
|
|
625
|
+
DevicePool.resetState();
|
|
975
626
|
|
|
976
|
-
|
|
977
|
-
if (this.params.targetsToUpdate?.length) {
|
|
978
|
-
return [...new Set(this.params.targetsToUpdate)];
|
|
979
|
-
}
|
|
980
|
-
const targets = new Set<FirmwareUpdateV4Target>();
|
|
981
|
-
Object.keys(this.params.componentArtifacts ?? {}).forEach(target =>
|
|
982
|
-
targets.add(target as FirmwareUpdateV4Target)
|
|
983
|
-
);
|
|
984
|
-
if (this.params.resourceBundleArtifacts?.length || this.params.resourceFiles?.length) {
|
|
985
|
-
targets.add('resource');
|
|
986
|
-
}
|
|
987
|
-
if (this.params.bootloaderBinary) targets.add('boot');
|
|
988
|
-
if (this.params.applicationP1Binary) targets.add('app_v1');
|
|
989
|
-
if (this.params.applicationP2Binary) targets.add('app_v2');
|
|
990
|
-
if (this.params.coprocessorBinary) targets.add('coprocessor');
|
|
991
|
-
if (this.params.se01Binary) targets.add('se01');
|
|
992
|
-
if (this.params.se02Binary) targets.add('se02');
|
|
993
|
-
if (this.params.se03Binary) targets.add('se03');
|
|
994
|
-
if (this.params.se04Binary) targets.add('se04');
|
|
995
|
-
return Array.from(targets);
|
|
627
|
+
return versions;
|
|
996
628
|
}
|
|
997
629
|
|
|
998
630
|
private async getProtocolV2DeviceFeatures(): Promise<Features> {
|
|
@@ -1036,17 +668,8 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1036
668
|
private async captureProtocolV2PhysicalIdentity() {
|
|
1037
669
|
const deviceInfo = await this.requestProtocolV2PhysicalIdentity();
|
|
1038
670
|
const serialNumber = this.getProtocolV2SerialNumber(deviceInfo);
|
|
1039
|
-
const path = this.device.originalDescriptor
|
|
1040
|
-
|
|
1041
|
-
assertFirmwareUpdatePreparedPlanDeviceIdentity({
|
|
1042
|
-
preparedPlan: this.params.preparedPlan,
|
|
1043
|
-
deviceIdentity: serialNumber,
|
|
1044
|
-
});
|
|
1045
|
-
} else if (this.params?.expectedDeviceId) {
|
|
1046
|
-
assertProtocolV2ReconnectIdentity(this.params?.expectedDeviceId, serialNumber);
|
|
1047
|
-
} else {
|
|
1048
|
-
assertProtocolV2ReconnectIdentity(serialNumber, serialNumber, path, path);
|
|
1049
|
-
}
|
|
671
|
+
const path = this.device.originalDescriptor.path?.trim() || undefined;
|
|
672
|
+
assertProtocolV2ReconnectIdentity(serialNumber, serialNumber, path, path);
|
|
1050
673
|
this.protocolV2ExpectedSerialNumber = serialNumber;
|
|
1051
674
|
this.protocolV2ExpectedPath = path;
|
|
1052
675
|
}
|
|
@@ -1223,7 +846,12 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1223
846
|
private async prepareProtocolV2BootResources(): Promise<
|
|
1224
847
|
ProtocolV2ResourceBundleBinary[] | undefined
|
|
1225
848
|
> {
|
|
1226
|
-
if (
|
|
849
|
+
if (
|
|
850
|
+
!this.params.targetsToUpdate?.includes('resource') &&
|
|
851
|
+
!this.params.targetsToUpdate?.includes('boot_resources')
|
|
852
|
+
) {
|
|
853
|
+
return undefined;
|
|
854
|
+
}
|
|
1227
855
|
const resource = DataManager.getProtocolV2BootResources(this.getProtocolV2DeviceType());
|
|
1228
856
|
if (!resource) throw new Error('Missing Protocol V2 boot resources configuration');
|
|
1229
857
|
|
|
@@ -1330,88 +958,38 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1330
958
|
};
|
|
1331
959
|
}
|
|
1332
960
|
|
|
1333
|
-
private
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
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();
|
|
1338
967
|
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
const
|
|
1342
|
-
const pathInfoRes = await typedCall('FilesystemPathInfoQuery', 'FilesystemPathInfo', {
|
|
1343
|
-
path: filePath,
|
|
1344
|
-
});
|
|
1345
|
-
const fileSize = toProtocolV2FiniteNumber(pathInfoRes.message?.size);
|
|
1346
|
-
if (
|
|
1347
|
-
!pathInfoRes.message?.exist ||
|
|
1348
|
-
pathInfoRes.message?.directory ||
|
|
1349
|
-
fileSize === undefined ||
|
|
1350
|
-
fileSize < PROTOCOL_V2_OKPP_HEADER_SIZE
|
|
1351
|
-
) {
|
|
1352
|
-
return null;
|
|
1353
|
-
}
|
|
968
|
+
// Write directly to the device through FileWrite.
|
|
969
|
+
let totalSize = 0;
|
|
970
|
+
for (const b of bundles) totalSize += b.binary.byteLength;
|
|
1354
971
|
|
|
1355
|
-
const
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
chunk_len: readLen,
|
|
1367
|
-
ui_percentage: undefined,
|
|
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,
|
|
1368
983
|
});
|
|
1369
|
-
const data = toProtocolV2Bytes(res.message?.data);
|
|
1370
|
-
if (data.byteLength === 0) return null;
|
|
1371
|
-
chunks.push(data);
|
|
1372
|
-
offset += data.byteLength;
|
|
1373
984
|
}
|
|
1374
985
|
|
|
1375
|
-
const
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
return
|
|
1382
|
-
}
|
|
1383
|
-
|
|
1384
|
-
/** Compare a prepared okpkg header when its manifest supplies version or hash metadata. */
|
|
1385
|
-
private async isProtocolV2ResourceBundleUpToDate(
|
|
1386
|
-
bundle: Pick<
|
|
1387
|
-
ProtocolV2ResourceBundleSource,
|
|
1388
|
-
'name' | 'devicePath' | 'version' | 'payloadHash' | 'headerHash'
|
|
1389
|
-
>
|
|
1390
|
-
): Promise<boolean> {
|
|
1391
|
-
if (this.params?.forcedUpdateRes) return false;
|
|
1392
|
-
if (!bundle.version && !bundle.payloadHash) return false;
|
|
1393
|
-
|
|
1394
|
-
try {
|
|
1395
|
-
const header = await this.readProtocolV2DeviceFileHeader(bundle.devicePath);
|
|
1396
|
-
if (!header) return false;
|
|
1397
|
-
|
|
1398
|
-
if (bundle.version) {
|
|
1399
|
-
const cmp = compareProtocolV2Versions(header.version, bundle.version);
|
|
1400
|
-
if (cmp === undefined || cmp !== 0) return false;
|
|
1401
|
-
}
|
|
1402
|
-
if (bundle.payloadHash) {
|
|
1403
|
-
const expected = normalizeProtocolV2Hex(bundle.payloadHash);
|
|
1404
|
-
if (expected && header.payloadHash !== expected) return false;
|
|
1405
|
-
}
|
|
1406
|
-
if (bundle.headerHash) {
|
|
1407
|
-
const expected = normalizeProtocolV2Hex(bundle.headerHash);
|
|
1408
|
-
if (expected && header.headerHash !== expected) return false;
|
|
1409
|
-
}
|
|
1410
|
-
return true;
|
|
1411
|
-
} catch (error) {
|
|
1412
|
-
Log.log(`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check failed: `, error);
|
|
1413
|
-
return false;
|
|
1414
|
-
}
|
|
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 };
|
|
1415
993
|
}
|
|
1416
994
|
|
|
1417
995
|
private isProtocolV2BootloaderMode() {
|
|
@@ -1443,12 +1021,10 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1443
1021
|
// It rejects DeviceRebootType.Bootloader, so reuse the current connection.
|
|
1444
1022
|
if (this.isProtocolV2RomloaderMode()) {
|
|
1445
1023
|
Log.debug('Protocol V2 device is in romloader mode; start firmware update directly');
|
|
1446
|
-
this.protocolV2ExecutionInLoader = true;
|
|
1447
1024
|
return false;
|
|
1448
1025
|
}
|
|
1449
1026
|
if (this.isProtocolV2BootloaderMode()) {
|
|
1450
1027
|
Log.debug('Protocol V2 device is already in bootloader mode, skip reboot');
|
|
1451
|
-
this.protocolV2ExecutionInLoader = true;
|
|
1452
1028
|
this.postTipMessage(FirmwareUpdateTipMessage.GoToBootloaderSuccess);
|
|
1453
1029
|
return false;
|
|
1454
1030
|
}
|
|
@@ -1458,7 +1034,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1458
1034
|
await this.protocolV2Reboot(DeviceRebootType.Bootloader);
|
|
1459
1035
|
await wait(1000);
|
|
1460
1036
|
await this.waitForProtocolV2BootloaderMode();
|
|
1461
|
-
this.protocolV2ExecutionInLoader = true;
|
|
1462
1037
|
this.postTipMessage(FirmwareUpdateTipMessage.GoToBootloaderSuccess);
|
|
1463
1038
|
return true;
|
|
1464
1039
|
} catch (error) {
|
|
@@ -1554,98 +1129,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1554
1129
|
return entries;
|
|
1555
1130
|
}
|
|
1556
1131
|
|
|
1557
|
-
private buildProtocolV2ExecutionPhases({
|
|
1558
|
-
installSources,
|
|
1559
|
-
resourceSources,
|
|
1560
|
-
}: {
|
|
1561
|
-
installSources: ProtocolV2InstallSource[];
|
|
1562
|
-
resourceSources: ProtocolV2ResourceBundleSource[];
|
|
1563
|
-
}): ProtocolV2ExecutionPhase[] {
|
|
1564
|
-
const phases: ProtocolV2ExecutionPhase[] = [];
|
|
1565
|
-
if (resourceSources.length > 0) {
|
|
1566
|
-
phases.push({
|
|
1567
|
-
kind: 'resource-sync',
|
|
1568
|
-
installSources: [],
|
|
1569
|
-
resourceSources,
|
|
1570
|
-
});
|
|
1571
|
-
}
|
|
1572
|
-
const bootloaderSources = installSources.filter(source => source.kind === 'bootloader');
|
|
1573
|
-
if (bootloaderSources.length > 0) {
|
|
1574
|
-
phases.push(
|
|
1575
|
-
{
|
|
1576
|
-
kind: 'bootloader-install',
|
|
1577
|
-
installSources: bootloaderSources,
|
|
1578
|
-
resourceSources: [],
|
|
1579
|
-
},
|
|
1580
|
-
{
|
|
1581
|
-
kind: 'bootloader-verify',
|
|
1582
|
-
installSources: [],
|
|
1583
|
-
resourceSources: [],
|
|
1584
|
-
}
|
|
1585
|
-
);
|
|
1586
|
-
}
|
|
1587
|
-
const componentSources = installSources.filter(source => source.kind !== 'bootloader');
|
|
1588
|
-
if (componentSources.length > 0) {
|
|
1589
|
-
phases.push({
|
|
1590
|
-
kind: 'component-install',
|
|
1591
|
-
installSources: componentSources,
|
|
1592
|
-
resourceSources: [],
|
|
1593
|
-
});
|
|
1594
|
-
}
|
|
1595
|
-
phases.push({
|
|
1596
|
-
kind: 'final-verify',
|
|
1597
|
-
installSources: [],
|
|
1598
|
-
resourceSources: [],
|
|
1599
|
-
});
|
|
1600
|
-
return phases;
|
|
1601
|
-
}
|
|
1602
|
-
|
|
1603
|
-
private async executeProtocolV2Phases({
|
|
1604
|
-
installSources,
|
|
1605
|
-
resourceSources,
|
|
1606
|
-
}: {
|
|
1607
|
-
installSources: ProtocolV2InstallSource[];
|
|
1608
|
-
resourceSources: ProtocolV2ResourceBundleSource[];
|
|
1609
|
-
}) {
|
|
1610
|
-
const phases = this.buildProtocolV2ExecutionPhases({
|
|
1611
|
-
installSources,
|
|
1612
|
-
resourceSources,
|
|
1613
|
-
});
|
|
1614
|
-
for (const phase of phases) {
|
|
1615
|
-
if (phase.kind === 'final-verify') {
|
|
1616
|
-
return this.completeProtocolV2FinalVerification();
|
|
1617
|
-
}
|
|
1618
|
-
if (phase.kind === 'resource-sync') {
|
|
1619
|
-
await this.enterProtocolV2BootloaderMode();
|
|
1620
|
-
await this.executeProtocolV2TransferPhase(phase);
|
|
1621
|
-
} else if (phase.kind === 'bootloader-install' || phase.kind === 'component-install') {
|
|
1622
|
-
await this.enterProtocolV2BootloaderMode();
|
|
1623
|
-
await this.executeProtocolV2TransferPhase(phase);
|
|
1624
|
-
await this.exitProtocolV2BootloaderToNormal();
|
|
1625
|
-
} else if (phase.kind === 'bootloader-verify') {
|
|
1626
|
-
await this.waitForProtocolV2FinalFeatures();
|
|
1627
|
-
this.assertExpectedProtocolV2Versions(['boot']);
|
|
1628
|
-
}
|
|
1629
|
-
}
|
|
1630
|
-
throw ERRORS.TypedError(
|
|
1631
|
-
HardwareErrorCode.RuntimeError,
|
|
1632
|
-
'Protocol V2 execution has no final verification phase'
|
|
1633
|
-
);
|
|
1634
|
-
}
|
|
1635
|
-
|
|
1636
|
-
private async executeProtocolV2SourceUpdate({
|
|
1637
|
-
installSources,
|
|
1638
|
-
resourceSources,
|
|
1639
|
-
}: {
|
|
1640
|
-
installSources: ProtocolV2InstallSource[];
|
|
1641
|
-
resourceSources: ProtocolV2ResourceBundleSource[];
|
|
1642
|
-
}) {
|
|
1643
|
-
return this.executeProtocolV2Phases({
|
|
1644
|
-
installSources,
|
|
1645
|
-
resourceSources,
|
|
1646
|
-
});
|
|
1647
|
-
}
|
|
1648
|
-
|
|
1649
1132
|
private async executeProtocolV2Update({
|
|
1650
1133
|
fwBinaryMap,
|
|
1651
1134
|
bootloaderBinary,
|
|
@@ -1657,168 +1140,104 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1657
1140
|
installItems?: ProtocolV2InstallItem[];
|
|
1658
1141
|
resourceBundles?: ProtocolV2ResourceBundleBinary[];
|
|
1659
1142
|
}) {
|
|
1660
|
-
const
|
|
1143
|
+
const orderedInstallItems =
|
|
1661
1144
|
installItems ??
|
|
1662
1145
|
this.buildProtocolV2InstallItems({
|
|
1663
|
-
fwBinaryMap: fwBinaryMap ?? [],
|
|
1664
1146
|
bootloaderBinary: bootloaderBinary ?? null,
|
|
1147
|
+
fwBinaryMap: fwBinaryMap ?? [],
|
|
1665
1148
|
});
|
|
1666
|
-
try {
|
|
1667
|
-
const installSources = await Promise.all(
|
|
1668
|
-
memoryInstallItems.map(async item => ({
|
|
1669
|
-
fileName: item.fileName,
|
|
1670
|
-
source: await this.openProtocolV2MemorySource(item.binary),
|
|
1671
|
-
targetId: item.targetId,
|
|
1672
|
-
kind: item.kind,
|
|
1673
|
-
}))
|
|
1674
|
-
);
|
|
1675
|
-
const resourceSources = await Promise.all(
|
|
1676
|
-
(resourceBundles ?? []).map(async bundle => ({
|
|
1677
|
-
name: bundle.name,
|
|
1678
|
-
source: await this.openProtocolV2MemorySource(bundle.binary),
|
|
1679
|
-
devicePath: bundle.devicePath,
|
|
1680
|
-
}))
|
|
1681
|
-
);
|
|
1682
|
-
return await this.executeProtocolV2Phases({
|
|
1683
|
-
installSources,
|
|
1684
|
-
resourceSources,
|
|
1685
|
-
});
|
|
1686
|
-
} finally {
|
|
1687
|
-
await this.closeProtocolV2PreparedSources();
|
|
1688
|
-
}
|
|
1689
|
-
}
|
|
1690
1149
|
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
resourceSources,
|
|
1694
|
-
}: ProtocolV2ExecutionPhase) {
|
|
1695
|
-
let totalSize = installSources.reduce((total, item) => total + item.source.size, 0);
|
|
1696
|
-
const resourcesToSync: ProtocolV2ResourceBundleSource[] = [];
|
|
1697
|
-
for (const resource of resourceSources) {
|
|
1698
|
-
if (await this.isProtocolV2ResourceBundleUpToDate(resource)) {
|
|
1699
|
-
Log.log(`[FirmwareUpdateV4] skip RESC bundle ${resource.name}; already up to date`);
|
|
1700
|
-
} else {
|
|
1701
|
-
resourcesToSync.push(resource);
|
|
1702
|
-
totalSize += resource.source.size;
|
|
1703
|
-
}
|
|
1704
|
-
}
|
|
1150
|
+
let firmwareSize = 0;
|
|
1151
|
+
for (const item of orderedInstallItems) firmwareSize += item.binary.byteLength;
|
|
1705
1152
|
|
|
1706
1153
|
this.postTipMessage(FirmwareUpdateTipMessage.StartTransferData);
|
|
1154
|
+
|
|
1707
1155
|
let processedSize = 0;
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
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;
|
|
1716
1164
|
}
|
|
1717
1165
|
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
totalSize,
|
|
1726
|
-
});
|
|
1727
|
-
await this.verifyProtocolV2StagedFile(filePath, item.source.size);
|
|
1728
|
-
stagedInstallTargets.push({
|
|
1729
|
-
targetId: item.targetId,
|
|
1730
|
-
path: filePath,
|
|
1731
|
-
});
|
|
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;
|
|
1732
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
|
+
);
|
|
1733
1184
|
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
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;
|
|
1739
1227
|
}
|
|
1740
1228
|
|
|
1741
1229
|
this.postTipMessage(FirmwareUpdateTipMessage.ConfirmOnDevice);
|
|
1742
|
-
|
|
1230
|
+
|
|
1231
|
+
const allTargets = stagedInstallTargets.map(item => ({
|
|
1743
1232
|
target_id: item.targetId,
|
|
1744
1233
|
path: item.path,
|
|
1745
1234
|
}));
|
|
1746
|
-
|
|
1747
|
-
await this.
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
private async protocolV2SourceUpdateProcess({
|
|
1751
|
-
source,
|
|
1752
|
-
filePath,
|
|
1753
|
-
processedSize,
|
|
1754
|
-
totalSize,
|
|
1755
|
-
}: {
|
|
1756
|
-
source: FirmwareByteSource;
|
|
1757
|
-
filePath: string;
|
|
1758
|
-
processedSize: number;
|
|
1759
|
-
totalSize: number;
|
|
1760
|
-
}) {
|
|
1761
|
-
let lastError: unknown;
|
|
1762
|
-
for (let attempt = 1; attempt <= PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT; attempt += 1) {
|
|
1763
|
-
try {
|
|
1764
|
-
const transferStartedAt = Date.now();
|
|
1765
|
-
await writeFirmwareByteSource({
|
|
1766
|
-
source,
|
|
1767
|
-
chunkSize: this.getProtocolV2FirmwareChunkSize(),
|
|
1768
|
-
write: async ({ data, sourceOffset, length, first }) => {
|
|
1769
|
-
const chunkEnd = sourceOffset + length;
|
|
1770
|
-
const deviceProgress = getProtocolV2DeviceTransferProgress(
|
|
1771
|
-
processedSize + sourceOffset,
|
|
1772
|
-
processedSize + chunkEnd,
|
|
1773
|
-
totalSize
|
|
1774
|
-
);
|
|
1775
|
-
const response = await this.fileWriteChunk(
|
|
1776
|
-
filePath,
|
|
1777
|
-
source.size,
|
|
1778
|
-
sourceOffset,
|
|
1779
|
-
data,
|
|
1780
|
-
first,
|
|
1781
|
-
deviceProgress
|
|
1782
|
-
);
|
|
1783
|
-
const rawProcessedByte = response.message.processed_byte;
|
|
1784
|
-
const nextOffset = rawProcessedByte === undefined ? chunkEnd : Number(rawProcessedByte);
|
|
1785
|
-
if (!Number.isFinite(nextOffset) || nextOffset !== chunkEnd) {
|
|
1786
|
-
throw ERRORS.TypedError(
|
|
1787
|
-
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
1788
|
-
`invalid processed_byte ${rawProcessedByte} for offset ${sourceOffset}`
|
|
1789
|
-
);
|
|
1790
|
-
}
|
|
1791
|
-
const transferredBytes = processedSize + chunkEnd;
|
|
1792
|
-
const elapsedMs = Math.max(Date.now() - transferStartedAt, 0);
|
|
1793
|
-
this.postProgressMessage(
|
|
1794
|
-
Math.min(Math.ceil((transferredBytes / totalSize) * 100), 99),
|
|
1795
|
-
'transferData',
|
|
1796
|
-
{
|
|
1797
|
-
transferredBytes,
|
|
1798
|
-
totalBytes: totalSize,
|
|
1799
|
-
rateBytesPerSecond:
|
|
1800
|
-
elapsedMs > 0 ? Math.round((chunkEnd / elapsedMs) * 1000) : undefined,
|
|
1801
|
-
elapsedMs,
|
|
1802
|
-
}
|
|
1803
|
-
);
|
|
1804
|
-
return length;
|
|
1805
|
-
},
|
|
1806
|
-
});
|
|
1807
|
-
return processedSize + source.size;
|
|
1808
|
-
} catch (error) {
|
|
1809
|
-
lastError = error;
|
|
1810
|
-
if (attempt < PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT) {
|
|
1811
|
-
await this.recoverProtocolV2FileTransfer();
|
|
1812
|
-
}
|
|
1813
|
-
}
|
|
1814
|
-
}
|
|
1815
|
-
throw ERRORS.TypedError(
|
|
1816
|
-
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
1817
|
-
`transfer data error: ${getProtocolV2UnknownErrorText(lastError)}`
|
|
1818
|
-
);
|
|
1235
|
+
Log.log(`[FirmwareUpdateV4] DeviceFirmwareUpdateRequest targets=${JSON.stringify(allTargets)}`);
|
|
1236
|
+
await this.protocolV2StartFirmwareUpdate({ targets: allTargets });
|
|
1237
|
+
await this.waitForProtocolV2FirmwareUpdateComplete(allTargets);
|
|
1819
1238
|
}
|
|
1820
1239
|
|
|
1821
|
-
private getProtocolV2InstallItemStagingPath(item:
|
|
1240
|
+
private getProtocolV2InstallItemStagingPath(item: ProtocolV2InstallItem) {
|
|
1822
1241
|
return `${PROTOCOL_V2_FIRMWARE_STAGING_VOLUME}${item.fileName}`;
|
|
1823
1242
|
}
|
|
1824
1243
|
|
|
@@ -1837,8 +1256,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1837
1256
|
|
|
1838
1257
|
private assertProtocolV2TargetStatus(
|
|
1839
1258
|
statusTargets: ProtocolV2FirmwareUpdateStatusTarget[],
|
|
1840
|
-
expectedTargetIds: Set<number
|
|
1841
|
-
expectedPaths = new Map<number, string>()
|
|
1259
|
+
expectedTargetIds: Set<number>
|
|
1842
1260
|
) {
|
|
1843
1261
|
Log.log(
|
|
1844
1262
|
`[FirmwareUpdateV4] DeviceFirmwareUpdateStatus records=${JSON.stringify(statusTargets)}`
|
|
@@ -1869,57 +1287,17 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1869
1287
|
const matchingTargets = statusTargets.filter(target =>
|
|
1870
1288
|
expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1)
|
|
1871
1289
|
);
|
|
1872
|
-
const seenTargetIds = new Set<number>();
|
|
1873
|
-
for (const target of matchingTargets) {
|
|
1874
|
-
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
1875
|
-
if (
|
|
1876
|
-
targetId === undefined ||
|
|
1877
|
-
seenTargetIds.has(targetId) ||
|
|
1878
|
-
(target.path && expectedPaths.get(targetId) && target.path !== expectedPaths.get(targetId))
|
|
1879
|
-
) {
|
|
1880
|
-
throw ERRORS.TypedError(
|
|
1881
|
-
HardwareErrorCode.RuntimeError,
|
|
1882
|
-
`Protocol V2 install status conflicts with target ${target.target_id}`,
|
|
1883
|
-
{
|
|
1884
|
-
firmwareUpdateCode: PROTOCOL_V2_INSTALL_STATUS_CONFLICT_CODE,
|
|
1885
|
-
}
|
|
1886
|
-
);
|
|
1887
|
-
}
|
|
1888
|
-
seenTargetIds.add(targetId);
|
|
1889
|
-
}
|
|
1890
|
-
const completedTargets = matchingTargets.filter(target =>
|
|
1891
|
-
isProtocolV2TargetStatusFinished(target.status)
|
|
1892
|
-
);
|
|
1893
1290
|
const completedTargetIds = new Set<number>();
|
|
1894
|
-
|
|
1291
|
+
matchingTargets.forEach(target => {
|
|
1895
1292
|
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
1896
|
-
if (targetId !== undefined) {
|
|
1293
|
+
if (targetId !== undefined && isProtocolV2TargetStatusFinished(target.status)) {
|
|
1897
1294
|
completedTargetIds.add(targetId);
|
|
1898
1295
|
}
|
|
1899
1296
|
});
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
if (targetId === undefined) {
|
|
1905
|
-
throw ERRORS.TypedError(
|
|
1906
|
-
HardwareErrorCode.RuntimeError,
|
|
1907
|
-
'Protocol V2 completed target identity is invalid'
|
|
1908
|
-
);
|
|
1909
|
-
}
|
|
1910
|
-
const expectedVersion = this.getExpectedProtocolV2TargetVersion(targetId);
|
|
1911
|
-
if (expectedVersion !== undefined && payloadVersion !== undefined) {
|
|
1912
|
-
if (payloadVersion !== expectedVersion) {
|
|
1913
|
-
throw ERRORS.TypedError(
|
|
1914
|
-
HardwareErrorCode.FirmwareVerificationFailed,
|
|
1915
|
-
`Protocol V2 target ${targetId} reached payload version ${payloadVersion}, expected ${expectedVersion}`
|
|
1916
|
-
);
|
|
1917
|
-
}
|
|
1918
|
-
}
|
|
1919
|
-
if (payloadVersion !== undefined) {
|
|
1920
|
-
this.protocolV2CompletedTargetVersions.set(targetId, payloadVersion);
|
|
1921
|
-
}
|
|
1922
|
-
}
|
|
1297
|
+
const allExpectedTargetsCompleted =
|
|
1298
|
+
expectedTargetIds.size > 0 &&
|
|
1299
|
+
Array.from(expectedTargetIds).every(targetId => completedTargetIds.has(targetId));
|
|
1300
|
+
if (allExpectedTargetsCompleted) {
|
|
1923
1301
|
this.postProgressMessage(100, 'installingFirmware');
|
|
1924
1302
|
return true;
|
|
1925
1303
|
}
|
|
@@ -1957,9 +1335,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1957
1335
|
private async waitForProtocolV2FirmwareUpdateComplete(
|
|
1958
1336
|
targets: Array<{ target_id: number; path: string }>
|
|
1959
1337
|
) {
|
|
1960
|
-
this.protocolV2FinalStatusVerified = false;
|
|
1961
1338
|
const expectedTargetIds = new Set(targets.map(target => target.target_id));
|
|
1962
|
-
const expectedPaths = new Map(targets.map(target => [target.target_id, target.path]));
|
|
1963
1339
|
const startTime = Date.now();
|
|
1964
1340
|
let lastError: unknown;
|
|
1965
1341
|
let shouldReconnect = true;
|
|
@@ -1994,8 +1370,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1994
1370
|
);
|
|
1995
1371
|
const statusTargets = (statusResponse.message.records ??
|
|
1996
1372
|
[]) as ProtocolV2FirmwareUpdateStatusTarget[];
|
|
1997
|
-
if (this.assertProtocolV2TargetStatus(statusTargets, expectedTargetIds
|
|
1998
|
-
this.protocolV2FinalStatusVerified = true;
|
|
1373
|
+
if (this.assertProtocolV2TargetStatus(statusTargets, expectedTargetIds)) {
|
|
1999
1374
|
return;
|
|
2000
1375
|
}
|
|
2001
1376
|
|
|
@@ -2043,7 +1418,10 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2043
1418
|
lastError = new Error('Protocol V2 firmware targets are still installing');
|
|
2044
1419
|
}
|
|
2045
1420
|
} catch (error) {
|
|
2046
|
-
if (
|
|
1421
|
+
if (
|
|
1422
|
+
error instanceof HardwareError &&
|
|
1423
|
+
error.errorCode === HardwareErrorCode.FirmwareError
|
|
1424
|
+
) {
|
|
2047
1425
|
throw error;
|
|
2048
1426
|
}
|
|
2049
1427
|
// Only consecutive full status dumps can prove that a record is absent.
|
|
@@ -2083,13 +1461,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2083
1461
|
if (this.isProtocolV2ReconnectIdentityError(error)) {
|
|
2084
1462
|
throw error;
|
|
2085
1463
|
}
|
|
2086
|
-
if (
|
|
2087
|
-
throw error;
|
|
2088
|
-
}
|
|
2089
|
-
if (
|
|
2090
|
-
error instanceof HardwareError &&
|
|
2091
|
-
error.params?.firmwareUpdateCode === 'FirmwareInstallStatusUnavailable'
|
|
2092
|
-
) {
|
|
1464
|
+
if (error instanceof HardwareError && error.errorCode === HardwareErrorCode.FirmwareError) {
|
|
2093
1465
|
throw error;
|
|
2094
1466
|
}
|
|
2095
1467
|
shouldReconnect = true;
|
|
@@ -2120,7 +1492,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2120
1492
|
Log.log('[FirmwareUpdateV4] unable to confirm App mode before Normal reboot: ', error);
|
|
2121
1493
|
}
|
|
2122
1494
|
await this.protocolV2Reboot(DeviceRebootType.Normal);
|
|
2123
|
-
this.protocolV2ExecutionInLoader = false;
|
|
2124
1495
|
}
|
|
2125
1496
|
|
|
2126
1497
|
private async probeProtocolV2NormalMode(deviceInfo: ProtocolV2DeviceInfo) {
|
|
@@ -2143,7 +1514,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2143
1514
|
const bootloaderVersion = getDeviceBootloaderVersion(features).join('.');
|
|
2144
1515
|
const bleVersion = getDeviceBLEFirmwareVersion(features).join('.');
|
|
2145
1516
|
const firmwareVersion = getDeviceFirmwareVersion(features).join('.');
|
|
2146
|
-
this.protocolV2LatestFinalFeatures = features;
|
|
2147
1517
|
if (firmwareVersion === '0.0.0') {
|
|
2148
1518
|
Log.warn(
|
|
2149
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.'
|
|
@@ -2157,17 +1527,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2157
1527
|
};
|
|
2158
1528
|
}
|
|
2159
1529
|
|
|
2160
|
-
private async completeProtocolV2FinalVerification() {
|
|
2161
|
-
if (this.protocolV2ExecutionInLoader || this.isProtocolV2BootloaderMode()) {
|
|
2162
|
-
await this.exitProtocolV2BootloaderToNormal();
|
|
2163
|
-
}
|
|
2164
|
-
const versions = await this.waitForProtocolV2FinalFeatures();
|
|
2165
|
-
this.assertExpectedProtocolV2Versions();
|
|
2166
|
-
this.postTipMessage(FirmwareUpdateTipMessage.FirmwareUpdateCompleted);
|
|
2167
|
-
DevicePool.resetState();
|
|
2168
|
-
return versions;
|
|
2169
|
-
}
|
|
2170
|
-
|
|
2171
1530
|
private async waitForProtocolV2ReconnectAndFeatures(timeout: number) {
|
|
2172
1531
|
const startTime = Date.now();
|
|
2173
1532
|
let lastError: unknown;
|
|
@@ -2289,43 +1648,106 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2289
1648
|
}
|
|
2290
1649
|
|
|
2291
1650
|
private isProtocolV2ReconnectIdentityError(error: unknown) {
|
|
2292
|
-
|
|
1651
|
+
// 序列号在 Loader 刚启动时可能暂时不可用,BLE descriptor 也没有稳定的
|
|
1652
|
+
// path。此时应继续轮询,只有明确读到不同序列号时才立即终止更新。
|
|
1653
|
+
return this.normalizeErrorMessage(error).includes(
|
|
1654
|
+
'Protocol V2 reconnect physical identity mismatch'
|
|
1655
|
+
);
|
|
2293
1656
|
}
|
|
2294
1657
|
|
|
2295
|
-
private async
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
file: {
|
|
2309
|
-
path: filePath,
|
|
2310
|
-
offset,
|
|
2311
|
-
total_size: totalFileSize,
|
|
2312
|
-
data: chunk,
|
|
2313
|
-
},
|
|
2314
|
-
overwrite,
|
|
2315
|
-
append: false,
|
|
2316
|
-
ui_percentage: progress ?? undefined,
|
|
2317
|
-
},
|
|
2318
|
-
{ writeWithResponse: true }
|
|
2319
|
-
);
|
|
2320
|
-
if (writeRes.type !== 'FilesystemFile') {
|
|
2321
|
-
if ((writeRes as any).type === 'CallMethodError') {
|
|
2322
|
-
if (((writeRes as any).message.error ?? '').indexOf(SESSION_ERROR) > -1) {
|
|
2323
|
-
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;
|
|
2324
1671
|
}
|
|
1672
|
+
await this.recoverProtocolV2FileTransfer();
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
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);
|
|
2325
1693
|
}
|
|
2326
|
-
|
|
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';
|
|
2327
1749
|
}
|
|
2328
|
-
return
|
|
1750
|
+
return env ?? 'unknown';
|
|
2329
1751
|
}
|
|
2330
1752
|
|
|
2331
1753
|
private async recoverProtocolV2FileTransfer() {
|