@onekeyfe/hd-core 1.2.0-alpha.112 → 1.2.0-alpha.113
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__/DeviceCommands.test.ts +2 -2
- package/__tests__/device-lifecycle-events.test.ts +1 -1
- package/__tests__/device-state-mapper.test.ts +4 -4
- package/__tests__/device-utils.test.ts +1 -1
- package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +116 -0
- package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +89 -36
- package/__tests__/get-device-state.test.ts +8 -8
- package/__tests__/protocol-v2-resources.test.ts +0 -20
- package/__tests__/protocol-v2.test.ts +240 -219
- package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +3 -0
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceInfoGet.d.ts +1 -1
- package/dist/api/protocol-v2/DeviceInfoGet.d.ts.map +1 -1
- package/dist/index.d.ts +20 -5
- package/dist/index.js +230 -140
- package/dist/protocols/protocol-v2/features.d.ts +4 -4
- package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -1
- package/dist/types/settings.d.ts +19 -4
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/patch.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/FirmwareUpdateV3.ts +29 -19
- package/src/api/FirmwareUpdateV4.ts +178 -85
- package/src/api/protocol-v2/DeviceFactoryInfoSet.ts +1 -1
- package/src/api/protocol-v2/DeviceInfoGet.ts +3 -3
- package/src/data/messages/messages-protocol-v2.json +14 -31
- package/src/device/DeviceStateMapper.ts +15 -15
- package/src/deviceProfile/buildDeviceFeatures.ts +3 -3
- package/src/protocols/protocol-v2/features.ts +6 -6
- package/src/protocols/protocol-v2/resources.ts +49 -10
- package/src/protocols/protocol-v2/unlockPolicyRunner.ts +0 -1
- package/src/types/settings.ts +19 -4
|
@@ -28,6 +28,7 @@ import { DevicePool } from '../device/DevicePool';
|
|
|
28
28
|
import {
|
|
29
29
|
PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST,
|
|
30
30
|
ProtocolV2FirmwareTargetType,
|
|
31
|
+
isLegacyProtocolV2ProtocolInfo,
|
|
31
32
|
} from '../protocols/protocol-v2';
|
|
32
33
|
import { requestProtocolV2DeviceInfo } from '../protocols/protocol-v2/features';
|
|
33
34
|
import {
|
|
@@ -69,7 +70,6 @@ import type {
|
|
|
69
70
|
Features,
|
|
70
71
|
IFirmwareReleaseInfo,
|
|
71
72
|
IProtocolV2FirmwareComponent,
|
|
72
|
-
IProtocolV2ResourceManifestFile,
|
|
73
73
|
IVersionArray,
|
|
74
74
|
} from '../types';
|
|
75
75
|
import type { FirmwareByteSource } from './firmware/FirmwareArtifactSource';
|
|
@@ -89,6 +89,7 @@ const PROTOCOL_V2_BOOTLOADER_RECONNECT_TIMEOUT = 90 * 1000;
|
|
|
89
89
|
const PROTOCOL_V2_FINAL_RECONNECT_TIMEOUT = 3 * 60 * 1000;
|
|
90
90
|
const PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT = 5 * 1000;
|
|
91
91
|
const PROTOCOL_V2_FIRMWARE_STATUS_RESPONSE_TIMEOUT = 15 * 1000;
|
|
92
|
+
const PROTOCOL_V2_START_UPDATE_TIMEOUT = 3 * 60 * 1000;
|
|
92
93
|
const PROTOCOL_V2_INSTALL_TIMEOUT = 8 * 60 * 1000;
|
|
93
94
|
const PROTOCOL_V2_MISSING_TARGET_STATUS_GRACE_TIMEOUT = 30 * 1000;
|
|
94
95
|
const PROTOCOL_V2_TARGET_STATUS_PENDING = 0;
|
|
@@ -112,13 +113,6 @@ const PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES = 1024 * 1024;
|
|
|
112
113
|
const PROTOCOL_V2_RESOURCE_FILE_MAX_COUNT = 512;
|
|
113
114
|
const PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES = 256 * 1024 * 1024;
|
|
114
115
|
|
|
115
|
-
const getProtocolV2LocalResourceArchivePath = (entryName: string) => {
|
|
116
|
-
const match = entryName.match(
|
|
117
|
-
/(?:^|\/)((?:bundles\/|loaders\/(?:bootloader|rom)\/).+\.okpkg)$/iu
|
|
118
|
-
);
|
|
119
|
-
return match?.[1];
|
|
120
|
-
};
|
|
121
|
-
|
|
122
116
|
const PROTOCOL_V2_NEO_UNSUPPORTED_TARGETS = new Set<FirmwareUpdateV4Target>(['se03', 'se04']);
|
|
123
117
|
|
|
124
118
|
const getProtocolV2ZipEntrySizes = (entry: JSZip.JSZipObject) => {
|
|
@@ -187,11 +181,14 @@ const getProtocolV2DeviceTransferProgress = (
|
|
|
187
181
|
totalBytes: number
|
|
188
182
|
) => {
|
|
189
183
|
if (!Number.isFinite(totalBytes) || totalBytes <= 0) {
|
|
190
|
-
return
|
|
184
|
+
return 100;
|
|
191
185
|
}
|
|
192
186
|
if (bytesBeforeChunk <= 0 && bytesAfterChunk < totalBytes) {
|
|
193
187
|
return 0;
|
|
194
188
|
}
|
|
189
|
+
if (bytesAfterChunk >= totalBytes) {
|
|
190
|
+
return 100;
|
|
191
|
+
}
|
|
195
192
|
return Math.min(Math.max(Math.ceil((bytesAfterChunk / totalBytes) * 100), 1), 99);
|
|
196
193
|
};
|
|
197
194
|
|
|
@@ -202,6 +199,8 @@ type ProtocolV2FirmwareUpdateStatusTarget = {
|
|
|
202
199
|
path?: string;
|
|
203
200
|
};
|
|
204
201
|
|
|
202
|
+
type ProtocolV2FirmwareUpdateStartResponse = TypedResponseMessage<'Success'>;
|
|
203
|
+
|
|
205
204
|
type ProtocolV2TargetBinary = { fileName: string; binary: ArrayBuffer; targetId: number };
|
|
206
205
|
type ProtocolV2InstallItem = ProtocolV2TargetBinary & {
|
|
207
206
|
kind: ProtocolV2RemoteComponentTarget['kind'];
|
|
@@ -378,6 +377,31 @@ const isProtocolV2ReconnectProbeError = (error: unknown) => {
|
|
|
378
377
|
);
|
|
379
378
|
};
|
|
380
379
|
|
|
380
|
+
const PROTOCOL_V2_BLE_INSTALL_INTERRUPTION_ERROR_CODES = new Set<number>([
|
|
381
|
+
HardwareErrorCode.BleConnectedError,
|
|
382
|
+
HardwareErrorCode.BleCharacteristicNotifyError,
|
|
383
|
+
HardwareErrorCode.BleForceCleanRunPromise,
|
|
384
|
+
HardwareErrorCode.BleDeviceDisconnected,
|
|
385
|
+
]);
|
|
386
|
+
|
|
387
|
+
const isProtocolV2BleInstallInterruptionError = (error: unknown) => {
|
|
388
|
+
if (
|
|
389
|
+
error instanceof HardwareError &&
|
|
390
|
+
PROTOCOL_V2_BLE_INSTALL_INTERRUPTION_ERROR_CODES.has(error.errorCode)
|
|
391
|
+
) {
|
|
392
|
+
return true;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const message = getProtocolV2UnknownErrorText(error).toLowerCase();
|
|
396
|
+
const compactMessage = message.replace(/\s+/gu, '');
|
|
397
|
+
return (
|
|
398
|
+
/react native ble transport (?:released|disconnected)/u.test(message) ||
|
|
399
|
+
(compactMessage.includes('rxerrorerror6') &&
|
|
400
|
+
(compactMessage.includes('multiplatformbleadapter') ||
|
|
401
|
+
compactMessage.includes('multipalformebleadapter')))
|
|
402
|
+
);
|
|
403
|
+
};
|
|
404
|
+
|
|
381
405
|
const isProtocolV2FirmwareStatusEndpointUnavailable = (error: unknown) => {
|
|
382
406
|
const message = getProtocolV2UnknownErrorText(error).toLowerCase();
|
|
383
407
|
return (
|
|
@@ -387,6 +411,15 @@ const isProtocolV2FirmwareStatusEndpointUnavailable = (error: unknown) => {
|
|
|
387
411
|
);
|
|
388
412
|
};
|
|
389
413
|
|
|
414
|
+
const isProtocolV2FirmwareUpdateEndpointUnavailable = (error: unknown) => {
|
|
415
|
+
const message = getProtocolV2UnknownErrorText(error).toLowerCase();
|
|
416
|
+
return (
|
|
417
|
+
message.includes('handler not registered') ||
|
|
418
|
+
message.includes('message handler not found') ||
|
|
419
|
+
message.includes('unsupported message')
|
|
420
|
+
);
|
|
421
|
+
};
|
|
422
|
+
|
|
390
423
|
const isProtocolV2TerminalInstallStatusError = (error: unknown) =>
|
|
391
424
|
error instanceof HardwareError &&
|
|
392
425
|
(error.errorCode === HardwareErrorCode.FirmwareError ||
|
|
@@ -563,7 +596,7 @@ export const assertProtocolV2ReconnectIdentity = (
|
|
|
563
596
|
*
|
|
564
597
|
* It intentionally does not fall back to FirmwareUpdateV3/V1 behavior:
|
|
565
598
|
* - upload uses FilesystemFileWrite
|
|
566
|
-
* - install uses
|
|
599
|
+
* - install uses DeviceFirmwareUpdateRequest
|
|
567
600
|
* - completion waits for target status to finish, reboots to normal, then polls DeviceInfo
|
|
568
601
|
*/
|
|
569
602
|
export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareUpdateV4Params> {
|
|
@@ -581,6 +614,8 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
581
614
|
|
|
582
615
|
private protocolV2ExecutionInLoader = false;
|
|
583
616
|
|
|
617
|
+
private protocolV2LegacyDirectUpdate = false;
|
|
618
|
+
|
|
584
619
|
private protocolV2BootResourceStagingSafe = false;
|
|
585
620
|
|
|
586
621
|
private protocolV2CompletedTargetVersions = new Map<number, number>();
|
|
@@ -589,6 +624,8 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
589
624
|
|
|
590
625
|
private protocolV2FinalStatusVerified = false;
|
|
591
626
|
|
|
627
|
+
private protocolV2InstallAckReceived = false;
|
|
628
|
+
|
|
592
629
|
private protocolV2InstallBaselineVersions = new Map<number, string>();
|
|
593
630
|
|
|
594
631
|
private protocolV2LastRuntimeProbeFeatures?: Features;
|
|
@@ -1256,86 +1293,92 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1256
1293
|
);
|
|
1257
1294
|
}
|
|
1258
1295
|
const entries = zipEntries.filter(entry => !entry.dir);
|
|
1259
|
-
if (entries.length === 0) {
|
|
1296
|
+
if (entries.length === 0 || entries.length > PROTOCOL_V2_RESOURCE_FILE_MAX_COUNT + 1) {
|
|
1260
1297
|
throw ERRORS.TypedError(
|
|
1261
1298
|
HardwareErrorCode.RuntimeError,
|
|
1262
1299
|
'Protocol V2 local resource ZIP entry set is invalid',
|
|
1263
1300
|
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1264
1301
|
);
|
|
1265
1302
|
}
|
|
1266
|
-
|
|
1267
|
-
let
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1303
|
+
let declaredUncompressedSize = 0;
|
|
1304
|
+
let declaredCompressedSize = 0;
|
|
1305
|
+
for (const entry of entries) {
|
|
1306
|
+
const sizes = getProtocolV2ZipEntrySizes(entry);
|
|
1307
|
+
declaredCompressedSize += sizes.compressedSize;
|
|
1308
|
+
declaredUncompressedSize += sizes.uncompressedSize;
|
|
1309
|
+
const entryLimit =
|
|
1310
|
+
entry.name === 'manifest.json'
|
|
1311
|
+
? PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES
|
|
1312
|
+
: PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES;
|
|
1271
1313
|
if (
|
|
1272
|
-
|
|
1273
|
-
|
|
1314
|
+
sizes.uncompressedSize > entryLimit ||
|
|
1315
|
+
declaredCompressedSize > binary.byteLength ||
|
|
1316
|
+
declaredUncompressedSize >
|
|
1317
|
+
PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES + PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES
|
|
1274
1318
|
) {
|
|
1275
1319
|
throw ERRORS.TypedError(
|
|
1276
1320
|
HardwareErrorCode.RuntimeError,
|
|
1277
|
-
'Protocol V2 local resource
|
|
1321
|
+
'Protocol V2 local resource ZIP declared size exceeds the allowed limit',
|
|
1278
1322
|
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1279
1323
|
);
|
|
1280
1324
|
}
|
|
1281
|
-
manifestBinary = await manifestEntry.async('arraybuffer');
|
|
1282
|
-
if (
|
|
1283
|
-
manifestBinary.byteLength <= 0 ||
|
|
1284
|
-
manifestBinary.byteLength > PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES
|
|
1285
|
-
) {
|
|
1286
|
-
throw ERRORS.TypedError(
|
|
1287
|
-
HardwareErrorCode.RuntimeError,
|
|
1288
|
-
'Protocol V2 local resource manifest size is invalid',
|
|
1289
|
-
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1290
|
-
);
|
|
1291
|
-
}
|
|
1292
|
-
let manifestValue: unknown;
|
|
1293
|
-
try {
|
|
1294
|
-
manifestValue = JSON.parse(new TextDecoder().decode(manifestBinary));
|
|
1295
|
-
} catch (error) {
|
|
1296
|
-
throw ERRORS.TypedError(
|
|
1297
|
-
HardwareErrorCode.RuntimeError,
|
|
1298
|
-
`Protocol V2 local resource manifest is invalid: ${String(error)}`,
|
|
1299
|
-
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1300
|
-
);
|
|
1301
|
-
}
|
|
1302
|
-
selectedFiles = selectProtocolV2ResourceManifestFiles({
|
|
1303
|
-
manifest: parseProtocolV2ResourceManifest(manifestValue),
|
|
1304
|
-
targetsToUpdate: this.params.targetsToUpdate ?? [],
|
|
1305
|
-
});
|
|
1306
|
-
manifestDirectory = manifestEntry.name.slice(0, -'manifest.json'.length);
|
|
1307
|
-
} else {
|
|
1308
|
-
selectedFiles = entries.flatMap(entry => {
|
|
1309
|
-
const archivePath = getProtocolV2LocalResourceArchivePath(entry.name);
|
|
1310
|
-
if (!archivePath) return [];
|
|
1311
|
-
return [
|
|
1312
|
-
{
|
|
1313
|
-
archive_path: archivePath,
|
|
1314
|
-
original_name: archivePath.split('/').pop() ?? archivePath,
|
|
1315
|
-
device_path: `vol0:/${archivePath}`,
|
|
1316
|
-
size: getProtocolV2ZipEntrySizes(entry).uncompressedSize,
|
|
1317
|
-
sha256: '',
|
|
1318
|
-
},
|
|
1319
|
-
];
|
|
1320
|
-
});
|
|
1321
1325
|
}
|
|
1322
|
-
|
|
1326
|
+
const manifestEntry = zip.file('manifest.json');
|
|
1327
|
+
if (!manifestEntry) {
|
|
1328
|
+
throw ERRORS.TypedError(
|
|
1329
|
+
HardwareErrorCode.RuntimeError,
|
|
1330
|
+
'Protocol V2 local resource ZIP has no manifest.json',
|
|
1331
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1332
|
+
);
|
|
1333
|
+
}
|
|
1334
|
+
const manifestBinary = await manifestEntry.async('arraybuffer');
|
|
1335
|
+
if (
|
|
1336
|
+
manifestBinary.byteLength <= 0 ||
|
|
1337
|
+
manifestBinary.byteLength > PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES
|
|
1338
|
+
) {
|
|
1339
|
+
throw ERRORS.TypedError(
|
|
1340
|
+
HardwareErrorCode.RuntimeError,
|
|
1341
|
+
'Protocol V2 local resource manifest size is invalid',
|
|
1342
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1343
|
+
);
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
let manifestValue: unknown;
|
|
1347
|
+
try {
|
|
1348
|
+
manifestValue = JSON.parse(new TextDecoder().decode(manifestBinary));
|
|
1349
|
+
} catch (error) {
|
|
1350
|
+
throw ERRORS.TypedError(
|
|
1351
|
+
HardwareErrorCode.RuntimeError,
|
|
1352
|
+
`Protocol V2 local resource manifest is invalid: ${String(error)}`,
|
|
1353
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1354
|
+
);
|
|
1355
|
+
}
|
|
1356
|
+
const manifest = parseProtocolV2ResourceManifest(manifestValue);
|
|
1357
|
+
const selectedFiles = selectProtocolV2ResourceManifestFiles({
|
|
1358
|
+
manifest,
|
|
1359
|
+
targetsToUpdate: this.params.targetsToUpdate ?? [],
|
|
1360
|
+
});
|
|
1361
|
+
const expectedEntryNames = new Set([
|
|
1362
|
+
'manifest.json',
|
|
1363
|
+
...selectedFiles.map(file => file.archive_path),
|
|
1364
|
+
]);
|
|
1365
|
+
if (
|
|
1366
|
+
entries.length !== expectedEntryNames.size ||
|
|
1367
|
+
entries.some(entry => !expectedEntryNames.has(entry.name))
|
|
1368
|
+
) {
|
|
1323
1369
|
throw ERRORS.TypedError(
|
|
1324
1370
|
HardwareErrorCode.RuntimeError,
|
|
1325
|
-
'Protocol V2 local resource ZIP
|
|
1371
|
+
'Protocol V2 local resource ZIP contains an unexpected entry',
|
|
1326
1372
|
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
1327
1373
|
);
|
|
1328
1374
|
}
|
|
1329
1375
|
|
|
1330
1376
|
let totalSize = 0;
|
|
1331
|
-
const materializedEntries: FirmwareMemoryArtifactEntry[] = [
|
|
1332
|
-
|
|
1377
|
+
const materializedEntries: FirmwareMemoryArtifactEntry[] = [
|
|
1378
|
+
{ entryName: 'manifest.json', binary: manifestBinary },
|
|
1379
|
+
];
|
|
1333
1380
|
for (const file of selectedFiles) {
|
|
1334
|
-
const entry =
|
|
1335
|
-
? zip.file(`${manifestDirectory}${file.archive_path}`)
|
|
1336
|
-
: entries.find(
|
|
1337
|
-
candidate => getProtocolV2LocalResourceArchivePath(candidate.name) === file.archive_path
|
|
1338
|
-
);
|
|
1381
|
+
const entry = zip.file(file.archive_path);
|
|
1339
1382
|
if (!entry) {
|
|
1340
1383
|
throw ERRORS.TypedError(
|
|
1341
1384
|
HardwareErrorCode.RuntimeError,
|
|
@@ -1354,21 +1397,15 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1354
1397
|
}
|
|
1355
1398
|
const fileBinary = await entry.async('arraybuffer');
|
|
1356
1399
|
const digest = bytesToHex(sha256(new Uint8Array(fileBinary)));
|
|
1357
|
-
if (
|
|
1358
|
-
fileBinary.byteLength !== file.size ||
|
|
1359
|
-
(file.sha256 && digest !== file.sha256.toLowerCase())
|
|
1360
|
-
) {
|
|
1400
|
+
if (fileBinary.byteLength !== file.size || digest !== file.sha256.toLowerCase()) {
|
|
1361
1401
|
throw ERRORS.TypedError(
|
|
1362
1402
|
HardwareErrorCode.RuntimeError,
|
|
1363
1403
|
`Protocol V2 local resource file does not match manifest: ${file.archive_path}`,
|
|
1364
1404
|
{ firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch' }
|
|
1365
1405
|
);
|
|
1366
1406
|
}
|
|
1367
|
-
normalizedFiles.push({ ...file, sha256: digest });
|
|
1368
1407
|
materializedEntries.push({ entryName: file.archive_path, binary: fileBinary });
|
|
1369
1408
|
}
|
|
1370
|
-
manifestBinary ??= new TextEncoder().encode(JSON.stringify({ files: normalizedFiles })).buffer;
|
|
1371
|
-
materializedEntries.unshift({ entryName: 'manifest.json', binary: manifestBinary });
|
|
1372
1409
|
return { binary, materializedEntries };
|
|
1373
1410
|
}
|
|
1374
1411
|
|
|
@@ -2131,6 +2168,11 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2131
2168
|
return this.device.features?.mode === 'romloader';
|
|
2132
2169
|
}
|
|
2133
2170
|
|
|
2171
|
+
private isLegacyProtocolV2Runtime() {
|
|
2172
|
+
const protocolInfo = this.device.state?.raw?.protocolV2ProtocolInfo;
|
|
2173
|
+
return protocolInfo ? isLegacyProtocolV2ProtocolInfo(protocolInfo) : false;
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2134
2176
|
private async rebootProtocolV2ToBootloader() {
|
|
2135
2177
|
try {
|
|
2136
2178
|
this.postTipMessage(FirmwareUpdateTipMessage.AutoRebootToBootloader);
|
|
@@ -2150,15 +2192,18 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2150
2192
|
}
|
|
2151
2193
|
|
|
2152
2194
|
async enterProtocolV2BootloaderMode() {
|
|
2195
|
+
this.protocolV2LegacyDirectUpdate = false;
|
|
2153
2196
|
// romloader is the first update environment and forwards targets to bootloader.
|
|
2154
2197
|
// It rejects DeviceRebootType.Bootloader, so reuse the current connection.
|
|
2155
2198
|
if (this.isProtocolV2RomloaderMode()) {
|
|
2156
2199
|
Log.debug('Protocol V2 device is in romloader mode; start firmware update directly');
|
|
2200
|
+
this.protocolV2LegacyDirectUpdate = this.isLegacyProtocolV2Runtime();
|
|
2157
2201
|
this.protocolV2ExecutionInLoader = true;
|
|
2158
2202
|
return false;
|
|
2159
2203
|
}
|
|
2160
2204
|
if (this.isProtocolV2BootloaderMode()) {
|
|
2161
2205
|
Log.debug('Protocol V2 device is already in bootloader mode, skip reboot');
|
|
2206
|
+
this.protocolV2LegacyDirectUpdate = this.isLegacyProtocolV2Runtime();
|
|
2162
2207
|
this.protocolV2ExecutionInLoader = true;
|
|
2163
2208
|
this.postTipMessage(FirmwareUpdateTipMessage.GoToBootloaderSuccess);
|
|
2164
2209
|
return false;
|
|
@@ -2669,7 +2714,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2669
2714
|
let missingTargetStatusSince: number | undefined;
|
|
2670
2715
|
let missingTargetStatusKey: string | undefined;
|
|
2671
2716
|
let normalModeWithoutInstallEvidenceSince: number | undefined;
|
|
2672
|
-
let installEvidenceObserved =
|
|
2717
|
+
let installEvidenceObserved = this.protocolV2InstallAckReceived;
|
|
2673
2718
|
const resetMissingTargetStatusGrace = () => {
|
|
2674
2719
|
missingTargetStatusSince = undefined;
|
|
2675
2720
|
missingTargetStatusKey = undefined;
|
|
@@ -2689,7 +2734,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2689
2734
|
try {
|
|
2690
2735
|
const statusResponse = await this.device.getCommands().typedCall(
|
|
2691
2736
|
'DeviceFirmwareUpdateStatusGet',
|
|
2692
|
-
|
|
2737
|
+
'DeviceFirmwareUpdateStatus',
|
|
2693
2738
|
{
|
|
2694
2739
|
fields: {
|
|
2695
2740
|
status: true,
|
|
@@ -2699,11 +2744,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2699
2744
|
},
|
|
2700
2745
|
{ timeoutMs: PROTOCOL_V2_FIRMWARE_STATUS_RESPONSE_TIMEOUT }
|
|
2701
2746
|
);
|
|
2702
|
-
if (statusResponse.type === 'Success') {
|
|
2703
|
-
this.protocolV2FinalStatusVerified = true;
|
|
2704
|
-
this.postProgressMessage(100, 'installingFirmware');
|
|
2705
|
-
return;
|
|
2706
|
-
}
|
|
2707
2747
|
const statusTargets = (statusResponse.message.records ??
|
|
2708
2748
|
[]) as ProtocolV2FirmwareUpdateStatusTarget[];
|
|
2709
2749
|
if (
|
|
@@ -3108,12 +3148,65 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
3108
3148
|
}: {
|
|
3109
3149
|
targets: Array<{ target_id: number; path: string }>;
|
|
3110
3150
|
}) {
|
|
3151
|
+
this.protocolV2InstallAckReceived = false;
|
|
3111
3152
|
this.protocolV2LastRuntimeProbeFeatures = undefined;
|
|
3112
|
-
const
|
|
3113
|
-
|
|
3114
|
-
|
|
3153
|
+
const startUpdate = () =>
|
|
3154
|
+
this.device
|
|
3155
|
+
.getCommands()
|
|
3156
|
+
.typedCall(
|
|
3157
|
+
'DeviceFirmwareUpdateRequest',
|
|
3158
|
+
'Success',
|
|
3159
|
+
{ targets },
|
|
3160
|
+
{ timeoutMs: PROTOCOL_V2_START_UPDATE_TIMEOUT }
|
|
3161
|
+
);
|
|
3162
|
+
let response: ProtocolV2FirmwareUpdateStartResponse | undefined;
|
|
3163
|
+
try {
|
|
3164
|
+
response = await startUpdate();
|
|
3165
|
+
this.protocolV2InstallAckReceived = true;
|
|
3166
|
+
} catch (error) {
|
|
3167
|
+
if (this.isBleReconnect() && isProtocolV2BleInstallInterruptionError(error)) {
|
|
3168
|
+
this.throwIfAborted();
|
|
3169
|
+
// Installation can reboot the device before the Success response reaches
|
|
3170
|
+
// the host. The request has side effects, so do not replay it; reconnect
|
|
3171
|
+
// and let status polling determine whether installation was accepted.
|
|
3172
|
+
Log.log(
|
|
3173
|
+
'[FirmwareUpdateV4] install request interrupted by device reboot; continue status polling: ',
|
|
3174
|
+
error
|
|
3175
|
+
);
|
|
3176
|
+
} else if (
|
|
3177
|
+
this.protocolV2LegacyDirectUpdate &&
|
|
3178
|
+
isProtocolV2FirmwareUpdateEndpointUnavailable(error)
|
|
3179
|
+
) {
|
|
3180
|
+
// Both legacy loaders register this request. A missing handler therefore identifies
|
|
3181
|
+
// a legacy App before dispatch, so it is safe to reboot and retry the unhandled request.
|
|
3182
|
+
this.protocolV2LegacyDirectUpdate = false;
|
|
3183
|
+
Log.debug(
|
|
3184
|
+
'[FirmwareUpdateV4] legacy App does not expose DeviceFirmwareUpdateRequest; rebooting to bootloader'
|
|
3185
|
+
);
|
|
3186
|
+
await this.rebootProtocolV2ToBootloader();
|
|
3187
|
+
try {
|
|
3188
|
+
response = await startUpdate();
|
|
3189
|
+
this.protocolV2InstallAckReceived = true;
|
|
3190
|
+
} catch (retryError) {
|
|
3191
|
+
if (!(this.isBleReconnect() && isProtocolV2BleInstallInterruptionError(retryError))) {
|
|
3192
|
+
throw retryError;
|
|
3193
|
+
}
|
|
3194
|
+
this.throwIfAborted();
|
|
3195
|
+
Log.log(
|
|
3196
|
+
'[FirmwareUpdateV4] install request interrupted after legacy reboot; continue status polling: ',
|
|
3197
|
+
retryError
|
|
3198
|
+
);
|
|
3199
|
+
}
|
|
3200
|
+
} else {
|
|
3201
|
+
throw error;
|
|
3202
|
+
}
|
|
3203
|
+
}
|
|
3204
|
+
this.protocolV2LegacyDirectUpdate = false;
|
|
3205
|
+
// A Success ACK or an install-time disconnect both end confirmation. In the
|
|
3206
|
+
// latter case only status polling may establish the final outcome.
|
|
3115
3207
|
this.postTipMessage(FirmwareUpdateTipMessage.FirmwareUpdating);
|
|
3116
3208
|
this.postProgressMessage(0, 'installingFirmware');
|
|
3209
|
+
return response;
|
|
3117
3210
|
}
|
|
3118
3211
|
|
|
3119
3212
|
private async protocolV2Reboot(rebootType: DeviceRebootType) {
|
|
@@ -25,7 +25,7 @@ export default class DeviceFactoryInfoSet extends BaseMethod<DeviceFactoryInfoSe
|
|
|
25
25
|
info: {
|
|
26
26
|
version: this.params.version,
|
|
27
27
|
serial_number: this.params.serial_number,
|
|
28
|
-
|
|
28
|
+
burn_in_completed: this.params.burn_in_completed,
|
|
29
29
|
factory_test_completed: this.params.factory_test_completed,
|
|
30
30
|
manufacture_time: this.params.manufacture_time,
|
|
31
31
|
},
|
|
@@ -5,7 +5,7 @@ import { invalidParameter } from '../helpers/filesystemValidation';
|
|
|
5
5
|
|
|
6
6
|
export type DeviceInfoGetTargets = {
|
|
7
7
|
hw?: boolean;
|
|
8
|
-
|
|
8
|
+
fw?: boolean;
|
|
9
9
|
coprocessor?: boolean;
|
|
10
10
|
se1?: boolean;
|
|
11
11
|
se2?: boolean;
|
|
@@ -27,7 +27,7 @@ export type DeviceInfoGetParams = {
|
|
|
27
27
|
|
|
28
28
|
const TARGET_KEYS: (keyof DeviceInfoGetTargets)[] = [
|
|
29
29
|
'hw',
|
|
30
|
-
'
|
|
30
|
+
'fw',
|
|
31
31
|
'coprocessor',
|
|
32
32
|
'se1',
|
|
33
33
|
'se2',
|
|
@@ -39,7 +39,7 @@ const TYPE_KEYS: (keyof DeviceInfoGetTypes)[] = ['version', 'build_id', 'hash',
|
|
|
39
39
|
|
|
40
40
|
const DEFAULT_TARGETS: DeviceInfoGetTargets = {
|
|
41
41
|
hw: true,
|
|
42
|
-
|
|
42
|
+
fw: true,
|
|
43
43
|
coprocessor: true,
|
|
44
44
|
};
|
|
45
45
|
|
|
@@ -462,10 +462,9 @@
|
|
|
462
462
|
"MessageType_FilesystemDirMake": 60809,
|
|
463
463
|
"MessageType_FilesystemDirRemove": 60810,
|
|
464
464
|
"MessageType_FilesystemFormat": 60811,
|
|
465
|
-
"
|
|
466
|
-
"
|
|
467
|
-
"
|
|
468
|
-
"MessageType_DeviceFirmwareUpdateStatus": 61003,
|
|
465
|
+
"MessageType_DeviceFirmwareUpdateRequest": 61000,
|
|
466
|
+
"MessageType_DeviceFirmwareUpdateStatusGet": 61001,
|
|
467
|
+
"MessageType_DeviceFirmwareUpdateStatus": 61002,
|
|
469
468
|
"MessageType_DeviceSessionGet": 61200,
|
|
470
469
|
"MessageType_DeviceSession": 61201,
|
|
471
470
|
"MessageType_DeviceSessionAskPin": 61202,
|
|
@@ -10925,7 +10924,7 @@
|
|
|
10925
10924
|
"type": "bytes",
|
|
10926
10925
|
"id": 1
|
|
10927
10926
|
},
|
|
10928
|
-
"
|
|
10927
|
+
"signing_message": {
|
|
10929
10928
|
"type": "bytes",
|
|
10930
10929
|
"id": 2
|
|
10931
10930
|
},
|
|
@@ -11814,13 +11813,13 @@
|
|
|
11814
11813
|
"type": "string",
|
|
11815
11814
|
"id": 2
|
|
11816
11815
|
},
|
|
11817
|
-
"
|
|
11816
|
+
"burn_in_completed": {
|
|
11818
11817
|
"type": "bool",
|
|
11819
|
-
"id":
|
|
11818
|
+
"id": 3
|
|
11820
11819
|
},
|
|
11821
|
-
"
|
|
11820
|
+
"factory_test_completed": {
|
|
11822
11821
|
"type": "bool",
|
|
11823
|
-
"id":
|
|
11822
|
+
"id": 4
|
|
11824
11823
|
},
|
|
11825
11824
|
"manufacture_time": {
|
|
11826
11825
|
"type": "DeviceFactoryInfoManufactureTime",
|
|
@@ -11907,7 +11906,7 @@
|
|
|
11907
11906
|
}
|
|
11908
11907
|
}
|
|
11909
11908
|
},
|
|
11910
|
-
"
|
|
11909
|
+
"DeviceFirmwareUpdateRequest": {
|
|
11911
11910
|
"fields": {
|
|
11912
11911
|
"targets": {
|
|
11913
11912
|
"rule": "repeated",
|
|
@@ -11916,9 +11915,6 @@
|
|
|
11916
11915
|
}
|
|
11917
11916
|
}
|
|
11918
11917
|
},
|
|
11919
|
-
"DeviceFirmwareUpdateRequest": {
|
|
11920
|
-
"fields": {}
|
|
11921
|
-
},
|
|
11922
11918
|
"DeviceFirmwareUpdateRecord": {
|
|
11923
11919
|
"fields": {
|
|
11924
11920
|
"target_id": {
|
|
@@ -12100,7 +12096,7 @@
|
|
|
12100
12096
|
"type": "bool",
|
|
12101
12097
|
"id": 100
|
|
12102
12098
|
},
|
|
12103
|
-
"
|
|
12099
|
+
"fw": {
|
|
12104
12100
|
"type": "bool",
|
|
12105
12101
|
"id": 200
|
|
12106
12102
|
},
|
|
@@ -12169,7 +12165,7 @@
|
|
|
12169
12165
|
"type": "DeviceHardwareInfo",
|
|
12170
12166
|
"id": 100
|
|
12171
12167
|
},
|
|
12172
|
-
"
|
|
12168
|
+
"fw": {
|
|
12173
12169
|
"type": "DeviceMainMcuInfo",
|
|
12174
12170
|
"id": 200
|
|
12175
12171
|
},
|
|
@@ -12707,12 +12703,9 @@
|
|
|
12707
12703
|
"id": 1
|
|
12708
12704
|
},
|
|
12709
12705
|
"text": {
|
|
12706
|
+
"rule": "required",
|
|
12710
12707
|
"type": "string",
|
|
12711
12708
|
"id": 2
|
|
12712
|
-
},
|
|
12713
|
-
"text_id": {
|
|
12714
|
-
"type": "uint32",
|
|
12715
|
-
"id": 3
|
|
12716
12709
|
}
|
|
12717
12710
|
}
|
|
12718
12711
|
},
|
|
@@ -12743,6 +12736,7 @@
|
|
|
12743
12736
|
"ViewSignPage": {
|
|
12744
12737
|
"fields": {
|
|
12745
12738
|
"title": {
|
|
12739
|
+
"rule": "required",
|
|
12746
12740
|
"type": "string",
|
|
12747
12741
|
"id": 1
|
|
12748
12742
|
},
|
|
@@ -12776,16 +12770,13 @@
|
|
|
12776
12770
|
"options": {
|
|
12777
12771
|
"default": "LayoutDefault"
|
|
12778
12772
|
}
|
|
12779
|
-
},
|
|
12780
|
-
"title_id": {
|
|
12781
|
-
"type": "uint32",
|
|
12782
|
-
"id": 8
|
|
12783
12773
|
}
|
|
12784
12774
|
}
|
|
12785
12775
|
},
|
|
12786
12776
|
"ViewVerifyPage": {
|
|
12787
12777
|
"fields": {
|
|
12788
12778
|
"title": {
|
|
12779
|
+
"rule": "required",
|
|
12789
12780
|
"type": "string",
|
|
12790
12781
|
"id": 1
|
|
12791
12782
|
},
|
|
@@ -12810,14 +12801,6 @@
|
|
|
12810
12801
|
"value_key": {
|
|
12811
12802
|
"type": "uint32",
|
|
12812
12803
|
"id": 6
|
|
12813
|
-
},
|
|
12814
|
-
"title_id": {
|
|
12815
|
-
"type": "uint32",
|
|
12816
|
-
"id": 7
|
|
12817
|
-
},
|
|
12818
|
-
"chain_id": {
|
|
12819
|
-
"type": "uint32",
|
|
12820
|
-
"id": 8
|
|
12821
12804
|
}
|
|
12822
12805
|
}
|
|
12823
12806
|
},
|
|
@@ -267,11 +267,11 @@ export const mapProtocolV2DeviceInfoToState = (
|
|
|
267
267
|
}
|
|
268
268
|
: { mode },
|
|
269
269
|
versions: definedEntries({
|
|
270
|
-
firmware: imageVersion(info.
|
|
271
|
-
applicationP1: imageVersion(info.
|
|
272
|
-
applicationP2: imageVersion(info.
|
|
273
|
-
bootloader: imageVersion(info.
|
|
274
|
-
board: imageVersion(info.
|
|
270
|
+
firmware: imageVersion(info.fw?.application),
|
|
271
|
+
applicationP1: imageVersion(info.fw?.application),
|
|
272
|
+
applicationP2: imageVersion(info.fw?.application_data),
|
|
273
|
+
bootloader: imageVersion(info.fw?.bootloader),
|
|
274
|
+
board: imageVersion(info.fw?.romloader),
|
|
275
275
|
ble: imageVersion(info.coprocessor?.application),
|
|
276
276
|
se01: imageVersion(info.se1?.application),
|
|
277
277
|
se02: imageVersion(info.se2?.application),
|
|
@@ -283,16 +283,16 @@ export const mapProtocolV2DeviceInfoToState = (
|
|
|
283
283
|
se04Boot: imageVersion(info.se4?.bootloader),
|
|
284
284
|
}),
|
|
285
285
|
verification: definedEntries({
|
|
286
|
-
firmwareBuildId: imageBuildId(info.
|
|
287
|
-
firmwareHash: imageHash(info.
|
|
288
|
-
applicationP1BuildId: imageBuildId(info.
|
|
289
|
-
applicationP1Hash: imageHash(info.
|
|
290
|
-
applicationP2BuildId: imageBuildId(info.
|
|
291
|
-
applicationP2Hash: imageHash(info.
|
|
292
|
-
bootloaderBuildId: imageBuildId(info.
|
|
293
|
-
bootloaderHash: imageHash(info.
|
|
294
|
-
boardBuildId: imageBuildId(info.
|
|
295
|
-
boardHash: imageHash(info.
|
|
286
|
+
firmwareBuildId: imageBuildId(info.fw?.application),
|
|
287
|
+
firmwareHash: imageHash(info.fw?.application),
|
|
288
|
+
applicationP1BuildId: imageBuildId(info.fw?.application),
|
|
289
|
+
applicationP1Hash: imageHash(info.fw?.application),
|
|
290
|
+
applicationP2BuildId: imageBuildId(info.fw?.application_data),
|
|
291
|
+
applicationP2Hash: imageHash(info.fw?.application_data),
|
|
292
|
+
bootloaderBuildId: imageBuildId(info.fw?.bootloader),
|
|
293
|
+
bootloaderHash: imageHash(info.fw?.bootloader),
|
|
294
|
+
boardBuildId: imageBuildId(info.fw?.romloader),
|
|
295
|
+
boardHash: imageHash(info.fw?.romloader),
|
|
296
296
|
bleBuildId: imageBuildId(info.coprocessor?.application),
|
|
297
297
|
bleHash: imageHash(info.coprocessor?.application),
|
|
298
298
|
se01BuildId: imageBuildId(info.se1?.application),
|
|
@@ -216,9 +216,9 @@ export const buildProtocolV2FeaturesPayload = ({
|
|
|
216
216
|
runtimeMode?: ProtocolV2RuntimeMode;
|
|
217
217
|
}): Features => {
|
|
218
218
|
const info = deviceInfo;
|
|
219
|
-
const fwApplication = info?.
|
|
220
|
-
const fwBootloader = info?.
|
|
221
|
-
const fwBoard = info?.
|
|
219
|
+
const fwApplication = info?.fw?.application;
|
|
220
|
+
const fwBootloader = info?.fw?.bootloader;
|
|
221
|
+
const fwBoard = info?.fw?.romloader;
|
|
222
222
|
const bleApplication = info?.coprocessor?.application;
|
|
223
223
|
const status = deviceStatus;
|
|
224
224
|
const incomingSerialNo = info?.hw?.serial_no;
|