@onekeyfe/hd-core 1.2.0-alpha.20 → 1.2.0-alpha.21
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__/device-lifecycle-events.test.ts +1 -1
- package/__tests__/device-settings.test.ts +110 -5
- package/__tests__/device-state-events.test.ts +3 -3
- package/__tests__/device-state-mapper.test.ts +15 -3
- package/__tests__/device-state-store.test.ts +1 -1
- package/__tests__/deviceSettings.test.ts +55 -15
- package/__tests__/evmLedgerLegacySafety.test.ts +6 -5
- package/__tests__/get-device-state.test.ts +7 -0
- package/__tests__/protocol-v2.test.ts +40 -153
- package/__tests__/public-device-state-api.test.ts +19 -15
- package/__tests__/public-pro2-api-boundary.test.ts +20 -1
- package/dist/api/BaseMethod.d.ts +0 -3
- package/dist/api/BaseMethod.d.ts.map +1 -1
- package/dist/api/UploadPortfolio.d.ts +0 -1
- package/dist/api/UploadPortfolio.d.ts.map +1 -1
- package/dist/api/device/DeviceSettings.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/index.d.ts +0 -3
- package/dist/api/index.d.ts.map +1 -1
- package/dist/core/RequestQueue.d.ts +1 -19
- package/dist/core/RequestQueue.d.ts.map +1 -1
- package/dist/core/index.d.ts +0 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/Device.d.ts +1 -1
- package/dist/device/DeviceFeaturesState.d.ts +0 -1
- package/dist/device/DeviceFeaturesState.d.ts.map +1 -1
- package/dist/device/DeviceStateMapper.d.ts.map +1 -1
- package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
- package/dist/events/call.d.ts +0 -7
- package/dist/events/call.d.ts.map +1 -1
- package/dist/events/core.d.ts +2 -2
- package/dist/events/core.d.ts.map +1 -1
- package/dist/events/iframe.d.ts +0 -1
- package/dist/events/iframe.d.ts.map +1 -1
- package/dist/index.d.ts +59 -55
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +169 -310
- package/dist/inject.d.ts +2 -3
- package/dist/inject.d.ts.map +1 -1
- package/dist/lowLevelInject.d.ts +1 -2
- package/dist/lowLevelInject.d.ts.map +1 -1
- package/dist/topLevelInject.d.ts.map +1 -1
- package/dist/types/api/deviceSettings.d.ts +2 -2
- package/dist/types/api/deviceSettings.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/index.d.ts +1 -5
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +1 -9
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/device.d.ts +4 -4
- package/dist/types/device.d.ts.map +1 -1
- package/dist/utils/deviceSettings.d.ts +36 -7
- package/dist/utils/deviceSettings.d.ts.map +1 -1
- package/dist/utils/index.d.ts +2 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/BaseMethod.ts +3 -9
- package/src/api/UploadPortfolio.ts +2 -12
- package/src/api/device/DeviceSettings.ts +66 -5
- package/src/api/device/DeviceUnlock.ts +2 -1
- package/src/api/index.ts +0 -3
- package/src/core/RequestQueue.ts +4 -123
- package/src/core/index.ts +25 -31
- package/src/device/Device.ts +1 -1
- package/src/device/DeviceFeaturesState.ts +0 -9
- package/src/device/DeviceStateMapper.ts +8 -7
- package/src/deviceProfile/buildDeviceFeatures.ts +2 -1
- package/src/events/call.ts +0 -6
- package/src/events/core.ts +0 -2
- package/src/events/iframe.ts +0 -1
- package/src/index.ts +0 -4
- package/src/inject.ts +0 -10
- package/src/lowLevelInject.ts +0 -3
- package/src/topLevelInject.ts +0 -2
- package/src/types/api/deviceSettings.ts +17 -2
- package/src/types/api/deviceUnlock.ts +5 -2
- package/src/types/api/index.ts +0 -7
- package/src/types/api/protocolV2.ts +0 -31
- package/src/types/device.ts +5 -3
- package/src/utils/deviceSettings.ts +130 -56
- package/src/utils/index.ts +18 -1
- package/__tests__/RequestQueue.test.ts +0 -140
- package/__tests__/UploadPortfolio.test.ts +0 -46
- package/dist/api/protocol-v2/DeviceSettingsGet.d.ts +0 -6
- package/dist/api/protocol-v2/DeviceSettingsGet.d.ts.map +0 -1
- package/dist/api/protocol-v2/DeviceSettingsPageShow.d.ts +0 -16
- package/dist/api/protocol-v2/DeviceSettingsPageShow.d.ts.map +0 -1
- package/dist/api/protocol-v2/DeviceSettingsSet.d.ts +0 -12
- package/dist/api/protocol-v2/DeviceSettingsSet.d.ts.map +0 -1
- package/src/api/protocol-v2/DeviceSettingsGet.ts +0 -18
- package/src/api/protocol-v2/DeviceSettingsPageShow.ts +0 -70
- package/src/api/protocol-v2/DeviceSettingsSet.ts +0 -44
|
@@ -10,11 +10,56 @@ import {
|
|
|
10
10
|
mapDeviceSettingsToState,
|
|
11
11
|
} from '../../device/DeviceStateMapper';
|
|
12
12
|
import { getProtocolV2SettingsBehavior } from '../../protocols/protocol-v2/settingsUnlockPolicy';
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
DEVICE_SETTINGS_V1_ONLY_FIELDS,
|
|
15
|
+
DEVICE_SETTINGS_V2_ONLY_FIELDS,
|
|
16
|
+
LANGUAGE_LABELS,
|
|
17
|
+
getDeviceSettingsCapabilities,
|
|
18
|
+
} from '../../utils/deviceSettings';
|
|
14
19
|
|
|
15
20
|
import type { ApplySettings } from '@onekeyfe/hd-transport';
|
|
16
21
|
import type { DeviceSettingsParams } from '../../types/api/deviceSettings';
|
|
17
22
|
|
|
23
|
+
const assertSettingsSupported = (
|
|
24
|
+
payload: DeviceSettingsParams,
|
|
25
|
+
unsupported: readonly (keyof DeviceSettingsParams)[],
|
|
26
|
+
protocol: 'Protocol V1' | 'Protocol V2'
|
|
27
|
+
) => {
|
|
28
|
+
const provided = unsupported.filter(key => payload[key] !== undefined);
|
|
29
|
+
if (provided.length > 0) {
|
|
30
|
+
throw invalidParameter(`${protocol} does not support settings: ${provided.join(', ')}.`);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const assertProtocolV2SettingValues = (
|
|
35
|
+
payload: DeviceSettingsParams,
|
|
36
|
+
capabilities: ReturnType<typeof getDeviceSettingsCapabilities>
|
|
37
|
+
) => {
|
|
38
|
+
const brightnessRange = capabilities.ranges.brightness;
|
|
39
|
+
if (
|
|
40
|
+
payload.brightness !== undefined &&
|
|
41
|
+
brightnessRange &&
|
|
42
|
+
(payload.brightness < brightnessRange.min || payload.brightness > brightnessRange.max)
|
|
43
|
+
) {
|
|
44
|
+
throw invalidParameter(
|
|
45
|
+
`Protocol V2 brightness must be between ${brightnessRange.min} and ${brightnessRange.max}.`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const delayFields = [
|
|
50
|
+
['autoLockDelayMs', capabilities.autoLockDelayOptions],
|
|
51
|
+
['autoShutdownDelayMs', capabilities.autoShutdownDelayOptions],
|
|
52
|
+
] as const;
|
|
53
|
+
delayFields.forEach(([field, options]) => {
|
|
54
|
+
const value = payload[field];
|
|
55
|
+
if (value !== undefined && !options.some(option => option.valueMs === value)) {
|
|
56
|
+
throw invalidParameter(
|
|
57
|
+
`Protocol V2 ${field} must be one of: ${options.map(option => option.valueMs).join(', ')}.`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
|
|
18
63
|
const settingsPageForPayload = (
|
|
19
64
|
payload: DeviceSettingsParams
|
|
20
65
|
): DeviceSettingsPage.DevicePassphrase | DeviceSettingsPage.DeviceAirgap | undefined => {
|
|
@@ -70,6 +115,7 @@ export default class DeviceSettings extends BaseMethod<ApplySettings> {
|
|
|
70
115
|
safety_checks: this.payload.safetyChecks,
|
|
71
116
|
experimental_features: this.payload.experimentalFeatures,
|
|
72
117
|
auto_shutdown_delay_ms: this.payload.autoShutdownDelayMs,
|
|
118
|
+
use_ble: this.payload.bluetoothEnabled,
|
|
73
119
|
...(this.payload.changeBrightness
|
|
74
120
|
? { change_brightness: this.payload.changeBrightness }
|
|
75
121
|
: undefined),
|
|
@@ -113,6 +159,12 @@ export default class DeviceSettings extends BaseMethod<ApplySettings> {
|
|
|
113
159
|
async run() {
|
|
114
160
|
try {
|
|
115
161
|
if (this.device.isProtocolV2()) {
|
|
162
|
+
assertSettingsSupported(this.payload, DEVICE_SETTINGS_V1_ONLY_FIELDS, 'Protocol V2');
|
|
163
|
+
const capabilities = getDeviceSettingsCapabilities(
|
|
164
|
+
this.device.getCurrentDeviceType(),
|
|
165
|
+
'V2'
|
|
166
|
+
);
|
|
167
|
+
assertProtocolV2SettingValues(this.payload, capabilities);
|
|
116
168
|
const settings = mapCommonSettingsToProtocolV2(this.payload);
|
|
117
169
|
const requestedPassphrase = this.payload.usePassphrase;
|
|
118
170
|
const requestedAirgap = this.payload.airgapMode;
|
|
@@ -172,18 +224,27 @@ export default class DeviceSettings extends BaseMethod<ApplySettings> {
|
|
|
172
224
|
const res = await this.device.commands.typedCall('DeviceSettingsSet', 'Success', {
|
|
173
225
|
settings,
|
|
174
226
|
});
|
|
175
|
-
this.device.updateState(mapDeviceSettingsToState(settings), '
|
|
227
|
+
this.device.updateState(mapDeviceSettingsToState(settings), 'settings-write');
|
|
176
228
|
return res.message;
|
|
177
229
|
}
|
|
178
230
|
|
|
179
|
-
|
|
180
|
-
|
|
231
|
+
assertSettingsSupported(this.payload, DEVICE_SETTINGS_V2_ONLY_FIELDS, 'Protocol V1');
|
|
232
|
+
const capabilities = getDeviceSettingsCapabilities(this.device.getCurrentDeviceType(), 'V1');
|
|
233
|
+
if (
|
|
234
|
+
this.payload.safetyChecks !== undefined &&
|
|
235
|
+
!capabilities.safetyCheckOptions.some(option => option.value === this.payload.safetyChecks)
|
|
236
|
+
) {
|
|
237
|
+
throw invalidParameter(
|
|
238
|
+
`Protocol V1 safetyChecks must be one of: ${capabilities.safetyCheckOptions
|
|
239
|
+
.map(option => option.value)
|
|
240
|
+
.join(', ')}.`
|
|
241
|
+
);
|
|
181
242
|
}
|
|
182
243
|
|
|
183
244
|
const res = await this.device.commands.typedCall('ApplySettings', 'Success', {
|
|
184
245
|
...this.params,
|
|
185
246
|
});
|
|
186
|
-
this.device.updateState(mapApplySettingsToState(this.params), '
|
|
247
|
+
this.device.updateState(mapApplySettingsToState(this.params), 'settings-write');
|
|
187
248
|
return res.message;
|
|
188
249
|
} catch (error) {
|
|
189
250
|
if (error.message?.toLowerCase().includes('no setting provided')) {
|
package/src/api/index.ts
CHANGED
|
@@ -51,15 +51,12 @@ export { default as deviceUploadWallpaper } from './protocol-v2/DeviceUploadWall
|
|
|
51
51
|
// Protocol V2 development APIs. These remain public while Pro2 integration is in development.
|
|
52
52
|
export { default as deviceInfoGet } from './protocol-v2/DeviceInfoGet';
|
|
53
53
|
export { default as deviceStatusGet } from './protocol-v2/DeviceStatusGet';
|
|
54
|
-
export { default as deviceSettingsGet } from './protocol-v2/DeviceSettingsGet';
|
|
55
54
|
export { default as protocolInfoRequest } from './protocol-v2/ProtocolInfoRequest';
|
|
56
55
|
export { default as ping } from './protocol-v2/Ping';
|
|
57
56
|
export { default as deviceFirmwareUpdate } from './protocol-v2/DeviceFirmwareUpdate';
|
|
58
57
|
export { default as deviceGetFirmwareUpdateStatus } from './protocol-v2/DeviceGetFirmwareUpdateStatus';
|
|
59
58
|
export { default as deviceFactoryInfoSet } from './protocol-v2/DeviceFactoryInfoSet';
|
|
60
59
|
export { default as deviceFactoryInfoGet } from './protocol-v2/DeviceFactoryInfoGet';
|
|
61
|
-
export { default as deviceSettingsSet } from './protocol-v2/DeviceSettingsSet';
|
|
62
|
-
export { default as deviceSettingsPageShow } from './protocol-v2/DeviceSettingsPageShow';
|
|
63
60
|
export { default as filesystemPermissionFix } from './protocol-v2/FilesystemPermissionFix';
|
|
64
61
|
export { default as filesystemFormat } from './protocol-v2/FilesystemFormat';
|
|
65
62
|
export { default as fileRead } from './FileRead';
|
package/src/core/RequestQueue.ts
CHANGED
|
@@ -1,47 +1,21 @@
|
|
|
1
|
-
import { type Deferred, createDeferred } from '@onekeyfe/hd-shared';
|
|
2
|
-
|
|
3
1
|
import { LoggerNames, getLogger } from '../utils';
|
|
4
2
|
|
|
3
|
+
import type { Deferred } from '@onekeyfe/hd-shared';
|
|
5
4
|
import type { BaseMethod } from '../api/BaseMethod';
|
|
6
5
|
|
|
7
6
|
const Log = getLogger(LoggerNames.Core);
|
|
8
|
-
const OPERATION_PRE_CANCEL_TTL_MS = 30_000;
|
|
9
|
-
const DEFAULT_BACKGROUND_PREEMPT_TIMEOUT_MS = 5_500;
|
|
10
|
-
|
|
11
7
|
export type RequestTask = {
|
|
12
8
|
id: number;
|
|
13
9
|
method: BaseMethod;
|
|
14
10
|
callPromise?: Deferred<any> | undefined;
|
|
15
11
|
abortController?: AbortController;
|
|
16
|
-
settled: Deferred<void>;
|
|
17
|
-
released: Deferred<void>;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export type RequestAdmission =
|
|
21
|
-
| { status: 'reserved'; task: RequestTask }
|
|
22
|
-
| { status: 'busy'; blockingTask?: RequestTask };
|
|
23
|
-
|
|
24
|
-
export type RequestAdmissionOptions = {
|
|
25
|
-
backgroundPreemptTimeoutMs?: number;
|
|
26
12
|
};
|
|
27
13
|
|
|
28
14
|
export default class RequestQueue {
|
|
29
15
|
private requestQueue = new Map<number, RequestTask>();
|
|
30
16
|
|
|
31
|
-
private operationRequestIds = new Map<string, number>();
|
|
32
|
-
|
|
33
|
-
private pendingOperationCancellations = new Map<string, number>();
|
|
34
|
-
|
|
35
17
|
private pendingCallbackTasks = new Map<string, Deferred<void>>();
|
|
36
18
|
|
|
37
|
-
private prunePendingOperationCancellations(now = Date.now()) {
|
|
38
|
-
this.pendingOperationCancellations.forEach((expiresAt, operationId) => {
|
|
39
|
-
if (expiresAt <= now) {
|
|
40
|
-
this.pendingOperationCancellations.delete(operationId);
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
19
|
// 生成唯一请求ID
|
|
46
20
|
public generateRequestId = (method?: BaseMethod) => {
|
|
47
21
|
if (method && method.responseID != null) {
|
|
@@ -57,93 +31,11 @@ export default class RequestQueue {
|
|
|
57
31
|
}
|
|
58
32
|
const abortController = new AbortController();
|
|
59
33
|
method.abortSignal = abortController.signal;
|
|
60
|
-
const task = {
|
|
61
|
-
id: requestId,
|
|
62
|
-
method,
|
|
63
|
-
abortController,
|
|
64
|
-
settled: createDeferred<void>(),
|
|
65
|
-
released: createDeferred<void>(),
|
|
66
|
-
};
|
|
34
|
+
const task = { id: requestId, method, abortController };
|
|
67
35
|
this.requestQueue.set(requestId, task);
|
|
68
|
-
const operationId = method.payload?.operationId;
|
|
69
|
-
if (typeof operationId === 'string' && operationId) {
|
|
70
|
-
this.operationRequestIds.set(operationId, requestId);
|
|
71
|
-
this.prunePendingOperationCancellations();
|
|
72
|
-
if (this.pendingOperationCancellations.delete(operationId)) {
|
|
73
|
-
abortController.abort();
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
36
|
return task;
|
|
77
37
|
}
|
|
78
38
|
|
|
79
|
-
public async admitTask(
|
|
80
|
-
method: BaseMethod,
|
|
81
|
-
options: RequestAdmissionOptions = {}
|
|
82
|
-
): Promise<RequestAdmission> {
|
|
83
|
-
const activeTasks = Array.from(this.requestQueue.values()).filter(
|
|
84
|
-
task => task.method.connectId === method.connectId
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
if (method.executionPriority === 'background') {
|
|
88
|
-
if (activeTasks.length) {
|
|
89
|
-
Log.debug(
|
|
90
|
-
`Reject background method ${method.name}: device is occupied by ${activeTasks[0].method.name}`
|
|
91
|
-
);
|
|
92
|
-
return { status: 'busy', blockingTask: activeTasks[0] };
|
|
93
|
-
}
|
|
94
|
-
return { status: 'reserved', task: this.createTask(method) };
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const backgroundTasks = activeTasks.filter(
|
|
98
|
-
task => task.method.executionPriority === 'background'
|
|
99
|
-
);
|
|
100
|
-
if (!backgroundTasks.length) {
|
|
101
|
-
return { status: 'reserved', task: this.createTask(method) };
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
Log.debug(
|
|
105
|
-
`Preempt ${backgroundTasks.length} background request(s) for user method ${method.name}`
|
|
106
|
-
);
|
|
107
|
-
backgroundTasks.forEach(task => task.abortController?.abort());
|
|
108
|
-
const timeoutMs =
|
|
109
|
-
options.backgroundPreemptTimeoutMs ??
|
|
110
|
-
Math.max(
|
|
111
|
-
DEFAULT_BACKGROUND_PREEMPT_TIMEOUT_MS,
|
|
112
|
-
...backgroundTasks.map(task => task.method.maxAbortLatencyMs ?? 0)
|
|
113
|
-
);
|
|
114
|
-
const released = await this.waitForTasksReleased(backgroundTasks, timeoutMs);
|
|
115
|
-
if (!released) {
|
|
116
|
-
Log.warn(`Background request cleanup timed out before user method ${method.name}`);
|
|
117
|
-
return { status: 'busy', blockingTask: backgroundTasks[0] };
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return { status: 'reserved', task: this.createTask(method) };
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
private async waitForTasksReleased(tasks: RequestTask[], timeoutMs: number) {
|
|
124
|
-
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
125
|
-
const timeoutPromise = new Promise<false>(resolve => {
|
|
126
|
-
timer = setTimeout(() => resolve(false), timeoutMs);
|
|
127
|
-
});
|
|
128
|
-
const releasedPromise = Promise.all(tasks.map(task => task.released.promise)).then(() => true);
|
|
129
|
-
const released = await Promise.race([releasedPromise, timeoutPromise]);
|
|
130
|
-
if (timer) {
|
|
131
|
-
clearTimeout(timer);
|
|
132
|
-
}
|
|
133
|
-
return released;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
public abortOperation(operationId: string) {
|
|
137
|
-
const requestId = this.operationRequestIds.get(operationId);
|
|
138
|
-
if (requestId !== undefined) {
|
|
139
|
-
return this.abortRequest(requestId);
|
|
140
|
-
}
|
|
141
|
-
const now = Date.now();
|
|
142
|
-
this.prunePendingOperationCancellations(now);
|
|
143
|
-
this.pendingOperationCancellations.set(operationId, now + OPERATION_PRE_CANCEL_TTL_MS);
|
|
144
|
-
return false;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
39
|
public getTask(requestId: number): RequestTask | undefined {
|
|
148
40
|
return this.requestQueue.get(requestId);
|
|
149
41
|
}
|
|
@@ -186,7 +78,6 @@ export default class RequestQueue {
|
|
|
186
78
|
count++;
|
|
187
79
|
}
|
|
188
80
|
});
|
|
189
|
-
this.pendingOperationCancellations.clear();
|
|
190
81
|
return count;
|
|
191
82
|
}
|
|
192
83
|
|
|
@@ -200,8 +91,8 @@ export default class RequestQueue {
|
|
|
200
91
|
const request = this.requestQueue.get(requestId);
|
|
201
92
|
if (request) {
|
|
202
93
|
request.callPromise?.resolve(response);
|
|
203
|
-
request.settled.resolve();
|
|
204
94
|
}
|
|
95
|
+
this.releaseTask(requestId);
|
|
205
96
|
}
|
|
206
97
|
|
|
207
98
|
// 拒绝请求
|
|
@@ -209,22 +100,12 @@ export default class RequestQueue {
|
|
|
209
100
|
const request = this.requestQueue.get(requestId);
|
|
210
101
|
if (request) {
|
|
211
102
|
request.callPromise?.reject(error);
|
|
212
|
-
request.settled.resolve();
|
|
213
103
|
}
|
|
104
|
+
this.releaseTask(requestId);
|
|
214
105
|
}
|
|
215
106
|
|
|
216
107
|
// 删除请求
|
|
217
108
|
public releaseTask(requestId: number) {
|
|
218
|
-
const task = this.requestQueue.get(requestId);
|
|
219
|
-
const operationId = task?.method.payload?.operationId;
|
|
220
|
-
if (
|
|
221
|
-
typeof operationId === 'string' &&
|
|
222
|
-
this.operationRequestIds.get(operationId) === requestId
|
|
223
|
-
) {
|
|
224
|
-
this.operationRequestIds.delete(operationId);
|
|
225
|
-
}
|
|
226
|
-
task?.settled.resolve();
|
|
227
|
-
task?.released.resolve();
|
|
228
109
|
this.requestQueue.delete(requestId);
|
|
229
110
|
}
|
|
230
111
|
|
package/src/core/index.ts
CHANGED
|
@@ -196,25 +196,34 @@ export const callAPI = async (context: CoreContext, message: CoreMessage) => {
|
|
|
196
196
|
// );
|
|
197
197
|
// }
|
|
198
198
|
|
|
199
|
+
const { requestQueue, methodSynchronize } = context;
|
|
200
|
+
const error = await methodSynchronize(() => {
|
|
201
|
+
for (const requestId of requestQueue.getRequestTasksId()) {
|
|
202
|
+
const task = requestQueue.getTask(requestId);
|
|
203
|
+
Log.debug(
|
|
204
|
+
'pre request task: ',
|
|
205
|
+
`task?.id: ${task?.id},
|
|
206
|
+
task?.method.connectId: ${task?.method.connectId},
|
|
207
|
+
task?.method.deviceId: ${task?.method.deviceId},
|
|
208
|
+
task?.method.name: ${task?.method.name}`
|
|
209
|
+
);
|
|
210
|
+
// if (task) {
|
|
211
|
+
// return Promise.reject(ERRORS.TypedError(HardwareErrorCode.DeviceBusy));
|
|
212
|
+
// }
|
|
213
|
+
}
|
|
214
|
+
return null;
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
if (error) {
|
|
218
|
+
return createResponseMessage(method.responseID, false, { error });
|
|
219
|
+
}
|
|
220
|
+
|
|
199
221
|
// only the pre-warm signal (PreInitialize) forks here; normal methods fall
|
|
200
222
|
// through to onCallDevice below, so the pre-warm dedup/guards never touch them
|
|
201
223
|
if (method.isPreWarmSignal) {
|
|
202
224
|
return handlePreWarmSignal(context, message, method);
|
|
203
225
|
}
|
|
204
226
|
|
|
205
|
-
const { requestQueue, methodSynchronize } = context;
|
|
206
|
-
const admission = await methodSynchronize(
|
|
207
|
-
() => requestQueue.admitTask(method),
|
|
208
|
-
method.connectId ?? method.deviceId ?? 'unknown-device'
|
|
209
|
-
);
|
|
210
|
-
if (admission.status === 'busy') {
|
|
211
|
-
const error = ERRORS.TypedError(HardwareErrorCode.DeviceBusy);
|
|
212
|
-
DevicePool.emitter.removeListener(DEVICE.CONNECT, onDeviceConnectHandler);
|
|
213
|
-
completeMethodRequestContext(method, error);
|
|
214
|
-
method.dispose();
|
|
215
|
-
return createResponseMessage(method.responseID, false, { error });
|
|
216
|
-
}
|
|
217
|
-
|
|
218
227
|
return onCallDevice(context, message, method);
|
|
219
228
|
};
|
|
220
229
|
|
|
@@ -334,7 +343,7 @@ const onCallDevice = async (
|
|
|
334
343
|
|
|
335
344
|
await waitForPendingPromise(getPrePendingCallPromise, setPrePendingCallPromise);
|
|
336
345
|
|
|
337
|
-
const task = requestQueue.
|
|
346
|
+
const task = requestQueue.createTask(method);
|
|
338
347
|
|
|
339
348
|
// Pre-warm holds the device as a per-connectId callback task so a concurrent
|
|
340
349
|
// real call waits (before ensureConnected) instead of racing its Initialize.
|
|
@@ -378,10 +387,6 @@ const onCallDevice = async (
|
|
|
378
387
|
if (method.payload?.onlyConnectBleDevice) {
|
|
379
388
|
preWarmCallbackTask?.resolve();
|
|
380
389
|
Log.debug('Call API - only connect ble device: ', device?.mainId);
|
|
381
|
-
DevicePool.emitter.removeListener(DEVICE.CONNECT, onDeviceConnectHandler);
|
|
382
|
-
requestQueue.releaseTask(method.responseID);
|
|
383
|
-
completeMethodRequestContext(method);
|
|
384
|
-
method.dispose();
|
|
385
390
|
return createResponseMessage(method.responseID, true, null);
|
|
386
391
|
}
|
|
387
392
|
|
|
@@ -661,9 +666,8 @@ const onCallDevice = async (
|
|
|
661
666
|
skipInitialize: canSkipInitialize(method, device),
|
|
662
667
|
...parseInitOptions(method),
|
|
663
668
|
};
|
|
664
|
-
const
|
|
665
|
-
task.callPromise = createDeferred<any>();
|
|
666
|
-
deviceRunPromise.catch(error => task.callPromise?.reject(error));
|
|
669
|
+
const deviceRun = () => device.run(inner, runOptions);
|
|
670
|
+
task.callPromise = createDeferred<any>(deviceRun);
|
|
667
671
|
|
|
668
672
|
try {
|
|
669
673
|
return await task.callPromise.promise;
|
|
@@ -671,8 +675,6 @@ const onCallDevice = async (
|
|
|
671
675
|
Log.debug('Device Run Error: ', e);
|
|
672
676
|
completeMethodRequestContext(method, e);
|
|
673
677
|
return createResponseMessage(method.responseID, false, { error: e });
|
|
674
|
-
} finally {
|
|
675
|
-
await deviceRunPromise.catch(() => undefined);
|
|
676
678
|
}
|
|
677
679
|
} catch (error) {
|
|
678
680
|
messageResponse = createResponseMessage(method.responseID, false, { error });
|
|
@@ -1389,10 +1391,6 @@ export default class Core extends EventEmitter {
|
|
|
1389
1391
|
Log.debug(`[Core] Created SDK instance: ${this.sdkInstanceId}`);
|
|
1390
1392
|
}
|
|
1391
1393
|
|
|
1392
|
-
cancelOperation(operationId: string) {
|
|
1393
|
-
this.requestQueue.abortOperation(operationId);
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1396
1394
|
private getCoreContext() {
|
|
1397
1395
|
return {
|
|
1398
1396
|
sdkInstanceId: this.sdkInstanceId,
|
|
@@ -1468,10 +1466,6 @@ export default class Core extends EventEmitter {
|
|
|
1468
1466
|
cancel(this.getCoreContext(), message.payload.connectId);
|
|
1469
1467
|
break;
|
|
1470
1468
|
}
|
|
1471
|
-
case IFRAME.CANCEL_OPERATION: {
|
|
1472
|
-
this.cancelOperation(message.payload.operationId);
|
|
1473
|
-
break;
|
|
1474
|
-
}
|
|
1475
1469
|
case IFRAME.CALLBACK: {
|
|
1476
1470
|
Log.log('callback message: ', message);
|
|
1477
1471
|
postMessage(message);
|
package/src/device/Device.ts
CHANGED
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
import type { Features } from '../types';
|
|
2
2
|
|
|
3
|
-
export type DeviceFeaturesUpdateSource =
|
|
4
|
-
| 'device-info'
|
|
5
|
-
| 'device-status'
|
|
6
|
-
| 'device-settings-get'
|
|
7
|
-
| 'device-settings-set'
|
|
8
|
-
| 'apply-settings'
|
|
9
|
-
| 'unlock'
|
|
10
|
-
| 'cache';
|
|
11
|
-
|
|
12
3
|
/**
|
|
13
4
|
* Merge a field-level device-state patch.
|
|
14
5
|
*
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
|
|
2
2
|
|
|
3
3
|
import { buildProtocolV1FeaturesPayload } from '../deviceProfile/buildDeviceFeatures';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
mapLanguageFromProtocolV2,
|
|
6
|
+
mapLanguageToProtocolV2,
|
|
7
|
+
normalizeSafetyCheckLevel,
|
|
8
|
+
} from '../utils/deviceSettings';
|
|
5
9
|
|
|
6
10
|
import type {
|
|
7
11
|
ApplySettings,
|
|
@@ -289,9 +293,10 @@ export const mapApplySettingsToState = (settings: ApplySettings): DeviceStatePat
|
|
|
289
293
|
autoShutdownDelayMs: settings.auto_shutdown_delay_ms,
|
|
290
294
|
displayRotation: settings.display_rotation,
|
|
291
295
|
passphraseAlwaysOnDevice: settings.passphrase_always_on_device,
|
|
292
|
-
safetyChecks: settings.safety_checks,
|
|
296
|
+
safetyChecks: normalizeSafetyCheckLevel(settings.safety_checks),
|
|
293
297
|
experimentalFeatures: settings.experimental_features,
|
|
294
298
|
hapticFeedback: settings.haptic_feedback,
|
|
299
|
+
bleEnabled: settings.use_ble,
|
|
295
300
|
});
|
|
296
301
|
return {
|
|
297
302
|
...(Object.keys(identity).length ? { identity } : {}),
|
|
@@ -301,14 +306,11 @@ export const mapApplySettingsToState = (settings: ApplySettings): DeviceStatePat
|
|
|
301
306
|
};
|
|
302
307
|
|
|
303
308
|
export const mapDeviceSettingsToState = (settings: DeviceSettings): DeviceStatePatch => {
|
|
304
|
-
const settingsWithExperimental = settings as DeviceSettings & {
|
|
305
|
-
experimental_features?: boolean;
|
|
306
|
-
};
|
|
307
309
|
const identity = definedEntries({ label: settings.label });
|
|
308
310
|
const status = definedEntries({ passphraseProtection: settings.passphrase_enable });
|
|
309
311
|
const stateSettings = definedEntries({
|
|
310
312
|
bleEnabled: settings.bt_enable,
|
|
311
|
-
language: settings.language,
|
|
313
|
+
language: mapLanguageFromProtocolV2(settings.language),
|
|
312
314
|
wallpaperPath: settings.wallpaper_path,
|
|
313
315
|
brightness: settings.brightness,
|
|
314
316
|
autoLockDelayMs: settings.autolock_delay_ms,
|
|
@@ -319,7 +321,6 @@ export const mapDeviceSettingsToState = (settings: DeviceSettings): DeviceStateP
|
|
|
319
321
|
deviceNameDisplayEnabled: settings.device_name_display_enabled,
|
|
320
322
|
airgapMode: settings.airgap_mode,
|
|
321
323
|
fidoEnabled: settings.fido_enabled,
|
|
322
|
-
experimentalFeatures: settingsWithExperimental.experimental_features,
|
|
323
324
|
usbLockEnabled: settings.usb_lock_enable,
|
|
324
325
|
randomKeypad: settings.random_keypad,
|
|
325
326
|
});
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
resolveDeviceSerialNo,
|
|
11
11
|
resolveDeviceType,
|
|
12
12
|
} from '../utils/deviceFeaturesCompat';
|
|
13
|
+
import { normalizeSafetyCheckLevel } from '../utils/deviceSettings';
|
|
13
14
|
|
|
14
15
|
import type {
|
|
15
16
|
DeviceFirmwareImageInfo,
|
|
@@ -121,7 +122,7 @@ export const buildProtocolV1FeaturesPayload = (
|
|
|
121
122
|
passphraseAlwaysOnDevice: features.passphrase_always_on_device ?? null,
|
|
122
123
|
attachToPinEnabled: features.attach_to_pin_user ?? previous?.attachToPinEnabled ?? null,
|
|
123
124
|
unlockedAttachPin: features.unlocked_attach_pin ?? previous?.unlockedAttachPin ?? undefined,
|
|
124
|
-
safetyChecks: features.safety_checks ?? null,
|
|
125
|
+
safetyChecks: normalizeSafetyCheckLevel(features.safety_checks) ?? null,
|
|
125
126
|
autoLockDelayMs: features.auto_lock_delay_ms ?? null,
|
|
126
127
|
autoShutdownDelayMs: features.auto_shutdown_delay_ms ?? null,
|
|
127
128
|
displayRotation: features.display_rotation ?? null,
|
package/src/events/call.ts
CHANGED
|
@@ -53,12 +53,6 @@ export interface IFrameCancelMessage {
|
|
|
53
53
|
payload: { connectId?: string };
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
export interface IFrameCancelOperationMessage {
|
|
57
|
-
event: typeof IFRAME.CANCEL_OPERATION;
|
|
58
|
-
type: typeof IFRAME.CANCEL_OPERATION;
|
|
59
|
-
payload: { operationId: string };
|
|
60
|
-
}
|
|
61
|
-
|
|
62
56
|
export interface IFrameSwitchTransportMessage {
|
|
63
57
|
event: typeof IFRAME.SWITCH_TRANSPORT;
|
|
64
58
|
type: typeof IFRAME.SWITCH_TRANSPORT;
|
package/src/events/core.ts
CHANGED
|
@@ -5,7 +5,6 @@ import type {
|
|
|
5
5
|
IFrameCallMessage,
|
|
6
6
|
IFrameCallbackMessage,
|
|
7
7
|
IFrameCancelMessage,
|
|
8
|
-
IFrameCancelOperationMessage,
|
|
9
8
|
IFrameSwitchTransportMessage,
|
|
10
9
|
} from './call';
|
|
11
10
|
import type { DeviceEventMessage } from './device';
|
|
@@ -24,7 +23,6 @@ export type CoreMessage = {
|
|
|
24
23
|
| IFrameEventMessage
|
|
25
24
|
| IFrameCallMessage
|
|
26
25
|
| IFrameCancelMessage
|
|
27
|
-
| IFrameCancelOperationMessage
|
|
28
26
|
| IFrameSwitchTransportMessage
|
|
29
27
|
| IFrameCallbackMessage
|
|
30
28
|
| UiResponseMessage
|
package/src/events/iframe.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -27,7 +27,6 @@ const HardwareSdk = ({
|
|
|
27
27
|
eventEmitter,
|
|
28
28
|
uiResponse,
|
|
29
29
|
cancel,
|
|
30
|
-
cancelOperation,
|
|
31
30
|
updateSettings,
|
|
32
31
|
switchTransport,
|
|
33
32
|
}: InjectApi): CoreApi =>
|
|
@@ -38,7 +37,6 @@ const HardwareSdk = ({
|
|
|
38
37
|
eventEmitter,
|
|
39
38
|
uiResponse,
|
|
40
39
|
cancel,
|
|
41
|
-
cancelOperation,
|
|
42
40
|
updateSettings,
|
|
43
41
|
switchTransport,
|
|
44
42
|
});
|
|
@@ -51,7 +49,6 @@ const HardwareSDKLowLevel = ({
|
|
|
51
49
|
addHardwareGlobalEventListener,
|
|
52
50
|
uiResponse,
|
|
53
51
|
cancel,
|
|
54
|
-
cancelOperation,
|
|
55
52
|
updateSettings,
|
|
56
53
|
switchTransport,
|
|
57
54
|
}: LowLevelInjectApi): LowLevelCoreApi =>
|
|
@@ -63,7 +60,6 @@ const HardwareSDKLowLevel = ({
|
|
|
63
60
|
addHardwareGlobalEventListener,
|
|
64
61
|
uiResponse,
|
|
65
62
|
cancel,
|
|
66
|
-
cancelOperation,
|
|
67
63
|
updateSettings,
|
|
68
64
|
switchTransport,
|
|
69
65
|
});
|
package/src/inject.ts
CHANGED
|
@@ -36,14 +36,12 @@ export interface InjectApi {
|
|
|
36
36
|
dispose: CoreApi['dispose'];
|
|
37
37
|
uiResponse: CoreApi['uiResponse'];
|
|
38
38
|
cancel: CoreApi['cancel'];
|
|
39
|
-
cancelOperation: CoreApi['cancelOperation'];
|
|
40
39
|
switchTransport: CoreApi['switchTransport'];
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
export const inject = ({
|
|
44
43
|
call,
|
|
45
44
|
cancel,
|
|
46
|
-
cancelOperation,
|
|
47
45
|
dispose,
|
|
48
46
|
eventEmitter,
|
|
49
47
|
init,
|
|
@@ -75,7 +73,6 @@ export const inject = ({
|
|
|
75
73
|
uiResponse,
|
|
76
74
|
|
|
77
75
|
cancel,
|
|
78
|
-
cancelOperation,
|
|
79
76
|
|
|
80
77
|
updateSettings,
|
|
81
78
|
|
|
@@ -99,7 +96,6 @@ export const createCoreApi = (
|
|
|
99
96
|
| 'dispose'
|
|
100
97
|
| 'uiResponse'
|
|
101
98
|
| 'cancel'
|
|
102
|
-
| 'cancelOperation'
|
|
103
99
|
| 'updateSettings'
|
|
104
100
|
| 'switchTransport'
|
|
105
101
|
> => ({
|
|
@@ -166,8 +162,6 @@ export const createCoreApi = (
|
|
|
166
162
|
call({ ...params, connectId, method: 'deviceUploadWallpaper' }),
|
|
167
163
|
deviceInfoGet: (connectId, params) => call({ ...params, connectId, method: 'deviceInfoGet' }),
|
|
168
164
|
deviceStatusGet: (connectId, params) => call({ ...params, connectId, method: 'deviceStatusGet' }),
|
|
169
|
-
deviceSettingsGet: (connectId, params) =>
|
|
170
|
-
call({ ...params, connectId, method: 'deviceSettingsGet' }),
|
|
171
165
|
protocolInfoRequest: (connectId, params) =>
|
|
172
166
|
call({ ...params, connectId, method: 'protocolInfoRequest' }),
|
|
173
167
|
ping: (connectId, params) => call({ ...params, connectId, method: 'ping' }),
|
|
@@ -179,10 +173,6 @@ export const createCoreApi = (
|
|
|
179
173
|
call({ ...params, connectId, method: 'deviceFactoryInfoSet' }),
|
|
180
174
|
deviceFactoryInfoGet: (connectId, params) =>
|
|
181
175
|
call({ ...params, connectId, method: 'deviceFactoryInfoGet' }),
|
|
182
|
-
deviceSettingsSet: (connectId, params) =>
|
|
183
|
-
call({ ...params, connectId, method: 'deviceSettingsSet' }),
|
|
184
|
-
deviceSettingsPageShow: (connectId, params) =>
|
|
185
|
-
call({ ...params, connectId, method: 'deviceSettingsPageShow' }),
|
|
186
176
|
filesystemPermissionFix: (connectId, params) =>
|
|
187
177
|
call({ ...params, connectId, method: 'filesystemPermissionFix' }),
|
|
188
178
|
filesystemFormat: connectId => call({ connectId, method: 'filesystemFormat' }),
|
package/src/lowLevelInject.ts
CHANGED
|
@@ -13,7 +13,6 @@ export interface LowLevelInjectApi {
|
|
|
13
13
|
dispose: CoreApi['dispose'];
|
|
14
14
|
uiResponse: CoreApi['uiResponse'];
|
|
15
15
|
cancel: CoreApi['cancel'];
|
|
16
|
-
cancelOperation: CoreApi['cancelOperation'];
|
|
17
16
|
updateSettings: CoreApi['updateSettings'];
|
|
18
17
|
switchTransport: CoreApi['switchTransport'];
|
|
19
18
|
addHardwareGlobalEventListener: (listener: IAddHardwareGlobalEventListener) => void;
|
|
@@ -26,7 +25,6 @@ export type LowLevelCoreApi = Omit<CoreApi, 'on' | 'off'> & {
|
|
|
26
25
|
export const lowLevelInject = ({
|
|
27
26
|
call,
|
|
28
27
|
cancel,
|
|
29
|
-
cancelOperation,
|
|
30
28
|
dispose,
|
|
31
29
|
eventEmitter,
|
|
32
30
|
init,
|
|
@@ -50,7 +48,6 @@ export const lowLevelInject = ({
|
|
|
50
48
|
uiResponse,
|
|
51
49
|
|
|
52
50
|
cancel,
|
|
53
|
-
cancelOperation,
|
|
54
51
|
|
|
55
52
|
updateSettings,
|
|
56
53
|
|
package/src/topLevelInject.ts
CHANGED
|
@@ -52,8 +52,6 @@ export const topLevelInject = () => {
|
|
|
52
52
|
|
|
53
53
|
cancel: (connectId?: string) => lowLevelApi?.cancel(connectId),
|
|
54
54
|
|
|
55
|
-
cancelOperation: (operationId: string) => lowLevelApi?.cancelOperation(operationId),
|
|
56
|
-
|
|
57
55
|
updateSettings: settings => lowLevelApi?.updateSettings(settings) ?? Promise.resolve(false),
|
|
58
56
|
|
|
59
57
|
switchTransport: (env: ConnectSettings['env']) =>
|