@onekeyfe/hd-core 1.2.0-alpha.23 → 1.2.0-alpha.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/DeviceCommands.test.ts +35 -0
- package/__tests__/connectSettings.test.ts +5 -47
- package/__tests__/device-lifecycle-events.test.ts +59 -0
- package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +30 -39
- package/__tests__/open-wallet-session.test.ts +192 -129
- package/__tests__/protocol-v2-ping.test.ts +28 -0
- package/__tests__/protocol-v2.test.ts +338 -376
- package/__tests__/public-pro2-api-boundary.test.ts +22 -0
- package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV2.d.ts +0 -11
- package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV3.d.ts +3 -7
- package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +5 -21
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/OpenWalletSession.d.ts +1 -8
- package/dist/api/OpenWalletSession.d.ts.map +1 -1
- package/dist/api/device/DeviceUpdateBootloader.d.ts +1 -5
- package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +0 -7
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
- package/dist/api/firmware/getBinary.d.ts +0 -7
- package/dist/api/firmware/getBinary.d.ts.map +1 -1
- package/dist/api/firmware/updateBootloader.d.ts +0 -2
- package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
- package/dist/api/firmware/uploadFirmware.d.ts +1 -9
- package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/protocol-v2/Ping.d.ts +2 -0
- package/dist/api/protocol-v2/Ping.d.ts.map +1 -1
- package/dist/api/stellar/StellarSignTransaction.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/data-manager/DataManager.d.ts +0 -1
- 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.map +1 -1
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/index.d.ts +89 -273
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +783 -2443
- package/dist/inject.d.ts.map +1 -1
- package/dist/lowLevelInject.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockRetry.d.ts +2 -3
- package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts +12 -0
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/topLevelInject.d.ts.map +1 -1
- package/dist/types/api/checkAllFirmwareRelease.d.ts +0 -3
- package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
- package/dist/types/api/deviceUpdateBootloader.d.ts +0 -6
- package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
- package/dist/types/api/export.d.ts +1 -4
- package/dist/types/api/export.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdate.d.ts +0 -69
- package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
- package/dist/types/api/index.d.ts +4 -10
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/api/openWalletSession.d.ts +0 -12
- package/dist/types/api/openWalletSession.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +4 -0
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/settings.d.ts +0 -13
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/patch.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/CheckAllFirmwareRelease.ts +3 -23
- package/src/api/FirmwareUpdateV2.ts +120 -292
- package/src/api/FirmwareUpdateV3.ts +55 -256
- package/src/api/FirmwareUpdateV4.ts +297 -757
- package/src/api/OpenWalletSession.ts +23 -86
- package/src/api/device/DeviceUpdateBootloader.ts +6 -122
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +0 -50
- package/src/api/firmware/updateBootloader.ts +4 -19
- package/src/api/firmware/uploadFirmware.ts +22 -140
- package/src/api/index.ts +1 -0
- package/src/api/protocol-v2/Ping.ts +32 -1
- package/src/core/index.ts +39 -7
- package/src/data/messages/messages-protocol-v2.json +33 -1
- package/src/data-manager/DataManager.ts +33 -33
- package/src/data-manager/connectSettings.ts +0 -11
- package/src/device/Device.ts +7 -9
- package/src/device/DeviceCommands.ts +19 -0
- package/src/index.ts +0 -5
- package/src/inject.ts +4 -22
- package/src/lowLevelInject.ts +1 -5
- package/src/protocols/protocol-v2/unlockRetry.ts +20 -3
- package/src/protocols/protocol-v2/walletSession.ts +26 -8
- package/src/topLevelInject.ts +1 -5
- package/src/types/api/checkAllFirmwareRelease.ts +0 -3
- package/src/types/api/deviceUpdateBootloader.ts +0 -6
- package/src/types/api/export.ts +0 -18
- package/src/types/api/firmwareUpdate.ts +0 -76
- package/src/types/api/index.ts +3 -14
- package/src/types/api/openWalletSession.ts +0 -19
- package/src/types/api/protocolV2.ts +10 -0
- package/src/types/settings.ts +0 -13
- package/src/utils/deviceInfoUtils.ts +15 -3
- package/__tests__/firmware-update/check-all-firmware-release.test.ts +0 -86
- package/__tests__/firmware-update/firmware-artifact-source.test.ts +0 -119
- package/__tests__/firmware-update/firmware-host-binding.test.ts +0 -70
- package/__tests__/firmware-update/firmware-update-capabilities.test.ts +0 -13
- package/__tests__/firmware-update/firmware-update-plan.test.ts +0 -326
- package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +0 -213
- package/__tests__/firmware-update/firmware-upload-source.test.ts +0 -70
- package/dist/api/firmware/FirmwareArtifactSource.d.ts +0 -25
- package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +0 -1
- package/dist/api/firmware/FirmwareHostBinding.d.ts +0 -6
- package/dist/api/firmware/FirmwareHostBinding.d.ts.map +0 -1
- package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +0 -3
- package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +0 -1
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts +0 -23
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +0 -1
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +0 -30
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +0 -1
- package/dist/types/api/firmwareUpdateCapabilities.d.ts +0 -9
- package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +0 -1
- package/dist/types/api/firmwareUpdatePlan.d.ts +0 -27
- package/dist/types/api/firmwareUpdatePlan.d.ts.map +0 -1
- package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +0 -42
- package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +0 -1
- package/src/api/firmware/FirmwareArtifactSource.ts +0 -278
- package/src/api/firmware/FirmwareHostBinding.ts +0 -100
- package/src/api/firmware/FirmwareUpdateCapabilities.ts +0 -9
- package/src/api/firmware/FirmwareUpdatePlan.ts +0 -621
- package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +0 -392
- package/src/types/api/firmwareUpdateCapabilities.ts +0 -9
- package/src/types/api/firmwareUpdatePlan.ts +0 -36
- package/src/types/api/firmwareUpdatePreparedPlan.ts +0 -53
|
@@ -10,7 +10,6 @@ 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';
|
|
14
13
|
|
|
15
14
|
import type { KnownDevice } from '../../types';
|
|
16
15
|
import type { TypedCall, TypedResponseMessage } from '../../device/DeviceCommands';
|
|
@@ -18,7 +17,6 @@ import type { PROTO } from '../../constants';
|
|
|
18
17
|
import type { CoreMessage, IFirmwareUpdateProgressType } from '../../events';
|
|
19
18
|
import type { Success } from '@onekeyfe/hd-transport';
|
|
20
19
|
import type { Device } from '../../device/Device';
|
|
21
|
-
import type { FirmwareByteSource } from './FirmwareArtifactSource';
|
|
22
20
|
|
|
23
21
|
const NEW_BOOT_UPRATE_FIRMWARE_VERSION = '2.4.5';
|
|
24
22
|
const SESSION_ERROR = 'session not found';
|
|
@@ -90,20 +88,17 @@ export const waitBleInstall = async (updateType: string) => {
|
|
|
90
88
|
}
|
|
91
89
|
};
|
|
92
90
|
|
|
93
|
-
const
|
|
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 (
|
|
91
|
+
export const uploadFirmware = async (
|
|
101
92
|
updateType: 'firmware' | 'ble',
|
|
102
93
|
typedCall: TypedCall,
|
|
103
94
|
postMessage: (message: CoreMessage) => void,
|
|
104
95
|
device: Device,
|
|
105
|
-
|
|
106
|
-
|
|
96
|
+
{
|
|
97
|
+
payload,
|
|
98
|
+
rebootOnSuccess,
|
|
99
|
+
}: PROTO.FirmwareUpload & {
|
|
100
|
+
rebootOnSuccess?: boolean;
|
|
101
|
+
},
|
|
107
102
|
isUpdateBootloader?: boolean
|
|
108
103
|
) => {
|
|
109
104
|
const deviceType = device.getCurrentDeviceType();
|
|
@@ -124,20 +119,20 @@ export const uploadFirmwareFromSource = async (
|
|
|
124
119
|
if (supportUpgradeFileHeader) {
|
|
125
120
|
// Extract and validate firmware header (first 1KB)
|
|
126
121
|
const HEADER_SIZE = 1024;
|
|
127
|
-
if (
|
|
122
|
+
if (payload.byteLength < HEADER_SIZE) {
|
|
128
123
|
throw ERRORS.TypedError(
|
|
129
124
|
HardwareErrorCode.RuntimeError,
|
|
130
|
-
`firmware payload too small: ${
|
|
125
|
+
`firmware payload too small: ${payload.byteLength} bytes, expected at least ${HEADER_SIZE} bytes`
|
|
131
126
|
);
|
|
132
127
|
}
|
|
133
128
|
|
|
134
129
|
Log.debug('Uploading firmware header:', {
|
|
135
130
|
size: HEADER_SIZE,
|
|
136
|
-
totalSize:
|
|
131
|
+
totalSize: payload.byteLength,
|
|
137
132
|
});
|
|
138
133
|
postProgressTip(device, 'UploadingFirmwareHeader', postMessage);
|
|
139
134
|
|
|
140
|
-
const header = new Uint8Array(
|
|
135
|
+
const header = new Uint8Array(payload.slice(0, HEADER_SIZE));
|
|
141
136
|
|
|
142
137
|
try {
|
|
143
138
|
const headerRes = await typedCall('UpgradeFileHeader', 'Success', {
|
|
@@ -174,9 +169,6 @@ export const uploadFirmwareFromSource = async (
|
|
|
174
169
|
postProgressMessage(device, 0, 'installingFirmware', postMessage);
|
|
175
170
|
let updateResponse: TypedResponseMessage<'Success'>;
|
|
176
171
|
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);
|
|
180
172
|
updateResponse = await typedCall('FirmwareUpload', 'Success', {
|
|
181
173
|
payload,
|
|
182
174
|
});
|
|
@@ -208,7 +200,9 @@ export const uploadFirmwareFromSource = async (
|
|
|
208
200
|
updateType,
|
|
209
201
|
postMessage,
|
|
210
202
|
device,
|
|
211
|
-
|
|
203
|
+
{
|
|
204
|
+
payload,
|
|
205
|
+
},
|
|
212
206
|
rebootOnSuccess
|
|
213
207
|
);
|
|
214
208
|
return response.message;
|
|
@@ -217,7 +211,7 @@ export const uploadFirmwareFromSource = async (
|
|
|
217
211
|
|
|
218
212
|
postConfirmationMessage(device);
|
|
219
213
|
postProgressTip(device, 'ConfirmOnDevice', postMessage);
|
|
220
|
-
const length =
|
|
214
|
+
const length = payload.byteLength;
|
|
221
215
|
|
|
222
216
|
let response = await typedCall('FirmwareErase', ['FirmwareRequest', 'Success'], { length });
|
|
223
217
|
postProgressTip(device, 'FirmwareEraseSuccess', postMessage);
|
|
@@ -226,7 +220,7 @@ export const uploadFirmwareFromSource = async (
|
|
|
226
220
|
const start = response.message.offset!;
|
|
227
221
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
228
222
|
const end = response.message.offset! + response.message.length!;
|
|
229
|
-
const chunk =
|
|
223
|
+
const chunk = payload.slice(start, end);
|
|
230
224
|
|
|
231
225
|
if (start > 0) {
|
|
232
226
|
postProgressMessage(
|
|
@@ -255,45 +249,11 @@ export const uploadFirmwareFromSource = async (
|
|
|
255
249
|
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'uploadFirmware: unknown device model');
|
|
256
250
|
};
|
|
257
251
|
|
|
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
|
-
|
|
292
252
|
const newTouchUpdateProcess = async (
|
|
293
253
|
updateType: 'firmware' | 'ble',
|
|
294
254
|
postMessage: (message: CoreMessage) => void,
|
|
295
255
|
device: Device,
|
|
296
|
-
|
|
256
|
+
{ payload }: PROTO.FirmwareUpload,
|
|
297
257
|
rebootOnSuccess = true
|
|
298
258
|
) => {
|
|
299
259
|
let typedCall = device.getCommands().typedCall.bind(device.getCommands());
|
|
@@ -303,13 +263,13 @@ const newTouchUpdateProcess = async (
|
|
|
303
263
|
const env = DataManager.getSettings('env');
|
|
304
264
|
const perPackageSize = DataManager.isBleConnect(env) ? 16 : 128;
|
|
305
265
|
const chunkSize = 1024 * perPackageSize;
|
|
306
|
-
const totalChunks = Math.ceil(
|
|
266
|
+
const totalChunks = Math.ceil(payload.byteLength / chunkSize);
|
|
307
267
|
let offset = 0;
|
|
308
268
|
for (let i = 0; i < totalChunks; i++) {
|
|
309
269
|
const chunkStart = i * chunkSize;
|
|
310
|
-
const chunkEnd = Math.min(chunkStart + chunkSize,
|
|
270
|
+
const chunkEnd = Math.min(chunkStart + chunkSize, payload.byteLength);
|
|
311
271
|
const chunkLength = chunkEnd - chunkStart;
|
|
312
|
-
const chunk =
|
|
272
|
+
const chunk = payload.slice(chunkStart, chunkEnd);
|
|
313
273
|
const overwrite = i === 0;
|
|
314
274
|
const progress = Math.round(((i + 1) / totalChunks) * 100);
|
|
315
275
|
const writeRes = await emmcFileWriteWithRetry(
|
|
@@ -530,67 +490,6 @@ export const updateResource = async (
|
|
|
530
490
|
return processResourceRequest(typedCall, res, data);
|
|
531
491
|
};
|
|
532
492
|
|
|
533
|
-
export const updateResourceFromSource = async (
|
|
534
|
-
typedCall: TypedCall,
|
|
535
|
-
fileName: string,
|
|
536
|
-
source: FirmwareByteSource,
|
|
537
|
-
onConfirmAfter?: () => void
|
|
538
|
-
) => {
|
|
539
|
-
const initialLength = Math.min(INIT_DATA_CHUNK_SIZE, source.size);
|
|
540
|
-
const initialChunk = new Uint8Array(await source.readAt(0, initialLength));
|
|
541
|
-
let response = await typedCall('ResourceUpdate', ['ResourceRequest', 'Success'], {
|
|
542
|
-
file_name: fileName,
|
|
543
|
-
data_length: source.size,
|
|
544
|
-
initial_data_chunk: bytesToHex(initialChunk),
|
|
545
|
-
hash: bytesToHex(blake2s(initialChunk)),
|
|
546
|
-
});
|
|
547
|
-
onConfirmAfter?.();
|
|
548
|
-
|
|
549
|
-
while (response.type !== 'Success') {
|
|
550
|
-
const { offset, data_length: dataLength } = response.message;
|
|
551
|
-
if (
|
|
552
|
-
!Number.isSafeInteger(offset) ||
|
|
553
|
-
offset === undefined ||
|
|
554
|
-
offset < 0 ||
|
|
555
|
-
!Number.isSafeInteger(dataLength) ||
|
|
556
|
-
dataLength === undefined ||
|
|
557
|
-
dataLength <= 0 ||
|
|
558
|
-
offset + dataLength > source.size
|
|
559
|
-
) {
|
|
560
|
-
throw ERRORS.TypedError(
|
|
561
|
-
HardwareErrorCode.RuntimeError,
|
|
562
|
-
'Device requested an invalid firmware resource range'
|
|
563
|
-
);
|
|
564
|
-
}
|
|
565
|
-
const chunk = new Uint8Array(await source.readAt(offset, dataLength));
|
|
566
|
-
response = await typedCall('ResourceAck', ['ResourceRequest', 'Success'], {
|
|
567
|
-
data_chunk: bytesToHex(chunk),
|
|
568
|
-
hash: bytesToHex(blake2s(chunk)),
|
|
569
|
-
});
|
|
570
|
-
}
|
|
571
|
-
return response.message;
|
|
572
|
-
};
|
|
573
|
-
|
|
574
|
-
export const updateResourcesFromSources = async (
|
|
575
|
-
typedCall: TypedCall,
|
|
576
|
-
postMessage: (message: CoreMessage) => void,
|
|
577
|
-
device: Device,
|
|
578
|
-
entries: ReadonlyArray<{ entryName: string; source: FirmwareByteSource }>
|
|
579
|
-
) => {
|
|
580
|
-
postProgressTip(device, 'UpdateSysResource', postMessage);
|
|
581
|
-
for (const [index, entry] of entries.entries()) {
|
|
582
|
-
await updateResourceFromSource(typedCall, entry.entryName, entry.source);
|
|
583
|
-
postProgressMessage(
|
|
584
|
-
device,
|
|
585
|
-
Math.floor(((index + 1) / entries.length) * 100),
|
|
586
|
-
'installingFirmware',
|
|
587
|
-
postMessage
|
|
588
|
-
);
|
|
589
|
-
}
|
|
590
|
-
postProgressTip(device, 'UpdateSysResourceSuccess', postMessage);
|
|
591
|
-
return true;
|
|
592
|
-
};
|
|
593
|
-
|
|
594
493
|
export const updateResources = async (
|
|
595
494
|
typedCall: TypedCall,
|
|
596
495
|
postMessage: (message: CoreMessage) => void,
|
|
@@ -625,28 +524,11 @@ export const updateBootloader = async (
|
|
|
625
524
|
typedCall: TypedCall,
|
|
626
525
|
postMessage: (message: CoreMessage) => void,
|
|
627
526
|
device: Device,
|
|
628
|
-
|
|
629
|
-
) => {
|
|
630
|
-
const source = await openFirmwareByteSource({ binary: data });
|
|
631
|
-
if (!source) {
|
|
632
|
-
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'bootloader payload is empty');
|
|
633
|
-
}
|
|
634
|
-
try {
|
|
635
|
-
return await updateBootloaderFromSource(typedCall, postMessage, device, source);
|
|
636
|
-
} finally {
|
|
637
|
-
await source.close();
|
|
638
|
-
}
|
|
639
|
-
};
|
|
640
|
-
|
|
641
|
-
export const updateBootloaderFromSource = async (
|
|
642
|
-
typedCall: TypedCall,
|
|
643
|
-
postMessage: (message: CoreMessage) => void,
|
|
644
|
-
device: Device,
|
|
645
|
-
source: FirmwareByteSource
|
|
527
|
+
source: ArrayBuffer
|
|
646
528
|
) => {
|
|
647
529
|
postProgressTip(device, 'UpdateBootloader', postMessage);
|
|
648
530
|
postProgressMessage(device, Math.floor(0), 'installingFirmware', postMessage);
|
|
649
|
-
await
|
|
531
|
+
await updateResource(typedCall, 'bootloader.bin', source, () => {
|
|
650
532
|
postProcessingMessage('resource', postMessage);
|
|
651
533
|
});
|
|
652
534
|
postProgressMessage(device, Math.floor(100), 'installingFirmware', postMessage);
|
package/src/api/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { default as testInitializeDeviceDuration } from './test/TestInitializeDeviceDuration';
|
|
2
|
+
export { default as testProtocolV2Ping } from './protocol-v2/Ping';
|
|
2
3
|
export { default as preInitialize } from './device/PreInitialize';
|
|
3
4
|
|
|
4
5
|
export { default as searchDevices } from './SearchDevices';
|
|
@@ -1,12 +1,43 @@
|
|
|
1
|
+
import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
+
|
|
1
3
|
import { BaseMethod } from '../BaseMethod';
|
|
2
4
|
|
|
5
|
+
// nanopb max_size:64 includes the terminating NUL for string fields.
|
|
6
|
+
export const PROTOCOL_V2_PING_MAX_MESSAGE_BYTES = 63;
|
|
7
|
+
|
|
8
|
+
const getUtf8ByteLength = (value: string) =>
|
|
9
|
+
Array.from(value).reduce((length, character) => {
|
|
10
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
11
|
+
if (codePoint <= 0x7f) return length + 1;
|
|
12
|
+
if (codePoint <= 0x7ff) return length + 2;
|
|
13
|
+
if (codePoint <= 0xffff) return length + 3;
|
|
14
|
+
return length + 4;
|
|
15
|
+
}, 0);
|
|
16
|
+
|
|
17
|
+
export function validateProtocolV2PingMessage(value: unknown): string {
|
|
18
|
+
if (value !== undefined && typeof value !== 'string') {
|
|
19
|
+
throw ERRORS.TypedError(
|
|
20
|
+
HardwareErrorCode.CallMethodInvalidParameter,
|
|
21
|
+
'Protocol V2 Ping message must be a string.'
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
const message = value ?? '';
|
|
25
|
+
if (getUtf8ByteLength(message) > PROTOCOL_V2_PING_MAX_MESSAGE_BYTES) {
|
|
26
|
+
throw ERRORS.TypedError(
|
|
27
|
+
HardwareErrorCode.CallMethodInvalidParameter,
|
|
28
|
+
`Protocol V2 Ping message must not exceed ${PROTOCOL_V2_PING_MAX_MESSAGE_BYTES} UTF-8 bytes.`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
return message;
|
|
32
|
+
}
|
|
33
|
+
|
|
3
34
|
export default class Ping extends BaseMethod<{ message?: string }> {
|
|
4
35
|
init() {
|
|
5
36
|
// Protocol V2 (Pro2) only; Core rejects non-V2 devices.
|
|
6
37
|
this.requireProtocolV2 = true;
|
|
7
38
|
this.skipForceUpdateCheck = true;
|
|
8
39
|
this.useDevicePassphraseState = false;
|
|
9
|
-
this.params = { message: this.payload.message };
|
|
40
|
+
this.params = { message: validateProtocolV2PingMessage(this.payload.message) };
|
|
10
41
|
}
|
|
11
42
|
|
|
12
43
|
async run() {
|
package/src/core/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import semver from 'semver';
|
|
2
2
|
import EventEmitter from 'events';
|
|
3
|
+
import { TRANSPORT_EVENT } from '@onekeyfe/hd-transport';
|
|
3
4
|
import {
|
|
4
5
|
ERRORS,
|
|
5
6
|
ERROR_CODES_REQUIRE_RELEASE,
|
|
@@ -70,7 +71,11 @@ import type { CoreMessage, IFrameCallMessage, UiPromise, UiPromiseResponse } fro
|
|
|
70
71
|
import type { DeviceEvents, InitOptions, RunOptions } from '../device/Device';
|
|
71
72
|
import type { SdkTracingContext } from '../utils/tracing';
|
|
72
73
|
import type { Deferred } from '@onekeyfe/hd-shared';
|
|
73
|
-
import type {
|
|
74
|
+
import type {
|
|
75
|
+
LowlevelTransportSharedPlugin,
|
|
76
|
+
OneKeyDeviceInfo,
|
|
77
|
+
TransportDeviceDisconnectEvent,
|
|
78
|
+
} from '@onekeyfe/hd-transport';
|
|
74
79
|
import type { BaseMethod } from '../api/BaseMethod';
|
|
75
80
|
|
|
76
81
|
const Log = getLogger(LoggerNames.Core);
|
|
@@ -173,8 +178,6 @@ export const callAPI = async (context: CoreContext, message: CoreMessage) => {
|
|
|
173
178
|
return createResponseMessage(method?.responseID ?? message.id, false, { error });
|
|
174
179
|
}
|
|
175
180
|
|
|
176
|
-
DevicePool.emitter.on(DEVICE.CONNECT, onDeviceConnectHandler);
|
|
177
|
-
|
|
178
181
|
if (!method.useDevice) {
|
|
179
182
|
updateMethodRequestContext(method, { status: 'running' });
|
|
180
183
|
try {
|
|
@@ -236,7 +239,6 @@ const handlePreWarmSignal = async (
|
|
|
236
239
|
method: BaseMethod
|
|
237
240
|
): Promise<any> => {
|
|
238
241
|
const createAckResponse = () => {
|
|
239
|
-
DevicePool.emitter.removeListener(DEVICE.CONNECT, onDeviceConnectHandler);
|
|
240
242
|
completeMethodRequestContext(method);
|
|
241
243
|
method.dispose();
|
|
242
244
|
return createResponseMessage(method.responseID, true, true);
|
|
@@ -874,8 +876,15 @@ function isRetryableBleProtocolV2ProbeError(method: BaseMethod, error: unknown)
|
|
|
874
876
|
*/
|
|
875
877
|
async function connectDeviceForBle(method: BaseMethod, device: Device, retryCount = 0) {
|
|
876
878
|
try {
|
|
877
|
-
|
|
879
|
+
const shouldAcquire =
|
|
880
|
+
!device.hasDeviceAcquire() || !device.commands || device.commands.disposed;
|
|
881
|
+
if (shouldAcquire) {
|
|
882
|
+
await device.acquire(method.payload.connectProtocol);
|
|
883
|
+
}
|
|
878
884
|
if (method.payload?.onlyConnectBleDevice) {
|
|
885
|
+
if (shouldAcquire) {
|
|
886
|
+
DevicePool.emitter.emit(DEVICE.CONNECT, device);
|
|
887
|
+
}
|
|
879
888
|
return;
|
|
880
889
|
}
|
|
881
890
|
// Skip initialize if conditions are met
|
|
@@ -886,6 +895,9 @@ async function connectDeviceForBle(method: BaseMethod, device: Device, retryCoun
|
|
|
886
895
|
passphraseState: initOptions.passphraseState,
|
|
887
896
|
});
|
|
888
897
|
}
|
|
898
|
+
if (shouldAcquire) {
|
|
899
|
+
DevicePool.emitter.emit(DEVICE.CONNECT, device);
|
|
900
|
+
}
|
|
889
901
|
} catch (err) {
|
|
890
902
|
if (
|
|
891
903
|
(err.errorCode === HardwareErrorCode.BleTimeoutError ||
|
|
@@ -1186,8 +1198,6 @@ const cleanup = () => {
|
|
|
1186
1198
|
|
|
1187
1199
|
const removeDeviceListener = (device: Device) => {
|
|
1188
1200
|
device.removeAllListeners();
|
|
1189
|
-
DevicePool.emitter.removeAllListeners(DEVICE.CONNECT);
|
|
1190
|
-
// DevicePool.emitter.removeListener(DEVICE.DISCONNECT, onDeviceDisconnectHandler);
|
|
1191
1201
|
};
|
|
1192
1202
|
|
|
1193
1203
|
/**
|
|
@@ -1210,6 +1220,20 @@ const onDeviceDisconnectHandler = (device: Device) => {
|
|
|
1210
1220
|
postMessage(createDeviceMessage(DEVICE.DISCONNECT, { device: deviceObject }));
|
|
1211
1221
|
};
|
|
1212
1222
|
|
|
1223
|
+
const onTransportDeviceDisconnectHandler = (event: TransportDeviceDisconnectEvent) => {
|
|
1224
|
+
const device =
|
|
1225
|
+
deviceCacheMap.get(event.connectId) ??
|
|
1226
|
+
DevicePool.devicesCache[event.connectId] ??
|
|
1227
|
+
DevicePool.getDeviceByPath(event.connectId);
|
|
1228
|
+
if (!device) {
|
|
1229
|
+
Log.debug('Ignoring transport disconnect for an unknown device:', event.connectId);
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
device.markTransportDisconnected();
|
|
1234
|
+
DevicePool.emitter.emit(DEVICE.DISCONNECT, device);
|
|
1235
|
+
};
|
|
1236
|
+
|
|
1213
1237
|
const onDevicePinHandler = async (...[device, type, callback]: DeviceEvents['pin']) => {
|
|
1214
1238
|
Log.log('request Input PIN');
|
|
1215
1239
|
// create ui promise
|
|
@@ -1528,7 +1552,15 @@ export const initCore = () => {
|
|
|
1528
1552
|
|
|
1529
1553
|
export const initConnector = () => {
|
|
1530
1554
|
_connector = new DeviceConnector();
|
|
1555
|
+
DevicePool.emitter.removeListener(DEVICE.CONNECT, onDeviceConnectHandler);
|
|
1556
|
+
DevicePool.emitter.removeListener(DEVICE.DISCONNECT, onDeviceDisconnectHandler);
|
|
1557
|
+
DevicePool.emitter.removeListener(
|
|
1558
|
+
TRANSPORT_EVENT.DEVICE_DISCONNECT,
|
|
1559
|
+
onTransportDeviceDisconnectHandler
|
|
1560
|
+
);
|
|
1561
|
+
DevicePool.emitter.on(DEVICE.CONNECT, onDeviceConnectHandler);
|
|
1531
1562
|
DevicePool.emitter.on(DEVICE.DISCONNECT, onDeviceDisconnectHandler);
|
|
1563
|
+
DevicePool.emitter.on(TRANSPORT_EVENT.DEVICE_DISCONNECT, onTransportDeviceDisconnectHandler);
|
|
1532
1564
|
return _connector;
|
|
1533
1565
|
};
|
|
1534
1566
|
|
|
@@ -11506,6 +11506,33 @@
|
|
|
11506
11506
|
}
|
|
11507
11507
|
}
|
|
11508
11508
|
},
|
|
11509
|
+
"UiAnimationType": {
|
|
11510
|
+
"values": {
|
|
11511
|
+
"Unknown": 0,
|
|
11512
|
+
"Signing": 1
|
|
11513
|
+
}
|
|
11514
|
+
},
|
|
11515
|
+
"UiAnimationCommand": {
|
|
11516
|
+
"values": {
|
|
11517
|
+
"CommandUnknown": 0,
|
|
11518
|
+
"Start": 1,
|
|
11519
|
+
"Stop": 2,
|
|
11520
|
+
"Refresh": 3
|
|
11521
|
+
}
|
|
11522
|
+
},
|
|
11523
|
+
"UiAnimationRequest": {
|
|
11524
|
+
"fields": {
|
|
11525
|
+
"command": {
|
|
11526
|
+
"rule": "required",
|
|
11527
|
+
"type": "UiAnimationCommand",
|
|
11528
|
+
"id": 1
|
|
11529
|
+
},
|
|
11530
|
+
"type": {
|
|
11531
|
+
"type": "UiAnimationType",
|
|
11532
|
+
"id": 2
|
|
11533
|
+
}
|
|
11534
|
+
}
|
|
11535
|
+
},
|
|
11509
11536
|
"ViewAmount": {
|
|
11510
11537
|
"fields": {
|
|
11511
11538
|
"is_unlimited": {
|
|
@@ -12482,7 +12509,12 @@
|
|
|
12482
12509
|
}
|
|
12483
12510
|
},
|
|
12484
12511
|
"DeviceSessionAskPassphrase": {
|
|
12485
|
-
"fields": {
|
|
12512
|
+
"fields": {
|
|
12513
|
+
"passphrase": {
|
|
12514
|
+
"type": "string",
|
|
12515
|
+
"id": 1
|
|
12516
|
+
}
|
|
12517
|
+
}
|
|
12486
12518
|
},
|
|
12487
12519
|
"DeviceSessionAskPin_FailureSubCodes": {
|
|
12488
12520
|
"values": {
|
|
@@ -399,17 +399,7 @@ export default class DataManager {
|
|
|
399
399
|
|
|
400
400
|
static async load(settings: ConnectSettings) {
|
|
401
401
|
this.settings = settings;
|
|
402
|
-
|
|
403
|
-
settings.firmwareManifestMode ?? (settings.fetchConfig ? 'sdk-managed' : undefined);
|
|
404
|
-
if (settings.preloadedConfig) {
|
|
405
|
-
this.applyRemoteConfig(settings.preloadedConfig);
|
|
406
|
-
Log.log('[DataConfig] Preloaded firmware config loaded');
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
if (manifestMode === 'external-only') {
|
|
410
|
-
throw new Error('External firmware config snapshot is required');
|
|
411
|
-
}
|
|
412
|
-
if (manifestMode !== 'sdk-managed') {
|
|
402
|
+
if (!settings.fetchConfig) {
|
|
413
403
|
return;
|
|
414
404
|
}
|
|
415
405
|
|
|
@@ -419,50 +409,60 @@ export default class DataManager {
|
|
|
419
409
|
|
|
420
410
|
const urlWithCache = `${url}?noCache=${getTimeStamp()}`;
|
|
421
411
|
let data: RemoteConfigResponse | null = null;
|
|
412
|
+
let fetchMethod: 'configFetcher' | 'axios' | 'none' = 'none';
|
|
413
|
+
|
|
414
|
+
// 1. Try custom configFetcher first (client-side IP direct connection support)
|
|
422
415
|
if (settings.configFetcher) {
|
|
423
|
-
Log.debug('[DataConfig] Trying client
|
|
416
|
+
Log.debug('[DataConfig] Trying configFetcher (client-side fetcher)...');
|
|
424
417
|
try {
|
|
425
418
|
data = await settings.configFetcher(urlWithCache);
|
|
419
|
+
if (data) {
|
|
420
|
+
fetchMethod = 'configFetcher';
|
|
421
|
+
Log.log('[DataConfig] ConfigFetcher success');
|
|
422
|
+
} else {
|
|
423
|
+
Log.debug('[DataConfig] ConfigFetcher returned null, will fallback to axios');
|
|
424
|
+
}
|
|
426
425
|
} catch (e) {
|
|
427
|
-
Log.warn('[DataConfig]
|
|
426
|
+
Log.warn('[DataConfig] ConfigFetcher error, will fallback to axios:', e);
|
|
428
427
|
}
|
|
429
428
|
}
|
|
429
|
+
|
|
430
|
+
// 2. Fallback to default axios request
|
|
430
431
|
if (!data) {
|
|
431
|
-
Log.debug('[DataConfig] Trying SDK
|
|
432
|
+
Log.debug('[DataConfig] Trying axios (SDK default fetcher)...');
|
|
432
433
|
try {
|
|
433
434
|
const response = await axios.get<RemoteConfigResponse>(urlWithCache, {
|
|
434
435
|
// because of iframe timeout is 10000
|
|
435
436
|
timeout: 7000,
|
|
436
437
|
});
|
|
437
438
|
data = response.data;
|
|
438
|
-
|
|
439
|
+
fetchMethod = 'axios';
|
|
440
|
+
Log.log('[DataConfig] Axios fetch success');
|
|
439
441
|
} catch (e) {
|
|
440
|
-
Log.warn('[DataConfig]
|
|
442
|
+
Log.warn('[DataConfig] Axios fetch error:', e);
|
|
441
443
|
}
|
|
442
444
|
}
|
|
443
445
|
|
|
446
|
+
// 3. Apply config if available
|
|
444
447
|
if (data) {
|
|
445
|
-
|
|
448
|
+
Log.log(`[DataConfig] Config loaded successfully via [${fetchMethod}]`);
|
|
449
|
+
this.deviceMap = {
|
|
450
|
+
[EDeviceType.Classic]: this.enrichFirmwareReleaseInfo(data.classic),
|
|
451
|
+
[EDeviceType.Classic1s]: this.enrichFirmwareReleaseInfo(data.classic1s),
|
|
452
|
+
[EDeviceType.ClassicPure]: this.enrichFirmwareReleaseInfo(data.classicpure),
|
|
453
|
+
[EDeviceType.Mini]: this.enrichFirmwareReleaseInfo(data.mini),
|
|
454
|
+
[EDeviceType.Touch]: this.enrichFirmwareReleaseInfo(data.touch),
|
|
455
|
+
[EDeviceType.Pro]: this.enrichFirmwareReleaseInfo(data.pro),
|
|
456
|
+
[EDeviceType.Pro2]: this.enrichFirmwareReleaseInfo(data.pro2),
|
|
457
|
+
};
|
|
458
|
+
this.assets = {
|
|
459
|
+
bridge: data.bridge,
|
|
460
|
+
};
|
|
446
461
|
} else {
|
|
447
|
-
Log.warn('[DataConfig]
|
|
462
|
+
Log.warn('[DataConfig] All fetch methods failed, using built-in default config');
|
|
448
463
|
}
|
|
449
464
|
}
|
|
450
465
|
|
|
451
|
-
private static applyRemoteConfig(data: RemoteConfigResponse) {
|
|
452
|
-
this.deviceMap = {
|
|
453
|
-
[EDeviceType.Classic]: this.enrichFirmwareReleaseInfo(data.classic),
|
|
454
|
-
[EDeviceType.Classic1s]: this.enrichFirmwareReleaseInfo(data.classic1s),
|
|
455
|
-
[EDeviceType.ClassicPure]: this.enrichFirmwareReleaseInfo(data.classicpure),
|
|
456
|
-
[EDeviceType.Mini]: this.enrichFirmwareReleaseInfo(data.mini),
|
|
457
|
-
[EDeviceType.Touch]: this.enrichFirmwareReleaseInfo(data.touch),
|
|
458
|
-
[EDeviceType.Pro]: this.enrichFirmwareReleaseInfo(data.pro),
|
|
459
|
-
[EDeviceType.Pro2]: this.enrichFirmwareReleaseInfo(data.pro2),
|
|
460
|
-
};
|
|
461
|
-
this.assets = {
|
|
462
|
-
bridge: data.bridge,
|
|
463
|
-
};
|
|
464
|
-
}
|
|
465
|
-
|
|
466
466
|
static updateEnv(newEnv: ConnectSettings['env']) {
|
|
467
467
|
if (this.settings) {
|
|
468
468
|
const prevEnv = this.settings.env;
|
|
@@ -115,21 +115,10 @@ 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
|
-
|
|
125
118
|
if (input.configFetcher) {
|
|
126
119
|
settings.configFetcher = input.configFetcher;
|
|
127
120
|
}
|
|
128
121
|
|
|
129
|
-
if (input.preloadedConfig) {
|
|
130
|
-
settings.preloadedConfig = input.preloadedConfig;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
122
|
return settings;
|
|
134
123
|
};
|
|
135
124
|
|
package/src/device/Device.ts
CHANGED
|
@@ -344,6 +344,10 @@ export class Device extends EventEmitter {
|
|
|
344
344
|
// eslint-disable-next-line no-async-promise-executor
|
|
345
345
|
return new Promise<boolean>(async (resolve, reject) => {
|
|
346
346
|
if (DataManager.isBleConnect(env)) {
|
|
347
|
+
if (this.hasDeviceAcquire() && this.commands && !this.commands.disposed) {
|
|
348
|
+
resolve(true);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
347
351
|
try {
|
|
348
352
|
await this.acquire(connectProtocol);
|
|
349
353
|
resolve(true);
|
|
@@ -1065,6 +1069,7 @@ export class Device extends EventEmitter {
|
|
|
1065
1069
|
}
|
|
1066
1070
|
|
|
1067
1071
|
markTransportDisconnected() {
|
|
1072
|
+
this.deviceAcquired = false;
|
|
1068
1073
|
if (!this.isProtocolV2()) return;
|
|
1069
1074
|
this.protocolV2StateNeedsReload = true;
|
|
1070
1075
|
this.clearPreInitialized();
|
|
@@ -1357,7 +1362,7 @@ export class Device extends EventEmitter {
|
|
|
1357
1362
|
isUsedHere() {
|
|
1358
1363
|
const env = DataManager.getSettings('env');
|
|
1359
1364
|
if (DataManager.isBleConnect(env)) {
|
|
1360
|
-
return
|
|
1365
|
+
return this.deviceAcquired;
|
|
1361
1366
|
}
|
|
1362
1367
|
return this.isUsed() && this.originalDescriptor.session === this.mainId;
|
|
1363
1368
|
}
|
|
@@ -1439,14 +1444,7 @@ export class Device extends EventEmitter {
|
|
|
1439
1444
|
async unlockDevice(pinType: DeviceSessionPinType = DeviceSessionPinType.Main) {
|
|
1440
1445
|
if (this.isProtocolV2()) {
|
|
1441
1446
|
try {
|
|
1442
|
-
await this.commands.typedCall(
|
|
1443
|
-
'DeviceSessionAskPin',
|
|
1444
|
-
'Success',
|
|
1445
|
-
{ type: pinType },
|
|
1446
|
-
{
|
|
1447
|
-
timeoutMs: 120_000,
|
|
1448
|
-
}
|
|
1449
|
-
);
|
|
1447
|
+
await this.commands.typedCall('DeviceSessionAskPin', 'Success', { type: pinType });
|
|
1450
1448
|
} catch (error) {
|
|
1451
1449
|
const errorText =
|
|
1452
1450
|
error instanceof Error
|
|
@@ -515,6 +515,25 @@ export class DeviceCommands {
|
|
|
515
515
|
subcode,
|
|
516
516
|
firmwareMessage: message,
|
|
517
517
|
});
|
|
518
|
+
} else if (
|
|
519
|
+
DEVICE_SESSION_CALLS.has(callType) &&
|
|
520
|
+
(subcode === DeviceSessionErrorCode.DeviceSessionError_Busy ||
|
|
521
|
+
/^(?:busy|another flow in progress)$/i.test(normalizedMessage))
|
|
522
|
+
) {
|
|
523
|
+
error = ERRORS.TypedError(HardwareErrorCode.DeviceBusy, message, {
|
|
524
|
+
failureCode: code,
|
|
525
|
+
subcode,
|
|
526
|
+
firmwareMessage: message,
|
|
527
|
+
});
|
|
528
|
+
} else if (
|
|
529
|
+
callType === 'DeviceSessionAskPin' &&
|
|
530
|
+
/^passphrase disabled$/i.test(normalizedMessage)
|
|
531
|
+
) {
|
|
532
|
+
error = ERRORS.TypedError(HardwareErrorCode.DeviceNotOpenedPassphrase, message, {
|
|
533
|
+
failureCode: code,
|
|
534
|
+
subcode,
|
|
535
|
+
firmwareMessage: message,
|
|
536
|
+
});
|
|
518
537
|
} else if (
|
|
519
538
|
subcode === DeviceErrorCode.DeviceError_ActionCancelled ||
|
|
520
539
|
isLegacyProtocolV2ActionCancelledFailure
|
package/src/index.ts
CHANGED
|
@@ -19,11 +19,6 @@ 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';
|
|
27
22
|
|
|
28
23
|
const HardwareSdk = ({
|
|
29
24
|
init,
|