@onekeyfe/hd-core 1.2.0-alpha.107 → 1.2.0-alpha.108
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/DeviceCommands.test.ts +140 -26
- package/__tests__/base64Data.test.ts +70 -0
- package/__tests__/device-lifecycle-events.test.ts +113 -2
- package/__tests__/device-pool-state.test.ts +25 -0
- package/__tests__/deviceSettings.test.ts +0 -1
- package/__tests__/deviceUploadNft.test.ts +33 -4
- package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +178 -0
- package/__tests__/get-device-state.test.ts +52 -13
- package/__tests__/logBlockEvent.test.ts +13 -1
- package/__tests__/protocol-v2.test.ts +762 -124
- package/__tests__/refresh-device-state.test.ts +3 -1
- package/__tests__/resourceBase64Boundary.test.ts +48 -0
- package/__tests__/ton-sign-message.test.ts +84 -0
- package/dist/api/FirmwareUpdateV4.d.ts +10 -3
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/UploadPortfolio.d.ts +1 -1
- package/dist/api/UploadPortfolio.d.ts.map +1 -1
- package/dist/api/helpers/base64Data.d.ts +16 -0
- package/dist/api/helpers/base64Data.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts +2 -3
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts +2 -3
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
- package/dist/api/ton/TonSignMessage.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/Device.d.ts +7 -2
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/device/DevicePool.d.ts.map +1 -1
- package/dist/events/logBlockEvent.d.ts.map +1 -1
- package/dist/index.d.ts +11 -14
- package/dist/index.js +455 -200
- package/dist/protocols/protocol-v2/features.d.ts +9 -1
- package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/index.d.ts +2 -2
- package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +1 -1
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/utils/pro2Nft.d.ts +7 -0
- package/dist/utils/pro2Nft.d.ts.map +1 -1
- package/package.json +6 -4
- package/src/api/FirmwareUpdateV4.ts +326 -174
- package/src/api/UploadPortfolio.ts +9 -2
- package/src/api/helpers/base64Data.ts +85 -0
- package/src/api/protocol-v2/DeviceUploadNft.ts +56 -8
- package/src/api/protocol-v2/DeviceUploadWallpaper.ts +37 -18
- package/src/api/ton/TonSignMessage.ts +5 -3
- package/src/core/index.ts +6 -2
- package/src/device/Device.ts +53 -31
- package/src/device/DeviceCommands.ts +4 -14
- package/src/device/DevicePool.ts +6 -2
- package/src/events/logBlockEvent.ts +14 -1
- package/src/protocols/protocol-v2/features.ts +19 -2
- package/src/protocols/protocol-v2/index.ts +2 -0
- package/src/types/api/protocolV2.ts +1 -1
- package/src/utils/deviceSettings.ts +1 -1
- package/src/utils/pro2Nft.ts +31 -8
|
@@ -28,6 +28,7 @@ import { DevicePool } from '../device/DevicePool';
|
|
|
28
28
|
import {
|
|
29
29
|
PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST,
|
|
30
30
|
ProtocolV2FirmwareTargetType,
|
|
31
|
+
isLegacyProtocolV2ProtocolInfo,
|
|
31
32
|
} from '../protocols/protocol-v2';
|
|
32
33
|
import { requestProtocolV2DeviceInfo } from '../protocols/protocol-v2/features';
|
|
33
34
|
import {
|
|
@@ -243,15 +244,7 @@ type JSZipSizedEntry = JSZip.JSZipObject & {
|
|
|
243
244
|
};
|
|
244
245
|
};
|
|
245
246
|
|
|
246
|
-
type
|
|
247
|
-
| 'resource-sync'
|
|
248
|
-
| 'bootloader-install'
|
|
249
|
-
| 'bootloader-verify'
|
|
250
|
-
| 'component-install'
|
|
251
|
-
| 'final-verify';
|
|
252
|
-
|
|
253
|
-
type ProtocolV2ExecutionPhase = {
|
|
254
|
-
kind: ProtocolV2ExecutionPhaseKind;
|
|
247
|
+
type ProtocolV2TransferBatch = {
|
|
255
248
|
installSources: ProtocolV2InstallSource[];
|
|
256
249
|
resourceSources: ProtocolV2ResourceBundleSource[];
|
|
257
250
|
};
|
|
@@ -384,6 +377,31 @@ const isProtocolV2ReconnectProbeError = (error: unknown) => {
|
|
|
384
377
|
);
|
|
385
378
|
};
|
|
386
379
|
|
|
380
|
+
const PROTOCOL_V2_BLE_INSTALL_INTERRUPTION_ERROR_CODES = new Set<number>([
|
|
381
|
+
HardwareErrorCode.BleConnectedError,
|
|
382
|
+
HardwareErrorCode.BleCharacteristicNotifyError,
|
|
383
|
+
HardwareErrorCode.BleForceCleanRunPromise,
|
|
384
|
+
HardwareErrorCode.BleDeviceDisconnected,
|
|
385
|
+
]);
|
|
386
|
+
|
|
387
|
+
const isProtocolV2BleInstallInterruptionError = (error: unknown) => {
|
|
388
|
+
if (
|
|
389
|
+
error instanceof HardwareError &&
|
|
390
|
+
PROTOCOL_V2_BLE_INSTALL_INTERRUPTION_ERROR_CODES.has(error.errorCode)
|
|
391
|
+
) {
|
|
392
|
+
return true;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const message = getProtocolV2UnknownErrorText(error).toLowerCase();
|
|
396
|
+
const compactMessage = message.replace(/\s+/gu, '');
|
|
397
|
+
return (
|
|
398
|
+
/react native ble transport (?:released|disconnected)/u.test(message) ||
|
|
399
|
+
(compactMessage.includes('rxerrorerror6') &&
|
|
400
|
+
(compactMessage.includes('multiplatformbleadapter') ||
|
|
401
|
+
compactMessage.includes('multipalformebleadapter')))
|
|
402
|
+
);
|
|
403
|
+
};
|
|
404
|
+
|
|
387
405
|
const isProtocolV2FirmwareStatusEndpointUnavailable = (error: unknown) => {
|
|
388
406
|
const message = getProtocolV2UnknownErrorText(error).toLowerCase();
|
|
389
407
|
return (
|
|
@@ -393,6 +411,15 @@ const isProtocolV2FirmwareStatusEndpointUnavailable = (error: unknown) => {
|
|
|
393
411
|
);
|
|
394
412
|
};
|
|
395
413
|
|
|
414
|
+
const isProtocolV2FirmwareUpdateEndpointUnavailable = (error: unknown) => {
|
|
415
|
+
const message = getProtocolV2UnknownErrorText(error).toLowerCase();
|
|
416
|
+
return (
|
|
417
|
+
message.includes('handler not registered') ||
|
|
418
|
+
message.includes('message handler not found') ||
|
|
419
|
+
message.includes('unsupported message')
|
|
420
|
+
);
|
|
421
|
+
};
|
|
422
|
+
|
|
396
423
|
const isProtocolV2TerminalInstallStatusError = (error: unknown) =>
|
|
397
424
|
error instanceof HardwareError &&
|
|
398
425
|
(error.errorCode === HardwareErrorCode.FirmwareError ||
|
|
@@ -587,6 +614,8 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
587
614
|
|
|
588
615
|
private protocolV2ExecutionInLoader = false;
|
|
589
616
|
|
|
617
|
+
private protocolV2LegacyDirectUpdate = false;
|
|
618
|
+
|
|
590
619
|
private protocolV2BootResourceStagingSafe = false;
|
|
591
620
|
|
|
592
621
|
private protocolV2CompletedTargetVersions = new Map<number, number>();
|
|
@@ -595,6 +624,12 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
595
624
|
|
|
596
625
|
private protocolV2FinalStatusVerified = false;
|
|
597
626
|
|
|
627
|
+
private protocolV2InstallAckReceived = false;
|
|
628
|
+
|
|
629
|
+
private protocolV2InstallBaselineVersions = new Map<number, string>();
|
|
630
|
+
|
|
631
|
+
private protocolV2LastRuntimeProbeFeatures?: Features;
|
|
632
|
+
|
|
598
633
|
init() {
|
|
599
634
|
this.allowDeviceMode = [UI_REQUEST.BOOTLOADER, UI_REQUEST.NOT_INITIALIZE];
|
|
600
635
|
this.requireDeviceMode = [];
|
|
@@ -813,6 +848,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
813
848
|
private async runProtocolV2() {
|
|
814
849
|
await this.captureProtocolV2PhysicalIdentity();
|
|
815
850
|
const deviceFeatures = await this.getProtocolV2DeviceFeatures();
|
|
851
|
+
this.protocolV2InstallBaselineVersions =
|
|
852
|
+
this.getProtocolV2ObservableTargetVersions(deviceFeatures);
|
|
853
|
+
this.protocolV2LastRuntimeProbeFeatures = undefined;
|
|
816
854
|
const currentDeviceType = this.device.getCurrentDeviceType();
|
|
817
855
|
const capabilityDeviceType =
|
|
818
856
|
currentDeviceType === EDeviceType.Pro2 || currentDeviceType === EDeviceType.Neo
|
|
@@ -826,6 +864,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
826
864
|
const deviceFirmwareType = getFirmwareType(deviceFeatures);
|
|
827
865
|
const firmwareType = this.params.firmwareType ?? deviceFirmwareType;
|
|
828
866
|
this.validateExpectedTargetVersions();
|
|
867
|
+
const wantsResources = !!this.params.targetsToUpdate?.includes('resource');
|
|
829
868
|
|
|
830
869
|
if (
|
|
831
870
|
!this.params.preparedPlan &&
|
|
@@ -849,11 +888,11 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
849
888
|
if (this.params.preparedPlan || hasPreparedComponentArtifacts) {
|
|
850
889
|
return this.runProtocolV2PreparedArtifacts(deviceFeatures, firmwareType);
|
|
851
890
|
}
|
|
852
|
-
const wantsResources = !!this.params.targetsToUpdate?.includes('resource');
|
|
853
891
|
|
|
854
892
|
let fwBinaryMap: ProtocolV2TargetBinary[] = [];
|
|
855
893
|
let bootloaderBinary: ArrayBuffer | null = null;
|
|
856
894
|
let installItems: ProtocolV2InstallItem[] | undefined;
|
|
895
|
+
let resourceMemoryHost: FirmwareUpdateV4MemoryHost | undefined;
|
|
857
896
|
try {
|
|
858
897
|
this.postTipMessage(FirmwareUpdateTipMessage.StartDownloadFirmware);
|
|
859
898
|
fwBinaryMap = this.collectExplicitTargetBinaries();
|
|
@@ -866,17 +905,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
866
905
|
const needsRemoteFirmware = this.params.targetsToUpdate?.length
|
|
867
906
|
? missingFirmwareTargets.length > 0
|
|
868
907
|
: explicitInstallItems.length === 0;
|
|
869
|
-
|
|
870
|
-
throw ERRORS.TypedError(
|
|
871
|
-
HardwareErrorCode.RuntimeError,
|
|
872
|
-
'Protocol V2 resource archive must be provided through a local or external PreparedPlan',
|
|
873
|
-
{
|
|
874
|
-
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
875
|
-
}
|
|
876
|
-
);
|
|
877
|
-
}
|
|
908
|
+
const needsSdkManagedArtifacts = needsRemoteFirmware || wantsResources;
|
|
878
909
|
if (
|
|
879
|
-
|
|
910
|
+
needsSdkManagedArtifacts &&
|
|
880
911
|
(this.params.artifactReader ||
|
|
881
912
|
DataManager.getSettings('firmwareManifestMode') === 'external-only')
|
|
882
913
|
) {
|
|
@@ -888,9 +919,13 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
888
919
|
}
|
|
889
920
|
);
|
|
890
921
|
}
|
|
891
|
-
if (
|
|
922
|
+
if (needsSdkManagedArtifacts) {
|
|
892
923
|
// Remote updates must use a freshly fetched config before any reboot or file write.
|
|
893
|
-
await DataManager.forceReloadData(
|
|
924
|
+
await DataManager.forceReloadData({
|
|
925
|
+
requireResources: wantsResources,
|
|
926
|
+
resourceDeviceType:
|
|
927
|
+
capabilityDeviceType === EDeviceType.Neo ? EDeviceType.Neo : EDeviceType.Pro2,
|
|
928
|
+
});
|
|
894
929
|
}
|
|
895
930
|
if (needsRemoteFirmware) {
|
|
896
931
|
const remoteBinaries = await this.prepareRemoteProtocolV2Binaries(
|
|
@@ -913,8 +948,19 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
913
948
|
targetId: item.targetId,
|
|
914
949
|
}));
|
|
915
950
|
}
|
|
951
|
+
if (wantsResources) {
|
|
952
|
+
this.params.resourceArchiveBinary = await this.downloadRemoteProtocolV2ResourceArchive(
|
|
953
|
+
deviceFeatures
|
|
954
|
+
);
|
|
955
|
+
resourceMemoryHost = await this.prepareProtocolV2LocalMemoryHost({
|
|
956
|
+
features: deviceFeatures,
|
|
957
|
+
firmwareType,
|
|
958
|
+
availableInstallItems: installItems ?? explicitInstallItems,
|
|
959
|
+
});
|
|
960
|
+
}
|
|
916
961
|
this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
917
962
|
} catch (err) {
|
|
963
|
+
resourceMemoryHost?.release();
|
|
918
964
|
if (
|
|
919
965
|
typeof err === 'object' &&
|
|
920
966
|
err !== null &&
|
|
@@ -929,6 +975,14 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
929
975
|
throw normalizeFirmwarePreparationError(err);
|
|
930
976
|
}
|
|
931
977
|
|
|
978
|
+
if (resourceMemoryHost) {
|
|
979
|
+
try {
|
|
980
|
+
return await this.runProtocolV2PreparedArtifacts(deviceFeatures, firmwareType, false);
|
|
981
|
+
} finally {
|
|
982
|
+
resourceMemoryHost.release();
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
|
|
932
986
|
if (!bootloaderBinary && fwBinaryMap.length === 0 && !installItems?.length) {
|
|
933
987
|
throw ERRORS.TypedError(
|
|
934
988
|
HardwareErrorCode.FirmwareUpdateDownloadFailed,
|
|
@@ -1082,14 +1136,15 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1082
1136
|
private async prepareProtocolV2LocalMemoryHost({
|
|
1083
1137
|
features,
|
|
1084
1138
|
firmwareType,
|
|
1139
|
+
availableInstallItems = this.buildProtocolV2InstallItems({
|
|
1140
|
+
bootloaderBinary: this.prepareBootloaderBinary(),
|
|
1141
|
+
fwBinaryMap: this.collectExplicitTargetBinaries(),
|
|
1142
|
+
}),
|
|
1085
1143
|
}: {
|
|
1086
1144
|
features: Features;
|
|
1087
1145
|
firmwareType: EFirmwareType;
|
|
1146
|
+
availableInstallItems?: ProtocolV2InstallItem[];
|
|
1088
1147
|
}): Promise<FirmwareUpdateV4MemoryHost> {
|
|
1089
|
-
const availableInstallItems = this.buildProtocolV2InstallItems({
|
|
1090
|
-
bootloaderBinary: this.prepareBootloaderBinary(),
|
|
1091
|
-
fwBinaryMap: this.collectExplicitTargetBinaries(),
|
|
1092
|
-
});
|
|
1093
1148
|
const requestedComponentTargets = new Set(
|
|
1094
1149
|
(this.params.targetsToUpdate ?? []).filter(
|
|
1095
1150
|
(target): target is Exclude<FirmwareUpdateV4Target, 'resource' | 'boot_resources'> =>
|
|
@@ -1517,9 +1572,15 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1517
1572
|
return sources;
|
|
1518
1573
|
}
|
|
1519
1574
|
|
|
1520
|
-
private async runProtocolV2PreparedArtifacts(
|
|
1575
|
+
private async runProtocolV2PreparedArtifacts(
|
|
1576
|
+
features: Features,
|
|
1577
|
+
firmwareType: EFirmwareType,
|
|
1578
|
+
announceDownload = true
|
|
1579
|
+
) {
|
|
1521
1580
|
try {
|
|
1522
|
-
|
|
1581
|
+
if (announceDownload) {
|
|
1582
|
+
this.postTipMessage(FirmwareUpdateTipMessage.StartDownloadFirmware);
|
|
1583
|
+
}
|
|
1523
1584
|
const installSources = await this.prepareProtocolV2InstallSources(firmwareType, features);
|
|
1524
1585
|
const resourceSources = await this.prepareProtocolV2ResourceSources();
|
|
1525
1586
|
if (installSources.length === 0 && resourceSources.length === 0) {
|
|
@@ -1528,7 +1589,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1528
1589
|
'No firmware to update'
|
|
1529
1590
|
);
|
|
1530
1591
|
}
|
|
1531
|
-
|
|
1592
|
+
if (announceDownload) {
|
|
1593
|
+
this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
1594
|
+
}
|
|
1532
1595
|
|
|
1533
1596
|
return await this.executeProtocolV2SourceUpdate({
|
|
1534
1597
|
installSources,
|
|
@@ -1882,6 +1945,44 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1882
1945
|
};
|
|
1883
1946
|
}
|
|
1884
1947
|
|
|
1948
|
+
private async downloadRemoteProtocolV2ResourceArchive(features: Features): Promise<ArrayBuffer> {
|
|
1949
|
+
const deviceType = getDeviceType(features);
|
|
1950
|
+
if (deviceType !== EDeviceType.Pro2 && deviceType !== EDeviceType.Neo) {
|
|
1951
|
+
throw ERRORS.TypedError(
|
|
1952
|
+
HardwareErrorCode.RuntimeError,
|
|
1953
|
+
'Protocol V2 resource archive requires a Pro2 or Neo device'
|
|
1954
|
+
);
|
|
1955
|
+
}
|
|
1956
|
+
const source = DataManager.getProtocolV2ResourceSource(deviceType);
|
|
1957
|
+
const expectedSha256 = normalizeProtocolV2Hex(source?.archiveSha256);
|
|
1958
|
+
if (
|
|
1959
|
+
!source?.archiveUrl ||
|
|
1960
|
+
!Number.isSafeInteger(source.archiveSize) ||
|
|
1961
|
+
source.archiveSize <= 0 ||
|
|
1962
|
+
source.archiveSize > PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES ||
|
|
1963
|
+
!expectedSha256 ||
|
|
1964
|
+
!/^[0-9a-f]{64}$/u.test(expectedSha256)
|
|
1965
|
+
) {
|
|
1966
|
+
throw ERRORS.TypedError(
|
|
1967
|
+
HardwareErrorCode.RuntimeError,
|
|
1968
|
+
'Protocol V2 resource archive integrity metadata is invalid',
|
|
1969
|
+
{ firmwareUpdateCode: 'FirmwarePlanInvalid' }
|
|
1970
|
+
);
|
|
1971
|
+
}
|
|
1972
|
+
const { binary } = await getSysResourceBinary(source.archiveUrl);
|
|
1973
|
+
if (
|
|
1974
|
+
binary.byteLength !== source.archiveSize ||
|
|
1975
|
+
bytesToHex(sha256(new Uint8Array(binary))) !== expectedSha256
|
|
1976
|
+
) {
|
|
1977
|
+
throw ERRORS.TypedError(
|
|
1978
|
+
HardwareErrorCode.RuntimeError,
|
|
1979
|
+
'Protocol V2 resource archive does not match the remote config',
|
|
1980
|
+
{ firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch' }
|
|
1981
|
+
);
|
|
1982
|
+
}
|
|
1983
|
+
return binary;
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1885
1986
|
private async prepareRemoteProtocolV2Binaries(
|
|
1886
1987
|
firmwareType: EFirmwareType,
|
|
1887
1988
|
features: Features,
|
|
@@ -2067,21 +2168,12 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2067
2168
|
return this.device.features?.mode === 'romloader';
|
|
2068
2169
|
}
|
|
2069
2170
|
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
Log.debug('Protocol V2 device is in romloader mode; start firmware update directly');
|
|
2075
|
-
this.protocolV2ExecutionInLoader = true;
|
|
2076
|
-
return false;
|
|
2077
|
-
}
|
|
2078
|
-
if (this.isProtocolV2BootloaderMode()) {
|
|
2079
|
-
Log.debug('Protocol V2 device is already in bootloader mode, skip reboot');
|
|
2080
|
-
this.protocolV2ExecutionInLoader = true;
|
|
2081
|
-
this.postTipMessage(FirmwareUpdateTipMessage.GoToBootloaderSuccess);
|
|
2082
|
-
return false;
|
|
2083
|
-
}
|
|
2171
|
+
private isLegacyProtocolV2Runtime() {
|
|
2172
|
+
const protocolInfo = this.device.state?.raw?.protocolV2ProtocolInfo;
|
|
2173
|
+
return protocolInfo ? isLegacyProtocolV2ProtocolInfo(protocolInfo) : false;
|
|
2174
|
+
}
|
|
2084
2175
|
|
|
2176
|
+
private async rebootProtocolV2ToBootloader() {
|
|
2085
2177
|
try {
|
|
2086
2178
|
this.postTipMessage(FirmwareUpdateTipMessage.AutoRebootToBootloader);
|
|
2087
2179
|
await this.protocolV2Reboot(DeviceRebootType.Bootloader);
|
|
@@ -2099,6 +2191,27 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2099
2191
|
}
|
|
2100
2192
|
}
|
|
2101
2193
|
|
|
2194
|
+
async enterProtocolV2BootloaderMode() {
|
|
2195
|
+
this.protocolV2LegacyDirectUpdate = false;
|
|
2196
|
+
// romloader is the first update environment and forwards targets to bootloader.
|
|
2197
|
+
// It rejects DeviceRebootType.Bootloader, so reuse the current connection.
|
|
2198
|
+
if (this.isProtocolV2RomloaderMode()) {
|
|
2199
|
+
Log.debug('Protocol V2 device is in romloader mode; start firmware update directly');
|
|
2200
|
+
this.protocolV2LegacyDirectUpdate = this.isLegacyProtocolV2Runtime();
|
|
2201
|
+
this.protocolV2ExecutionInLoader = true;
|
|
2202
|
+
return false;
|
|
2203
|
+
}
|
|
2204
|
+
if (this.isProtocolV2BootloaderMode()) {
|
|
2205
|
+
Log.debug('Protocol V2 device is already in bootloader mode, skip reboot');
|
|
2206
|
+
this.protocolV2LegacyDirectUpdate = this.isLegacyProtocolV2Runtime();
|
|
2207
|
+
this.protocolV2ExecutionInLoader = true;
|
|
2208
|
+
this.postTipMessage(FirmwareUpdateTipMessage.GoToBootloaderSuccess);
|
|
2209
|
+
return false;
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
return this.rebootProtocolV2ToBootloader();
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2102
2215
|
private async waitForProtocolV2BootloaderMode(
|
|
2103
2216
|
timeout = PROTOCOL_V2_BOOTLOADER_RECONNECT_TIMEOUT,
|
|
2104
2217
|
retryInterval = 1000
|
|
@@ -2183,68 +2296,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2183
2296
|
return entries;
|
|
2184
2297
|
}
|
|
2185
2298
|
|
|
2186
|
-
private
|
|
2187
|
-
installSources,
|
|
2188
|
-
resourceSources,
|
|
2189
|
-
}: {
|
|
2190
|
-
installSources: ProtocolV2InstallSource[];
|
|
2191
|
-
resourceSources: ProtocolV2ResourceBundleSource[];
|
|
2192
|
-
}): ProtocolV2ExecutionPhase[] {
|
|
2193
|
-
const phases: ProtocolV2ExecutionPhase[] = [];
|
|
2194
|
-
const bootResourceSources = resourceSources.filter(source =>
|
|
2195
|
-
isProtocolV2BootResourcePackagePath(source.devicePath)
|
|
2196
|
-
);
|
|
2197
|
-
const remainingResourceSources = resourceSources.filter(
|
|
2198
|
-
source => !isProtocolV2BootResourcePackagePath(source.devicePath)
|
|
2199
|
-
);
|
|
2200
|
-
// A previous interrupted run may have left a promotable staging file. Replace
|
|
2201
|
-
// it with the current approved package before any firmware-triggered reboot.
|
|
2202
|
-
if (bootResourceSources.length > 0) {
|
|
2203
|
-
phases.push({
|
|
2204
|
-
kind: 'resource-sync',
|
|
2205
|
-
installSources: [],
|
|
2206
|
-
resourceSources: bootResourceSources,
|
|
2207
|
-
});
|
|
2208
|
-
}
|
|
2209
|
-
const bootloaderSources = installSources.filter(source => source.kind === 'bootloader');
|
|
2210
|
-
if (bootloaderSources.length > 0) {
|
|
2211
|
-
phases.push(
|
|
2212
|
-
{
|
|
2213
|
-
kind: 'bootloader-install',
|
|
2214
|
-
installSources: bootloaderSources,
|
|
2215
|
-
resourceSources: [],
|
|
2216
|
-
},
|
|
2217
|
-
{
|
|
2218
|
-
kind: 'bootloader-verify',
|
|
2219
|
-
installSources: [],
|
|
2220
|
-
resourceSources: [],
|
|
2221
|
-
}
|
|
2222
|
-
);
|
|
2223
|
-
}
|
|
2224
|
-
if (remainingResourceSources.length > 0) {
|
|
2225
|
-
phases.push({
|
|
2226
|
-
kind: 'resource-sync',
|
|
2227
|
-
installSources: [],
|
|
2228
|
-
resourceSources: remainingResourceSources,
|
|
2229
|
-
});
|
|
2230
|
-
}
|
|
2231
|
-
const componentSources = installSources.filter(source => source.kind !== 'bootloader');
|
|
2232
|
-
if (componentSources.length > 0) {
|
|
2233
|
-
phases.push({
|
|
2234
|
-
kind: 'component-install',
|
|
2235
|
-
installSources: componentSources,
|
|
2236
|
-
resourceSources: [],
|
|
2237
|
-
});
|
|
2238
|
-
}
|
|
2239
|
-
phases.push({
|
|
2240
|
-
kind: 'final-verify',
|
|
2241
|
-
installSources: [],
|
|
2242
|
-
resourceSources: [],
|
|
2243
|
-
});
|
|
2244
|
-
return phases;
|
|
2245
|
-
}
|
|
2246
|
-
|
|
2247
|
-
private async executeProtocolV2Phases({
|
|
2299
|
+
private async executeProtocolV2SourceUpdate({
|
|
2248
2300
|
installSources,
|
|
2249
2301
|
resourceSources,
|
|
2250
2302
|
}: {
|
|
@@ -2252,38 +2304,22 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2252
2304
|
resourceSources: ProtocolV2ResourceBundleSource[];
|
|
2253
2305
|
}) {
|
|
2254
2306
|
this.protocolV2BootResourceStagingSafe = false;
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
) {
|
|
2270
|
-
await this.ensureProtocolV2BootResourceStagingIsEmpty();
|
|
2271
|
-
}
|
|
2272
|
-
await this.executeProtocolV2TransferPhase(phase);
|
|
2273
|
-
} else if (phase.kind === 'bootloader-install' || phase.kind === 'component-install') {
|
|
2274
|
-
await this.enterProtocolV2BootloaderMode();
|
|
2275
|
-
await this.ensureProtocolV2BootResourceStagingIsEmpty();
|
|
2276
|
-
await this.executeProtocolV2TransferPhase(phase);
|
|
2277
|
-
await this.exitProtocolV2BootloaderToNormal();
|
|
2278
|
-
} else if (phase.kind === 'bootloader-verify') {
|
|
2279
|
-
await this.waitForProtocolV2FinalFeatures();
|
|
2280
|
-
this.assertExpectedProtocolV2Versions(['boot']);
|
|
2281
|
-
}
|
|
2307
|
+
if (installSources.length > 0 || resourceSources.length > 0) {
|
|
2308
|
+
await this.enterProtocolV2BootloaderMode();
|
|
2309
|
+
// Clear stale boot-resource staging before writing any artifacts. The new
|
|
2310
|
+
// boot resource, resources and firmware then share one transfer session,
|
|
2311
|
+
// one global progress range and one multi-target install request.
|
|
2312
|
+
// Preserve the approved source order instead of synthesizing a boot-first
|
|
2313
|
+
// phase. Loader firmware owns the cross-stage handoff: bootloader runs its
|
|
2314
|
+
// component targets and leaves boot pending for romloader, while pending
|
|
2315
|
+
// runner-backed records always resume before the application boots.
|
|
2316
|
+
await this.ensureProtocolV2BootResourceStagingIsEmpty();
|
|
2317
|
+
await this.executeProtocolV2TransferPhase({
|
|
2318
|
+
installSources,
|
|
2319
|
+
resourceSources,
|
|
2320
|
+
});
|
|
2282
2321
|
}
|
|
2283
|
-
|
|
2284
|
-
HardwareErrorCode.RuntimeError,
|
|
2285
|
-
'Protocol V2 execution has no final verification phase'
|
|
2286
|
-
);
|
|
2322
|
+
return this.completeProtocolV2FinalVerification();
|
|
2287
2323
|
}
|
|
2288
2324
|
|
|
2289
2325
|
private async ensureProtocolV2BootResourceStagingIsEmpty() {
|
|
@@ -2315,19 +2351,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2315
2351
|
this.protocolV2BootResourceStagingSafe = true;
|
|
2316
2352
|
}
|
|
2317
2353
|
|
|
2318
|
-
private async executeProtocolV2SourceUpdate({
|
|
2319
|
-
installSources,
|
|
2320
|
-
resourceSources,
|
|
2321
|
-
}: {
|
|
2322
|
-
installSources: ProtocolV2InstallSource[];
|
|
2323
|
-
resourceSources: ProtocolV2ResourceBundleSource[];
|
|
2324
|
-
}) {
|
|
2325
|
-
return this.executeProtocolV2Phases({
|
|
2326
|
-
installSources,
|
|
2327
|
-
resourceSources,
|
|
2328
|
-
});
|
|
2329
|
-
}
|
|
2330
|
-
|
|
2331
2354
|
private async executeProtocolV2Update({
|
|
2332
2355
|
fwBinaryMap,
|
|
2333
2356
|
bootloaderBinary,
|
|
@@ -2352,7 +2375,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2352
2375
|
kind: item.kind,
|
|
2353
2376
|
}))
|
|
2354
2377
|
);
|
|
2355
|
-
return await this.
|
|
2378
|
+
return await this.executeProtocolV2SourceUpdate({
|
|
2356
2379
|
installSources,
|
|
2357
2380
|
resourceSources: [],
|
|
2358
2381
|
});
|
|
@@ -2364,7 +2387,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2364
2387
|
private async executeProtocolV2TransferPhase({
|
|
2365
2388
|
installSources,
|
|
2366
2389
|
resourceSources,
|
|
2367
|
-
}:
|
|
2390
|
+
}: ProtocolV2TransferBatch) {
|
|
2368
2391
|
let totalSize = installSources.reduce((total, item) => total + item.source.size, 0);
|
|
2369
2392
|
const resourcesToSync: ProtocolV2ResourceBundleSource[] = [];
|
|
2370
2393
|
for (const resource of resourceSources) {
|
|
@@ -2637,6 +2660,47 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2637
2660
|
return Array.from(expectedTargetIds).filter(targetId => !reportedTargetIds.has(targetId));
|
|
2638
2661
|
}
|
|
2639
2662
|
|
|
2663
|
+
private getProtocolV2ObservableTargetVersions(features: Features) {
|
|
2664
|
+
const versions = new Map<number, string>();
|
|
2665
|
+
versions.set(
|
|
2666
|
+
ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER,
|
|
2667
|
+
getDeviceBootloaderVersion(features).join('.')
|
|
2668
|
+
);
|
|
2669
|
+
const applicationVersion = getDeviceFirmwareVersion(features).join('.');
|
|
2670
|
+
versions.set(ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1, applicationVersion);
|
|
2671
|
+
versions.set(ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P2, applicationVersion);
|
|
2672
|
+
versions.set(
|
|
2673
|
+
ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_COPROCESSOR,
|
|
2674
|
+
getDeviceBLEFirmwareVersion(features).join('.')
|
|
2675
|
+
);
|
|
2676
|
+
const secureElementVersions: Array<[number, string | null | undefined]> = [
|
|
2677
|
+
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE01, features.se01Version],
|
|
2678
|
+
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE02, features.se02Version],
|
|
2679
|
+
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE03, features.se03Version],
|
|
2680
|
+
[ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE04, features.se04Version],
|
|
2681
|
+
];
|
|
2682
|
+
secureElementVersions.forEach(([targetId, version]) => {
|
|
2683
|
+
if (version) versions.set(targetId, version);
|
|
2684
|
+
});
|
|
2685
|
+
return versions;
|
|
2686
|
+
}
|
|
2687
|
+
|
|
2688
|
+
private hasProtocolV2InstallVersionChanged(expectedTargetIds: Set<number>) {
|
|
2689
|
+
if (!this.protocolV2LastRuntimeProbeFeatures) return false;
|
|
2690
|
+
const currentVersions = this.getProtocolV2ObservableTargetVersions(
|
|
2691
|
+
this.protocolV2LastRuntimeProbeFeatures
|
|
2692
|
+
);
|
|
2693
|
+
return Array.from(expectedTargetIds).some(targetId => {
|
|
2694
|
+
const previousVersion = this.protocolV2InstallBaselineVersions.get(targetId);
|
|
2695
|
+
const currentVersion = currentVersions.get(targetId);
|
|
2696
|
+
return (
|
|
2697
|
+
previousVersion !== undefined &&
|
|
2698
|
+
currentVersion !== undefined &&
|
|
2699
|
+
previousVersion !== currentVersion
|
|
2700
|
+
);
|
|
2701
|
+
});
|
|
2702
|
+
}
|
|
2703
|
+
|
|
2640
2704
|
private async waitForProtocolV2FirmwareUpdateComplete(
|
|
2641
2705
|
targets: Array<{ target_id: number; path: string }>
|
|
2642
2706
|
) {
|
|
@@ -2649,12 +2713,17 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2649
2713
|
let deviceInfo: ProtocolV2DeviceInfo | undefined;
|
|
2650
2714
|
let missingTargetStatusSince: number | undefined;
|
|
2651
2715
|
let missingTargetStatusKey: string | undefined;
|
|
2716
|
+
let normalModeWithoutInstallEvidenceSince: number | undefined;
|
|
2717
|
+
let installEvidenceObserved = this.protocolV2InstallAckReceived;
|
|
2652
2718
|
const resetMissingTargetStatusGrace = () => {
|
|
2653
2719
|
missingTargetStatusSince = undefined;
|
|
2654
2720
|
missingTargetStatusKey = undefined;
|
|
2655
2721
|
};
|
|
2656
2722
|
|
|
2657
2723
|
while (Date.now() - startTime < PROTOCOL_V2_INSTALL_TIMEOUT) {
|
|
2724
|
+
// A transport release caused by an explicit workflow cancellation must not
|
|
2725
|
+
// be mistaken for the expected device reboot during installation.
|
|
2726
|
+
this.throwIfAborted();
|
|
2658
2727
|
try {
|
|
2659
2728
|
if (shouldReconnect) {
|
|
2660
2729
|
await this.reconnectProtocolV2Device();
|
|
@@ -2677,21 +2746,33 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2677
2746
|
);
|
|
2678
2747
|
const statusTargets = (statusResponse.message.records ??
|
|
2679
2748
|
[]) as ProtocolV2FirmwareUpdateStatusTarget[];
|
|
2749
|
+
if (
|
|
2750
|
+
statusTargets.some(target => {
|
|
2751
|
+
const targetId = normalizeProtocolV2TargetId(target.target_id);
|
|
2752
|
+
return targetId !== undefined && expectedTargetIds.has(targetId);
|
|
2753
|
+
})
|
|
2754
|
+
) {
|
|
2755
|
+
installEvidenceObserved = true;
|
|
2756
|
+
normalModeWithoutInstallEvidenceSince = undefined;
|
|
2757
|
+
}
|
|
2680
2758
|
if (this.assertProtocolV2TargetStatus(statusTargets, expectedTargetIds, expectedPaths)) {
|
|
2681
2759
|
this.protocolV2FinalStatusVerified = true;
|
|
2682
2760
|
return;
|
|
2683
2761
|
}
|
|
2684
2762
|
|
|
2685
|
-
if (
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2763
|
+
if (statusTargets.length === 0 && currentDeviceInfo) {
|
|
2764
|
+
const isNormalMode = await this.probeProtocolV2NormalMode(currentDeviceInfo);
|
|
2765
|
+
if (
|
|
2766
|
+
isNormalMode &&
|
|
2767
|
+
(installEvidenceObserved ||
|
|
2768
|
+
this.hasProtocolV2InstallVersionChanged(expectedTargetIds))
|
|
2769
|
+
) {
|
|
2770
|
+
Log.log(
|
|
2771
|
+
'[FirmwareUpdateV4] empty firmware status after confirmed App reboot; update complete'
|
|
2772
|
+
);
|
|
2773
|
+
this.postProgressMessage(100, 'installingFirmware');
|
|
2774
|
+
return;
|
|
2775
|
+
}
|
|
2695
2776
|
}
|
|
2696
2777
|
|
|
2697
2778
|
const missingTargetIds = this.getProtocolV2MissingTargetIds(
|
|
@@ -2741,16 +2822,39 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2741
2822
|
'Protocol V2 device identity is unavailable during install polling'
|
|
2742
2823
|
);
|
|
2743
2824
|
}
|
|
2744
|
-
|
|
2825
|
+
const isNormalMode = await this.probeProtocolV2NormalMode(currentDeviceInfo);
|
|
2826
|
+
if (
|
|
2827
|
+
isNormalMode &&
|
|
2828
|
+
(installEvidenceObserved ||
|
|
2829
|
+
this.hasProtocolV2InstallVersionChanged(expectedTargetIds))
|
|
2830
|
+
) {
|
|
2745
2831
|
Log.log(
|
|
2746
2832
|
'[FirmwareUpdateV4] firmware status endpoint unavailable after confirmed App reboot'
|
|
2747
2833
|
);
|
|
2748
2834
|
this.postProgressMessage(100, 'installingFirmware');
|
|
2749
2835
|
return;
|
|
2750
2836
|
}
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2837
|
+
if (isNormalMode) {
|
|
2838
|
+
const now = Date.now();
|
|
2839
|
+
normalModeWithoutInstallEvidenceSince ??= now;
|
|
2840
|
+
if (
|
|
2841
|
+
now - normalModeWithoutInstallEvidenceSince >=
|
|
2842
|
+
PROTOCOL_V2_MISSING_TARGET_STATUS_GRACE_TIMEOUT
|
|
2843
|
+
) {
|
|
2844
|
+
throw ERRORS.TypedError(
|
|
2845
|
+
HardwareErrorCode.FirmwareError,
|
|
2846
|
+
'Protocol V2 device returned to normal mode without install ACK, target status, or version change'
|
|
2847
|
+
);
|
|
2848
|
+
}
|
|
2849
|
+
lastError = new Error(
|
|
2850
|
+
'Protocol V2 device is in normal mode but installation is not yet confirmed'
|
|
2851
|
+
);
|
|
2852
|
+
} else {
|
|
2853
|
+
normalModeWithoutInstallEvidenceSince = undefined;
|
|
2854
|
+
lastError = new Error(
|
|
2855
|
+
'Protocol V2 firmware status endpoint is unavailable while the device remains in loader mode'
|
|
2856
|
+
);
|
|
2857
|
+
}
|
|
2754
2858
|
} else {
|
|
2755
2859
|
shouldReconnect = true;
|
|
2756
2860
|
deviceInfo = undefined;
|
|
@@ -2811,6 +2915,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2811
2915
|
deviceInfo,
|
|
2812
2916
|
PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT
|
|
2813
2917
|
);
|
|
2918
|
+
this.protocolV2LastRuntimeProbeFeatures = features;
|
|
2814
2919
|
return features.mode === 'normal' && !features.bootloaderMode;
|
|
2815
2920
|
}
|
|
2816
2921
|
|
|
@@ -3043,15 +3148,62 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
3043
3148
|
}: {
|
|
3044
3149
|
targets: Array<{ target_id: number; path: string }>;
|
|
3045
3150
|
}) {
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3151
|
+
this.protocolV2InstallAckReceived = false;
|
|
3152
|
+
this.protocolV2LastRuntimeProbeFeatures = undefined;
|
|
3153
|
+
const startUpdate = () =>
|
|
3154
|
+
this.device
|
|
3155
|
+
.getCommands()
|
|
3156
|
+
.typedCall(
|
|
3157
|
+
'DeviceFirmwareUpdateRequest',
|
|
3158
|
+
'Success',
|
|
3159
|
+
{ targets },
|
|
3160
|
+
{ timeoutMs: PROTOCOL_V2_START_UPDATE_TIMEOUT }
|
|
3161
|
+
);
|
|
3162
|
+
let response: ProtocolV2FirmwareUpdateStartResponse | undefined;
|
|
3163
|
+
try {
|
|
3164
|
+
response = await startUpdate();
|
|
3165
|
+
this.protocolV2InstallAckReceived = true;
|
|
3166
|
+
} catch (error) {
|
|
3167
|
+
if (this.isBleReconnect() && isProtocolV2BleInstallInterruptionError(error)) {
|
|
3168
|
+
this.throwIfAborted();
|
|
3169
|
+
// Installation can reboot the device before the Success response reaches
|
|
3170
|
+
// the host. The request has side effects, so do not replay it; reconnect
|
|
3171
|
+
// and let status polling determine whether installation was accepted.
|
|
3172
|
+
Log.log(
|
|
3173
|
+
'[FirmwareUpdateV4] install request interrupted by device reboot; continue status polling: ',
|
|
3174
|
+
error
|
|
3175
|
+
);
|
|
3176
|
+
} else if (
|
|
3177
|
+
this.protocolV2LegacyDirectUpdate &&
|
|
3178
|
+
isProtocolV2FirmwareUpdateEndpointUnavailable(error)
|
|
3179
|
+
) {
|
|
3180
|
+
// Both legacy loaders register this request. A missing handler therefore identifies
|
|
3181
|
+
// a legacy App before dispatch, so it is safe to reboot and retry the unhandled request.
|
|
3182
|
+
this.protocolV2LegacyDirectUpdate = false;
|
|
3183
|
+
Log.debug(
|
|
3184
|
+
'[FirmwareUpdateV4] legacy App does not expose DeviceFirmwareUpdateRequest; rebooting to bootloader'
|
|
3185
|
+
);
|
|
3186
|
+
await this.rebootProtocolV2ToBootloader();
|
|
3187
|
+
try {
|
|
3188
|
+
response = await startUpdate();
|
|
3189
|
+
this.protocolV2InstallAckReceived = true;
|
|
3190
|
+
} catch (retryError) {
|
|
3191
|
+
if (!(this.isBleReconnect() && isProtocolV2BleInstallInterruptionError(retryError))) {
|
|
3192
|
+
throw retryError;
|
|
3193
|
+
}
|
|
3194
|
+
this.throwIfAborted();
|
|
3195
|
+
Log.log(
|
|
3196
|
+
'[FirmwareUpdateV4] install request interrupted after legacy reboot; continue status polling: ',
|
|
3197
|
+
retryError
|
|
3198
|
+
);
|
|
3199
|
+
}
|
|
3200
|
+
} else {
|
|
3201
|
+
throw error;
|
|
3202
|
+
}
|
|
3203
|
+
}
|
|
3204
|
+
this.protocolV2LegacyDirectUpdate = false;
|
|
3205
|
+
// A Success ACK or an install-time disconnect both end confirmation. In the
|
|
3206
|
+
// latter case only status polling may establish the final outcome.
|
|
3055
3207
|
this.postTipMessage(FirmwareUpdateTipMessage.FirmwareUpdating);
|
|
3056
3208
|
this.postProgressMessage(0, 'installingFirmware');
|
|
3057
3209
|
return response;
|