@onekeyfe/hd-core 1.2.6-alpha.0 → 1.2.6-alpha.2
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 +64 -0
- package/__tests__/device-state-mapper.test.ts +27 -3
- package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +300 -0
- package/__tests__/protocol-v2.test.ts +105 -10
- package/__tests__/sol-sign-offchain-message.test.ts +0 -8
- package/dist/api/FirmwareUpdateV4.d.ts +1 -0
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/device/DeviceStateMapper.d.ts.map +1 -1
- package/dist/index.js +63 -40
- package/package.json +4 -4
- package/src/api/FirmwareUpdateV4.ts +95 -48
- package/src/device/DeviceStateMapper.ts +5 -3
|
@@ -4,8 +4,11 @@ import CheckAllFirmwareRelease, {
|
|
|
4
4
|
buildProtocolV2FirmwareRelease,
|
|
5
5
|
} from '../src/api/CheckAllFirmwareRelease';
|
|
6
6
|
import { DataManager } from '../src/data-manager';
|
|
7
|
+
import { mapProtocolV2DeviceInfoToState } from '../src/device/DeviceStateMapper';
|
|
8
|
+
import { DeviceStateStore } from '../src/device/DeviceStateStore';
|
|
7
9
|
import { createCoreApi } from '../src/inject';
|
|
8
10
|
|
|
11
|
+
import type { ProtocolV2DeviceInfo } from '@onekeyfe/hd-transport';
|
|
9
12
|
import type { CoreApi } from '../src/types/api';
|
|
10
13
|
import type { DeviceStateVersions, IFirmwareReleaseInfo } from '../src/types';
|
|
11
14
|
|
|
@@ -290,6 +293,67 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
|
|
|
290
293
|
expect(result.targetsToUpdate).toEqual(['app_v1', 'app_v2']);
|
|
291
294
|
});
|
|
292
295
|
|
|
296
|
+
test('does not repeat a P2 update after normal firmware follows an older loader P2 reading', () => {
|
|
297
|
+
const store = new DeviceStateStore();
|
|
298
|
+
store.update(
|
|
299
|
+
mapProtocolV2DeviceInfoToState(
|
|
300
|
+
{
|
|
301
|
+
main_mcu: {
|
|
302
|
+
application: { version: '1.0.2' },
|
|
303
|
+
application_data: { version: '1.0.2', build_id: 'old-p2', hash: [0xbb] },
|
|
304
|
+
},
|
|
305
|
+
} as ProtocolV2DeviceInfo,
|
|
306
|
+
'bootloader'
|
|
307
|
+
),
|
|
308
|
+
'device-info'
|
|
309
|
+
);
|
|
310
|
+
const { state } = store.update(
|
|
311
|
+
mapProtocolV2DeviceInfoToState(
|
|
312
|
+
{
|
|
313
|
+
main_mcu: {
|
|
314
|
+
application: { version: '1.0.3', hash: [0xaa] },
|
|
315
|
+
application_data: { version: '1.0.2', build_id: 'old-p2', hash: [0xbb] },
|
|
316
|
+
},
|
|
317
|
+
} as ProtocolV2DeviceInfo,
|
|
318
|
+
'normal'
|
|
319
|
+
),
|
|
320
|
+
'device-info'
|
|
321
|
+
);
|
|
322
|
+
const packageSet: IFirmwareReleaseInfo = {
|
|
323
|
+
...release,
|
|
324
|
+
installOrder: ['applicationP1', 'applicationP2'],
|
|
325
|
+
components: {
|
|
326
|
+
applicationP1: {
|
|
327
|
+
target: 'APPLICATION_P1',
|
|
328
|
+
version: [1, 0, 3],
|
|
329
|
+
url: 'https://example.com/p1.okpkg',
|
|
330
|
+
},
|
|
331
|
+
applicationP2: {
|
|
332
|
+
target: 'APPLICATION_P2',
|
|
333
|
+
version: [1, 0, 3],
|
|
334
|
+
url: 'https://example.com/p2.okpkg',
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
expect(state.versions.applicationP2).toBeNull();
|
|
340
|
+
expect(state.verification?.applicationP2BuildId).toBeNull();
|
|
341
|
+
expect(state.verification?.applicationP2Hash).toBeNull();
|
|
342
|
+
expect(
|
|
343
|
+
buildProtocolV2FirmwareRelease({
|
|
344
|
+
currentVersions: state.versions,
|
|
345
|
+
currentVerification: state.verification,
|
|
346
|
+
checkFirmwareHash: true,
|
|
347
|
+
remotePayloadHashes: {
|
|
348
|
+
applicationP1: 'aa'.repeat(64),
|
|
349
|
+
applicationP2: 'cc'.repeat(64),
|
|
350
|
+
},
|
|
351
|
+
firmwareType: EFirmwareType.Universal,
|
|
352
|
+
release: packageSet,
|
|
353
|
+
}).targetsToUpdate
|
|
354
|
+
).toEqual([]);
|
|
355
|
+
});
|
|
356
|
+
|
|
293
357
|
test('does not read vol0 resource inventory while Protocol V2 is in Application mode', async () => {
|
|
294
358
|
const method = new CheckAllFirmwareRelease({
|
|
295
359
|
id: 1,
|
|
@@ -110,7 +110,7 @@ describe('DeviceStateMapper', () => {
|
|
|
110
110
|
expect(patch.identity?.firmwareType).toBe(EFirmwareType.Universal);
|
|
111
111
|
});
|
|
112
112
|
|
|
113
|
-
test('maps Protocol V2 DeviceInfo
|
|
113
|
+
test('maps Protocol V2 bootloader DeviceInfo including P2', () => {
|
|
114
114
|
const patch = mapProtocolV2DeviceInfoToState(
|
|
115
115
|
{
|
|
116
116
|
protocol_version: 2,
|
|
@@ -130,7 +130,7 @@ describe('DeviceStateMapper', () => {
|
|
|
130
130
|
application: { version: '3.0.0', build_id: 'se1-build', hash: [0x05, 0x06] },
|
|
131
131
|
},
|
|
132
132
|
} as ProtocolV2DeviceInfo,
|
|
133
|
-
'
|
|
133
|
+
'bootloader'
|
|
134
134
|
);
|
|
135
135
|
|
|
136
136
|
expect(patch.identity).toMatchObject({
|
|
@@ -157,9 +157,33 @@ describe('DeviceStateMapper', () => {
|
|
|
157
157
|
se01: { type: 'THD89', state: 'APP' },
|
|
158
158
|
});
|
|
159
159
|
expect(patch).toMatchObject({ protocolVersion: 2 });
|
|
160
|
-
expect(patch.status?.unlocked).
|
|
160
|
+
expect(patch.status?.unlocked).toBeNull();
|
|
161
161
|
});
|
|
162
162
|
|
|
163
|
+
test.each([undefined, { version: '1.0.2', build_id: 'old-p2', hash: [0x01, 0x02] }])(
|
|
164
|
+
'clears cached loader P2 version and verification when normal firmware reports P1',
|
|
165
|
+
applicationData => {
|
|
166
|
+
const patch = mapProtocolV2DeviceInfoToState(
|
|
167
|
+
{
|
|
168
|
+
main_mcu: {
|
|
169
|
+
application: { version: '1.0.3' },
|
|
170
|
+
application_data: applicationData,
|
|
171
|
+
},
|
|
172
|
+
} as ProtocolV2DeviceInfo,
|
|
173
|
+
'normal'
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
expect(patch.versions).toMatchObject({
|
|
177
|
+
applicationP1: '1.0.3',
|
|
178
|
+
applicationP2: null,
|
|
179
|
+
});
|
|
180
|
+
expect(patch.verification).toMatchObject({
|
|
181
|
+
applicationP2BuildId: null,
|
|
182
|
+
applicationP2Hash: null,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
);
|
|
186
|
+
|
|
163
187
|
test('maps the Protocol V2 Neo device type without treating it as Pro2', () => {
|
|
164
188
|
const patch = mapProtocolV2DeviceInfoToState({
|
|
165
189
|
hw: { Device_type: DeviceType.NEO, serial_no: 'NEO-SERIAL-1' },
|
|
@@ -3,6 +3,8 @@ import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
|
3
3
|
import FirmwareUpdateV4 from '../../src/api/FirmwareUpdateV4';
|
|
4
4
|
|
|
5
5
|
import type { Device } from '../../src/device/Device';
|
|
6
|
+
import type { ProtocolV2DeviceInfo } from '@onekeyfe/hd-transport';
|
|
7
|
+
import type { Features } from '../../src/types';
|
|
6
8
|
|
|
7
9
|
jest.mock('../../src/data/config', () => ({
|
|
8
10
|
DEFAULT_DOMAIN: 'https://example.com/',
|
|
@@ -833,6 +835,304 @@ describe('FirmwareUpdateV4 install polling', () => {
|
|
|
833
835
|
expect(method.postProgressMessage).toHaveBeenCalledWith(100, 'installingFirmware');
|
|
834
836
|
});
|
|
835
837
|
|
|
838
|
+
test.each([
|
|
839
|
+
{ scenario: 'missed progress after reconnect', updatedAt: 0, loaderBeforeUpdate: false },
|
|
840
|
+
{ scenario: 'a reboot on the reconnected link', updatedAt: 1000, loaderBeforeUpdate: true },
|
|
841
|
+
{ scenario: 'completion at the deadline', updatedAt: 600_000, loaderBeforeUpdate: false },
|
|
842
|
+
{
|
|
843
|
+
scenario: 'completion at the deadline without a disconnect',
|
|
844
|
+
updatedAt: 600_000,
|
|
845
|
+
loaderBeforeUpdate: false,
|
|
846
|
+
skipReconnect: true,
|
|
847
|
+
},
|
|
848
|
+
{
|
|
849
|
+
scenario: 'a reboot with an unavailable status endpoint',
|
|
850
|
+
updatedAt: 1000,
|
|
851
|
+
loaderBeforeUpdate: true,
|
|
852
|
+
statusUnavailable: true,
|
|
853
|
+
},
|
|
854
|
+
{
|
|
855
|
+
scenario: 'a different physical device after reconnect',
|
|
856
|
+
updatedAt: 0,
|
|
857
|
+
loaderBeforeUpdate: false,
|
|
858
|
+
differentDevice: true,
|
|
859
|
+
},
|
|
860
|
+
{
|
|
861
|
+
scenario: 'unchanged versions despite cached P2',
|
|
862
|
+
updatedAt: Infinity,
|
|
863
|
+
loaderBeforeUpdate: false,
|
|
864
|
+
},
|
|
865
|
+
{
|
|
866
|
+
scenario: 'normal firmware P1 updated while P2 is unobservable',
|
|
867
|
+
updatedAt: Infinity,
|
|
868
|
+
p1UpdatedAt: 0,
|
|
869
|
+
loaderBeforeUpdate: false,
|
|
870
|
+
},
|
|
871
|
+
{
|
|
872
|
+
scenario: 'P2-only install cannot be inferred from an already updated P1',
|
|
873
|
+
updatedAt: Infinity,
|
|
874
|
+
p1UpdatedAt: 0,
|
|
875
|
+
loaderBeforeUpdate: false,
|
|
876
|
+
p2Only: true,
|
|
877
|
+
},
|
|
878
|
+
{
|
|
879
|
+
scenario: 'normal firmware P1 updated after in-progress status disappears',
|
|
880
|
+
updatedAt: 3000,
|
|
881
|
+
p1UpdatedAt: 0,
|
|
882
|
+
statusInProgressAtStart: true,
|
|
883
|
+
loaderBeforeUpdate: false,
|
|
884
|
+
},
|
|
885
|
+
])(
|
|
886
|
+
'checks fresh versions when BLE status is unavailable: $scenario',
|
|
887
|
+
async ({
|
|
888
|
+
updatedAt,
|
|
889
|
+
p1UpdatedAt,
|
|
890
|
+
p2Only,
|
|
891
|
+
statusInProgressAtStart,
|
|
892
|
+
loaderBeforeUpdate,
|
|
893
|
+
skipReconnect,
|
|
894
|
+
statusUnavailable,
|
|
895
|
+
differentDevice,
|
|
896
|
+
}) => {
|
|
897
|
+
let now = 0;
|
|
898
|
+
jest.spyOn(Date, 'now').mockImplementation(() => now);
|
|
899
|
+
jest.spyOn(global, 'setTimeout').mockImplementation(((
|
|
900
|
+
callback: () => void,
|
|
901
|
+
delay: number
|
|
902
|
+
) => {
|
|
903
|
+
now += delay;
|
|
904
|
+
callback();
|
|
905
|
+
return 0;
|
|
906
|
+
}) as typeof setTimeout);
|
|
907
|
+
const method = new FirmwareUpdateV4({
|
|
908
|
+
id: 1,
|
|
909
|
+
payload: { method: 'firmwareUpdateV4', connectId: 'pro2-ble' },
|
|
910
|
+
});
|
|
911
|
+
const targets = [
|
|
912
|
+
{ target_id: 4, path: 'vol0:/application_p1.bin' },
|
|
913
|
+
{ target_id: 5, path: 'vol0:/application_p2.bin' },
|
|
914
|
+
].filter(target => !p2Only || target.target_id === 5);
|
|
915
|
+
let statusPolls = 0;
|
|
916
|
+
const statusGet = jest.fn().mockImplementation(() => {
|
|
917
|
+
if (statusUnavailable) throw new Error('unsupported message');
|
|
918
|
+
if (statusInProgressAtStart && statusPolls++ === 0) {
|
|
919
|
+
return {
|
|
920
|
+
type: 'DeviceFirmwareUpdateStatus',
|
|
921
|
+
message: { records: [{ ...targets[targets.length - 1], status: 1 }] },
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
return { type: 'DeviceFirmwareUpdateStatus', message: { records: [] } };
|
|
925
|
+
});
|
|
926
|
+
let lastDeviceInfo: ProtocolV2DeviceInfo | undefined;
|
|
927
|
+
const deviceInfoGet = jest.fn().mockImplementation(() => {
|
|
928
|
+
lastDeviceInfo = {
|
|
929
|
+
hw: { serial_no: differentDevice ? 'pro2-other' : 'pro2-test' },
|
|
930
|
+
main_mcu: {
|
|
931
|
+
application: { version: now >= (p1UpdatedAt ?? updatedAt) ? '1.0.2' : '1.0.1' },
|
|
932
|
+
application_data: { version: now >= updatedAt ? '1.0.2' : '1.0.1' },
|
|
933
|
+
},
|
|
934
|
+
};
|
|
935
|
+
return { type: 'DeviceInfo', message: lastDeviceInfo };
|
|
936
|
+
});
|
|
937
|
+
const typedCall = jest.fn().mockImplementation((type: string) => {
|
|
938
|
+
if (type === 'DeviceFirmwareUpdateStatusGet') return statusGet();
|
|
939
|
+
if (type === 'DeviceInfoGet') return deviceInfoGet();
|
|
940
|
+
throw new Error(`Unexpected command: ${type}`);
|
|
941
|
+
});
|
|
942
|
+
let finalFeatures: Features | undefined;
|
|
943
|
+
const probeProtocolV2RuntimeState = jest
|
|
944
|
+
.fn()
|
|
945
|
+
.mockImplementation(
|
|
946
|
+
(
|
|
947
|
+
info: ProtocolV2DeviceInfo,
|
|
948
|
+
_timeout: number,
|
|
949
|
+
options?: { forceRuntimeContextRefresh?: boolean }
|
|
950
|
+
) => {
|
|
951
|
+
const normal =
|
|
952
|
+
options?.forceRuntimeContextRefresh && (!loaderBeforeUpdate || now >= updatedAt);
|
|
953
|
+
finalFeatures = {
|
|
954
|
+
mode: normal ? 'normal' : 'bootloader',
|
|
955
|
+
bootloaderMode: !normal,
|
|
956
|
+
firmwareVersion: info.main_mcu?.application?.version,
|
|
957
|
+
} as Features;
|
|
958
|
+
return finalFeatures;
|
|
959
|
+
}
|
|
960
|
+
);
|
|
961
|
+
method.device = {
|
|
962
|
+
originalDescriptor: { path: 'pro2-ble' },
|
|
963
|
+
getCommands: () => ({ typedCall }),
|
|
964
|
+
// A retained P2 field is deliberately newer than the live pre-install version.
|
|
965
|
+
state: { versions: { applicationP2: '1.0.2' } },
|
|
966
|
+
probeProtocolV2RuntimeState,
|
|
967
|
+
setCancelableAction: jest.fn(),
|
|
968
|
+
} as unknown as Device;
|
|
969
|
+
method.postProgressMessage = jest.fn();
|
|
970
|
+
const firmwareUpdate = method as unknown as {
|
|
971
|
+
isBleReconnect: () => boolean;
|
|
972
|
+
protocolV2InstallNeedsReconnect: boolean;
|
|
973
|
+
protocolV2ExpectedSerialNumber: string;
|
|
974
|
+
protocolV2InstallBaselineVersions: Map<number, string>;
|
|
975
|
+
protocolV2LatestFinalFeatures?: Features;
|
|
976
|
+
protocolV2LatestFinalDeviceInfo?: ProtocolV2DeviceInfo;
|
|
977
|
+
params: { expectedTargetVersions: { app_v1: string; app_v2: string } };
|
|
978
|
+
waitForProtocolV2FirmwareUpdateComplete: (
|
|
979
|
+
value: typeof targets,
|
|
980
|
+
requireCurrentInstallStatus: boolean
|
|
981
|
+
) => Promise<void>;
|
|
982
|
+
reconnectProtocolV2Device: () => Promise<void>;
|
|
983
|
+
assertExpectedProtocolV2Versions: () => void;
|
|
984
|
+
};
|
|
985
|
+
firmwareUpdate.isBleReconnect = () => true;
|
|
986
|
+
firmwareUpdate.protocolV2InstallNeedsReconnect = !skipReconnect;
|
|
987
|
+
firmwareUpdate.protocolV2ExpectedSerialNumber = 'pro2-test';
|
|
988
|
+
firmwareUpdate.protocolV2InstallBaselineVersions = new Map([
|
|
989
|
+
[4, '1.0.1'],
|
|
990
|
+
[5, '1.0.1'],
|
|
991
|
+
]);
|
|
992
|
+
firmwareUpdate.params = { expectedTargetVersions: { app_v1: '1.0.2', app_v2: '1.0.2' } };
|
|
993
|
+
firmwareUpdate.reconnectProtocolV2Device = jest.fn().mockResolvedValue(undefined);
|
|
994
|
+
|
|
995
|
+
const polling = firmwareUpdate.waitForProtocolV2FirmwareUpdateComplete(targets, true);
|
|
996
|
+
if (differentDevice) {
|
|
997
|
+
await expect(polling).rejects.toThrow('physical identity mismatch');
|
|
998
|
+
expect(probeProtocolV2RuntimeState).not.toHaveBeenCalled();
|
|
999
|
+
expect(method.postProgressMessage).not.toHaveBeenCalled();
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
let completionAt = updatedAt;
|
|
1003
|
+
if (p2Only) {
|
|
1004
|
+
completionAt = Infinity;
|
|
1005
|
+
} else if (!loaderBeforeUpdate) {
|
|
1006
|
+
completionAt = Math.min(updatedAt, p1UpdatedAt ?? Infinity);
|
|
1007
|
+
}
|
|
1008
|
+
if (Number.isFinite(completionAt)) {
|
|
1009
|
+
await polling;
|
|
1010
|
+
expect(now).toBe(Math.max(completionAt, statusInProgressAtStart ? 1000 : 0));
|
|
1011
|
+
firmwareUpdate.protocolV2LatestFinalFeatures = finalFeatures;
|
|
1012
|
+
firmwareUpdate.protocolV2LatestFinalDeviceInfo = lastDeviceInfo;
|
|
1013
|
+
expect(() => firmwareUpdate.assertExpectedProtocolV2Versions()).not.toThrow();
|
|
1014
|
+
firmwareUpdate.params.expectedTargetVersions.app_v1 = '1.0.3';
|
|
1015
|
+
expect(() => firmwareUpdate.assertExpectedProtocolV2Versions()).toThrow('expected 1.0.3');
|
|
1016
|
+
expect(method.postProgressMessage).toHaveBeenCalledWith(100, 'installingFirmware');
|
|
1017
|
+
} else {
|
|
1018
|
+
await expect(polling).rejects.toMatchObject({
|
|
1019
|
+
params: { firmwareUpdateCode: 'FirmwareInstallTimeout' },
|
|
1020
|
+
});
|
|
1021
|
+
expect(now).toBe(600_000);
|
|
1022
|
+
expect(method.postProgressMessage).not.toHaveBeenCalled();
|
|
1023
|
+
}
|
|
1024
|
+
expect(deviceInfoGet).toHaveBeenCalledTimes(
|
|
1025
|
+
skipReconnect ? 1 : now / 1000 + 1 - (statusInProgressAtStart ? 1 : 0)
|
|
1026
|
+
);
|
|
1027
|
+
expect(probeProtocolV2RuntimeState).toHaveBeenLastCalledWith(
|
|
1028
|
+
expect.anything(),
|
|
1029
|
+
expect.any(Number),
|
|
1030
|
+
{ forceRuntimeContextRefresh: true }
|
|
1031
|
+
);
|
|
1032
|
+
expect(typedCall).toHaveBeenCalledWith(
|
|
1033
|
+
'DeviceInfoGet',
|
|
1034
|
+
'DeviceInfo',
|
|
1035
|
+
expect.objectContaining({
|
|
1036
|
+
targets: {
|
|
1037
|
+
hw: true,
|
|
1038
|
+
main_mcu: true,
|
|
1039
|
+
coprocessor: true,
|
|
1040
|
+
se1: true,
|
|
1041
|
+
se2: true,
|
|
1042
|
+
se3: true,
|
|
1043
|
+
se4: true,
|
|
1044
|
+
},
|
|
1045
|
+
}),
|
|
1046
|
+
expect.anything()
|
|
1047
|
+
);
|
|
1048
|
+
}
|
|
1049
|
+
);
|
|
1050
|
+
|
|
1051
|
+
test.each([
|
|
1052
|
+
{
|
|
1053
|
+
scenario: 'finishes after five minutes',
|
|
1054
|
+
terminalAt: 360_000,
|
|
1055
|
+
terminalStatus: 2,
|
|
1056
|
+
error: undefined,
|
|
1057
|
+
},
|
|
1058
|
+
{
|
|
1059
|
+
scenario: 'finishes at the deadline',
|
|
1060
|
+
terminalAt: 600_000,
|
|
1061
|
+
terminalStatus: 2,
|
|
1062
|
+
error: undefined,
|
|
1063
|
+
},
|
|
1064
|
+
{
|
|
1065
|
+
scenario: 'remains in progress',
|
|
1066
|
+
terminalAt: Infinity,
|
|
1067
|
+
terminalStatus: 1,
|
|
1068
|
+
error: 'FirmwareInstallTimeout',
|
|
1069
|
+
},
|
|
1070
|
+
{
|
|
1071
|
+
scenario: 'fails at the deadline',
|
|
1072
|
+
terminalAt: 600_000,
|
|
1073
|
+
terminalStatus: 3,
|
|
1074
|
+
error: 'FirmwareInstallFailed',
|
|
1075
|
+
},
|
|
1076
|
+
{
|
|
1077
|
+
scenario: 'is cancelled at the deadline',
|
|
1078
|
+
terminalAt: 600_000,
|
|
1079
|
+
terminalStatus: -1,
|
|
1080
|
+
error: 'cancelled',
|
|
1081
|
+
},
|
|
1082
|
+
])(
|
|
1083
|
+
'polls installation for ten minutes: $scenario',
|
|
1084
|
+
async ({ terminalAt, terminalStatus, error }) => {
|
|
1085
|
+
let now = 0;
|
|
1086
|
+
jest.spyOn(Date, 'now').mockImplementation(() => now);
|
|
1087
|
+
jest.spyOn(global, 'setTimeout').mockImplementation(((
|
|
1088
|
+
callback: () => void,
|
|
1089
|
+
delay: number
|
|
1090
|
+
) => {
|
|
1091
|
+
now += delay;
|
|
1092
|
+
callback();
|
|
1093
|
+
return 0;
|
|
1094
|
+
}) as typeof setTimeout);
|
|
1095
|
+
const method = new FirmwareUpdateV4({
|
|
1096
|
+
id: 1,
|
|
1097
|
+
payload: { method: 'firmwareUpdateV4', connectId: 'pro2-ble' },
|
|
1098
|
+
});
|
|
1099
|
+
const targets = [{ target_id: 4, path: 'vol0:/application_p1.bin' }];
|
|
1100
|
+
const typedCall = jest.fn().mockImplementation(() => {
|
|
1101
|
+
if (now >= terminalAt && terminalStatus === -1) {
|
|
1102
|
+
throw ERRORS.TypedError(HardwareErrorCode.ActionCancelled);
|
|
1103
|
+
}
|
|
1104
|
+
return {
|
|
1105
|
+
type: 'DeviceFirmwareUpdateStatus',
|
|
1106
|
+
message: { records: [{ ...targets[0], status: now >= terminalAt ? terminalStatus : 1 }] },
|
|
1107
|
+
};
|
|
1108
|
+
});
|
|
1109
|
+
method.device = { getCommands: () => ({ typedCall }) } as unknown as Device;
|
|
1110
|
+
method.postProgressMessage = jest.fn();
|
|
1111
|
+
const firmwareUpdate = method as unknown as {
|
|
1112
|
+
isBleReconnect: () => boolean;
|
|
1113
|
+
waitForProtocolV2FirmwareUpdateComplete: (
|
|
1114
|
+
value: typeof targets,
|
|
1115
|
+
requireCurrentInstallStatus: boolean
|
|
1116
|
+
) => Promise<void>;
|
|
1117
|
+
};
|
|
1118
|
+
firmwareUpdate.isBleReconnect = () => true;
|
|
1119
|
+
const polling = firmwareUpdate.waitForProtocolV2FirmwareUpdateComplete(targets, true);
|
|
1120
|
+
if (error) {
|
|
1121
|
+
await expect(polling).rejects.toMatchObject(
|
|
1122
|
+
error === 'cancelled'
|
|
1123
|
+
? { errorCode: HardwareErrorCode.ActionCancelled }
|
|
1124
|
+
: { params: { firmwareUpdateCode: error } }
|
|
1125
|
+
);
|
|
1126
|
+
expect(method.postProgressMessage).not.toHaveBeenCalledWith(100, 'installingFirmware');
|
|
1127
|
+
} else {
|
|
1128
|
+
await polling;
|
|
1129
|
+
expect(method.postProgressMessage).toHaveBeenCalledWith(100, 'installingFirmware');
|
|
1130
|
+
}
|
|
1131
|
+
expect(now).toBe(Math.min(terminalAt, 600_000));
|
|
1132
|
+
expect(typedCall).toHaveBeenCalledTimes(now / 1000 + 1);
|
|
1133
|
+
}
|
|
1134
|
+
);
|
|
1135
|
+
|
|
836
1136
|
test('does not send Request when Stage is rejected', async () => {
|
|
837
1137
|
const method = new FirmwareUpdateV4({
|
|
838
1138
|
id: 1,
|
|
@@ -6328,7 +6328,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6328
6328
|
expect(typedCall).toHaveBeenCalledTimes(3);
|
|
6329
6329
|
});
|
|
6330
6330
|
|
|
6331
|
-
test('waits
|
|
6331
|
+
test('waits ten minutes before rejecting normal mode without install evidence', async () => {
|
|
6332
6332
|
const method = new FirmwareUpdateV4({
|
|
6333
6333
|
id: 1,
|
|
6334
6334
|
payload: {
|
|
@@ -6366,7 +6366,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6366
6366
|
});
|
|
6367
6367
|
|
|
6368
6368
|
expect(method.postProgressMessage).not.toHaveBeenCalledWith(100, 'installingFirmware');
|
|
6369
|
-
expect(typedCall).toHaveBeenCalledTimes(
|
|
6369
|
+
expect(typedCall).toHaveBeenCalledTimes(11);
|
|
6370
6370
|
});
|
|
6371
6371
|
|
|
6372
6372
|
test('preserves multi-app completion evidence when App mode replaces the status endpoint', async () => {
|
|
@@ -6381,6 +6381,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6381
6381
|
},
|
|
6382
6382
|
},
|
|
6383
6383
|
});
|
|
6384
|
+
method.init();
|
|
6384
6385
|
const typedCall = jest
|
|
6385
6386
|
.fn()
|
|
6386
6387
|
.mockRejectedValue(new Error('Failure: Handler not registered for this message'));
|
|
@@ -6410,7 +6411,9 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6410
6411
|
])
|
|
6411
6412
|
).resolves.toBeUndefined();
|
|
6412
6413
|
|
|
6413
|
-
expect(probeProtocolV2RuntimeState).toHaveBeenCalledWith(deviceInfo, 5000
|
|
6414
|
+
expect(probeProtocolV2RuntimeState).toHaveBeenCalledWith(deviceInfo, 5000, {
|
|
6415
|
+
forceRuntimeContextRefresh: true,
|
|
6416
|
+
});
|
|
6414
6417
|
expect(method.postProgressMessage).toHaveBeenCalledWith(100, 'installingFirmware');
|
|
6415
6418
|
expect(Array.from((method as any).protocolV2CompletedTargetIds)).toEqual([4, 5]);
|
|
6416
6419
|
|
|
@@ -6535,7 +6538,9 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6535
6538
|
(method as any).device = stubDevice({ probeProtocolV2RuntimeState });
|
|
6536
6539
|
|
|
6537
6540
|
await expect((method as any).probeProtocolV2NormalMode(deviceInfo)).resolves.toBe(expected);
|
|
6538
|
-
expect(probeProtocolV2RuntimeState).toHaveBeenCalledWith(deviceInfo, 5000
|
|
6541
|
+
expect(probeProtocolV2RuntimeState).toHaveBeenCalledWith(deviceInfo, 5000, {
|
|
6542
|
+
forceRuntimeContextRefresh: true,
|
|
6543
|
+
});
|
|
6539
6544
|
});
|
|
6540
6545
|
|
|
6541
6546
|
test('keeps polling when the firmware status handler is missing in loader mode', async () => {
|
|
@@ -6735,7 +6740,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6735
6740
|
expect(() => (method as any).assertExpectedProtocolV2Versions()).not.toThrow();
|
|
6736
6741
|
});
|
|
6737
6742
|
|
|
6738
|
-
test('uses
|
|
6743
|
+
test('uses bootloader DeviceInfo for the P2 version', () => {
|
|
6739
6744
|
const method = new FirmwareUpdateV4({
|
|
6740
6745
|
id: 1,
|
|
6741
6746
|
payload: {
|
|
@@ -6753,6 +6758,9 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6753
6758
|
major_version: 1,
|
|
6754
6759
|
minor_version: 0,
|
|
6755
6760
|
patch_version: 0,
|
|
6761
|
+
mode: 'bootloader',
|
|
6762
|
+
bootloaderMode: true,
|
|
6763
|
+
firmwareVersion: '1.0.0',
|
|
6756
6764
|
};
|
|
6757
6765
|
(method as any).protocolV2LatestFinalDeviceInfo = {
|
|
6758
6766
|
main_mcu: {
|
|
@@ -6764,7 +6772,36 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6764
6772
|
expect(() => (method as any).assertExpectedProtocolV2Versions()).not.toThrow();
|
|
6765
6773
|
});
|
|
6766
6774
|
|
|
6767
|
-
test('
|
|
6775
|
+
test('uses P1 after normal boot even when DeviceInfo retains an old P2 version', () => {
|
|
6776
|
+
const method = new FirmwareUpdateV4({
|
|
6777
|
+
id: 1,
|
|
6778
|
+
payload: {
|
|
6779
|
+
method: 'firmwareUpdateV4',
|
|
6780
|
+
platform: 'desktop',
|
|
6781
|
+
targetsToUpdate: ['app_v1', 'app_v2'],
|
|
6782
|
+
expectedTargetVersions: { app_v1: '2.0.0', app_v2: '2.0.0' },
|
|
6783
|
+
},
|
|
6784
|
+
});
|
|
6785
|
+
method.init();
|
|
6786
|
+
(method as any).protocolV2LatestFinalFeatures = {
|
|
6787
|
+
major_version: 2,
|
|
6788
|
+
minor_version: 0,
|
|
6789
|
+
patch_version: 0,
|
|
6790
|
+
mode: 'normal',
|
|
6791
|
+
bootloaderMode: false,
|
|
6792
|
+
};
|
|
6793
|
+
(method as any).protocolV2LatestFinalDeviceInfo = {
|
|
6794
|
+
main_mcu: {
|
|
6795
|
+
application: { version: '2.0.0' },
|
|
6796
|
+
application_data: { version: '1.0.1' },
|
|
6797
|
+
},
|
|
6798
|
+
};
|
|
6799
|
+
(method as any).protocolV2CompletedTargetVersions = new Map([[5, 0x20000]]);
|
|
6800
|
+
|
|
6801
|
+
expect(() => (method as any).assertExpectedProtocolV2Versions()).not.toThrow();
|
|
6802
|
+
});
|
|
6803
|
+
|
|
6804
|
+
test('rejects a mismatched bootloader DeviceInfo application slot version', () => {
|
|
6768
6805
|
const method = new FirmwareUpdateV4({
|
|
6769
6806
|
id: 1,
|
|
6770
6807
|
payload: {
|
|
@@ -6782,6 +6819,9 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6782
6819
|
major_version: 1,
|
|
6783
6820
|
minor_version: 0,
|
|
6784
6821
|
patch_version: 0,
|
|
6822
|
+
mode: 'bootloader',
|
|
6823
|
+
bootloaderMode: true,
|
|
6824
|
+
firmwareVersion: '1.0.0',
|
|
6785
6825
|
};
|
|
6786
6826
|
(method as any).protocolV2LatestFinalDeviceInfo = {
|
|
6787
6827
|
main_mcu: {
|
|
@@ -6888,7 +6928,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6888
6928
|
expect(method.postProgressMessage).toHaveBeenCalledWith(100, 'installingFirmware');
|
|
6889
6929
|
});
|
|
6890
6930
|
|
|
6891
|
-
test('keeps polling missing target records until the
|
|
6931
|
+
test('keeps polling missing target records until the ten-minute timeout', async () => {
|
|
6892
6932
|
const method = new FirmwareUpdateV4({
|
|
6893
6933
|
id: 1,
|
|
6894
6934
|
payload: {
|
|
@@ -6926,7 +6966,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6926
6966
|
message: 'Protocol V2 firmware install timed out',
|
|
6927
6967
|
params: { firmwareUpdateCode: 'FirmwareInstallTimeout' },
|
|
6928
6968
|
});
|
|
6929
|
-
expect(typedCall).toHaveBeenCalledTimes(
|
|
6969
|
+
expect(typedCall).toHaveBeenCalledTimes(11);
|
|
6930
6970
|
});
|
|
6931
6971
|
|
|
6932
6972
|
test('keeps polling incomplete records after a Pro2 reboot interruption', async () => {
|
|
@@ -6982,7 +7022,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6982
7022
|
expect(typedCall).toHaveBeenCalledTimes(4);
|
|
6983
7023
|
});
|
|
6984
7024
|
|
|
6985
|
-
test('uses a
|
|
7025
|
+
test('uses a ten-minute Pro2 install status window with a final status check', async () => {
|
|
6986
7026
|
const method = new FirmwareUpdateV4({
|
|
6987
7027
|
id: 1,
|
|
6988
7028
|
payload: {
|
|
@@ -6992,7 +7032,13 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6992
7032
|
jest
|
|
6993
7033
|
.spyOn(Date, 'now')
|
|
6994
7034
|
.mockReturnValueOnce(0)
|
|
6995
|
-
.mockReturnValue(
|
|
7035
|
+
.mockReturnValue(10 * 60 * 1000);
|
|
7036
|
+
const typedCall = jest.fn().mockResolvedValue({
|
|
7037
|
+
type: 'DeviceFirmwareUpdateStatus',
|
|
7038
|
+
message: { records: [{ target_id: 4, status: 1 }] },
|
|
7039
|
+
});
|
|
7040
|
+
method.device = stubDevice({ getCommands: () => ({ typedCall }) });
|
|
7041
|
+
method.postProgressMessage = jest.fn();
|
|
6996
7042
|
|
|
6997
7043
|
await expect(
|
|
6998
7044
|
(method as any).waitForProtocolV2FirmwareUpdateComplete([
|
|
@@ -7003,6 +7049,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
7003
7049
|
message: 'Protocol V2 firmware install timed out',
|
|
7004
7050
|
params: { firmwareUpdateCode: 'FirmwareInstallTimeout' },
|
|
7005
7051
|
});
|
|
7052
|
+
expect(typedCall).toHaveBeenCalledTimes(1);
|
|
7006
7053
|
});
|
|
7007
7054
|
|
|
7008
7055
|
test('uses a 90-second Pro2 bootloader reconnect window', async () => {
|
|
@@ -8914,6 +8961,54 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
8914
8961
|
});
|
|
8915
8962
|
});
|
|
8916
8963
|
|
|
8964
|
+
test.each([
|
|
8965
|
+
['react-native', 10_000],
|
|
8966
|
+
['webusb', undefined],
|
|
8967
|
+
] as const)('uses the native firmware write timeout in %s', async (env, timeoutMs) => {
|
|
8968
|
+
const getSettingsSpy = jest.spyOn(DataManager, 'getSettings').mockReturnValue(env);
|
|
8969
|
+
const method = new FirmwareUpdateV4({
|
|
8970
|
+
id: 1,
|
|
8971
|
+
payload: {
|
|
8972
|
+
method: 'firmwareUpdateV4',
|
|
8973
|
+
},
|
|
8974
|
+
});
|
|
8975
|
+
const typedCall = jest.fn().mockResolvedValue({
|
|
8976
|
+
type: 'FilesystemFile',
|
|
8977
|
+
message: { processed_byte: 1 },
|
|
8978
|
+
});
|
|
8979
|
+
(method as any).device = stubDevice({
|
|
8980
|
+
getCommands: () => ({ typedCall }),
|
|
8981
|
+
});
|
|
8982
|
+
|
|
8983
|
+
try {
|
|
8984
|
+
await (method as any).fileWriteChunk(
|
|
8985
|
+
'vol0:/application_p1.bin',
|
|
8986
|
+
1,
|
|
8987
|
+
0,
|
|
8988
|
+
new Uint8Array([1]),
|
|
8989
|
+
true,
|
|
8990
|
+
1
|
|
8991
|
+
);
|
|
8992
|
+
} finally {
|
|
8993
|
+
getSettingsSpy.mockRestore();
|
|
8994
|
+
}
|
|
8995
|
+
|
|
8996
|
+
expect(typedCall).toHaveBeenCalledWith(
|
|
8997
|
+
'FilesystemFileWrite',
|
|
8998
|
+
'FilesystemFile',
|
|
8999
|
+
expect.objectContaining({
|
|
9000
|
+
file: expect.objectContaining({
|
|
9001
|
+
path: 'vol0:/application_p1.bin',
|
|
9002
|
+
offset: 0,
|
|
9003
|
+
}),
|
|
9004
|
+
}),
|
|
9005
|
+
expect.objectContaining({
|
|
9006
|
+
timeoutMs,
|
|
9007
|
+
writeWithResponse: false,
|
|
9008
|
+
})
|
|
9009
|
+
);
|
|
9010
|
+
});
|
|
9011
|
+
|
|
8917
9012
|
test('does not retry or wrap a stale BLE bond during a V4 file transfer', async () => {
|
|
8918
9013
|
const method = new FirmwareUpdateV4({
|
|
8919
9014
|
id: 1,
|
|
@@ -54,14 +54,6 @@ describe('SolSignOffchainMessage', () => {
|
|
|
54
54
|
});
|
|
55
55
|
});
|
|
56
56
|
|
|
57
|
-
test('maps omitted required signers to an empty repeated field', () => {
|
|
58
|
-
const method = createMethod({ requiredSigners: undefined });
|
|
59
|
-
|
|
60
|
-
method.init();
|
|
61
|
-
|
|
62
|
-
expect(method.params.required_signers).toEqual([]);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
57
|
test.each([
|
|
66
58
|
{ requiredSigners: ['11'], error: '32-byte hex public key' },
|
|
67
59
|
{ requiredSigners: [signerB, signerA], error: 'strictly sorted and unique' },
|
|
@@ -16,6 +16,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
16
16
|
private protocolV2CompletedTargetIds;
|
|
17
17
|
private protocolV2LatestFinalFeatures?;
|
|
18
18
|
private protocolV2LatestFinalDeviceInfo?;
|
|
19
|
+
private protocolV2InitialDeviceInfo?;
|
|
19
20
|
private protocolV2InstallBaselineVersions;
|
|
20
21
|
private protocolV2InstallNeedsReconnect;
|
|
21
22
|
private protocolV2InstallDisconnectObserved;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FirmwareUpdateV4.d.ts","sourceRoot":"","sources":["../../src/api/FirmwareUpdateV4.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EAMZ,MAAM,qBAAqB,CAAC;AA0B7B,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAiC/E,OAAO,KAAK,EAEV,sBAAsB,EAEvB,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"FirmwareUpdateV4.d.ts","sourceRoot":"","sources":["../../src/api/FirmwareUpdateV4.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EAMZ,MAAM,qBAAqB,CAAC;AA0B7B,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAiC/E,OAAO,KAAK,EAEV,sBAAsB,EAEvB,MAAM,6BAA6B,CAAC;AAuErC,wBAAgB,wCAAwC,CACtD,UAAU,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,EAC5C,MAAM,EAAE,sBAAsB,EAC9B,0BAA0B,UAAQ,QAiCnC;AA8UD,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,OAAO,CAAC,oCAAoC,CAAS;IAErD,qBAAqB;IAIrB,OAAO,CAAC,yBAAyB,CAA4B;IAE7D,OAAO,CAAC,2BAA2B,CAAS;IAE5C,OAAO,CAAC,iCAAiC,CAAS;IAElD,OAAO,CAAC,iCAAiC,CAA6B;IAEtE,OAAO,CAAC,4BAA4B,CAAqB;IAEzD,OAAO,CAAC,6BAA6B,CAAC,CAAW;IAEjD,OAAO,CAAC,+BAA+B,CAAC,CAAuB;IAE/D,OAAO,CAAC,2BAA2B,CAAC,CAAuB;IAE3D,OAAO,CAAC,iCAAiC,CAA6B;IAEtE,OAAO,CAAC,+BAA+B,CAAS;IAEhD,OAAO,CAAC,mCAAmC,CAAS;IAEpD,OAAO,CAAC,wCAAwC,CAAS;IAEzD,OAAO,CAAC,kCAAkC,CAAC,CAAW;IAEtD,OAAO,CAAC,8BAA8B,CAAC,CAAS;IAEhD,OAAO,CAAC,gCAAgC,CAAK;IAE7C,OAAO,CAAC,8BAA8B,CAAK;IAE3C,IAAI;IA6LJ,OAAO,CAAC,8BAA8B;IAwBhC,GAAG;;;;;YAKK,aAAa;YA4Ib,4BAA4B;YAkB5B,0BAA0B;YAY1B,8BAA8B;YAK9B,+BAA+B;YAqG/B,4BAA4B;YA+C5B,0CAA0C;YAkB1C,qCAAqC;YAoFrC,gCAAgC;YAgBhC,uCAAuC;YAmDvC,8BAA8B;IA8B5C,OAAO,CAAC,8BAA8B;IA0BtC,OAAO,CAAC,kCAAkC;IAc1C,OAAO,CAAC,gCAAgC;YA2D1B,2BAA2B;IAmBzC,OAAO,CAAC,yBAAyB;IAKjC,OAAO,CAAC,oCAAoC;IAY5C,OAAO,CAAC,4BAA4B;IAUpC,OAAO,CAAC,kCAAkC;IAS1C,OAAO,CAAC,8BAA8B;YAMxB,iCAAiC;YAOjC,iCAAiC;YAoBjC,iCAAiC;IAU/C,OAAO,CAAC,uBAAuB;IAI/B,OAAO,CAAC,mCAAmC;IAe3C,OAAO,CAAC,iCAAiC;IAWzC,OAAO,CAAC,2BAA2B;IAsBnC,OAAO,CAAC,yBAAyB;IAiBjC,OAAO,CAAC,wBAAwB;YAkBlB,iCAAiC;YA6CjC,uCAAuC;YA0CvC,+BAA+B;IAmF7C,OAAO,CAAC,6BAA6B;IAMrC,OAAO,CAAC,gCAAgC;YAM1B,8BAA8B;YAmD9B,kCAAkC;IAkChD,OAAO,CAAC,0BAA0B;IAUlC,OAAO,CAAC,yBAAyB;YAcnB,4BAA4B;IAkBpC,6BAA6B;YAkBrB,+BAA+B;IAkD7C,OAAO,CAAC,6BAA6B;YAkCvB,6BAA6B;YA0B7B,0CAA0C;YA6B1C,uBAAuB;YAiCvB,8BAA8B;YAkF9B,6BAA6B;IA0F3C,OAAO,CAAC,mCAAmC;YAI7B,0BAA0B;IAaxC,OAAO,CAAC,8BAA8B;IAiBtC,OAAO,CAAC,4BAA4B;IAoLpC,OAAO,CAAC,6BAA6B;IAcrC,OAAO,CAAC,qCAAqC;IAqC7C,OAAO,CAAC,kCAAkC;IAqC1C,OAAO,CAAC,8CAA8C;YAIxC,uCAAuC;YA8UvC,gCAAgC;YAkBhC,yBAAyB;IAUvC,OAAO,CAAC,2BAA2B;YAMrB,8BAA8B;IAQ5C,OAAO,CAAC,0BAA0B;YAkBpB,mCAAmC;YAWnC,qCAAqC;YAmDrC,yBAAyB;YAgGzB,8BAA8B;IAU5C,OAAO,CAAC,kCAAkC;YAQ5B,cAAc;YAyCd,6BAA6B;YAW7B,0BAA0B;YAoB1B,6BAA6B;YAwD7B,gBAAgB;IA0B9B,OAAO,CAAC,qBAAqB;CAM9B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeviceStateMapper.d.ts","sourceRoot":"","sources":["../../src/device/DeviceStateMapper.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,aAAa,EAEb,cAAc,EACd,YAAY,EACZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAEV,eAAe,EACf,gBAAgB,EAChB,QAAQ,EACR,cAAc,EACf,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAsDxE,eAAO,MAAM,kBAAkB,aAAc,QAAQ,KAAG,gBAqEtD,CAAC;AAEH,eAAO,MAAM,4BAA4B,uBACnB,cAAc,KACjC,gBAA0F,CAAC;AAc9F,eAAO,MAAM,kCAAkC,aAAc,cAAc,KAAG,gBAgE7E,CAAC;AAEF,eAAO,MAAM,8BAA8B,SACnC,oBAAoB,SACpB,eAAe,KACpB,
|
|
1
|
+
{"version":3,"file":"DeviceStateMapper.d.ts","sourceRoot":"","sources":["../../src/device/DeviceStateMapper.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,aAAa,EAEb,cAAc,EACd,YAAY,EACZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAEV,eAAe,EACf,gBAAgB,EAChB,QAAQ,EACR,cAAc,EACf,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAsDxE,eAAO,MAAM,kBAAkB,aAAc,QAAQ,KAAG,gBAqEtD,CAAC;AAEH,eAAO,MAAM,4BAA4B,uBACnB,cAAc,KACjC,gBAA0F,CAAC;AAc9F,eAAO,MAAM,kCAAkC,aAAc,cAAc,KAAG,gBAgE7E,CAAC;AAEF,eAAO,MAAM,8BAA8B,SACnC,oBAAoB,SACpB,eAAe,KACpB,gBAyGF,CAAC;AAEF,eAAO,MAAM,gCAAgC,WAAY,YAAY,KAAG,gBActE,CAAC;AAEH,eAAO,MAAM,uBAAuB,aAAc,aAAa,KAAG,gBAmBjE,CAAC;AAEF,eAAO,MAAM,wBAAwB,aAAc,cAAc,KAAG,gBA0BnE,CAAC;AAEF,eAAO,MAAM,6BAA6B,aAAc,oBAAoB,KAAG,cAe3E,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -44899,7 +44899,7 @@ const mapProtocolV2DeviceInfoToState = (info, mode = 'unknown') => {
|
|
|
44899
44899
|
versions: definedEntries({
|
|
44900
44900
|
firmware: imageVersion((_d = info.main_mcu) === null || _d === void 0 ? void 0 : _d.application),
|
|
44901
44901
|
applicationP1: imageVersion((_e = info.main_mcu) === null || _e === void 0 ? void 0 : _e.application),
|
|
44902
|
-
applicationP2: imageVersion((_f = info.main_mcu) === null || _f === void 0 ? void 0 : _f.application_data),
|
|
44902
|
+
applicationP2: mode === 'normal' ? null : imageVersion((_f = info.main_mcu) === null || _f === void 0 ? void 0 : _f.application_data),
|
|
44903
44903
|
bootloader: imageVersion((_g = info.main_mcu) === null || _g === void 0 ? void 0 : _g.bootloader),
|
|
44904
44904
|
board: imageVersion((_h = info.main_mcu) === null || _h === void 0 ? void 0 : _h.romloader),
|
|
44905
44905
|
ble: imageVersion((_j = info.coprocessor) === null || _j === void 0 ? void 0 : _j.application),
|
|
@@ -44917,8 +44917,8 @@ const mapProtocolV2DeviceInfoToState = (info, mode = 'unknown') => {
|
|
|
44917
44917
|
firmwareHash: imageHash((_u = info.main_mcu) === null || _u === void 0 ? void 0 : _u.application),
|
|
44918
44918
|
applicationP1BuildId: imageBuildId((_v = info.main_mcu) === null || _v === void 0 ? void 0 : _v.application),
|
|
44919
44919
|
applicationP1Hash: imageHash((_w = info.main_mcu) === null || _w === void 0 ? void 0 : _w.application),
|
|
44920
|
-
applicationP2BuildId: imageBuildId((_x = info.main_mcu) === null || _x === void 0 ? void 0 : _x.application_data),
|
|
44921
|
-
applicationP2Hash: imageHash((_y = info.main_mcu) === null || _y === void 0 ? void 0 : _y.application_data),
|
|
44920
|
+
applicationP2BuildId: mode === 'normal' ? null : imageBuildId((_x = info.main_mcu) === null || _x === void 0 ? void 0 : _x.application_data),
|
|
44921
|
+
applicationP2Hash: mode === 'normal' ? null : imageHash((_y = info.main_mcu) === null || _y === void 0 ? void 0 : _y.application_data),
|
|
44922
44922
|
bootloaderBuildId: imageBuildId((_z = info.main_mcu) === null || _z === void 0 ? void 0 : _z.bootloader),
|
|
44923
44923
|
bootloaderHash: imageHash((_0 = info.main_mcu) === null || _0 === void 0 ? void 0 : _0.bootloader),
|
|
44924
44924
|
boardBuildId: imageBuildId((_1 = info.main_mcu) === null || _1 === void 0 ? void 0 : _1.romloader),
|
|
@@ -52412,7 +52412,8 @@ const PROTOCOL_V2_BOOTLOADER_RECONNECT_TIMEOUT = 90 * 1000;
|
|
|
52412
52412
|
const PROTOCOL_V2_FINAL_RECONNECT_TIMEOUT = 3 * 60 * 1000;
|
|
52413
52413
|
const PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT = 5 * 1000;
|
|
52414
52414
|
const PROTOCOL_V2_FIRMWARE_STATUS_RESPONSE_TIMEOUT = 15 * 1000;
|
|
52415
|
-
const
|
|
52415
|
+
const PROTOCOL_V2_FILE_WRITE_RESPONSE_TIMEOUT = 10 * 1000;
|
|
52416
|
+
const PROTOCOL_V2_INSTALL_TIMEOUT = 10 * 60 * 1000;
|
|
52416
52417
|
const PROTOCOL_V2_INSTALL_STATUS_INITIAL_DELAY = 1000;
|
|
52417
52418
|
const PROTOCOL_V2_INSTALL_FINISHED_AFTER_DISCONNECT_POLLS = 4;
|
|
52418
52419
|
const PROTOCOL_V2_TARGET_STATUS_PENDING = 0;
|
|
@@ -52891,8 +52892,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
52891
52892
|
return __awaiter(this, void 0, void 0, function* () {
|
|
52892
52893
|
yield this.captureProtocolV2PhysicalIdentity();
|
|
52893
52894
|
const deviceFeatures = yield this.getProtocolV2DeviceFeatures();
|
|
52894
|
-
this.protocolV2InstallBaselineVersions =
|
|
52895
|
-
this.getProtocolV2ObservableTargetVersions(deviceFeatures);
|
|
52895
|
+
this.protocolV2InstallBaselineVersions = this.getProtocolV2ObservableTargetVersions(deviceFeatures, this.protocolV2InitialDeviceInfo);
|
|
52896
52896
|
this.protocolV2LastRuntimeProbeFeatures = undefined;
|
|
52897
52897
|
const currentDeviceType = this.device.getCurrentDeviceType();
|
|
52898
52898
|
const capabilityDeviceType = currentDeviceType === hdShared.EDeviceType.Pro2 || currentDeviceType === hdShared.EDeviceType.Neo
|
|
@@ -53297,7 +53297,9 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
53297
53297
|
app_v1: features
|
|
53298
53298
|
? getDeviceFirmwareVersion(features).join('.')
|
|
53299
53299
|
: (_c = (_b = deviceInfo === null || deviceInfo === void 0 ? void 0 : deviceInfo.main_mcu) === null || _b === void 0 ? void 0 : _b.application) === null || _c === void 0 ? void 0 : _c.version,
|
|
53300
|
-
app_v2: (
|
|
53300
|
+
app_v2: (features === null || features === void 0 ? void 0 : features.mode) === 'normal'
|
|
53301
|
+
? getDeviceFirmwareVersion(features).join('.')
|
|
53302
|
+
: (_e = (_d = deviceInfo === null || deviceInfo === void 0 ? void 0 : deviceInfo.main_mcu) === null || _d === void 0 ? void 0 : _d.application_data) === null || _e === void 0 ? void 0 : _e.version,
|
|
53301
53303
|
coprocessor: features ? getDeviceBLEFirmwareVersion(features).join('.') : undefined,
|
|
53302
53304
|
se01: (_f = features === null || features === void 0 ? void 0 : features.se01Version) !== null && _f !== void 0 ? _f : undefined,
|
|
53303
53305
|
se02: (_g = features === null || features === void 0 ? void 0 : features.se02Version) !== null && _g !== void 0 ? _g : undefined,
|
|
@@ -53402,11 +53404,16 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
53402
53404
|
}
|
|
53403
53405
|
this.protocolV2ExpectedSerialNumber = serialNumber;
|
|
53404
53406
|
this.protocolV2ExpectedPath = path;
|
|
53407
|
+
this.protocolV2InitialDeviceInfo = deviceInfo;
|
|
53405
53408
|
});
|
|
53406
53409
|
}
|
|
53407
|
-
verifyProtocolV2ReconnectIdentity() {
|
|
53410
|
+
verifyProtocolV2ReconnectIdentity(request) {
|
|
53408
53411
|
return __awaiter(this, void 0, void 0, function* () {
|
|
53409
|
-
const deviceInfo = yield
|
|
53412
|
+
const deviceInfo = yield requestProtocolV2DeviceInfo({
|
|
53413
|
+
commands: this.device.getCommands(),
|
|
53414
|
+
timeoutMs: this.getProtocolV2DeviceInfoTimeout(),
|
|
53415
|
+
request,
|
|
53416
|
+
});
|
|
53410
53417
|
this.assertProtocolV2DeviceInfoIdentity(deviceInfo);
|
|
53411
53418
|
return deviceInfo;
|
|
53412
53419
|
});
|
|
@@ -54158,12 +54165,18 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
54158
54165
|
});
|
|
54159
54166
|
return Array.from(expectedTargetIds).filter(targetId => !reportedTargetIds.has(targetId));
|
|
54160
54167
|
}
|
|
54161
|
-
getProtocolV2ObservableTargetVersions(features) {
|
|
54168
|
+
getProtocolV2ObservableTargetVersions(features, deviceInfo) {
|
|
54169
|
+
var _a, _b;
|
|
54162
54170
|
const versions = new Map();
|
|
54163
54171
|
versions.set(ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER, getDeviceBootloaderVersion(features).join('.'));
|
|
54164
54172
|
const applicationVersion = getDeviceFirmwareVersion(features).join('.');
|
|
54165
54173
|
versions.set(ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1, applicationVersion);
|
|
54166
|
-
|
|
54174
|
+
const applicationP2Version = features.mode === 'normal'
|
|
54175
|
+
? applicationVersion
|
|
54176
|
+
: (_b = (_a = deviceInfo === null || deviceInfo === void 0 ? void 0 : deviceInfo.main_mcu) === null || _a === void 0 ? void 0 : _a.application_data) === null || _b === void 0 ? void 0 : _b.version;
|
|
54177
|
+
if (applicationP2Version) {
|
|
54178
|
+
versions.set(ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P2, applicationP2Version);
|
|
54179
|
+
}
|
|
54167
54180
|
versions.set(ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_COPROCESSOR, getDeviceBLEFirmwareVersion(features).join('.'));
|
|
54168
54181
|
const secureElementVersions = [
|
|
54169
54182
|
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE01, features.se01Version],
|
|
@@ -54177,17 +54190,31 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
54177
54190
|
});
|
|
54178
54191
|
return versions;
|
|
54179
54192
|
}
|
|
54180
|
-
hasProtocolV2InstallVersionChanged(expectedTargetIds) {
|
|
54193
|
+
hasProtocolV2InstallVersionChanged(expectedTargetIds, deviceInfo) {
|
|
54181
54194
|
if (!this.protocolV2LastRuntimeProbeFeatures)
|
|
54182
54195
|
return false;
|
|
54183
|
-
const currentVersions = this.getProtocolV2ObservableTargetVersions(this.protocolV2LastRuntimeProbeFeatures);
|
|
54184
|
-
|
|
54185
|
-
|
|
54186
|
-
|
|
54187
|
-
|
|
54188
|
-
|
|
54189
|
-
|
|
54190
|
-
|
|
54196
|
+
const currentVersions = this.getProtocolV2ObservableTargetVersions(this.protocolV2LastRuntimeProbeFeatures, deviceInfo);
|
|
54197
|
+
const p1TargetId = ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1;
|
|
54198
|
+
const p2TargetId = ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P2;
|
|
54199
|
+
return (expectedTargetIds.size > 0 &&
|
|
54200
|
+
Array.from(expectedTargetIds).every(targetId => {
|
|
54201
|
+
var _a, _b, _c;
|
|
54202
|
+
if (targetId === p2TargetId && ((_a = this.protocolV2LastRuntimeProbeFeatures) === null || _a === void 0 ? void 0 : _a.mode) === 'normal') {
|
|
54203
|
+
if (!expectedTargetIds.has(p1TargetId))
|
|
54204
|
+
return false;
|
|
54205
|
+
const previousP1Version = this.protocolV2InstallBaselineVersions.get(p1TargetId);
|
|
54206
|
+
const currentP1Version = currentVersions.get(p1TargetId);
|
|
54207
|
+
return (previousP1Version !== undefined &&
|
|
54208
|
+
currentP1Version !== undefined &&
|
|
54209
|
+
previousP1Version !== currentP1Version &&
|
|
54210
|
+
currentP1Version === ((_c = (_b = this.params) === null || _b === void 0 ? void 0 : _b.expectedTargetVersions) === null || _c === void 0 ? void 0 : _c.app_v2));
|
|
54211
|
+
}
|
|
54212
|
+
const previousVersion = this.protocolV2InstallBaselineVersions.get(targetId);
|
|
54213
|
+
const currentVersion = currentVersions.get(targetId);
|
|
54214
|
+
return (previousVersion !== undefined &&
|
|
54215
|
+
currentVersion !== undefined &&
|
|
54216
|
+
previousVersion !== currentVersion);
|
|
54217
|
+
}));
|
|
54191
54218
|
}
|
|
54192
54219
|
recordProtocolV2AuthoritativeInstallCompletion(expectedTargetIds) {
|
|
54193
54220
|
expectedTargetIds.forEach(targetId => this.protocolV2CompletedTargetIds.add(targetId));
|
|
@@ -54224,8 +54251,9 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
54224
54251
|
finishedStatusSnapshotKey = undefined;
|
|
54225
54252
|
finishedStatusSnapshotPolls = 0;
|
|
54226
54253
|
};
|
|
54227
|
-
|
|
54254
|
+
for (;;) {
|
|
54228
54255
|
this.throwIfAborted();
|
|
54256
|
+
const isFinalStatusCheck = Date.now() - startTime >= PROTOCOL_V2_INSTALL_TIMEOUT;
|
|
54229
54257
|
try {
|
|
54230
54258
|
if (shouldReconnect) {
|
|
54231
54259
|
const isBleInstallReconnect = this.isBleReconnect();
|
|
@@ -54347,17 +54375,13 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
54347
54375
|
lastError = new Error('Protocol V2 firmware status is stale; waiting for the current install to start');
|
|
54348
54376
|
}
|
|
54349
54377
|
if (statusTargets.length === 0 &&
|
|
54350
|
-
|
|
54351
|
-
|
|
54352
|
-
installEvidenceObserved) {
|
|
54353
|
-
currentDeviceInfo = yield this.verifyProtocolV2ReconnectIdentity();
|
|
54378
|
+
(currentDeviceInfo || bleInstallLinkReady || isFinalStatusCheck)) {
|
|
54379
|
+
currentDeviceInfo = yield this.verifyProtocolV2ReconnectIdentity(PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST);
|
|
54354
54380
|
deviceInfo = currentDeviceInfo;
|
|
54355
|
-
}
|
|
54356
|
-
if (statusTargets.length === 0 && currentDeviceInfo) {
|
|
54357
54381
|
const isNormalMode = yield this.probeProtocolV2NormalMode(currentDeviceInfo);
|
|
54358
54382
|
if (isNormalMode &&
|
|
54359
54383
|
(installEvidenceObserved ||
|
|
54360
|
-
this.hasProtocolV2InstallVersionChanged(expectedTargetIds))) {
|
|
54384
|
+
this.hasProtocolV2InstallVersionChanged(expectedTargetIds, currentDeviceInfo))) {
|
|
54361
54385
|
Log$6.log('[FirmwareUpdateV4] empty firmware status after confirmed App reboot; update complete');
|
|
54362
54386
|
this.recordProtocolV2AuthoritativeInstallCompletion(expectedTargetIds);
|
|
54363
54387
|
this.postProgressMessage(100, 'installingFirmware');
|
|
@@ -54379,24 +54403,19 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
54379
54403
|
}
|
|
54380
54404
|
}
|
|
54381
54405
|
catch (error) {
|
|
54382
|
-
if (
|
|
54406
|
+
if (this.isProtocolV2ReconnectIdentityError(error) ||
|
|
54407
|
+
isProtocolV2TerminalInstallStatusError(error)) {
|
|
54383
54408
|
throw error;
|
|
54384
54409
|
}
|
|
54385
54410
|
if (isProtocolV2FirmwareStatusEndpointUnavailable(error)) {
|
|
54386
|
-
if (!currentDeviceInfo) {
|
|
54387
|
-
if (!bleInstallLinkReady) {
|
|
54388
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 device identity is unavailable during install polling');
|
|
54389
|
-
}
|
|
54390
|
-
deviceInfo = yield this.verifyProtocolV2ReconnectIdentity();
|
|
54391
|
-
}
|
|
54392
|
-
const reconnectDeviceInfo = currentDeviceInfo !== null && currentDeviceInfo !== void 0 ? currentDeviceInfo : deviceInfo;
|
|
54393
|
-
if (!reconnectDeviceInfo) {
|
|
54411
|
+
if (!currentDeviceInfo && !bleInstallLinkReady && !isFinalStatusCheck) {
|
|
54394
54412
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 device identity is unavailable during install polling');
|
|
54395
54413
|
}
|
|
54396
|
-
|
|
54414
|
+
deviceInfo = yield this.verifyProtocolV2ReconnectIdentity(PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST);
|
|
54415
|
+
const isNormalMode = yield this.probeProtocolV2NormalMode(deviceInfo);
|
|
54397
54416
|
if (isNormalMode &&
|
|
54398
54417
|
(installEvidenceObserved ||
|
|
54399
|
-
this.hasProtocolV2InstallVersionChanged(expectedTargetIds))) {
|
|
54418
|
+
this.hasProtocolV2InstallVersionChanged(expectedTargetIds, deviceInfo))) {
|
|
54400
54419
|
Log$6.log('[FirmwareUpdateV4] firmware status endpoint unavailable after confirmed App reboot');
|
|
54401
54420
|
this.recordProtocolV2AuthoritativeInstallCompletion(expectedTargetIds);
|
|
54402
54421
|
this.postProgressMessage(100, 'installingFirmware');
|
|
@@ -54437,6 +54456,8 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
54437
54456
|
bleInstallLinkReady = false;
|
|
54438
54457
|
Log$6.log('Protocol V2 firmware install device readiness probe failed: ', error);
|
|
54439
54458
|
}
|
|
54459
|
+
if (isFinalStatusCheck)
|
|
54460
|
+
break;
|
|
54440
54461
|
yield hdShared.wait(1000);
|
|
54441
54462
|
}
|
|
54442
54463
|
Log$6.error(`[FirmwareUpdateV4] install timed out after ${PROTOCOL_V2_INSTALL_TIMEOUT / 1000}s: ${this.normalizeErrorMessage(lastError)}`);
|
|
@@ -54467,7 +54488,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
54467
54488
|
}
|
|
54468
54489
|
probeProtocolV2NormalMode(deviceInfo) {
|
|
54469
54490
|
return __awaiter(this, void 0, void 0, function* () {
|
|
54470
|
-
const features = yield this.device.probeProtocolV2RuntimeState(deviceInfo, PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT);
|
|
54491
|
+
const features = yield this.device.probeProtocolV2RuntimeState(deviceInfo, PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT, { forceRuntimeContextRefresh: true });
|
|
54471
54492
|
this.protocolV2LastRuntimeProbeFeatures = features;
|
|
54472
54493
|
return this.isProtocolV2ApplicationMode(features);
|
|
54473
54494
|
});
|
|
@@ -54627,6 +54648,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
54627
54648
|
var _a;
|
|
54628
54649
|
return __awaiter(this, void 0, void 0, function* () {
|
|
54629
54650
|
const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
|
|
54651
|
+
const env = DataManager.getSettings('env');
|
|
54630
54652
|
const writeRes = yield typedCall('FilesystemFileWrite', 'FilesystemFile', {
|
|
54631
54653
|
file: {
|
|
54632
54654
|
path: filePath,
|
|
@@ -54638,6 +54660,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
54638
54660
|
append: false,
|
|
54639
54661
|
ui_percentage: progress !== null && progress !== void 0 ? progress : undefined,
|
|
54640
54662
|
}, {
|
|
54663
|
+
timeoutMs: env === 'react-native' ? PROTOCOL_V2_FILE_WRITE_RESPONSE_TIMEOUT : undefined,
|
|
54641
54664
|
writeWithResponse: false,
|
|
54642
54665
|
onWriteCompleted: () => undefined,
|
|
54643
54666
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-core",
|
|
3
|
-
"version": "1.2.6-alpha.
|
|
3
|
+
"version": "1.2.6-alpha.2",
|
|
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.6-alpha.
|
|
29
|
-
"@onekeyfe/hd-transport": "1.2.6-alpha.
|
|
28
|
+
"@onekeyfe/hd-shared": "1.2.6-alpha.2",
|
|
29
|
+
"@onekeyfe/hd-transport": "1.2.6-alpha.2",
|
|
30
30
|
"axios": "1.15.2",
|
|
31
31
|
"bignumber.js": "^9.0.2",
|
|
32
32
|
"buffer": "^6.0.3",
|
|
@@ -46,5 +46,5 @@
|
|
|
46
46
|
"@types/w3c-web-usb": "^1.0.10",
|
|
47
47
|
"@types/web-bluetooth": "^0.0.21"
|
|
48
48
|
},
|
|
49
|
-
"gitHead": "
|
|
49
|
+
"gitHead": "c01014c750c5c0bc4f6bff2e07b2fee985893f59"
|
|
50
50
|
}
|
|
@@ -70,7 +70,7 @@ import type {
|
|
|
70
70
|
FirmwareUpdateV4Target,
|
|
71
71
|
} from '../types/api/firmwareUpdate';
|
|
72
72
|
import type { EFirmwareType } from '@onekeyfe/hd-shared';
|
|
73
|
-
import type { ProtocolV2DeviceInfo } from '@onekeyfe/hd-transport';
|
|
73
|
+
import type { DeviceInfoGet, ProtocolV2DeviceInfo } from '@onekeyfe/hd-transport';
|
|
74
74
|
import type { TypedResponseMessage } from '../device/DeviceCommands';
|
|
75
75
|
import type {
|
|
76
76
|
Features,
|
|
@@ -91,7 +91,9 @@ const PROTOCOL_V2_BOOTLOADER_RECONNECT_TIMEOUT = 90 * 1000;
|
|
|
91
91
|
const PROTOCOL_V2_FINAL_RECONNECT_TIMEOUT = 3 * 60 * 1000;
|
|
92
92
|
const PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT = 5 * 1000;
|
|
93
93
|
const PROTOCOL_V2_FIRMWARE_STATUS_RESPONSE_TIMEOUT = 15 * 1000;
|
|
94
|
-
|
|
94
|
+
// React Native invalidates a timed-out BLE link, and V4 can safely replay the staging file.
|
|
95
|
+
const PROTOCOL_V2_FILE_WRITE_RESPONSE_TIMEOUT = 10 * 1000;
|
|
96
|
+
const PROTOCOL_V2_INSTALL_TIMEOUT = 10 * 60 * 1000;
|
|
95
97
|
const PROTOCOL_V2_INSTALL_STATUS_INITIAL_DELAY = 1000;
|
|
96
98
|
const PROTOCOL_V2_INSTALL_FINISHED_AFTER_DISCONNECT_POLLS = 4;
|
|
97
99
|
const PROTOCOL_V2_TARGET_STATUS_PENDING = 0;
|
|
@@ -577,6 +579,8 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
577
579
|
|
|
578
580
|
private protocolV2LatestFinalDeviceInfo?: ProtocolV2DeviceInfo;
|
|
579
581
|
|
|
582
|
+
private protocolV2InitialDeviceInfo?: ProtocolV2DeviceInfo;
|
|
583
|
+
|
|
580
584
|
private protocolV2InstallBaselineVersions = new Map<number, string>();
|
|
581
585
|
|
|
582
586
|
private protocolV2InstallNeedsReconnect = false;
|
|
@@ -814,8 +818,10 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
814
818
|
private async runProtocolV2() {
|
|
815
819
|
await this.captureProtocolV2PhysicalIdentity();
|
|
816
820
|
const deviceFeatures = await this.getProtocolV2DeviceFeatures();
|
|
817
|
-
this.protocolV2InstallBaselineVersions =
|
|
818
|
-
|
|
821
|
+
this.protocolV2InstallBaselineVersions = this.getProtocolV2ObservableTargetVersions(
|
|
822
|
+
deviceFeatures,
|
|
823
|
+
this.protocolV2InitialDeviceInfo
|
|
824
|
+
);
|
|
819
825
|
this.protocolV2LastRuntimeProbeFeatures = undefined;
|
|
820
826
|
const currentDeviceType = this.device.getCurrentDeviceType();
|
|
821
827
|
const capabilityDeviceType =
|
|
@@ -1382,7 +1388,11 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1382
1388
|
app_v1: features
|
|
1383
1389
|
? getDeviceFirmwareVersion(features).join('.')
|
|
1384
1390
|
: deviceInfo?.main_mcu?.application?.version,
|
|
1385
|
-
|
|
1391
|
+
// Production boot verifies P2 against P1 before normal firmware can run.
|
|
1392
|
+
app_v2:
|
|
1393
|
+
features?.mode === 'normal'
|
|
1394
|
+
? getDeviceFirmwareVersion(features).join('.')
|
|
1395
|
+
: deviceInfo?.main_mcu?.application_data?.version,
|
|
1386
1396
|
coprocessor: features ? getDeviceBLEFirmwareVersion(features).join('.') : undefined,
|
|
1387
1397
|
se01: features?.se01Version ?? undefined,
|
|
1388
1398
|
se02: features?.se02Version ?? undefined,
|
|
@@ -1511,10 +1521,15 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1511
1521
|
}
|
|
1512
1522
|
this.protocolV2ExpectedSerialNumber = serialNumber;
|
|
1513
1523
|
this.protocolV2ExpectedPath = path;
|
|
1524
|
+
this.protocolV2InitialDeviceInfo = deviceInfo;
|
|
1514
1525
|
}
|
|
1515
1526
|
|
|
1516
|
-
private async verifyProtocolV2ReconnectIdentity() {
|
|
1517
|
-
const deviceInfo = await
|
|
1527
|
+
private async verifyProtocolV2ReconnectIdentity(request?: DeviceInfoGet) {
|
|
1528
|
+
const deviceInfo = await requestProtocolV2DeviceInfo({
|
|
1529
|
+
commands: this.device.getCommands(),
|
|
1530
|
+
timeoutMs: this.getProtocolV2DeviceInfoTimeout(),
|
|
1531
|
+
request,
|
|
1532
|
+
});
|
|
1518
1533
|
this.assertProtocolV2DeviceInfoIdentity(deviceInfo);
|
|
1519
1534
|
return deviceInfo;
|
|
1520
1535
|
}
|
|
@@ -2505,7 +2520,10 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2505
2520
|
return Array.from(expectedTargetIds).filter(targetId => !reportedTargetIds.has(targetId));
|
|
2506
2521
|
}
|
|
2507
2522
|
|
|
2508
|
-
private getProtocolV2ObservableTargetVersions(
|
|
2523
|
+
private getProtocolV2ObservableTargetVersions(
|
|
2524
|
+
features: Features,
|
|
2525
|
+
deviceInfo?: ProtocolV2DeviceInfo
|
|
2526
|
+
) {
|
|
2509
2527
|
const versions = new Map<number, string>();
|
|
2510
2528
|
versions.set(
|
|
2511
2529
|
ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER,
|
|
@@ -2513,7 +2531,16 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2513
2531
|
);
|
|
2514
2532
|
const applicationVersion = getDeviceFirmwareVersion(features).join('.');
|
|
2515
2533
|
versions.set(ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1, applicationVersion);
|
|
2516
|
-
|
|
2534
|
+
const applicationP2Version =
|
|
2535
|
+
features.mode === 'normal'
|
|
2536
|
+
? applicationVersion
|
|
2537
|
+
: deviceInfo?.main_mcu?.application_data?.version;
|
|
2538
|
+
if (applicationP2Version) {
|
|
2539
|
+
versions.set(
|
|
2540
|
+
ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P2,
|
|
2541
|
+
applicationP2Version
|
|
2542
|
+
);
|
|
2543
|
+
}
|
|
2517
2544
|
versions.set(
|
|
2518
2545
|
ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_COPROCESSOR,
|
|
2519
2546
|
getDeviceBLEFirmwareVersion(features).join('.')
|
|
@@ -2530,20 +2557,41 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2530
2557
|
return versions;
|
|
2531
2558
|
}
|
|
2532
2559
|
|
|
2533
|
-
private hasProtocolV2InstallVersionChanged(
|
|
2560
|
+
private hasProtocolV2InstallVersionChanged(
|
|
2561
|
+
expectedTargetIds: Set<number>,
|
|
2562
|
+
deviceInfo: ProtocolV2DeviceInfo
|
|
2563
|
+
) {
|
|
2534
2564
|
if (!this.protocolV2LastRuntimeProbeFeatures) return false;
|
|
2535
2565
|
const currentVersions = this.getProtocolV2ObservableTargetVersions(
|
|
2536
|
-
this.protocolV2LastRuntimeProbeFeatures
|
|
2566
|
+
this.protocolV2LastRuntimeProbeFeatures,
|
|
2567
|
+
deviceInfo
|
|
2568
|
+
);
|
|
2569
|
+
const p1TargetId = ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1;
|
|
2570
|
+
const p2TargetId = ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P2;
|
|
2571
|
+
return (
|
|
2572
|
+
expectedTargetIds.size > 0 &&
|
|
2573
|
+
Array.from(expectedTargetIds).every(targetId => {
|
|
2574
|
+
if (targetId === p2TargetId && this.protocolV2LastRuntimeProbeFeatures?.mode === 'normal') {
|
|
2575
|
+
// P1 must change in this install; a pre-existing P1 version cannot prove a P2-only update.
|
|
2576
|
+
if (!expectedTargetIds.has(p1TargetId)) return false;
|
|
2577
|
+
const previousP1Version = this.protocolV2InstallBaselineVersions.get(p1TargetId);
|
|
2578
|
+
const currentP1Version = currentVersions.get(p1TargetId);
|
|
2579
|
+
return (
|
|
2580
|
+
previousP1Version !== undefined &&
|
|
2581
|
+
currentP1Version !== undefined &&
|
|
2582
|
+
previousP1Version !== currentP1Version &&
|
|
2583
|
+
currentP1Version === this.params?.expectedTargetVersions?.app_v2
|
|
2584
|
+
);
|
|
2585
|
+
}
|
|
2586
|
+
const previousVersion = this.protocolV2InstallBaselineVersions.get(targetId);
|
|
2587
|
+
const currentVersion = currentVersions.get(targetId);
|
|
2588
|
+
return (
|
|
2589
|
+
previousVersion !== undefined &&
|
|
2590
|
+
currentVersion !== undefined &&
|
|
2591
|
+
previousVersion !== currentVersion
|
|
2592
|
+
);
|
|
2593
|
+
})
|
|
2537
2594
|
);
|
|
2538
|
-
return Array.from(expectedTargetIds).some(targetId => {
|
|
2539
|
-
const previousVersion = this.protocolV2InstallBaselineVersions.get(targetId);
|
|
2540
|
-
const currentVersion = currentVersions.get(targetId);
|
|
2541
|
-
return (
|
|
2542
|
-
previousVersion !== undefined &&
|
|
2543
|
-
currentVersion !== undefined &&
|
|
2544
|
-
previousVersion !== currentVersion
|
|
2545
|
-
);
|
|
2546
|
-
});
|
|
2547
2595
|
}
|
|
2548
2596
|
|
|
2549
2597
|
private recordProtocolV2AuthoritativeInstallCompletion(expectedTargetIds: Set<number>) {
|
|
@@ -2588,10 +2636,12 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2588
2636
|
finishedStatusSnapshotPolls = 0;
|
|
2589
2637
|
};
|
|
2590
2638
|
|
|
2591
|
-
|
|
2639
|
+
for (;;) {
|
|
2592
2640
|
// A transport release caused by an explicit workflow cancellation must not
|
|
2593
2641
|
// be mistaken for the expected device reboot during installation.
|
|
2594
2642
|
this.throwIfAborted();
|
|
2643
|
+
// Read once more at the deadline so a completed reboot is not reported as a timeout.
|
|
2644
|
+
const isFinalStatusCheck = Date.now() - startTime >= PROTOCOL_V2_INSTALL_TIMEOUT;
|
|
2595
2645
|
try {
|
|
2596
2646
|
if (shouldReconnect) {
|
|
2597
2647
|
const isBleInstallReconnect = this.isBleReconnect();
|
|
@@ -2750,22 +2800,19 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2750
2800
|
|
|
2751
2801
|
if (
|
|
2752
2802
|
statusTargets.length === 0 &&
|
|
2753
|
-
|
|
2754
|
-
bleInstallLinkReady &&
|
|
2755
|
-
installEvidenceObserved
|
|
2803
|
+
(currentDeviceInfo || bleInstallLinkReady || isFinalStatusCheck)
|
|
2756
2804
|
) {
|
|
2757
|
-
//
|
|
2758
|
-
//
|
|
2759
|
-
currentDeviceInfo = await this.verifyProtocolV2ReconnectIdentity(
|
|
2805
|
+
// Installation can finish while BLE is disconnected, hiding all progress.
|
|
2806
|
+
// Refresh identity and target versions even without observed install status.
|
|
2807
|
+
currentDeviceInfo = await this.verifyProtocolV2ReconnectIdentity(
|
|
2808
|
+
PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST
|
|
2809
|
+
);
|
|
2760
2810
|
deviceInfo = currentDeviceInfo;
|
|
2761
|
-
}
|
|
2762
|
-
|
|
2763
|
-
if (statusTargets.length === 0 && currentDeviceInfo) {
|
|
2764
2811
|
const isNormalMode = await this.probeProtocolV2NormalMode(currentDeviceInfo);
|
|
2765
2812
|
if (
|
|
2766
2813
|
isNormalMode &&
|
|
2767
2814
|
(installEvidenceObserved ||
|
|
2768
|
-
this.hasProtocolV2InstallVersionChanged(expectedTargetIds))
|
|
2815
|
+
this.hasProtocolV2InstallVersionChanged(expectedTargetIds, currentDeviceInfo))
|
|
2769
2816
|
) {
|
|
2770
2817
|
Log.log(
|
|
2771
2818
|
'[FirmwareUpdateV4] empty firmware status after confirmed App reboot; update complete'
|
|
@@ -2795,33 +2842,29 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2795
2842
|
}
|
|
2796
2843
|
}
|
|
2797
2844
|
} catch (error) {
|
|
2798
|
-
if (
|
|
2845
|
+
if (
|
|
2846
|
+
this.isProtocolV2ReconnectIdentityError(error) ||
|
|
2847
|
+
isProtocolV2TerminalInstallStatusError(error)
|
|
2848
|
+
) {
|
|
2799
2849
|
throw error;
|
|
2800
2850
|
}
|
|
2801
|
-
//
|
|
2802
|
-
//
|
|
2851
|
+
// Some firmware builds do not expose install status. Require a fresh runtime
|
|
2852
|
+
// probe and current-install evidence or changed versions before accepting completion.
|
|
2803
2853
|
if (isProtocolV2FirmwareStatusEndpointUnavailable(error)) {
|
|
2804
|
-
if (!currentDeviceInfo) {
|
|
2805
|
-
if (!bleInstallLinkReady) {
|
|
2806
|
-
throw ERRORS.TypedError(
|
|
2807
|
-
HardwareErrorCode.RuntimeError,
|
|
2808
|
-
'Protocol V2 device identity is unavailable during install polling'
|
|
2809
|
-
);
|
|
2810
|
-
}
|
|
2811
|
-
deviceInfo = await this.verifyProtocolV2ReconnectIdentity();
|
|
2812
|
-
}
|
|
2813
|
-
const reconnectDeviceInfo = currentDeviceInfo ?? deviceInfo;
|
|
2814
|
-
if (!reconnectDeviceInfo) {
|
|
2854
|
+
if (!currentDeviceInfo && !bleInstallLinkReady && !isFinalStatusCheck) {
|
|
2815
2855
|
throw ERRORS.TypedError(
|
|
2816
2856
|
HardwareErrorCode.RuntimeError,
|
|
2817
2857
|
'Protocol V2 device identity is unavailable during install polling'
|
|
2818
2858
|
);
|
|
2819
2859
|
}
|
|
2820
|
-
|
|
2860
|
+
deviceInfo = await this.verifyProtocolV2ReconnectIdentity(
|
|
2861
|
+
PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST
|
|
2862
|
+
);
|
|
2863
|
+
const isNormalMode = await this.probeProtocolV2NormalMode(deviceInfo);
|
|
2821
2864
|
if (
|
|
2822
2865
|
isNormalMode &&
|
|
2823
2866
|
(installEvidenceObserved ||
|
|
2824
|
-
this.hasProtocolV2InstallVersionChanged(expectedTargetIds))
|
|
2867
|
+
this.hasProtocolV2InstallVersionChanged(expectedTargetIds, deviceInfo))
|
|
2825
2868
|
) {
|
|
2826
2869
|
Log.log(
|
|
2827
2870
|
'[FirmwareUpdateV4] firmware status endpoint unavailable after confirmed App reboot'
|
|
@@ -2871,6 +2914,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2871
2914
|
bleInstallLinkReady = false;
|
|
2872
2915
|
Log.log('Protocol V2 firmware install device readiness probe failed: ', error);
|
|
2873
2916
|
}
|
|
2917
|
+
if (isFinalStatusCheck) break;
|
|
2874
2918
|
await wait(1000);
|
|
2875
2919
|
}
|
|
2876
2920
|
|
|
@@ -2909,7 +2953,8 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2909
2953
|
private async probeProtocolV2NormalMode(deviceInfo: ProtocolV2DeviceInfo) {
|
|
2910
2954
|
const features = await this.device.probeProtocolV2RuntimeState(
|
|
2911
2955
|
deviceInfo,
|
|
2912
|
-
PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT
|
|
2956
|
+
PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
|
|
2957
|
+
{ forceRuntimeContextRefresh: true }
|
|
2913
2958
|
);
|
|
2914
2959
|
this.protocolV2LastRuntimeProbeFeatures = features;
|
|
2915
2960
|
return this.isProtocolV2ApplicationMode(features);
|
|
@@ -3132,6 +3177,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
3132
3177
|
progress: number | null
|
|
3133
3178
|
): Promise<TypedResponseMessage<'FilesystemFile'>> {
|
|
3134
3179
|
const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
|
|
3180
|
+
const env = DataManager.getSettings('env');
|
|
3135
3181
|
const writeRes = await typedCall(
|
|
3136
3182
|
'FilesystemFileWrite',
|
|
3137
3183
|
'FilesystemFile',
|
|
@@ -3147,6 +3193,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
3147
3193
|
ui_percentage: progress ?? undefined,
|
|
3148
3194
|
},
|
|
3149
3195
|
{
|
|
3196
|
+
timeoutMs: env === 'react-native' ? PROTOCOL_V2_FILE_WRITE_RESPONSE_TIMEOUT : undefined,
|
|
3150
3197
|
writeWithResponse: false,
|
|
3151
3198
|
onWriteCompleted: () => undefined,
|
|
3152
3199
|
}
|
|
@@ -271,7 +271,8 @@ export const mapProtocolV2DeviceInfoToState = (
|
|
|
271
271
|
versions: definedEntries({
|
|
272
272
|
firmware: imageVersion(info.main_mcu?.application),
|
|
273
273
|
applicationP1: imageVersion(info.main_mcu?.application),
|
|
274
|
-
|
|
274
|
+
// The running application does not report P2; discard a loader-era version.
|
|
275
|
+
applicationP2: mode === 'normal' ? null : imageVersion(info.main_mcu?.application_data),
|
|
275
276
|
bootloader: imageVersion(info.main_mcu?.bootloader),
|
|
276
277
|
board: imageVersion(info.main_mcu?.romloader),
|
|
277
278
|
ble: imageVersion(info.coprocessor?.application),
|
|
@@ -289,8 +290,9 @@ export const mapProtocolV2DeviceInfoToState = (
|
|
|
289
290
|
firmwareHash: imageHash(info.main_mcu?.application),
|
|
290
291
|
applicationP1BuildId: imageBuildId(info.main_mcu?.application),
|
|
291
292
|
applicationP1Hash: imageHash(info.main_mcu?.application),
|
|
292
|
-
applicationP2BuildId:
|
|
293
|
-
|
|
293
|
+
applicationP2BuildId:
|
|
294
|
+
mode === 'normal' ? null : imageBuildId(info.main_mcu?.application_data),
|
|
295
|
+
applicationP2Hash: mode === 'normal' ? null : imageHash(info.main_mcu?.application_data),
|
|
294
296
|
bootloaderBuildId: imageBuildId(info.main_mcu?.bootloader),
|
|
295
297
|
bootloaderHash: imageHash(info.main_mcu?.bootloader),
|
|
296
298
|
boardBuildId: imageBuildId(info.main_mcu?.romloader),
|