@onekeyfe/hd-core 1.2.0-alpha.0 → 1.2.0-alpha.1
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__/protocol-v2.test.ts +147 -264
- package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +2 -2
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/GetDeviceInfo.d.ts.map +1 -1
- package/dist/api/GetFeatures.d.ts +1 -1
- package/dist/api/GetOnekeyFeatures.d.ts.map +1 -1
- package/dist/api/GetPassphraseState.d.ts +4 -4
- package/dist/api/device/DeviceUnlock.d.ts +1 -1
- package/dist/api/firmware/bootloaderHelper.d.ts.map +1 -1
- package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
- package/dist/api/protocol-v2/helpers.d.ts +2 -3
- package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
- package/dist/constants/index.d.ts +2 -1
- package/dist/constants/index.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/data-manager/connectSettings.d.ts.map +1 -1
- package/dist/device/Device.d.ts +11 -15
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/deviceProfile/buildDeviceFeatures.d.ts +6 -0
- package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -0
- package/dist/deviceProfile/buildDeviceProfile.d.ts +3 -4
- package/dist/deviceProfile/buildDeviceProfile.d.ts.map +1 -1
- package/dist/deviceProfile/index.d.ts +1 -1
- package/dist/deviceProfile/index.d.ts.map +1 -1
- package/dist/index.d.ts +579 -502
- package/dist/index.js +881 -931
- package/dist/protocols/protocol-v2/features.d.ts +2 -2
- package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
- package/dist/types/api/getDeviceInfo.d.ts +2 -2
- package/dist/types/api/getDeviceInfo.d.ts.map +1 -1
- package/dist/types/api/getPassphraseState.d.ts +4 -4
- package/dist/types/api/getPassphraseState.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +3 -3
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/device.d.ts +87 -8
- package/dist/types/device.d.ts.map +1 -1
- package/dist/types/settings.d.ts +1 -0
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/capabilitieUtils.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
- package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
- package/dist/utils/deviceVersionUtils.d.ts.map +1 -1
- package/dist/utils/findDefectiveBatchDevice.d.ts +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/FirmwareUpdateV2.ts +9 -2
- package/src/api/FirmwareUpdateV3.ts +2 -1
- package/src/api/FirmwareUpdateV4.ts +24 -31
- package/src/api/GetDeviceInfo.ts +6 -13
- package/src/api/GetOnekeyFeatures.ts +3 -14
- package/src/api/GetPassphraseState.ts +4 -4
- package/src/api/firmware/bootloaderHelper.ts +3 -2
- package/src/api/firmware/getBinary.ts +1 -1
- package/src/api/firmware/releaseHelper.ts +3 -3
- package/src/api/firmware/uploadFirmware.ts +5 -2
- package/src/api/protocol-v2/DeviceReboot.ts +3 -3
- package/src/api/protocol-v2/helpers.ts +1 -26
- package/src/constants/index.ts +10 -1
- package/src/core/index.ts +5 -7
- package/src/data/messages/messages-protocol-v2.json +329 -323
- package/src/data-manager/DataManager.ts +4 -4
- package/src/data-manager/connectSettings.ts +6 -0
- package/src/device/Device.ts +86 -241
- package/src/device/DevicePool.ts +9 -9
- package/src/deviceProfile/buildDeviceFeatures.ts +368 -0
- package/src/deviceProfile/buildDeviceProfile.ts +101 -155
- package/src/deviceProfile/index.ts +4 -1
- package/src/protocols/protocol-v2/features.ts +14 -16
- package/src/types/api/getDeviceInfo.ts +2 -2
- package/src/types/api/getPassphraseState.ts +4 -4
- package/src/types/api/protocolV2.ts +2 -3
- package/src/types/device.ts +97 -34
- package/src/types/settings.ts +5 -0
- package/src/utils/capabilitieUtils.ts +1 -2
- package/src/utils/deviceFeaturesUtils.ts +11 -17
- package/src/utils/deviceInfoUtils.ts +11 -29
- package/src/utils/deviceVersionUtils.ts +7 -25
- package/src/utils/findDefectiveBatchDevice.ts +6 -6
- package/dist/deviceProfile/legacyFeaturesView.d.ts +0 -5
- package/dist/deviceProfile/legacyFeaturesView.d.ts.map +0 -1
- package/src/deviceProfile/legacyFeaturesView.ts +0 -123
|
@@ -114,11 +114,11 @@ export default class DataManager {
|
|
|
114
114
|
|
|
115
115
|
const deviceFirmwareType = getFirmwareType(features);
|
|
116
116
|
const deviceFirmwareVersion = getDeviceFirmwareVersion(features);
|
|
117
|
-
if (features.
|
|
117
|
+
if (features.firmwarePresent === false) {
|
|
118
118
|
return 'none';
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
if (DeviceModelToTypes.model_mini.includes(deviceType) && features.
|
|
121
|
+
if (DeviceModelToTypes.model_mini.includes(deviceType) && features.bootloaderMode) {
|
|
122
122
|
return 'unknown';
|
|
123
123
|
}
|
|
124
124
|
|
|
@@ -264,8 +264,8 @@ export default class DataManager {
|
|
|
264
264
|
const targetDeviceConfigList = this.deviceMap[deviceType]?.[firmwareUpdateField] ?? [];
|
|
265
265
|
|
|
266
266
|
if (
|
|
267
|
-
features.
|
|
268
|
-
(DeviceModelToTypes.model_classic.includes(deviceType) && features.
|
|
267
|
+
features.firmwarePresent === false ||
|
|
268
|
+
(DeviceModelToTypes.model_classic.includes(deviceType) && features.bootloaderMode)
|
|
269
269
|
) {
|
|
270
270
|
// Always return least changelog
|
|
271
271
|
return getReleaseChangelog(targetDeviceConfigList, '0.0.0');
|
|
@@ -27,6 +27,8 @@ const initialSettings: ConnectSettings = {
|
|
|
27
27
|
env: 'web',
|
|
28
28
|
lazyLoad: false,
|
|
29
29
|
timestamp: new Date().getTime(),
|
|
30
|
+
// 临时开关:仅用于本地/测试固件兼容;正式链路默认调用真实 DevGetDeviceInfo。
|
|
31
|
+
protocolV2DeviceInfoMockEnabled: false,
|
|
30
32
|
};
|
|
31
33
|
|
|
32
34
|
export const getEnv = () => {
|
|
@@ -115,6 +117,10 @@ export const parseConnectSettings = (input: Partial<ConnectSettings> = {}) => {
|
|
|
115
117
|
settings.fetchConfig = input.fetchConfig;
|
|
116
118
|
}
|
|
117
119
|
|
|
120
|
+
if (typeof input.protocolV2DeviceInfoMockEnabled === 'boolean') {
|
|
121
|
+
settings.protocolV2DeviceInfoMockEnabled = input.protocolV2DeviceInfoMockEnabled;
|
|
122
|
+
}
|
|
123
|
+
|
|
118
124
|
return settings;
|
|
119
125
|
};
|
|
120
126
|
|
package/src/device/Device.ts
CHANGED
|
@@ -40,7 +40,6 @@ import {
|
|
|
40
40
|
type Features,
|
|
41
41
|
type IDeviceModel,
|
|
42
42
|
type IDeviceType,
|
|
43
|
-
type IVersionArray,
|
|
44
43
|
type IVersionRange,
|
|
45
44
|
type SupportFeatureType,
|
|
46
45
|
type UnavailableCapabilities,
|
|
@@ -54,11 +53,7 @@ import {
|
|
|
54
53
|
PROTOCOL_V2_STATUS_DEVICE_INFO_REQUEST,
|
|
55
54
|
requestProtocolV2DeviceInfo,
|
|
56
55
|
} from '../protocols/protocol-v2/features';
|
|
57
|
-
import {
|
|
58
|
-
buildProfileFromProtocolV1,
|
|
59
|
-
buildProfileFromProtocolV2,
|
|
60
|
-
buildProtocolV2GetFeaturesPayload,
|
|
61
|
-
} from '../deviceProfile';
|
|
56
|
+
import { buildProtocolV1FeaturesPayload, buildProtocolV2FeaturesPayload } from '../deviceProfile';
|
|
62
57
|
|
|
63
58
|
import type { PROTO } from '../constants';
|
|
64
59
|
import type {
|
|
@@ -74,7 +69,6 @@ import type {
|
|
|
74
69
|
Success,
|
|
75
70
|
} from '@onekeyfe/hd-transport';
|
|
76
71
|
import type DeviceConnector from './DeviceConnector';
|
|
77
|
-
import type { DeviceProfile } from '../types/api/getDeviceInfo';
|
|
78
72
|
|
|
79
73
|
export type InitOptions = {
|
|
80
74
|
initSession?: boolean;
|
|
@@ -96,11 +90,6 @@ const parseRunOptions = (options?: RunOptions): RunOptions => {
|
|
|
96
90
|
|
|
97
91
|
const Log = getLogger(LoggerNames.Device);
|
|
98
92
|
|
|
99
|
-
const profileVersionToArray = (version?: string | null): IVersionArray | null => {
|
|
100
|
-
if (!version) return null;
|
|
101
|
-
return version.split('.').map(part => Number(part) || 0) as IVersionArray;
|
|
102
|
-
};
|
|
103
|
-
|
|
104
93
|
export interface DeviceEvents {
|
|
105
94
|
[DEVICE.PIN]: [Device, PROTO.PinMatrixRequestType | undefined, (err: any, pin: string) => void];
|
|
106
95
|
[DEVICE.PASSPHRASE_ON_DEVICE]: [Device, ((response: any) => void)?];
|
|
@@ -198,32 +187,12 @@ export class Device extends EventEmitter {
|
|
|
198
187
|
/**
|
|
199
188
|
* 唯一设备状态缓存。
|
|
200
189
|
*
|
|
201
|
-
* V1 直接保存原生 Features;V2 保存由 DevGetDeviceInfo
|
|
202
|
-
*
|
|
203
|
-
*
|
|
190
|
+
* V1 直接保存原生 Features;V2 保存由 DevGetDeviceInfo 映射出的
|
|
191
|
+
* Features 视图。Device 不再保存 profile,结构化 DeviceProfile 只作为
|
|
192
|
+
* getDeviceInfo() 的 API 返回值存在。
|
|
204
193
|
*/
|
|
205
194
|
features: Features | undefined = undefined;
|
|
206
195
|
|
|
207
|
-
private getFeaturesProfile(): DeviceProfile | undefined {
|
|
208
|
-
if (!this.features) return undefined;
|
|
209
|
-
const protocol = this.originalDescriptor.protocolType === 'V2' ? 'V2' : 'V1';
|
|
210
|
-
return buildProfileFromProtocolV1({
|
|
211
|
-
protocol,
|
|
212
|
-
features: this.features,
|
|
213
|
-
onekeyFeatures: this.features as Parameters<typeof buildProfileFromProtocolV1>[0]['onekeyFeatures'],
|
|
214
|
-
sources: ['features'],
|
|
215
|
-
scope: 'verify',
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
get profile(): DeviceProfile | undefined {
|
|
220
|
-
return this.getFeaturesProfile();
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
set profile(profile: DeviceProfile | undefined) {
|
|
224
|
-
this.updateProfile(profile);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
196
|
/**
|
|
228
197
|
* 是否需要更新设备信息。
|
|
229
198
|
*
|
|
@@ -292,13 +261,14 @@ export class Device extends EventEmitter {
|
|
|
292
261
|
const bleName = this.getCurrentBleName();
|
|
293
262
|
const label = this.getCurrentLabel();
|
|
294
263
|
const serialNo = this.getCurrentSerialNo();
|
|
264
|
+
const connectId = this.getConnectId();
|
|
295
265
|
const deviceId = this.getCurrentDeviceId() || null;
|
|
296
266
|
|
|
297
267
|
const features = this.features;
|
|
298
268
|
|
|
299
269
|
return {
|
|
300
270
|
/** Android uses Mac address, iOS uses uuid, USB uses uuid */
|
|
301
|
-
connectId: DataManager.isBleConnect(env) ? this.mainId || null :
|
|
271
|
+
connectId: DataManager.isBleConnect(env) ? this.mainId || null : connectId,
|
|
302
272
|
/** Hardware ID, will not change at any time */
|
|
303
273
|
uuid: serialNo,
|
|
304
274
|
commType: this.originalDescriptor.commType,
|
|
@@ -314,8 +284,7 @@ export class Device extends EventEmitter {
|
|
|
314
284
|
label: label || 'OneKey',
|
|
315
285
|
mode: this.getMode(),
|
|
316
286
|
features,
|
|
317
|
-
|
|
318
|
-
sessionId: this.features?.session_id ?? null,
|
|
287
|
+
sessionId: this.features?.sessionId ?? null,
|
|
319
288
|
firmwareVersion: this.getFirmwareVersion(),
|
|
320
289
|
bleFirmwareVersion: this.getBLEFirmwareVersion(),
|
|
321
290
|
unavailableCapabilities: this.unavailableCapabilities,
|
|
@@ -509,9 +478,9 @@ export class Device extends EventEmitter {
|
|
|
509
478
|
* 唯一协议判别器。
|
|
510
479
|
*
|
|
511
480
|
* descriptor.protocolType 是协议探测后的结果;V2 features 由 DevGetDeviceInfo
|
|
512
|
-
*
|
|
481
|
+
* 映射产生。
|
|
513
482
|
* 全 SDK 的协议分支都必须走这里,不要直接读 originalDescriptor.protocolType
|
|
514
|
-
*
|
|
483
|
+
* 或从 features 反推。
|
|
515
484
|
*/
|
|
516
485
|
getProtocol(): 'V1' | 'V2' {
|
|
517
486
|
return this.originalDescriptor.protocolType === 'V2' ? 'V2' : 'V1';
|
|
@@ -522,57 +491,54 @@ export class Device extends EventEmitter {
|
|
|
522
491
|
}
|
|
523
492
|
|
|
524
493
|
getCurrentDeviceType() {
|
|
525
|
-
return
|
|
494
|
+
return getDeviceType(this.features);
|
|
526
495
|
}
|
|
527
496
|
|
|
528
497
|
getCurrentDeviceId() {
|
|
529
|
-
|
|
530
|
-
return this.profile.deviceId || undefined;
|
|
531
|
-
}
|
|
532
|
-
return this.features?.device_id || undefined;
|
|
498
|
+
return this.features?.deviceId || undefined;
|
|
533
499
|
}
|
|
534
500
|
|
|
535
501
|
getCurrentSerialNo() {
|
|
536
|
-
if (this.profile) {
|
|
537
|
-
return this.profile.serialNo || '';
|
|
538
|
-
}
|
|
539
502
|
return this.features ? getDeviceUUID(this.features) : '';
|
|
540
503
|
}
|
|
541
504
|
|
|
505
|
+
getConnectId() {
|
|
506
|
+
const serialNo = this.getCurrentSerialNo();
|
|
507
|
+
if (serialNo) return serialNo;
|
|
508
|
+
|
|
509
|
+
// connectId 是 SDK 内部连接路由 key;Protocol V2 早期固件/mock
|
|
510
|
+
// 可能还没有 serial_no,此时用 transport descriptor 兜底,不改变
|
|
511
|
+
// features.serialNo / deviceId 的业务语义。
|
|
512
|
+
return this.originalDescriptor.path || this.originalDescriptor.id || '';
|
|
513
|
+
}
|
|
514
|
+
|
|
542
515
|
getCurrentBleName() {
|
|
543
|
-
|
|
544
|
-
if (this.isProtocolV2()) return this.profile?.bleName ?? null;
|
|
545
|
-
return this.profile?.bleName ?? getDeviceBleName(this.features);
|
|
516
|
+
return getDeviceBleName(this.features);
|
|
546
517
|
}
|
|
547
518
|
|
|
548
519
|
getCurrentLabel() {
|
|
549
|
-
|
|
550
|
-
return this.profile?.label ?? getDeviceLabel(this.features);
|
|
520
|
+
return getDeviceLabel(this.features);
|
|
551
521
|
}
|
|
552
522
|
|
|
553
523
|
getCurrentPassphraseProtection() {
|
|
554
|
-
|
|
555
|
-
return this.profile.status.passphraseProtection;
|
|
556
|
-
}
|
|
557
|
-
return this.features?.passphrase_protection;
|
|
524
|
+
return this.features?.passphraseProtection;
|
|
558
525
|
}
|
|
559
526
|
|
|
560
527
|
getCurrentFirmwareType() {
|
|
561
|
-
return
|
|
528
|
+
return getFirmwareType(this.features);
|
|
562
529
|
}
|
|
563
530
|
|
|
564
531
|
getCurrentFirmwareVersionString() {
|
|
565
|
-
return
|
|
532
|
+
return getDeviceFirmwareVersion(this.features)?.join('.');
|
|
566
533
|
}
|
|
567
534
|
|
|
568
535
|
getCurrentBLEFirmwareVersionString() {
|
|
569
|
-
if (this.profile?.versions.ble) return this.profile.versions.ble;
|
|
570
536
|
if (!this.features) return undefined;
|
|
571
537
|
return getDeviceBLEFirmwareVersion(this.features).join('.');
|
|
572
538
|
}
|
|
573
539
|
|
|
574
540
|
getCurrentSafetyChecks() {
|
|
575
|
-
return this.features?.
|
|
541
|
+
return this.features?.safetyChecks;
|
|
576
542
|
}
|
|
577
543
|
|
|
578
544
|
getCurrentMethodVersionRange(
|
|
@@ -663,17 +629,25 @@ export class Device extends EventEmitter {
|
|
|
663
629
|
return deviceId;
|
|
664
630
|
}
|
|
665
631
|
|
|
632
|
+
private getSessionCacheDeviceKey(_deviceId?: string) {
|
|
633
|
+
const deviceId = _deviceId || this.getCurrentDeviceId();
|
|
634
|
+
if (deviceId) return deviceId;
|
|
635
|
+
if (this.isProtocolV2()) {
|
|
636
|
+
return this.originalDescriptor.path || this.originalDescriptor.id;
|
|
637
|
+
}
|
|
638
|
+
return undefined;
|
|
639
|
+
}
|
|
640
|
+
|
|
666
641
|
getInternalState(_deviceId?: string) {
|
|
667
642
|
Log.debug('getInternalState session cache: ', deviceSessionCache);
|
|
668
643
|
Log.debug(
|
|
669
644
|
'getInternalState session param: ',
|
|
670
645
|
`device_id: ${_deviceId}`,
|
|
671
|
-
`features.
|
|
672
|
-
`profile.deviceId: ${this.profile?.deviceId}`,
|
|
646
|
+
`features.deviceId: ${this.features?.deviceId}`,
|
|
673
647
|
`passphraseState: ${this.passphraseState}`
|
|
674
648
|
);
|
|
675
649
|
|
|
676
|
-
const deviceId =
|
|
650
|
+
const deviceId = this.getSessionCacheDeviceKey(_deviceId);
|
|
677
651
|
if (!deviceId) return undefined;
|
|
678
652
|
// Security invariant: no passphraseState → no session lookup.
|
|
679
653
|
// A previous fallback that scanned `${deviceId}@*` keys could silently
|
|
@@ -691,7 +665,7 @@ export class Device extends EventEmitter {
|
|
|
691
665
|
updateInternalState(
|
|
692
666
|
enablePassphrase: boolean,
|
|
693
667
|
passphraseState: string | undefined,
|
|
694
|
-
deviceId: string,
|
|
668
|
+
deviceId: string | undefined,
|
|
695
669
|
sessionId: string | null = null,
|
|
696
670
|
featuresSessionId: string | null = null
|
|
697
671
|
) {
|
|
@@ -704,17 +678,21 @@ export class Device extends EventEmitter {
|
|
|
704
678
|
`featuresSessionId: ${featuresSessionId}`
|
|
705
679
|
);
|
|
706
680
|
|
|
681
|
+
const cacheDeviceKey = this.getSessionCacheDeviceKey(deviceId);
|
|
682
|
+
if (!cacheDeviceKey) return;
|
|
683
|
+
|
|
707
684
|
if (enablePassphrase) {
|
|
708
685
|
// update the sessionId
|
|
709
686
|
if (sessionId) {
|
|
710
|
-
deviceSessionCache[this.generateStateKey(
|
|
687
|
+
deviceSessionCache[this.generateStateKey(cacheDeviceKey, passphraseState)] = sessionId;
|
|
711
688
|
} else if (featuresSessionId) {
|
|
712
|
-
deviceSessionCache[this.generateStateKey(
|
|
689
|
+
deviceSessionCache[this.generateStateKey(cacheDeviceKey, passphraseState)] =
|
|
690
|
+
featuresSessionId;
|
|
713
691
|
}
|
|
714
692
|
}
|
|
715
693
|
|
|
716
694
|
// delete the old sessionId
|
|
717
|
-
const oldKey = `${
|
|
695
|
+
const oldKey = `${cacheDeviceKey}`;
|
|
718
696
|
if (deviceSessionCache[oldKey]) {
|
|
719
697
|
delete deviceSessionCache[oldKey];
|
|
720
698
|
}
|
|
@@ -727,14 +705,13 @@ export class Device extends EventEmitter {
|
|
|
727
705
|
'setInternalState session param: ',
|
|
728
706
|
`state: ${state}`,
|
|
729
707
|
`initSession: ${initSession}`,
|
|
730
|
-
`
|
|
731
|
-
`profile.deviceId: ${this.profile?.deviceId}`,
|
|
708
|
+
`deviceId: ${this.features?.deviceId}`,
|
|
732
709
|
`passphraseState: ${this.passphraseState}`
|
|
733
710
|
);
|
|
734
711
|
|
|
735
712
|
if (!this.passphraseState && !initSession) return;
|
|
736
713
|
|
|
737
|
-
const deviceId = this.
|
|
714
|
+
const deviceId = this.getSessionCacheDeviceKey();
|
|
738
715
|
if (!deviceId) return;
|
|
739
716
|
|
|
740
717
|
const key = this.generateStateKey(deviceId, this.passphraseState);
|
|
@@ -748,7 +725,7 @@ export class Device extends EventEmitter {
|
|
|
748
725
|
clearInternalState(_deviceId?: string) {
|
|
749
726
|
Log.debug('clearInternalState param: ', _deviceId);
|
|
750
727
|
|
|
751
|
-
const deviceId =
|
|
728
|
+
const deviceId = this.getSessionCacheDeviceKey(_deviceId);
|
|
752
729
|
if (!deviceId) return;
|
|
753
730
|
const key = `${deviceId}`;
|
|
754
731
|
delete deviceSessionCache[key];
|
|
@@ -763,11 +740,10 @@ export class Device extends EventEmitter {
|
|
|
763
740
|
// Protocol V2 不支持传统 Initialize,直接使用协议专用初始化流程。
|
|
764
741
|
if (this.isProtocolV2()) {
|
|
765
742
|
this.passphraseState = options?.passphraseState;
|
|
766
|
-
if (this.
|
|
767
|
-
// 不能直接信任缓存
|
|
768
|
-
//
|
|
769
|
-
//
|
|
770
|
-
// 不会降级已有的 verify / SE versions 数据。
|
|
743
|
+
if (this.features && !this.featuresNeedsReload && !options?.initSession) {
|
|
744
|
+
// 不能直接信任缓存 features:设备端 wipe / 完成初始化 / 改 label 后
|
|
745
|
+
// features 会永久陈旧。每次 run 做一次轻量 status 刷新(不含 fw/SE),
|
|
746
|
+
// 用字段级合并保留已有版本和 SE 信息。
|
|
771
747
|
await this._refreshProtocolV2Status();
|
|
772
748
|
return;
|
|
773
749
|
}
|
|
@@ -821,11 +797,11 @@ export class Device extends EventEmitter {
|
|
|
821
797
|
/**
|
|
822
798
|
* Device initialization over Protocol V2.
|
|
823
799
|
*
|
|
824
|
-
* Protocol V2 不走传统 Initialize/GetFeatures
|
|
825
|
-
*
|
|
800
|
+
* Protocol V2 不走传统 Initialize/GetFeatures;直接用 DevGetDeviceInfo
|
|
801
|
+
* 生成唯一的 features 状态。
|
|
826
802
|
*/
|
|
827
803
|
private async _initializeProtocolV2() {
|
|
828
|
-
Log.debug('Initialize device via Protocol V2
|
|
804
|
+
Log.debug('Initialize device via Protocol V2 features adapter');
|
|
829
805
|
|
|
830
806
|
try {
|
|
831
807
|
// 超时由 requestProtocolV2DeviceInfo 内部的 typedCall timeoutMs(默认 10s)负责,
|
|
@@ -836,16 +812,8 @@ export class Device extends EventEmitter {
|
|
|
836
812
|
});
|
|
837
813
|
// 默认请求不含 SE/hash 数据,scope 如实标注为 basic;
|
|
838
814
|
// 完整数据由 getDeviceInfo(scope:'verify'|'full') 获取。
|
|
839
|
-
const
|
|
840
|
-
|
|
841
|
-
deviceInfo,
|
|
842
|
-
sources: ['deviceInfo'],
|
|
843
|
-
scope: 'basic',
|
|
844
|
-
fallbackSerialNo: this.originalDescriptor?.path,
|
|
845
|
-
}),
|
|
846
|
-
deviceInfo
|
|
847
|
-
);
|
|
848
|
-
Log.debug('Protocol V2 profile:', profile);
|
|
815
|
+
const features = this.updateProtocolV2Features(deviceInfo);
|
|
816
|
+
Log.debug('Protocol V2 features:', features);
|
|
849
817
|
this.featuresNeedsReload = false;
|
|
850
818
|
} catch (error) {
|
|
851
819
|
Log.error('Protocol V2 initialization failed:', error);
|
|
@@ -857,8 +825,7 @@ export class Device extends EventEmitter {
|
|
|
857
825
|
* Protocol V2 的轻量状态刷新(每次 run 前调用)。
|
|
858
826
|
*
|
|
859
827
|
* 请求 hw + bt + status(不含 fw/SE target):status 提供 init_states / label /
|
|
860
|
-
* passphrase_protection 等会在设备端变化的字段;hw/bt 提供 serialNo / bleName
|
|
861
|
-
* 避免 applyProfileUpdate 的顶层字段覆盖把已有身份字段清空。
|
|
828
|
+
* passphrase_protection 等会在设备端变化的字段;hw/bt 提供 serialNo / bleName。
|
|
862
829
|
* versions 为空时按字段级合并保留旧值,verify 数据不会被降级。
|
|
863
830
|
*/
|
|
864
831
|
private async _refreshProtocolV2Status() {
|
|
@@ -867,16 +834,8 @@ export class Device extends EventEmitter {
|
|
|
867
834
|
commands: this.commands,
|
|
868
835
|
request: PROTOCOL_V2_STATUS_DEVICE_INFO_REQUEST,
|
|
869
836
|
});
|
|
870
|
-
const
|
|
871
|
-
|
|
872
|
-
deviceInfo,
|
|
873
|
-
sources: ['deviceInfo'],
|
|
874
|
-
scope: 'basic',
|
|
875
|
-
fallbackSerialNo: this.originalDescriptor?.path,
|
|
876
|
-
}),
|
|
877
|
-
deviceInfo
|
|
878
|
-
);
|
|
879
|
-
Log.debug('Protocol V2 profile (status refresh):', profile);
|
|
837
|
+
const features = this.updateProtocolV2Features(deviceInfo);
|
|
838
|
+
Log.debug('Protocol V2 features (status refresh):', features);
|
|
880
839
|
} catch (error) {
|
|
881
840
|
Log.error('Protocol V2 status refresh failed:', error);
|
|
882
841
|
throw error;
|
|
@@ -888,108 +847,43 @@ export class Device extends EventEmitter {
|
|
|
888
847
|
const deviceInfo = await requestProtocolV2DeviceInfo({
|
|
889
848
|
commands: this.commands,
|
|
890
849
|
});
|
|
891
|
-
|
|
892
|
-
buildProfileFromProtocolV2({
|
|
893
|
-
deviceInfo,
|
|
894
|
-
sources: ['deviceInfo'],
|
|
895
|
-
scope: 'basic',
|
|
896
|
-
fallbackSerialNo: this.originalDescriptor?.path,
|
|
897
|
-
}),
|
|
898
|
-
deviceInfo
|
|
899
|
-
);
|
|
900
|
-
return this.features ?? this.updateProtocolV2Features(profile, deviceInfo);
|
|
850
|
+
return this.updateProtocolV2Features(deviceInfo);
|
|
901
851
|
}
|
|
902
852
|
|
|
903
853
|
const { message } = await this.commands.typedCall('GetFeatures', 'Features', {});
|
|
904
854
|
this._updateFeatures(message);
|
|
905
|
-
return
|
|
855
|
+
return this.features;
|
|
906
856
|
}
|
|
907
857
|
|
|
908
|
-
_updateFeatures(
|
|
858
|
+
_updateFeatures(protoFeatures: PROTO.Features | Features, initSession?: boolean) {
|
|
859
|
+
let feat =
|
|
860
|
+
'protocol' in protoFeatures
|
|
861
|
+
? protoFeatures
|
|
862
|
+
: buildProtocolV1FeaturesPayload(protoFeatures, this.features);
|
|
863
|
+
|
|
909
864
|
// GetFeatures doesn't return 'session_id'
|
|
910
|
-
if (this.features &&
|
|
911
|
-
feat.
|
|
865
|
+
if (this.features?.sessionId && !feat.sessionId) {
|
|
866
|
+
feat.sessionId = this.features.sessionId;
|
|
912
867
|
}
|
|
913
|
-
if (this.getCurrentDeviceId() && feat.
|
|
914
|
-
this.setInternalState(feat.
|
|
868
|
+
if (this.getCurrentDeviceId() && feat.sessionId) {
|
|
869
|
+
this.setInternalState(feat.sessionId, initSession);
|
|
915
870
|
}
|
|
916
871
|
feat.unlocked = feat.unlocked ?? true;
|
|
917
872
|
|
|
918
873
|
feat = fixFeaturesFirmwareVersion(feat);
|
|
919
874
|
|
|
920
875
|
this.features = feat;
|
|
921
|
-
if (!this.isProtocolV2()) {
|
|
922
|
-
this.updateProfile(
|
|
923
|
-
buildProfileFromProtocolV1({
|
|
924
|
-
features: feat,
|
|
925
|
-
sources: ['features'],
|
|
926
|
-
})
|
|
927
|
-
);
|
|
928
|
-
}
|
|
929
876
|
this.featuresNeedsReload = false;
|
|
930
877
|
this.emit(DEVICE.FEATURES, this, feat);
|
|
931
878
|
}
|
|
932
879
|
|
|
933
|
-
|
|
934
|
-
if (!profile) {
|
|
935
|
-
this.features = undefined;
|
|
936
|
-
return;
|
|
937
|
-
}
|
|
938
|
-
if (profile.protocol === 'V2') {
|
|
939
|
-
this.updateProtocolV2Features(profile, profile.raw?.protocolV2DeviceInfo);
|
|
940
|
-
return;
|
|
941
|
-
}
|
|
942
|
-
if (profile.raw?.features) {
|
|
943
|
-
this.features = fixFeaturesFirmwareVersion(profile.raw.features);
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
/**
|
|
948
|
-
* 字段级合并刷新 profile,并返回合并后的结果。
|
|
949
|
-
*
|
|
950
|
-
* basic 范围的刷新(initialize / getFeatures)拿不到 SE 版本和 verify 数据,
|
|
951
|
-
* 不能整体替换掉 getDeviceInfo(scope:'verify'|'full') 建立的完整 profile。
|
|
952
|
-
*/
|
|
953
|
-
applyProfileUpdate(next: DeviceProfile, deviceInfo?: ProtocolV2DeviceInfo): DeviceProfile {
|
|
954
|
-
const prev = this.profile;
|
|
955
|
-
if (!prev || prev.protocol !== next.protocol) {
|
|
956
|
-
this.updateProfile({
|
|
957
|
-
...next,
|
|
958
|
-
raw: {
|
|
959
|
-
...next.raw,
|
|
960
|
-
...(deviceInfo ? { protocolV2DeviceInfo: deviceInfo } : {}),
|
|
961
|
-
},
|
|
962
|
-
});
|
|
963
|
-
return next;
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
const versions = { ...prev.versions };
|
|
967
|
-
for (const [key, value] of Object.entries(next.versions)) {
|
|
968
|
-
if (value != null) {
|
|
969
|
-
(versions as Record<string, string | null | undefined>)[key] = value;
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
const merged: DeviceProfile = {
|
|
974
|
-
...prev,
|
|
975
|
-
...next,
|
|
976
|
-
versions,
|
|
977
|
-
verify: next.verify ?? prev.verify,
|
|
978
|
-
raw: {
|
|
979
|
-
...prev.raw,
|
|
980
|
-
...next.raw,
|
|
981
|
-
...(deviceInfo ? { protocolV2DeviceInfo: deviceInfo } : {}),
|
|
982
|
-
},
|
|
983
|
-
};
|
|
984
|
-
this.updateProfile(merged);
|
|
985
|
-
return merged;
|
|
986
|
-
}
|
|
987
|
-
|
|
988
|
-
private updateProtocolV2Features(profile: DeviceProfile, deviceInfo?: ProtocolV2DeviceInfo) {
|
|
880
|
+
updateProtocolV2Features(deviceInfo?: ProtocolV2DeviceInfo) {
|
|
989
881
|
const features = fixFeaturesFirmwareVersion(
|
|
990
|
-
|
|
882
|
+
buildProtocolV2FeaturesPayload(deviceInfo, this.features)
|
|
991
883
|
);
|
|
992
|
-
this.
|
|
884
|
+
this.features = features;
|
|
885
|
+
this.featuresNeedsReload = false;
|
|
886
|
+
this.emit(DEVICE.FEATURES, this, features);
|
|
993
887
|
return features;
|
|
994
888
|
}
|
|
995
889
|
|
|
@@ -1027,7 +921,6 @@ export class Device extends EventEmitter {
|
|
|
1027
921
|
if (device.features) {
|
|
1028
922
|
this._updateFeatures(device.features);
|
|
1029
923
|
}
|
|
1030
|
-
this.updateProfile(device.profile);
|
|
1031
924
|
}
|
|
1032
925
|
|
|
1033
926
|
async run(fn?: () => Promise<void>, options?: RunOptions) {
|
|
@@ -1160,17 +1053,7 @@ export class Device extends EventEmitter {
|
|
|
1160
1053
|
}
|
|
1161
1054
|
|
|
1162
1055
|
getMode() {
|
|
1163
|
-
if (this.
|
|
1164
|
-
if (this.profile.status.mode === 'bootloader') return EOneKeyDeviceMode.bootloader;
|
|
1165
|
-
if (this.profile.status.mode === 'notInitialized') return EOneKeyDeviceMode.notInitialized;
|
|
1166
|
-
if (this.profile.status.noBackup === true) return EOneKeyDeviceMode.backupMode;
|
|
1167
|
-
if (this.profile.status.mode === 'normal') return EOneKeyDeviceMode.normal;
|
|
1168
|
-
// mode 'unknown'(V2 设备未上报 init_states)保守按未初始化处理,
|
|
1169
|
-
// 与 isInitialized() 的 fail-closed 行为保持一致。
|
|
1170
|
-
if (this.isProtocolV2()) return EOneKeyDeviceMode.notInitialized;
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
|
-
if (this.features?.bootloader_mode) {
|
|
1056
|
+
if (this.features?.bootloaderMode) {
|
|
1174
1057
|
// bootloader mode
|
|
1175
1058
|
return EOneKeyDeviceMode.bootloader;
|
|
1176
1059
|
}
|
|
@@ -1180,7 +1063,7 @@ export class Device extends EventEmitter {
|
|
|
1180
1063
|
return EOneKeyDeviceMode.notInitialized;
|
|
1181
1064
|
}
|
|
1182
1065
|
|
|
1183
|
-
if (this.features?.
|
|
1066
|
+
if (this.features?.noBackup) {
|
|
1184
1067
|
// backup mode
|
|
1185
1068
|
return EOneKeyDeviceMode.backupMode;
|
|
1186
1069
|
}
|
|
@@ -1190,17 +1073,11 @@ export class Device extends EventEmitter {
|
|
|
1190
1073
|
}
|
|
1191
1074
|
|
|
1192
1075
|
getFirmwareVersion() {
|
|
1193
|
-
const profileVersion = profileVersionToArray(this.profile?.versions.firmware);
|
|
1194
|
-
if (profileVersion) return profileVersion;
|
|
1195
|
-
if (this.isProtocolV2()) return null;
|
|
1196
1076
|
if (!this.features) return null;
|
|
1197
1077
|
return getDeviceFirmwareVersion(this.features);
|
|
1198
1078
|
}
|
|
1199
1079
|
|
|
1200
1080
|
getBLEFirmwareVersion() {
|
|
1201
|
-
const profileVersion = profileVersionToArray(this.profile?.versions.ble);
|
|
1202
|
-
if (profileVersion) return profileVersion;
|
|
1203
|
-
if (this.isProtocolV2()) return null;
|
|
1204
1081
|
if (!this.features) return null;
|
|
1205
1082
|
return getDeviceBLEFirmwareVersion(this.features);
|
|
1206
1083
|
}
|
|
@@ -1230,37 +1107,19 @@ export class Device extends EventEmitter {
|
|
|
1230
1107
|
}
|
|
1231
1108
|
|
|
1232
1109
|
isBootloader() {
|
|
1233
|
-
|
|
1234
|
-
return (
|
|
1235
|
-
this.profile.status.mode === 'bootloader' || this.profile.status.bootloaderMode === true
|
|
1236
|
-
);
|
|
1237
|
-
}
|
|
1238
|
-
return this.features && !!this.features.bootloader_mode;
|
|
1110
|
+
return this.features && !!this.features.bootloaderMode;
|
|
1239
1111
|
}
|
|
1240
1112
|
|
|
1241
1113
|
isInitialized() {
|
|
1242
|
-
if (this.profile) {
|
|
1243
|
-
if (this.profile.status.initialized != null) return this.profile.status.initialized;
|
|
1244
|
-
if (this.profile.status.mode === 'normal') return true;
|
|
1245
|
-
if (this.profile.status.mode === 'notInitialized') return false;
|
|
1246
|
-
// V2 设备未上报 init_states 时按未初始化处理(fail-closed):
|
|
1247
|
-
// 未知状态放行会让未初始化设备绕过 NOT_INITIALIZE 门禁。
|
|
1248
|
-
if (this.isProtocolV2()) return false;
|
|
1249
|
-
if (this.features) return !!this.features.initialized;
|
|
1250
|
-
return false;
|
|
1251
|
-
}
|
|
1252
1114
|
return this.features && !!this.features.initialized;
|
|
1253
1115
|
}
|
|
1254
1116
|
|
|
1255
1117
|
isSeedless() {
|
|
1256
|
-
|
|
1257
|
-
return this.profile.status.noBackup === true;
|
|
1258
|
-
}
|
|
1259
|
-
return this.features && !!this.features.no_backup;
|
|
1118
|
+
return this.features && !!this.features.noBackup;
|
|
1260
1119
|
}
|
|
1261
1120
|
|
|
1262
1121
|
isUnacquired(): boolean {
|
|
1263
|
-
return this.features === undefined
|
|
1122
|
+
return this.features === undefined;
|
|
1264
1123
|
}
|
|
1265
1124
|
|
|
1266
1125
|
hasUnexpectedMode(allow: string[], require: string[]) {
|
|
@@ -1291,7 +1150,7 @@ export class Device extends EventEmitter {
|
|
|
1291
1150
|
deviceType === EDeviceType.Touch ||
|
|
1292
1151
|
deviceType === EDeviceType.Pro ||
|
|
1293
1152
|
deviceType === EDeviceType.Pro2;
|
|
1294
|
-
const unlocked = this.
|
|
1153
|
+
const unlocked = this.features?.unlocked;
|
|
1295
1154
|
const preCheckTouch = isModeT && unlocked === false;
|
|
1296
1155
|
const passphraseProtection = this.getCurrentPassphraseProtection();
|
|
1297
1156
|
|
|
@@ -1319,7 +1178,6 @@ export class Device extends EventEmitter {
|
|
|
1319
1178
|
|
|
1320
1179
|
async unlockDevice() {
|
|
1321
1180
|
const firmwareVersion = this.getCurrentFirmwareVersionString() ?? '0.0.0';
|
|
1322
|
-
// profile 优先的版本范围解析;features 仅作为 V1 capability 判断来源
|
|
1323
1181
|
const versionRange = this.getCurrentMethodVersionRange(
|
|
1324
1182
|
type => this.supportUnlockVersionRange()[type]
|
|
1325
1183
|
);
|
|
@@ -1338,24 +1196,11 @@ export class Device extends EventEmitter {
|
|
|
1338
1196
|
|
|
1339
1197
|
if (supportUnlock) {
|
|
1340
1198
|
const res = await this.commands.typedCall('UnLockDevice', 'UnLockDeviceResponse');
|
|
1341
|
-
// 解锁结果同步到 profile(标准模型),features 仅在 V1 缓存存在时回写
|
|
1342
|
-
if (this.profile) {
|
|
1343
|
-
this.updateProfile({
|
|
1344
|
-
...this.profile,
|
|
1345
|
-
status: {
|
|
1346
|
-
...this.profile.status,
|
|
1347
|
-
unlocked: res.message.unlocked == null ? null : res.message.unlocked,
|
|
1348
|
-
...(res.message.passphrase_protection != null
|
|
1349
|
-
? { passphraseProtection: res.message.passphrase_protection }
|
|
1350
|
-
: {}),
|
|
1351
|
-
},
|
|
1352
|
-
});
|
|
1353
|
-
}
|
|
1354
1199
|
if (this.features) {
|
|
1355
1200
|
this.features.unlocked = res.message.unlocked == null ? null : res.message.unlocked;
|
|
1356
|
-
this.features.
|
|
1201
|
+
this.features.unlockedAttachPin =
|
|
1357
1202
|
res.message.unlocked_attach_pin == null ? undefined : res.message.unlocked_attach_pin;
|
|
1358
|
-
this.features.
|
|
1203
|
+
this.features.passphraseProtection =
|
|
1359
1204
|
res.message.passphrase_protection == null ? null : res.message.passphrase_protection;
|
|
1360
1205
|
|
|
1361
1206
|
return Promise.resolve(this.features);
|