@onekeyfe/hd-core 1.2.0-alpha.78 → 1.2.0-alpha.79
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 +12 -71
- package/__tests__/protocol-v2-resources.test.ts +102 -222
- package/__tests__/protocol-v2.test.ts +110 -380
- package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +0 -6
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/UploadPortfolio.d.ts.map +1 -1
- package/dist/data-manager/DataManager.d.ts +1 -2
- package/dist/data-manager/DataManager.d.ts.map +1 -1
- package/dist/index.d.ts +74 -32
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +255 -499
- package/dist/inject.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/resources.d.ts +31 -28
- package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
- package/dist/types/api/checkAllFirmwareRelease.d.ts +1 -0
- package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
- package/dist/types/api/export.d.ts +1 -0
- package/dist/types/api/export.d.ts.map +1 -1
- package/dist/types/api/index.d.ts +2 -0
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/api/protocolV2ResourceManifest.d.ts +17 -0
- package/dist/types/api/protocolV2ResourceManifest.d.ts.map +1 -0
- package/dist/types/settings.d.ts +30 -21
- package/dist/types/settings.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/CheckAllFirmwareRelease.ts +5 -34
- package/src/api/FirmwareUpdateV4.ts +48 -220
- package/src/api/UploadPortfolio.ts +18 -0
- package/src/data-manager/DataManager.ts +2 -6
- package/src/index.ts +6 -0
- package/src/inject.ts +2 -0
- package/src/protocols/protocol-v2/resources.ts +193 -324
- package/src/types/api/checkAllFirmwareRelease.ts +1 -0
- package/src/types/api/export.ts +4 -0
- package/src/types/api/index.ts +2 -0
- package/src/types/api/protocolV2ResourceManifest.ts +19 -0
- package/src/types/settings.ts +30 -36
package/dist/index.js
CHANGED
|
@@ -1265,6 +1265,204 @@ const assertFirmwareUpdatePreparedPlanBinding = ({ preparedPlan: value, executor
|
|
|
1265
1265
|
return preparedPlan;
|
|
1266
1266
|
};
|
|
1267
1267
|
|
|
1268
|
+
const PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH$1 = 'vol0:/loaders/bootloader/boot_resource.okpkg';
|
|
1269
|
+
const SHA256_HEX_LENGTH = 64;
|
|
1270
|
+
function normalizeHex$1(value, expectedLength, field) {
|
|
1271
|
+
if (typeof value !== 'string') {
|
|
1272
|
+
throw new Error(`Invalid Pro2 resource ${field}: expected a hexadecimal string`);
|
|
1273
|
+
}
|
|
1274
|
+
const normalized = value.replace(/^0x/i, '').toLowerCase();
|
|
1275
|
+
if (normalized.length !== expectedLength || !/^[0-9a-f]+$/.test(normalized)) {
|
|
1276
|
+
throw new Error(`Invalid Pro2 resource ${field}: expected ${expectedLength} hexadecimal characters`);
|
|
1277
|
+
}
|
|
1278
|
+
return normalized;
|
|
1279
|
+
}
|
|
1280
|
+
function parseProtocolV2Resources(value) {
|
|
1281
|
+
if (value === undefined)
|
|
1282
|
+
return undefined;
|
|
1283
|
+
if (!value || typeof value !== 'object') {
|
|
1284
|
+
throw new Error('Invalid Pro2 resources config');
|
|
1285
|
+
}
|
|
1286
|
+
const { source } = value;
|
|
1287
|
+
if (!source || typeof source !== 'object') {
|
|
1288
|
+
throw new Error('Invalid Pro2 resources config: source is required');
|
|
1289
|
+
}
|
|
1290
|
+
const { manifestUrl } = source;
|
|
1291
|
+
if (typeof manifestUrl !== 'string' || !manifestUrl.startsWith('https://')) {
|
|
1292
|
+
throw new Error('Invalid Pro2 resources config: source.manifestUrl must use HTTPS');
|
|
1293
|
+
}
|
|
1294
|
+
return {
|
|
1295
|
+
source: { manifestUrl },
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1298
|
+
const PROTOCOL_V2_RESOURCE_MANIFEST_DEVICE_ROOTS = [
|
|
1299
|
+
'vol0:/bundles/',
|
|
1300
|
+
'vol0:/loaders/rom/',
|
|
1301
|
+
];
|
|
1302
|
+
function isAllowedManifestDevicePath(path) {
|
|
1303
|
+
if (!path.endsWith('.okpkg') ||
|
|
1304
|
+
path.includes('\\') ||
|
|
1305
|
+
path.includes('//') ||
|
|
1306
|
+
path.split('/').some(part => part === '.' || part === '..')) {
|
|
1307
|
+
return false;
|
|
1308
|
+
}
|
|
1309
|
+
if (path === PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH$1) {
|
|
1310
|
+
return true;
|
|
1311
|
+
}
|
|
1312
|
+
return PROTOCOL_V2_RESOURCE_MANIFEST_DEVICE_ROOTS.some(root => path.startsWith(root));
|
|
1313
|
+
}
|
|
1314
|
+
function assertManifestString(value, field) {
|
|
1315
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
1316
|
+
throw new Error(`Invalid Pro2 resource manifest ${field}`);
|
|
1317
|
+
}
|
|
1318
|
+
return value;
|
|
1319
|
+
}
|
|
1320
|
+
function assertManifestRelativePath(value, field) {
|
|
1321
|
+
const path = assertManifestString(value, field);
|
|
1322
|
+
if (path.startsWith('/') ||
|
|
1323
|
+
path.includes('\\') ||
|
|
1324
|
+
path.includes(':') ||
|
|
1325
|
+
path.split('/').some(part => !part || part === '.' || part === '..')) {
|
|
1326
|
+
throw new Error(`Invalid Pro2 resource manifest ${field}`);
|
|
1327
|
+
}
|
|
1328
|
+
return path;
|
|
1329
|
+
}
|
|
1330
|
+
function parseProtocolV2ResourceManifestFile(value, index) {
|
|
1331
|
+
var _a;
|
|
1332
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
1333
|
+
throw new Error(`Invalid Pro2 resource manifest files[${index}]`);
|
|
1334
|
+
}
|
|
1335
|
+
const file = value;
|
|
1336
|
+
const archivePath = assertManifestRelativePath(file.archive_path, `files[${index}].archive_path`);
|
|
1337
|
+
const originalName = assertManifestRelativePath(file.original_name, `files[${index}].original_name`);
|
|
1338
|
+
if (originalName.includes('/')) {
|
|
1339
|
+
throw new Error(`Invalid Pro2 resource manifest files[${index}].original_name`);
|
|
1340
|
+
}
|
|
1341
|
+
const devicePath = assertManifestString(file.device_path, `files[${index}].device_path`);
|
|
1342
|
+
if (!isAllowedManifestDevicePath(devicePath)) {
|
|
1343
|
+
throw new Error(`Invalid Pro2 resource manifest files[${index}].device_path`);
|
|
1344
|
+
}
|
|
1345
|
+
if (!Number.isSafeInteger(file.size) || Number(file.size) <= 0) {
|
|
1346
|
+
throw new Error(`Invalid Pro2 resource manifest files[${index}].size`);
|
|
1347
|
+
}
|
|
1348
|
+
const digest = normalizeHex$1(file.sha256, SHA256_HEX_LENGTH, `files[${index}].sha256`);
|
|
1349
|
+
if (file.signed !== true) {
|
|
1350
|
+
throw new Error(`Invalid Pro2 resource manifest files[${index}].signed`);
|
|
1351
|
+
}
|
|
1352
|
+
if (file.sig_algo !== 'ed25519' && file.sig_algo !== 'mldsa65') {
|
|
1353
|
+
throw new Error(`Invalid Pro2 resource manifest files[${index}].sig_algo`);
|
|
1354
|
+
}
|
|
1355
|
+
if (file.payload_version !== null && typeof file.payload_version !== 'string') {
|
|
1356
|
+
throw new Error(`Invalid Pro2 resource manifest files[${index}].payload_version`);
|
|
1357
|
+
}
|
|
1358
|
+
if (!archivePath.endsWith('.okpkg') || !originalName.endsWith('.okpkg')) {
|
|
1359
|
+
throw new Error(`Invalid Pro2 resource manifest files[${index}] package extension`);
|
|
1360
|
+
}
|
|
1361
|
+
return {
|
|
1362
|
+
archive_path: archivePath,
|
|
1363
|
+
original_name: originalName,
|
|
1364
|
+
device_path: devicePath,
|
|
1365
|
+
size: Number(file.size),
|
|
1366
|
+
sha256: digest,
|
|
1367
|
+
signed: true,
|
|
1368
|
+
sig_algo: file.sig_algo,
|
|
1369
|
+
payload_version: (_a = file.payload_version) !== null && _a !== void 0 ? _a : null,
|
|
1370
|
+
};
|
|
1371
|
+
}
|
|
1372
|
+
function parseProtocolV2ResourceManifest(value) {
|
|
1373
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
1374
|
+
throw new Error('Invalid Pro2 resource manifest');
|
|
1375
|
+
}
|
|
1376
|
+
const manifest = value;
|
|
1377
|
+
if (manifest.schema !== 1 ||
|
|
1378
|
+
manifest.variant !== 'resource' ||
|
|
1379
|
+
manifest.device_root !== 'vol0:' ||
|
|
1380
|
+
manifest.restore_mode !== 'bootloader_update' ||
|
|
1381
|
+
!Array.isArray(manifest.trees) ||
|
|
1382
|
+
!Array.isArray(manifest.files)) {
|
|
1383
|
+
throw new Error('Invalid Pro2 resource manifest contract');
|
|
1384
|
+
}
|
|
1385
|
+
const files = manifest.files.map(parseProtocolV2ResourceManifestFile);
|
|
1386
|
+
const devicePaths = new Set(files.map(file => file.device_path));
|
|
1387
|
+
const archivePaths = new Set(files.map(file => file.archive_path));
|
|
1388
|
+
if (files.length === 0 ||
|
|
1389
|
+
devicePaths.size !== files.length ||
|
|
1390
|
+
archivePaths.size !== files.length ||
|
|
1391
|
+
!devicePaths.has(PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH$1) ||
|
|
1392
|
+
!files.some(file => file.device_path.startsWith('vol0:/bundles/'))) {
|
|
1393
|
+
throw new Error('Invalid Pro2 resource manifest file set');
|
|
1394
|
+
}
|
|
1395
|
+
return {
|
|
1396
|
+
schema: 1,
|
|
1397
|
+
artifact_name: assertManifestString(manifest.artifact_name, 'artifact_name'),
|
|
1398
|
+
release_name: assertManifestString(manifest.release_name, 'release_name'),
|
|
1399
|
+
variant: 'resource',
|
|
1400
|
+
commit: assertManifestString(manifest.commit, 'commit'),
|
|
1401
|
+
short_sha: assertManifestString(manifest.short_sha, 'short_sha'),
|
|
1402
|
+
timestamp_utc: assertManifestString(manifest.timestamp_utc, 'timestamp_utc'),
|
|
1403
|
+
core_version: assertManifestString(manifest.core_version, 'core_version'),
|
|
1404
|
+
key_set: assertManifestString(manifest.key_set, 'key_set'),
|
|
1405
|
+
device_root: 'vol0:',
|
|
1406
|
+
restore_mode: 'bootloader_update',
|
|
1407
|
+
trees: manifest.trees.map((tree, index) => {
|
|
1408
|
+
if (!tree || typeof tree !== 'object') {
|
|
1409
|
+
throw new Error(`Invalid Pro2 resource manifest trees[${index}]`);
|
|
1410
|
+
}
|
|
1411
|
+
const item = tree;
|
|
1412
|
+
return {
|
|
1413
|
+
path: assertManifestRelativePath(item.path, `trees[${index}].path`),
|
|
1414
|
+
device: assertManifestString(item.device, `trees[${index}].device`),
|
|
1415
|
+
};
|
|
1416
|
+
}),
|
|
1417
|
+
files,
|
|
1418
|
+
};
|
|
1419
|
+
}
|
|
1420
|
+
function selectProtocolV2ResourceManifestFiles({ manifest, targetsToUpdate, }) {
|
|
1421
|
+
const targets = new Set(targetsToUpdate);
|
|
1422
|
+
return manifest.files.filter(file => {
|
|
1423
|
+
if (file.device_path.startsWith('vol0:/bundles/')) {
|
|
1424
|
+
return targets.has('resource');
|
|
1425
|
+
}
|
|
1426
|
+
return targets.has('boot_resources');
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1429
|
+
function resolveProtocolV2ResourceManifestFileUrl({ manifestUrl, archivePath, }) {
|
|
1430
|
+
const url = new URL(assertManifestRelativePath(archivePath, 'archive_path'), manifestUrl);
|
|
1431
|
+
if (url.protocol !== 'https:') {
|
|
1432
|
+
throw new Error('Invalid Pro2 resource manifest file URL');
|
|
1433
|
+
}
|
|
1434
|
+
return url.toString();
|
|
1435
|
+
}
|
|
1436
|
+
function prepareProtocolV2ResourceFiles({ manifest: value, files, targetsToUpdate, }) {
|
|
1437
|
+
const manifest = parseProtocolV2ResourceManifest(value);
|
|
1438
|
+
const selected = selectProtocolV2ResourceManifestFiles({ manifest, targetsToUpdate });
|
|
1439
|
+
const binaries = new Map(files.map(file => [file.archivePath, file.binary]));
|
|
1440
|
+
return selected.map(file => {
|
|
1441
|
+
const binary = binaries.get(file.archive_path);
|
|
1442
|
+
if (!binary ||
|
|
1443
|
+
!isProtocolV2ResourceFileValid(binary, {
|
|
1444
|
+
size: file.size,
|
|
1445
|
+
fileHash: file.sha256,
|
|
1446
|
+
})) {
|
|
1447
|
+
throw new Error(`Pro2 resource manifest file verification failed: ${file.archive_path}`);
|
|
1448
|
+
}
|
|
1449
|
+
return {
|
|
1450
|
+
binary,
|
|
1451
|
+
devicePath: file.device_path,
|
|
1452
|
+
size: file.size,
|
|
1453
|
+
fileHash: file.sha256,
|
|
1454
|
+
};
|
|
1455
|
+
});
|
|
1456
|
+
}
|
|
1457
|
+
function bytesToHex$3(bytes) {
|
|
1458
|
+
return Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');
|
|
1459
|
+
}
|
|
1460
|
+
function isProtocolV2ResourceFileValid(binary, resource) {
|
|
1461
|
+
if (binary.byteLength !== resource.size)
|
|
1462
|
+
return false;
|
|
1463
|
+
return bytesToHex$3(sha256.sha256(new Uint8Array(binary))) === resource.fileHash.toLowerCase();
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1268
1466
|
const bindingError = (message) => {
|
|
1269
1467
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, message, {
|
|
1270
1468
|
firmwareUpdateCode: 'FirmwareArtifactReaderInvalid',
|
|
@@ -1414,6 +1612,7 @@ const createCoreApi = (call) => ({
|
|
|
1414
1612
|
getFirmwareUpdateHostBindingGeneration,
|
|
1415
1613
|
prepareFirmwareUpdatePlan,
|
|
1416
1614
|
validateFirmwareUpdatePreparedPlan,
|
|
1615
|
+
prepareProtocolV2ResourceFiles,
|
|
1417
1616
|
getLogs: () => call({ method: 'getLogs' }),
|
|
1418
1617
|
clearSessionCache: params => call(Object.assign(Object.assign({}, params), { method: 'clearSessionCache' })),
|
|
1419
1618
|
searchDevices: params => call(Object.assign(Object.assign({}, params), { method: 'searchDevices' })),
|
|
@@ -40504,281 +40703,6 @@ const findLatestRelease = (releases) => {
|
|
|
40504
40703
|
return leastRelease;
|
|
40505
40704
|
};
|
|
40506
40705
|
|
|
40507
|
-
const PROTOCOL_V2_RESOURCE_TYPES = [
|
|
40508
|
-
'images',
|
|
40509
|
-
'animation',
|
|
40510
|
-
'wallpaper',
|
|
40511
|
-
'translations',
|
|
40512
|
-
'roobert',
|
|
40513
|
-
'noto',
|
|
40514
|
-
'firmware_logo',
|
|
40515
|
-
];
|
|
40516
|
-
const PROTOCOL_V2_RESOURCE_DEVICE_PATHS = {
|
|
40517
|
-
images: 'vol0:/bundles/images/images.okpkg',
|
|
40518
|
-
animation: 'vol0:/bundles/images/animation.okpkg',
|
|
40519
|
-
wallpaper: 'vol0:/bundles/images/wallpaper.okpkg',
|
|
40520
|
-
translations: 'vol0:/bundles/translations/translations.okpkg',
|
|
40521
|
-
roobert: 'vol0:/bundles/font/roobert.okpkg',
|
|
40522
|
-
noto: 'vol0:/bundles/font/noto.okpkg',
|
|
40523
|
-
firmware_logo: 'vol0:/bundles/firmware_logo.okpkg',
|
|
40524
|
-
};
|
|
40525
|
-
const RESOURCE_TYPE_SET = new Set(PROTOCOL_V2_RESOURCE_TYPES);
|
|
40526
|
-
const SHA256_HEX_LENGTH = 64;
|
|
40527
|
-
const SHA3_512_HEX_LENGTH = 128;
|
|
40528
|
-
const PROTOCOL_V2_OKPP_HEADER_SIZE$1 = 0x52a0;
|
|
40529
|
-
const PROTOCOL_V2_OKPP_TYPE_OFFSET = 0x08;
|
|
40530
|
-
const PROTOCOL_V2_OKPP_HEADER_LENGTH_OFFSET = 0x0c;
|
|
40531
|
-
const PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET$1 = 0x240;
|
|
40532
|
-
const PROTOCOL_V2_OKPP_HASH_SIZE$1 = 64;
|
|
40533
|
-
const PROTOCOL_V2_RESOURCE_IDENTITY_READ_SIZE = PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET$1 + PROTOCOL_V2_OKPP_HASH_SIZE$1;
|
|
40534
|
-
const PROTOCOL_V2_MIN_FILE_READ_CHUNK_SIZE = 64;
|
|
40535
|
-
const PROTOCOL_V2_RESOURCE_INVENTORY_TIMEOUT_MS = 5 * 1000;
|
|
40536
|
-
function toFiniteNumber(value) {
|
|
40537
|
-
if (typeof value === 'number' && Number.isFinite(value))
|
|
40538
|
-
return value;
|
|
40539
|
-
if (typeof value === 'string') {
|
|
40540
|
-
const numeric = Number(value);
|
|
40541
|
-
return Number.isFinite(numeric) ? numeric : undefined;
|
|
40542
|
-
}
|
|
40543
|
-
if (value && typeof value === 'object') {
|
|
40544
|
-
const longLike = value;
|
|
40545
|
-
if (typeof longLike.toNumber === 'function') {
|
|
40546
|
-
const numeric = longLike.toNumber();
|
|
40547
|
-
return Number.isFinite(numeric) ? numeric : undefined;
|
|
40548
|
-
}
|
|
40549
|
-
}
|
|
40550
|
-
return undefined;
|
|
40551
|
-
}
|
|
40552
|
-
function toUint8Array(value) {
|
|
40553
|
-
if (value instanceof Uint8Array)
|
|
40554
|
-
return value;
|
|
40555
|
-
if (value instanceof ArrayBuffer)
|
|
40556
|
-
return new Uint8Array(value);
|
|
40557
|
-
if (ArrayBuffer.isView(value)) {
|
|
40558
|
-
return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
|
40559
|
-
}
|
|
40560
|
-
if (typeof value === 'string') {
|
|
40561
|
-
const hex = value.replace(/^0x/i, '');
|
|
40562
|
-
if (!hex || hex.length % 2 !== 0 || /[^0-9a-f]/i.test(hex)) {
|
|
40563
|
-
return new Uint8Array(0);
|
|
40564
|
-
}
|
|
40565
|
-
const bytes = new Uint8Array(hex.length / 2);
|
|
40566
|
-
for (let index = 0; index < bytes.length; index += 1) {
|
|
40567
|
-
bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
|
|
40568
|
-
}
|
|
40569
|
-
return bytes;
|
|
40570
|
-
}
|
|
40571
|
-
return new Uint8Array(0);
|
|
40572
|
-
}
|
|
40573
|
-
function readAscii(bytes, offset, length) {
|
|
40574
|
-
return Array.from(bytes.slice(offset, offset + length), byte => String.fromCharCode(byte)).join('');
|
|
40575
|
-
}
|
|
40576
|
-
function parseProtocolV2ResourceHeaderHash(bytes) {
|
|
40577
|
-
if (bytes.byteLength < PROTOCOL_V2_RESOURCE_IDENTITY_READ_SIZE)
|
|
40578
|
-
return undefined;
|
|
40579
|
-
if (readAscii(bytes, 0, 4) !== 'OKPP')
|
|
40580
|
-
return undefined;
|
|
40581
|
-
if (readAscii(bytes, PROTOCOL_V2_OKPP_TYPE_OFFSET, 4) !== 'RESC')
|
|
40582
|
-
return undefined;
|
|
40583
|
-
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
40584
|
-
if (view.getUint32(PROTOCOL_V2_OKPP_HEADER_LENGTH_OFFSET, true) !== PROTOCOL_V2_OKPP_HEADER_SIZE$1) {
|
|
40585
|
-
return undefined;
|
|
40586
|
-
}
|
|
40587
|
-
return bytesToHex$3(bytes.slice(PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET$1, PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET$1 + PROTOCOL_V2_OKPP_HASH_SIZE$1));
|
|
40588
|
-
}
|
|
40589
|
-
function readProtocolV2ResourceIdentity({ commands, resource, chunkSize, timeoutMs = PROTOCOL_V2_RESOURCE_INVENTORY_TIMEOUT_MS, }) {
|
|
40590
|
-
var _a, _b, _c, _d;
|
|
40591
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
40592
|
-
const path = PROTOCOL_V2_RESOURCE_DEVICE_PATHS[resource.type];
|
|
40593
|
-
const pathInfo = yield commands.typedCall('FilesystemPathInfoQuery', 'FilesystemPathInfo', { path }, { timeoutMs });
|
|
40594
|
-
const size = toFiniteNumber((_a = pathInfo.message) === null || _a === void 0 ? void 0 : _a.size);
|
|
40595
|
-
if (!((_b = pathInfo.message) === null || _b === void 0 ? void 0 : _b.exist) ||
|
|
40596
|
-
((_c = pathInfo.message) === null || _c === void 0 ? void 0 : _c.directory) ||
|
|
40597
|
-
!Number.isSafeInteger(size) ||
|
|
40598
|
-
size !== resource.size ||
|
|
40599
|
-
size < PROTOCOL_V2_OKPP_HEADER_SIZE$1) {
|
|
40600
|
-
return undefined;
|
|
40601
|
-
}
|
|
40602
|
-
const header = new Uint8Array(PROTOCOL_V2_RESOURCE_IDENTITY_READ_SIZE);
|
|
40603
|
-
let offset = 0;
|
|
40604
|
-
while (offset < header.byteLength) {
|
|
40605
|
-
const readLength = Math.min(chunkSize, header.byteLength - offset);
|
|
40606
|
-
const response = yield commands.typedCall('FilesystemFileRead', 'FilesystemFile', {
|
|
40607
|
-
file: { path, offset, total_size: 0 },
|
|
40608
|
-
chunk_len: readLength,
|
|
40609
|
-
}, { timeoutMs });
|
|
40610
|
-
const data = toUint8Array((_d = response.message) === null || _d === void 0 ? void 0 : _d.data);
|
|
40611
|
-
if (data.byteLength === 0)
|
|
40612
|
-
return undefined;
|
|
40613
|
-
const copied = Math.min(data.byteLength, header.byteLength - offset);
|
|
40614
|
-
header.set(data.subarray(0, copied), offset);
|
|
40615
|
-
offset += copied;
|
|
40616
|
-
}
|
|
40617
|
-
const headerHash = parseProtocolV2ResourceHeaderHash(header);
|
|
40618
|
-
return headerHash ? { type: resource.type, size, headerHash } : undefined;
|
|
40619
|
-
});
|
|
40620
|
-
}
|
|
40621
|
-
function readProtocolV2ResourceInventory({ commands, resources, chunkSize = hdTransport.PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE, timeoutMs = PROTOCOL_V2_RESOURCE_INVENTORY_TIMEOUT_MS, }) {
|
|
40622
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
40623
|
-
const normalizedChunkSize = Number.isFinite(chunkSize)
|
|
40624
|
-
? Math.max(Math.floor(chunkSize), PROTOCOL_V2_MIN_FILE_READ_CHUNK_SIZE)
|
|
40625
|
-
: hdTransport.PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE;
|
|
40626
|
-
const inventory = [];
|
|
40627
|
-
for (const resource of resources) {
|
|
40628
|
-
try {
|
|
40629
|
-
const item = yield readProtocolV2ResourceIdentity({
|
|
40630
|
-
commands,
|
|
40631
|
-
resource,
|
|
40632
|
-
chunkSize: normalizedChunkSize,
|
|
40633
|
-
timeoutMs,
|
|
40634
|
-
});
|
|
40635
|
-
if (item)
|
|
40636
|
-
inventory.push(item);
|
|
40637
|
-
}
|
|
40638
|
-
catch (_a) {
|
|
40639
|
-
}
|
|
40640
|
-
}
|
|
40641
|
-
return inventory;
|
|
40642
|
-
});
|
|
40643
|
-
}
|
|
40644
|
-
function normalizeHex$1(value, expectedLength, field) {
|
|
40645
|
-
if (typeof value !== 'string') {
|
|
40646
|
-
throw new Error(`Invalid Pro2 resource ${field}: expected a hexadecimal string`);
|
|
40647
|
-
}
|
|
40648
|
-
const normalized = value.replace(/^0x/i, '').toLowerCase();
|
|
40649
|
-
if (normalized.length !== expectedLength || !/^[0-9a-f]+$/.test(normalized)) {
|
|
40650
|
-
throw new Error(`Invalid Pro2 resource ${field}: expected ${expectedLength} hexadecimal characters`);
|
|
40651
|
-
}
|
|
40652
|
-
return normalized;
|
|
40653
|
-
}
|
|
40654
|
-
function validateResource(value, index) {
|
|
40655
|
-
if (!value || typeof value !== 'object') {
|
|
40656
|
-
throw new Error(`Invalid Pro2 resource at stable[${index}]`);
|
|
40657
|
-
}
|
|
40658
|
-
const resource = value;
|
|
40659
|
-
if (typeof resource.type !== 'string' || !RESOURCE_TYPE_SET.has(resource.type)) {
|
|
40660
|
-
throw new Error(`Invalid Pro2 resource type at stable[${index}]`);
|
|
40661
|
-
}
|
|
40662
|
-
if (typeof resource.url !== 'string' || !resource.url.startsWith('https://')) {
|
|
40663
|
-
throw new Error(`Invalid Pro2 resource url at stable[${index}]`);
|
|
40664
|
-
}
|
|
40665
|
-
if (!Number.isSafeInteger(resource.size) || Number(resource.size) <= 0) {
|
|
40666
|
-
throw new Error(`Invalid Pro2 resource size at stable[${index}]`);
|
|
40667
|
-
}
|
|
40668
|
-
return {
|
|
40669
|
-
type: resource.type,
|
|
40670
|
-
url: resource.url,
|
|
40671
|
-
size: Number(resource.size),
|
|
40672
|
-
fileHash: normalizeHex$1(resource.fileHash, SHA256_HEX_LENGTH, 'fileHash'),
|
|
40673
|
-
headerHash: normalizeHex$1(resource.headerHash, SHA3_512_HEX_LENGTH, 'headerHash'),
|
|
40674
|
-
};
|
|
40675
|
-
}
|
|
40676
|
-
function validateBootResources(value) {
|
|
40677
|
-
if (!value || typeof value !== 'object') {
|
|
40678
|
-
throw new Error('Invalid Pro2 boot resources config');
|
|
40679
|
-
}
|
|
40680
|
-
const resource = value;
|
|
40681
|
-
if (resource.required !== false) {
|
|
40682
|
-
throw new Error('Invalid Pro2 boot resources required flag: expected false');
|
|
40683
|
-
}
|
|
40684
|
-
if (resource.target !== 'RES') {
|
|
40685
|
-
throw new Error('Invalid Pro2 boot resources target: expected RES');
|
|
40686
|
-
}
|
|
40687
|
-
if (resource.manifestUrl !== undefined &&
|
|
40688
|
-
(typeof resource.manifestUrl !== 'string' || !resource.manifestUrl.startsWith('https://'))) {
|
|
40689
|
-
throw new Error('Invalid Pro2 boot resources manifestUrl');
|
|
40690
|
-
}
|
|
40691
|
-
if (!Array.isArray(resource.files) || resource.files.length === 0) {
|
|
40692
|
-
throw new Error('Invalid Pro2 boot resources files');
|
|
40693
|
-
}
|
|
40694
|
-
const files = resource.files.map((value, index) => {
|
|
40695
|
-
if (!value || typeof value !== 'object') {
|
|
40696
|
-
throw new Error(`Invalid Pro2 boot resource file at files[${index}]`);
|
|
40697
|
-
}
|
|
40698
|
-
const file = value;
|
|
40699
|
-
if (typeof file.url !== 'string' || !file.url.startsWith('https://')) {
|
|
40700
|
-
throw new Error(`Invalid Pro2 boot resource url at files[${index}]`);
|
|
40701
|
-
}
|
|
40702
|
-
if (typeof file.devicePath !== 'string' ||
|
|
40703
|
-
!file.devicePath.startsWith('vol0:/') ||
|
|
40704
|
-
file.devicePath.includes('..') ||
|
|
40705
|
-
file.devicePath.includes('\\')) {
|
|
40706
|
-
throw new Error(`Invalid Pro2 boot resource devicePath at files[${index}]`);
|
|
40707
|
-
}
|
|
40708
|
-
if (!Number.isSafeInteger(file.size) || Number(file.size) <= 0) {
|
|
40709
|
-
throw new Error(`Invalid Pro2 boot resource size at files[${index}]`);
|
|
40710
|
-
}
|
|
40711
|
-
return Object.assign(Object.assign({}, (typeof file.name === 'string' && file.name ? { name: file.name } : undefined)), { url: file.url, devicePath: file.devicePath, size: Number(file.size), fileHash: normalizeHex$1(file.fileHash, SHA256_HEX_LENGTH, `boot files[${index}].fileHash`) });
|
|
40712
|
-
});
|
|
40713
|
-
if (new Set(files.map(file => file.devicePath)).size !== files.length) {
|
|
40714
|
-
throw new Error('Invalid Pro2 boot resources files: duplicate devicePath');
|
|
40715
|
-
}
|
|
40716
|
-
return Object.assign(Object.assign({ required: false, target: 'RES' }, (resource.manifestUrl ? { manifestUrl: resource.manifestUrl } : undefined)), { files });
|
|
40717
|
-
}
|
|
40718
|
-
function parseProtocolV2Resources(value) {
|
|
40719
|
-
if (value === undefined)
|
|
40720
|
-
return undefined;
|
|
40721
|
-
if (!value ||
|
|
40722
|
-
typeof value !== 'object' ||
|
|
40723
|
-
!Array.isArray(value.stable)) {
|
|
40724
|
-
throw new Error('Invalid Pro2 resources config: stable must be an array');
|
|
40725
|
-
}
|
|
40726
|
-
const config = value;
|
|
40727
|
-
const stable = config.stable.map(validateResource);
|
|
40728
|
-
const types = new Set(stable.map(resource => resource.type));
|
|
40729
|
-
if (stable.length !== PROTOCOL_V2_RESOURCE_TYPES.length || types.size !== stable.length) {
|
|
40730
|
-
throw new Error(`Invalid Pro2 resources config: stable must contain ${PROTOCOL_V2_RESOURCE_TYPES.length} unique resource types`);
|
|
40731
|
-
}
|
|
40732
|
-
for (const type of PROTOCOL_V2_RESOURCE_TYPES) {
|
|
40733
|
-
if (!types.has(type)) {
|
|
40734
|
-
throw new Error(`Invalid Pro2 resources config: stable is missing ${type}`);
|
|
40735
|
-
}
|
|
40736
|
-
}
|
|
40737
|
-
const boot = config.boot === undefined ? undefined : validateBootResources(config.boot);
|
|
40738
|
-
return Object.assign({ stable: PROTOCOL_V2_RESOURCE_TYPES.map(type => {
|
|
40739
|
-
const resource = stable.find(item => item.type === type);
|
|
40740
|
-
if (!resource) {
|
|
40741
|
-
throw new Error(`Invalid Pro2 resources config: stable is missing ${type}`);
|
|
40742
|
-
}
|
|
40743
|
-
return resource;
|
|
40744
|
-
}) }, (boot ? { boot } : undefined));
|
|
40745
|
-
}
|
|
40746
|
-
function buildProtocolV2ResourceUpdatePlan({ resources, inventory, mode, forced = false, }) {
|
|
40747
|
-
if (forced) {
|
|
40748
|
-
return {
|
|
40749
|
-
status: resources.length > 0 ? 'outdated' : 'valid',
|
|
40750
|
-
resources: [...resources],
|
|
40751
|
-
};
|
|
40752
|
-
}
|
|
40753
|
-
if (!inventory) {
|
|
40754
|
-
return mode === 'bootloader-recovery'
|
|
40755
|
-
? {
|
|
40756
|
-
status: resources.length > 0 ? 'outdated' : 'valid',
|
|
40757
|
-
resources: [...resources],
|
|
40758
|
-
}
|
|
40759
|
-
: { status: 'unknown', resources: [] };
|
|
40760
|
-
}
|
|
40761
|
-
const inventoryByType = new Map(inventory.map(item => [item.type, item]));
|
|
40762
|
-
const changedResources = resources.filter(resource => {
|
|
40763
|
-
const current = inventoryByType.get(resource.type);
|
|
40764
|
-
return (!current ||
|
|
40765
|
-
current.size !== resource.size ||
|
|
40766
|
-
current.headerHash.toLowerCase() !== resource.headerHash.toLowerCase());
|
|
40767
|
-
});
|
|
40768
|
-
return {
|
|
40769
|
-
status: changedResources.length === 0 ? 'valid' : 'outdated',
|
|
40770
|
-
resources: changedResources,
|
|
40771
|
-
};
|
|
40772
|
-
}
|
|
40773
|
-
function bytesToHex$3(bytes) {
|
|
40774
|
-
return Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');
|
|
40775
|
-
}
|
|
40776
|
-
function isProtocolV2ResourceFileValid(binary, resource) {
|
|
40777
|
-
if (binary.byteLength !== resource.size)
|
|
40778
|
-
return false;
|
|
40779
|
-
return bytesToHex$3(sha256.sha256(new Uint8Array(binary))) === resource.fileHash.toLowerCase();
|
|
40780
|
-
}
|
|
40781
|
-
|
|
40782
40706
|
var _a$1;
|
|
40783
40707
|
const FIRMWARE_UPDATE_CONFIG_FRESHNESS_MS = 5 * 60 * 1000;
|
|
40784
40708
|
const Log$k = getLogger(exports.LoggerNames.Core);
|
|
@@ -40963,13 +40887,9 @@ class DataManager {
|
|
|
40963
40887
|
this.lastCheckTimestamp = getTimeStamp();
|
|
40964
40888
|
});
|
|
40965
40889
|
}
|
|
40966
|
-
static
|
|
40890
|
+
static getProtocolV2ResourceSource(deviceType = hdShared.EDeviceType.Pro2) {
|
|
40967
40891
|
var _b, _c;
|
|
40968
|
-
return (_c = (_b = this.deviceMap[deviceType]) === null || _b === void 0 ? void 0 : _b.resources) === null || _c === void 0 ? void 0 : _c.
|
|
40969
|
-
}
|
|
40970
|
-
static getProtocolV2BootResources(deviceType = hdShared.EDeviceType.Pro2) {
|
|
40971
|
-
var _b, _c;
|
|
40972
|
-
return (_c = (_b = this.deviceMap[deviceType]) === null || _b === void 0 ? void 0 : _b.resources) === null || _c === void 0 ? void 0 : _c.boot;
|
|
40892
|
+
return (_c = (_b = this.deviceMap[deviceType]) === null || _b === void 0 ? void 0 : _b.resources) === null || _c === void 0 ? void 0 : _c.source;
|
|
40973
40893
|
}
|
|
40974
40894
|
static getProtobufMessages(schema = 'v1CurrentSchema') {
|
|
40975
40895
|
return this.messages[schema];
|
|
@@ -47831,34 +47751,9 @@ class CheckAllFirmwareRelease extends BaseMethod {
|
|
|
47831
47751
|
deviceType: state.identity.deviceType,
|
|
47832
47752
|
});
|
|
47833
47753
|
const resourceDeviceType = state.identity.deviceType === 'neo' ? hdShared.EDeviceType.Neo : hdShared.EDeviceType.Pro2;
|
|
47834
|
-
const
|
|
47835
|
-
|
|
47836
|
-
|
|
47837
|
-
const loaderMode = state.status.mode === 'bootloader' || state.status.mode === 'romloader';
|
|
47838
|
-
if (loaderMode) {
|
|
47839
|
-
try {
|
|
47840
|
-
const inventory = yield readProtocolV2ResourceInventory({
|
|
47841
|
-
commands: this.device.getCommands(),
|
|
47842
|
-
resources,
|
|
47843
|
-
});
|
|
47844
|
-
resourceStatus = buildProtocolV2ResourceUpdatePlan({
|
|
47845
|
-
resources,
|
|
47846
|
-
inventory,
|
|
47847
|
-
mode: 'bootloader-recovery',
|
|
47848
|
-
}).status;
|
|
47849
|
-
}
|
|
47850
|
-
catch (_b) {
|
|
47851
|
-
resourceStatus = buildProtocolV2ResourceUpdatePlan({
|
|
47852
|
-
resources,
|
|
47853
|
-
mode: 'bootloader-recovery',
|
|
47854
|
-
}).status;
|
|
47855
|
-
}
|
|
47856
|
-
}
|
|
47857
|
-
}
|
|
47858
|
-
const targetsToUpdate = [
|
|
47859
|
-
...plan.targetsToUpdate,
|
|
47860
|
-
...(resourceStatus === 'outdated' ? ['resource'] : []),
|
|
47861
|
-
];
|
|
47754
|
+
const resourceSource = DataManager.getProtocolV2ResourceSource(resourceDeviceType);
|
|
47755
|
+
const resourceStatus = 'unknown';
|
|
47756
|
+
const targetsToUpdate = [...plan.targetsToUpdate];
|
|
47862
47757
|
const firmwareStatus = plan.status === 'unavailable' ? 'unknown' : plan.status;
|
|
47863
47758
|
const emptyRelease = 'none';
|
|
47864
47759
|
return Object.assign(Object.assign({ firmware: {
|
|
@@ -47872,7 +47767,7 @@ class CheckAllFirmwareRelease extends BaseMethod {
|
|
|
47872
47767
|
}, bootloader: {
|
|
47873
47768
|
status: 'valid',
|
|
47874
47769
|
release: emptyRelease,
|
|
47875
|
-
}, features, protocol: 'V2', deviceType: state.identity.deviceType }, plan), { resourceStatus,
|
|
47770
|
+
}, features, protocol: 'V2', deviceType: state.identity.deviceType }, plan), { resourceStatus, resourceManifestUrl: resourceSource === null || resourceSource === void 0 ? void 0 : resourceSource.manifestUrl, hasUpgrade: plan.hasUpgrade, targetsToUpdate });
|
|
47876
47771
|
});
|
|
47877
47772
|
}
|
|
47878
47773
|
}
|
|
@@ -51185,6 +51080,14 @@ const PROTOCOL_V2_REMOTE_COMPONENT_TARGETS = {
|
|
|
51185
51080
|
},
|
|
51186
51081
|
};
|
|
51187
51082
|
const PROTOCOL_V2_FIRMWARE_STAGING_PATHS = new Set(Object.values(PROTOCOL_V2_REMOTE_COMPONENT_TARGETS).map(target => `${PROTOCOL_V2_FIRMWARE_STAGING_VOLUME}${target.fileName}`));
|
|
51083
|
+
const PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH = 'vol0:/loaders/bootloader/boot_resource.okpkg';
|
|
51084
|
+
const PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_STAGING_PATH = `${PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH}.staging`;
|
|
51085
|
+
const resolveProtocolV2ResourceWritePath = (devicePath) => {
|
|
51086
|
+
const normalizedPath = devicePath.replace(/^vol0:(?!\/)/i, 'vol0:/').toLowerCase();
|
|
51087
|
+
return normalizedPath === PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH
|
|
51088
|
+
? PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_STAGING_PATH
|
|
51089
|
+
: devicePath;
|
|
51090
|
+
};
|
|
51188
51091
|
const PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID = new Map([
|
|
51189
51092
|
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER, 'boot'],
|
|
51190
51093
|
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1, 'app_v1'],
|
|
@@ -51488,7 +51391,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
51488
51391
|
});
|
|
51489
51392
|
}
|
|
51490
51393
|
runProtocolV2() {
|
|
51491
|
-
var _a, _b, _c, _d, _e;
|
|
51394
|
+
var _a, _b, _c, _d, _e, _f;
|
|
51492
51395
|
return __awaiter(this, void 0, void 0, function* () {
|
|
51493
51396
|
yield this.captureProtocolV2PhysicalIdentity();
|
|
51494
51397
|
const deviceFeatures = yield this.getProtocolV2DeviceFeatures();
|
|
@@ -51507,8 +51410,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
51507
51410
|
const hasExplicitResourceFiles = !!((_c = this.params.resourceFiles) === null || _c === void 0 ? void 0 : _c.length);
|
|
51508
51411
|
const wantsStableResources = !!((_d = this.params.targetsToUpdate) === null || _d === void 0 ? void 0 : _d.includes('resource'));
|
|
51509
51412
|
const wantsBootResources = !!((_e = this.params.targetsToUpdate) === null || _e === void 0 ? void 0 : _e.includes('boot_resources'));
|
|
51510
|
-
const
|
|
51511
|
-
const needsRemoteBootResources = !hasExplicitResourceFiles && (wantsBootResources || wantsStableResources);
|
|
51413
|
+
const needsPreparedResources = !hasExplicitResourceFiles && (wantsStableResources || wantsBootResources);
|
|
51512
51414
|
let fwBinaryMap = [];
|
|
51513
51415
|
let bootloaderBinary = null;
|
|
51514
51416
|
let installItems;
|
|
@@ -51519,17 +51421,20 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
51519
51421
|
fwBinaryMap = this.collectExplicitTargetBinaries();
|
|
51520
51422
|
bootloaderBinary = this.prepareBootloaderBinary();
|
|
51521
51423
|
const needsRemoteFirmware = !this.hasExplicitProtocolV2Payload(fwBinaryMap);
|
|
51522
|
-
if (
|
|
51424
|
+
if (needsPreparedResources) {
|
|
51425
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 resource manifest must be prepared by the external firmware host', {
|
|
51426
|
+
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
51427
|
+
});
|
|
51428
|
+
}
|
|
51429
|
+
if (needsRemoteFirmware &&
|
|
51523
51430
|
(this.params.artifactReader ||
|
|
51524
51431
|
DataManager.getSettings('firmwareManifestMode') === 'external-only')) {
|
|
51525
51432
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 firmware artifacts must be prepared by the external firmware host', {
|
|
51526
51433
|
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
51527
51434
|
});
|
|
51528
51435
|
}
|
|
51529
|
-
if (needsRemoteFirmware
|
|
51530
|
-
yield DataManager.forceReloadData(
|
|
51531
|
-
requireResources: needsRemoteResources || needsRemoteBootResources,
|
|
51532
|
-
});
|
|
51436
|
+
if (needsRemoteFirmware) {
|
|
51437
|
+
yield DataManager.forceReloadData();
|
|
51533
51438
|
}
|
|
51534
51439
|
if (needsRemoteFirmware) {
|
|
51535
51440
|
const remoteBinaries = yield this.prepareRemoteProtocolV2Binaries(firmwareType, deviceFeatures);
|
|
@@ -51537,15 +51442,15 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
51537
51442
|
fwBinaryMap = remoteBinaries.fwBinaryMap;
|
|
51538
51443
|
installItems = remoteBinaries.installItems;
|
|
51539
51444
|
}
|
|
51540
|
-
|
|
51541
|
-
if (bootResourceFiles === null || bootResourceFiles === void 0 ? void 0 : bootResourceFiles.length) {
|
|
51542
|
-
resourceBundles = this.mergeProtocolV2ResourceBundles(resourceBundles, bootResourceFiles);
|
|
51543
|
-
}
|
|
51544
|
-
if (!needsRemoteResources) {
|
|
51545
|
-
this.postTipMessage(exports.FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
51546
|
-
}
|
|
51445
|
+
this.postTipMessage(exports.FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
51547
51446
|
}
|
|
51548
51447
|
catch (err) {
|
|
51448
|
+
if (typeof err === 'object' &&
|
|
51449
|
+
err !== null &&
|
|
51450
|
+
'params' in err &&
|
|
51451
|
+
((_f = err.params) === null || _f === void 0 ? void 0 : _f.firmwareUpdateCode) === 'FirmwareArtifactsNotPrepared') {
|
|
51452
|
+
throw err;
|
|
51453
|
+
}
|
|
51549
51454
|
if (err instanceof hdShared.HardwareError && err.errorCode === hdShared.HardwareErrorCode.NetworkError) {
|
|
51550
51455
|
throw err;
|
|
51551
51456
|
}
|
|
@@ -51554,29 +51459,9 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
51554
51459
|
if (!bootloaderBinary &&
|
|
51555
51460
|
fwBinaryMap.length === 0 &&
|
|
51556
51461
|
!(installItems === null || installItems === void 0 ? void 0 : installItems.length) &&
|
|
51557
|
-
!(resourceBundles === null || resourceBundles === void 0 ? void 0 : resourceBundles.length)
|
|
51558
|
-
!needsRemoteResources) {
|
|
51462
|
+
!(resourceBundles === null || resourceBundles === void 0 ? void 0 : resourceBundles.length)) {
|
|
51559
51463
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateDownloadFailed, 'No firmware to update');
|
|
51560
51464
|
}
|
|
51561
|
-
if (needsRemoteResources) {
|
|
51562
|
-
const enteredBootloader = yield this.enterProtocolV2BootloaderMode();
|
|
51563
|
-
try {
|
|
51564
|
-
const stableResources = yield this.prepareProtocolV2ResourceBundles();
|
|
51565
|
-
resourceBundles = this.mergeProtocolV2ResourceBundles(resourceBundles, stableResources);
|
|
51566
|
-
this.postTipMessage(exports.FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
51567
|
-
}
|
|
51568
|
-
catch (err) {
|
|
51569
|
-
if (enteredBootloader) {
|
|
51570
|
-
try {
|
|
51571
|
-
yield this.exitProtocolV2BootloaderToNormal();
|
|
51572
|
-
}
|
|
51573
|
-
catch (restoreError) {
|
|
51574
|
-
Log$6.warn('[FirmwareUpdateV4] failed to restore App mode after resource preparation error:', restoreError);
|
|
51575
|
-
}
|
|
51576
|
-
}
|
|
51577
|
-
throw normalizeFirmwarePreparationError(err);
|
|
51578
|
-
}
|
|
51579
|
-
}
|
|
51580
51465
|
return this.executeProtocolV2Update(Object.assign(Object.assign({ fwBinaryMap,
|
|
51581
51466
|
bootloaderBinary }, (installItems ? { installItems } : undefined)), ((resourceBundles === null || resourceBundles === void 0 ? void 0 : resourceBundles.length) ? { resourceBundles } : undefined)));
|
|
51582
51467
|
});
|
|
@@ -51984,102 +51869,6 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
51984
51869
|
};
|
|
51985
51870
|
});
|
|
51986
51871
|
}
|
|
51987
|
-
getProtocolV2DeviceType() {
|
|
51988
|
-
const deviceType = this.device.getCurrentDeviceType();
|
|
51989
|
-
if (deviceType === hdShared.EDeviceType.Pro2 || deviceType === hdShared.EDeviceType.Neo)
|
|
51990
|
-
return deviceType;
|
|
51991
|
-
throw new Error(`Unsupported Protocol V2 device type: ${deviceType}`);
|
|
51992
|
-
}
|
|
51993
|
-
prepareProtocolV2BootResources() {
|
|
51994
|
-
var _a, _b, _c, _d, _e;
|
|
51995
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
51996
|
-
const wantsStableResources = !!((_a = this.params.targetsToUpdate) === null || _a === void 0 ? void 0 : _a.includes('resource'));
|
|
51997
|
-
const wantsBootResources = !!((_b = this.params.targetsToUpdate) === null || _b === void 0 ? void 0 : _b.includes('boot_resources'));
|
|
51998
|
-
if (!wantsStableResources && !wantsBootResources) {
|
|
51999
|
-
return undefined;
|
|
52000
|
-
}
|
|
52001
|
-
if ((_c = this.params.resourceFiles) === null || _c === void 0 ? void 0 : _c.length) {
|
|
52002
|
-
return undefined;
|
|
52003
|
-
}
|
|
52004
|
-
const resource = DataManager.getProtocolV2BootResources(this.getProtocolV2DeviceType());
|
|
52005
|
-
if (!resource) {
|
|
52006
|
-
if (wantsBootResources) {
|
|
52007
|
-
throw new Error('Missing Protocol V2 boot resources configuration');
|
|
52008
|
-
}
|
|
52009
|
-
Log$6.debug('[FirmwareUpdateV4] no boot resources configured; continue with stable resources');
|
|
52010
|
-
return undefined;
|
|
52011
|
-
}
|
|
52012
|
-
const files = [];
|
|
52013
|
-
for (const file of resource.files) {
|
|
52014
|
-
const isCurrent = !this.params.forcedUpdateRes && (yield this.isProtocolV2BootResourceCurrent(file));
|
|
52015
|
-
if (isCurrent) {
|
|
52016
|
-
Log$6.log(`[FirmwareUpdateV4] boot resource unchanged, skipping ${file.devicePath}`);
|
|
52017
|
-
}
|
|
52018
|
-
else {
|
|
52019
|
-
Log$6.log(`[FirmwareUpdateV4] downloading boot resource ${file.devicePath}`);
|
|
52020
|
-
const { binary } = yield getSysResourceBinary(file.url);
|
|
52021
|
-
if (!isProtocolV2ResourceFileValid(binary, file)) {
|
|
52022
|
-
throw new Error(`Boot resource file verification failed: ${file.devicePath}`);
|
|
52023
|
-
}
|
|
52024
|
-
files.push({
|
|
52025
|
-
name: (_e = (_d = file.name) !== null && _d !== void 0 ? _d : file.devicePath.split('/').pop()) !== null && _e !== void 0 ? _e : file.devicePath,
|
|
52026
|
-
binary,
|
|
52027
|
-
devicePath: file.devicePath,
|
|
52028
|
-
});
|
|
52029
|
-
}
|
|
52030
|
-
}
|
|
52031
|
-
return files;
|
|
52032
|
-
});
|
|
52033
|
-
}
|
|
52034
|
-
isProtocolV2BootResourceCurrent(file) {
|
|
52035
|
-
var _a, _b, _c, _d;
|
|
52036
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
52037
|
-
try {
|
|
52038
|
-
const commands = this.device.getCommands();
|
|
52039
|
-
const pathInfo = yield commands.typedCall('FilesystemPathInfoQuery', 'FilesystemPathInfo', { path: file.devicePath }, { timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT });
|
|
52040
|
-
const size = toProtocolV2FiniteNumber((_a = pathInfo.message) === null || _a === void 0 ? void 0 : _a.size);
|
|
52041
|
-
if (!((_b = pathInfo.message) === null || _b === void 0 ? void 0 : _b.exist) ||
|
|
52042
|
-
((_c = pathInfo.message) === null || _c === void 0 ? void 0 : _c.directory) ||
|
|
52043
|
-
!Number.isSafeInteger(size) ||
|
|
52044
|
-
size !== file.size) {
|
|
52045
|
-
return false;
|
|
52046
|
-
}
|
|
52047
|
-
const digest = sha256.sha256.create();
|
|
52048
|
-
const chunkSize = this.getProtocolV2FirmwareChunkSize('write');
|
|
52049
|
-
let offset = 0;
|
|
52050
|
-
while (offset < file.size) {
|
|
52051
|
-
const response = yield commands.typedCall('FilesystemFileRead', 'FilesystemFile', {
|
|
52052
|
-
file: { path: file.devicePath, offset, total_size: 0 },
|
|
52053
|
-
chunk_len: Math.min(chunkSize, file.size - offset),
|
|
52054
|
-
}, { timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT });
|
|
52055
|
-
const data = toProtocolV2Bytes((_d = response.message) === null || _d === void 0 ? void 0 : _d.data);
|
|
52056
|
-
if (data.byteLength === 0)
|
|
52057
|
-
return false;
|
|
52058
|
-
const consumed = data.subarray(0, Math.min(data.byteLength, file.size - offset));
|
|
52059
|
-
digest.update(consumed);
|
|
52060
|
-
offset += consumed.byteLength;
|
|
52061
|
-
}
|
|
52062
|
-
return bytesToHex(digest.digest()) === normalizeProtocolV2Hex(file.fileHash);
|
|
52063
|
-
}
|
|
52064
|
-
catch (error) {
|
|
52065
|
-
Log$6.debug(`[FirmwareUpdateV4] unable to compare boot resource ${file.devicePath}; scheduling rewrite`, error);
|
|
52066
|
-
return false;
|
|
52067
|
-
}
|
|
52068
|
-
});
|
|
52069
|
-
}
|
|
52070
|
-
mergeProtocolV2ResourceBundles(...groups) {
|
|
52071
|
-
const merged = groups.flatMap(group => group !== null && group !== void 0 ? group : []);
|
|
52072
|
-
if (!merged.length)
|
|
52073
|
-
return undefined;
|
|
52074
|
-
const seenPaths = new Set();
|
|
52075
|
-
for (const bundle of merged) {
|
|
52076
|
-
if (seenPaths.has(bundle.devicePath)) {
|
|
52077
|
-
throw new Error(`Duplicate Protocol V2 resource devicePath: ${bundle.devicePath}`);
|
|
52078
|
-
}
|
|
52079
|
-
seenPaths.add(bundle.devicePath);
|
|
52080
|
-
}
|
|
52081
|
-
return merged;
|
|
52082
|
-
}
|
|
52083
51872
|
prepareExplicitProtocolV2ResourceFiles() {
|
|
52084
51873
|
var _a;
|
|
52085
51874
|
const files = (_a = this.params.resourceFiles) !== null && _a !== void 0 ? _a : [];
|
|
@@ -52110,52 +51899,6 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
52110
51899
|
}
|
|
52111
51900
|
return prepared;
|
|
52112
51901
|
}
|
|
52113
|
-
prepareProtocolV2ResourceBundles() {
|
|
52114
|
-
var _a;
|
|
52115
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
52116
|
-
if (!((_a = this.params.targetsToUpdate) === null || _a === void 0 ? void 0 : _a.includes('resource'))) {
|
|
52117
|
-
return undefined;
|
|
52118
|
-
}
|
|
52119
|
-
const resources = DataManager.getProtocolV2Resources(this.getProtocolV2DeviceType());
|
|
52120
|
-
if (!(resources === null || resources === void 0 ? void 0 : resources.length)) {
|
|
52121
|
-
throw new Error('Missing Pro2 stable resource configuration');
|
|
52122
|
-
}
|
|
52123
|
-
const inventory = this.params.forcedUpdateRes
|
|
52124
|
-
? undefined
|
|
52125
|
-
: yield readProtocolV2ResourceInventory({
|
|
52126
|
-
commands: this.device.getCommands(),
|
|
52127
|
-
resources,
|
|
52128
|
-
chunkSize: this.getProtocolV2FirmwareChunkSize('write'),
|
|
52129
|
-
timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
|
|
52130
|
-
});
|
|
52131
|
-
const plan = buildProtocolV2ResourceUpdatePlan({
|
|
52132
|
-
resources,
|
|
52133
|
-
inventory,
|
|
52134
|
-
mode: 'bootloader-recovery',
|
|
52135
|
-
forced: this.params.forcedUpdateRes,
|
|
52136
|
-
});
|
|
52137
|
-
Log$6.log(`[FirmwareUpdateV4] Protocol V2 resource plan mode=bootloader-recovery status=${plan.status} count=${plan.resources.length}`);
|
|
52138
|
-
const bundles = [];
|
|
52139
|
-
for (const resource of plan.resources) {
|
|
52140
|
-
bundles.push(yield this.downloadProtocolV2Resource(resource));
|
|
52141
|
-
}
|
|
52142
|
-
return bundles;
|
|
52143
|
-
});
|
|
52144
|
-
}
|
|
52145
|
-
downloadProtocolV2Resource(resource) {
|
|
52146
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
52147
|
-
Log$6.log(`[FirmwareUpdateV4] downloading Pro2 resource ${resource.type}`);
|
|
52148
|
-
const { binary } = yield getSysResourceBinary(resource.url);
|
|
52149
|
-
if (!isProtocolV2ResourceFileValid(binary, resource)) {
|
|
52150
|
-
throw new Error(`Pro2 resource file verification failed: ${resource.type}`);
|
|
52151
|
-
}
|
|
52152
|
-
return {
|
|
52153
|
-
name: `${resource.type}.okpkg`,
|
|
52154
|
-
binary,
|
|
52155
|
-
devicePath: PROTOCOL_V2_RESOURCE_DEVICE_PATHS[resource.type],
|
|
52156
|
-
};
|
|
52157
|
-
});
|
|
52158
|
-
}
|
|
52159
51902
|
getProtocolV2ResourceFilePath(path) {
|
|
52160
51903
|
if (path.startsWith('vol'))
|
|
52161
51904
|
return path;
|
|
@@ -52347,13 +52090,6 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
52347
52090
|
}
|
|
52348
52091
|
buildProtocolV2ExecutionPhases({ installSources, resourceSources, }) {
|
|
52349
52092
|
const phases = [];
|
|
52350
|
-
if (resourceSources.length > 0) {
|
|
52351
|
-
phases.push({
|
|
52352
|
-
kind: 'resource-sync',
|
|
52353
|
-
installSources: [],
|
|
52354
|
-
resourceSources,
|
|
52355
|
-
});
|
|
52356
|
-
}
|
|
52357
52093
|
const bootloaderSources = installSources.filter(source => source.kind === 'bootloader');
|
|
52358
52094
|
if (bootloaderSources.length > 0) {
|
|
52359
52095
|
phases.push({
|
|
@@ -52366,6 +52102,13 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
52366
52102
|
resourceSources: [],
|
|
52367
52103
|
});
|
|
52368
52104
|
}
|
|
52105
|
+
if (resourceSources.length > 0) {
|
|
52106
|
+
phases.push({
|
|
52107
|
+
kind: 'resource-sync',
|
|
52108
|
+
installSources: [],
|
|
52109
|
+
resourceSources,
|
|
52110
|
+
});
|
|
52111
|
+
}
|
|
52369
52112
|
const componentSources = installSources.filter(source => source.kind !== 'bootloader');
|
|
52370
52113
|
if (componentSources.length > 0) {
|
|
52371
52114
|
phases.push({
|
|
@@ -52464,13 +52207,14 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
52464
52207
|
this.postTipMessage(exports.FirmwareUpdateTipMessage.StartTransferData);
|
|
52465
52208
|
let processedSize = 0;
|
|
52466
52209
|
for (const resource of resourcesToSync) {
|
|
52210
|
+
const writePath = resolveProtocolV2ResourceWritePath(resource.devicePath);
|
|
52467
52211
|
processedSize = yield this.protocolV2SourceUpdateProcess({
|
|
52468
52212
|
source: resource.source,
|
|
52469
|
-
filePath:
|
|
52213
|
+
filePath: writePath,
|
|
52470
52214
|
processedSize,
|
|
52471
52215
|
totalSize,
|
|
52472
52216
|
});
|
|
52473
|
-
yield this.verifyProtocolV2StagedFile(
|
|
52217
|
+
yield this.verifyProtocolV2StagedFile(writePath, resource.source.size);
|
|
52474
52218
|
}
|
|
52475
52219
|
const stagedInstallTargets = [];
|
|
52476
52220
|
for (const item of installSources) {
|
|
@@ -53460,7 +53204,7 @@ class DeviceUploadWallpaper extends BaseMethod {
|
|
|
53460
53204
|
}
|
|
53461
53205
|
|
|
53462
53206
|
const FILESYSTEM_PATH_INFO_QUERY_MESSAGE_TYPE = 60802;
|
|
53463
|
-
const FILESYSTEM_FILE_WRITE_MESSAGE_TYPE = 60805;
|
|
53207
|
+
const FILESYSTEM_FILE_WRITE_MESSAGE_TYPE$1 = 60805;
|
|
53464
53208
|
const FILESYSTEM_DIR_LIST_MESSAGE_TYPE = 60808;
|
|
53465
53209
|
const NFT_UPDATE_MESSAGE_TYPE = 61500;
|
|
53466
53210
|
class DeviceUploadNft extends BaseMethod {
|
|
@@ -53489,7 +53233,7 @@ class DeviceUploadNft extends BaseMethod {
|
|
|
53489
53233
|
assertCapabilities() {
|
|
53490
53234
|
return __awaiter(this, void 0, void 0, function* () {
|
|
53491
53235
|
const protocolInfo = yield this.device.ensureProtocolV2RuntimeContext();
|
|
53492
|
-
const hasFileWrite = supportsProtocolV2Message(protocolInfo, FILESYSTEM_FILE_WRITE_MESSAGE_TYPE);
|
|
53236
|
+
const hasFileWrite = supportsProtocolV2Message(protocolInfo, FILESYSTEM_FILE_WRITE_MESSAGE_TYPE$1);
|
|
53493
53237
|
const hasPathInfo = supportsProtocolV2Message(protocolInfo, FILESYSTEM_PATH_INFO_QUERY_MESSAGE_TYPE);
|
|
53494
53238
|
const hasDirList = supportsProtocolV2Message(protocolInfo, FILESYSTEM_DIR_LIST_MESSAGE_TYPE);
|
|
53495
53239
|
const hasNftUpdate = supportsProtocolV2Message(protocolInfo, NFT_UPDATE_MESSAGE_TYPE);
|
|
@@ -53624,6 +53368,8 @@ class FileWrite extends BaseMethod {
|
|
|
53624
53368
|
|
|
53625
53369
|
const PORTFOLIO_PENDING_PATH = 'vol1:/portfolio/portfolio.okpkg.pending';
|
|
53626
53370
|
const PORTFOLIO_CHUNK_SIZE = 2048;
|
|
53371
|
+
const FILESYSTEM_FILE_WRITE_MESSAGE_TYPE = 60805;
|
|
53372
|
+
const PORTFOLIO_UPDATE_MESSAGE_TYPE = 61400;
|
|
53627
53373
|
class UploadPortfolio extends FileWrite {
|
|
53628
53374
|
init() {
|
|
53629
53375
|
const { packageBytes, timeoutMs } = this.payload;
|
|
@@ -53637,6 +53383,12 @@ class UploadPortfolio extends FileWrite {
|
|
|
53637
53383
|
run: { get: () => super.run }
|
|
53638
53384
|
});
|
|
53639
53385
|
return __awaiter(this, void 0, void 0, function* () {
|
|
53386
|
+
const protocolInfo = yield this.device.ensureProtocolV2RuntimeContext();
|
|
53387
|
+
const hasFileWrite = supportsProtocolV2Message(protocolInfo, FILESYSTEM_FILE_WRITE_MESSAGE_TYPE);
|
|
53388
|
+
const hasPortfolioUpdate = supportsProtocolV2Message(protocolInfo, PORTFOLIO_UPDATE_MESSAGE_TYPE);
|
|
53389
|
+
if (!hasFileWrite || !hasPortfolioUpdate) {
|
|
53390
|
+
throw hdShared.createDeviceNotSupportMethodError(this.name, this.device.getCurrentFirmwareType());
|
|
53391
|
+
}
|
|
53640
53392
|
const stagedFile = yield _super.run.call(this);
|
|
53641
53393
|
this.throwIfAborted();
|
|
53642
53394
|
yield this.device.commands.typedCall('PortfolioUpdate', 'Success', {});
|
|
@@ -65178,11 +64930,15 @@ exports.normalizeSafetyCheckLevel = normalizeSafetyCheckLevel;
|
|
|
65178
64930
|
exports.normalizeVersionArray = normalizeVersionArray;
|
|
65179
64931
|
exports.parseConnectSettings = parseConnectSettings;
|
|
65180
64932
|
exports.parseMessage = parseMessage;
|
|
64933
|
+
exports.parseProtocolV2ResourceManifest = parseProtocolV2ResourceManifest;
|
|
65181
64934
|
exports.patchFeatures = patchFeatures;
|
|
65182
64935
|
exports.preloadSessionCache = preloadSessionCache;
|
|
64936
|
+
exports.prepareProtocolV2ResourceFiles = prepareProtocolV2ResourceFiles;
|
|
65183
64937
|
exports.projectDeviceStateFeatures = projectFeatures;
|
|
65184
64938
|
exports.registerFirmwareUpdateHostBinding = registerFirmwareUpdateHostBinding;
|
|
64939
|
+
exports.resolveProtocolV2ResourceManifestFileUrl = resolveProtocolV2ResourceManifestFileUrl;
|
|
65185
64940
|
exports.safeThrowError = safeThrowError;
|
|
64941
|
+
exports.selectProtocolV2ResourceManifestFiles = selectProtocolV2ResourceManifestFiles;
|
|
65186
64942
|
exports.setLoggerPostMessage = setLoggerPostMessage;
|
|
65187
64943
|
exports.supportInputPinOnSoftware = supportInputPinOnSoftware;
|
|
65188
64944
|
exports.switchTransport = switchTransport;
|