@onekeyfe/hd-core 1.2.2-alpha.100 → 1.2.2-alpha.11
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__/AllNetworkGetAddressBase.tracing.test.ts +20 -4
- package/__tests__/DeviceCommands.test.ts +13 -0
- package/__tests__/check-all-firmware-release-protocol-v2.test.ts +0 -2
- package/__tests__/check-firmware-release-protocol-v2.test.ts +0 -2
- package/__tests__/device-lifecycle-events.test.ts +86 -0
- package/__tests__/deviceUploadNft.test.ts +68 -0
- package/__tests__/evmSignTypedData.test.ts +42 -0
- package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +825 -12
- package/__tests__/logBlockEvent.test.ts +45 -122
- package/__tests__/open-wallet-session.test.ts +781 -69
- package/__tests__/pro2HostAssetPackage.test.ts +165 -0
- package/__tests__/protocol-v2-resources.test.ts +7 -4
- package/__tests__/protocol-v2-unlock-policy.test.ts +35 -1
- package/__tests__/protocol-v2.test.ts +925 -97
- package/__tests__/protocolV2FileWrite.test.ts +40 -0
- package/dist/api/FirmwareUpdateV4.d.ts +5 -0
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/OpenWalletSession.d.ts.map +1 -1
- package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
- package/dist/api/evm/EVMSignTypedData.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +2 -2
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
- package/dist/api/firmware/getBinary.d.ts +2 -2
- package/dist/api/helpers/protocolV2FileWrite.d.ts +1 -0
- package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
- package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
- package/dist/core/RequestQueue.d.ts +2 -0
- package/dist/core/RequestQueue.d.ts.map +1 -1
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/Device.d.ts +1 -1
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceConnector.d.ts +1 -1
- package/dist/device/DeviceConnector.d.ts.map +1 -1
- package/dist/events/logBlockEvent.d.ts.map +1 -1
- package/dist/events/ui-request.d.ts +3 -0
- package/dist/events/ui-request.d.ts.map +1 -1
- package/dist/index.d.ts +21 -5
- package/dist/index.js +2511 -1538
- package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +2 -0
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts +3 -0
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/types/settings.d.ts +2 -2
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts +3 -0
- 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/dist/utils/pro2HostAssetPackage.d.ts +8 -0
- package/dist/utils/pro2HostAssetPackage.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/api/FirmwareUpdateV4.ts +474 -83
- package/src/api/OpenWalletSession.ts +39 -12
- package/src/api/allnetwork/AllNetworkGetAddressBase.ts +5 -2
- package/src/api/evm/EVMSignTypedData.ts +1 -0
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +9 -3
- package/src/api/helpers/protocolV2FileWrite.ts +11 -5
- package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +7 -1
- package/src/api/protocol-v2/DeviceUploadNft.ts +31 -8
- package/src/api/protocol-v2/DeviceUploadWallpaper.ts +33 -8
- package/src/api/protocol-v2/helpers.ts +8 -0
- package/src/core/RequestQueue.ts +16 -1
- package/src/core/index.ts +164 -26
- package/src/data/messages/messages-protocol-v2.json +1589 -1375
- package/src/device/Device.ts +3 -1
- package/src/device/DeviceConnector.ts +7 -3
- package/src/events/logBlockEvent.ts +6 -75
- package/src/events/ui-request.ts +3 -0
- package/src/protocols/protocol-v2/resources.ts +9 -1
- package/src/protocols/protocol-v2/unlockPolicyRunner.ts +8 -1
- package/src/protocols/protocol-v2/walletSession.ts +223 -56
- package/src/types/settings.ts +4 -2
- package/src/utils/deviceFeaturesUtils.ts +4 -0
- package/src/utils/pro2HostAssetPackage.ts +400 -0
|
@@ -29,6 +29,12 @@ const wasResumed = (session: unknown) =>
|
|
|
29
29
|
'resumed' in session &&
|
|
30
30
|
(session as { resumed?: unknown }).resumed === true;
|
|
31
31
|
|
|
32
|
+
const wasWalletStatusRefreshed = (session: unknown) =>
|
|
33
|
+
!!session &&
|
|
34
|
+
typeof session === 'object' &&
|
|
35
|
+
'walletStatusRefreshed' in session &&
|
|
36
|
+
(session as { walletStatusRefreshed?: unknown }).walletStatusRefreshed === true;
|
|
37
|
+
|
|
32
38
|
const requireHiddenWalletResponse = (session: { passphraseState?: string }) => {
|
|
33
39
|
if (!session.passphraseState) {
|
|
34
40
|
throw ERRORS.TypedError(
|
|
@@ -87,12 +93,20 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
|
|
|
87
93
|
this.unlockPolicy = 'unlock-before-run';
|
|
88
94
|
this.skipForceUpdateCheck = true;
|
|
89
95
|
this.params = normalizeParams(this.payload as unknown as Record<string, unknown>);
|
|
96
|
+
this.protocolV2PreUnlockPinType =
|
|
97
|
+
this.params.mode === OpenWalletSessionMode.SelectHidden
|
|
98
|
+
? DeviceSessionPinType.Any
|
|
99
|
+
: DeviceSessionPinType.Main;
|
|
90
100
|
this.payload.useEmptyPassphrase = this.params.mode === OpenWalletSessionMode.Standard;
|
|
91
101
|
}
|
|
92
102
|
|
|
93
103
|
async run(): Promise<OpenWalletSessionPayload> {
|
|
94
104
|
const isProtocolV2 = this.device.isProtocolV2();
|
|
95
|
-
|
|
105
|
+
const reusePreflightStatus =
|
|
106
|
+
isProtocolV2 && this.protocolV2UnlockContext?.preflightStatusRefreshed === true;
|
|
107
|
+
let state = reusePreflightStatus
|
|
108
|
+
? await this.device.getDeviceState()
|
|
109
|
+
: await this.device.getDeviceState({ refreshSections: ['status'] });
|
|
96
110
|
let currentDeviceId = state.identity.deviceId;
|
|
97
111
|
const hasAuthoritativeProtocolV2WalletStatus = (candidate: typeof state) =>
|
|
98
112
|
candidate.status.unlocked === true &&
|
|
@@ -115,14 +129,25 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
|
|
|
115
129
|
currentDeviceId = state.identity.deviceId;
|
|
116
130
|
return state;
|
|
117
131
|
};
|
|
132
|
+
const resolveProtocolV2DeviceStateAfterSession = async (session: unknown) => {
|
|
133
|
+
if (!wasWalletStatusRefreshed(session)) {
|
|
134
|
+
return refreshProtocolV2DeviceState();
|
|
135
|
+
}
|
|
136
|
+
state = await this.device.getDeviceState();
|
|
137
|
+
currentDeviceId = state.identity.deviceId;
|
|
138
|
+
return state;
|
|
139
|
+
};
|
|
118
140
|
const ensureProtocolV2WalletStatus = async () => {
|
|
119
141
|
if (isProtocolV2 && !hasAuthoritativeProtocolV2WalletStatus(state)) {
|
|
120
|
-
await this.device.unlockDevice(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
142
|
+
await this.device.unlockDevice(
|
|
143
|
+
this.protocolV2PreUnlockPinType ?? DeviceSessionPinType.Main,
|
|
144
|
+
{
|
|
145
|
+
source: 'unlock-coordinator',
|
|
146
|
+
reason: 'device-locked',
|
|
147
|
+
deviceOnly: true,
|
|
148
|
+
method: 'openWalletSession',
|
|
149
|
+
}
|
|
150
|
+
);
|
|
126
151
|
requireAuthoritativeProtocolV2WalletStatus(await refreshProtocolV2DeviceState());
|
|
127
152
|
}
|
|
128
153
|
return state;
|
|
@@ -135,17 +160,19 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
|
|
|
135
160
|
const session = isProtocolV2
|
|
136
161
|
? await getProtocolV2WalletSession(this.device, {
|
|
137
162
|
onlyMainPin: true,
|
|
138
|
-
deriveCardano: this.payload.deriveCardano,
|
|
139
163
|
selectMainWalletBeforeRestore:
|
|
140
164
|
!hasAuthoritativeProtocolV2WalletStatus(state) ||
|
|
141
165
|
state.status.unlockedAttachPin === true,
|
|
166
|
+
mainPinSelected: this.protocolV2UnlockContext?.preflightMainPinSelected,
|
|
142
167
|
})
|
|
143
168
|
: await getPassphraseStateWithRefreshDeviceInfo(this.device, {
|
|
144
169
|
onlyMainPin: true,
|
|
145
170
|
initSession: this.payload.initSession,
|
|
146
171
|
});
|
|
147
172
|
const refreshedState = isProtocolV2
|
|
148
|
-
? requireAuthoritativeProtocolV2WalletStatus(
|
|
173
|
+
? requireAuthoritativeProtocolV2WalletStatus(
|
|
174
|
+
await resolveProtocolV2DeviceStateAfterSession(session)
|
|
175
|
+
)
|
|
149
176
|
: state;
|
|
150
177
|
const deviceId = requireDeviceId();
|
|
151
178
|
if (
|
|
@@ -197,7 +224,6 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
|
|
|
197
224
|
const session = isProtocolV2
|
|
198
225
|
? await getProtocolV2WalletSession(this.device, {
|
|
199
226
|
expectedPassphraseState: this.params.passphraseState,
|
|
200
|
-
deriveCardano: this.payload.deriveCardano,
|
|
201
227
|
})
|
|
202
228
|
: await getPassphraseStateWithRefreshDeviceInfo(this.device, {
|
|
203
229
|
expectPassphraseState: this.params.passphraseState,
|
|
@@ -229,11 +255,12 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
|
|
|
229
255
|
...(readCurrentAttachPinSession
|
|
230
256
|
? { readCurrentAttachPinSession: true }
|
|
231
257
|
: { forceWalletSelection: true }),
|
|
232
|
-
deriveCardano: this.payload.deriveCardano,
|
|
233
258
|
})
|
|
234
259
|
: await getPassphraseStateWithRefreshDeviceInfo(this.device, { initSession: true });
|
|
235
260
|
const refreshedState = isProtocolV2
|
|
236
|
-
? requireAuthoritativeProtocolV2WalletStatus(
|
|
261
|
+
? requireAuthoritativeProtocolV2WalletStatus(
|
|
262
|
+
await resolveProtocolV2DeviceStateAfterSession(session)
|
|
263
|
+
)
|
|
237
264
|
: state;
|
|
238
265
|
const deviceId = requireDeviceId();
|
|
239
266
|
if (isProtocolV2 && refreshedState.status.passphraseProtection !== true) {
|
|
@@ -393,14 +393,17 @@ export default abstract class AllNetworkGetAddressBase extends BaseMethod<
|
|
|
393
393
|
// the root fingerprint, so each nested chain method must resume the
|
|
394
394
|
// requested standard or hidden wallet before sending its device command.
|
|
395
395
|
const useEmptyPassphrase = this.payload.useEmptyPassphrase === true;
|
|
396
|
-
|
|
396
|
+
// Nested Cardano methods opt in to [Standard, Cardano] if Ask rebuilds.
|
|
397
|
+
// Other chains stay Standard-only.
|
|
398
|
+
const deriveCardano = method.name.startsWith('cardano') ? true : undefined;
|
|
397
399
|
const shouldResumeWalletSession = useEmptyPassphrase || !!this.payload.passphraseState;
|
|
398
400
|
if (this.device.isProtocolV2() && shouldResumeWalletSession) {
|
|
399
401
|
const passphraseStateSafety = await this.device.checkPassphraseStateSafety(
|
|
400
402
|
this.payload.passphraseState,
|
|
401
403
|
useEmptyPassphrase,
|
|
402
404
|
this.payload.skipPassphraseCheck,
|
|
403
|
-
deriveCardano
|
|
405
|
+
deriveCardano,
|
|
406
|
+
this.protocolV2UnlockContext?.preflightMainPinSelected
|
|
404
407
|
);
|
|
405
408
|
if (!passphraseStateSafety) {
|
|
406
409
|
throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckPassphraseStateError);
|
|
@@ -360,6 +360,7 @@ export default class EVMSignTypedData extends BaseMethod<EVMSignTypedDataParams>
|
|
|
360
360
|
|
|
361
361
|
const supportBiggerData =
|
|
362
362
|
DeviceModelToTypes.model_classic1s.includes(currentDeviceType) ||
|
|
363
|
+
DeviceModelToTypes.model_pro2.includes(currentDeviceType) ||
|
|
363
364
|
(DeviceModelToTypes.model_touch.includes(currentDeviceType) &&
|
|
364
365
|
semver.gte(currentVersion, supportBiggerDataVersion));
|
|
365
366
|
|
|
@@ -41,9 +41,15 @@ const FIRMWARE_UPDATE_CONFIRM = 'Firmware install confirmed';
|
|
|
41
41
|
*/
|
|
42
42
|
export const BOOTLOADER_POLL_INITIALIZE_TIMEOUT_MS = 5000;
|
|
43
43
|
|
|
44
|
-
type
|
|
44
|
+
type FirmwareProgressMetadata = Pick<
|
|
45
45
|
FirmwareProgress['payload'],
|
|
46
|
-
|
|
46
|
+
| 'transferredBytes'
|
|
47
|
+
| 'totalBytes'
|
|
48
|
+
| 'rateBytesPerSecond'
|
|
49
|
+
| 'elapsedMs'
|
|
50
|
+
| 'installTargetId'
|
|
51
|
+
| 'installPhase'
|
|
52
|
+
| 'installPhaseProgress'
|
|
47
53
|
>;
|
|
48
54
|
|
|
49
55
|
const isDeviceDisconnectedError = (error: unknown) => {
|
|
@@ -103,7 +109,7 @@ export class FirmwareUpdateBaseMethod<Params> extends BaseMethod<Params> {
|
|
|
103
109
|
postProgressMessage = (
|
|
104
110
|
progress: number,
|
|
105
111
|
progressType: IFirmwareUpdateProgressType,
|
|
106
|
-
metrics?:
|
|
112
|
+
metrics?: FirmwareProgressMetadata
|
|
107
113
|
) => {
|
|
108
114
|
this.postMessage(
|
|
109
115
|
createUiMessage(UI_REQUEST.FIRMWARE_PROGRESS, {
|
|
@@ -36,6 +36,8 @@ export type ProtocolV2FileWriteOptions = {
|
|
|
36
36
|
chunkSize?: number;
|
|
37
37
|
chunkLen?: number;
|
|
38
38
|
chunkSizeLimit?: number;
|
|
39
|
+
/** BLE-only limit for a caller whose fixed short path has a verified larger frame budget. */
|
|
40
|
+
bleChunkSizeLimit?: number;
|
|
39
41
|
overwrite?: boolean;
|
|
40
42
|
append?: boolean;
|
|
41
43
|
uiPercentage?: number;
|
|
@@ -114,11 +116,15 @@ export function isProtocolV2ResponseTimeout(error: unknown) {
|
|
|
114
116
|
);
|
|
115
117
|
}
|
|
116
118
|
|
|
117
|
-
function getProtocolV2FileChunkLimit() {
|
|
119
|
+
function getProtocolV2FileChunkLimit(bleChunkSizeLimit?: number) {
|
|
118
120
|
const env = DataManager.getSettings('env');
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
121
|
+
if (env && DataManager.isBleConnect(env)) {
|
|
122
|
+
const configuredLimit = Number(bleChunkSizeLimit);
|
|
123
|
+
return Number.isFinite(configuredLimit) && configuredLimit > 0
|
|
124
|
+
? Math.floor(configuredLimit)
|
|
125
|
+
: PROTOCOL_V2_BLE_FILE_CHUNK_SIZE;
|
|
126
|
+
}
|
|
127
|
+
return PROTOCOL_V2_WEBUSB_FILE_CHUNK_SIZE;
|
|
122
128
|
}
|
|
123
129
|
|
|
124
130
|
async function dataToUint8Array(data: ProtocolV2FileWriteData): Promise<Uint8Array> {
|
|
@@ -178,7 +184,7 @@ export async function writeProtocolV2File(options: ProtocolV2FileWriteOptions) {
|
|
|
178
184
|
);
|
|
179
185
|
}
|
|
180
186
|
|
|
181
|
-
const defaultChunkSizeLimit = getProtocolV2FileChunkLimit();
|
|
187
|
+
const defaultChunkSizeLimit = getProtocolV2FileChunkLimit(options.bleChunkSizeLimit);
|
|
182
188
|
const configuredChunkSizeLimit = Number(options.chunkSizeLimit);
|
|
183
189
|
const chunkSizeLimit =
|
|
184
190
|
Number.isFinite(configuredChunkSizeLimit) && configuredChunkSizeLimit > 0
|
|
@@ -4,7 +4,13 @@ import { invalidParameter } from '../helpers/filesystemValidation';
|
|
|
4
4
|
import type { DeviceFirmwareUpdateStatusGetParams } from './helpers';
|
|
5
5
|
import type { DeviceFirmwareUpdateRecordFields } from '@onekeyfe/hd-transport';
|
|
6
6
|
|
|
7
|
-
const DEVICE_FIRMWARE_UPDATE_STATUS_FIELDS = [
|
|
7
|
+
const DEVICE_FIRMWARE_UPDATE_STATUS_FIELDS = [
|
|
8
|
+
'status',
|
|
9
|
+
'progress_percent',
|
|
10
|
+
'phase_info',
|
|
11
|
+
'payload_version',
|
|
12
|
+
'path',
|
|
13
|
+
] as const;
|
|
8
14
|
|
|
9
15
|
function normalizeStatusFields(
|
|
10
16
|
fields: DeviceFirmwareUpdateStatusGetParams['fields']
|
|
@@ -20,6 +20,10 @@ import {
|
|
|
20
20
|
getCompletePro2NftBasenames,
|
|
21
21
|
} from '../../utils/pro2Nft';
|
|
22
22
|
import { encodePro2Image } from '../../utils/pro2Wallpaper';
|
|
23
|
+
import {
|
|
24
|
+
buildPro2HostAssetPackage,
|
|
25
|
+
supportsPro2HostAssetPackage,
|
|
26
|
+
} from '../../utils/pro2HostAssetPackage';
|
|
23
27
|
import { BaseMethod } from '../BaseMethod';
|
|
24
28
|
import { decodeJpegBase64ToRgba } from '../helpers/base64Data';
|
|
25
29
|
import { invalidParameter } from '../helpers/filesystemValidation';
|
|
@@ -38,8 +42,11 @@ export type DeviceUploadNftParams = {
|
|
|
38
42
|
|
|
39
43
|
export type DeviceUploadNftResponse = {
|
|
40
44
|
basename: string;
|
|
45
|
+
/** Final image path after firmware extracts a host-asset package. */
|
|
41
46
|
imagePath: string;
|
|
47
|
+
/** Final thumbnail path after firmware extracts a host-asset package. */
|
|
42
48
|
thumbnailPath: string;
|
|
49
|
+
/** Final metadata path after firmware extracts a host-asset package. */
|
|
43
50
|
metadataPath: string;
|
|
44
51
|
totalSize: number;
|
|
45
52
|
nftUpdated: true;
|
|
@@ -138,7 +145,7 @@ export default class DeviceUploadNft extends BaseMethod<DeviceUploadNftParams> {
|
|
|
138
145
|
this.useDevicePassphraseState = false;
|
|
139
146
|
}
|
|
140
147
|
|
|
141
|
-
private async assertCapabilities() {
|
|
148
|
+
private async assertCapabilities(useHostAssetPackage: boolean) {
|
|
142
149
|
const protocolInfo = await this.device.ensureProtocolV2RuntimeContext();
|
|
143
150
|
const hasFileWrite = supportsProtocolV2Message(
|
|
144
151
|
protocolInfo,
|
|
@@ -150,7 +157,7 @@ export default class DeviceUploadNft extends BaseMethod<DeviceUploadNftParams> {
|
|
|
150
157
|
);
|
|
151
158
|
const hasDirList = supportsProtocolV2Message(protocolInfo, FILESYSTEM_DIR_LIST_MESSAGE_TYPE);
|
|
152
159
|
const hasNftUpdate = supportsProtocolV2Message(protocolInfo, NFT_UPDATE_MESSAGE_TYPE);
|
|
153
|
-
if (!hasFileWrite || !
|
|
160
|
+
if (!hasFileWrite || !hasNftUpdate || (!useHostAssetPackage && (!hasPathInfo || !hasDirList))) {
|
|
154
161
|
throw createDeviceNotSupportMethodError(this.name, this.device.getCurrentFirmwareType());
|
|
155
162
|
}
|
|
156
163
|
}
|
|
@@ -192,16 +199,32 @@ export default class DeviceUploadNft extends BaseMethod<DeviceUploadNftParams> {
|
|
|
192
199
|
const { bundle } = this;
|
|
193
200
|
if (!bundle) throw invalidParameter('NFT data has not been initialized.');
|
|
194
201
|
|
|
195
|
-
|
|
202
|
+
const useHostAssetPackage = supportsPro2HostAssetPackage(
|
|
203
|
+
this.device.state?.versions.firmware ?? undefined
|
|
204
|
+
);
|
|
205
|
+
await this.assertCapabilities(useHostAssetPackage);
|
|
196
206
|
this.throwIfAborted();
|
|
197
|
-
await this.assertStorageCapacity(bundle.basename);
|
|
207
|
+
if (!useHostAssetPackage) await this.assertStorageCapacity(bundle.basename);
|
|
198
208
|
this.throwIfAborted();
|
|
199
209
|
|
|
200
|
-
const
|
|
210
|
+
const extractedFiles = [
|
|
201
211
|
{ path: `${PRO2_NFT_DIRECTORY}/${bundle.basename}.bin`, data: bundle.image },
|
|
202
212
|
{ path: `${PRO2_NFT_DIRECTORY}/${bundle.basename}_m.bin`, data: bundle.thumbnail },
|
|
203
213
|
{ path: `${PRO2_NFT_DIRECTORY}/${bundle.basename}.json`, data: bundle.metadata },
|
|
204
214
|
];
|
|
215
|
+
const files = useHostAssetPackage
|
|
216
|
+
? [
|
|
217
|
+
{
|
|
218
|
+
path: `${PRO2_NFT_DIRECTORY}/${bundle.basename}.okpkg`,
|
|
219
|
+
data: buildPro2HostAssetPackage(
|
|
220
|
+
extractedFiles.map(file => ({
|
|
221
|
+
name: file.path.slice(`${PRO2_NFT_DIRECTORY}/`.length),
|
|
222
|
+
data: file.data,
|
|
223
|
+
}))
|
|
224
|
+
),
|
|
225
|
+
},
|
|
226
|
+
]
|
|
227
|
+
: extractedFiles;
|
|
205
228
|
const totalSize = files.reduce((sum, file) => sum + file.data.byteLength, 0);
|
|
206
229
|
let transferredBeforeFile = 0;
|
|
207
230
|
|
|
@@ -238,9 +261,9 @@ export default class DeviceUploadNft extends BaseMethod<DeviceUploadNftParams> {
|
|
|
238
261
|
const response = await this.updateNft(bundle.basename);
|
|
239
262
|
return {
|
|
240
263
|
basename: bundle.basename,
|
|
241
|
-
imagePath:
|
|
242
|
-
thumbnailPath:
|
|
243
|
-
metadataPath:
|
|
264
|
+
imagePath: extractedFiles[0].path,
|
|
265
|
+
thumbnailPath: extractedFiles[1].path,
|
|
266
|
+
metadataPath: extractedFiles[2].path,
|
|
244
267
|
totalSize,
|
|
245
268
|
nftUpdated: true,
|
|
246
269
|
message: response.message?.message,
|
|
@@ -16,14 +16,26 @@ import {
|
|
|
16
16
|
type Pro2WallpaperColorFormat,
|
|
17
17
|
encodePro2Wallpaper,
|
|
18
18
|
} from '../../utils/pro2Wallpaper';
|
|
19
|
+
import {
|
|
20
|
+
buildPro2HostAssetPackage,
|
|
21
|
+
supportsPro2HostAssetPackage,
|
|
22
|
+
} from '../../utils/pro2HostAssetPackage';
|
|
19
23
|
|
|
20
24
|
export type DeviceUploadWallpaperParams = {
|
|
21
25
|
jpegBase64: string;
|
|
26
|
+
/**
|
|
27
|
+
* Legacy firmware uses this name for the uploaded `.bin` file. Firmware
|
|
28
|
+
* 1.0.1+ always consumes the fixed `wallpaper.okpkg` package path.
|
|
29
|
+
*/
|
|
22
30
|
fileName?: string;
|
|
23
31
|
chunkSize?: number;
|
|
24
32
|
};
|
|
25
33
|
|
|
26
34
|
export type DeviceUploadWallpaperResponse = {
|
|
35
|
+
/**
|
|
36
|
+
* Filesystem path sent to `DeviceSettingsSet`. On firmware 1.0.1+ this is
|
|
37
|
+
* the temporary package path; firmware extracts and persists wallpaper.bin.
|
|
38
|
+
*/
|
|
27
39
|
path: string;
|
|
28
40
|
size: number;
|
|
29
41
|
colorFormat: Pro2WallpaperColorFormat;
|
|
@@ -31,10 +43,13 @@ export type DeviceUploadWallpaperResponse = {
|
|
|
31
43
|
};
|
|
32
44
|
|
|
33
45
|
const WALLPAPER_DIRECTORY = 'vol1:/wallpapers';
|
|
46
|
+
const WALLPAPER_PACKAGE_PATH = `${WALLPAPER_DIRECTORY}/wallpaper.okpkg`;
|
|
47
|
+
const WALLPAPER_PACKAGE_ENTRY = 'wallpaper.bin';
|
|
34
48
|
const SAFE_FILE_NAME = /^[A-Za-z0-9_-]+(?:\.bin)?$/;
|
|
35
49
|
const DEVICE_SETTINGS_SET_MESSAGE_TYPE = 60412;
|
|
36
50
|
const FILESYSTEM_FILE_WRITE_MESSAGE_TYPE = 60805;
|
|
37
51
|
const FILESYSTEM_DIR_MAKE_MESSAGE_TYPE = 60809;
|
|
52
|
+
const WALLPAPER_PACKAGE_BLE_CHUNK_SIZE = 1960;
|
|
38
53
|
const Log = getLogger(LoggerNames.Method);
|
|
39
54
|
|
|
40
55
|
function normalizeFileName(fileName: string | undefined, data: Uint8Array): string {
|
|
@@ -116,17 +131,16 @@ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpa
|
|
|
116
131
|
this.directoryReady = true;
|
|
117
132
|
}
|
|
118
133
|
|
|
119
|
-
private async upload() {
|
|
134
|
+
private async upload(path: string, data: Uint8Array, bleChunkSizeLimit?: number) {
|
|
120
135
|
if (this.uploaded) return;
|
|
121
|
-
const { encoded } = this;
|
|
122
|
-
if (!encoded) throw invalidParameter('Wallpaper data has not been initialized.');
|
|
123
136
|
|
|
124
137
|
await writeProtocolV2File({
|
|
125
138
|
commands: this.device.commands,
|
|
126
|
-
path
|
|
127
|
-
data
|
|
128
|
-
totalSize:
|
|
139
|
+
path,
|
|
140
|
+
data,
|
|
141
|
+
totalSize: data.byteLength,
|
|
129
142
|
chunkSize: this.params.chunkSize,
|
|
143
|
+
bleChunkSizeLimit,
|
|
130
144
|
maxChunkRetries: 3,
|
|
131
145
|
overwrite: true,
|
|
132
146
|
append: false,
|
|
@@ -145,7 +159,18 @@ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpa
|
|
|
145
159
|
if (!encoded) throw invalidParameter('Wallpaper data has not been initialized.');
|
|
146
160
|
await this.assertCapabilities();
|
|
147
161
|
await this.ensureDirectory();
|
|
148
|
-
|
|
162
|
+
const useHostAssetPackage = supportsPro2HostAssetPackage(
|
|
163
|
+
this.device.state?.versions.firmware ?? undefined
|
|
164
|
+
);
|
|
165
|
+
const data = useHostAssetPackage
|
|
166
|
+
? buildPro2HostAssetPackage([{ name: WALLPAPER_PACKAGE_ENTRY, data: encoded.data }])
|
|
167
|
+
: encoded.data;
|
|
168
|
+
if (useHostAssetPackage) this.path = WALLPAPER_PACKAGE_PATH;
|
|
169
|
+
await this.upload(
|
|
170
|
+
this.path,
|
|
171
|
+
data,
|
|
172
|
+
useHostAssetPackage ? WALLPAPER_PACKAGE_BLE_CHUNK_SIZE : undefined
|
|
173
|
+
);
|
|
149
174
|
const response = await this.device.commands.typedCall('DeviceSettingsSet', 'Success', {
|
|
150
175
|
settings: { wallpaper_path: this.path },
|
|
151
176
|
});
|
|
@@ -160,7 +185,7 @@ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpa
|
|
|
160
185
|
}
|
|
161
186
|
return {
|
|
162
187
|
path: this.path,
|
|
163
|
-
size:
|
|
188
|
+
size: data.byteLength,
|
|
164
189
|
colorFormat: encoded.colorFormat,
|
|
165
190
|
message: response.message?.message,
|
|
166
191
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DeviceRebootType } from '@onekeyfe/hd-transport';
|
|
2
|
+
import { HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
3
|
|
|
3
4
|
import { invalidParameter, validateNonEmptyString } from '../helpers/filesystemValidation';
|
|
4
5
|
import { ProtocolV2FirmwareTargetType } from '../../protocols/protocol-v2/firmware';
|
|
@@ -103,6 +104,13 @@ export const getProtocolV2UnknownErrorText = (error: unknown) => {
|
|
|
103
104
|
};
|
|
104
105
|
|
|
105
106
|
export const isProtocolV2DeviceDisconnectedError = (error: unknown) => {
|
|
107
|
+
const errorCode =
|
|
108
|
+
error && typeof error === 'object'
|
|
109
|
+
? (error as { errorCode?: number; code?: number }).errorCode ??
|
|
110
|
+
(error as { errorCode?: number; code?: number }).code
|
|
111
|
+
: undefined;
|
|
112
|
+
if (errorCode === HardwareErrorCode.BleDeviceDisconnected) return true;
|
|
113
|
+
|
|
106
114
|
const message = getProtocolV2UnknownErrorText(error).toLowerCase();
|
|
107
115
|
const compactMessage = message.replace(/\s+/g, '');
|
|
108
116
|
return (
|
package/src/core/RequestQueue.ts
CHANGED
|
@@ -56,11 +56,20 @@ export default class RequestQueue {
|
|
|
56
56
|
return false;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
private isRequestForConnectId(request: RequestTask, connectId: string) {
|
|
60
|
+
const { method } = request;
|
|
61
|
+
return (
|
|
62
|
+
method.connectId === connectId ||
|
|
63
|
+
method.device?.mainId === connectId ||
|
|
64
|
+
method.device?.getConnectId() === connectId
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
59
68
|
// 取消与指定connectId相关的所有请求
|
|
60
69
|
public abortRequestsByConnectId(connectId: string) {
|
|
61
70
|
let count = 0;
|
|
62
71
|
this.requestQueue.forEach((request, _) => {
|
|
63
|
-
if (request.abortController && request
|
|
72
|
+
if (request.abortController && this.isRequestForConnectId(request, connectId)) {
|
|
64
73
|
request.abortController.abort();
|
|
65
74
|
request.abortController = undefined;
|
|
66
75
|
count++;
|
|
@@ -69,6 +78,12 @@ export default class RequestQueue {
|
|
|
69
78
|
return count;
|
|
70
79
|
}
|
|
71
80
|
|
|
81
|
+
public getRequestTasksIdByConnectId(connectId: string) {
|
|
82
|
+
return Array.from(this.requestQueue.values())
|
|
83
|
+
.filter(request => this.isRequestForConnectId(request, connectId))
|
|
84
|
+
.map(request => request.id);
|
|
85
|
+
}
|
|
86
|
+
|
|
72
87
|
// 取消所有请求
|
|
73
88
|
public abortAllRequests() {
|
|
74
89
|
let count = 0;
|