@onekeyfe/hd-core 1.2.0-alpha.21 → 1.2.0-alpha.22
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 +87 -2
- package/__tests__/connectSettings.test.ts +47 -5
- package/__tests__/device-identity.test.ts +24 -2
- package/__tests__/device-settings.test.ts +1 -1
- package/__tests__/device-wallet-session-store.test.ts +111 -7
- 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-update-capabilities.test.ts +13 -0
- package/__tests__/firmware-update/firmware-update-plan.test.ts +326 -0
- package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +200 -0
- package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +39 -30
- package/__tests__/firmware-update/firmware-upload-source.test.ts +70 -0
- package/__tests__/open-wallet-session.test.ts +253 -13
- package/__tests__/pro2Wallpaper.test.ts +7 -18
- package/__tests__/protocol-v2-unlock-policy.test.ts +45 -0
- package/__tests__/protocol-v2.test.ts +415 -145
- package/__tests__/public-device-state-api.test.ts +69 -62
- package/__tests__/public-pro2-api-boundary.test.ts +8 -34
- package/dist/api/BaseMethod.d.ts +2 -1
- package/dist/api/BaseMethod.d.ts.map +1 -1
- package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
- package/dist/api/ClearSessionCache.d.ts.map +1 -1
- package/dist/api/FirmwareUpdate.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV2.d.ts +11 -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 +29 -10
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/OpenWalletSession.d.ts.map +1 -1
- package/dist/api/conflux/ConfluxGetAddress.d.ts +1 -0
- package/dist/api/conflux/ConfluxGetAddress.d.ts.map +1 -1
- package/dist/api/conflux/ConfluxSignMessage.d.ts +1 -0
- package/dist/api/conflux/ConfluxSignMessage.d.ts.map +1 -1
- package/dist/api/conflux/ConfluxSignMessageCIP23.d.ts +1 -0
- package/dist/api/conflux/ConfluxSignMessageCIP23.d.ts.map +1 -1
- package/dist/api/conflux/ConfluxSignTransaction.d.ts +1 -0
- package/dist/api/conflux/ConfluxSignTransaction.d.ts.map +1 -1
- package/dist/api/device/DeviceUnlock.d.ts +1 -1
- package/dist/api/device/DeviceUnlock.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 +26 -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/FirmwareUpdateBaseMethod.d.ts +7 -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 +24 -0
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -0
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +25 -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/index.d.ts +0 -17
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/nexa/NexaSignTransaction.d.ts +1 -0
- package/dist/api/nexa/NexaSignTransaction.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.map +1 -1
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/device/DeviceStateMapper.d.ts.map +1 -1
- package/dist/device/DeviceStateProjector.d.ts.map +1 -1
- package/dist/events/logBlockEvent.d.ts.map +1 -1
- package/dist/index.d.ts +265 -226
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3045 -1550
- package/dist/inject.d.ts.map +1 -1
- package/dist/lowLevelInject.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockPolicy.d.ts +4 -0
- package/dist/protocols/protocol-v2/unlockPolicy.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +1 -1
- 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 +3 -0
- package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
- package/dist/types/api/deviceUnlock.d.ts +2 -2
- package/dist/types/api/deviceUnlock.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 +69 -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 +27 -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 +10 -19
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/api/openWalletSession.d.ts +1 -0
- package/dist/types/api/openWalletSession.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +3 -76
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/api/sessionCache.d.ts +4 -1
- package/dist/types/api/sessionCache.d.ts.map +1 -1
- package/dist/types/settings.d.ts +13 -0
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.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/BaseMethod.ts +8 -3
- package/src/api/CheckAllFirmwareRelease.ts +16 -3
- package/src/api/ClearSessionCache.ts +4 -0
- package/src/api/FirmwareUpdate.ts +8 -1
- package/src/api/FirmwareUpdateV2.ts +284 -119
- package/src/api/FirmwareUpdateV3.ts +248 -55
- package/src/api/FirmwareUpdateV4.ts +816 -328
- package/src/api/OpenWalletSession.ts +74 -25
- package/src/api/UploadPortfolio.ts +1 -1
- package/src/api/conflux/ConfluxGetAddress.ts +2 -0
- package/src/api/conflux/ConfluxSignMessage.ts +2 -0
- package/src/api/conflux/ConfluxSignMessageCIP23.ts +2 -0
- package/src/api/conflux/ConfluxSignTransaction.ts +2 -0
- package/src/api/device/DeviceChangePin.ts +1 -1
- package/src/api/device/DeviceUnlock.ts +1 -2
- package/src/api/device/DeviceUpdateBootloader.ts +114 -6
- package/src/api/device/DeviceWipe.ts +1 -1
- package/src/api/firmware/FirmwareArtifactSource.ts +278 -0
- package/src/api/firmware/FirmwareHostBinding.ts +100 -0
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +50 -0
- package/src/api/firmware/FirmwareUpdateCapabilities.ts +9 -0
- package/src/api/firmware/FirmwareUpdatePlan.ts +621 -0
- package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +378 -0
- package/src/api/firmware/updateBootloader.ts +19 -4
- package/src/api/firmware/uploadFirmware.ts +140 -22
- package/src/api/helpers/protocolV2FileWrite.ts +6 -0
- package/src/api/index.ts +0 -18
- package/src/api/nexa/NexaSignTransaction.ts +2 -0
- package/src/api/protocol-v2/DeviceUploadWallpaper.ts +1 -1
- package/src/data/messages/messages-protocol-v2.json +77 -4
- package/src/data-manager/DataManager.ts +33 -33
- package/src/data-manager/connectSettings.ts +11 -0
- package/src/device/Device.ts +50 -60
- package/src/device/DeviceCommands.ts +58 -14
- package/src/device/DeviceStateMapper.ts +18 -1
- package/src/device/DeviceStateProjector.ts +7 -1
- package/src/events/logBlockEvent.ts +1 -0
- package/src/index.ts +5 -0
- package/src/inject.ts +22 -25
- package/src/lowLevelInject.ts +5 -1
- package/src/protocols/protocol-v2/deviceSession.ts +1 -1
- package/src/protocols/protocol-v2/settingsUnlockPolicy.ts +2 -2
- package/src/protocols/protocol-v2/unlockPolicy.ts +105 -0
- package/src/protocols/protocol-v2/unlockRetry.ts +1 -3
- package/src/protocols/protocol-v2/walletSession.ts +68 -12
- package/src/topLevelInject.ts +5 -1
- package/src/types/api/checkAllFirmwareRelease.ts +3 -0
- package/src/types/api/deviceUnlock.ts +2 -5
- package/src/types/api/deviceUpdateBootloader.ts +6 -0
- package/src/types/api/export.ts +18 -0
- package/src/types/api/firmwareUpdate.ts +76 -0
- package/src/types/api/firmwareUpdateCapabilities.ts +9 -0
- package/src/types/api/firmwareUpdatePlan.ts +36 -0
- package/src/types/api/firmwareUpdatePreparedPlan.ts +53 -0
- package/src/types/api/index.ts +14 -34
- package/src/types/api/openWalletSession.ts +2 -0
- package/src/types/api/protocolV2.ts +3 -156
- package/src/types/api/sessionCache.ts +9 -4
- package/src/types/settings.ts +13 -0
- package/src/utils/deviceFeaturesUtils.ts +5 -1
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
FirmwareArtifactReader,
|
|
5
|
+
FirmwareArtifactReference,
|
|
6
|
+
} from '../../types/api/firmwareUpdate';
|
|
7
|
+
|
|
8
|
+
export const MAX_FIRMWARE_ARTIFACT_READ_BYTES = 256 * 1024;
|
|
9
|
+
|
|
10
|
+
export interface FirmwareByteSource {
|
|
11
|
+
readonly size: number;
|
|
12
|
+
readAt(offset: number, length: number): Promise<ArrayBuffer>;
|
|
13
|
+
close(): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const artifactError = (
|
|
17
|
+
code:
|
|
18
|
+
| 'FirmwareArtifactReceiptMismatch'
|
|
19
|
+
| 'FirmwareArtifactReaderInvalid'
|
|
20
|
+
| 'FirmwareArtifactReadOutOfBounds'
|
|
21
|
+
| 'FirmwareArtifactReadTooLarge',
|
|
22
|
+
message: string
|
|
23
|
+
): never => {
|
|
24
|
+
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, message, {
|
|
25
|
+
firmwareUpdateCode: code,
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const assertSafeSize = (value: number, name: string) => {
|
|
30
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
31
|
+
artifactError('FirmwareArtifactReceiptMismatch', `${name} must be a positive safe integer`);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const assertFirmwareArtifactReference = (
|
|
36
|
+
artifact: FirmwareArtifactReference
|
|
37
|
+
): FirmwareArtifactReference => {
|
|
38
|
+
if (
|
|
39
|
+
!artifact ||
|
|
40
|
+
typeof artifact.artifactRef !== 'string' ||
|
|
41
|
+
artifact.artifactRef.length === 0 ||
|
|
42
|
+
artifact.artifactRef.length > 160 ||
|
|
43
|
+
artifact.artifactRef.includes('/') ||
|
|
44
|
+
artifact.artifactRef.includes('\\')
|
|
45
|
+
) {
|
|
46
|
+
artifactError(
|
|
47
|
+
'FirmwareArtifactReceiptMismatch',
|
|
48
|
+
'Firmware artifactRef must be an opaque identifier'
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
assertSafeSize(artifact.size, 'Firmware artifact size');
|
|
52
|
+
if (typeof artifact.sha256 !== 'string' || !/^[a-f0-9]{64}$/i.test(artifact.sha256)) {
|
|
53
|
+
artifactError(
|
|
54
|
+
'FirmwareArtifactReceiptMismatch',
|
|
55
|
+
'Firmware artifact sha256 must be a 32-byte hexadecimal digest'
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
return artifact;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const assertFirmwareArtifactRead = (size: number, offset: number, length: number) => {
|
|
62
|
+
if (
|
|
63
|
+
!Number.isSafeInteger(offset) ||
|
|
64
|
+
offset < 0 ||
|
|
65
|
+
offset >= size ||
|
|
66
|
+
!Number.isSafeInteger(length) ||
|
|
67
|
+
length <= 0 ||
|
|
68
|
+
offset + length > size
|
|
69
|
+
) {
|
|
70
|
+
artifactError(
|
|
71
|
+
'FirmwareArtifactReadOutOfBounds',
|
|
72
|
+
`Firmware artifact read is outside size ${size}: offset=${offset}, length=${length}`
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
if (length > MAX_FIRMWARE_ARTIFACT_READ_BYTES) {
|
|
76
|
+
artifactError(
|
|
77
|
+
'FirmwareArtifactReadTooLarge',
|
|
78
|
+
`Firmware artifact read length ${length} exceeds ${MAX_FIRMWARE_ARTIFACT_READ_BYTES}`
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const createMemoryFirmwareByteSource = (binary: ArrayBuffer): FirmwareByteSource => {
|
|
84
|
+
let bytes: Uint8Array | null = new Uint8Array(binary);
|
|
85
|
+
const { byteLength: size } = binary;
|
|
86
|
+
assertSafeSize(size, 'Firmware binary size');
|
|
87
|
+
return {
|
|
88
|
+
size,
|
|
89
|
+
readAt: (offset, length) => {
|
|
90
|
+
if (!bytes) {
|
|
91
|
+
return Promise.reject(
|
|
92
|
+
ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'Firmware byte source is closed', {
|
|
93
|
+
firmwareUpdateCode: 'FirmwareArtifactReaderInvalid',
|
|
94
|
+
})
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
assertFirmwareArtifactRead(size, offset, length);
|
|
98
|
+
return Promise.resolve(bytes.slice(offset, offset + length).buffer);
|
|
99
|
+
},
|
|
100
|
+
close: () => {
|
|
101
|
+
bytes = null;
|
|
102
|
+
return Promise.resolve();
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
class ReaderFirmwareByteSource implements FirmwareByteSource {
|
|
108
|
+
readonly size: number;
|
|
109
|
+
|
|
110
|
+
private closed = false;
|
|
111
|
+
|
|
112
|
+
private reading = false;
|
|
113
|
+
|
|
114
|
+
private constructor(
|
|
115
|
+
private readonly reader: FirmwareArtifactReader,
|
|
116
|
+
private readonly readerId: string,
|
|
117
|
+
size: number
|
|
118
|
+
) {
|
|
119
|
+
this.size = size;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
static async open(
|
|
123
|
+
artifact: FirmwareArtifactReference,
|
|
124
|
+
reader: FirmwareArtifactReader
|
|
125
|
+
): Promise<ReaderFirmwareByteSource> {
|
|
126
|
+
const opened = await reader.open({ artifactRef: artifact.artifactRef });
|
|
127
|
+
if (
|
|
128
|
+
!opened ||
|
|
129
|
+
typeof opened.readerId !== 'string' ||
|
|
130
|
+
opened.readerId.length === 0 ||
|
|
131
|
+
!Number.isSafeInteger(opened.size) ||
|
|
132
|
+
opened.size !== artifact.size
|
|
133
|
+
) {
|
|
134
|
+
if (opened?.readerId) {
|
|
135
|
+
await reader.close({ readerId: opened.readerId }).catch(() => undefined);
|
|
136
|
+
}
|
|
137
|
+
return artifactError(
|
|
138
|
+
'FirmwareArtifactReceiptMismatch',
|
|
139
|
+
`Firmware artifact opened with size ${opened?.size ?? 'unknown'}, expected ${artifact.size}`
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
return new ReaderFirmwareByteSource(reader, opened.readerId, opened.size);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async readAt(offset: number, length: number): Promise<ArrayBuffer> {
|
|
146
|
+
if (this.closed) {
|
|
147
|
+
return artifactError('FirmwareArtifactReaderInvalid', 'Firmware artifact reader is closed');
|
|
148
|
+
}
|
|
149
|
+
if (this.reading) {
|
|
150
|
+
return artifactError(
|
|
151
|
+
'FirmwareArtifactReaderInvalid',
|
|
152
|
+
'Firmware artifact reader only permits one in-flight read'
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
assertFirmwareArtifactRead(this.size, offset, length);
|
|
156
|
+
this.reading = true;
|
|
157
|
+
try {
|
|
158
|
+
const result = await this.reader.read({
|
|
159
|
+
readerId: this.readerId,
|
|
160
|
+
offset,
|
|
161
|
+
length,
|
|
162
|
+
});
|
|
163
|
+
const expectedEof = offset + length === this.size;
|
|
164
|
+
if (
|
|
165
|
+
!result ||
|
|
166
|
+
!(result.data instanceof ArrayBuffer) ||
|
|
167
|
+
result.data.byteLength !== length ||
|
|
168
|
+
result.bytesRead !== length ||
|
|
169
|
+
result.eof !== expectedEof
|
|
170
|
+
) {
|
|
171
|
+
return artifactError(
|
|
172
|
+
'FirmwareArtifactReaderInvalid',
|
|
173
|
+
'Firmware artifact reader returned an invalid read result'
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
return result.data;
|
|
177
|
+
} finally {
|
|
178
|
+
this.reading = false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async close(): Promise<void> {
|
|
183
|
+
if (this.closed) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
this.closed = true;
|
|
187
|
+
await this.reader.close({ readerId: this.readerId });
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export const openFirmwareByteSource = async ({
|
|
192
|
+
binary,
|
|
193
|
+
artifact,
|
|
194
|
+
reader,
|
|
195
|
+
}: {
|
|
196
|
+
binary?: ArrayBuffer;
|
|
197
|
+
artifact?: FirmwareArtifactReference;
|
|
198
|
+
reader?: FirmwareArtifactReader;
|
|
199
|
+
}): Promise<FirmwareByteSource | null> => {
|
|
200
|
+
if (binary && artifact) {
|
|
201
|
+
return artifactError(
|
|
202
|
+
'FirmwareArtifactReceiptMismatch',
|
|
203
|
+
'Firmware input must not contain both binary and artifactRef'
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
if (binary) {
|
|
207
|
+
return createMemoryFirmwareByteSource(binary);
|
|
208
|
+
}
|
|
209
|
+
if (!artifact) {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
assertFirmwareArtifactReference(artifact);
|
|
213
|
+
if (!reader) {
|
|
214
|
+
return artifactError(
|
|
215
|
+
'FirmwareArtifactReaderInvalid',
|
|
216
|
+
'Firmware artifact reader is required for artifactRef input'
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
return ReaderFirmwareByteSource.open(artifact, reader);
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
export const readFirmwareByteSourceFully = async (
|
|
223
|
+
source: FirmwareByteSource
|
|
224
|
+
): Promise<ArrayBuffer> => {
|
|
225
|
+
const output = new Uint8Array(source.size);
|
|
226
|
+
let offset = 0;
|
|
227
|
+
while (offset < source.size) {
|
|
228
|
+
const length = Math.min(MAX_FIRMWARE_ARTIFACT_READ_BYTES, source.size - offset);
|
|
229
|
+
output.set(new Uint8Array(await source.readAt(offset, length)), offset);
|
|
230
|
+
offset += length;
|
|
231
|
+
}
|
|
232
|
+
return output.buffer;
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
export const writeFirmwareByteSource = async ({
|
|
236
|
+
source,
|
|
237
|
+
chunkSize,
|
|
238
|
+
write,
|
|
239
|
+
}: {
|
|
240
|
+
source: FirmwareByteSource;
|
|
241
|
+
chunkSize: number;
|
|
242
|
+
write: (input: {
|
|
243
|
+
data: ArrayBuffer;
|
|
244
|
+
sourceOffset: number;
|
|
245
|
+
length: number;
|
|
246
|
+
first: boolean;
|
|
247
|
+
}) => Promise<number>;
|
|
248
|
+
}) => {
|
|
249
|
+
if (
|
|
250
|
+
!Number.isSafeInteger(chunkSize) ||
|
|
251
|
+
chunkSize <= 0 ||
|
|
252
|
+
chunkSize > MAX_FIRMWARE_ARTIFACT_READ_BYTES
|
|
253
|
+
) {
|
|
254
|
+
return artifactError(
|
|
255
|
+
'FirmwareArtifactReadTooLarge',
|
|
256
|
+
`Firmware artifact chunk size must be between 1 and ${MAX_FIRMWARE_ARTIFACT_READ_BYTES}`
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
let offset = 0;
|
|
260
|
+
while (offset < source.size) {
|
|
261
|
+
const length = Math.min(chunkSize, source.size - offset);
|
|
262
|
+
const data = await source.readAt(offset, length);
|
|
263
|
+
const processed = await write({
|
|
264
|
+
data,
|
|
265
|
+
sourceOffset: offset,
|
|
266
|
+
length,
|
|
267
|
+
first: offset === 0,
|
|
268
|
+
});
|
|
269
|
+
if (processed !== length) {
|
|
270
|
+
return artifactError(
|
|
271
|
+
'FirmwareArtifactReaderInvalid',
|
|
272
|
+
`Firmware artifact writer processed ${processed} bytes, expected ${length}`
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
offset += length;
|
|
276
|
+
}
|
|
277
|
+
return offset;
|
|
278
|
+
};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
+
|
|
3
|
+
import type { FirmwareUpdateHostBinding } from '../../types/api/firmwareUpdate';
|
|
4
|
+
|
|
5
|
+
const bindingError = (message: string): never => {
|
|
6
|
+
throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, message, {
|
|
7
|
+
firmwareUpdateCode: 'FirmwareArtifactReaderInvalid',
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
class FirmwareHostBindingRegistry {
|
|
12
|
+
private generation = 0;
|
|
13
|
+
|
|
14
|
+
private binding?: FirmwareUpdateHostBinding;
|
|
15
|
+
|
|
16
|
+
register(binding: FirmwareUpdateHostBinding): number {
|
|
17
|
+
if (
|
|
18
|
+
!binding ||
|
|
19
|
+
typeof binding.artifactReader?.open !== 'function' ||
|
|
20
|
+
typeof binding.artifactReader.read !== 'function' ||
|
|
21
|
+
typeof binding.artifactReader.close !== 'function'
|
|
22
|
+
) {
|
|
23
|
+
return bindingError('Firmware host binding is invalid');
|
|
24
|
+
}
|
|
25
|
+
this.generation += 1;
|
|
26
|
+
this.binding = binding;
|
|
27
|
+
return this.generation;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
unregister(generation?: number): boolean {
|
|
31
|
+
if (generation !== undefined && generation !== this.generation) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
this.generation += 1;
|
|
35
|
+
this.binding = undefined;
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getGeneration(): number {
|
|
40
|
+
return this.generation;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
resolve(generation: number): FirmwareUpdateHostBinding {
|
|
44
|
+
if (
|
|
45
|
+
!Number.isSafeInteger(generation) ||
|
|
46
|
+
generation <= 0 ||
|
|
47
|
+
generation !== this.generation ||
|
|
48
|
+
!this.binding
|
|
49
|
+
) {
|
|
50
|
+
return bindingError(`Firmware host binding generation ${generation} is stale`);
|
|
51
|
+
}
|
|
52
|
+
const { binding } = this;
|
|
53
|
+
const assertCurrent = () => {
|
|
54
|
+
if (generation !== this.generation || binding !== this.binding) {
|
|
55
|
+
bindingError(`Firmware host binding generation ${generation} is stale`);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
return {
|
|
59
|
+
artifactReader: {
|
|
60
|
+
open: async input => {
|
|
61
|
+
assertCurrent();
|
|
62
|
+
const opened = await binding.artifactReader.open(input);
|
|
63
|
+
try {
|
|
64
|
+
assertCurrent();
|
|
65
|
+
} catch (error) {
|
|
66
|
+
if (opened?.readerId) {
|
|
67
|
+
await binding.artifactReader
|
|
68
|
+
.close({ readerId: opened.readerId })
|
|
69
|
+
.catch(() => undefined);
|
|
70
|
+
}
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
return opened;
|
|
74
|
+
},
|
|
75
|
+
read: async input => {
|
|
76
|
+
assertCurrent();
|
|
77
|
+
const result = await binding.artifactReader.read(input);
|
|
78
|
+
assertCurrent();
|
|
79
|
+
return result;
|
|
80
|
+
},
|
|
81
|
+
close: input => binding.artifactReader.close(input),
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const firmwareHostBindingRegistry = new FirmwareHostBindingRegistry();
|
|
88
|
+
|
|
89
|
+
export const registerFirmwareUpdateHostBinding = (binding: FirmwareUpdateHostBinding): number =>
|
|
90
|
+
firmwareHostBindingRegistry.register(binding);
|
|
91
|
+
|
|
92
|
+
export const unregisterFirmwareUpdateHostBinding = (generation?: number): boolean =>
|
|
93
|
+
firmwareHostBindingRegistry.unregister(generation);
|
|
94
|
+
|
|
95
|
+
export const getFirmwareUpdateHostBindingGeneration = (): number =>
|
|
96
|
+
firmwareHostBindingRegistry.getGeneration();
|
|
97
|
+
|
|
98
|
+
export const resolveFirmwareUpdateHostBinding = (
|
|
99
|
+
generation: number | undefined
|
|
100
|
+
): FirmwareUpdateHostBinding => firmwareHostBindingRegistry.resolve(generation ?? Number.NaN);
|
|
@@ -14,6 +14,7 @@ import { DataManager } from '../../data-manager';
|
|
|
14
14
|
import { BaseMethod } from '../BaseMethod';
|
|
15
15
|
import { DEVICE } from '../../events';
|
|
16
16
|
import { createFirmwareProgressThrottle } from './progressThrottle';
|
|
17
|
+
import { writeFirmwareByteSource } from './FirmwareArtifactSource';
|
|
17
18
|
|
|
18
19
|
import type {
|
|
19
20
|
IFirmwareUpdateProgressType,
|
|
@@ -24,6 +25,7 @@ import type { RebootType } from '@onekeyfe/hd-transport';
|
|
|
24
25
|
import type { Deferred } from '@onekeyfe/hd-shared';
|
|
25
26
|
import type { KnownDevice } from '../../types';
|
|
26
27
|
import type { TypedResponseMessage } from '../../device/DeviceCommands';
|
|
28
|
+
import type { FirmwareByteSource } from './FirmwareArtifactSource';
|
|
27
29
|
|
|
28
30
|
const Log = getLogger(LoggerNames.Method);
|
|
29
31
|
const SESSION_ERROR = 'session not found';
|
|
@@ -387,6 +389,54 @@ export class FirmwareUpdateBaseMethod<Params> extends BaseMethod<Params> {
|
|
|
387
389
|
return totalSize !== undefined ? (processedSize ?? 0) + payload.byteLength : 0;
|
|
388
390
|
}
|
|
389
391
|
|
|
392
|
+
async emmcCommonUpdateProcessFromSource({
|
|
393
|
+
source,
|
|
394
|
+
filePath,
|
|
395
|
+
processedSize,
|
|
396
|
+
totalSize,
|
|
397
|
+
}: {
|
|
398
|
+
source: FirmwareByteSource;
|
|
399
|
+
filePath: string;
|
|
400
|
+
processedSize?: number;
|
|
401
|
+
totalSize?: number;
|
|
402
|
+
}) {
|
|
403
|
+
if (!filePath.startsWith('0:')) {
|
|
404
|
+
throw new Error('filePath must start with 0:');
|
|
405
|
+
}
|
|
406
|
+
const env = DataManager.getSettings('env');
|
|
407
|
+
const chunkSize = 1024 * (DataManager.isBleConnect(env) ? 16 : 128);
|
|
408
|
+
|
|
409
|
+
await writeFirmwareByteSource({
|
|
410
|
+
source,
|
|
411
|
+
chunkSize,
|
|
412
|
+
write: async ({ data, sourceOffset, length, first }) => {
|
|
413
|
+
const progress =
|
|
414
|
+
totalSize !== undefined && processedSize !== undefined
|
|
415
|
+
? Math.min(Math.ceil(((processedSize + sourceOffset + length) / totalSize) * 100), 99)
|
|
416
|
+
: Math.min(Math.ceil(((sourceOffset + length) / source.size) * 100), 99);
|
|
417
|
+
const writeRes = await this.emmcFileWriteWithRetry(
|
|
418
|
+
filePath,
|
|
419
|
+
length,
|
|
420
|
+
sourceOffset,
|
|
421
|
+
data,
|
|
422
|
+
first,
|
|
423
|
+
progress
|
|
424
|
+
);
|
|
425
|
+
if (!writeRes) {
|
|
426
|
+
throw ERRORS.TypedError(
|
|
427
|
+
HardwareErrorCode.EmmcFileWriteFirmwareError,
|
|
428
|
+
'transfer data error'
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
const processedBytes = Number(writeRes.message.processed_byte);
|
|
432
|
+
this.postProgressMessage(progress, 'transferData');
|
|
433
|
+
return Number.isFinite(processedBytes) ? processedBytes : length;
|
|
434
|
+
},
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
return totalSize !== undefined ? (processedSize ?? 0) + source.size : 0;
|
|
438
|
+
}
|
|
439
|
+
|
|
390
440
|
async emmcFileWriteWithRetry(
|
|
391
441
|
filePath: string,
|
|
392
442
|
chunkLength: number,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FirmwareUpdateCapabilities } from '../../types/api/firmwareUpdateCapabilities';
|
|
2
|
+
|
|
3
|
+
export const getFirmwareUpdateCapabilities = (): FirmwareUpdateCapabilities => ({
|
|
4
|
+
planSchemaVersion: 2,
|
|
5
|
+
preparedPlanSchemaVersion: 2,
|
|
6
|
+
hostBindingProtocolVersion: 2,
|
|
7
|
+
manifestModes: ['external-only', 'sdk-managed'],
|
|
8
|
+
supportsArtifactReader: true,
|
|
9
|
+
});
|