@onekeyfe/hd-core 1.2.0-alpha.71 → 1.2.0-alpha.72
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__/protocol-v2-firmware-targets.test.ts +17 -1
- package/__tests__/protocol-v2.test.ts +351 -49
- package/dist/api/FirmwareUpdateV4.d.ts +2 -0
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/helpers/protocolV2FileWrite.d.ts +1 -0
- package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +109 -31
- package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/FirmwareUpdateV4.ts +122 -35
- package/src/api/helpers/protocolV2FileWrite.ts +8 -4
- package/src/types/api/firmwareUpdate.ts +1 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EDeviceType } from '@onekeyfe/hd-shared';
|
|
1
|
+
import { EDeviceType, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
2
|
|
|
3
3
|
import { ProtocolV2FirmwareTargetType } from '../src/protocols/protocol-v2/firmware';
|
|
4
4
|
import {
|
|
@@ -66,4 +66,20 @@ describe('Protocol V2 firmware target contract', () => {
|
|
|
66
66
|
})
|
|
67
67
|
).not.toThrow();
|
|
68
68
|
});
|
|
69
|
+
|
|
70
|
+
test('fails closed when SE03 or SE04 is requested without a confirmed device type', () => {
|
|
71
|
+
try {
|
|
72
|
+
assertProtocolV2FirmwareTargetsSupported(undefined, {
|
|
73
|
+
platform: 'web',
|
|
74
|
+
targetsToUpdate: ['se03'],
|
|
75
|
+
});
|
|
76
|
+
throw new Error('Expected unsupported target validation to fail');
|
|
77
|
+
} catch (error) {
|
|
78
|
+
expect(error).toMatchObject({ errorCode: HardwareErrorCode.DeviceNotSupportMethod });
|
|
79
|
+
expect(error).toHaveProperty(
|
|
80
|
+
'message',
|
|
81
|
+
'Cannot safely update se03 without a confirmed Pro2 device type'
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
69
85
|
});
|
|
@@ -6096,54 +6096,61 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6096
6096
|
});
|
|
6097
6097
|
});
|
|
6098
6098
|
|
|
6099
|
-
test('
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
6099
|
+
test.each(['resource', 'boot_resources'] as const)(
|
|
6100
|
+
'treats manual resource files as authoritative payload for %s',
|
|
6101
|
+
async target => {
|
|
6102
|
+
const resourceBundle = new Uint8Array([1, 2, 3]).buffer;
|
|
6103
|
+
const method = new FirmwareUpdateV4({
|
|
6104
|
+
id: 1,
|
|
6105
|
+
payload: {
|
|
6106
|
+
method: 'firmwareUpdateV4',
|
|
6107
|
+
platform: 'web',
|
|
6108
|
+
targetsToUpdate: [target],
|
|
6109
|
+
resourceFiles: [
|
|
6110
|
+
{
|
|
6111
|
+
binary: resourceBundle,
|
|
6112
|
+
devicePath: ' VOL0:/resource/images/images.okpkg ',
|
|
6113
|
+
},
|
|
6114
|
+
],
|
|
6115
|
+
},
|
|
6116
|
+
});
|
|
6117
|
+
method.init();
|
|
6118
|
+
(method as any).captureProtocolV2PhysicalIdentity = jest.fn().mockResolvedValue(undefined);
|
|
6119
|
+
const bootResourcesSpy = jest.spyOn(DataManager, 'getProtocolV2BootResources');
|
|
6116
6120
|
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
6129
|
-
|
|
6130
|
-
|
|
6121
|
+
(method as any).device = stubDevice({
|
|
6122
|
+
originalDescriptor: { protocolType: 'V2' },
|
|
6123
|
+
features: { deviceType: 'pro2', firmwareVersion: '0.0.0', capabilities: [] },
|
|
6124
|
+
});
|
|
6125
|
+
(method as any).prepareRemoteProtocolV2Binaries = jest.fn();
|
|
6126
|
+
(method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(true);
|
|
6127
|
+
(method as any).executeProtocolV2Update = jest.fn().mockResolvedValue(undefined);
|
|
6128
|
+
(method as any).exitProtocolV2BootloaderToNormal = jest.fn().mockResolvedValue(undefined);
|
|
6129
|
+
(method as any).waitForProtocolV2FinalFeatures = jest.fn().mockResolvedValue({
|
|
6130
|
+
bootloaderVersion: '1.0.0',
|
|
6131
|
+
bleVersion: '0.0.0',
|
|
6132
|
+
firmwareVersion: '1.0.0',
|
|
6133
|
+
});
|
|
6134
|
+
method.postTipMessage = jest.fn();
|
|
6131
6135
|
|
|
6132
|
-
|
|
6136
|
+
await method.run();
|
|
6133
6137
|
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
expect.
|
|
6137
|
-
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6138
|
+
expect(forceReloadDataSpy).not.toHaveBeenCalled();
|
|
6139
|
+
expect(bootResourcesSpy).not.toHaveBeenCalled();
|
|
6140
|
+
expect((method as any).prepareRemoteProtocolV2Binaries).not.toHaveBeenCalled();
|
|
6141
|
+
expect((method as any).executeProtocolV2Update).toHaveBeenCalledWith(
|
|
6142
|
+
expect.objectContaining({
|
|
6143
|
+
resourceBundles: [
|
|
6144
|
+
{
|
|
6145
|
+
name: 'images.okpkg',
|
|
6146
|
+
binary: resourceBundle,
|
|
6147
|
+
devicePath: 'vol0:/resource/images/images.okpkg',
|
|
6148
|
+
},
|
|
6149
|
+
],
|
|
6150
|
+
})
|
|
6151
|
+
);
|
|
6152
|
+
}
|
|
6153
|
+
);
|
|
6147
6154
|
|
|
6148
6155
|
test('rejects manual RESC bundle paths before bootloader entry', async () => {
|
|
6149
6156
|
const method = new FirmwareUpdateV4({
|
|
@@ -6270,7 +6277,13 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6270
6277
|
});
|
|
6271
6278
|
});
|
|
6272
6279
|
|
|
6273
|
-
|
|
6280
|
+
// TODO(#850/#855): PR #855 added resume-on-retry and per-chunk retry on the
|
|
6281
|
+
// writeProtocolV2File path. PR #850 replaced that path with FirmwareByteSource
|
|
6282
|
+
// streaming (protocolV2SourceUpdateProcess), which restarts a failed transfer from
|
|
6283
|
+
// offset 0 and retries the whole file instead of a single chunk. Re-enable after the
|
|
6284
|
+
// resume/per-chunk-retry behavior is re-implemented on writeFirmwareByteSource, which
|
|
6285
|
+
// needs a start-offset parameter in FirmwareArtifactSource.ts.
|
|
6286
|
+
test.skip('resumes from the last confirmed offset after an ambiguous chunk write failure', async () => {
|
|
6274
6287
|
const method = new FirmwareUpdateV4({
|
|
6275
6288
|
id: 1,
|
|
6276
6289
|
payload: {
|
|
@@ -6278,14 +6291,25 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6278
6291
|
},
|
|
6279
6292
|
});
|
|
6280
6293
|
const writeOffsets: number[] = [];
|
|
6294
|
+
const overwriteFlags: boolean[] = [];
|
|
6281
6295
|
let failedSecondChunk = false;
|
|
6282
6296
|
const typedCall = jest.fn(
|
|
6283
6297
|
(
|
|
6284
|
-
|
|
6298
|
+
name: string,
|
|
6285
6299
|
_resType: string,
|
|
6286
|
-
params: { file
|
|
6300
|
+
params: { file?: { offset: number; data: { byteLength: number } }; overwrite?: boolean }
|
|
6287
6301
|
) => {
|
|
6302
|
+
if (name === 'FilesystemPathInfoQuery') {
|
|
6303
|
+
return Promise.resolve({
|
|
6304
|
+
type: 'FilesystemPathInfo',
|
|
6305
|
+
message: { exist: true, directory: false, size: 8000 },
|
|
6306
|
+
});
|
|
6307
|
+
}
|
|
6308
|
+
if (!params.file) {
|
|
6309
|
+
return Promise.reject(new Error(`unexpected call ${name}`));
|
|
6310
|
+
}
|
|
6288
6311
|
writeOffsets.push(params.file.offset);
|
|
6312
|
+
overwriteFlags.push(params.overwrite ?? false);
|
|
6289
6313
|
if (params.file.offset === 4000 && !failedSecondChunk) {
|
|
6290
6314
|
failedSecondChunk = true;
|
|
6291
6315
|
return Promise.reject(new Error('response lost after device write'));
|
|
@@ -6331,12 +6355,105 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6331
6355
|
setTimeoutSpy.mockRestore();
|
|
6332
6356
|
}
|
|
6333
6357
|
|
|
6334
|
-
expect(writeOffsets).toEqual([0, 4000,
|
|
6358
|
+
expect(writeOffsets).toEqual([0, 4000, 4000, 8000]);
|
|
6359
|
+
expect(overwriteFlags).toEqual([true, false, false, false]);
|
|
6360
|
+
expect(typedCall.mock.calls.filter(call => call[0] === 'FilesystemPathInfoQuery')).toHaveLength(
|
|
6361
|
+
1
|
|
6362
|
+
);
|
|
6335
6363
|
expect((method as any).reconnectProtocolV2Device).toHaveBeenCalledTimes(1);
|
|
6336
6364
|
expect((method as any).verifyProtocolV2ReconnectIdentity).toHaveBeenCalledTimes(1);
|
|
6337
6365
|
expect(initialize).toHaveBeenCalledTimes(1);
|
|
6338
6366
|
});
|
|
6339
6367
|
|
|
6368
|
+
// TODO(#850/#855): PR #855 added resume-on-retry and per-chunk retry on the
|
|
6369
|
+
// writeProtocolV2File path. PR #850 replaced that path with FirmwareByteSource
|
|
6370
|
+
// streaming (protocolV2SourceUpdateProcess), which restarts a failed transfer from
|
|
6371
|
+
// offset 0 and retries the whole file instead of a single chunk. Re-enable after the
|
|
6372
|
+
// resume/per-chunk-retry behavior is re-implemented on writeFirmwareByteSource, which
|
|
6373
|
+
// needs a start-offset parameter in FirmwareArtifactSource.ts.
|
|
6374
|
+
test.skip('restarts from offset zero when remote staging is behind the confirmed offset', async () => {
|
|
6375
|
+
const method = new FirmwareUpdateV4({
|
|
6376
|
+
id: 1,
|
|
6377
|
+
payload: { method: 'firmwareUpdateV4' },
|
|
6378
|
+
});
|
|
6379
|
+
const writeOffsets: number[] = [];
|
|
6380
|
+
let failedSecondChunk = false;
|
|
6381
|
+
const typedCall = jest.fn((name: string, _resType: string, params: any) => {
|
|
6382
|
+
if (name === 'FilesystemPathInfoQuery') {
|
|
6383
|
+
return Promise.resolve({
|
|
6384
|
+
type: 'FilesystemPathInfo',
|
|
6385
|
+
message: { exist: true, directory: false, size: 3999 },
|
|
6386
|
+
});
|
|
6387
|
+
}
|
|
6388
|
+
if (name !== 'FilesystemFileWrite') {
|
|
6389
|
+
return Promise.reject(new Error(`unexpected call ${name}`));
|
|
6390
|
+
}
|
|
6391
|
+
writeOffsets.push(params.file.offset);
|
|
6392
|
+
if (params.file.offset === 4000 && !failedSecondChunk) {
|
|
6393
|
+
failedSecondChunk = true;
|
|
6394
|
+
return Promise.reject(new Error('device disconnected before write confirmation'));
|
|
6395
|
+
}
|
|
6396
|
+
return Promise.resolve({
|
|
6397
|
+
type: 'FilesystemFile',
|
|
6398
|
+
message: {
|
|
6399
|
+
processed_byte: Number(params.file.offset) + Number(params.file.data.byteLength),
|
|
6400
|
+
},
|
|
6401
|
+
});
|
|
6402
|
+
});
|
|
6403
|
+
(method as any).device = stubDevice({ getCommands: () => ({ typedCall }) });
|
|
6404
|
+
(method as any).recoverProtocolV2FileTransfer = jest.fn().mockResolvedValue(undefined);
|
|
6405
|
+
method.postProgressMessage = jest.fn();
|
|
6406
|
+
|
|
6407
|
+
await (method as any).protocolV2CommonUpdateProcess({
|
|
6408
|
+
payload: new Uint8Array(8001).buffer,
|
|
6409
|
+
filePath: 'vol0:/firmware.bin',
|
|
6410
|
+
processedSize: 0,
|
|
6411
|
+
totalSize: 8001,
|
|
6412
|
+
});
|
|
6413
|
+
|
|
6414
|
+
expect(writeOffsets).toEqual([0, 4000, 0, 4000, 8000]);
|
|
6415
|
+
expect((method as any).recoverProtocolV2FileTransfer).toHaveBeenCalledTimes(1);
|
|
6416
|
+
});
|
|
6417
|
+
|
|
6418
|
+
// TODO(#850/#855): PR #855 added resume-on-retry and per-chunk retry on the
|
|
6419
|
+
// writeProtocolV2File path. PR #850 replaced that path with FirmwareByteSource
|
|
6420
|
+
// streaming (protocolV2SourceUpdateProcess), which restarts a failed transfer from
|
|
6421
|
+
// offset 0 and retries the whole file instead of a single chunk. Re-enable after the
|
|
6422
|
+
// resume/per-chunk-retry behavior is re-implemented on writeFirmwareByteSource, which
|
|
6423
|
+
// needs a start-offset parameter in FirmwareArtifactSource.ts.
|
|
6424
|
+
test.skip('retries an idempotent firmware chunk before restarting the whole file', async () => {
|
|
6425
|
+
const method = new FirmwareUpdateV4({
|
|
6426
|
+
id: 1,
|
|
6427
|
+
payload: { method: 'firmwareUpdateV4' },
|
|
6428
|
+
});
|
|
6429
|
+
const timeoutError = ERRORS.TypedError(HardwareErrorCode.BleTimeoutError, 'response timeout');
|
|
6430
|
+
const typedCall = jest
|
|
6431
|
+
.fn()
|
|
6432
|
+
.mockRejectedValueOnce(timeoutError)
|
|
6433
|
+
.mockImplementation((_name: string, _response: string, params: any) =>
|
|
6434
|
+
Promise.resolve({
|
|
6435
|
+
type: 'FilesystemFile',
|
|
6436
|
+
message: {
|
|
6437
|
+
processed_byte: Number(params.file.offset) + Number(params.file.data.byteLength),
|
|
6438
|
+
},
|
|
6439
|
+
})
|
|
6440
|
+
);
|
|
6441
|
+
(method as any).device = stubDevice({ getCommands: () => ({ typedCall }) });
|
|
6442
|
+
(method as any).reconnectProtocolV2Device = jest.fn();
|
|
6443
|
+
method.postProgressMessage = jest.fn();
|
|
6444
|
+
|
|
6445
|
+
await (method as any).protocolV2CommonUpdateProcess({
|
|
6446
|
+
payload: new Uint8Array([1, 2, 3]).buffer,
|
|
6447
|
+
filePath: 'vol0:/firmware.bin',
|
|
6448
|
+
processedSize: 0,
|
|
6449
|
+
totalSize: 3,
|
|
6450
|
+
});
|
|
6451
|
+
|
|
6452
|
+
expect(typedCall).toHaveBeenCalledTimes(2);
|
|
6453
|
+
expect(typedCall.mock.calls.map(call => call[2].file.offset)).toEqual([0, 0]);
|
|
6454
|
+
expect((method as any).reconnectProtocolV2Device).not.toHaveBeenCalled();
|
|
6455
|
+
});
|
|
6456
|
+
|
|
6340
6457
|
test('rejects a chunk-relative processed_byte during firmware staging', async () => {
|
|
6341
6458
|
const method = new FirmwareUpdateV4({
|
|
6342
6459
|
id: 1,
|
|
@@ -6378,6 +6495,48 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6378
6495
|
expect(typedCall).toHaveBeenCalledTimes(6);
|
|
6379
6496
|
});
|
|
6380
6497
|
|
|
6498
|
+
// TODO(#850/#855): PR #855 added resume-on-retry and per-chunk retry on the
|
|
6499
|
+
// writeProtocolV2File path. PR #850 replaced that path with FirmwareByteSource
|
|
6500
|
+
// streaming (protocolV2SourceUpdateProcess), which restarts a failed transfer from
|
|
6501
|
+
// offset 0 and retries the whole file instead of a single chunk. Re-enable after the
|
|
6502
|
+
// resume/per-chunk-retry behavior is re-implemented on writeFirmwareByteSource, which
|
|
6503
|
+
// needs a start-offset parameter in FirmwareArtifactSource.ts.
|
|
6504
|
+
test.skip('coalesces public progress events without dropping confirmed-byte callbacks', async () => {
|
|
6505
|
+
const method = new FirmwareUpdateV4({
|
|
6506
|
+
id: 1,
|
|
6507
|
+
payload: { method: 'firmwareUpdateV4' },
|
|
6508
|
+
});
|
|
6509
|
+
const typedCall = jest.fn((_name: string, _resType: string, params: any) =>
|
|
6510
|
+
Promise.resolve({
|
|
6511
|
+
type: 'FilesystemFile',
|
|
6512
|
+
message: {
|
|
6513
|
+
processed_byte: Number(params.file.offset) + Number(params.file.data.byteLength),
|
|
6514
|
+
},
|
|
6515
|
+
})
|
|
6516
|
+
);
|
|
6517
|
+
const onTransferredBytes = jest.fn();
|
|
6518
|
+
const dateNowSpy = jest.spyOn(Date, 'now').mockReturnValue(1000);
|
|
6519
|
+
(method as any).device = stubDevice({ getCommands: () => ({ typedCall }) });
|
|
6520
|
+
method.postProgressMessage = jest.fn();
|
|
6521
|
+
|
|
6522
|
+
try {
|
|
6523
|
+
await (method as any).protocolV2CommonUpdateProcess({
|
|
6524
|
+
payload: new Uint8Array(1_000_000).buffer,
|
|
6525
|
+
filePath: 'vol1:firmware.bin',
|
|
6526
|
+
processedSize: 0,
|
|
6527
|
+
totalSize: 1_000_000,
|
|
6528
|
+
onTransferredBytes,
|
|
6529
|
+
});
|
|
6530
|
+
} finally {
|
|
6531
|
+
dateNowSpy.mockRestore();
|
|
6532
|
+
}
|
|
6533
|
+
|
|
6534
|
+
expect(typedCall).toHaveBeenCalledTimes(250);
|
|
6535
|
+
expect(onTransferredBytes).toHaveBeenCalledTimes(250);
|
|
6536
|
+
expect(onTransferredBytes).toHaveBeenLastCalledWith(1_000_000);
|
|
6537
|
+
expect(method.postProgressMessage).toHaveBeenCalledTimes(100);
|
|
6538
|
+
});
|
|
6539
|
+
|
|
6381
6540
|
test('caps native BLE firmware upload chunks below the WebUSB limit', async () => {
|
|
6382
6541
|
const method = new FirmwareUpdateV4({
|
|
6383
6542
|
id: 1,
|
|
@@ -6438,6 +6597,52 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6438
6597
|
]);
|
|
6439
6598
|
});
|
|
6440
6599
|
|
|
6600
|
+
test('uses the optimized BLE chunk only for fixed firmware staging paths', async () => {
|
|
6601
|
+
const method = new FirmwareUpdateV4({
|
|
6602
|
+
id: 1,
|
|
6603
|
+
payload: {
|
|
6604
|
+
method: 'firmwareUpdateV4',
|
|
6605
|
+
},
|
|
6606
|
+
});
|
|
6607
|
+
const typedCall = jest.fn(
|
|
6608
|
+
(
|
|
6609
|
+
_name: string,
|
|
6610
|
+
_resType: string,
|
|
6611
|
+
params: { file: { offset: number; data: { byteLength: number } } }
|
|
6612
|
+
) =>
|
|
6613
|
+
Promise.resolve({
|
|
6614
|
+
type: 'FilesystemFile',
|
|
6615
|
+
message: {
|
|
6616
|
+
processed_byte: params.file.offset + params.file.data.byteLength,
|
|
6617
|
+
},
|
|
6618
|
+
})
|
|
6619
|
+
);
|
|
6620
|
+
|
|
6621
|
+
(method as any).params = {
|
|
6622
|
+
platform: 'native',
|
|
6623
|
+
chunkSize: 4096,
|
|
6624
|
+
};
|
|
6625
|
+
(method as any).device = stubDevice({
|
|
6626
|
+
getCommands: () => ({ typedCall }),
|
|
6627
|
+
});
|
|
6628
|
+
method.postProgressMessage = jest.fn();
|
|
6629
|
+
|
|
6630
|
+
const source = await openFirmwareByteSource({
|
|
6631
|
+
binary: new Uint8Array(1961).buffer,
|
|
6632
|
+
});
|
|
6633
|
+
await (method as any).protocolV2SourceUpdateProcess({
|
|
6634
|
+
source,
|
|
6635
|
+
filePath: 'vol0:/application_p1.bin',
|
|
6636
|
+
processedSize: 0,
|
|
6637
|
+
totalSize: 1961,
|
|
6638
|
+
});
|
|
6639
|
+
await source?.close();
|
|
6640
|
+
|
|
6641
|
+
const writePayloads = typedCall.mock.calls.map(call => call[2]);
|
|
6642
|
+
expect(writePayloads.map(payload => payload.file.offset)).toEqual([0, 1960]);
|
|
6643
|
+
expect(writePayloads.map(payload => payload.file.data.byteLength)).toEqual([1960, 1]);
|
|
6644
|
+
});
|
|
6645
|
+
|
|
6441
6646
|
test('ends device confirmation and starts install progress only after Protocol V2 ACK', async () => {
|
|
6442
6647
|
const method = new FirmwareUpdateV4({
|
|
6443
6648
|
id: 1,
|
|
@@ -7015,6 +7220,103 @@ describe('Protocol V2 firmware reconnect identity', () => {
|
|
|
7015
7220
|
expect(downloadSpy).not.toHaveBeenCalled();
|
|
7016
7221
|
});
|
|
7017
7222
|
|
|
7223
|
+
test('continues a normal resource update when boot resources are not configured', async () => {
|
|
7224
|
+
const method = new FirmwareUpdateV4({
|
|
7225
|
+
id: 1,
|
|
7226
|
+
payload: { method: 'firmwareUpdateV4', platform: 'web', targetsToUpdate: ['resource'] },
|
|
7227
|
+
});
|
|
7228
|
+
method.init();
|
|
7229
|
+
(method as any).device = stubDevice({ getCurrentDeviceType: () => 'pro2' });
|
|
7230
|
+
jest.spyOn(DataManager, 'getProtocolV2BootResources').mockReturnValue(undefined);
|
|
7231
|
+
const downloadSpy = jest.spyOn(firmwareBinaryApi, 'getSysResourceBinary');
|
|
7232
|
+
|
|
7233
|
+
await expect((method as any).prepareProtocolV2BootResources()).resolves.toBeUndefined();
|
|
7234
|
+
expect(downloadSpy).not.toHaveBeenCalled();
|
|
7235
|
+
});
|
|
7236
|
+
|
|
7237
|
+
test('requires boot configuration for an explicit boot_resources target', async () => {
|
|
7238
|
+
const method = new FirmwareUpdateV4({
|
|
7239
|
+
id: 1,
|
|
7240
|
+
payload: {
|
|
7241
|
+
method: 'firmwareUpdateV4',
|
|
7242
|
+
platform: 'web',
|
|
7243
|
+
targetsToUpdate: ['boot_resources'],
|
|
7244
|
+
},
|
|
7245
|
+
});
|
|
7246
|
+
method.init();
|
|
7247
|
+
(method as any).device = stubDevice({ getCurrentDeviceType: () => 'pro2' });
|
|
7248
|
+
jest.spyOn(DataManager, 'getProtocolV2BootResources').mockReturnValue(undefined);
|
|
7249
|
+
|
|
7250
|
+
await expect((method as any).prepareProtocolV2BootResources()).rejects.toThrow(
|
|
7251
|
+
'Missing Protocol V2 boot resources configuration'
|
|
7252
|
+
);
|
|
7253
|
+
});
|
|
7254
|
+
|
|
7255
|
+
test('skips downloading a boot resource when the installed SHA-256 matches', async () => {
|
|
7256
|
+
const bytes = new Uint8Array([1, 2, 3, 4]);
|
|
7257
|
+
const fileHash = Array.from(sha256(bytes), byte => byte.toString(16).padStart(2, '0')).join('');
|
|
7258
|
+
const resource = {
|
|
7259
|
+
required: false as const,
|
|
7260
|
+
target: 'RES' as const,
|
|
7261
|
+
files: [
|
|
7262
|
+
{
|
|
7263
|
+
name: 'bootloader_crest.bin',
|
|
7264
|
+
url: 'https://example.com/bootloader_crest.bin',
|
|
7265
|
+
devicePath: 'vol0:/assets/loaders/boot.staging/graphics/bootloader_crest.bin',
|
|
7266
|
+
size: bytes.byteLength,
|
|
7267
|
+
fileHash,
|
|
7268
|
+
},
|
|
7269
|
+
],
|
|
7270
|
+
};
|
|
7271
|
+
const typedCall = jest.fn((name: string, _response: string, payload: any) => {
|
|
7272
|
+
if (name === 'FilesystemPathInfoQuery') {
|
|
7273
|
+
return Promise.resolve({
|
|
7274
|
+
message: { exist: true, directory: false, size: bytes.byteLength },
|
|
7275
|
+
});
|
|
7276
|
+
}
|
|
7277
|
+
if (name === 'FilesystemFileRead') {
|
|
7278
|
+
const offset = Number(payload.file.offset);
|
|
7279
|
+
return Promise.resolve({
|
|
7280
|
+
message: { data: bytes.slice(offset, offset + Number(payload.chunk_len)) },
|
|
7281
|
+
});
|
|
7282
|
+
}
|
|
7283
|
+
return Promise.reject(new Error(`Unexpected request: ${name}`));
|
|
7284
|
+
});
|
|
7285
|
+
const method = new FirmwareUpdateV4({
|
|
7286
|
+
id: 1,
|
|
7287
|
+
payload: { method: 'firmwareUpdateV4', platform: 'web', targetsToUpdate: ['resource'] },
|
|
7288
|
+
});
|
|
7289
|
+
method.init();
|
|
7290
|
+
(method as any).device = stubDevice({
|
|
7291
|
+
getCurrentDeviceType: () => 'pro2',
|
|
7292
|
+
getCommands: () => ({ typedCall }),
|
|
7293
|
+
});
|
|
7294
|
+
jest.spyOn(DataManager, 'getProtocolV2BootResources').mockReturnValue(resource);
|
|
7295
|
+
const downloadSpy = jest.spyOn(firmwareBinaryApi, 'getSysResourceBinary');
|
|
7296
|
+
|
|
7297
|
+
await expect((method as any).prepareProtocolV2BootResources()).resolves.toEqual([]);
|
|
7298
|
+
expect(typedCall.mock.calls.map(call => call[0])).toEqual([
|
|
7299
|
+
'FilesystemPathInfoQuery',
|
|
7300
|
+
'FilesystemFileRead',
|
|
7301
|
+
]);
|
|
7302
|
+
expect(downloadSpy).not.toHaveBeenCalled();
|
|
7303
|
+
});
|
|
7304
|
+
|
|
7305
|
+
test('rejects duplicate device paths across explicit and remote resource sources', () => {
|
|
7306
|
+
const method = new FirmwareUpdateV4({
|
|
7307
|
+
id: 1,
|
|
7308
|
+
payload: { method: 'firmwareUpdateV4' },
|
|
7309
|
+
});
|
|
7310
|
+
const devicePath = 'vol0:/assets/shared.bin';
|
|
7311
|
+
|
|
7312
|
+
expect(() =>
|
|
7313
|
+
(method as any).mergeProtocolV2ResourceBundles(
|
|
7314
|
+
[{ name: 'local.bin', binary: new ArrayBuffer(1), devicePath }],
|
|
7315
|
+
[{ name: 'remote.bin', binary: new ArrayBuffer(1), devicePath }]
|
|
7316
|
+
)
|
|
7317
|
+
).toThrow(`Duplicate Protocol V2 resource devicePath: ${devicePath}`);
|
|
7318
|
+
});
|
|
7319
|
+
|
|
7018
7320
|
test('includes startup resources in the complete resource target', async () => {
|
|
7019
7321
|
const bytes = new Uint8Array([1, 2, 3, 4]);
|
|
7020
7322
|
const binary = bytes.buffer as ArrayBuffer;
|
|
@@ -47,6 +47,8 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
47
47
|
private prepareRemoteProtocolV2Binaries;
|
|
48
48
|
private getProtocolV2DeviceType;
|
|
49
49
|
private prepareProtocolV2BootResources;
|
|
50
|
+
private isProtocolV2BootResourceCurrent;
|
|
51
|
+
private mergeProtocolV2ResourceBundles;
|
|
50
52
|
private prepareExplicitProtocolV2ResourceFiles;
|
|
51
53
|
private prepareProtocolV2ResourceBundles;
|
|
52
54
|
private downloadProtocolV2Resource;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FirmwareUpdateV4.d.ts","sourceRoot":"","sources":["../../src/api/FirmwareUpdateV4.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAkD,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"FirmwareUpdateV4.d.ts","sourceRoot":"","sources":["../../src/api/FirmwareUpdateV4.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAkD,MAAM,qBAAqB,CAAC;AAyBlG,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAwB/E,OAAO,KAAK,EAEV,sBAAsB,EAEvB,MAAM,6BAA6B,CAAC;AA2CrC,wBAAgB,wCAAwC,CACtD,UAAU,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,EAC5C,MAAM,EAAE,sBAAsB,QAgB/B;AAgUD,eAAO,MAAM,oCAAoC,WACvC,WAAW,GAAG,UAAU,eACnB,MAAM,GAAG,SAAS,YAKhC,CAAC;AAEF,eAAO,MAAM,iCAAiC,0BACrB,MAAM,uBACR,MAAM,iBACZ,MAAM,eACR,MAAM,SAsBpB,CAAC;AAUF,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,wBAAwB,CAAC,sBAAsB,CAAC;IAC5F,OAAO,CAAC,8BAA8B,CAAC,CAAS;IAEhD,OAAO,CAAC,sBAAsB,CAAC,CAAS;IAExC,qBAAqB;IAIrB,OAAO,CAAC,yBAAyB,CAA4B;IAE7D,OAAO,CAAC,2BAA2B,CAAS;IAE5C,OAAO,CAAC,iCAAiC,CAA6B;IAEtE,OAAO,CAAC,6BAA6B,CAAC,CAAW;IAEjD,OAAO,CAAC,6BAA6B,CAAS;IAE9C,IAAI;IAoHJ,OAAO,CAAC,8BAA8B;IAwBhC,GAAG;;;;;YAKK,aAAa;YAwHb,4BAA4B;YAkB5B,0BAA0B;YAY1B,8BAA8B;YAK9B,+BAA+B;YAqD/B,gCAAgC;YAmDhC,8BAA8B;IAsB5C,OAAO,CAAC,8BAA8B;IA0BtC,OAAO,CAAC,kCAAkC;IAc1C,OAAO,CAAC,gCAAgC;IAsDxC,OAAO,CAAC,6BAA6B;YAsBvB,2BAA2B;IAWzC,OAAO,CAAC,yBAAyB;IAKjC,OAAO,CAAC,kCAAkC;IAS1C,OAAO,CAAC,8BAA8B;YAMxB,iCAAiC;YAOjC,iCAAiC;YAkBjC,iCAAiC;IAM/C,OAAO,CAAC,uBAAuB;IAI/B,OAAO,CAAC,4BAA4B;IAQpC,OAAO,CAAC,2BAA2B;IAsBnC,OAAO,CAAC,yBAAyB;IAiBjC,OAAO,CAAC,wBAAwB;YAkBlB,iCAAiC;YAmCjC,+BAA+B;IAqD7C,OAAO,CAAC,uBAAuB;YAMjB,8BAA8B;YA0C9B,+BAA+B;IAgD7C,OAAO,CAAC,8BAA8B;IAetC,OAAO,CAAC,sCAAsC;YAmChC,gCAAgC;YAqChC,0BAA0B;IAexC,OAAO,CAAC,6BAA6B;YAMvB,8BAA8B;YA8C9B,kCAAkC;IAgChD,OAAO,CAAC,0BAA0B;IAUlC,OAAO,CAAC,yBAAyB;IAc3B,6BAA6B;YAgCrB,+BAA+B;IAkD7C,OAAO,CAAC,6BAA6B;IAkCrC,OAAO,CAAC,8BAA8B;YA8CxB,uBAAuB;YAiCvB,6BAA6B;YAa7B,uBAAuB;YA0CvB,8BAA8B;YA2D9B,6BAA6B;IAuE3C,OAAO,CAAC,mCAAmC;YAI7B,0BAA0B;IAaxC,OAAO,CAAC,4BAA4B;IAyGpC,OAAO,CAAC,6BAA6B;YAcvB,uCAAuC;YA0JvC,gCAAgC;YAehC,yBAAyB;YAQzB,8BAA8B;IAQ5C,OAAO,CAAC,0BAA0B;YAkBpB,mCAAmC;YAWnC,qCAAqC;YAgDrC,yBAAyB;YA8DzB,8BAA8B;IAU5C,OAAO,CAAC,kCAAkC;YAQ5B,cAAc;YAuCd,6BAA6B;YAW7B,0BAA0B;YAS1B,6BAA6B;YAmB7B,gBAAgB;IAiB9B,OAAO,CAAC,qBAAqB;CAM9B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocolV2FileWrite.d.ts","sourceRoot":"","sources":["../../../src/api/helpers/protocolV2FileWrite.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAIlE,MAAM,MAAM,uBAAuB,GAAG,WAAW,GAAG,UAAU,GAAG,IAAI,GAAG,MAAM,CAAC;AAE/E,MAAM,MAAM,2BAA2B,GAAG;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,uBAAuB,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,+BAA+B,KAAK,MAAM,GAAG,SAAS,CAAC;IACnF,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,2BAA2B,KAAK,IAAI,CAAC;CAC9D,CAAC;AAyDF,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,WASzD;AA8CD,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B;;;;;;
|
|
1
|
+
{"version":3,"file":"protocolV2FileWrite.d.ts","sourceRoot":"","sources":["../../../src/api/helpers/protocolV2FileWrite.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAIlE,MAAM,MAAM,uBAAuB,GAAG,WAAW,GAAG,UAAU,GAAG,IAAI,GAAG,MAAM,CAAC;AAE/E,MAAM,MAAM,2BAA2B,GAAG;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,uBAAuB,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,+BAA+B,KAAK,MAAM,GAAG,SAAS,CAAC;IACnF,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,2BAA2B,KAAK,IAAI,CAAC;CAC9D,CAAC;AAyDF,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,WASzD;AA8CD,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B;;;;;;GAuO5E"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1454,6 +1454,7 @@ interface FirmwareUpdateV4Params {
|
|
|
1454
1454
|
/**
|
|
1455
1455
|
* Arbitrary Protocol V2 resource files written directly with FilesystemFileWrite.
|
|
1456
1456
|
* Use this for manifest-driven boot resources and other non-RESC files.
|
|
1457
|
+
* When provided, these files are authoritative for resource and boot_resources targets.
|
|
1457
1458
|
*/
|
|
1458
1459
|
resourceFiles?: Array<{
|
|
1459
1460
|
binary: ArrayBuffer;
|
package/dist/index.js
CHANGED
|
@@ -50952,16 +50952,18 @@ const PROTOCOL_V2_OKPP_HASH_SIZE = 64;
|
|
|
50952
50952
|
const PROTOCOL_V2_NEO_UNSUPPORTED_TARGETS = new Set(['se03', 'se04']);
|
|
50953
50953
|
function assertProtocolV2FirmwareTargetsSupported(deviceType, params) {
|
|
50954
50954
|
var _a;
|
|
50955
|
-
if (deviceType !== hdShared.EDeviceType.Neo)
|
|
50956
|
-
return;
|
|
50957
50955
|
const unsupportedTargets = new Set(((_a = params.targetsToUpdate) !== null && _a !== void 0 ? _a : []).filter(target => PROTOCOL_V2_NEO_UNSUPPORTED_TARGETS.has(target)));
|
|
50958
50956
|
if (params.se03Binary)
|
|
50959
50957
|
unsupportedTargets.add('se03');
|
|
50960
50958
|
if (params.se04Binary)
|
|
50961
50959
|
unsupportedTargets.add('se04');
|
|
50962
|
-
if (unsupportedTargets.size)
|
|
50963
|
-
|
|
50964
|
-
|
|
50960
|
+
if (!unsupportedTargets.size || deviceType === hdShared.EDeviceType.Pro2)
|
|
50961
|
+
return;
|
|
50962
|
+
const targetList = Array.from(unsupportedTargets).join(', ');
|
|
50963
|
+
const message = deviceType === hdShared.EDeviceType.Neo
|
|
50964
|
+
? `Neo only supports SE01 and SE02; unsupported firmware targets: ${targetList}`
|
|
50965
|
+
: `Cannot safely update ${targetList} without a confirmed Pro2 device type`;
|
|
50966
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotSupportMethod, message);
|
|
50965
50967
|
}
|
|
50966
50968
|
const getProtocolV2DeviceTransferProgress = (bytesBeforeChunk, bytesAfterChunk, totalBytes) => {
|
|
50967
50969
|
if (!Number.isFinite(totalBytes) || totalBytes <= 0) {
|
|
@@ -51017,6 +51019,7 @@ const PROTOCOL_V2_REMOTE_COMPONENT_TARGETS = {
|
|
|
51017
51019
|
kind: 'firmware',
|
|
51018
51020
|
},
|
|
51019
51021
|
};
|
|
51022
|
+
const PROTOCOL_V2_FIRMWARE_STAGING_PATHS = new Set(Object.values(PROTOCOL_V2_REMOTE_COMPONENT_TARGETS).map(target => `${PROTOCOL_V2_FIRMWARE_STAGING_VOLUME}${target.fileName}`));
|
|
51020
51023
|
const PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID = new Map([
|
|
51021
51024
|
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER, 'boot'],
|
|
51022
51025
|
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1, 'app_v1'],
|
|
@@ -51292,17 +51295,21 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
51292
51295
|
resourceBundleArtifacts: payload.resourceBundleArtifacts,
|
|
51293
51296
|
};
|
|
51294
51297
|
}
|
|
51295
|
-
getProtocolV2FirmwareChunkSize(direction = 'write') {
|
|
51298
|
+
getProtocolV2FirmwareChunkSize(direction = 'write', filePath) {
|
|
51296
51299
|
var _a, _b;
|
|
51297
51300
|
const payloadChunkSize = Number((_a = this.params) === null || _a === void 0 ? void 0 : _a.chunkSize);
|
|
51298
51301
|
const env = DataManager.getSettings('env');
|
|
51299
51302
|
const isBle = ((_b = this.params) === null || _b === void 0 ? void 0 : _b.platform) === 'native' || (env && DataManager.isBleConnect(env));
|
|
51300
51303
|
let maxChunkSize = hdTransport.PROTOCOL_V2_WEBUSB_FILE_CHUNK_SIZE;
|
|
51301
51304
|
if (isBle) {
|
|
51302
|
-
|
|
51303
|
-
|
|
51304
|
-
|
|
51305
|
+
if (direction === 'read') {
|
|
51306
|
+
maxChunkSize = hdTransport.PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE;
|
|
51307
|
+
}
|
|
51308
|
+
else {
|
|
51309
|
+
maxChunkSize = PROTOCOL_V2_FIRMWARE_STAGING_PATHS.has(filePath !== null && filePath !== void 0 ? filePath : '')
|
|
51310
|
+
? hdTransport.PROTOCOL_V2_BLE_FIRMWARE_FILE_CHUNK_SIZE
|
|
51305
51311
|
: hdTransport.PROTOCOL_V2_BLE_FILE_CHUNK_SIZE;
|
|
51312
|
+
}
|
|
51306
51313
|
}
|
|
51307
51314
|
if (!Number.isFinite(payloadChunkSize) || payloadChunkSize <= 0) {
|
|
51308
51315
|
return maxChunkSize;
|
|
@@ -51316,7 +51323,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
51316
51323
|
});
|
|
51317
51324
|
}
|
|
51318
51325
|
runProtocolV2() {
|
|
51319
|
-
var _a, _b, _c, _d, _e
|
|
51326
|
+
var _a, _b, _c, _d, _e;
|
|
51320
51327
|
return __awaiter(this, void 0, void 0, function* () {
|
|
51321
51328
|
yield this.captureProtocolV2PhysicalIdentity();
|
|
51322
51329
|
const deviceFeatures = yield this.getProtocolV2DeviceFeatures();
|
|
@@ -51332,9 +51339,11 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
51332
51339
|
((_b = this.params.resourceBundleArtifacts) === null || _b === void 0 ? void 0 : _b.length)) {
|
|
51333
51340
|
return this.runProtocolV2PreparedArtifacts(deviceFeatures, firmwareType);
|
|
51334
51341
|
}
|
|
51335
|
-
const
|
|
51336
|
-
const
|
|
51337
|
-
|
|
51342
|
+
const hasExplicitResourceFiles = !!((_c = this.params.resourceFiles) === null || _c === void 0 ? void 0 : _c.length);
|
|
51343
|
+
const wantsStableResources = !!((_d = this.params.targetsToUpdate) === null || _d === void 0 ? void 0 : _d.includes('resource'));
|
|
51344
|
+
const wantsBootResources = !!((_e = this.params.targetsToUpdate) === null || _e === void 0 ? void 0 : _e.includes('boot_resources'));
|
|
51345
|
+
const needsRemoteResources = !hasExplicitResourceFiles && wantsStableResources;
|
|
51346
|
+
const needsRemoteBootResources = !hasExplicitResourceFiles && (wantsBootResources || wantsStableResources);
|
|
51338
51347
|
let fwBinaryMap = [];
|
|
51339
51348
|
let bootloaderBinary = null;
|
|
51340
51349
|
let installItems;
|
|
@@ -51365,7 +51374,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
51365
51374
|
}
|
|
51366
51375
|
const bootResourceFiles = yield this.prepareProtocolV2BootResources();
|
|
51367
51376
|
if (bootResourceFiles === null || bootResourceFiles === void 0 ? void 0 : bootResourceFiles.length) {
|
|
51368
|
-
resourceBundles =
|
|
51377
|
+
resourceBundles = this.mergeProtocolV2ResourceBundles(resourceBundles, bootResourceFiles);
|
|
51369
51378
|
}
|
|
51370
51379
|
if (!needsRemoteResources) {
|
|
51371
51380
|
this.postTipMessage(exports.FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
@@ -51388,7 +51397,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
51388
51397
|
const enteredBootloader = yield this.enterProtocolV2BootloaderMode();
|
|
51389
51398
|
try {
|
|
51390
51399
|
const stableResources = yield this.prepareProtocolV2ResourceBundles();
|
|
51391
|
-
resourceBundles =
|
|
51400
|
+
resourceBundles = this.mergeProtocolV2ResourceBundles(resourceBundles, stableResources);
|
|
51392
51401
|
this.postTipMessage(exports.FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
51393
51402
|
}
|
|
51394
51403
|
catch (err) {
|
|
@@ -51817,31 +51826,95 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
51817
51826
|
throw new Error(`Unsupported Protocol V2 device type: ${deviceType}`);
|
|
51818
51827
|
}
|
|
51819
51828
|
prepareProtocolV2BootResources() {
|
|
51820
|
-
var _a, _b, _c, _d;
|
|
51829
|
+
var _a, _b, _c, _d, _e;
|
|
51821
51830
|
return __awaiter(this, void 0, void 0, function* () {
|
|
51822
|
-
|
|
51823
|
-
|
|
51831
|
+
const wantsStableResources = !!((_a = this.params.targetsToUpdate) === null || _a === void 0 ? void 0 : _a.includes('resource'));
|
|
51832
|
+
const wantsBootResources = !!((_b = this.params.targetsToUpdate) === null || _b === void 0 ? void 0 : _b.includes('boot_resources'));
|
|
51833
|
+
if (!wantsStableResources && !wantsBootResources) {
|
|
51834
|
+
return undefined;
|
|
51835
|
+
}
|
|
51836
|
+
if ((_c = this.params.resourceFiles) === null || _c === void 0 ? void 0 : _c.length) {
|
|
51824
51837
|
return undefined;
|
|
51825
51838
|
}
|
|
51826
51839
|
const resource = DataManager.getProtocolV2BootResources(this.getProtocolV2DeviceType());
|
|
51827
|
-
if (!resource)
|
|
51828
|
-
|
|
51840
|
+
if (!resource) {
|
|
51841
|
+
if (wantsBootResources) {
|
|
51842
|
+
throw new Error('Missing Protocol V2 boot resources configuration');
|
|
51843
|
+
}
|
|
51844
|
+
Log$6.debug('[FirmwareUpdateV4] no boot resources configured; continue with stable resources');
|
|
51845
|
+
return undefined;
|
|
51846
|
+
}
|
|
51829
51847
|
const files = [];
|
|
51830
51848
|
for (const file of resource.files) {
|
|
51831
|
-
|
|
51832
|
-
|
|
51833
|
-
|
|
51834
|
-
|
|
51849
|
+
const isCurrent = !this.params.forcedUpdateRes && (yield this.isProtocolV2BootResourceCurrent(file));
|
|
51850
|
+
if (isCurrent) {
|
|
51851
|
+
Log$6.log(`[FirmwareUpdateV4] boot resource unchanged, skipping ${file.devicePath}`);
|
|
51852
|
+
}
|
|
51853
|
+
else {
|
|
51854
|
+
Log$6.log(`[FirmwareUpdateV4] downloading boot resource ${file.devicePath}`);
|
|
51855
|
+
const { binary } = yield getSysResourceBinary(file.url);
|
|
51856
|
+
if (!isProtocolV2ResourceFileValid(binary, file)) {
|
|
51857
|
+
throw new Error(`Boot resource file verification failed: ${file.devicePath}`);
|
|
51858
|
+
}
|
|
51859
|
+
files.push({
|
|
51860
|
+
name: (_e = (_d = file.name) !== null && _d !== void 0 ? _d : file.devicePath.split('/').pop()) !== null && _e !== void 0 ? _e : file.devicePath,
|
|
51861
|
+
binary,
|
|
51862
|
+
devicePath: file.devicePath,
|
|
51863
|
+
});
|
|
51835
51864
|
}
|
|
51836
|
-
files.push({
|
|
51837
|
-
name: (_d = (_c = file.name) !== null && _c !== void 0 ? _c : file.devicePath.split('/').pop()) !== null && _d !== void 0 ? _d : file.devicePath,
|
|
51838
|
-
binary,
|
|
51839
|
-
devicePath: file.devicePath,
|
|
51840
|
-
});
|
|
51841
51865
|
}
|
|
51842
51866
|
return files;
|
|
51843
51867
|
});
|
|
51844
51868
|
}
|
|
51869
|
+
isProtocolV2BootResourceCurrent(file) {
|
|
51870
|
+
var _a, _b, _c, _d;
|
|
51871
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
51872
|
+
try {
|
|
51873
|
+
const commands = this.device.getCommands();
|
|
51874
|
+
const pathInfo = yield commands.typedCall('FilesystemPathInfoQuery', 'FilesystemPathInfo', { path: file.devicePath }, { timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT });
|
|
51875
|
+
const size = toProtocolV2FiniteNumber((_a = pathInfo.message) === null || _a === void 0 ? void 0 : _a.size);
|
|
51876
|
+
if (!((_b = pathInfo.message) === null || _b === void 0 ? void 0 : _b.exist) ||
|
|
51877
|
+
((_c = pathInfo.message) === null || _c === void 0 ? void 0 : _c.directory) ||
|
|
51878
|
+
!Number.isSafeInteger(size) ||
|
|
51879
|
+
size !== file.size) {
|
|
51880
|
+
return false;
|
|
51881
|
+
}
|
|
51882
|
+
const digest = sha256.sha256.create();
|
|
51883
|
+
const chunkSize = this.getProtocolV2FirmwareChunkSize();
|
|
51884
|
+
let offset = 0;
|
|
51885
|
+
while (offset < file.size) {
|
|
51886
|
+
const response = yield commands.typedCall('FilesystemFileRead', 'FilesystemFile', {
|
|
51887
|
+
file: { path: file.devicePath, offset, total_size: 0 },
|
|
51888
|
+
chunk_len: Math.min(chunkSize, file.size - offset),
|
|
51889
|
+
}, { timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT });
|
|
51890
|
+
const data = toProtocolV2Bytes((_d = response.message) === null || _d === void 0 ? void 0 : _d.data);
|
|
51891
|
+
if (data.byteLength === 0)
|
|
51892
|
+
return false;
|
|
51893
|
+
const consumed = data.subarray(0, Math.min(data.byteLength, file.size - offset));
|
|
51894
|
+
digest.update(consumed);
|
|
51895
|
+
offset += consumed.byteLength;
|
|
51896
|
+
}
|
|
51897
|
+
return bytesToHex(digest.digest()) === normalizeProtocolV2Hex(file.fileHash);
|
|
51898
|
+
}
|
|
51899
|
+
catch (error) {
|
|
51900
|
+
Log$6.debug(`[FirmwareUpdateV4] unable to compare boot resource ${file.devicePath}; scheduling rewrite`, error);
|
|
51901
|
+
return false;
|
|
51902
|
+
}
|
|
51903
|
+
});
|
|
51904
|
+
}
|
|
51905
|
+
mergeProtocolV2ResourceBundles(...groups) {
|
|
51906
|
+
const merged = groups.flatMap(group => group !== null && group !== void 0 ? group : []);
|
|
51907
|
+
if (!merged.length)
|
|
51908
|
+
return undefined;
|
|
51909
|
+
const seenPaths = new Set();
|
|
51910
|
+
for (const bundle of merged) {
|
|
51911
|
+
if (seenPaths.has(bundle.devicePath)) {
|
|
51912
|
+
throw new Error(`Duplicate Protocol V2 resource devicePath: ${bundle.devicePath}`);
|
|
51913
|
+
}
|
|
51914
|
+
seenPaths.add(bundle.devicePath);
|
|
51915
|
+
}
|
|
51916
|
+
return merged;
|
|
51917
|
+
}
|
|
51845
51918
|
prepareExplicitProtocolV2ResourceFiles() {
|
|
51846
51919
|
var _a;
|
|
51847
51920
|
const files = (_a = this.params.resourceFiles) !== null && _a !== void 0 ? _a : [];
|
|
@@ -52272,7 +52345,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
52272
52345
|
const transferStartedAt = Date.now();
|
|
52273
52346
|
yield writeFirmwareByteSource({
|
|
52274
52347
|
source,
|
|
52275
|
-
chunkSize: this.getProtocolV2FirmwareChunkSize(),
|
|
52348
|
+
chunkSize: this.getProtocolV2FirmwareChunkSize('write', filePath),
|
|
52276
52349
|
write: ({ data, sourceOffset, length, first }) => __awaiter(this, void 0, void 0, function* () {
|
|
52277
52350
|
const chunkEnd = sourceOffset + length;
|
|
52278
52351
|
const deviceProgress = getProtocolV2DeviceTransferProgress(processedSize + sourceOffset, processedSize + chunkEnd, totalSize);
|
|
@@ -52935,7 +53008,12 @@ function writeProtocolV2File(options) {
|
|
|
52935
53008
|
if (totalSize < startOffset + dataLength) {
|
|
52936
53009
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `FilesystemFileWrite totalSize ${totalSize} is smaller than offset + data length ${startOffset + dataLength}`);
|
|
52937
53010
|
}
|
|
52938
|
-
const
|
|
53011
|
+
const defaultChunkSizeLimit = getProtocolV2FileChunkLimit();
|
|
53012
|
+
const configuredChunkSizeLimit = Number(options.chunkSizeLimit);
|
|
53013
|
+
const chunkSizeLimit = Number.isFinite(configuredChunkSizeLimit) && configuredChunkSizeLimit > 0
|
|
53014
|
+
? Math.floor(configuredChunkSizeLimit)
|
|
53015
|
+
: defaultChunkSizeLimit;
|
|
53016
|
+
const chunkSize = normalizeChunkSize((_b = options.chunkSize) !== null && _b !== void 0 ? _b : options.chunkLen, chunkSizeLimit);
|
|
52939
53017
|
let written = 0;
|
|
52940
53018
|
let chunks = 0;
|
|
52941
53019
|
let lastMessage;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"firmwareUpdate.d.ts","sourceRoot":"","sources":["../../../src/types/api/firmwareUpdate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAClD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAE/E,KAAK,WAAW,GAAG,UAAU,GAAG,KAAK,CAAC;AAEtC,MAAM,WAAW,yBAAyB;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,KAAK,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAC5C,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC,CAAC;IACH,IAAI,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QACzE,IAAI,EAAE,WAAW,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,GAAG,EAAE,OAAO,CAAC;KACd,CAAC,CAAC;IACH,KAAK,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD;AAED,MAAM,WAAW,yBAAyB;IACxC,cAAc,EAAE,sBAAsB,CAAC;CACxC;AAED,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,WAAW,CAAC;IACpB,UAAU,EAAE,WAAW,CAAC;CACzB;AAED,MAAM,WAAW,4BAA4B;IAC3C,YAAY,EAAE,0BAA0B,CAAC;IACzC,qBAAqB,EAAE,MAAM,CAAC;IAC9B,QAAQ,EAAE,yBAAyB,CAAC;IACpC,eAAe,CAAC,EAAE,KAAK,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,yBAAyB,CAAC;KACrC,CAAC,CAAC;IACH,UAAU,EAAE,WAAW,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,YAAY,CAAC,EAAE,aAAa,CAAC;CAC9B;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,EAAE,WAAW,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,YAAY,CAAC,EAAE,aAAa,CAAC;CAC9B;AAED,MAAM,CAAC,OAAO,UAAU,cAAc,CACpC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,MAAM,EAAE,MAAM,CAAC,oBAAoB,CAAC,GAAG;IAAE,eAAe,CAAC,EAAE,OAAO,CAAA;CAAE,GACnE,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AAC3B,MAAM,CAAC,OAAO,UAAU,cAAc,CACpC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,MAAM,EAAE,MAAM,CAAC,0BAA0B,CAAC,GAAG;IAAE,eAAe,CAAC,EAAE,OAAO,CAAA;CAAE,GACzE,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AAE3B,KAAK,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG,WAAW,CAAC;AACpE,KAAK,QAAQ,GAAG;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,CAAC;AAExC,MAAM,CAAC,OAAO,UAAU,gBAAgB,CACtC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,MAAM,EAAE,MAAM,CAAC,oBAAoB,GAAG,QAAQ,CAAC,GAC9C,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AAC3B,MAAM,CAAC,OAAO,UAAU,gBAAgB,CACtC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,MAAM,EAAE,MAAM,CAAC,0BAA0B,GAAG,QAAQ,CAAC,GACpD,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AAC3B,MAAM,CAAC,OAAO,UAAU,gBAAgB,CACtC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,MAAM,EAAE,MAAM,CAAC,4BAA4B,GAAG,QAAQ,CAAC,GACtD,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AAE3B,MAAM,WAAW,sBAAsB;IACrC,YAAY,CAAC,EAAE,0BAA0B,CAAC;IAC1C,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,cAAc,CAAC,EAAE,WAAW,CAAC;IAE7B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,gBAAgB,CAAC,EAAE,WAAW,CAAC;IAE/B,cAAc,CAAC,EAAE,WAAW,CAAC;IAC7B,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B,YAAY,CAAC,EAAE,aAAa,CAAC;IAE7B,QAAQ,EAAE,SAAS,CAAC;IAEpB,cAAc,CAAC,EAAE,sBAAsB,CAAC;IACxC,SAAS,CAAC,EAAE;QACV,GAAG,CAAC,EAAE,yBAAyB,CAAC;QAChC,QAAQ,CAAC,EAAE,yBAAyB,CAAC;QACrC,UAAU,CAAC,EAAE,yBAAyB,CAAC;QACvC,eAAe,CAAC,EAAE,KAAK,CAAC;YACtB,SAAS,EAAE,MAAM,CAAC;YAClB,QAAQ,EAAE,yBAAyB,CAAC;SACrC,CAAC,CAAC;KACJ,CAAC;CACH;AAMD,MAAM,MAAM,sBAAsB,GAC9B,MAAM,GACN,gBAAgB,GAChB,QAAQ,GACR,QAAQ,GACR,aAAa,GACb,UAAU,GACV,MAAM,GACN,MAAM,GACN,MAAM,GACN,MAAM,CAAC;AAEX,MAAM,WAAW,sBAAsB;IACrC,YAAY,CAAC,EAAE,0BAA0B,CAAC;IAC1C,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,EAAE,SAAS,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,eAAe,CAAC,EAAE,sBAAsB,EAAE,CAAC;IAC3C,sBAAsB,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,sBAAsB,EAAE,MAAM,CAAC,CAAC,CAAC;IAGzE,eAAe,CAAC,EAAE,WAAW,CAAC;IAE9B,gBAAgB,CAAC,EAAE,WAAW,CAAC;IAE/B,mBAAmB,CAAC,EAAE,WAAW,CAAC;IAElC,mBAAmB,CAAC,EAAE,WAAW,CAAC;IAElC,iBAAiB,CAAC,EAAE,WAAW,CAAC;IAEhC,UAAU,CAAC,EAAE,WAAW,CAAC;IACzB,UAAU,CAAC,EAAE,WAAW,CAAC;IACzB,UAAU,CAAC,EAAE,WAAW,CAAC;IACzB,UAAU,CAAC,EAAE,WAAW,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"firmwareUpdate.d.ts","sourceRoot":"","sources":["../../../src/types/api/firmwareUpdate.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-core",
|
|
3
|
-
"version": "1.2.0-alpha.
|
|
3
|
+
"version": "1.2.0-alpha.72",
|
|
4
4
|
"description": "Core processes and APIs for communicating with OneKey hardware devices.",
|
|
5
5
|
"author": "OneKey",
|
|
6
6
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"url": "https://github.com/OneKeyHQ/hardware-js-sdk/issues"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@onekeyfe/hd-shared": "1.2.0-alpha.
|
|
29
|
-
"@onekeyfe/hd-transport": "1.2.0-alpha.
|
|
28
|
+
"@onekeyfe/hd-shared": "1.2.0-alpha.72",
|
|
29
|
+
"@onekeyfe/hd-transport": "1.2.0-alpha.72",
|
|
30
30
|
"axios": "1.15.2",
|
|
31
31
|
"bignumber.js": "^9.0.2",
|
|
32
32
|
"bytebuffer": "^5.0.1",
|
|
@@ -44,5 +44,5 @@
|
|
|
44
44
|
"@types/w3c-web-usb": "^1.0.10",
|
|
45
45
|
"@types/web-bluetooth": "^0.0.21"
|
|
46
46
|
},
|
|
47
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "76311ccd3753eca8e492b5324f93542bd38525ba"
|
|
48
48
|
}
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
DeviceRebootType,
|
|
4
4
|
PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE,
|
|
5
5
|
PROTOCOL_V2_BLE_FILE_CHUNK_SIZE,
|
|
6
|
+
PROTOCOL_V2_BLE_FIRMWARE_FILE_CHUNK_SIZE,
|
|
6
7
|
PROTOCOL_V2_WEBUSB_FILE_CHUNK_SIZE,
|
|
7
8
|
} from '@onekeyfe/hd-transport';
|
|
8
9
|
import { sha256 } from '@noble/hashes/sha256';
|
|
@@ -97,22 +98,20 @@ export function assertProtocolV2FirmwareTargetsSupported(
|
|
|
97
98
|
deviceType: EDeviceType | string | undefined,
|
|
98
99
|
params: FirmwareUpdateV4Params
|
|
99
100
|
) {
|
|
100
|
-
if (deviceType !== EDeviceType.Neo) return;
|
|
101
|
-
|
|
102
101
|
const unsupportedTargets = new Set(
|
|
103
102
|
(params.targetsToUpdate ?? []).filter(target => PROTOCOL_V2_NEO_UNSUPPORTED_TARGETS.has(target))
|
|
104
103
|
);
|
|
105
104
|
if (params.se03Binary) unsupportedTargets.add('se03');
|
|
106
105
|
if (params.se04Binary) unsupportedTargets.add('se04');
|
|
107
106
|
|
|
108
|
-
if (unsupportedTargets.size)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
107
|
+
if (!unsupportedTargets.size || deviceType === EDeviceType.Pro2) return;
|
|
108
|
+
|
|
109
|
+
const targetList = Array.from(unsupportedTargets).join(', ');
|
|
110
|
+
const message =
|
|
111
|
+
deviceType === EDeviceType.Neo
|
|
112
|
+
? `Neo only supports SE01 and SE02; unsupported firmware targets: ${targetList}`
|
|
113
|
+
: `Cannot safely update ${targetList} without a confirmed Pro2 device type`;
|
|
114
|
+
throw ERRORS.TypedError(HardwareErrorCode.DeviceNotSupportMethod, message);
|
|
116
115
|
}
|
|
117
116
|
|
|
118
117
|
const getProtocolV2DeviceTransferProgress = (
|
|
@@ -243,6 +242,12 @@ const PROTOCOL_V2_REMOTE_COMPONENT_TARGETS: Readonly<
|
|
|
243
242
|
},
|
|
244
243
|
};
|
|
245
244
|
|
|
245
|
+
const PROTOCOL_V2_FIRMWARE_STAGING_PATHS = new Set(
|
|
246
|
+
Object.values(PROTOCOL_V2_REMOTE_COMPONENT_TARGETS).map(
|
|
247
|
+
target => `${PROTOCOL_V2_FIRMWARE_STAGING_VOLUME}${target.fileName}`
|
|
248
|
+
)
|
|
249
|
+
);
|
|
250
|
+
|
|
246
251
|
const PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID = new Map<number, FirmwareUpdateV4Target>([
|
|
247
252
|
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER, 'boot'],
|
|
248
253
|
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1, 'app_v1'],
|
|
@@ -607,16 +612,20 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
607
612
|
};
|
|
608
613
|
}
|
|
609
614
|
|
|
610
|
-
private getProtocolV2FirmwareChunkSize(direction: 'read' | 'write' = 'write') {
|
|
615
|
+
private getProtocolV2FirmwareChunkSize(direction: 'read' | 'write' = 'write', filePath?: string) {
|
|
611
616
|
const payloadChunkSize = Number(this.params?.chunkSize);
|
|
612
617
|
const env = DataManager.getSettings('env');
|
|
613
618
|
const isBle = this.params?.platform === 'native' || (env && DataManager.isBleConnect(env));
|
|
614
619
|
let maxChunkSize = PROTOCOL_V2_WEBUSB_FILE_CHUNK_SIZE;
|
|
615
620
|
if (isBle) {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
621
|
+
if (direction === 'read') {
|
|
622
|
+
maxChunkSize = PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE;
|
|
623
|
+
} else {
|
|
624
|
+
// Firmware staging writes tolerate a larger BLE chunk than generic filesystem writes.
|
|
625
|
+
maxChunkSize = PROTOCOL_V2_FIRMWARE_STAGING_PATHS.has(filePath ?? '')
|
|
626
|
+
? PROTOCOL_V2_BLE_FIRMWARE_FILE_CHUNK_SIZE
|
|
619
627
|
: PROTOCOL_V2_BLE_FILE_CHUNK_SIZE;
|
|
628
|
+
}
|
|
620
629
|
}
|
|
621
630
|
if (!Number.isFinite(payloadChunkSize) || payloadChunkSize <= 0) {
|
|
622
631
|
return maxChunkSize;
|
|
@@ -651,11 +660,12 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
651
660
|
) {
|
|
652
661
|
return this.runProtocolV2PreparedArtifacts(deviceFeatures, firmwareType);
|
|
653
662
|
}
|
|
654
|
-
const
|
|
655
|
-
|
|
663
|
+
const hasExplicitResourceFiles = !!this.params.resourceFiles?.length;
|
|
664
|
+
const wantsStableResources = !!this.params.targetsToUpdate?.includes('resource');
|
|
665
|
+
const wantsBootResources = !!this.params.targetsToUpdate?.includes('boot_resources');
|
|
666
|
+
const needsRemoteResources = !hasExplicitResourceFiles && wantsStableResources;
|
|
656
667
|
const needsRemoteBootResources =
|
|
657
|
-
|
|
658
|
-
!!this.params.targetsToUpdate?.includes('boot_resources');
|
|
668
|
+
!hasExplicitResourceFiles && (wantsBootResources || wantsStableResources);
|
|
659
669
|
|
|
660
670
|
let fwBinaryMap: ProtocolV2TargetBinary[] = [];
|
|
661
671
|
let bootloaderBinary: ArrayBuffer | null = null;
|
|
@@ -697,7 +707,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
697
707
|
}
|
|
698
708
|
const bootResourceFiles = await this.prepareProtocolV2BootResources();
|
|
699
709
|
if (bootResourceFiles?.length) {
|
|
700
|
-
resourceBundles =
|
|
710
|
+
resourceBundles = this.mergeProtocolV2ResourceBundles(resourceBundles, bootResourceFiles);
|
|
701
711
|
}
|
|
702
712
|
if (!needsRemoteResources) {
|
|
703
713
|
this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
@@ -726,7 +736,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
726
736
|
const enteredBootloader = await this.enterProtocolV2BootloaderMode();
|
|
727
737
|
try {
|
|
728
738
|
const stableResources = await this.prepareProtocolV2ResourceBundles();
|
|
729
|
-
resourceBundles =
|
|
739
|
+
resourceBundles = this.mergeProtocolV2ResourceBundles(resourceBundles, stableResources);
|
|
730
740
|
this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
731
741
|
} catch (err) {
|
|
732
742
|
if (enteredBootloader) {
|
|
@@ -1256,31 +1266,108 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1256
1266
|
private async prepareProtocolV2BootResources(): Promise<
|
|
1257
1267
|
ProtocolV2ResourceBundleBinary[] | undefined
|
|
1258
1268
|
> {
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1269
|
+
const wantsStableResources = !!this.params.targetsToUpdate?.includes('resource');
|
|
1270
|
+
const wantsBootResources = !!this.params.targetsToUpdate?.includes('boot_resources');
|
|
1271
|
+
if (!wantsStableResources && !wantsBootResources) {
|
|
1272
|
+
return undefined;
|
|
1273
|
+
}
|
|
1274
|
+
if (this.params.resourceFiles?.length) {
|
|
1263
1275
|
return undefined;
|
|
1264
1276
|
}
|
|
1265
1277
|
const resource = DataManager.getProtocolV2BootResources(this.getProtocolV2DeviceType());
|
|
1266
|
-
if (!resource)
|
|
1278
|
+
if (!resource) {
|
|
1279
|
+
if (wantsBootResources) {
|
|
1280
|
+
throw new Error('Missing Protocol V2 boot resources configuration');
|
|
1281
|
+
}
|
|
1282
|
+
Log.debug('[FirmwareUpdateV4] no boot resources configured; continue with stable resources');
|
|
1283
|
+
return undefined;
|
|
1284
|
+
}
|
|
1267
1285
|
|
|
1268
1286
|
const files: ProtocolV2ResourceBundleBinary[] = [];
|
|
1269
1287
|
for (const file of resource.files) {
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
if (
|
|
1273
|
-
|
|
1288
|
+
const isCurrent =
|
|
1289
|
+
!this.params.forcedUpdateRes && (await this.isProtocolV2BootResourceCurrent(file));
|
|
1290
|
+
if (isCurrent) {
|
|
1291
|
+
Log.log(`[FirmwareUpdateV4] boot resource unchanged, skipping ${file.devicePath}`);
|
|
1292
|
+
} else {
|
|
1293
|
+
Log.log(`[FirmwareUpdateV4] downloading boot resource ${file.devicePath}`);
|
|
1294
|
+
const { binary } = await getSysResourceBinary(file.url);
|
|
1295
|
+
if (!isProtocolV2ResourceFileValid(binary, file)) {
|
|
1296
|
+
throw new Error(`Boot resource file verification failed: ${file.devicePath}`);
|
|
1297
|
+
}
|
|
1298
|
+
files.push({
|
|
1299
|
+
name: file.name ?? file.devicePath.split('/').pop() ?? file.devicePath,
|
|
1300
|
+
binary,
|
|
1301
|
+
devicePath: file.devicePath,
|
|
1302
|
+
});
|
|
1274
1303
|
}
|
|
1275
|
-
files.push({
|
|
1276
|
-
name: file.name ?? file.devicePath.split('/').pop() ?? file.devicePath,
|
|
1277
|
-
binary,
|
|
1278
|
-
devicePath: file.devicePath,
|
|
1279
|
-
});
|
|
1280
1304
|
}
|
|
1281
1305
|
return files;
|
|
1282
1306
|
}
|
|
1283
1307
|
|
|
1308
|
+
private async isProtocolV2BootResourceCurrent(file: IProtocolV2ResourceFile) {
|
|
1309
|
+
try {
|
|
1310
|
+
const commands = this.device.getCommands();
|
|
1311
|
+
const pathInfo = await commands.typedCall(
|
|
1312
|
+
'FilesystemPathInfoQuery',
|
|
1313
|
+
'FilesystemPathInfo',
|
|
1314
|
+
{ path: file.devicePath },
|
|
1315
|
+
{ timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT }
|
|
1316
|
+
);
|
|
1317
|
+
const size = toProtocolV2FiniteNumber(pathInfo.message?.size);
|
|
1318
|
+
if (
|
|
1319
|
+
!pathInfo.message?.exist ||
|
|
1320
|
+
pathInfo.message?.directory ||
|
|
1321
|
+
!Number.isSafeInteger(size) ||
|
|
1322
|
+
size !== file.size
|
|
1323
|
+
) {
|
|
1324
|
+
return false;
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
const digest = sha256.create();
|
|
1328
|
+
const chunkSize = this.getProtocolV2FirmwareChunkSize();
|
|
1329
|
+
let offset = 0;
|
|
1330
|
+
while (offset < file.size) {
|
|
1331
|
+
const response = await commands.typedCall(
|
|
1332
|
+
'FilesystemFileRead',
|
|
1333
|
+
'FilesystemFile',
|
|
1334
|
+
{
|
|
1335
|
+
file: { path: file.devicePath, offset, total_size: 0 },
|
|
1336
|
+
chunk_len: Math.min(chunkSize, file.size - offset),
|
|
1337
|
+
},
|
|
1338
|
+
{ timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT }
|
|
1339
|
+
);
|
|
1340
|
+
const data = toProtocolV2Bytes(response.message?.data);
|
|
1341
|
+
if (data.byteLength === 0) return false;
|
|
1342
|
+
const consumed = data.subarray(0, Math.min(data.byteLength, file.size - offset));
|
|
1343
|
+
digest.update(consumed);
|
|
1344
|
+
offset += consumed.byteLength;
|
|
1345
|
+
}
|
|
1346
|
+
return bytesToHex(digest.digest()) === normalizeProtocolV2Hex(file.fileHash);
|
|
1347
|
+
} catch (error) {
|
|
1348
|
+
Log.debug(
|
|
1349
|
+
`[FirmwareUpdateV4] unable to compare boot resource ${file.devicePath}; scheduling rewrite`,
|
|
1350
|
+
error
|
|
1351
|
+
);
|
|
1352
|
+
return false;
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
private mergeProtocolV2ResourceBundles(
|
|
1357
|
+
...groups: Array<ProtocolV2ResourceBundleBinary[] | undefined>
|
|
1358
|
+
): ProtocolV2ResourceBundleBinary[] | undefined {
|
|
1359
|
+
const merged = groups.flatMap(group => group ?? []);
|
|
1360
|
+
if (!merged.length) return undefined;
|
|
1361
|
+
const seenPaths = new Set<string>();
|
|
1362
|
+
for (const bundle of merged) {
|
|
1363
|
+
if (seenPaths.has(bundle.devicePath)) {
|
|
1364
|
+
throw new Error(`Duplicate Protocol V2 resource devicePath: ${bundle.devicePath}`);
|
|
1365
|
+
}
|
|
1366
|
+
seenPaths.add(bundle.devicePath);
|
|
1367
|
+
}
|
|
1368
|
+
return merged;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1284
1371
|
private prepareExplicitProtocolV2ResourceFiles(): ProtocolV2ResourceBundleBinary[] | undefined {
|
|
1285
1372
|
const files = this.params.resourceFiles ?? [];
|
|
1286
1373
|
if (!files?.length) return undefined;
|
|
@@ -1802,7 +1889,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1802
1889
|
const transferStartedAt = Date.now();
|
|
1803
1890
|
await writeFirmwareByteSource({
|
|
1804
1891
|
source,
|
|
1805
|
-
chunkSize: this.getProtocolV2FirmwareChunkSize(),
|
|
1892
|
+
chunkSize: this.getProtocolV2FirmwareChunkSize('write', filePath),
|
|
1806
1893
|
write: async ({ data, sourceOffset, length, first }) => {
|
|
1807
1894
|
const chunkEnd = sourceOffset + length;
|
|
1808
1895
|
const deviceProgress = getProtocolV2DeviceTransferProgress(
|
|
@@ -35,6 +35,7 @@ export type ProtocolV2FileWriteOptions = {
|
|
|
35
35
|
totalSize?: number;
|
|
36
36
|
chunkSize?: number;
|
|
37
37
|
chunkLen?: number;
|
|
38
|
+
chunkSizeLimit?: number;
|
|
38
39
|
overwrite?: boolean;
|
|
39
40
|
append?: boolean;
|
|
40
41
|
uiPercentage?: number;
|
|
@@ -177,10 +178,13 @@ export async function writeProtocolV2File(options: ProtocolV2FileWriteOptions) {
|
|
|
177
178
|
);
|
|
178
179
|
}
|
|
179
180
|
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
181
|
+
const defaultChunkSizeLimit = getProtocolV2FileChunkLimit();
|
|
182
|
+
const configuredChunkSizeLimit = Number(options.chunkSizeLimit);
|
|
183
|
+
const chunkSizeLimit =
|
|
184
|
+
Number.isFinite(configuredChunkSizeLimit) && configuredChunkSizeLimit > 0
|
|
185
|
+
? Math.floor(configuredChunkSizeLimit)
|
|
186
|
+
: defaultChunkSizeLimit;
|
|
187
|
+
const chunkSize = normalizeChunkSize(options.chunkSize ?? options.chunkLen, chunkSizeLimit);
|
|
184
188
|
let written = 0;
|
|
185
189
|
let chunks = 0;
|
|
186
190
|
let lastMessage: Record<string, unknown> | undefined;
|
|
@@ -157,6 +157,7 @@ export interface FirmwareUpdateV4Params {
|
|
|
157
157
|
/**
|
|
158
158
|
* Arbitrary Protocol V2 resource files written directly with FilesystemFileWrite.
|
|
159
159
|
* Use this for manifest-driven boot resources and other non-RESC files.
|
|
160
|
+
* When provided, these files are authoritative for resource and boot_resources targets.
|
|
160
161
|
*/
|
|
161
162
|
resourceFiles?: Array<{
|
|
162
163
|
binary: ArrayBuffer;
|