@onekeyfe/hd-core 1.2.0-alpha.69 → 1.2.0-alpha.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/check-all-firmware-release-protocol-v2.test.ts +0 -28
- package/__tests__/connectSettings.test.ts +45 -5
- package/__tests__/device-initialize-timeout.test.ts +56 -0
- package/__tests__/firmware-update/check-all-firmware-release.test.ts +175 -0
- package/__tests__/firmware-update/device-update-bootloader.test.ts +87 -0
- package/__tests__/firmware-update/firmware-artifact-source.test.ts +119 -0
- package/__tests__/firmware-update/firmware-host-binding.test.ts +70 -0
- package/__tests__/firmware-update/firmware-preparation-error.test.ts +27 -0
- package/__tests__/firmware-update/firmware-update-bootloader-poll.test.ts +200 -0
- package/__tests__/firmware-update/firmware-update-capabilities.test.ts +13 -0
- package/__tests__/firmware-update/firmware-update-plan-bootloader-identity.test.ts +394 -0
- package/__tests__/firmware-update/firmware-update-plan.test.ts +593 -0
- package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +231 -0
- package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +59 -31
- package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +63 -0
- package/__tests__/firmware-update/firmware-upload-source.test.ts +70 -0
- package/__tests__/protocol-v2-firmware-targets.test.ts +1 -33
- package/__tests__/protocol-v2-resources.test.ts +6 -9
- package/__tests__/protocol-v2.test.ts +383 -174
- package/__tests__/protocolV2FileWrite.test.ts +7 -48
- package/dist/api/CheckAllFirmwareRelease.d.ts +1 -2
- package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
- package/dist/api/FirmwareUpdate.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV2.d.ts +10 -0
- package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV3.d.ts +7 -3
- package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +25 -6
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/device/DeviceUpdateBootloader.d.ts +5 -1
- package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareArtifactSource.d.ts +25 -0
- package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +1 -0
- package/dist/api/firmware/FirmwareHostBinding.d.ts +6 -0
- package/dist/api/firmware/FirmwareHostBinding.d.ts.map +1 -0
- package/dist/api/firmware/FirmwarePreparationError.d.ts +3 -0
- package/dist/api/firmware/FirmwarePreparationError.d.ts.map +1 -0
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +8 -0
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +3 -0
- package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +1 -0
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts +25 -0
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -0
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +32 -0
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -0
- package/dist/api/firmware/getBinary.d.ts +7 -0
- package/dist/api/firmware/getBinary.d.ts.map +1 -1
- package/dist/api/firmware/updateBootloader.d.ts +2 -0
- package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
- package/dist/api/firmware/uploadFirmware.d.ts +9 -1
- package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
- package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
- package/dist/api/stellar/StellarSignTransaction.d.ts +1 -1
- package/dist/data-manager/DataManager.d.ts +1 -0
- package/dist/data-manager/DataManager.d.ts.map +1 -1
- package/dist/data-manager/connectSettings.d.ts.map +1 -1
- package/dist/device/Device.d.ts +1 -0
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/index.d.ts +180 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3038 -1041
- package/dist/inject.d.ts.map +1 -1
- package/dist/lowLevelInject.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/resources.d.ts +1 -1
- package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
- package/dist/topLevelInject.d.ts.map +1 -1
- package/dist/types/api/checkAllFirmwareRelease.d.ts +4 -0
- package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
- package/dist/types/api/deviceUpdateBootloader.d.ts +6 -0
- package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
- package/dist/types/api/export.d.ts +4 -1
- package/dist/types/api/export.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdate.d.ts +68 -0
- package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdateCapabilities.d.ts +9 -0
- package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +1 -0
- package/dist/types/api/firmwareUpdatePlan.d.ts +28 -0
- package/dist/types/api/firmwareUpdatePlan.d.ts.map +1 -0
- package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +42 -0
- package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +1 -0
- package/dist/types/api/index.d.ts +9 -1
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/settings.d.ts +14 -1
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesCompat.d.ts +1 -0
- package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/CheckAllFirmwareRelease.ts +61 -25
- package/src/api/FirmwareUpdate.ts +13 -1
- package/src/api/FirmwareUpdateV2.ts +296 -121
- package/src/api/FirmwareUpdateV3.ts +263 -57
- package/src/api/FirmwareUpdateV4.ts +865 -287
- package/src/api/device/DeviceUpdateBootloader.ts +125 -6
- package/src/api/firmware/FirmwareArtifactSource.ts +278 -0
- package/src/api/firmware/FirmwareHostBinding.ts +100 -0
- package/src/api/firmware/FirmwarePreparationError.ts +12 -0
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +72 -1
- package/src/api/firmware/FirmwareUpdateCapabilities.ts +9 -0
- package/src/api/firmware/FirmwareUpdatePlan.ts +772 -0
- package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +449 -0
- package/src/api/firmware/updateBootloader.ts +19 -4
- package/src/api/firmware/uploadFirmware.ts +140 -22
- package/src/api/helpers/protocolV2FileWrite.ts +4 -54
- package/src/data-manager/DataManager.ts +62 -54
- package/src/data-manager/connectSettings.ts +11 -0
- package/src/device/Device.ts +6 -1
- package/src/index.ts +5 -0
- package/src/inject.ts +22 -2
- package/src/lowLevelInject.ts +5 -1
- package/src/protocols/protocol-v2/resources.ts +1 -5
- package/src/topLevelInject.ts +5 -1
- package/src/types/api/checkAllFirmwareRelease.ts +4 -0
- package/src/types/api/deviceUpdateBootloader.ts +6 -0
- package/src/types/api/export.ts +18 -0
- package/src/types/api/firmwareUpdate.ts +75 -0
- package/src/types/api/firmwareUpdateCapabilities.ts +9 -0
- package/src/types/api/firmwareUpdatePlan.ts +38 -0
- package/src/types/api/firmwareUpdatePreparedPlan.ts +53 -0
- package/src/types/api/index.ts +14 -0
- package/src/types/settings.ts +14 -2
- package/src/utils/deviceFeaturesCompat.ts +6 -0
|
@@ -10,6 +10,7 @@ import { bytesToHex } from '../helpers/hexUtils';
|
|
|
10
10
|
import { DataManager } from '../../data-manager';
|
|
11
11
|
import { DevicePool } from '../../device/DevicePool';
|
|
12
12
|
import { buildProtocolV1FeaturesPayload } from '../../deviceProfile';
|
|
13
|
+
import { openFirmwareByteSource, readFirmwareByteSourceFully } from './FirmwareArtifactSource';
|
|
13
14
|
|
|
14
15
|
import type { KnownDevice } from '../../types';
|
|
15
16
|
import type { TypedCall, TypedResponseMessage } from '../../device/DeviceCommands';
|
|
@@ -17,6 +18,7 @@ import type { PROTO } from '../../constants';
|
|
|
17
18
|
import type { CoreMessage, IFirmwareUpdateProgressType } from '../../events';
|
|
18
19
|
import type { Success } from '@onekeyfe/hd-transport';
|
|
19
20
|
import type { Device } from '../../device/Device';
|
|
21
|
+
import type { FirmwareByteSource } from './FirmwareArtifactSource';
|
|
20
22
|
|
|
21
23
|
const NEW_BOOT_UPRATE_FIRMWARE_VERSION = '2.4.5';
|
|
22
24
|
const SESSION_ERROR = 'session not found';
|
|
@@ -88,17 +90,20 @@ export const waitBleInstall = async (updateType: string) => {
|
|
|
88
90
|
}
|
|
89
91
|
};
|
|
90
92
|
|
|
91
|
-
|
|
93
|
+
const toArrayBuffer = (payload: ArrayBuffer | Buffer): ArrayBuffer => {
|
|
94
|
+
if (payload instanceof ArrayBuffer) {
|
|
95
|
+
return payload;
|
|
96
|
+
}
|
|
97
|
+
return new Uint8Array(payload.buffer, payload.byteOffset, payload.byteLength).slice().buffer;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export const uploadFirmwareFromSource = async (
|
|
92
101
|
updateType: 'firmware' | 'ble',
|
|
93
102
|
typedCall: TypedCall,
|
|
94
103
|
postMessage: (message: CoreMessage) => void,
|
|
95
104
|
device: Device,
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
rebootOnSuccess,
|
|
99
|
-
}: PROTO.FirmwareUpload & {
|
|
100
|
-
rebootOnSuccess?: boolean;
|
|
101
|
-
},
|
|
105
|
+
source: FirmwareByteSource,
|
|
106
|
+
rebootOnSuccess?: boolean,
|
|
102
107
|
isUpdateBootloader?: boolean
|
|
103
108
|
) => {
|
|
104
109
|
const deviceType = device.getCurrentDeviceType();
|
|
@@ -119,20 +124,20 @@ export const uploadFirmware = async (
|
|
|
119
124
|
if (supportUpgradeFileHeader) {
|
|
120
125
|
// Extract and validate firmware header (first 1KB)
|
|
121
126
|
const HEADER_SIZE = 1024;
|
|
122
|
-
if (
|
|
127
|
+
if (source.size < HEADER_SIZE) {
|
|
123
128
|
throw ERRORS.TypedError(
|
|
124
129
|
HardwareErrorCode.RuntimeError,
|
|
125
|
-
`firmware payload too small: ${
|
|
130
|
+
`firmware payload too small: ${source.size} bytes, expected at least ${HEADER_SIZE} bytes`
|
|
126
131
|
);
|
|
127
132
|
}
|
|
128
133
|
|
|
129
134
|
Log.debug('Uploading firmware header:', {
|
|
130
135
|
size: HEADER_SIZE,
|
|
131
|
-
totalSize:
|
|
136
|
+
totalSize: source.size,
|
|
132
137
|
});
|
|
133
138
|
postProgressTip(device, 'UploadingFirmwareHeader', postMessage);
|
|
134
139
|
|
|
135
|
-
const header = new Uint8Array(
|
|
140
|
+
const header = new Uint8Array(await source.readAt(0, HEADER_SIZE));
|
|
136
141
|
|
|
137
142
|
try {
|
|
138
143
|
const headerRes = await typedCall('UpgradeFileHeader', 'Success', {
|
|
@@ -169,6 +174,9 @@ export const uploadFirmware = async (
|
|
|
169
174
|
postProgressMessage(device, 0, 'installingFirmware', postMessage);
|
|
170
175
|
let updateResponse: TypedResponseMessage<'Success'>;
|
|
171
176
|
try {
|
|
177
|
+
// Classic/Mini bootloaders accept one protobuf payload and do not expose
|
|
178
|
+
// a request-range protocol. Other device families remain fully streamed.
|
|
179
|
+
const payload = await readFirmwareByteSourceFully(source);
|
|
172
180
|
updateResponse = await typedCall('FirmwareUpload', 'Success', {
|
|
173
181
|
payload,
|
|
174
182
|
});
|
|
@@ -200,9 +208,7 @@ export const uploadFirmware = async (
|
|
|
200
208
|
updateType,
|
|
201
209
|
postMessage,
|
|
202
210
|
device,
|
|
203
|
-
|
|
204
|
-
payload,
|
|
205
|
-
},
|
|
211
|
+
source,
|
|
206
212
|
rebootOnSuccess
|
|
207
213
|
);
|
|
208
214
|
return response.message;
|
|
@@ -211,7 +217,7 @@ export const uploadFirmware = async (
|
|
|
211
217
|
|
|
212
218
|
postConfirmationMessage(device);
|
|
213
219
|
postProgressTip(device, 'ConfirmOnDevice', postMessage);
|
|
214
|
-
const length =
|
|
220
|
+
const length = source.size;
|
|
215
221
|
|
|
216
222
|
let response = await typedCall('FirmwareErase', ['FirmwareRequest', 'Success'], { length });
|
|
217
223
|
postProgressTip(device, 'FirmwareEraseSuccess', postMessage);
|
|
@@ -220,7 +226,7 @@ export const uploadFirmware = async (
|
|
|
220
226
|
const start = response.message.offset!;
|
|
221
227
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
222
228
|
const end = response.message.offset! + response.message.length!;
|
|
223
|
-
const chunk =
|
|
229
|
+
const chunk = await source.readAt(start, end - start);
|
|
224
230
|
|
|
225
231
|
if (start > 0) {
|
|
226
232
|
postProgressMessage(
|
|
@@ -249,11 +255,45 @@ export const uploadFirmware = async (
|
|
|
249
255
|
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'uploadFirmware: unknown device model');
|
|
250
256
|
};
|
|
251
257
|
|
|
258
|
+
export const uploadFirmware = async (
|
|
259
|
+
updateType: 'firmware' | 'ble',
|
|
260
|
+
typedCall: TypedCall,
|
|
261
|
+
postMessage: (message: CoreMessage) => void,
|
|
262
|
+
device: Device,
|
|
263
|
+
{
|
|
264
|
+
payload,
|
|
265
|
+
rebootOnSuccess,
|
|
266
|
+
}: PROTO.FirmwareUpload & {
|
|
267
|
+
rebootOnSuccess?: boolean;
|
|
268
|
+
},
|
|
269
|
+
isUpdateBootloader?: boolean
|
|
270
|
+
) => {
|
|
271
|
+
const source = await openFirmwareByteSource({
|
|
272
|
+
binary: toArrayBuffer(payload),
|
|
273
|
+
});
|
|
274
|
+
if (!source) {
|
|
275
|
+
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'firmware payload is empty');
|
|
276
|
+
}
|
|
277
|
+
try {
|
|
278
|
+
return await uploadFirmwareFromSource(
|
|
279
|
+
updateType,
|
|
280
|
+
typedCall,
|
|
281
|
+
postMessage,
|
|
282
|
+
device,
|
|
283
|
+
source,
|
|
284
|
+
rebootOnSuccess,
|
|
285
|
+
isUpdateBootloader
|
|
286
|
+
);
|
|
287
|
+
} finally {
|
|
288
|
+
await source.close();
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
|
|
252
292
|
const newTouchUpdateProcess = async (
|
|
253
293
|
updateType: 'firmware' | 'ble',
|
|
254
294
|
postMessage: (message: CoreMessage) => void,
|
|
255
295
|
device: Device,
|
|
256
|
-
|
|
296
|
+
source: FirmwareByteSource,
|
|
257
297
|
rebootOnSuccess = true
|
|
258
298
|
) => {
|
|
259
299
|
let typedCall = device.getCommands().typedCall.bind(device.getCommands());
|
|
@@ -263,13 +303,13 @@ const newTouchUpdateProcess = async (
|
|
|
263
303
|
const env = DataManager.getSettings('env');
|
|
264
304
|
const perPackageSize = DataManager.isBleConnect(env) ? 16 : 128;
|
|
265
305
|
const chunkSize = 1024 * perPackageSize;
|
|
266
|
-
const totalChunks = Math.ceil(
|
|
306
|
+
const totalChunks = Math.ceil(source.size / chunkSize);
|
|
267
307
|
let offset = 0;
|
|
268
308
|
for (let i = 0; i < totalChunks; i++) {
|
|
269
309
|
const chunkStart = i * chunkSize;
|
|
270
|
-
const chunkEnd = Math.min(chunkStart + chunkSize,
|
|
310
|
+
const chunkEnd = Math.min(chunkStart + chunkSize, source.size);
|
|
271
311
|
const chunkLength = chunkEnd - chunkStart;
|
|
272
|
-
const chunk =
|
|
312
|
+
const chunk = await source.readAt(chunkStart, chunkLength);
|
|
273
313
|
const overwrite = i === 0;
|
|
274
314
|
const progress = Math.round(((i + 1) / totalChunks) * 100);
|
|
275
315
|
const writeRes = await emmcFileWriteWithRetry(
|
|
@@ -503,6 +543,67 @@ export const updateResource = async (
|
|
|
503
543
|
return processResourceRequest(typedCall, res, data);
|
|
504
544
|
};
|
|
505
545
|
|
|
546
|
+
export const updateResourceFromSource = async (
|
|
547
|
+
typedCall: TypedCall,
|
|
548
|
+
fileName: string,
|
|
549
|
+
source: FirmwareByteSource,
|
|
550
|
+
onConfirmAfter?: () => void
|
|
551
|
+
) => {
|
|
552
|
+
const initialLength = Math.min(INIT_DATA_CHUNK_SIZE, source.size);
|
|
553
|
+
const initialChunk = new Uint8Array(await source.readAt(0, initialLength));
|
|
554
|
+
let response = await typedCall('ResourceUpdate', ['ResourceRequest', 'Success'], {
|
|
555
|
+
file_name: fileName,
|
|
556
|
+
data_length: source.size,
|
|
557
|
+
initial_data_chunk: bytesToHex(initialChunk),
|
|
558
|
+
hash: bytesToHex(blake2s(initialChunk)),
|
|
559
|
+
});
|
|
560
|
+
onConfirmAfter?.();
|
|
561
|
+
|
|
562
|
+
while (response.type !== 'Success') {
|
|
563
|
+
const { offset, data_length: dataLength } = response.message;
|
|
564
|
+
if (
|
|
565
|
+
!Number.isSafeInteger(offset) ||
|
|
566
|
+
offset === undefined ||
|
|
567
|
+
offset < 0 ||
|
|
568
|
+
!Number.isSafeInteger(dataLength) ||
|
|
569
|
+
dataLength === undefined ||
|
|
570
|
+
dataLength <= 0 ||
|
|
571
|
+
offset + dataLength > source.size
|
|
572
|
+
) {
|
|
573
|
+
throw ERRORS.TypedError(
|
|
574
|
+
HardwareErrorCode.RuntimeError,
|
|
575
|
+
'Device requested an invalid firmware resource range'
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
const chunk = new Uint8Array(await source.readAt(offset, dataLength));
|
|
579
|
+
response = await typedCall('ResourceAck', ['ResourceRequest', 'Success'], {
|
|
580
|
+
data_chunk: bytesToHex(chunk),
|
|
581
|
+
hash: bytesToHex(blake2s(chunk)),
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
return response.message;
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
export const updateResourcesFromSources = async (
|
|
588
|
+
typedCall: TypedCall,
|
|
589
|
+
postMessage: (message: CoreMessage) => void,
|
|
590
|
+
device: Device,
|
|
591
|
+
entries: ReadonlyArray<{ entryName: string; source: FirmwareByteSource }>
|
|
592
|
+
) => {
|
|
593
|
+
postProgressTip(device, 'UpdateSysResource', postMessage);
|
|
594
|
+
for (const [index, entry] of entries.entries()) {
|
|
595
|
+
await updateResourceFromSource(typedCall, entry.entryName, entry.source);
|
|
596
|
+
postProgressMessage(
|
|
597
|
+
device,
|
|
598
|
+
Math.floor(((index + 1) / entries.length) * 100),
|
|
599
|
+
'installingFirmware',
|
|
600
|
+
postMessage
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
postProgressTip(device, 'UpdateSysResourceSuccess', postMessage);
|
|
604
|
+
return true;
|
|
605
|
+
};
|
|
606
|
+
|
|
506
607
|
export const updateResources = async (
|
|
507
608
|
typedCall: TypedCall,
|
|
508
609
|
postMessage: (message: CoreMessage) => void,
|
|
@@ -537,11 +638,28 @@ export const updateBootloader = async (
|
|
|
537
638
|
typedCall: TypedCall,
|
|
538
639
|
postMessage: (message: CoreMessage) => void,
|
|
539
640
|
device: Device,
|
|
540
|
-
|
|
641
|
+
data: ArrayBuffer
|
|
642
|
+
) => {
|
|
643
|
+
const source = await openFirmwareByteSource({ binary: data });
|
|
644
|
+
if (!source) {
|
|
645
|
+
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'bootloader payload is empty');
|
|
646
|
+
}
|
|
647
|
+
try {
|
|
648
|
+
return await updateBootloaderFromSource(typedCall, postMessage, device, source);
|
|
649
|
+
} finally {
|
|
650
|
+
await source.close();
|
|
651
|
+
}
|
|
652
|
+
};
|
|
653
|
+
|
|
654
|
+
export const updateBootloaderFromSource = async (
|
|
655
|
+
typedCall: TypedCall,
|
|
656
|
+
postMessage: (message: CoreMessage) => void,
|
|
657
|
+
device: Device,
|
|
658
|
+
source: FirmwareByteSource
|
|
541
659
|
) => {
|
|
542
660
|
postProgressTip(device, 'UpdateBootloader', postMessage);
|
|
543
661
|
postProgressMessage(device, Math.floor(0), 'installingFirmware', postMessage);
|
|
544
|
-
await
|
|
662
|
+
await updateResourceFromSource(typedCall, 'bootloader.bin', source, () => {
|
|
545
663
|
postProcessingMessage('resource', postMessage);
|
|
546
664
|
});
|
|
547
665
|
postProgressMessage(device, Math.floor(100), 'installingFirmware', postMessage);
|
|
@@ -9,7 +9,7 @@ import { LoggerNames, getLogger } from '../../utils/logger';
|
|
|
9
9
|
|
|
10
10
|
import type { DeviceCommands } from '../../device/DeviceCommands';
|
|
11
11
|
|
|
12
|
-
const Log = getLogger(LoggerNames.
|
|
12
|
+
const Log = getLogger(LoggerNames.Method);
|
|
13
13
|
|
|
14
14
|
export type ProtocolV2FileWriteData = ArrayBuffer | Uint8Array | Blob | string;
|
|
15
15
|
|
|
@@ -69,36 +69,22 @@ function getFileTransferTransport() {
|
|
|
69
69
|
function logFileTransferMetrics({
|
|
70
70
|
transport,
|
|
71
71
|
status,
|
|
72
|
-
path,
|
|
73
72
|
transferredBytes,
|
|
74
73
|
totalBytes,
|
|
75
74
|
elapsedMs,
|
|
76
75
|
rateBytesPerSecond,
|
|
77
|
-
hostWriteElapsedMs,
|
|
78
|
-
responseWaitElapsedMs,
|
|
79
|
-
measuredAttempts,
|
|
80
76
|
}: {
|
|
81
77
|
transport: string;
|
|
82
78
|
status: 'progress' | 'completed' | 'failed';
|
|
83
|
-
path: string;
|
|
84
79
|
transferredBytes: number;
|
|
85
80
|
totalBytes: number;
|
|
86
81
|
elapsedMs: number;
|
|
87
82
|
rateBytesPerSecond: number;
|
|
88
|
-
hostWriteElapsedMs: number;
|
|
89
|
-
responseWaitElapsedMs: number;
|
|
90
|
-
measuredAttempts: number;
|
|
91
83
|
}) {
|
|
92
|
-
const segmentedMetrics =
|
|
93
|
-
measuredAttempts > 0
|
|
94
|
-
? ` hostWriteTotal=${(hostWriteElapsedMs / 1000).toFixed(2)}s responseWaitTotal=${(
|
|
95
|
-
responseWaitElapsedMs / 1000
|
|
96
|
-
).toFixed(2)}s measuredAttempts=${measuredAttempts}`
|
|
97
|
-
: '';
|
|
98
84
|
Log.log(
|
|
99
|
-
`[FileWrite] metrics transport=${transport} status=${status}
|
|
85
|
+
`[FileWrite] metrics transport=${transport} status=${status} bytes=${transferredBytes}/${totalBytes} elapsed=${(
|
|
100
86
|
elapsedMs / 1000
|
|
101
|
-
).toFixed(2)}s speed=${formatFileTransferRate(rateBytesPerSecond)} KiB/s
|
|
87
|
+
).toFixed(2)}s speed=${formatFileTransferRate(rateBytesPerSecond)} KiB/s`
|
|
102
88
|
);
|
|
103
89
|
}
|
|
104
90
|
|
|
@@ -191,13 +177,7 @@ export async function writeProtocolV2File(options: ProtocolV2FileWriteOptions) {
|
|
|
191
177
|
let lastConfirmedAt = startTime;
|
|
192
178
|
let logWindowStartedAt = startTime;
|
|
193
179
|
let logWindowStartedBytes = 0;
|
|
194
|
-
let hostWriteElapsedMs = 0;
|
|
195
|
-
let responseWaitElapsedMs = 0;
|
|
196
|
-
let measuredAttempts = 0;
|
|
197
180
|
const transport = getFileTransferTransport();
|
|
198
|
-
Log.log(
|
|
199
|
-
`[FileWrite] started transport=${transport} path=${options.path} bytes=${dataLength} offset=${startOffset} chunk=${chunkSize}`
|
|
200
|
-
);
|
|
201
181
|
|
|
202
182
|
try {
|
|
203
183
|
while (written < dataLength) {
|
|
@@ -223,25 +203,13 @@ export async function writeProtocolV2File(options: ProtocolV2FileWriteOptions) {
|
|
|
223
203
|
let response;
|
|
224
204
|
let isWritePending = true;
|
|
225
205
|
while (isWritePending) {
|
|
226
|
-
let currentHostWriteElapsedMs: number | undefined;
|
|
227
|
-
let responseWaitStartedAt: number | undefined;
|
|
228
206
|
try {
|
|
229
207
|
const callOptions =
|
|
230
208
|
options.writeWithResponse === undefined
|
|
231
|
-
? {
|
|
232
|
-
timeoutMs: options.timeoutMs,
|
|
233
|
-
onWriteCompleted: ({ elapsedMs }: { elapsedMs: number }) => {
|
|
234
|
-
currentHostWriteElapsedMs = elapsedMs;
|
|
235
|
-
responseWaitStartedAt = Date.now();
|
|
236
|
-
},
|
|
237
|
-
}
|
|
209
|
+
? { timeoutMs: options.timeoutMs }
|
|
238
210
|
: {
|
|
239
211
|
...(options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }),
|
|
240
212
|
writeWithResponse: options.writeWithResponse,
|
|
241
|
-
onWriteCompleted: ({ elapsedMs }: { elapsedMs: number }) => {
|
|
242
|
-
currentHostWriteElapsedMs = elapsedMs;
|
|
243
|
-
responseWaitStartedAt = Date.now();
|
|
244
|
-
},
|
|
245
213
|
};
|
|
246
214
|
response = await options.commands.typedCall(
|
|
247
215
|
'FilesystemFileWrite',
|
|
@@ -254,12 +222,6 @@ export async function writeProtocolV2File(options: ProtocolV2FileWriteOptions) {
|
|
|
254
222
|
if (retryCount >= maxChunkRetries || !isProtocolV2ResponseTimeout(error)) throw error;
|
|
255
223
|
retryCount += 1;
|
|
256
224
|
options.throwIfAborted?.();
|
|
257
|
-
} finally {
|
|
258
|
-
if (currentHostWriteElapsedMs !== undefined && responseWaitStartedAt !== undefined) {
|
|
259
|
-
hostWriteElapsedMs += currentHostWriteElapsedMs;
|
|
260
|
-
responseWaitElapsedMs += Math.max(Date.now() - responseWaitStartedAt, 0);
|
|
261
|
-
measuredAttempts += 1;
|
|
262
|
-
}
|
|
263
225
|
}
|
|
264
226
|
}
|
|
265
227
|
if (!response) {
|
|
@@ -323,14 +285,10 @@ export async function writeProtocolV2File(options: ProtocolV2FileWriteOptions) {
|
|
|
323
285
|
logFileTransferMetrics({
|
|
324
286
|
transport,
|
|
325
287
|
status: 'progress',
|
|
326
|
-
path: options.path,
|
|
327
288
|
transferredBytes,
|
|
328
289
|
totalBytes: dataLength,
|
|
329
290
|
elapsedMs,
|
|
330
291
|
rateBytesPerSecond: getAverageFileTransferRate(logWindowBytes, logWindowElapsedMs),
|
|
331
|
-
hostWriteElapsedMs,
|
|
332
|
-
responseWaitElapsedMs,
|
|
333
|
-
measuredAttempts,
|
|
334
292
|
});
|
|
335
293
|
logWindowStartedAt = now;
|
|
336
294
|
logWindowStartedBytes = transferredBytes;
|
|
@@ -349,14 +307,10 @@ export async function writeProtocolV2File(options: ProtocolV2FileWriteOptions) {
|
|
|
349
307
|
logFileTransferMetrics({
|
|
350
308
|
transport,
|
|
351
309
|
status: 'failed',
|
|
352
|
-
path: options.path,
|
|
353
310
|
transferredBytes: written,
|
|
354
311
|
totalBytes: dataLength,
|
|
355
312
|
elapsedMs,
|
|
356
313
|
rateBytesPerSecond: getAverageFileTransferRate(logWindowBytes, logWindowElapsedMs),
|
|
357
|
-
hostWriteElapsedMs,
|
|
358
|
-
responseWaitElapsedMs,
|
|
359
|
-
measuredAttempts,
|
|
360
314
|
});
|
|
361
315
|
throw error;
|
|
362
316
|
}
|
|
@@ -367,14 +321,10 @@ export async function writeProtocolV2File(options: ProtocolV2FileWriteOptions) {
|
|
|
367
321
|
logFileTransferMetrics({
|
|
368
322
|
transport,
|
|
369
323
|
status: 'completed',
|
|
370
|
-
path: options.path,
|
|
371
324
|
transferredBytes: written,
|
|
372
325
|
totalBytes: dataLength,
|
|
373
326
|
elapsedMs,
|
|
374
327
|
rateBytesPerSecond: getAverageFileTransferRate(logWindowBytes, logWindowElapsedMs),
|
|
375
|
-
hostWriteElapsedMs,
|
|
376
|
-
responseWaitElapsedMs,
|
|
377
|
-
measuredAttempts,
|
|
378
328
|
});
|
|
379
329
|
|
|
380
330
|
return {
|
|
@@ -408,7 +408,20 @@ export default class DataManager {
|
|
|
408
408
|
static async load(settings: ConnectSettings): Promise<boolean> {
|
|
409
409
|
this.settings = settings;
|
|
410
410
|
this.protocolV2ResourcesConfigError = undefined;
|
|
411
|
-
|
|
411
|
+
const manifestMode =
|
|
412
|
+
settings.firmwareManifestMode ?? (settings.fetchConfig ? 'sdk-managed' : undefined);
|
|
413
|
+
if (settings.preloadedConfig) {
|
|
414
|
+
this.applyRemoteConfig(settings.preloadedConfig);
|
|
415
|
+
Log.log('[DataConfig] Preloaded firmware config loaded');
|
|
416
|
+
return true;
|
|
417
|
+
}
|
|
418
|
+
if (manifestMode === 'external-only') {
|
|
419
|
+
Log.warn(
|
|
420
|
+
'[DataConfig] External firmware config is unavailable; SDK-managed networking remains disabled'
|
|
421
|
+
);
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
424
|
+
if (manifestMode !== 'sdk-managed') {
|
|
412
425
|
return false;
|
|
413
426
|
}
|
|
414
427
|
|
|
@@ -419,26 +432,19 @@ export default class DataManager {
|
|
|
419
432
|
const urlWithCache = `${url}?noCache=${getTimeStamp()}`;
|
|
420
433
|
let data: RemoteConfigResponse | null = null;
|
|
421
434
|
let fetchMethod: 'configFetcher' | 'axios' | 'none' = 'none';
|
|
422
|
-
|
|
423
|
-
// 1. Try custom configFetcher first (client-side IP direct connection support)
|
|
424
435
|
if (settings.configFetcher) {
|
|
425
|
-
Log.debug('[DataConfig] Trying
|
|
436
|
+
Log.debug('[DataConfig] Trying client configFetcher...');
|
|
426
437
|
try {
|
|
427
438
|
data = await settings.configFetcher(urlWithCache);
|
|
428
439
|
if (data) {
|
|
429
440
|
fetchMethod = 'configFetcher';
|
|
430
|
-
Log.log('[DataConfig] ConfigFetcher success');
|
|
431
|
-
} else {
|
|
432
|
-
Log.debug('[DataConfig] ConfigFetcher returned null, will fallback to axios');
|
|
433
441
|
}
|
|
434
442
|
} catch (e) {
|
|
435
|
-
Log.warn('[DataConfig]
|
|
443
|
+
Log.warn('[DataConfig] Client configFetcher failed:', e);
|
|
436
444
|
}
|
|
437
445
|
}
|
|
438
|
-
|
|
439
|
-
// 2. Fallback to default axios request
|
|
440
446
|
if (!data) {
|
|
441
|
-
Log.debug('[DataConfig] Trying
|
|
447
|
+
Log.debug('[DataConfig] Trying SDK-managed config request...');
|
|
442
448
|
try {
|
|
443
449
|
const response = await axios.get<RemoteConfigResponse>(urlWithCache, {
|
|
444
450
|
// because of iframe timeout is 10000
|
|
@@ -446,61 +452,63 @@ export default class DataManager {
|
|
|
446
452
|
});
|
|
447
453
|
data = response.data;
|
|
448
454
|
fetchMethod = 'axios';
|
|
449
|
-
Log.log('[DataConfig]
|
|
455
|
+
Log.log('[DataConfig] SDK-managed config request succeeded');
|
|
450
456
|
} catch (e) {
|
|
451
|
-
Log.warn('[DataConfig]
|
|
457
|
+
Log.warn('[DataConfig] SDK-managed config request failed:', e);
|
|
452
458
|
}
|
|
453
459
|
}
|
|
454
460
|
|
|
455
|
-
// 3. Apply config if available
|
|
456
461
|
if (data) {
|
|
457
|
-
|
|
458
|
-
let neoResources: IProtocolV2Resources | undefined;
|
|
459
|
-
this.protocolV2ResourcesConfigError = undefined;
|
|
460
|
-
try {
|
|
461
|
-
pro2Resources = parseProtocolV2Resources(
|
|
462
|
-
(data.pro2 as { resources?: unknown } | undefined)?.resources
|
|
463
|
-
);
|
|
464
|
-
neoResources = parseProtocolV2Resources(
|
|
465
|
-
(data.neo as { resources?: unknown } | undefined)?.resources
|
|
466
|
-
);
|
|
467
|
-
} catch (error) {
|
|
468
|
-
// Firmware resource metadata is not required for base communication. If the
|
|
469
|
-
// remote config is temporarily incomplete, disable this resource update only.
|
|
470
|
-
this.protocolV2ResourcesConfigError =
|
|
471
|
-
error instanceof Error ? error : new Error(String(error));
|
|
472
|
-
Log.warn('[DataConfig] Ignoring invalid Pro2 resources config:', error);
|
|
473
|
-
}
|
|
474
|
-
const enrichedPro2Config = this.enrichFirmwareReleaseInfo(data.pro2);
|
|
475
|
-
const enrichedNeoConfig = this.enrichFirmwareReleaseInfo(data.neo);
|
|
476
|
-
const { resources: _unvalidatedResources, ...pro2Config } = enrichedPro2Config;
|
|
477
|
-
const { resources: _unvalidatedNeoResources, ...neoConfig } = enrichedNeoConfig;
|
|
462
|
+
this.applyRemoteConfig(data);
|
|
478
463
|
Log.log(`[DataConfig] Config loaded successfully via [${fetchMethod}]`);
|
|
479
|
-
this.deviceMap = {
|
|
480
|
-
[EDeviceType.Classic]: this.enrichFirmwareReleaseInfo(data.classic),
|
|
481
|
-
[EDeviceType.Classic1s]: this.enrichFirmwareReleaseInfo(data.classic1s),
|
|
482
|
-
[EDeviceType.ClassicPure]: this.enrichFirmwareReleaseInfo(data.classicpure),
|
|
483
|
-
[EDeviceType.Mini]: this.enrichFirmwareReleaseInfo(data.mini),
|
|
484
|
-
[EDeviceType.Touch]: this.enrichFirmwareReleaseInfo(data.touch),
|
|
485
|
-
[EDeviceType.Pro]: this.enrichFirmwareReleaseInfo(data.pro),
|
|
486
|
-
[EDeviceType.Pro2]: {
|
|
487
|
-
...pro2Config,
|
|
488
|
-
...(pro2Resources ? { resources: pro2Resources } : undefined),
|
|
489
|
-
},
|
|
490
|
-
[EDeviceType.Neo]: {
|
|
491
|
-
...neoConfig,
|
|
492
|
-
...(neoResources ? { resources: neoResources } : undefined),
|
|
493
|
-
},
|
|
494
|
-
};
|
|
495
|
-
this.assets = {
|
|
496
|
-
bridge: data.bridge,
|
|
497
|
-
};
|
|
498
464
|
return true;
|
|
499
465
|
}
|
|
500
466
|
Log.warn('[DataConfig] All fetch methods failed, using built-in default config');
|
|
501
467
|
return false;
|
|
502
468
|
}
|
|
503
469
|
|
|
470
|
+
private static applyRemoteConfig(data: RemoteConfigResponse) {
|
|
471
|
+
let pro2Resources: IProtocolV2Resources | undefined;
|
|
472
|
+
let neoResources: IProtocolV2Resources | undefined;
|
|
473
|
+
try {
|
|
474
|
+
pro2Resources = parseProtocolV2Resources(
|
|
475
|
+
(data.pro2 as { resources?: unknown } | undefined)?.resources
|
|
476
|
+
);
|
|
477
|
+
neoResources = parseProtocolV2Resources(
|
|
478
|
+
(data.neo as { resources?: unknown } | undefined)?.resources
|
|
479
|
+
);
|
|
480
|
+
} catch (error) {
|
|
481
|
+
// Firmware resource metadata is not required for base communication. If the
|
|
482
|
+
// remote config is temporarily incomplete, disable this resource update only.
|
|
483
|
+
this.protocolV2ResourcesConfigError =
|
|
484
|
+
error instanceof Error ? error : new Error(String(error));
|
|
485
|
+
Log.warn('[DataConfig] Ignoring invalid Protocol V2 resources config:', error);
|
|
486
|
+
}
|
|
487
|
+
const enrichedPro2Config = this.enrichFirmwareReleaseInfo(data.pro2);
|
|
488
|
+
const enrichedNeoConfig = this.enrichFirmwareReleaseInfo(data.neo);
|
|
489
|
+
const { resources: _unvalidatedResources, ...pro2Config } = enrichedPro2Config;
|
|
490
|
+
const { resources: _unvalidatedNeoResources, ...neoConfig } = enrichedNeoConfig;
|
|
491
|
+
this.deviceMap = {
|
|
492
|
+
[EDeviceType.Classic]: this.enrichFirmwareReleaseInfo(data.classic),
|
|
493
|
+
[EDeviceType.Classic1s]: this.enrichFirmwareReleaseInfo(data.classic1s),
|
|
494
|
+
[EDeviceType.ClassicPure]: this.enrichFirmwareReleaseInfo(data.classicpure),
|
|
495
|
+
[EDeviceType.Mini]: this.enrichFirmwareReleaseInfo(data.mini),
|
|
496
|
+
[EDeviceType.Touch]: this.enrichFirmwareReleaseInfo(data.touch),
|
|
497
|
+
[EDeviceType.Pro]: this.enrichFirmwareReleaseInfo(data.pro),
|
|
498
|
+
[EDeviceType.Pro2]: {
|
|
499
|
+
...pro2Config,
|
|
500
|
+
...(pro2Resources ? { resources: pro2Resources } : undefined),
|
|
501
|
+
},
|
|
502
|
+
[EDeviceType.Neo]: {
|
|
503
|
+
...neoConfig,
|
|
504
|
+
...(neoResources ? { resources: neoResources } : undefined),
|
|
505
|
+
},
|
|
506
|
+
};
|
|
507
|
+
this.assets = {
|
|
508
|
+
bridge: data.bridge,
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
|
|
504
512
|
static updateEnv(newEnv: ConnectSettings['env']) {
|
|
505
513
|
if (this.settings) {
|
|
506
514
|
const prevEnv = this.settings.env;
|
|
@@ -115,10 +115,21 @@ export const parseConnectSettings = (input: Partial<ConnectSettings> = {}) => {
|
|
|
115
115
|
settings.fetchConfig = input.fetchConfig;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
if (
|
|
119
|
+
input.firmwareManifestMode === 'sdk-managed' ||
|
|
120
|
+
input.firmwareManifestMode === 'external-only'
|
|
121
|
+
) {
|
|
122
|
+
settings.firmwareManifestMode = input.firmwareManifestMode;
|
|
123
|
+
}
|
|
124
|
+
|
|
118
125
|
if (input.configFetcher) {
|
|
119
126
|
settings.configFetcher = input.configFetcher;
|
|
120
127
|
}
|
|
121
128
|
|
|
129
|
+
if (input.preloadedConfig) {
|
|
130
|
+
settings.preloadedConfig = input.preloadedConfig;
|
|
131
|
+
}
|
|
132
|
+
|
|
122
133
|
return settings;
|
|
123
134
|
};
|
|
124
135
|
|
package/src/device/Device.ts
CHANGED
|
@@ -99,6 +99,11 @@ export type InitOptions = {
|
|
|
99
99
|
protocolV2DeviceInfoTimeoutMs?: number;
|
|
100
100
|
/** Refresh Protocol V2 runtime state before returning discovery results. */
|
|
101
101
|
refreshRuntimeState?: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Protocol V1 Initialize response timeout override. Reboot-wait polling passes a
|
|
104
|
+
* short value so an unanswered probe settles before the next poll tick.
|
|
105
|
+
*/
|
|
106
|
+
timeoutMs?: number;
|
|
102
107
|
};
|
|
103
108
|
|
|
104
109
|
export type RunOptions = {
|
|
@@ -908,7 +913,7 @@ export class Device extends EventEmitter {
|
|
|
908
913
|
const initStartAt = Date.now();
|
|
909
914
|
const { message } = await this.commands.typedCall('Initialize', 'Features', payload, {
|
|
910
915
|
// iOS BLE bound devices can need close to 20 seconds.
|
|
911
|
-
timeoutMs: 25 * 1000,
|
|
916
|
+
timeoutMs: options?.timeoutMs ?? 25 * 1000,
|
|
912
917
|
});
|
|
913
918
|
this.setLastInitializeDuration(Date.now() - initStartAt);
|
|
914
919
|
this._updateFeatures(message, initSession);
|
package/src/index.ts
CHANGED
|
@@ -19,6 +19,11 @@ export * from './types';
|
|
|
19
19
|
export { whitelist, whitelistExtension } from './data/config';
|
|
20
20
|
export { executeCallback, cleanupCallback };
|
|
21
21
|
export { preloadSessionCache } from './device/Device';
|
|
22
|
+
export {
|
|
23
|
+
getFirmwareUpdateHostBindingGeneration,
|
|
24
|
+
registerFirmwareUpdateHostBinding,
|
|
25
|
+
unregisterFirmwareUpdateHostBinding,
|
|
26
|
+
} from './api/firmware/FirmwareHostBinding';
|
|
22
27
|
|
|
23
28
|
const HardwareSdk = ({
|
|
24
29
|
init,
|
package/src/inject.ts
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
|
+
import { getFirmwareUpdateCapabilities } from './api/firmware/FirmwareUpdateCapabilities';
|
|
2
|
+
import {
|
|
3
|
+
prepareFirmwareUpdatePlan,
|
|
4
|
+
validateFirmwareUpdatePreparedPlan,
|
|
5
|
+
} from './api/firmware/FirmwareUpdatePreparedPlan';
|
|
6
|
+
import {
|
|
7
|
+
getFirmwareUpdateHostBindingGeneration,
|
|
8
|
+
registerFirmwareUpdateHostBinding,
|
|
9
|
+
unregisterFirmwareUpdateHostBinding,
|
|
10
|
+
} from './api/firmware/FirmwareHostBinding';
|
|
11
|
+
|
|
1
12
|
import type { HardwareConnectProtocol } from '@onekeyfe/hd-shared';
|
|
2
|
-
import type { Unsuccessful } from './types';
|
|
3
13
|
import type { EventEmitter } from 'events';
|
|
4
14
|
import type { CallMethod } from './events';
|
|
15
|
+
import type { Unsuccessful } from './types';
|
|
5
16
|
import type { CoreApi } from './types/api';
|
|
6
17
|
import type { AllNetworkAddress } from './types/api/allNetworkGetAddress';
|
|
7
18
|
|
|
@@ -116,7 +127,10 @@ export const inject = ({
|
|
|
116
127
|
|
|
117
128
|
setDeviceConnectProtocol: protocolAwareCall.setDeviceConnectProtocol,
|
|
118
129
|
|
|
119
|
-
dispose
|
|
130
|
+
dispose: async () => {
|
|
131
|
+
unregisterFirmwareUpdateHostBinding();
|
|
132
|
+
await dispose();
|
|
133
|
+
},
|
|
120
134
|
|
|
121
135
|
uiResponse,
|
|
122
136
|
|
|
@@ -148,6 +162,12 @@ export const createCoreApi = (
|
|
|
148
162
|
| 'updateSettings'
|
|
149
163
|
| 'switchTransport'
|
|
150
164
|
> => ({
|
|
165
|
+
getFirmwareUpdateCapabilities,
|
|
166
|
+
registerFirmwareUpdateHostBinding,
|
|
167
|
+
unregisterFirmwareUpdateHostBinding,
|
|
168
|
+
getFirmwareUpdateHostBindingGeneration,
|
|
169
|
+
prepareFirmwareUpdatePlan,
|
|
170
|
+
validateFirmwareUpdatePreparedPlan,
|
|
151
171
|
getLogs: () => call({ method: 'getLogs' }),
|
|
152
172
|
clearSessionCache: params => call({ ...params, method: 'clearSessionCache' }),
|
|
153
173
|
/**
|