@onekeyfe/hd-core 1.2.8-alpha.118 → 1.2.8-alpha.119
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/protocol-v2.test.ts +0 -425
- package/dist/api/FirmwareUpdateV4.d.ts +0 -4
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/index.js +39 -214
- package/dist/utils/logger.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/FirmwareUpdateV4.ts +54 -301
- package/src/utils/logger.ts +0 -4
- package/__tests__/logger.test.ts +0 -18
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { EDeviceType, ERRORS, HardwareError, HardwareErrorCode, wait } from '@onekeyfe/hd-shared';
|
|
2
2
|
import JSZip from 'jszip';
|
|
3
|
-
import semver from 'semver';
|
|
4
3
|
import {
|
|
5
4
|
DeviceRebootType,
|
|
6
5
|
DeviceSessionPinType,
|
|
7
6
|
PROTOCOL_V2_BLE_FILE_CHUNK_SIZE,
|
|
8
7
|
PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE,
|
|
9
8
|
PROTOCOL_V2_BLE_FIRMWARE_FILE_CHUNK_SIZE,
|
|
10
|
-
PROTOCOL_V2_FILE_WRITE_PIPELINE_WINDOW,
|
|
11
9
|
PROTOCOL_V2_WEBUSB_FILE_CHUNK_SIZE,
|
|
12
10
|
} from '@onekeyfe/hd-transport';
|
|
13
11
|
import { sha256 } from '@noble/hashes/sha256';
|
|
@@ -103,15 +101,6 @@ const PROTOCOL_V2_CONNECT_POLL_INTERVAL = 500;
|
|
|
103
101
|
const PROTOCOL_V2_CONNECT_SINGLE_TIMEOUT = 75 * 1000;
|
|
104
102
|
const PROTOCOL_V2_DEVICE_INFO_READY_TIMEOUT = 30 * 1000;
|
|
105
103
|
const PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT = 3;
|
|
106
|
-
const PROTOCOL_V2_RESOURCE_HEADER_RECOVERY_LIMIT = 2;
|
|
107
|
-
// Minimum running bootloader that receives two in-flight BLE FilesystemFileWrite frames.
|
|
108
|
-
// Bootloaders with the 2048-byte BLE UART FIFO have no room beside one waiting FileWrite frame,
|
|
109
|
-
// so a second frame can overflow it: the lost chunk is detected (no response or a mismatched
|
|
110
|
-
// confirmation) and Core rewrites the file, which slows the transfer or fails it after the
|
|
111
|
-
// retries. 1.0.2 also matches released 2048-byte FIFO bootloaders. Raise it to the first
|
|
112
|
-
// release that ships the 4096-byte FIFO once firmware confirms that version and the BLE
|
|
113
|
-
// coprocessor margin, or set it to undefined to keep every transfer serial.
|
|
114
|
-
const PROTOCOL_V2_BLE_FILE_WRITE_WINDOW_MIN_BOOTLOADER_VERSION: string | undefined = '1.0.2';
|
|
115
104
|
const PROTOCOL_V2_TRANSFER_PROGRESS_HEARTBEAT_MS = 1000;
|
|
116
105
|
const PROTOCOL_V2_INSTALL_STATUS_CONFLICT_CODE = 'FirmwareInstallStatusConflict';
|
|
117
106
|
const PROTOCOL_V2_INSTALL_FAILED_CODE = 'FirmwareInstallFailed';
|
|
@@ -181,18 +170,6 @@ export function assertProtocolV2FirmwareTargetsSupported(
|
|
|
181
170
|
throw ERRORS.TypedError(HardwareErrorCode.DeviceNotSupportMethod, message);
|
|
182
171
|
}
|
|
183
172
|
|
|
184
|
-
/** Whether the running bootloader accepts a second in-flight BLE FilesystemFileWrite. */
|
|
185
|
-
export function supportsProtocolV2BleFileWriteWindow(
|
|
186
|
-
bootloaderVersion: string,
|
|
187
|
-
minBootloaderVersion = PROTOCOL_V2_BLE_FILE_WRITE_WINDOW_MIN_BOOTLOADER_VERSION
|
|
188
|
-
) {
|
|
189
|
-
return (
|
|
190
|
-
!!minBootloaderVersion &&
|
|
191
|
-
!!semver.valid(bootloaderVersion) &&
|
|
192
|
-
semver.gte(bootloaderVersion, minBootloaderVersion)
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
173
|
const getProtocolV2DeviceTransferProgress = (
|
|
197
174
|
bytesBeforeChunk: number,
|
|
198
175
|
bytesAfterChunk: number,
|
|
@@ -207,12 +184,6 @@ const getProtocolV2DeviceTransferProgress = (
|
|
|
207
184
|
return Math.min(Math.max(Math.ceil((bytesAfterChunk / totalBytes) * 100), 1), 99);
|
|
208
185
|
};
|
|
209
186
|
|
|
210
|
-
const getProtocolV2TransferRate = (bytes: number, elapsedMs: number) =>
|
|
211
|
-
elapsedMs > 0 ? Math.round((bytes / elapsedMs) * 1000) : undefined;
|
|
212
|
-
|
|
213
|
-
const getProtocolV2ElapsedMs = (startedAt: number, now = Date.now()) =>
|
|
214
|
-
Math.max(now - startedAt, 0);
|
|
215
|
-
|
|
216
187
|
type ProtocolV2FirmwareUpdateStatusTarget = {
|
|
217
188
|
target_id: number | string;
|
|
218
189
|
status?: number | string;
|
|
@@ -411,23 +382,6 @@ const getProtocolV2ErrorCode = (error: unknown): number | string | undefined =>
|
|
|
411
382
|
return typeof causeCode === 'number' || typeof causeCode === 'string' ? causeCode : undefined;
|
|
412
383
|
};
|
|
413
384
|
|
|
414
|
-
/** Errors that mean the link is gone, so the read says nothing about the installed bundle. */
|
|
415
|
-
const isProtocolV2ResourceHeaderLinkError = (error: unknown) => {
|
|
416
|
-
if (isProtocolV2DeviceDisconnectedError(error)) return true;
|
|
417
|
-
const errorCode = getProtocolV2ErrorCode(error);
|
|
418
|
-
if (errorCode === HardwareErrorCode.BleTimeoutError) return true;
|
|
419
|
-
const message = getProtocolV2UnknownErrorText(error);
|
|
420
|
-
if (errorCode === HardwareErrorCode.TransportNotFound && /not connected/i.test(message)) {
|
|
421
|
-
return true;
|
|
422
|
-
}
|
|
423
|
-
return (
|
|
424
|
-
message.includes('ProtocolV2LinkError') ||
|
|
425
|
-
message.includes('Device protocol has not been detected') ||
|
|
426
|
-
message.includes('Electron BLE device state cleaned') ||
|
|
427
|
-
message.includes('Electron BLE transport released')
|
|
428
|
-
);
|
|
429
|
-
};
|
|
430
|
-
|
|
431
385
|
const isProtocolV2TerminalInstallStatusError = (error: unknown) =>
|
|
432
386
|
error instanceof HardwareError &&
|
|
433
387
|
(error.errorCode === HardwareErrorCode.ActionCancelled ||
|
|
@@ -581,10 +535,6 @@ export const assertProtocolV2ReconnectIdentity = (
|
|
|
581
535
|
);
|
|
582
536
|
};
|
|
583
537
|
|
|
584
|
-
/** Mismatch or unavailable: the reconnected device could not be re-verified. */
|
|
585
|
-
const isProtocolV2ReconnectIdentityUnverified = (error: unknown) =>
|
|
586
|
-
getProtocolV2UnknownErrorText(error).includes('Protocol V2 reconnect physical identity');
|
|
587
|
-
|
|
588
538
|
/**
|
|
589
539
|
* FirmwareUpdateV4 is the complete Protocol V2 firmware update flow.
|
|
590
540
|
*
|
|
@@ -825,11 +775,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
825
775
|
};
|
|
826
776
|
}
|
|
827
777
|
|
|
828
|
-
private getProtocolV2FirmwareChunkSize(
|
|
829
|
-
direction: 'read' | 'write',
|
|
830
|
-
filePath?: string,
|
|
831
|
-
fileWriteWindow = false
|
|
832
|
-
) {
|
|
778
|
+
private getProtocolV2FirmwareChunkSize(direction: 'read' | 'write', filePath?: string) {
|
|
833
779
|
const payloadChunkSize = Number(this.params?.chunkSize);
|
|
834
780
|
const env = DataManager.getSettings('env');
|
|
835
781
|
const isBle = this.params?.platform === 'native' || (env && DataManager.isBleConnect(env));
|
|
@@ -837,10 +783,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
837
783
|
if (isBle) {
|
|
838
784
|
if (direction === 'read') {
|
|
839
785
|
maxChunkSize = PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE;
|
|
840
|
-
} else if (fileWriteWindow) {
|
|
841
|
-
// Skip the larger staging chunk so two in-flight frames fit the UART FIFO; see
|
|
842
|
-
// PROTOCOL_V2_FILE_WRITE_PIPELINE_WINDOW.
|
|
843
|
-
maxChunkSize = PROTOCOL_V2_BLE_FILE_CHUNK_SIZE;
|
|
844
786
|
} else {
|
|
845
787
|
// Firmware staging writes tolerate a larger BLE chunk than generic filesystem writes.
|
|
846
788
|
maxChunkSize = PROTOCOL_V2_FIRMWARE_STAGING_PATHS.has(filePath ?? '')
|
|
@@ -1927,57 +1869,11 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1927
1869
|
if (!expectedHeaderHash || header.headerHash !== expectedHeaderHash) return false;
|
|
1928
1870
|
return true;
|
|
1929
1871
|
} catch (error) {
|
|
1930
|
-
if (isProtocolV2ResourceHeaderLinkError(error)) throw error;
|
|
1931
1872
|
Log.log(`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check failed: `, error);
|
|
1932
1873
|
return false;
|
|
1933
1874
|
}
|
|
1934
1875
|
}
|
|
1935
1876
|
|
|
1936
|
-
/**
|
|
1937
|
-
* A lost link would fail every remaining header check and resend bundles that are already
|
|
1938
|
-
* installed, so reconnect (a bounded number of times per transfer phase) and ask again.
|
|
1939
|
-
* Header reads have no side effects. If reconnecting fails or the budget is used up, the bundle
|
|
1940
|
-
* is resent as before; a failed identity check after reconnecting aborts the update.
|
|
1941
|
-
*/
|
|
1942
|
-
private async isProtocolV2ResourceBundleUpToDateWithRecovery(
|
|
1943
|
-
bundle: ProtocolV2ResourceBundleSource,
|
|
1944
|
-
recovery: { remaining: number }
|
|
1945
|
-
): Promise<boolean> {
|
|
1946
|
-
for (;;) {
|
|
1947
|
-
try {
|
|
1948
|
-
return await this.isProtocolV2ResourceBundleUpToDate(bundle);
|
|
1949
|
-
} catch (error) {
|
|
1950
|
-
this.throwIfAborted();
|
|
1951
|
-
if (recovery.remaining <= 0) {
|
|
1952
|
-
Log.log(
|
|
1953
|
-
`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check lost the link; reconnect budget used up: `,
|
|
1954
|
-
getProtocolV2UnknownErrorText(error)
|
|
1955
|
-
);
|
|
1956
|
-
return false;
|
|
1957
|
-
}
|
|
1958
|
-
recovery.remaining -= 1;
|
|
1959
|
-
Log.log(
|
|
1960
|
-
`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check lost the link; reconnecting: `,
|
|
1961
|
-
getProtocolV2UnknownErrorText(error)
|
|
1962
|
-
);
|
|
1963
|
-
}
|
|
1964
|
-
try {
|
|
1965
|
-
await this.recoverProtocolV2FileTransfer();
|
|
1966
|
-
} catch (recoverError) {
|
|
1967
|
-
this.throwIfAborted();
|
|
1968
|
-
// Never keep writing to a device that was not re-verified.
|
|
1969
|
-
if (isProtocolV2ReconnectIdentityUnverified(recoverError)) throw recoverError;
|
|
1970
|
-
recovery.remaining = 0;
|
|
1971
|
-
Log.log(
|
|
1972
|
-
'[FirmwareUpdateV4] RESC header check reconnect failed: ',
|
|
1973
|
-
getProtocolV2UnknownErrorText(recoverError)
|
|
1974
|
-
);
|
|
1975
|
-
return false;
|
|
1976
|
-
}
|
|
1977
|
-
this.throwIfAborted();
|
|
1978
|
-
}
|
|
1979
|
-
}
|
|
1980
|
-
|
|
1981
1877
|
private isProtocolV2BootloaderMode() {
|
|
1982
1878
|
if (typeof this.device.isBootloader === 'function') {
|
|
1983
1879
|
return !!this.device.isBootloader();
|
|
@@ -2216,9 +2112,8 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2216
2112
|
}: ProtocolV2TransferBatch) {
|
|
2217
2113
|
let totalSize = installSources.reduce((total, item) => total + item.source.size, 0);
|
|
2218
2114
|
const resourcesToSync: ProtocolV2ResourceBundleSource[] = [];
|
|
2219
|
-
const headerRecovery = { remaining: PROTOCOL_V2_RESOURCE_HEADER_RECOVERY_LIMIT };
|
|
2220
2115
|
for (const resource of resourceSources) {
|
|
2221
|
-
if (await this.
|
|
2116
|
+
if (await this.isProtocolV2ResourceBundleUpToDate(resource)) {
|
|
2222
2117
|
Log.log(`[FirmwareUpdateV4] skip RESC bundle ${resource.name}; already up to date`);
|
|
2223
2118
|
} else {
|
|
2224
2119
|
resourcesToSync.push(resource);
|
|
@@ -2267,19 +2162,12 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2267
2162
|
}
|
|
2268
2163
|
|
|
2269
2164
|
if (totalSize > 0) {
|
|
2270
|
-
const elapsedMs =
|
|
2271
|
-
const rateBytesPerSecond = getProtocolV2TransferRate(totalSize, elapsedMs);
|
|
2165
|
+
const elapsedMs = Math.max(Date.now() - transferStartedAt, 0);
|
|
2272
2166
|
this.postProgressMessage(100, 'transferData', {
|
|
2273
2167
|
transferredBytes: totalSize,
|
|
2274
2168
|
totalBytes: totalSize,
|
|
2275
|
-
rateBytesPerSecond,
|
|
2276
|
-
elapsedMs,
|
|
2277
|
-
});
|
|
2278
|
-
Log.debug('[FirmwareUpdateV4] Protocol V2 transfer completed', {
|
|
2279
|
-
files: resourcesToSync.length + installSources.length,
|
|
2280
|
-
totalBytes: totalSize,
|
|
2169
|
+
rateBytesPerSecond: elapsedMs > 0 ? Math.round((totalSize / elapsedMs) * 1000) : undefined,
|
|
2281
2170
|
elapsedMs,
|
|
2282
|
-
rateBytesPerSecond,
|
|
2283
2171
|
});
|
|
2284
2172
|
}
|
|
2285
2173
|
if (stagedInstallTargets.length === 0) {
|
|
@@ -2291,16 +2179,10 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2291
2179
|
target_id: item.targetId,
|
|
2292
2180
|
path: item.path,
|
|
2293
2181
|
}));
|
|
2294
|
-
// Includes the wait for on-device confirmation.
|
|
2295
|
-
const installStartedAt = Date.now();
|
|
2296
2182
|
try {
|
|
2297
2183
|
await this.protocolV2StartFirmwareUpdate({ targets });
|
|
2298
2184
|
await wait(PROTOCOL_V2_INSTALL_STATUS_INITIAL_DELAY);
|
|
2299
2185
|
await this.waitForProtocolV2FirmwareUpdateComplete(targets, true);
|
|
2300
|
-
Log.debug('[FirmwareUpdateV4] Protocol V2 install completed', {
|
|
2301
|
-
targets: targets.length,
|
|
2302
|
-
elapsedMs: getProtocolV2ElapsedMs(installStartedAt),
|
|
2303
|
-
});
|
|
2304
2186
|
} finally {
|
|
2305
2187
|
this.device?.clearCancelableAction();
|
|
2306
2188
|
}
|
|
@@ -2319,95 +2201,66 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2319
2201
|
totalSize: number;
|
|
2320
2202
|
transferStartedAt?: number;
|
|
2321
2203
|
}) {
|
|
2322
|
-
const reportConfirmedChunk = (chunkEnd: number) => {
|
|
2323
|
-
const transferredBytes = processedSize + chunkEnd;
|
|
2324
|
-
const now = Date.now();
|
|
2325
|
-
const elapsedMs = getProtocolV2ElapsedMs(transferStartedAt, now);
|
|
2326
|
-
const progress = Math.min(Math.ceil((transferredBytes / totalSize) * 100), 99);
|
|
2327
|
-
const shouldPostProgress =
|
|
2328
|
-
transferredBytes >= this.protocolV2LastTransferredBytes &&
|
|
2329
|
-
(progress !== this.protocolV2LastTransferProgress ||
|
|
2330
|
-
now - this.protocolV2LastTransferProgressAt >=
|
|
2331
|
-
PROTOCOL_V2_TRANSFER_PROGRESS_HEARTBEAT_MS ||
|
|
2332
|
-
chunkEnd === source.size);
|
|
2333
|
-
if (shouldPostProgress) {
|
|
2334
|
-
this.protocolV2LastTransferProgress = progress;
|
|
2335
|
-
this.protocolV2LastTransferProgressAt = now;
|
|
2336
|
-
this.protocolV2LastTransferredBytes = transferredBytes;
|
|
2337
|
-
this.postProgressMessage(progress, 'transferData', {
|
|
2338
|
-
transferredBytes,
|
|
2339
|
-
totalBytes: totalSize,
|
|
2340
|
-
rateBytesPerSecond: getProtocolV2TransferRate(transferredBytes, elapsedMs),
|
|
2341
|
-
elapsedMs,
|
|
2342
|
-
});
|
|
2343
|
-
}
|
|
2344
|
-
};
|
|
2345
2204
|
let lastError: unknown;
|
|
2346
2205
|
for (let attempt = 1; attempt <= PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT; attempt += 1) {
|
|
2347
2206
|
this.throwIfAborted();
|
|
2348
|
-
const attemptStartedAt = Date.now();
|
|
2349
2207
|
try {
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
source.size,
|
|
2376
|
-
sourceOffset,
|
|
2377
|
-
data,
|
|
2378
|
-
first,
|
|
2379
|
-
deviceProgress
|
|
2208
|
+
await writeFirmwareByteSource({
|
|
2209
|
+
source,
|
|
2210
|
+
chunkSize: this.getProtocolV2FirmwareChunkSize('write', filePath),
|
|
2211
|
+
write: async ({ data, sourceOffset, length, first }) => {
|
|
2212
|
+
this.throwIfAborted();
|
|
2213
|
+
const chunkEnd = sourceOffset + length;
|
|
2214
|
+
const deviceProgress = getProtocolV2DeviceTransferProgress(
|
|
2215
|
+
processedSize + sourceOffset,
|
|
2216
|
+
processedSize + chunkEnd,
|
|
2217
|
+
totalSize
|
|
2218
|
+
);
|
|
2219
|
+
const response = await this.fileWriteChunk(
|
|
2220
|
+
filePath,
|
|
2221
|
+
source.size,
|
|
2222
|
+
sourceOffset,
|
|
2223
|
+
data,
|
|
2224
|
+
first,
|
|
2225
|
+
deviceProgress
|
|
2226
|
+
);
|
|
2227
|
+
const rawProcessedByte = response.message.processed_byte;
|
|
2228
|
+
const nextOffset = rawProcessedByte === undefined ? chunkEnd : Number(rawProcessedByte);
|
|
2229
|
+
if (!Number.isFinite(nextOffset) || nextOffset !== chunkEnd) {
|
|
2230
|
+
throw ERRORS.TypedError(
|
|
2231
|
+
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
2232
|
+
`invalid processed_byte ${rawProcessedByte} for offset ${sourceOffset}`
|
|
2380
2233
|
);
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2234
|
+
}
|
|
2235
|
+
const transferredBytes = processedSize + chunkEnd;
|
|
2236
|
+
const now = Date.now();
|
|
2237
|
+
const elapsedMs = Math.max(now - transferStartedAt, 0);
|
|
2238
|
+
const progress = Math.min(Math.ceil((transferredBytes / totalSize) * 100), 99);
|
|
2239
|
+
const shouldPostProgress =
|
|
2240
|
+
transferredBytes >= this.protocolV2LastTransferredBytes &&
|
|
2241
|
+
(progress !== this.protocolV2LastTransferProgress ||
|
|
2242
|
+
now - this.protocolV2LastTransferProgressAt >=
|
|
2243
|
+
PROTOCOL_V2_TRANSFER_PROGRESS_HEARTBEAT_MS ||
|
|
2244
|
+
chunkEnd === source.size);
|
|
2245
|
+
if (shouldPostProgress) {
|
|
2246
|
+
this.protocolV2LastTransferProgress = progress;
|
|
2247
|
+
this.protocolV2LastTransferProgressAt = now;
|
|
2248
|
+
this.protocolV2LastTransferredBytes = transferredBytes;
|
|
2249
|
+
this.postProgressMessage(progress, 'transferData', {
|
|
2250
|
+
transferredBytes,
|
|
2251
|
+
totalBytes: totalSize,
|
|
2252
|
+
rateBytesPerSecond:
|
|
2253
|
+
elapsedMs > 0 ? Math.round((transferredBytes / elapsedMs) * 1000) : undefined,
|
|
2254
|
+
elapsedMs,
|
|
2255
|
+
});
|
|
2256
|
+
}
|
|
2257
|
+
return length;
|
|
2258
|
+
},
|
|
2401
2259
|
});
|
|
2402
2260
|
return processedSize + source.size;
|
|
2403
2261
|
} catch (error) {
|
|
2404
2262
|
this.throwIfAborted();
|
|
2405
2263
|
lastError = error;
|
|
2406
|
-
Log.debug('[FirmwareUpdateV4] Protocol V2 file transfer attempt failed', {
|
|
2407
|
-
path: filePath,
|
|
2408
|
-
attempt,
|
|
2409
|
-
error: getProtocolV2UnknownErrorText(error),
|
|
2410
|
-
});
|
|
2411
2264
|
if (attempt < PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT) {
|
|
2412
2265
|
await this.recoverProtocolV2FileTransfer();
|
|
2413
2266
|
this.throwIfAborted();
|
|
@@ -3339,106 +3192,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
3339
3192
|
return writeRes;
|
|
3340
3193
|
}
|
|
3341
3194
|
|
|
3342
|
-
private shouldUseProtocolV2BleFileWriteWindow(
|
|
3343
|
-
minBootloaderVersion = PROTOCOL_V2_BLE_FILE_WRITE_WINDOW_MIN_BOOTLOADER_VERSION
|
|
3344
|
-
) {
|
|
3345
|
-
const env = DataManager.getSettings('env');
|
|
3346
|
-
// These adapters queue frames that arrive while no read is pending. LowLevel plugins are not
|
|
3347
|
-
// required to keep notifications between receive() calls, so they stay serial.
|
|
3348
|
-
if (
|
|
3349
|
-
(env !== 'react-native' && env !== 'desktop-web-ble') ||
|
|
3350
|
-
!this.isProtocolV2BootloaderMode()
|
|
3351
|
-
) {
|
|
3352
|
-
return false;
|
|
3353
|
-
}
|
|
3354
|
-
// The transfer runs in the device's current bootloader, not the one being installed.
|
|
3355
|
-
const bootloaderVersion = getDeviceBootloaderVersion(this.device.features).join('.');
|
|
3356
|
-
const enabled = supportsProtocolV2BleFileWriteWindow(bootloaderVersion, minBootloaderVersion);
|
|
3357
|
-
Log.debug('[FirmwareUpdateV4] BLE FileWrite window', {
|
|
3358
|
-
enabled,
|
|
3359
|
-
bootloaderVersion,
|
|
3360
|
-
bootloaderBuildId: this.device.features?.verify?.bootloaderBuildId,
|
|
3361
|
-
});
|
|
3362
|
-
return enabled;
|
|
3363
|
-
}
|
|
3364
|
-
|
|
3365
|
-
/**
|
|
3366
|
-
* Streams one staged file as a single ordered FilesystemFileWrite pipeline. Every chunk is
|
|
3367
|
-
* still confirmed by the device; a failure restarts the whole file like the serial path.
|
|
3368
|
-
* Cancellation and source errors only stop the pipeline and are raised after the transport
|
|
3369
|
-
* call returns: an error thrown inside it would invalidate the BLE link.
|
|
3370
|
-
*/
|
|
3371
|
-
private async writeProtocolV2SourceWindowed({
|
|
3372
|
-
source,
|
|
3373
|
-
filePath,
|
|
3374
|
-
chunkSize,
|
|
3375
|
-
processedSize,
|
|
3376
|
-
totalSize,
|
|
3377
|
-
onChunkConfirmed,
|
|
3378
|
-
}: {
|
|
3379
|
-
source: FirmwareByteSource;
|
|
3380
|
-
filePath: string;
|
|
3381
|
-
chunkSize: number;
|
|
3382
|
-
processedSize: number;
|
|
3383
|
-
totalSize: number;
|
|
3384
|
-
onChunkConfirmed: (chunkEnd: number) => void;
|
|
3385
|
-
}) {
|
|
3386
|
-
let sendOffset = 0;
|
|
3387
|
-
let sourceError: Error | undefined;
|
|
3388
|
-
const readRequest = async () => {
|
|
3389
|
-
const offset = sendOffset;
|
|
3390
|
-
const length = Math.min(chunkSize, source.size - offset);
|
|
3391
|
-
const data = await source.readAt(offset, length);
|
|
3392
|
-
sendOffset += length;
|
|
3393
|
-
return {
|
|
3394
|
-
file: { path: filePath, offset, total_size: source.size, data },
|
|
3395
|
-
overwrite: offset === 0,
|
|
3396
|
-
append: false,
|
|
3397
|
-
ui_percentage: getProtocolV2DeviceTransferProgress(
|
|
3398
|
-
processedSize + offset,
|
|
3399
|
-
processedSize + sendOffset,
|
|
3400
|
-
totalSize
|
|
3401
|
-
),
|
|
3402
|
-
};
|
|
3403
|
-
};
|
|
3404
|
-
const nextRequest = async () => {
|
|
3405
|
-
if (this.abortSignal?.aborted || sendOffset >= source.size) return undefined;
|
|
3406
|
-
try {
|
|
3407
|
-
return await readRequest();
|
|
3408
|
-
} catch (error) {
|
|
3409
|
-
sourceError = error;
|
|
3410
|
-
return undefined;
|
|
3411
|
-
}
|
|
3412
|
-
};
|
|
3413
|
-
this.throwIfAborted();
|
|
3414
|
-
if (source.size === 0) return;
|
|
3415
|
-
const firstRequest = await readRequest();
|
|
3416
|
-
|
|
3417
|
-
const response = await this.device
|
|
3418
|
-
.getCommands()
|
|
3419
|
-
.typedCall('FilesystemFileWrite', 'FilesystemFile', firstRequest, {
|
|
3420
|
-
// Both windowed transports (React Native and Electron BLE) invalidate a timed-out link,
|
|
3421
|
-
// so a lost chunk is retried after 10 s instead of the 5-minute default.
|
|
3422
|
-
timeoutMs: PROTOCOL_V2_FILE_WRITE_RESPONSE_TIMEOUT,
|
|
3423
|
-
writeWithResponse: false,
|
|
3424
|
-
fileWritePipeline: {
|
|
3425
|
-
next: nextRequest,
|
|
3426
|
-
onResponse: confirmed => {
|
|
3427
|
-
onChunkConfirmed(Number(confirmed.message.processed_byte));
|
|
3428
|
-
},
|
|
3429
|
-
},
|
|
3430
|
-
});
|
|
3431
|
-
this.throwIfAborted();
|
|
3432
|
-
if (sourceError) throw sourceError;
|
|
3433
|
-
const rawProcessedByte = response.message.processed_byte;
|
|
3434
|
-
if (Number(rawProcessedByte) !== source.size) {
|
|
3435
|
-
throw ERRORS.TypedError(
|
|
3436
|
-
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
3437
|
-
`invalid processed_byte ${rawProcessedByte} for file size ${source.size}`
|
|
3438
|
-
);
|
|
3439
|
-
}
|
|
3440
|
-
}
|
|
3441
|
-
|
|
3442
3195
|
private async recoverProtocolV2FileTransfer() {
|
|
3443
3196
|
const env = DataManager.getSettings('env');
|
|
3444
3197
|
if (DataManager.isBleConnect(env)) {
|
package/src/utils/logger.ts
CHANGED
|
@@ -78,10 +78,6 @@ class Log {
|
|
|
78
78
|
const _logs: { [k: string]: Log } = {};
|
|
79
79
|
|
|
80
80
|
export const initLog = (prefix: string, enabled?: boolean) => {
|
|
81
|
-
// Loggers that share a prefix must share one instance; otherwise enableLog only reaches the
|
|
82
|
-
// last one registered and the others never print.
|
|
83
|
-
const existing = _logs[prefix];
|
|
84
|
-
if (existing) return existing;
|
|
85
81
|
const instance = new Log(prefix, !!enabled);
|
|
86
82
|
_logs[prefix] = instance;
|
|
87
83
|
return instance;
|
package/__tests__/logger.test.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { LoggerNames, enableLog, getLogger } from '../src/utils/logger';
|
|
2
|
-
|
|
3
|
-
describe('logger', () => {
|
|
4
|
-
afterEach(() => {
|
|
5
|
-
enableLog(false);
|
|
6
|
-
jest.restoreAllMocks();
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
test('enables method logs that share a prefix with common-connect-sdk', () => {
|
|
10
|
-
const consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => undefined);
|
|
11
|
-
enableLog(true);
|
|
12
|
-
|
|
13
|
-
expect(getLogger(LoggerNames.Method)).toBe(getLogger(LoggerNames.HdCommonConnectSdk));
|
|
14
|
-
getLogger(LoggerNames.Method).debug('shared prefix message');
|
|
15
|
-
|
|
16
|
-
expect(consoleSpy).toHaveBeenCalledWith(LoggerNames.Method, 'shared prefix message');
|
|
17
|
-
});
|
|
18
|
-
});
|