@onekeyfe/hd-core 1.2.8-alpha.115 → 1.2.8-alpha.117
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.
|
@@ -8884,6 +8884,154 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
8884
8884
|
expect((method as any).protocolV2SourceUpdateProcess).not.toHaveBeenCalled();
|
|
8885
8885
|
});
|
|
8886
8886
|
|
|
8887
|
+
describe('RESC header check link recovery', () => {
|
|
8888
|
+
const currentHeader = {
|
|
8889
|
+
version: [1, 0, 0],
|
|
8890
|
+
payloadHash: '11'.repeat(64),
|
|
8891
|
+
headerHash: '22'.repeat(64),
|
|
8892
|
+
};
|
|
8893
|
+
const linkLost = () =>
|
|
8894
|
+
ERRORS.TypedError(
|
|
8895
|
+
HardwareErrorCode.RuntimeError,
|
|
8896
|
+
'Device protocol has not been detected for E2:92:2E:16:47:B9'
|
|
8897
|
+
);
|
|
8898
|
+
const createResource = (name: string) => ({
|
|
8899
|
+
name,
|
|
8900
|
+
source: { size: 3, readAt: jest.fn(), close: jest.fn() },
|
|
8901
|
+
devicePath: `vol0:/bundles/${name}`,
|
|
8902
|
+
version: [1, 0, 0],
|
|
8903
|
+
payloadHash: '11'.repeat(64),
|
|
8904
|
+
headerHash: '22'.repeat(64),
|
|
8905
|
+
});
|
|
8906
|
+
const createMethod = () => {
|
|
8907
|
+
const method = new FirmwareUpdateV4({
|
|
8908
|
+
id: 1,
|
|
8909
|
+
payload: {
|
|
8910
|
+
method: 'firmwareUpdateV4',
|
|
8911
|
+
},
|
|
8912
|
+
});
|
|
8913
|
+
method.postTipMessage = jest.fn();
|
|
8914
|
+
method.postProgressMessage = jest.fn();
|
|
8915
|
+
(method as any).protocolV2SourceUpdateProcess = jest.fn(
|
|
8916
|
+
({ processedSize }: { processedSize: number }) => Promise.resolve(processedSize + 3)
|
|
8917
|
+
);
|
|
8918
|
+
(method as any).verifyProtocolV2StagedFile = jest.fn().mockResolvedValue(undefined);
|
|
8919
|
+
(method as any).recoverProtocolV2FileTransfer = jest.fn().mockResolvedValue(undefined);
|
|
8920
|
+
return method;
|
|
8921
|
+
};
|
|
8922
|
+
|
|
8923
|
+
test('reconnects and asks again when the link drops during a header check', async () => {
|
|
8924
|
+
const method = createMethod();
|
|
8925
|
+
(method as any).readProtocolV2DeviceFileHeader = jest
|
|
8926
|
+
.fn()
|
|
8927
|
+
.mockRejectedValueOnce(linkLost())
|
|
8928
|
+
.mockResolvedValue(currentHeader);
|
|
8929
|
+
|
|
8930
|
+
await (method as any).executeProtocolV2TransferPhase({
|
|
8931
|
+
installSources: [],
|
|
8932
|
+
resourceSources: [createResource('images.okpkg'), createResource('fonts.okpkg')],
|
|
8933
|
+
});
|
|
8934
|
+
|
|
8935
|
+
expect((method as any).recoverProtocolV2FileTransfer).toHaveBeenCalledTimes(1);
|
|
8936
|
+
expect((method as any).readProtocolV2DeviceFileHeader).toHaveBeenCalledTimes(3);
|
|
8937
|
+
expect((method as any).protocolV2SourceUpdateProcess).not.toHaveBeenCalled();
|
|
8938
|
+
});
|
|
8939
|
+
|
|
8940
|
+
test.each([
|
|
8941
|
+
['BLE disconnect', () => ERRORS.TypedError(HardwareErrorCode.BleDeviceDisconnected)],
|
|
8942
|
+
['BLE timeout', () => ERRORS.TypedError(HardwareErrorCode.BleTimeoutError)],
|
|
8943
|
+
[
|
|
8944
|
+
'invalidated Protocol V2 link',
|
|
8945
|
+
() =>
|
|
8946
|
+
Object.assign(new Error('Protocol V2 link generation changed'), {
|
|
8947
|
+
name: 'ProtocolV2LinkError',
|
|
8948
|
+
code: 'generation',
|
|
8949
|
+
}),
|
|
8950
|
+
],
|
|
8951
|
+
['released transport', () => new Error('React Native BLE transport released')],
|
|
8952
|
+
['cleaned Electron BLE state', () => new Error('Electron BLE device state cleaned')],
|
|
8953
|
+
[
|
|
8954
|
+
'Electron BLE device that is no longer connected',
|
|
8955
|
+
() => ERRORS.TypedError(HardwareErrorCode.TransportNotFound, 'Device AA:BB not connected'),
|
|
8956
|
+
],
|
|
8957
|
+
])('rethrows a %s from the header check for recovery', async (_label, createError) => {
|
|
8958
|
+
const method = createMethod();
|
|
8959
|
+
(method as any).readProtocolV2DeviceFileHeader = jest.fn().mockRejectedValue(createError());
|
|
8960
|
+
|
|
8961
|
+
await expect(
|
|
8962
|
+
(method as any).isProtocolV2ResourceBundleUpToDate(createResource('images.okpkg'))
|
|
8963
|
+
).rejects.toBeDefined();
|
|
8964
|
+
});
|
|
8965
|
+
|
|
8966
|
+
test('treats a device-side header read failure as stale without reconnecting', async () => {
|
|
8967
|
+
const method = createMethod();
|
|
8968
|
+
(method as any).readProtocolV2DeviceFileHeader = jest
|
|
8969
|
+
.fn()
|
|
8970
|
+
.mockRejectedValue(ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'Failure: busy'));
|
|
8971
|
+
|
|
8972
|
+
await (method as any).executeProtocolV2TransferPhase({
|
|
8973
|
+
installSources: [],
|
|
8974
|
+
resourceSources: [createResource('images.okpkg')],
|
|
8975
|
+
});
|
|
8976
|
+
|
|
8977
|
+
expect((method as any).recoverProtocolV2FileTransfer).not.toHaveBeenCalled();
|
|
8978
|
+
expect((method as any).protocolV2SourceUpdateProcess).toHaveBeenCalledTimes(1);
|
|
8979
|
+
});
|
|
8980
|
+
|
|
8981
|
+
test('limits header-check reconnects per transfer phase', async () => {
|
|
8982
|
+
const method = createMethod();
|
|
8983
|
+
(method as any).readProtocolV2DeviceFileHeader = jest.fn().mockRejectedValue(linkLost());
|
|
8984
|
+
|
|
8985
|
+
await (method as any).executeProtocolV2TransferPhase({
|
|
8986
|
+
installSources: [],
|
|
8987
|
+
resourceSources: ['a.okpkg', 'b.okpkg', 'c.okpkg'].map(createResource),
|
|
8988
|
+
});
|
|
8989
|
+
|
|
8990
|
+
expect((method as any).recoverProtocolV2FileTransfer).toHaveBeenCalledTimes(2);
|
|
8991
|
+
expect((method as any).protocolV2SourceUpdateProcess).toHaveBeenCalledTimes(3);
|
|
8992
|
+
});
|
|
8993
|
+
|
|
8994
|
+
test('resends without further reconnects when the header-check reconnect fails', async () => {
|
|
8995
|
+
const method = createMethod();
|
|
8996
|
+
(method as any).readProtocolV2DeviceFileHeader = jest.fn().mockRejectedValue(linkLost());
|
|
8997
|
+
(method as any).recoverProtocolV2FileTransfer = jest
|
|
8998
|
+
.fn()
|
|
8999
|
+
.mockRejectedValue(ERRORS.TypedError(HardwareErrorCode.DeviceNotFound));
|
|
9000
|
+
|
|
9001
|
+
await (method as any).executeProtocolV2TransferPhase({
|
|
9002
|
+
installSources: [],
|
|
9003
|
+
resourceSources: [createResource('images.okpkg'), createResource('fonts.okpkg')],
|
|
9004
|
+
});
|
|
9005
|
+
|
|
9006
|
+
expect((method as any).recoverProtocolV2FileTransfer).toHaveBeenCalledTimes(1);
|
|
9007
|
+
expect((method as any).protocolV2SourceUpdateProcess).toHaveBeenCalledTimes(2);
|
|
9008
|
+
});
|
|
9009
|
+
|
|
9010
|
+
test.each(['mismatch: expected A, received B', 'unavailable: expected path A'])(
|
|
9011
|
+
'aborts instead of resending when the reconnected device identity is %s',
|
|
9012
|
+
async identityFailure => {
|
|
9013
|
+
const method = createMethod();
|
|
9014
|
+
(method as any).readProtocolV2DeviceFileHeader = jest.fn().mockRejectedValue(linkLost());
|
|
9015
|
+
(method as any).recoverProtocolV2FileTransfer = jest
|
|
9016
|
+
.fn()
|
|
9017
|
+
.mockRejectedValue(
|
|
9018
|
+
ERRORS.TypedError(
|
|
9019
|
+
HardwareErrorCode.DeviceNotFound,
|
|
9020
|
+
`Protocol V2 reconnect physical identity ${identityFailure}`
|
|
9021
|
+
)
|
|
9022
|
+
);
|
|
9023
|
+
|
|
9024
|
+
await expect(
|
|
9025
|
+
(method as any).executeProtocolV2TransferPhase({
|
|
9026
|
+
installSources: [],
|
|
9027
|
+
resourceSources: [createResource('images.okpkg')],
|
|
9028
|
+
})
|
|
9029
|
+
).rejects.toMatchObject({ errorCode: HardwareErrorCode.DeviceNotFound });
|
|
9030
|
+
expect((method as any).protocolV2SourceUpdateProcess).not.toHaveBeenCalled();
|
|
9031
|
+
}
|
|
9032
|
+
);
|
|
9033
|
+
});
|
|
9034
|
+
|
|
8887
9035
|
test.each([
|
|
8888
9036
|
['1.0.3', '1.0.3', true],
|
|
8889
9037
|
['1.0.10', '1.0.3', true],
|
|
@@ -69,6 +69,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
69
69
|
private getProtocolV2ResourceComparePath;
|
|
70
70
|
private readProtocolV2DeviceFileHeader;
|
|
71
71
|
private isProtocolV2ResourceBundleUpToDate;
|
|
72
|
+
private isProtocolV2ResourceBundleUpToDateWithRecovery;
|
|
72
73
|
private isProtocolV2BootloaderMode;
|
|
73
74
|
private isProtocolV2RomloaderMode;
|
|
74
75
|
private rebootProtocolV2ToBootloader;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FirmwareUpdateV4.d.ts","sourceRoot":"","sources":["../../src/api/FirmwareUpdateV4.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAkD,MAAM,qBAAqB,CAAC;AA4BlG,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":"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"}
|
package/dist/index.js
CHANGED
|
@@ -52634,6 +52634,7 @@ const PROTOCOL_V2_CONNECT_POLL_INTERVAL = 500;
|
|
|
52634
52634
|
const PROTOCOL_V2_CONNECT_SINGLE_TIMEOUT = 75 * 1000;
|
|
52635
52635
|
const PROTOCOL_V2_DEVICE_INFO_READY_TIMEOUT = 30 * 1000;
|
|
52636
52636
|
const PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT = 3;
|
|
52637
|
+
const PROTOCOL_V2_RESOURCE_HEADER_RECOVERY_LIMIT = 2;
|
|
52637
52638
|
const PROTOCOL_V2_BLE_FILE_WRITE_WINDOW_MIN_BOOTLOADER_VERSION = '1.0.2';
|
|
52638
52639
|
const PROTOCOL_V2_TRANSFER_PROGRESS_HEARTBEAT_MS = 1000;
|
|
52639
52640
|
const PROTOCOL_V2_INSTALL_STATUS_CONFLICT_CODE = 'FirmwareInstallStatusConflict';
|
|
@@ -52796,6 +52797,21 @@ const getProtocolV2ErrorCode = (error) => {
|
|
|
52796
52797
|
const causeCode = errorCode !== null && errorCode !== void 0 ? errorCode : code;
|
|
52797
52798
|
return typeof causeCode === 'number' || typeof causeCode === 'string' ? causeCode : undefined;
|
|
52798
52799
|
};
|
|
52800
|
+
const isProtocolV2ResourceHeaderLinkError = (error) => {
|
|
52801
|
+
if (isProtocolV2DeviceDisconnectedError(error))
|
|
52802
|
+
return true;
|
|
52803
|
+
const errorCode = getProtocolV2ErrorCode(error);
|
|
52804
|
+
if (errorCode === hdShared.HardwareErrorCode.BleTimeoutError)
|
|
52805
|
+
return true;
|
|
52806
|
+
const message = getProtocolV2UnknownErrorText(error);
|
|
52807
|
+
if (errorCode === hdShared.HardwareErrorCode.TransportNotFound && /not connected/i.test(message)) {
|
|
52808
|
+
return true;
|
|
52809
|
+
}
|
|
52810
|
+
return (message.includes('ProtocolV2LinkError') ||
|
|
52811
|
+
message.includes('Device protocol has not been detected') ||
|
|
52812
|
+
message.includes('Electron BLE device state cleaned') ||
|
|
52813
|
+
message.includes('Electron BLE transport released'));
|
|
52814
|
+
};
|
|
52799
52815
|
const isProtocolV2TerminalInstallStatusError = (error) => {
|
|
52800
52816
|
var _a;
|
|
52801
52817
|
return error instanceof hdShared.HardwareError &&
|
|
@@ -53899,11 +53915,44 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
53899
53915
|
return true;
|
|
53900
53916
|
}
|
|
53901
53917
|
catch (error) {
|
|
53918
|
+
if (isProtocolV2ResourceHeaderLinkError(error))
|
|
53919
|
+
throw error;
|
|
53902
53920
|
Log$6.log(`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check failed: `, error);
|
|
53903
53921
|
return false;
|
|
53904
53922
|
}
|
|
53905
53923
|
});
|
|
53906
53924
|
}
|
|
53925
|
+
isProtocolV2ResourceBundleUpToDateWithRecovery(bundle, recovery) {
|
|
53926
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
53927
|
+
for (;;) {
|
|
53928
|
+
try {
|
|
53929
|
+
return yield this.isProtocolV2ResourceBundleUpToDate(bundle);
|
|
53930
|
+
}
|
|
53931
|
+
catch (error) {
|
|
53932
|
+
this.throwIfAborted();
|
|
53933
|
+
if (recovery.remaining <= 0) {
|
|
53934
|
+
Log$6.log(`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check failed: `, error);
|
|
53935
|
+
return false;
|
|
53936
|
+
}
|
|
53937
|
+
recovery.remaining -= 1;
|
|
53938
|
+
Log$6.log(`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check lost the link; reconnecting: `, getProtocolV2UnknownErrorText(error));
|
|
53939
|
+
try {
|
|
53940
|
+
yield this.recoverProtocolV2FileTransfer();
|
|
53941
|
+
}
|
|
53942
|
+
catch (recoverError) {
|
|
53943
|
+
this.throwIfAborted();
|
|
53944
|
+
if (this.normalizeErrorMessage(recoverError).includes('Protocol V2 reconnect physical identity')) {
|
|
53945
|
+
throw recoverError;
|
|
53946
|
+
}
|
|
53947
|
+
recovery.remaining = 0;
|
|
53948
|
+
Log$6.log('[FirmwareUpdateV4] RESC header check reconnect failed: ', recoverError);
|
|
53949
|
+
return false;
|
|
53950
|
+
}
|
|
53951
|
+
this.throwIfAborted();
|
|
53952
|
+
}
|
|
53953
|
+
}
|
|
53954
|
+
});
|
|
53955
|
+
}
|
|
53907
53956
|
isProtocolV2BootloaderMode() {
|
|
53908
53957
|
var _a, _b, _c;
|
|
53909
53958
|
if (typeof this.device.isBootloader === 'function') {
|
|
@@ -54082,8 +54131,9 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
|
|
|
54082
54131
|
return __awaiter(this, void 0, void 0, function* () {
|
|
54083
54132
|
let totalSize = installSources.reduce((total, item) => total + item.source.size, 0);
|
|
54084
54133
|
const resourcesToSync = [];
|
|
54134
|
+
const headerRecovery = { remaining: PROTOCOL_V2_RESOURCE_HEADER_RECOVERY_LIMIT };
|
|
54085
54135
|
for (const resource of resourceSources) {
|
|
54086
|
-
if (yield this.
|
|
54136
|
+
if (yield this.isProtocolV2ResourceBundleUpToDateWithRecovery(resource, headerRecovery)) {
|
|
54087
54137
|
Log$6.log(`[FirmwareUpdateV4] skip RESC bundle ${resource.name}; already up to date`);
|
|
54088
54138
|
}
|
|
54089
54139
|
else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-core",
|
|
3
|
-
"version": "1.2.8-alpha.
|
|
3
|
+
"version": "1.2.8-alpha.117",
|
|
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.8-alpha.
|
|
29
|
-
"@onekeyfe/hd-transport": "1.2.8-alpha.
|
|
28
|
+
"@onekeyfe/hd-shared": "1.2.8-alpha.117",
|
|
29
|
+
"@onekeyfe/hd-transport": "1.2.8-alpha.117",
|
|
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": "610f1a979c682817acf9b4d195365dc5ed806138"
|
|
50
50
|
}
|
|
@@ -103,6 +103,7 @@ const PROTOCOL_V2_CONNECT_POLL_INTERVAL = 500;
|
|
|
103
103
|
const PROTOCOL_V2_CONNECT_SINGLE_TIMEOUT = 75 * 1000;
|
|
104
104
|
const PROTOCOL_V2_DEVICE_INFO_READY_TIMEOUT = 30 * 1000;
|
|
105
105
|
const PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT = 3;
|
|
106
|
+
const PROTOCOL_V2_RESOURCE_HEADER_RECOVERY_LIMIT = 2;
|
|
106
107
|
// Bootloaders with the 2048-byte BLE UART FIFO have no room beside one waiting FileWrite
|
|
107
108
|
// frame, so a second in-flight write can overflow it and lose a chunk. Firmware has not yet
|
|
108
109
|
// confirmed which bootloader version ships the 4096-byte FIFO, nor that the BLE coprocessor
|
|
@@ -404,6 +405,23 @@ const getProtocolV2ErrorCode = (error: unknown): number | string | undefined =>
|
|
|
404
405
|
return typeof causeCode === 'number' || typeof causeCode === 'string' ? causeCode : undefined;
|
|
405
406
|
};
|
|
406
407
|
|
|
408
|
+
/** Errors that mean the link is gone, so the read says nothing about the installed bundle. */
|
|
409
|
+
const isProtocolV2ResourceHeaderLinkError = (error: unknown) => {
|
|
410
|
+
if (isProtocolV2DeviceDisconnectedError(error)) return true;
|
|
411
|
+
const errorCode = getProtocolV2ErrorCode(error);
|
|
412
|
+
if (errorCode === HardwareErrorCode.BleTimeoutError) return true;
|
|
413
|
+
const message = getProtocolV2UnknownErrorText(error);
|
|
414
|
+
if (errorCode === HardwareErrorCode.TransportNotFound && /not connected/i.test(message)) {
|
|
415
|
+
return true;
|
|
416
|
+
}
|
|
417
|
+
return (
|
|
418
|
+
message.includes('ProtocolV2LinkError') ||
|
|
419
|
+
message.includes('Device protocol has not been detected') ||
|
|
420
|
+
message.includes('Electron BLE device state cleaned') ||
|
|
421
|
+
message.includes('Electron BLE transport released')
|
|
422
|
+
);
|
|
423
|
+
};
|
|
424
|
+
|
|
407
425
|
const isProtocolV2TerminalInstallStatusError = (error: unknown) =>
|
|
408
426
|
error instanceof HardwareError &&
|
|
409
427
|
(error.errorCode === HardwareErrorCode.ActionCancelled ||
|
|
@@ -1900,11 +1918,57 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1900
1918
|
if (!expectedHeaderHash || header.headerHash !== expectedHeaderHash) return false;
|
|
1901
1919
|
return true;
|
|
1902
1920
|
} catch (error) {
|
|
1921
|
+
if (isProtocolV2ResourceHeaderLinkError(error)) throw error;
|
|
1903
1922
|
Log.log(`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check failed: `, error);
|
|
1904
1923
|
return false;
|
|
1905
1924
|
}
|
|
1906
1925
|
}
|
|
1907
1926
|
|
|
1927
|
+
/**
|
|
1928
|
+
* A lost link would fail every remaining header check and resend bundles that are already
|
|
1929
|
+
* installed, so reconnect (a bounded number of times per transfer phase) and ask again.
|
|
1930
|
+
* Header reads have no side effects. If reconnecting fails or the budget is used up, the bundle
|
|
1931
|
+
* is resent as before; a failed identity check after reconnecting aborts the update.
|
|
1932
|
+
*/
|
|
1933
|
+
private async isProtocolV2ResourceBundleUpToDateWithRecovery(
|
|
1934
|
+
bundle: ProtocolV2ResourceBundleSource,
|
|
1935
|
+
recovery: { remaining: number }
|
|
1936
|
+
): Promise<boolean> {
|
|
1937
|
+
for (;;) {
|
|
1938
|
+
try {
|
|
1939
|
+
return await this.isProtocolV2ResourceBundleUpToDate(bundle);
|
|
1940
|
+
} catch (error) {
|
|
1941
|
+
this.throwIfAborted();
|
|
1942
|
+
if (recovery.remaining <= 0) {
|
|
1943
|
+
Log.log(`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check failed: `, error);
|
|
1944
|
+
return false;
|
|
1945
|
+
}
|
|
1946
|
+
recovery.remaining -= 1;
|
|
1947
|
+
Log.log(
|
|
1948
|
+
`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check lost the link; reconnecting: `,
|
|
1949
|
+
getProtocolV2UnknownErrorText(error)
|
|
1950
|
+
);
|
|
1951
|
+
try {
|
|
1952
|
+
await this.recoverProtocolV2FileTransfer();
|
|
1953
|
+
} catch (recoverError) {
|
|
1954
|
+
this.throwIfAborted();
|
|
1955
|
+
// Mismatch or unavailable: never keep writing to a device that was not re-verified.
|
|
1956
|
+
if (
|
|
1957
|
+
this.normalizeErrorMessage(recoverError).includes(
|
|
1958
|
+
'Protocol V2 reconnect physical identity'
|
|
1959
|
+
)
|
|
1960
|
+
) {
|
|
1961
|
+
throw recoverError;
|
|
1962
|
+
}
|
|
1963
|
+
recovery.remaining = 0;
|
|
1964
|
+
Log.log('[FirmwareUpdateV4] RESC header check reconnect failed: ', recoverError);
|
|
1965
|
+
return false;
|
|
1966
|
+
}
|
|
1967
|
+
this.throwIfAborted();
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1908
1972
|
private isProtocolV2BootloaderMode() {
|
|
1909
1973
|
if (typeof this.device.isBootloader === 'function') {
|
|
1910
1974
|
return !!this.device.isBootloader();
|
|
@@ -2143,8 +2207,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2143
2207
|
}: ProtocolV2TransferBatch) {
|
|
2144
2208
|
let totalSize = installSources.reduce((total, item) => total + item.source.size, 0);
|
|
2145
2209
|
const resourcesToSync: ProtocolV2ResourceBundleSource[] = [];
|
|
2210
|
+
const headerRecovery = { remaining: PROTOCOL_V2_RESOURCE_HEADER_RECOVERY_LIMIT };
|
|
2146
2211
|
for (const resource of resourceSources) {
|
|
2147
|
-
if (await this.
|
|
2212
|
+
if (await this.isProtocolV2ResourceBundleUpToDateWithRecovery(resource, headerRecovery)) {
|
|
2148
2213
|
Log.log(`[FirmwareUpdateV4] skip RESC bundle ${resource.name}; already up to date`);
|
|
2149
2214
|
} else {
|
|
2150
2215
|
resourcesToSync.push(resource);
|