@onekeyfe/hd-core 1.2.8-alpha.117 → 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 -420
- package/__tests__/search-devices.test.ts +36 -0
- package/dist/api/FirmwareUpdateV4.d.ts +0 -4
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +38 -211
- package/dist/types/device.d.ts +1 -0
- package/dist/types/device.d.ts.map +1 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/FirmwareUpdateV4.ts +53 -293
- package/src/types/device.ts +6 -0
- package/src/utils/logger.ts +0 -4
- package/__tests__/logger.test.ts +0 -23
|
@@ -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
|
-
// Bootloaders with the 2048-byte BLE UART FIFO have no room beside one waiting FileWrite
|
|
108
|
-
// frame, so a second in-flight write can overflow it and lose a chunk. Firmware has not yet
|
|
109
|
-
// confirmed which bootloader version ships the 4096-byte FIFO, nor that the BLE coprocessor
|
|
110
|
-
// queues absorb two back-to-back frames.
|
|
111
|
-
// TEST ONLY: 1.0.2 also matches released bootloaders with the 2048-byte FIFO, so this build
|
|
112
|
-
// must only update test devices flashed with the 4096-byte FIFO bootloader. Replace it with
|
|
113
|
-
// the first released version that ships the 4096-byte FIFO before any release.
|
|
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,
|
|
@@ -405,23 +382,6 @@ const getProtocolV2ErrorCode = (error: unknown): number | string | undefined =>
|
|
|
405
382
|
return typeof causeCode === 'number' || typeof causeCode === 'string' ? causeCode : undefined;
|
|
406
383
|
};
|
|
407
384
|
|
|
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
|
-
|
|
425
385
|
const isProtocolV2TerminalInstallStatusError = (error: unknown) =>
|
|
426
386
|
error instanceof HardwareError &&
|
|
427
387
|
(error.errorCode === HardwareErrorCode.ActionCancelled ||
|
|
@@ -815,11 +775,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
815
775
|
};
|
|
816
776
|
}
|
|
817
777
|
|
|
818
|
-
private getProtocolV2FirmwareChunkSize(
|
|
819
|
-
direction: 'read' | 'write',
|
|
820
|
-
filePath?: string,
|
|
821
|
-
fileWriteWindow = false
|
|
822
|
-
) {
|
|
778
|
+
private getProtocolV2FirmwareChunkSize(direction: 'read' | 'write', filePath?: string) {
|
|
823
779
|
const payloadChunkSize = Number(this.params?.chunkSize);
|
|
824
780
|
const env = DataManager.getSettings('env');
|
|
825
781
|
const isBle = this.params?.platform === 'native' || (env && DataManager.isBleConnect(env));
|
|
@@ -827,11 +783,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
827
783
|
if (isBle) {
|
|
828
784
|
if (direction === 'read') {
|
|
829
785
|
maxChunkSize = PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE;
|
|
830
|
-
} else if (fileWriteWindow) {
|
|
831
|
-
// Pipelined frames share the main MCU's 4096-byte UART FIFO. Two 1800-byte chunks
|
|
832
|
-
// (about 3.75 KB of frames) leave room to spare; two 1960-byte staging chunks would
|
|
833
|
-
// leave under 60 bytes.
|
|
834
|
-
maxChunkSize = PROTOCOL_V2_BLE_FILE_CHUNK_SIZE;
|
|
835
786
|
} else {
|
|
836
787
|
// Firmware staging writes tolerate a larger BLE chunk than generic filesystem writes.
|
|
837
788
|
maxChunkSize = PROTOCOL_V2_FIRMWARE_STAGING_PATHS.has(filePath ?? '')
|
|
@@ -1918,57 +1869,11 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1918
1869
|
if (!expectedHeaderHash || header.headerHash !== expectedHeaderHash) return false;
|
|
1919
1870
|
return true;
|
|
1920
1871
|
} catch (error) {
|
|
1921
|
-
if (isProtocolV2ResourceHeaderLinkError(error)) throw error;
|
|
1922
1872
|
Log.log(`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check failed: `, error);
|
|
1923
1873
|
return false;
|
|
1924
1874
|
}
|
|
1925
1875
|
}
|
|
1926
1876
|
|
|
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
|
-
|
|
1972
1877
|
private isProtocolV2BootloaderMode() {
|
|
1973
1878
|
if (typeof this.device.isBootloader === 'function') {
|
|
1974
1879
|
return !!this.device.isBootloader();
|
|
@@ -2207,9 +2112,8 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2207
2112
|
}: ProtocolV2TransferBatch) {
|
|
2208
2113
|
let totalSize = installSources.reduce((total, item) => total + item.source.size, 0);
|
|
2209
2114
|
const resourcesToSync: ProtocolV2ResourceBundleSource[] = [];
|
|
2210
|
-
const headerRecovery = { remaining: PROTOCOL_V2_RESOURCE_HEADER_RECOVERY_LIMIT };
|
|
2211
2115
|
for (const resource of resourceSources) {
|
|
2212
|
-
if (await this.
|
|
2116
|
+
if (await this.isProtocolV2ResourceBundleUpToDate(resource)) {
|
|
2213
2117
|
Log.log(`[FirmwareUpdateV4] skip RESC bundle ${resource.name}; already up to date`);
|
|
2214
2118
|
} else {
|
|
2215
2119
|
resourcesToSync.push(resource);
|
|
@@ -2259,19 +2163,11 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2259
2163
|
|
|
2260
2164
|
if (totalSize > 0) {
|
|
2261
2165
|
const elapsedMs = Math.max(Date.now() - transferStartedAt, 0);
|
|
2262
|
-
const rateBytesPerSecond =
|
|
2263
|
-
elapsedMs > 0 ? Math.round((totalSize / elapsedMs) * 1000) : undefined;
|
|
2264
2166
|
this.postProgressMessage(100, 'transferData', {
|
|
2265
2167
|
transferredBytes: totalSize,
|
|
2266
2168
|
totalBytes: totalSize,
|
|
2267
|
-
rateBytesPerSecond,
|
|
2268
|
-
elapsedMs,
|
|
2269
|
-
});
|
|
2270
|
-
Log.debug('[FirmwareUpdateV4] Protocol V2 transfer completed', {
|
|
2271
|
-
files: resourcesToSync.length + installSources.length,
|
|
2272
|
-
totalBytes: totalSize,
|
|
2169
|
+
rateBytesPerSecond: elapsedMs > 0 ? Math.round((totalSize / elapsedMs) * 1000) : undefined,
|
|
2273
2170
|
elapsedMs,
|
|
2274
|
-
rateBytesPerSecond,
|
|
2275
2171
|
});
|
|
2276
2172
|
}
|
|
2277
2173
|
if (stagedInstallTargets.length === 0) {
|
|
@@ -2283,16 +2179,10 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2283
2179
|
target_id: item.targetId,
|
|
2284
2180
|
path: item.path,
|
|
2285
2181
|
}));
|
|
2286
|
-
// Includes the wait for on-device confirmation.
|
|
2287
|
-
const installStartedAt = Date.now();
|
|
2288
2182
|
try {
|
|
2289
2183
|
await this.protocolV2StartFirmwareUpdate({ targets });
|
|
2290
2184
|
await wait(PROTOCOL_V2_INSTALL_STATUS_INITIAL_DELAY);
|
|
2291
2185
|
await this.waitForProtocolV2FirmwareUpdateComplete(targets, true);
|
|
2292
|
-
Log.debug('[FirmwareUpdateV4] Protocol V2 install completed', {
|
|
2293
|
-
targets: targets.length,
|
|
2294
|
-
elapsedMs: Math.max(Date.now() - installStartedAt, 0),
|
|
2295
|
-
});
|
|
2296
2186
|
} finally {
|
|
2297
2187
|
this.device?.clearCancelableAction();
|
|
2298
2188
|
}
|
|
@@ -2311,96 +2201,66 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
2311
2201
|
totalSize: number;
|
|
2312
2202
|
transferStartedAt?: number;
|
|
2313
2203
|
}) {
|
|
2314
|
-
const reportConfirmedChunk = (chunkEnd: number) => {
|
|
2315
|
-
const transferredBytes = processedSize + chunkEnd;
|
|
2316
|
-
const now = Date.now();
|
|
2317
|
-
const elapsedMs = Math.max(now - transferStartedAt, 0);
|
|
2318
|
-
const progress = Math.min(Math.ceil((transferredBytes / totalSize) * 100), 99);
|
|
2319
|
-
const shouldPostProgress =
|
|
2320
|
-
transferredBytes >= this.protocolV2LastTransferredBytes &&
|
|
2321
|
-
(progress !== this.protocolV2LastTransferProgress ||
|
|
2322
|
-
now - this.protocolV2LastTransferProgressAt >=
|
|
2323
|
-
PROTOCOL_V2_TRANSFER_PROGRESS_HEARTBEAT_MS ||
|
|
2324
|
-
chunkEnd === source.size);
|
|
2325
|
-
if (shouldPostProgress) {
|
|
2326
|
-
this.protocolV2LastTransferProgress = progress;
|
|
2327
|
-
this.protocolV2LastTransferProgressAt = now;
|
|
2328
|
-
this.protocolV2LastTransferredBytes = transferredBytes;
|
|
2329
|
-
this.postProgressMessage(progress, 'transferData', {
|
|
2330
|
-
transferredBytes,
|
|
2331
|
-
totalBytes: totalSize,
|
|
2332
|
-
rateBytesPerSecond:
|
|
2333
|
-
elapsedMs > 0 ? Math.round((transferredBytes / elapsedMs) * 1000) : undefined,
|
|
2334
|
-
elapsedMs,
|
|
2335
|
-
});
|
|
2336
|
-
}
|
|
2337
|
-
};
|
|
2338
2204
|
let lastError: unknown;
|
|
2339
2205
|
for (let attempt = 1; attempt <= PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT; attempt += 1) {
|
|
2340
2206
|
this.throwIfAborted();
|
|
2341
|
-
const attemptStartedAt = Date.now();
|
|
2342
2207
|
try {
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
source.size,
|
|
2369
|
-
sourceOffset,
|
|
2370
|
-
data,
|
|
2371
|
-
first,
|
|
2372
|
-
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}`
|
|
2373
2233
|
);
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
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
|
+
},
|
|
2394
2259
|
});
|
|
2395
2260
|
return processedSize + source.size;
|
|
2396
2261
|
} catch (error) {
|
|
2397
2262
|
this.throwIfAborted();
|
|
2398
2263
|
lastError = error;
|
|
2399
|
-
Log.debug('[FirmwareUpdateV4] Protocol V2 file transfer attempt failed', {
|
|
2400
|
-
path: filePath,
|
|
2401
|
-
attempt,
|
|
2402
|
-
error: getProtocolV2UnknownErrorText(error),
|
|
2403
|
-
});
|
|
2404
2264
|
if (attempt < PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT) {
|
|
2405
2265
|
await this.recoverProtocolV2FileTransfer();
|
|
2406
2266
|
this.throwIfAborted();
|
|
@@ -3332,106 +3192,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
3332
3192
|
return writeRes;
|
|
3333
3193
|
}
|
|
3334
3194
|
|
|
3335
|
-
private shouldUseProtocolV2BleFileWriteWindow(
|
|
3336
|
-
minBootloaderVersion = PROTOCOL_V2_BLE_FILE_WRITE_WINDOW_MIN_BOOTLOADER_VERSION
|
|
3337
|
-
) {
|
|
3338
|
-
const env = DataManager.getSettings('env');
|
|
3339
|
-
// These adapters queue frames that arrive while no read is pending. LowLevel plugins are not
|
|
3340
|
-
// required to keep notifications between receive() calls, so they stay serial.
|
|
3341
|
-
if (
|
|
3342
|
-
(env !== 'react-native' && env !== 'desktop-web-ble') ||
|
|
3343
|
-
!this.isProtocolV2BootloaderMode()
|
|
3344
|
-
) {
|
|
3345
|
-
return false;
|
|
3346
|
-
}
|
|
3347
|
-
// The transfer runs in the device's current bootloader, not the one being installed.
|
|
3348
|
-
const bootloaderVersion = getDeviceBootloaderVersion(this.device.features).join('.');
|
|
3349
|
-
const enabled = supportsProtocolV2BleFileWriteWindow(bootloaderVersion, minBootloaderVersion);
|
|
3350
|
-
Log.debug('[FirmwareUpdateV4] BLE FileWrite window', {
|
|
3351
|
-
enabled,
|
|
3352
|
-
bootloaderVersion,
|
|
3353
|
-
bootloaderBuildId: this.device.features?.verify?.bootloaderBuildId,
|
|
3354
|
-
});
|
|
3355
|
-
return enabled;
|
|
3356
|
-
}
|
|
3357
|
-
|
|
3358
|
-
/**
|
|
3359
|
-
* Streams one staged file as a single ordered FilesystemFileWrite pipeline. Every chunk is
|
|
3360
|
-
* still confirmed by the device; a failure restarts the whole file like the serial path.
|
|
3361
|
-
* Cancellation and source errors only stop the pipeline and are raised after the transport
|
|
3362
|
-
* call returns: an error thrown inside it would invalidate the BLE link.
|
|
3363
|
-
*/
|
|
3364
|
-
private async writeProtocolV2SourceWindowed({
|
|
3365
|
-
source,
|
|
3366
|
-
filePath,
|
|
3367
|
-
chunkSize,
|
|
3368
|
-
processedSize,
|
|
3369
|
-
totalSize,
|
|
3370
|
-
onChunkConfirmed,
|
|
3371
|
-
}: {
|
|
3372
|
-
source: FirmwareByteSource;
|
|
3373
|
-
filePath: string;
|
|
3374
|
-
chunkSize: number;
|
|
3375
|
-
processedSize: number;
|
|
3376
|
-
totalSize: number;
|
|
3377
|
-
onChunkConfirmed: (chunkEnd: number) => void;
|
|
3378
|
-
}) {
|
|
3379
|
-
let sendOffset = 0;
|
|
3380
|
-
let sourceError: Error | undefined;
|
|
3381
|
-
const readRequest = async () => {
|
|
3382
|
-
const offset = sendOffset;
|
|
3383
|
-
const length = Math.min(chunkSize, source.size - offset);
|
|
3384
|
-
const data = await source.readAt(offset, length);
|
|
3385
|
-
sendOffset += length;
|
|
3386
|
-
return {
|
|
3387
|
-
file: { path: filePath, offset, total_size: source.size, data },
|
|
3388
|
-
overwrite: offset === 0,
|
|
3389
|
-
append: false,
|
|
3390
|
-
ui_percentage: getProtocolV2DeviceTransferProgress(
|
|
3391
|
-
processedSize + offset,
|
|
3392
|
-
processedSize + sendOffset,
|
|
3393
|
-
totalSize
|
|
3394
|
-
),
|
|
3395
|
-
};
|
|
3396
|
-
};
|
|
3397
|
-
const nextRequest = async () => {
|
|
3398
|
-
if (this.abortSignal?.aborted || sendOffset >= source.size) return undefined;
|
|
3399
|
-
try {
|
|
3400
|
-
return await readRequest();
|
|
3401
|
-
} catch (error) {
|
|
3402
|
-
sourceError = error;
|
|
3403
|
-
return undefined;
|
|
3404
|
-
}
|
|
3405
|
-
};
|
|
3406
|
-
this.throwIfAborted();
|
|
3407
|
-
if (source.size === 0) return;
|
|
3408
|
-
const firstRequest = await readRequest();
|
|
3409
|
-
|
|
3410
|
-
const response = await this.device
|
|
3411
|
-
.getCommands()
|
|
3412
|
-
.typedCall('FilesystemFileWrite', 'FilesystemFile', firstRequest, {
|
|
3413
|
-
// Both windowed transports (React Native and Electron BLE) invalidate a timed-out link,
|
|
3414
|
-
// so a lost chunk is retried after 10 s instead of the 5-minute default.
|
|
3415
|
-
timeoutMs: PROTOCOL_V2_FILE_WRITE_RESPONSE_TIMEOUT,
|
|
3416
|
-
writeWithResponse: false,
|
|
3417
|
-
fileWritePipeline: {
|
|
3418
|
-
next: nextRequest,
|
|
3419
|
-
onResponse: confirmed => {
|
|
3420
|
-
onChunkConfirmed(Number(confirmed.message.processed_byte));
|
|
3421
|
-
},
|
|
3422
|
-
},
|
|
3423
|
-
});
|
|
3424
|
-
this.throwIfAborted();
|
|
3425
|
-
if (sourceError) throw sourceError;
|
|
3426
|
-
const rawProcessedByte = response.message.processed_byte;
|
|
3427
|
-
if (Number(rawProcessedByte) !== source.size) {
|
|
3428
|
-
throw ERRORS.TypedError(
|
|
3429
|
-
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
3430
|
-
`invalid processed_byte ${rawProcessedByte} for file size ${source.size}`
|
|
3431
|
-
);
|
|
3432
|
-
}
|
|
3433
|
-
}
|
|
3434
|
-
|
|
3435
3195
|
private async recoverProtocolV2FileTransfer() {
|
|
3436
3196
|
const env = DataManager.getSettings('env');
|
|
3437
3197
|
if (DataManager.isBleConnect(env)) {
|
package/src/types/device.ts
CHANGED
|
@@ -83,6 +83,12 @@ export type SearchDevice = {
|
|
|
83
83
|
deviceType: IDeviceType;
|
|
84
84
|
name: string;
|
|
85
85
|
commType: OneKeyDeviceCommType;
|
|
86
|
+
/**
|
|
87
|
+
* React Native and Electron BLE discovery only. Serial-number color letter ('A'-'Z')
|
|
88
|
+
* advertised by Pro2 / Neo, possibly remembered from an earlier advertisement in this
|
|
89
|
+
* process. Absent when unknown; the letter's meaning depends on deviceType.
|
|
90
|
+
*/
|
|
91
|
+
bleColorCode?: string;
|
|
86
92
|
};
|
|
87
93
|
|
|
88
94
|
/** Identity confirmed over USB. The descriptor path is only a transport address. */
|
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,23 +0,0 @@
|
|
|
1
|
-
import { LoggerNames, enableLog, getLogger } from '../src/utils/logger';
|
|
2
|
-
|
|
3
|
-
describe('logger', () => {
|
|
4
|
-
afterEach(() => {
|
|
5
|
-
enableLog(false);
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
test('enables method logs that share a prefix with common-connect-sdk', () => {
|
|
9
|
-
const consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => undefined);
|
|
10
|
-
try {
|
|
11
|
-
enableLog(true);
|
|
12
|
-
getLogger(LoggerNames.Method).debug('[FirmwareUpdateV4] BLE FileWrite window');
|
|
13
|
-
|
|
14
|
-
expect(getLogger(LoggerNames.Method)).toBe(getLogger(LoggerNames.HdCommonConnectSdk));
|
|
15
|
-
expect(consoleSpy).toHaveBeenCalledWith(
|
|
16
|
-
LoggerNames.Method,
|
|
17
|
-
'[FirmwareUpdateV4] BLE FileWrite window'
|
|
18
|
-
);
|
|
19
|
-
} finally {
|
|
20
|
-
consoleSpy.mockRestore();
|
|
21
|
-
}
|
|
22
|
-
});
|
|
23
|
-
});
|