@onekeyfe/hd-core 1.2.0-alpha.47 → 1.2.0-alpha.48
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 +107 -12
- package/__tests__/protocol-v2-resources.test.ts +87 -42
- package/__tests__/protocol-v2.test.ts +73 -34
- package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +127 -102
- package/dist/protocols/protocol-v2/resources.d.ts +4 -4
- package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/patch.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/CheckAllFirmwareRelease.ts +9 -10
- package/src/api/FirmwareUpdateV4.ts +5 -3
- package/src/data/messages/messages-protocol-v2.json +0 -43
- package/src/protocols/protocol-v2/resources.ts +150 -56
|
@@ -5,6 +5,7 @@ import CheckAllFirmwareRelease, {
|
|
|
5
5
|
} from '../src/api/CheckAllFirmwareRelease';
|
|
6
6
|
import { DataManager } from '../src/data-manager';
|
|
7
7
|
import { createCoreApi } from '../src/inject';
|
|
8
|
+
import { PROTOCOL_V2_RESOURCE_DEVICE_PATHS } from '../src/protocols/protocol-v2/resources';
|
|
8
9
|
|
|
9
10
|
import type { CoreApi } from '../src/types/api';
|
|
10
11
|
import type { DeviceStateVersions, IFirmwareReleaseInfo } from '../src/types';
|
|
@@ -72,7 +73,74 @@ const release: IFirmwareReleaseInfo = {
|
|
|
72
73
|
],
|
|
73
74
|
};
|
|
74
75
|
|
|
76
|
+
const stableResources = ['images', 'animation', 'wallpaper', 'translations', 'roobert', 'noto'].map(
|
|
77
|
+
(type, index) => ({
|
|
78
|
+
type,
|
|
79
|
+
url: `https://example.com/${type}.okpkg`,
|
|
80
|
+
size: 0x52a0 + index + 1,
|
|
81
|
+
fileHash: 'a'.repeat(64),
|
|
82
|
+
headerHash: index.toString(16).padStart(128, '0'),
|
|
83
|
+
})
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const createFilesystemTypedCall = (missingAll = false) => {
|
|
87
|
+
const resourceByPath = new Map(
|
|
88
|
+
stableResources.map(resource => [
|
|
89
|
+
PROTOCOL_V2_RESOURCE_DEVICE_PATHS[
|
|
90
|
+
resource.type as keyof typeof PROTOCOL_V2_RESOURCE_DEVICE_PATHS
|
|
91
|
+
],
|
|
92
|
+
resource,
|
|
93
|
+
])
|
|
94
|
+
);
|
|
95
|
+
return jest.fn((requestType: string, _responseType: string, payload: Record<string, any>) => {
|
|
96
|
+
if (requestType === 'ResourceInventoryGet') {
|
|
97
|
+
throw new Error('ResourceInventoryGet is unavailable on released firmware');
|
|
98
|
+
}
|
|
99
|
+
if (requestType === 'FilesystemPathInfoQuery') {
|
|
100
|
+
const resource = resourceByPath.get(payload.path);
|
|
101
|
+
return {
|
|
102
|
+
message: {
|
|
103
|
+
exist: Boolean(resource) && !missingAll,
|
|
104
|
+
directory: false,
|
|
105
|
+
size: missingAll ? 0 : resource?.size,
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
if (requestType === 'FilesystemFileRead') {
|
|
110
|
+
const resource = resourceByPath.get(payload.file.path);
|
|
111
|
+
if (!resource || missingAll) throw new Error('missing resource');
|
|
112
|
+
const header = new Uint8Array(0x52a0);
|
|
113
|
+
const view = new DataView(header.buffer);
|
|
114
|
+
'OKPP'.split('').forEach((char, index) => {
|
|
115
|
+
header[index] = char.charCodeAt(0);
|
|
116
|
+
});
|
|
117
|
+
'RESC'.split('').forEach((char, index) => {
|
|
118
|
+
header[0x08 + index] = char.charCodeAt(0);
|
|
119
|
+
});
|
|
120
|
+
view.setUint32(0x0c, header.byteLength, true);
|
|
121
|
+
for (let index = 0; index < resource.headerHash.length / 2; index++) {
|
|
122
|
+
header[0x240 + index] = Number.parseInt(
|
|
123
|
+
resource.headerHash.slice(index * 2, index * 2 + 2),
|
|
124
|
+
16
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
const offset = Number(payload.file.offset);
|
|
128
|
+
const chunkLength = Number(payload.chunk_len);
|
|
129
|
+
return {
|
|
130
|
+
message: {
|
|
131
|
+
data: header.slice(offset, offset + chunkLength),
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
throw new Error(`Unexpected request: ${requestType}`);
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
|
|
75
139
|
describe('checkAllFirmwareRelease Protocol V2 support', () => {
|
|
140
|
+
afterEach(() => {
|
|
141
|
+
jest.restoreAllMocks();
|
|
142
|
+
});
|
|
143
|
+
|
|
76
144
|
test('builds ordered firmwareUpdateV4 targets from recommended component versions', () => {
|
|
77
145
|
const result = buildProtocolV2FirmwareRelease({
|
|
78
146
|
currentVersions,
|
|
@@ -240,6 +308,7 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
|
|
|
240
308
|
status: { mode: 'normal' },
|
|
241
309
|
versions: currentVersions,
|
|
242
310
|
});
|
|
311
|
+
const typedCall = createFilesystemTypedCall(true);
|
|
243
312
|
method.device = {
|
|
244
313
|
isProtocolV2: () => true,
|
|
245
314
|
features: {
|
|
@@ -247,20 +316,10 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
|
|
|
247
316
|
firmwareVersion: '1.0.0',
|
|
248
317
|
},
|
|
249
318
|
getDeviceState,
|
|
250
|
-
getCommands: () => ({ typedCall
|
|
319
|
+
getCommands: () => ({ typedCall }),
|
|
251
320
|
} as unknown as CheckAllFirmwareRelease['device'];
|
|
252
321
|
jest.spyOn(DataManager, 'getFirmwareLatestRelease').mockReturnValue(release);
|
|
253
|
-
jest.spyOn(DataManager, 'getProtocolV2Resources').mockReturnValue(
|
|
254
|
-
['images', 'animation', 'wallpaper', 'translations', 'roobert', 'noto'].map(
|
|
255
|
-
(type, index) => ({
|
|
256
|
-
type,
|
|
257
|
-
url: `https://example.com/${type}.okpkg`,
|
|
258
|
-
size: index + 1,
|
|
259
|
-
fileHash: 'a'.repeat(64),
|
|
260
|
-
headerHash: index.toString(16).padStart(128, '0'),
|
|
261
|
-
})
|
|
262
|
-
) as any
|
|
263
|
-
);
|
|
322
|
+
jest.spyOn(DataManager, 'getProtocolV2Resources').mockReturnValue(stableResources as any);
|
|
264
323
|
|
|
265
324
|
await expect(method.run()).resolves.toMatchObject({
|
|
266
325
|
protocol: 'V2',
|
|
@@ -272,9 +331,45 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
|
|
|
272
331
|
status: 'required',
|
|
273
332
|
},
|
|
274
333
|
});
|
|
334
|
+
expect(typedCall.mock.calls.some(call => call[0] === 'ResourceInventoryGet')).toBe(false);
|
|
275
335
|
expect(method.getSupportedProtocols()).toEqual(['V1', 'V2']);
|
|
276
336
|
});
|
|
277
337
|
|
|
338
|
+
test.each(['bootloader', 'romloader'] as const)(
|
|
339
|
+
'uses filesystem inventory in %s mode instead of forcing all resources',
|
|
340
|
+
async mode => {
|
|
341
|
+
const method = new CheckAllFirmwareRelease({
|
|
342
|
+
id: 1,
|
|
343
|
+
payload: {
|
|
344
|
+
method: 'checkAllFirmwareRelease',
|
|
345
|
+
firmwareType: EFirmwareType.Universal,
|
|
346
|
+
},
|
|
347
|
+
});
|
|
348
|
+
method.init();
|
|
349
|
+
const typedCall = createFilesystemTypedCall();
|
|
350
|
+
method.device = {
|
|
351
|
+
isProtocolV2: () => true,
|
|
352
|
+
features: { deviceType: 'pro2', firmwareVersion: '1.0.0' },
|
|
353
|
+
getDeviceState: jest.fn().mockResolvedValue({
|
|
354
|
+
identity: { deviceType: 'pro2', firmwareType: EFirmwareType.Universal },
|
|
355
|
+
status: { mode },
|
|
356
|
+
versions: currentVersions,
|
|
357
|
+
}),
|
|
358
|
+
getCommands: () => ({ typedCall }),
|
|
359
|
+
} as unknown as CheckAllFirmwareRelease['device'];
|
|
360
|
+
jest.spyOn(DataManager, 'getFirmwareLatestRelease').mockReturnValue(release);
|
|
361
|
+
jest.spyOn(DataManager, 'getProtocolV2Resources').mockReturnValue(stableResources as any);
|
|
362
|
+
|
|
363
|
+
await expect(method.run()).resolves.toMatchObject({
|
|
364
|
+
protocol: 'V2',
|
|
365
|
+
resourceStatus: 'valid',
|
|
366
|
+
targetsToUpdate: ['boot', 'app_v1'],
|
|
367
|
+
});
|
|
368
|
+
expect(typedCall.mock.calls.some(call => call[0] === 'ResourceInventoryGet')).toBe(false);
|
|
369
|
+
expect(typedCall.mock.calls.some(call => call[0] === 'FilesystemFileRead')).toBe(true);
|
|
370
|
+
}
|
|
371
|
+
);
|
|
372
|
+
|
|
278
373
|
test('continues forwarding the existing public method', async () => {
|
|
279
374
|
const call = jest.fn().mockResolvedValue({ success: true, payload: {} });
|
|
280
375
|
const api = createCoreApi(call as CoreApi['call']) as CoreApi;
|
|
@@ -7,9 +7,8 @@ import {
|
|
|
7
7
|
PROTOCOL_V2_RESOURCE_TYPES,
|
|
8
8
|
buildProtocolV2ResourceUpdatePlan,
|
|
9
9
|
isProtocolV2ResourceFileValid,
|
|
10
|
-
parseProtocolV2ResourceInventory,
|
|
11
10
|
parseProtocolV2Resources,
|
|
12
|
-
|
|
11
|
+
readProtocolV2ResourceInventory,
|
|
13
12
|
} from '../src/protocols/protocol-v2/resources';
|
|
14
13
|
|
|
15
14
|
import type {
|
|
@@ -28,6 +27,24 @@ jest.mock('../src/data/config', () => ({
|
|
|
28
27
|
const bytesToHex = (bytes: Uint8Array) =>
|
|
29
28
|
Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');
|
|
30
29
|
|
|
30
|
+
const PROTOCOL_V2_OKPP_HEADER_SIZE = 0x52a0;
|
|
31
|
+
|
|
32
|
+
const createResourceHeader = (headerHash: string) => {
|
|
33
|
+
const header = new Uint8Array(PROTOCOL_V2_OKPP_HEADER_SIZE);
|
|
34
|
+
const view = new DataView(header.buffer);
|
|
35
|
+
'OKPP'.split('').forEach((char, index) => {
|
|
36
|
+
header[index] = char.charCodeAt(0);
|
|
37
|
+
});
|
|
38
|
+
'RESC'.split('').forEach((char, index) => {
|
|
39
|
+
header[0x08 + index] = char.charCodeAt(0);
|
|
40
|
+
});
|
|
41
|
+
view.setUint32(0x0c, header.byteLength, true);
|
|
42
|
+
for (let index = 0; index < headerHash.length / 2; index++) {
|
|
43
|
+
header[0x240 + index] = Number.parseInt(headerHash.slice(index * 2, index * 2 + 2), 16);
|
|
44
|
+
}
|
|
45
|
+
return header;
|
|
46
|
+
};
|
|
47
|
+
|
|
31
48
|
const resources: IProtocolV2Resource[] = PROTOCOL_V2_RESOURCE_TYPES.map((type, index) => ({
|
|
32
49
|
type,
|
|
33
50
|
url: `https://example.com/${type}.okpkg`,
|
|
@@ -124,50 +141,62 @@ describe('Pro2 resource configuration', () => {
|
|
|
124
141
|
).toEqual({ status: 'valid', resources: [] });
|
|
125
142
|
});
|
|
126
143
|
|
|
127
|
-
test('
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
size:
|
|
131
|
-
header_hash: resource.headerHash,
|
|
144
|
+
test('builds inventory from existing filesystem calls without ResourceInventoryGet', async () => {
|
|
145
|
+
const installedResources = resources.map((resource, index) => ({
|
|
146
|
+
...resource,
|
|
147
|
+
size: PROTOCOL_V2_OKPP_HEADER_SIZE + index + 1,
|
|
132
148
|
}));
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
149
|
+
const resourceByPath = new Map(
|
|
150
|
+
installedResources.map(resource => [
|
|
151
|
+
PROTOCOL_V2_RESOURCE_DEVICE_PATHS[resource.type],
|
|
152
|
+
resource,
|
|
153
|
+
])
|
|
154
|
+
);
|
|
155
|
+
const typedCall = jest.fn(
|
|
156
|
+
(requestType: string, _responseType: string, payload: Record<string, any>) => {
|
|
157
|
+
if (requestType === 'ResourceInventoryGet') {
|
|
158
|
+
throw new Error('ResourceInventoryGet is unavailable on released firmware');
|
|
159
|
+
}
|
|
160
|
+
if (requestType === 'FilesystemPathInfoQuery') {
|
|
161
|
+
const resource = resourceByPath.get(payload.path);
|
|
162
|
+
return {
|
|
163
|
+
message: {
|
|
164
|
+
exist: Boolean(resource),
|
|
165
|
+
directory: false,
|
|
166
|
+
size: resource?.size ?? 0,
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
if (requestType === 'FilesystemFileRead') {
|
|
171
|
+
const resource = resourceByPath.get(payload.file.path);
|
|
172
|
+
if (!resource) throw new Error('missing resource');
|
|
173
|
+
const header = createResourceHeader(resource.headerHash);
|
|
174
|
+
const offset = Number(payload.file.offset);
|
|
175
|
+
const chunkLength = Number(payload.chunk_len);
|
|
176
|
+
return {
|
|
177
|
+
message: {
|
|
178
|
+
data: header.slice(offset, offset + chunkLength),
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
throw new Error(`Unexpected request: ${requestType}`);
|
|
183
|
+
}
|
|
154
184
|
);
|
|
155
|
-
});
|
|
156
185
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
{ type: 'IMAGES', size: 1, header_hash: 'b'.repeat(128) },
|
|
163
|
-
],
|
|
164
|
-
})
|
|
165
|
-
).toThrow('duplicate resource type');
|
|
166
|
-
expect(() =>
|
|
167
|
-
parseProtocolV2ResourceInventory({
|
|
168
|
-
items: [{ type: 'IMAGES', size: 1, header_hash: 'bad' }],
|
|
186
|
+
await expect(
|
|
187
|
+
readProtocolV2ResourceInventory({
|
|
188
|
+
commands: { typedCall },
|
|
189
|
+
resources: installedResources,
|
|
190
|
+
chunkSize: 4000,
|
|
169
191
|
})
|
|
170
|
-
).
|
|
192
|
+
).resolves.toEqual(
|
|
193
|
+
installedResources.map(({ type, size, headerHash }) => ({ type, size, headerHash }))
|
|
194
|
+
);
|
|
195
|
+
expect(typedCall.mock.calls.some(call => call[0] === 'ResourceInventoryGet')).toBe(false);
|
|
196
|
+
expect(typedCall.mock.calls.filter(call => call[0] === 'FilesystemPathInfoQuery')).toHaveLength(
|
|
197
|
+
6
|
|
198
|
+
);
|
|
199
|
+
expect(typedCall.mock.calls.some(call => call[0] === 'FilesystemFileRead')).toBe(true);
|
|
171
200
|
});
|
|
172
201
|
|
|
173
202
|
test('selects only the changed or missing resource in application mode', () => {
|
|
@@ -199,6 +228,22 @@ describe('Pro2 resource configuration', () => {
|
|
|
199
228
|
).toHaveLength(6);
|
|
200
229
|
});
|
|
201
230
|
|
|
231
|
+
test('uses a filesystem inventory for incremental recovery mode updates', () => {
|
|
232
|
+
const inventory = resources.slice(1).map(({ type, size, headerHash }) => ({
|
|
233
|
+
type,
|
|
234
|
+
size,
|
|
235
|
+
headerHash,
|
|
236
|
+
}));
|
|
237
|
+
|
|
238
|
+
expect(
|
|
239
|
+
buildProtocolV2ResourceUpdatePlan({
|
|
240
|
+
resources,
|
|
241
|
+
inventory,
|
|
242
|
+
mode: 'bootloader-recovery',
|
|
243
|
+
}).resources.map(resource => resource.type)
|
|
244
|
+
).toEqual(['images']);
|
|
245
|
+
});
|
|
246
|
+
|
|
202
247
|
test('verifies both full file size and SHA-256 before transfer', () => {
|
|
203
248
|
const bytes = new Uint8Array([1, 2, 3]);
|
|
204
249
|
const binary = bytes.buffer;
|
|
@@ -76,6 +76,7 @@ import {
|
|
|
76
76
|
requestProtocolV2ProtocolInfo,
|
|
77
77
|
supportsProtocolV2Message,
|
|
78
78
|
} from '../src/protocols/protocol-v2/features';
|
|
79
|
+
import { PROTOCOL_V2_RESOURCE_DEVICE_PATHS } from '../src/protocols/protocol-v2/resources';
|
|
79
80
|
import {
|
|
80
81
|
getProtocolV2WalletSession,
|
|
81
82
|
refreshProtocolV2DeviceStatus,
|
|
@@ -5789,6 +5790,64 @@ describe('Protocol V2 firmware update method', () => {
|
|
|
5789
5790
|
});
|
|
5790
5791
|
|
|
5791
5792
|
describe('Protocol V2 firmware reconnect identity', () => {
|
|
5793
|
+
const createResourceFilesystemTypedCall = (
|
|
5794
|
+
stable: Array<{ type: string; size: number; headerHash: string }>,
|
|
5795
|
+
missingTypes: string[] = []
|
|
5796
|
+
) => {
|
|
5797
|
+
const missing = new Set(missingTypes);
|
|
5798
|
+
const resourceByPath = new Map(
|
|
5799
|
+
stable.map(resource => [
|
|
5800
|
+
PROTOCOL_V2_RESOURCE_DEVICE_PATHS[
|
|
5801
|
+
resource.type as keyof typeof PROTOCOL_V2_RESOURCE_DEVICE_PATHS
|
|
5802
|
+
],
|
|
5803
|
+
resource,
|
|
5804
|
+
])
|
|
5805
|
+
);
|
|
5806
|
+
return jest.fn((requestType: string, _responseType: string, payload: Record<string, any>) => {
|
|
5807
|
+
if (requestType === 'ResourceInventoryGet') {
|
|
5808
|
+
throw new Error('ResourceInventoryGet is unavailable on released firmware');
|
|
5809
|
+
}
|
|
5810
|
+
if (requestType === 'FilesystemPathInfoQuery') {
|
|
5811
|
+
const resource = resourceByPath.get(payload.path);
|
|
5812
|
+
const exists = Boolean(resource && !missing.has(resource.type));
|
|
5813
|
+
return {
|
|
5814
|
+
message: {
|
|
5815
|
+
exist: exists,
|
|
5816
|
+
directory: false,
|
|
5817
|
+
size: exists ? resource?.size : 0,
|
|
5818
|
+
},
|
|
5819
|
+
};
|
|
5820
|
+
}
|
|
5821
|
+
if (requestType === 'FilesystemFileRead') {
|
|
5822
|
+
const resource = resourceByPath.get(payload.file.path);
|
|
5823
|
+
if (!resource || missing.has(resource.type)) throw new Error('missing resource');
|
|
5824
|
+
const header = new Uint8Array(0x52a0);
|
|
5825
|
+
const view = new DataView(header.buffer);
|
|
5826
|
+
'OKPP'.split('').forEach((char, index) => {
|
|
5827
|
+
header[index] = char.charCodeAt(0);
|
|
5828
|
+
});
|
|
5829
|
+
'RESC'.split('').forEach((char, index) => {
|
|
5830
|
+
header[0x08 + index] = char.charCodeAt(0);
|
|
5831
|
+
});
|
|
5832
|
+
view.setUint32(0x0c, header.byteLength, true);
|
|
5833
|
+
for (let index = 0; index < resource.headerHash.length / 2; index++) {
|
|
5834
|
+
header[0x240 + index] = Number.parseInt(
|
|
5835
|
+
resource.headerHash.slice(index * 2, index * 2 + 2),
|
|
5836
|
+
16
|
|
5837
|
+
);
|
|
5838
|
+
}
|
|
5839
|
+
const offset = Number(payload.file.offset);
|
|
5840
|
+
const chunkLength = Number(payload.chunk_len);
|
|
5841
|
+
return {
|
|
5842
|
+
message: {
|
|
5843
|
+
data: header.slice(offset, offset + chunkLength),
|
|
5844
|
+
},
|
|
5845
|
+
};
|
|
5846
|
+
}
|
|
5847
|
+
throw new Error(`Unexpected request: ${requestType}`);
|
|
5848
|
+
});
|
|
5849
|
+
};
|
|
5850
|
+
|
|
5792
5851
|
test('rejects a different physical serial before firmware transfer resumes', () => {
|
|
5793
5852
|
expect(() => assertProtocolV2ReconnectIdentity('expected-serial', 'other-serial')).toThrow(
|
|
5794
5853
|
'identity mismatch'
|
|
@@ -5851,7 +5910,7 @@ describe('Protocol V2 firmware reconnect identity', () => {
|
|
|
5851
5910
|
expect((method as any).protocolV2ExpectedSerialNumber).toBeUndefined();
|
|
5852
5911
|
});
|
|
5853
5912
|
|
|
5854
|
-
test('uses
|
|
5913
|
+
test('uses filesystem reads instead of ResourceInventoryGet for resource comparison', async () => {
|
|
5855
5914
|
const method = new FirmwareUpdateV4({
|
|
5856
5915
|
id: 1,
|
|
5857
5916
|
payload: {
|
|
@@ -5865,20 +5924,12 @@ describe('Protocol V2 firmware reconnect identity', () => {
|
|
|
5865
5924
|
(type, index) => ({
|
|
5866
5925
|
type,
|
|
5867
5926
|
url: `https://example.com/${type}.okpkg`,
|
|
5868
|
-
size: index + 1,
|
|
5927
|
+
size: 0x52a0 + index + 1,
|
|
5869
5928
|
fileHash: 'a'.repeat(64),
|
|
5870
5929
|
headerHash: index.toString(16).padStart(128, '0'),
|
|
5871
5930
|
})
|
|
5872
5931
|
);
|
|
5873
|
-
const typedCall =
|
|
5874
|
-
message: {
|
|
5875
|
-
items: stable.map(item => ({
|
|
5876
|
-
type: item.type.toUpperCase(),
|
|
5877
|
-
size: item.size,
|
|
5878
|
-
header_hash: item.headerHash,
|
|
5879
|
-
})),
|
|
5880
|
-
},
|
|
5881
|
-
});
|
|
5932
|
+
const typedCall = createResourceFilesystemTypedCall(stable);
|
|
5882
5933
|
(method as any).device = stubDevice({
|
|
5883
5934
|
getCommands: () => ({ typedCall }),
|
|
5884
5935
|
});
|
|
@@ -5888,13 +5939,8 @@ describe('Protocol V2 firmware reconnect identity', () => {
|
|
|
5888
5939
|
(method as any).downloadProtocolV2Resource = jest.fn();
|
|
5889
5940
|
|
|
5890
5941
|
await expect((method as any).prepareProtocolV2ResourceBundles(false)).resolves.toEqual([]);
|
|
5891
|
-
expect(typedCall).
|
|
5892
|
-
|
|
5893
|
-
'ResourceInventory',
|
|
5894
|
-
{},
|
|
5895
|
-
{ timeoutMs: 5000 }
|
|
5896
|
-
);
|
|
5897
|
-
expect(typedCall.mock.calls.some(call => call[0] === 'FilesystemFileRead')).toBe(false);
|
|
5942
|
+
expect(typedCall.mock.calls.some(call => call[0] === 'ResourceInventoryGet')).toBe(false);
|
|
5943
|
+
expect(typedCall.mock.calls.some(call => call[0] === 'FilesystemFileRead')).toBe(true);
|
|
5898
5944
|
expect((method as any).downloadProtocolV2Resource).not.toHaveBeenCalled();
|
|
5899
5945
|
resourcesSpy.mockRestore();
|
|
5900
5946
|
});
|
|
@@ -5909,20 +5955,12 @@ describe('Protocol V2 firmware reconnect identity', () => {
|
|
|
5909
5955
|
(type, index) => ({
|
|
5910
5956
|
type,
|
|
5911
5957
|
url: `https://example.com/${type}.okpkg`,
|
|
5912
|
-
size: index + 1,
|
|
5958
|
+
size: 0x52a0 + index + 1,
|
|
5913
5959
|
fileHash: 'a'.repeat(64),
|
|
5914
5960
|
headerHash: index.toString(16).padStart(128, '0'),
|
|
5915
5961
|
})
|
|
5916
5962
|
);
|
|
5917
|
-
const typedCall =
|
|
5918
|
-
message: {
|
|
5919
|
-
items: stable.slice(1).map(item => ({
|
|
5920
|
-
type: item.type.toUpperCase(),
|
|
5921
|
-
size: item.size,
|
|
5922
|
-
header_hash: item.headerHash,
|
|
5923
|
-
})),
|
|
5924
|
-
},
|
|
5925
|
-
});
|
|
5963
|
+
const typedCall = createResourceFilesystemTypedCall(stable, ['images']);
|
|
5926
5964
|
(method as any).device = stubDevice({ getCommands: () => ({ typedCall }) });
|
|
5927
5965
|
const resourcesSpy = jest
|
|
5928
5966
|
.spyOn(DataManager, 'getProtocolV2Resources')
|
|
@@ -5942,7 +5980,7 @@ describe('Protocol V2 firmware reconnect identity', () => {
|
|
|
5942
5980
|
resourcesSpy.mockRestore();
|
|
5943
5981
|
});
|
|
5944
5982
|
|
|
5945
|
-
test('
|
|
5983
|
+
test('uses filesystem inventory for incremental Bootloader recovery', async () => {
|
|
5946
5984
|
const method = new FirmwareUpdateV4({
|
|
5947
5985
|
id: 1,
|
|
5948
5986
|
payload: { method: 'firmwareUpdateV4', platform: 'web', targetsToUpdate: ['resource'] },
|
|
@@ -5952,12 +5990,12 @@ describe('Protocol V2 firmware reconnect identity', () => {
|
|
|
5952
5990
|
(type, index) => ({
|
|
5953
5991
|
type,
|
|
5954
5992
|
url: `https://example.com/${type}.okpkg`,
|
|
5955
|
-
size: index + 1,
|
|
5993
|
+
size: 0x52a0 + index + 1,
|
|
5956
5994
|
fileHash: 'a'.repeat(64),
|
|
5957
5995
|
headerHash: index.toString(16).padStart(128, '0'),
|
|
5958
5996
|
})
|
|
5959
5997
|
);
|
|
5960
|
-
const typedCall =
|
|
5998
|
+
const typedCall = createResourceFilesystemTypedCall(stable, ['images']);
|
|
5961
5999
|
(method as any).device = stubDevice({ getCommands: () => ({ typedCall }) });
|
|
5962
6000
|
const resourcesSpy = jest
|
|
5963
6001
|
.spyOn(DataManager, 'getProtocolV2Resources')
|
|
@@ -5972,9 +6010,10 @@ describe('Protocol V2 firmware reconnect identity', () => {
|
|
|
5972
6010
|
|
|
5973
6011
|
const bundles = await (method as any).prepareProtocolV2ResourceBundles(true);
|
|
5974
6012
|
|
|
5975
|
-
expect(bundles).toHaveLength(
|
|
5976
|
-
expect(typedCall
|
|
5977
|
-
expect((
|
|
6013
|
+
expect(bundles).toHaveLength(1);
|
|
6014
|
+
expect(typedCall.mock.calls.some(call => call[0] === 'ResourceInventoryGet')).toBe(false);
|
|
6015
|
+
expect(typedCall.mock.calls.some(call => call[0] === 'FilesystemPathInfoQuery')).toBe(true);
|
|
6016
|
+
expect((method as any).downloadProtocolV2Resource).toHaveBeenCalledWith(stable[0]);
|
|
5978
6017
|
resourcesSpy.mockRestore();
|
|
5979
6018
|
});
|
|
5980
6019
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CheckAllFirmwareRelease.d.ts","sourceRoot":"","sources":["../../src/api/CheckAllFirmwareRelease.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAA6B,MAAM,qBAAqB,CAAC;AAY/E,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAM1C,OAAO,KAAK,EACV,kBAAkB,EAElB,kCAAkC,EAClC,+BAA+B,EAChC,MAAM,sCAAsC,CAAC;AAC9C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,KAAK,EACV,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EAGrB,MAAM,UAAU,CAAC;AA6HlB,KAAK,6BAA6B,GAAG;IACnC,YAAY,EAAE,aAAa,CAAC;IAC5B,MAAM,EAAE,+BAA+B,CAAC;IACxC,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,mBAAmB,CAAC;IACrC,UAAU,EAAE,kCAAkC,EAAE,CAAC;IACjD,eAAe,EAAE,sBAAsB,EAAE,CAAC;IAC1C,OAAO,EAAE,oBAAoB,GAAG,SAAS,CAAC;CAC3C,CAAC;AAEF,wBAAgB,8BAA8B,CAAC,EAC7C,eAAe,EACf,mBAAwB,EACxB,mBAAwB,EACxB,YAAY,EACZ,OAAO,GACR,EAAE;IACD,eAAe,EAAE,mBAAmB,CAAC;IACrC,mBAAmB,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACpD,mBAAmB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;IAC9D,YAAY,EAAE,aAAa,CAAC;IAC5B,OAAO,EAAE,oBAAoB,GAAG,SAAS,CAAC;CAC3C,GAAG,6BAA6B,CAoEhC;AAED,MAAM,CAAC,OAAO,OAAO,uBAAwB,SAAQ,UAAU;IAC7D,qBAAqB;IAIrB,IAAI;IAME,GAAG;YAuDK,aAAa;
|
|
1
|
+
{"version":3,"file":"CheckAllFirmwareRelease.d.ts","sourceRoot":"","sources":["../../src/api/CheckAllFirmwareRelease.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAA6B,MAAM,qBAAqB,CAAC;AAY/E,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAM1C,OAAO,KAAK,EACV,kBAAkB,EAElB,kCAAkC,EAClC,+BAA+B,EAChC,MAAM,sCAAsC,CAAC;AAC9C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,KAAK,EACV,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EAGrB,MAAM,UAAU,CAAC;AA6HlB,KAAK,6BAA6B,GAAG;IACnC,YAAY,EAAE,aAAa,CAAC;IAC5B,MAAM,EAAE,+BAA+B,CAAC;IACxC,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,mBAAmB,CAAC;IACrC,UAAU,EAAE,kCAAkC,EAAE,CAAC;IACjD,eAAe,EAAE,sBAAsB,EAAE,CAAC;IAC1C,OAAO,EAAE,oBAAoB,GAAG,SAAS,CAAC;CAC3C,CAAC;AAEF,wBAAgB,8BAA8B,CAAC,EAC7C,eAAe,EACf,mBAAwB,EACxB,mBAAwB,EACxB,YAAY,EACZ,OAAO,GACR,EAAE;IACD,eAAe,EAAE,mBAAmB,CAAC;IACrC,mBAAmB,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACpD,mBAAmB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;IAC9D,YAAY,EAAE,aAAa,CAAC;IAC5B,OAAO,EAAE,oBAAoB,GAAG,SAAS,CAAC;CAC3C,GAAG,6BAA6B,CAoEhC;AAED,MAAM,CAAC,OAAO,OAAO,uBAAwB,SAAQ,UAAU;IAC7D,qBAAqB;IAIrB,IAAI;IAME,GAAG;YAuDK,aAAa;CAmF5B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FirmwareUpdateV4.d.ts","sourceRoot":"","sources":["../../src/api/FirmwareUpdateV4.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAkB/E,OAAO,KAAK,EAAE,sBAAsB,EAA0B,MAAM,6BAA6B,CAAC;AAiUlG,eAAO,MAAM,oCAAoC,WACvC,WAAW,GAAG,UAAU,eACnB,MAAM,GAAG,SAAS,YAKhC,CAAC;AAEF,eAAO,MAAM,iCAAiC,0BACrB,MAAM,uBACR,MAAM,SAa5B,CAAC;AAUF,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,wBAAwB,CAAC,sBAAsB,CAAC;IAC5F,OAAO,CAAC,8BAA8B,CAAC,CAAS;IAEhD,qBAAqB;IAIrB,IAAI;IA6DJ,OAAO,CAAC,8BAA8B;IAiBhC,GAAG;;;;;YAKK,aAAa;YAqFb,2BAA2B;IAWzC,OAAO,CAAC,yBAAyB;IAKjC,OAAO,CAAC,kCAAkC;YAO5B,iCAAiC;YAOjC,iCAAiC;YAOjC,iCAAiC;IAM/C,OAAO,CAAC,uBAAuB;IAI/B,OAAO,CAAC,4BAA4B;IASpC,OAAO,CAAC,2BAA2B;IAsBnC,OAAO,CAAC,yBAAyB;IAiBjC,OAAO,CAAC,wBAAwB;YAkBlB,iCAAiC;YAmCjC,+BAA+B;IAqD7C,OAAO,CAAC,qCAAqC;YAuB/B,8BAA8B;YAyB9B,gCAAgC;
|
|
1
|
+
{"version":3,"file":"FirmwareUpdateV4.d.ts","sourceRoot":"","sources":["../../src/api/FirmwareUpdateV4.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAkB/E,OAAO,KAAK,EAAE,sBAAsB,EAA0B,MAAM,6BAA6B,CAAC;AAiUlG,eAAO,MAAM,oCAAoC,WACvC,WAAW,GAAG,UAAU,eACnB,MAAM,GAAG,SAAS,YAKhC,CAAC;AAEF,eAAO,MAAM,iCAAiC,0BACrB,MAAM,uBACR,MAAM,SAa5B,CAAC;AAUF,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,wBAAwB,CAAC,sBAAsB,CAAC;IAC5F,OAAO,CAAC,8BAA8B,CAAC,CAAS;IAEhD,qBAAqB;IAIrB,IAAI;IA6DJ,OAAO,CAAC,8BAA8B;IAiBhC,GAAG;;;;;YAKK,aAAa;YAqFb,2BAA2B;IAWzC,OAAO,CAAC,yBAAyB;IAKjC,OAAO,CAAC,kCAAkC;YAO5B,iCAAiC;YAOjC,iCAAiC;YAOjC,iCAAiC;IAM/C,OAAO,CAAC,uBAAuB;IAI/B,OAAO,CAAC,4BAA4B;IASpC,OAAO,CAAC,2BAA2B;IAsBnC,OAAO,CAAC,yBAAyB;IAiBjC,OAAO,CAAC,wBAAwB;YAkBlB,iCAAiC;YAmCjC,+BAA+B;IAqD7C,OAAO,CAAC,qCAAqC;YAuB/B,8BAA8B;YAyB9B,gCAAgC;YAqDhC,0BAA0B;YAe1B,6BAA6B;IAkC3C,OAAO,CAAC,0BAA0B;IAUlC,OAAO,CAAC,yBAAyB;IAU3B,6BAA6B;YA4BrB,+BAA+B;IA6C7C,OAAO,CAAC,6BAA6B;YAkCvB,uBAAuB;IA4GrC,OAAO,CAAC,mCAAmC;YAI7B,0BAA0B;IAaxC,OAAO,CAAC,4BAA4B;YAgEtB,uCAAuC;YA+EvC,gCAAgC;YAQhC,yBAAyB;YAQzB,8BAA8B;IAQ5C,OAAO,CAAC,0BAA0B;YAiBpB,qCAAqC;YA2CrC,yBAAyB;YAiDzB,8BAA8B;IAU5C,OAAO,CAAC,kCAAkC;YAI5B,6BAA6B;YAwB7B,wBAAwB;IAoDtC,OAAO,CAAC,sCAAsC;YAchC,cAAc;YA+Bd,6BAA6B;YAW7B,0BAA0B;YAS1B,6BAA6B;YAmB7B,gBAAgB;IAiB9B,OAAO,CAAC,qBAAqB;CAM9B"}
|