@onekeyfe/hd-transport-web-device 1.2.0-alpha.3 → 1.2.0-alpha.30
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__/electron-ble-transport.test.ts +267 -18
- package/__tests__/webusb-protocol-v2-timeout.test.ts +288 -0
- package/dist/electron-ble-transport.d.ts +6 -5
- package/dist/electron-ble-transport.d.ts.map +1 -1
- package/dist/index.d.ts +106 -11
- package/dist/index.js +267 -294
- package/dist/transportLog.d.ts +7 -0
- package/dist/transportLog.d.ts.map +1 -0
- package/dist/webusb.d.ts +15 -7
- package/dist/webusb.d.ts.map +1 -1
- package/jest.config.js +5 -0
- package/package.json +6 -5
- package/src/electron-ble-transport.ts +153 -175
- package/src/transportLog.ts +11 -0
- package/src/webusb.ts +171 -179
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ProtocolType } from '@onekeyfe/hd-transport';
|
|
2
|
+
export declare function shouldSuppressHighVolumeCallLog(name: string): boolean;
|
|
3
|
+
export declare function createTransportCallLog(name: string, protocol: ProtocolType): {
|
|
4
|
+
name: string;
|
|
5
|
+
protocol: ProtocolType;
|
|
6
|
+
};
|
|
7
|
+
//# sourceMappingURL=transportLog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transportLog.d.ts","sourceRoot":"","sources":["../src/transportLog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAI3D,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,MAAM,WAE3D;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY;;;EAE1E"}
|
package/dist/webusb.d.ts
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
/// <reference types="w3c-web-usb" />
|
|
2
|
-
import transport from '@onekeyfe/hd-transport';
|
|
3
|
-
import type { AcquireInput, OneKeyDeviceInfoBase, ProtocolType, TransportCallOptions } from '@onekeyfe/hd-transport';
|
|
2
|
+
import transport, { ProtocolV2UsbTransportBase } from '@onekeyfe/hd-transport';
|
|
3
|
+
import type { AcquireInput, OneKeyDeviceInfoBase, ProtocolType, ProtocolV2CallContext, ProtocolV2Schemas, TransportCallOptions } from '@onekeyfe/hd-transport';
|
|
4
4
|
export interface DeviceInfo extends OneKeyDeviceInfoBase {
|
|
5
5
|
path: string;
|
|
6
6
|
device: USBDevice;
|
|
7
7
|
protocolType?: ProtocolType;
|
|
8
8
|
}
|
|
9
|
-
export default class WebUsbTransport {
|
|
9
|
+
export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string> {
|
|
10
10
|
messages: ReturnType<typeof transport.parseConfigure> | undefined;
|
|
11
11
|
messagesV2: ReturnType<typeof transport.parseConfigure> | undefined;
|
|
12
|
+
private protocolV2SchemaSource;
|
|
12
13
|
private deviceProtocol;
|
|
13
14
|
private deviceProtocolHints;
|
|
14
|
-
private protocolV2Assemblers;
|
|
15
|
-
private protocolV2Sessions;
|
|
16
|
-
private protocolV2ReadTimeouts;
|
|
17
15
|
private deviceEndpoints;
|
|
18
16
|
private mockSerialPaths;
|
|
19
17
|
private mockSerialCounter;
|
|
@@ -26,6 +24,7 @@ export default class WebUsbTransport {
|
|
|
26
24
|
configurationId: number;
|
|
27
25
|
endpointId: number;
|
|
28
26
|
interfaceId: number;
|
|
27
|
+
constructor();
|
|
29
28
|
init(logger: any): void;
|
|
30
29
|
configure(signedData: any): void;
|
|
31
30
|
configureProtocolV2(signedData: any): void;
|
|
@@ -35,6 +34,7 @@ export default class WebUsbTransport {
|
|
|
35
34
|
getConnectedDevices(): Promise<DeviceInfo[]>;
|
|
36
35
|
acquire(input: AcquireInput): Promise<string | undefined>;
|
|
37
36
|
private createProtocolMismatchError;
|
|
37
|
+
private createProtocolProbeTimeoutError;
|
|
38
38
|
private createProtocolDetectionError;
|
|
39
39
|
private detectProtocol;
|
|
40
40
|
findDevice(path: string): Promise<USBDevice>;
|
|
@@ -50,7 +50,9 @@ export default class WebUsbTransport {
|
|
|
50
50
|
private getTransferInData;
|
|
51
51
|
private toArrayBuffer;
|
|
52
52
|
private transferOutWithRetry;
|
|
53
|
+
private transferOutOnce;
|
|
53
54
|
private transferInWithRetry;
|
|
55
|
+
private transferInOnce;
|
|
54
56
|
private resetConnectionAfterProbe;
|
|
55
57
|
private withProtocolReadTimeout;
|
|
56
58
|
private probeProtocolV1;
|
|
@@ -58,9 +60,15 @@ export default class WebUsbTransport {
|
|
|
58
60
|
call(path: string, name: string, data: Record<string, unknown>, options?: TransportCallOptions): Promise<import("@onekeyfe/hd-transport").MessageFromOneKey>;
|
|
59
61
|
private callProtocolV1;
|
|
60
62
|
private callProtocolV2;
|
|
61
|
-
private receiveProtocolV2Frame;
|
|
62
63
|
receiveData(path: string, timeoutMs?: number): Promise<string>;
|
|
63
64
|
release(path: string): Promise<void>;
|
|
65
|
+
protected getProtocolV2UsbSchemas(): ProtocolV2Schemas;
|
|
66
|
+
protected getProtocolV2UsbLogger(): any;
|
|
67
|
+
protected writeProtocolV2UsbPacket(path: string, frame: Uint8Array, _context: ProtocolV2CallContext): Promise<void>;
|
|
68
|
+
protected readProtocolV2UsbPacket(path: string, _context: ProtocolV2CallContext): Promise<Uint8Array>;
|
|
69
|
+
protected resetProtocolV2UsbNativeLink(path: string, _reason: string): Promise<void>;
|
|
70
|
+
protected onProtocolV2UsbLinkInvalidated(path: string, reason: string): void;
|
|
71
|
+
protected createProtocolV2UsbTimeoutError(name: string, timeoutMs: number): Error;
|
|
64
72
|
getProtocolType(path: string): ProtocolType | undefined;
|
|
65
73
|
}
|
|
66
74
|
//# sourceMappingURL=webusb.d.ts.map
|
package/dist/webusb.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":";AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":";AACA,OAAO,SAAS,EAAE,EAQhB,0BAA0B,EAE3B,MAAM,wBAAwB,CAAC;AAYhC,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EACpB,YAAY,EACZ,qBAAqB,EACrB,iBAAiB,EACjB,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAsBhC,MAAM,WAAW,UAAW,SAAQ,oBAAoB;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,CAAC;IAClB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAaD,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,0BAA0B,CAAC,MAAM,CAAC;IAC7E,QAAQ,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAGlE,UAAU,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAEpE,OAAO,CAAC,sBAAsB,CAAqB;IAGnD,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAGnE,OAAO,CAAC,eAAe,CAA2C;IAMlE,OAAO,CAAC,eAAe,CAA6C;IAEpE,OAAO,CAAC,iBAAiB,CAAK;IAE9B,IAAI,SAAqB;IAEzB,OAAO,UAAS;IAEhB,UAAU,UAAS;IAEnB,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,GAAG,CAAC,EAAE,GAAG,CAAC;IAMV,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAM;IAEnC,eAAe,SAAoB;IAEnC,UAAU,SAAe;IAEzB,WAAW,SAAgB;;IAa3B,IAAI,CAAC,MAAM,EAAE,GAAG;IAgBhB,SAAS,CAAC,UAAU,EAAE,GAAG;IASzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAsB7B,kBAAkB;IAmBlB,SAAS;IAQf,OAAO,CAAC,aAAa;IAmBf,mBAAmB;IAgCnB,OAAO,CAAC,KAAK,EAAE,YAAY;IA2BjC,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,+BAA+B;IAOvC,OAAO,CAAC,4BAA4B;YAOtB,cAAc;IA6DtB,UAAU,CAAC,IAAI,EAAE,MAAM;IAwBvB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;IAkB1C,OAAO,CAAC,iBAAiB;IAiCnB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;YAgCpC,eAAe;YAkBf,iBAAiB;IAezB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAIvE,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,wBAAwB;YAalB,yBAAyB;IAiCvC,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,aAAa;YASP,oBAAoB;YA2BpB,eAAe;YAaf,mBAAmB;YA2CnB,cAAc;YAWd,yBAAyB;YAuBzB,uBAAuB;YA0CvB,eAAe;YAaf,eAAe;IAgBvB,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YA8BlB,cAAc;YAkCd,cAAc;IAYtB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;IAgD5C,OAAO,CAAC,IAAI,EAAE,MAAM;IAQ1B,SAAS,CAAC,uBAAuB,IAAI,iBAAiB;IAUtD,SAAS,CAAC,sBAAsB;cAIhB,wBAAwB,CACtC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,qBAAqB,GAC9B,OAAO,CAAC,IAAI,CAAC;cAIA,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,qBAAqB,GAC9B,OAAO,CAAC,UAAU,CAAC;cASN,4BAA4B,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1F,SAAS,CAAC,8BAA8B,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAKrE,SAAS,CAAC,+BAA+B,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,KAAK;IAWjF,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;CAGxD"}
|
package/jest.config.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-transport-web-device",
|
|
3
|
-
"version": "1.2.0-alpha.
|
|
3
|
+
"version": "1.2.0-alpha.30",
|
|
4
4
|
"author": "OneKey",
|
|
5
5
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
6
6
|
"license": "MIT",
|
|
@@ -16,17 +16,18 @@
|
|
|
16
16
|
"scripts": {
|
|
17
17
|
"dev": "rimraf dist && rollup -c ../../build/rollup.config.js -w",
|
|
18
18
|
"build": "rimraf dist && rollup -c ../../build/rollup.config.js",
|
|
19
|
+
"test": "jest",
|
|
19
20
|
"lint": "eslint .",
|
|
20
21
|
"lint:fix": "eslint . --fix"
|
|
21
22
|
},
|
|
22
23
|
"dependencies": {
|
|
23
|
-
"@onekeyfe/hd-shared": "1.2.0-alpha.
|
|
24
|
-
"@onekeyfe/hd-transport": "1.2.0-alpha.
|
|
24
|
+
"@onekeyfe/hd-shared": "1.2.0-alpha.30",
|
|
25
|
+
"@onekeyfe/hd-transport": "1.2.0-alpha.30"
|
|
25
26
|
},
|
|
26
27
|
"devDependencies": {
|
|
27
|
-
"@onekeyfe/hd-transport-electron": "1.2.0-alpha.
|
|
28
|
+
"@onekeyfe/hd-transport-electron": "1.2.0-alpha.30",
|
|
28
29
|
"@types/w3c-web-usb": "^1.0.6",
|
|
29
30
|
"@types/web-bluetooth": "^0.0.17"
|
|
30
31
|
},
|
|
31
|
-
"gitHead": "
|
|
32
|
+
"gitHead": "07405d2e3dcdd0ba351ae98b7cf4f61690ef2913"
|
|
32
33
|
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import transport, {
|
|
2
|
-
LogBlockCommand,
|
|
3
2
|
PROTOCOL_V1_MESSAGE_HEADER_SIZE,
|
|
3
|
+
PROTOCOL_V2_BLE_FRAME_MAX_BYTES,
|
|
4
4
|
PROTOCOL_V2_CHANNEL_BLE_UART,
|
|
5
5
|
ProtocolV2FrameAssembler,
|
|
6
|
-
|
|
6
|
+
ProtocolV2LinkManager,
|
|
7
|
+
TRANSPORT_EVENT,
|
|
7
8
|
bytesToHex,
|
|
8
9
|
hexToBytes,
|
|
9
10
|
probeProtocolV2 as probeProtocolV2Helper,
|
|
11
|
+
writeProtocolV2BleFrame,
|
|
10
12
|
} from '@onekeyfe/hd-transport';
|
|
11
13
|
import {
|
|
12
14
|
ERRORS,
|
|
@@ -14,25 +16,20 @@ import {
|
|
|
14
16
|
HardwareErrorCodeMessage,
|
|
15
17
|
createDeferred,
|
|
16
18
|
isHeaderChunk,
|
|
17
|
-
wait,
|
|
18
19
|
} from '@onekeyfe/hd-shared';
|
|
19
20
|
|
|
21
|
+
import { createTransportCallLog, shouldSuppressHighVolumeCallLog } from './transportLog';
|
|
22
|
+
|
|
20
23
|
import type { Deferred } from '@onekeyfe/hd-shared';
|
|
21
24
|
import type { DesktopAPI } from '@onekeyfe/hd-transport-electron';
|
|
22
|
-
import type {
|
|
25
|
+
import type {
|
|
26
|
+
OneKeyDeviceInfo,
|
|
27
|
+
ProtocolType,
|
|
28
|
+
ProtocolV2CallContext,
|
|
29
|
+
TransportCallOptions,
|
|
30
|
+
} from '@onekeyfe/hd-transport';
|
|
23
31
|
import type EventEmitter from 'events';
|
|
24
32
|
|
|
25
|
-
const FILE_WRITE_LOG_BLOCK_PATTERN = /(?:^|[^a-z])(?:raw)?(?:filesystem|emmc)?filewrite$/i;
|
|
26
|
-
|
|
27
|
-
function shouldSuppressHighVolumeCallLog(name: string) {
|
|
28
|
-
const normalized = name.replace(/[_\s-]/g, '');
|
|
29
|
-
return FILE_WRITE_LOG_BLOCK_PATTERN.test(normalized);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function isLogBlockCommand(name: string) {
|
|
33
|
-
return (LogBlockCommand as Set<string> | undefined)?.has?.(name) ?? false;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
33
|
const { parseConfigure, ProtocolV1, check } = transport;
|
|
37
34
|
|
|
38
35
|
declare global {
|
|
@@ -72,9 +69,6 @@ const toBleDescriptor = (
|
|
|
72
69
|
|
|
73
70
|
const BLE_PACKET_SIZE = 192;
|
|
74
71
|
const BLE_WRITE_DELAY_MS = 5;
|
|
75
|
-
const BLE_WRITE_MAX_RETRIES = 3;
|
|
76
|
-
const BLE_WRITE_RETRY_DELAY_MS = 300;
|
|
77
|
-
const BLE_RESPONSE_TIMEOUT_MS = 30_000;
|
|
78
72
|
const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
|
|
79
73
|
const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
|
|
80
74
|
|
|
@@ -89,6 +83,8 @@ export default class ElectronBleTransport {
|
|
|
89
83
|
|
|
90
84
|
private _messagesV2: ReturnType<typeof transport.parseConfigure> | undefined;
|
|
91
85
|
|
|
86
|
+
private protocolV2SchemaConfiguration: string | undefined;
|
|
87
|
+
|
|
92
88
|
name = 'ElectronBleTransport';
|
|
93
89
|
|
|
94
90
|
configured = false;
|
|
@@ -113,9 +109,26 @@ export default class ElectronBleTransport {
|
|
|
113
109
|
|
|
114
110
|
private v2FramePromises: Map<string, Deferred<Uint8Array>> = new Map();
|
|
115
111
|
|
|
116
|
-
private
|
|
117
|
-
|
|
118
|
-
|
|
112
|
+
private protocolV2Links = new ProtocolV2LinkManager<string>({
|
|
113
|
+
getSchemas: () => {
|
|
114
|
+
if (!this._messages || !this._messagesV2) {
|
|
115
|
+
throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
protocolV1: this._messages,
|
|
119
|
+
protocolV2: this._messagesV2,
|
|
120
|
+
};
|
|
121
|
+
},
|
|
122
|
+
classifyError: () => 'link-fatal',
|
|
123
|
+
onLinkInvalidated: async (uuid, reason) => {
|
|
124
|
+
this.v2Assemblers.get(uuid)?.reset();
|
|
125
|
+
this.rejectProtocolV2Frames(uuid, new Error(reason));
|
|
126
|
+
this.Log?.debug('[Electron BLE] Protocol V2 link invalidated:', uuid, reason);
|
|
127
|
+
if (reason.startsWith('Protocol V2 link-fatal error:')) {
|
|
128
|
+
await this.release(uuid);
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
});
|
|
119
132
|
|
|
120
133
|
private notificationCleanups: Map<string, () => void> = new Map();
|
|
121
134
|
|
|
@@ -157,6 +170,9 @@ export default class ElectronBleTransport {
|
|
|
157
170
|
}
|
|
158
171
|
|
|
159
172
|
private cleanupDeviceState(deviceId: string): void {
|
|
173
|
+
this.protocolV2Links
|
|
174
|
+
.invalidateLink(deviceId, 'Electron BLE device state cleaned')
|
|
175
|
+
.catch(error => this.Log?.debug('[Electron BLE] link cleanup failed:', error));
|
|
160
176
|
this.connectedDevices.delete(deviceId);
|
|
161
177
|
this.deviceProtocol.delete(deviceId);
|
|
162
178
|
// Keep deviceProtocolHints — it's inferred from device name (e.g. "Pro 2" → V2)
|
|
@@ -164,9 +180,6 @@ export default class ElectronBleTransport {
|
|
|
164
180
|
this.v1Buffers.delete(deviceId);
|
|
165
181
|
this.v2Assemblers.delete(deviceId);
|
|
166
182
|
this.resetProtocolV2Frames(deviceId);
|
|
167
|
-
if (this.activeProtocolV2Call?.uuid === deviceId) {
|
|
168
|
-
this.activeProtocolV2Call = null;
|
|
169
|
-
}
|
|
170
183
|
this.notificationTokens.delete(deviceId);
|
|
171
184
|
|
|
172
185
|
const notifyCleanup = this.notificationCleanups.get(deviceId);
|
|
@@ -202,8 +215,19 @@ export default class ElectronBleTransport {
|
|
|
202
215
|
}
|
|
203
216
|
|
|
204
217
|
configureProtocolV2(signedData: any) {
|
|
218
|
+
const configuration = typeof signedData === 'string' ? signedData : JSON.stringify(signedData);
|
|
219
|
+
if (this.protocolV2SchemaConfiguration === configuration) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const isReconfiguration = this.protocolV2SchemaConfiguration !== undefined;
|
|
205
224
|
this._messagesV2 = parseConfigure(signedData);
|
|
206
|
-
this.
|
|
225
|
+
this.protocolV2SchemaConfiguration = configuration;
|
|
226
|
+
if (isReconfiguration) {
|
|
227
|
+
this.protocolV2Links
|
|
228
|
+
.invalidateAllLinks('Protocol V2 schema reconfigured')
|
|
229
|
+
.catch(error => this.Log?.debug('[Electron BLE] schema link cleanup failed:', error));
|
|
230
|
+
}
|
|
207
231
|
}
|
|
208
232
|
|
|
209
233
|
async listen() {
|
|
@@ -238,12 +262,15 @@ export default class ElectronBleTransport {
|
|
|
238
262
|
throw ERRORS.TypedError(HardwareErrorCode.BleRequiredUUID);
|
|
239
263
|
}
|
|
240
264
|
|
|
265
|
+
if (this.connectedDevices.has(uuid)) {
|
|
266
|
+
await this.release(uuid);
|
|
267
|
+
}
|
|
268
|
+
|
|
241
269
|
if (forceCleanRunPromise && this.runPromise) {
|
|
242
270
|
const error = ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise);
|
|
243
271
|
this.runPromise.reject(error);
|
|
244
272
|
this.rejectAllProtocolV2Frames(error);
|
|
245
273
|
this.runPromise = null;
|
|
246
|
-
this.activeProtocolV2Call = null;
|
|
247
274
|
}
|
|
248
275
|
|
|
249
276
|
try {
|
|
@@ -270,7 +297,7 @@ export default class ElectronBleTransport {
|
|
|
270
297
|
}
|
|
271
298
|
|
|
272
299
|
this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
|
|
273
|
-
this.v2Assemblers.set(uuid, new ProtocolV2FrameAssembler());
|
|
300
|
+
this.v2Assemblers.set(uuid, new ProtocolV2FrameAssembler(PROTOCOL_V2_BLE_FRAME_MAX_BYTES));
|
|
274
301
|
|
|
275
302
|
await window.desktopApi.nobleBle.subscribe(uuid);
|
|
276
303
|
|
|
@@ -281,7 +308,7 @@ export default class ElectronBleTransport {
|
|
|
281
308
|
(disconnectedDevice: any) => {
|
|
282
309
|
if (disconnectedDevice.id === uuid) {
|
|
283
310
|
this.cleanupDeviceState(uuid);
|
|
284
|
-
this.emitter?.emit(
|
|
311
|
+
this.emitter?.emit(TRANSPORT_EVENT.DEVICE_DISCONNECT, {
|
|
285
312
|
name: disconnectedDevice.name,
|
|
286
313
|
id: disconnectedDevice.id,
|
|
287
314
|
connectId: disconnectedDevice.id,
|
|
@@ -293,12 +320,6 @@ export default class ElectronBleTransport {
|
|
|
293
320
|
|
|
294
321
|
const protocolType = await this.detectProtocol(uuid, expectedProtocol, protocolHint);
|
|
295
322
|
|
|
296
|
-
this.emitter?.emit('device-connect', {
|
|
297
|
-
name: device.name,
|
|
298
|
-
id: device.id,
|
|
299
|
-
connectId: device.id,
|
|
300
|
-
});
|
|
301
|
-
|
|
302
323
|
return {
|
|
303
324
|
...toBleDescriptor({ id: device.id, name: device.name }, protocolType),
|
|
304
325
|
uuid,
|
|
@@ -320,6 +341,7 @@ export default class ElectronBleTransport {
|
|
|
320
341
|
|
|
321
342
|
async release(id: string) {
|
|
322
343
|
try {
|
|
344
|
+
await this.protocolV2Links.invalidateLink(id, 'Electron BLE transport released');
|
|
323
345
|
if (this.connectedDevices.has(id)) {
|
|
324
346
|
if (window.desktopApi?.nobleBle) {
|
|
325
347
|
await window.desktopApi.nobleBle.unsubscribe(id);
|
|
@@ -368,23 +390,23 @@ export default class ElectronBleTransport {
|
|
|
368
390
|
}
|
|
369
391
|
|
|
370
392
|
if (expectedProtocol === 'V2') {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
393
|
+
if (await this.probeProtocolV2(uuid)) {
|
|
394
|
+
this.deviceProtocol.set(uuid, 'V2');
|
|
395
|
+
this.Log?.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> V2 (expected)`);
|
|
396
|
+
return 'V2';
|
|
397
|
+
}
|
|
398
|
+
throw this.createProtocolMismatchError(expectedProtocol);
|
|
376
399
|
}
|
|
377
400
|
|
|
378
|
-
//
|
|
379
|
-
//
|
|
380
|
-
// 不能作为最终结论。
|
|
401
|
+
// Protocol must be actively probed after connection. Name, PID, and descriptors only
|
|
402
|
+
// influence probe order; a V2 hint probes V2 first and falls back to V1.
|
|
381
403
|
const probeOrder: ProtocolType[] =
|
|
382
404
|
protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
|
|
383
405
|
|
|
384
406
|
for (let i = 0; i < probeOrder.length; i += 1) {
|
|
385
407
|
const protocol = probeOrder[i];
|
|
386
408
|
if (i > 0) {
|
|
387
|
-
//
|
|
409
|
+
// Reset subscriptions and buffers after a failed probe before trying another protocol.
|
|
388
410
|
await this.resetProbeStateAfterProtocolProbe(uuid, probeOrder[i - 1]);
|
|
389
411
|
}
|
|
390
412
|
const detected =
|
|
@@ -417,12 +439,13 @@ export default class ElectronBleTransport {
|
|
|
417
439
|
}
|
|
418
440
|
|
|
419
441
|
private async resetProbeStateAfterProtocolProbe(uuid: string, protocol: ProtocolType) {
|
|
442
|
+
await this.protocolV2Links.invalidateLink(
|
|
443
|
+
uuid,
|
|
444
|
+
`Reset notify state after Protocol ${protocol} probe`
|
|
445
|
+
);
|
|
420
446
|
this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
|
|
421
447
|
this.v2Assemblers.get(uuid)?.reset();
|
|
422
448
|
this.resetProtocolV2Frames(uuid);
|
|
423
|
-
if (this.activeProtocolV2Call?.uuid === uuid) {
|
|
424
|
-
this.activeProtocolV2Call = null;
|
|
425
|
-
}
|
|
426
449
|
if (this.runPromise) {
|
|
427
450
|
const error = ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise);
|
|
428
451
|
this.runPromise.reject(error);
|
|
@@ -491,63 +514,42 @@ export default class ElectronBleTransport {
|
|
|
491
514
|
return detected;
|
|
492
515
|
}
|
|
493
516
|
|
|
494
|
-
private async
|
|
495
|
-
const totalBytes = hexData.length / 2;
|
|
496
|
-
|
|
497
|
-
if (totalBytes <= BLE_PACKET_SIZE) {
|
|
498
|
-
await wait(BLE_WRITE_DELAY_MS);
|
|
499
|
-
await this.writeWithRetry(uuid, hexData);
|
|
500
|
-
return;
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
for (let offset = 0; offset < hexData.length; ) {
|
|
504
|
-
const chunkHexLen = Math.min(BLE_PACKET_SIZE * 2, hexData.length - offset);
|
|
505
|
-
const chunkHex = hexData.substring(offset, offset + chunkHexLen);
|
|
506
|
-
offset += chunkHexLen;
|
|
507
|
-
|
|
508
|
-
await this.writeWithRetry(uuid, chunkHex);
|
|
509
|
-
|
|
510
|
-
if (offset < hexData.length) {
|
|
511
|
-
await wait(BLE_WRITE_DELAY_MS);
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
private async writeWithRetry(uuid: string, hexData: string): Promise<void> {
|
|
517
|
-
let lastError: any;
|
|
517
|
+
private async writeOnce(uuid: string, hexData: string): Promise<void> {
|
|
518
518
|
const nobleBle = window.desktopApi?.nobleBle;
|
|
519
519
|
if (!nobleBle) {
|
|
520
520
|
throw new Error('Noble BLE API not available');
|
|
521
521
|
}
|
|
522
522
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
523
|
+
await nobleBle.write(uuid, hexData);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
private writeProtocolV2Frame(
|
|
527
|
+
uuid: string,
|
|
528
|
+
frame: Uint8Array,
|
|
529
|
+
context: ProtocolV2CallContext,
|
|
530
|
+
assertCurrentGeneration: () => void
|
|
531
|
+
) {
|
|
532
|
+
return writeProtocolV2BleFrame({
|
|
533
|
+
frame,
|
|
534
|
+
packetCapacity: BLE_PACKET_SIZE,
|
|
535
|
+
assertActive: assertCurrentGeneration,
|
|
536
|
+
signal: context.signal,
|
|
537
|
+
abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
|
|
538
|
+
initialDelayMs: frame.length <= BLE_PACKET_SIZE ? BLE_WRITE_DELAY_MS : 0,
|
|
539
|
+
burstSize: 1,
|
|
540
|
+
burstPauseMs: BLE_WRITE_DELAY_MS,
|
|
541
|
+
writePacket: packet => this.writeOnce(uuid, bytesToHex(packet)),
|
|
542
|
+
});
|
|
542
543
|
}
|
|
543
544
|
|
|
544
545
|
private handleNotification(deviceId: string, hexData: string): void {
|
|
545
546
|
if (hexData === 'PAIRING_REJECTED') {
|
|
546
547
|
this.Log?.debug('[Electron BLE] Pairing rejection detected for device:', deviceId);
|
|
547
|
-
|
|
548
|
-
|
|
548
|
+
const error = ERRORS.TypedError(HardwareErrorCode.BleDeviceBondedCanceled);
|
|
549
|
+
if (this.deviceProtocol.get(deviceId) === 'V2') {
|
|
550
|
+
this.rejectProtocolV2Frames(deviceId, error);
|
|
551
|
+
} else if (this.runPromise) {
|
|
549
552
|
this.runPromise.reject(error);
|
|
550
|
-
this.rejectAllProtocolV2Frames(error);
|
|
551
553
|
}
|
|
552
554
|
return;
|
|
553
555
|
}
|
|
@@ -566,12 +568,6 @@ export default class ElectronBleTransport {
|
|
|
566
568
|
|
|
567
569
|
private handleProtocolV2Notification(deviceId: string, hexData: string): void {
|
|
568
570
|
try {
|
|
569
|
-
if (!this.runPromise || this.activeProtocolV2Call?.uuid !== deviceId) {
|
|
570
|
-
this.v2Assemblers.get(deviceId)?.reset();
|
|
571
|
-
this.resetProtocolV2Frames(deviceId);
|
|
572
|
-
return;
|
|
573
|
-
}
|
|
574
|
-
|
|
575
571
|
const bytes = hexToBytes(hexData);
|
|
576
572
|
if (bytes.length === 0) return;
|
|
577
573
|
|
|
@@ -583,11 +579,8 @@ export default class ElectronBleTransport {
|
|
|
583
579
|
}
|
|
584
580
|
} catch (error) {
|
|
585
581
|
this.Log?.error('[Electron BLE] Protocol V2 notification error:', error);
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
this.runPromise.reject(notifyError);
|
|
589
|
-
this.rejectAllProtocolV2Frames(notifyError);
|
|
590
|
-
}
|
|
582
|
+
const notifyError = ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError);
|
|
583
|
+
this.rejectProtocolV2Frames(deviceId, notifyError);
|
|
591
584
|
}
|
|
592
585
|
}
|
|
593
586
|
|
|
@@ -623,8 +616,13 @@ export default class ElectronBleTransport {
|
|
|
623
616
|
this.v2FramePromises.delete(uuid);
|
|
624
617
|
}
|
|
625
618
|
|
|
626
|
-
private
|
|
627
|
-
|
|
619
|
+
private rejectProtocolV2Frames(uuid: string, error: Error) {
|
|
620
|
+
this.v2FrameQueues.delete(uuid);
|
|
621
|
+
const framePromise = this.v2FramePromises.get(uuid);
|
|
622
|
+
if (framePromise) {
|
|
623
|
+
this.v2FramePromises.delete(uuid);
|
|
624
|
+
framePromise.reject(error);
|
|
625
|
+
}
|
|
628
626
|
}
|
|
629
627
|
|
|
630
628
|
private async readProtocolV2Frame(uuid: string) {
|
|
@@ -681,12 +679,8 @@ export default class ElectronBleTransport {
|
|
|
681
679
|
`Device protocol has not been detected for ${uuid}`
|
|
682
680
|
);
|
|
683
681
|
}
|
|
684
|
-
if (shouldSuppressHighVolumeCallLog(name)) {
|
|
685
|
-
|
|
686
|
-
} else if (isLogBlockCommand(name)) {
|
|
687
|
-
this.Log?.debug('[Electron BLE] call', 'name:', name, 'protocol:', protocol);
|
|
688
|
-
} else {
|
|
689
|
-
this.Log?.debug('[Electron BLE] call', 'name:', name, 'data:', data, 'protocol:', protocol);
|
|
682
|
+
if (!shouldSuppressHighVolumeCallLog(name)) {
|
|
683
|
+
this.Log?.debug('transport call', createTransportCallLog(name, protocol));
|
|
690
684
|
}
|
|
691
685
|
|
|
692
686
|
if (protocol === 'V2') {
|
|
@@ -776,77 +770,61 @@ export default class ElectronBleTransport {
|
|
|
776
770
|
throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
|
|
777
771
|
}
|
|
778
772
|
|
|
779
|
-
const
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
773
|
+
const callOptions = options;
|
|
774
|
+
|
|
775
|
+
try {
|
|
776
|
+
return await this.protocolV2Links.call(
|
|
777
|
+
uuid,
|
|
778
|
+
() => this.createProtocolV2Adapter(uuid),
|
|
779
|
+
name,
|
|
780
|
+
data,
|
|
781
|
+
callOptions
|
|
782
|
+
);
|
|
783
|
+
} catch (e) {
|
|
784
|
+
this.Log?.error('[Electron BLE] Protocol V2 call error:', e);
|
|
785
|
+
throw e;
|
|
789
786
|
}
|
|
787
|
+
}
|
|
790
788
|
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
this.resetProtocolV2Frames(uuid);
|
|
798
|
-
let completed = false;
|
|
799
|
-
const callOptions = {
|
|
800
|
-
...options,
|
|
801
|
-
timeoutMs: options?.timeoutMs ?? BLE_RESPONSE_TIMEOUT_MS,
|
|
789
|
+
private createProtocolV2Adapter(uuid: string) {
|
|
790
|
+
const generation = this.notificationTokens.get(uuid) ?? 0;
|
|
791
|
+
const assertCurrentGeneration = () => {
|
|
792
|
+
if (this.notificationTokens.get(uuid) !== generation) {
|
|
793
|
+
throw new Error(`Protocol V2 notification generation changed for ${uuid}`);
|
|
794
|
+
}
|
|
802
795
|
};
|
|
803
796
|
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
router: PROTOCOL_V2_CHANNEL_BLE_UART,
|
|
811
|
-
writeFrame: (frame: Uint8Array) => this.writeWithChunking(uuid, bytesToHex(frame)),
|
|
812
|
-
readFrame: async () => {
|
|
813
|
-
const rxFrame = await this.readProtocolV2Frame(uuid);
|
|
814
|
-
if (!(rxFrame instanceof Uint8Array)) {
|
|
815
|
-
throw new Error('Response is not Uint8Array');
|
|
816
|
-
}
|
|
817
|
-
return rxFrame;
|
|
818
|
-
},
|
|
819
|
-
logger: this.Log,
|
|
820
|
-
logPrefix: 'ProtocolV2 BLE',
|
|
821
|
-
createTimeoutError: (_messageName: string, timeout: number) =>
|
|
822
|
-
ERRORS.TypedError(
|
|
823
|
-
HardwareErrorCode.BleTimeoutError,
|
|
824
|
-
`BLE response timeout after ${timeout}ms for ${name}`
|
|
825
|
-
),
|
|
826
|
-
});
|
|
827
|
-
|
|
828
|
-
const result = await session.call(name, data, callOptions);
|
|
829
|
-
completed = true;
|
|
830
|
-
return result;
|
|
831
|
-
} catch (e) {
|
|
832
|
-
if (this.isActiveProtocolV2Call(uuid, callToken)) {
|
|
797
|
+
return {
|
|
798
|
+
router: PROTOCOL_V2_CHANNEL_BLE_UART,
|
|
799
|
+
maxFrameBytes: PROTOCOL_V2_BLE_FRAME_MAX_BYTES,
|
|
800
|
+
generation,
|
|
801
|
+
prepareCall: () => {
|
|
802
|
+
assertCurrentGeneration();
|
|
833
803
|
this.v2Assemblers.get(uuid)?.reset();
|
|
834
804
|
this.resetProtocolV2Frames(uuid);
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
805
|
+
},
|
|
806
|
+
writeFrame: (frame: Uint8Array, context: ProtocolV2CallContext) =>
|
|
807
|
+
this.writeProtocolV2Frame(uuid, frame, context, assertCurrentGeneration),
|
|
808
|
+
readFrame: async () => {
|
|
809
|
+
assertCurrentGeneration();
|
|
810
|
+
const rxFrame = await this.readProtocolV2Frame(uuid);
|
|
811
|
+
if (!(rxFrame instanceof Uint8Array)) {
|
|
812
|
+
throw new Error('Response is not Uint8Array');
|
|
842
813
|
}
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
this.
|
|
848
|
-
}
|
|
849
|
-
|
|
814
|
+
return rxFrame;
|
|
815
|
+
},
|
|
816
|
+
reset: (reason: string) => {
|
|
817
|
+
this.v2Assemblers.get(uuid)?.reset();
|
|
818
|
+
this.rejectProtocolV2Frames(uuid, new Error(reason));
|
|
819
|
+
},
|
|
820
|
+
logger: this.Log,
|
|
821
|
+
logPrefix: 'ProtocolV2 BLE',
|
|
822
|
+
createTimeoutError: (messageName: string, timeout: number) =>
|
|
823
|
+
ERRORS.TypedError(
|
|
824
|
+
HardwareErrorCode.BleTimeoutError,
|
|
825
|
+
`BLE response timeout after ${timeout}ms for ${messageName}`
|
|
826
|
+
),
|
|
827
|
+
};
|
|
850
828
|
}
|
|
851
829
|
|
|
852
830
|
private processProtocolV1Notification(deviceId: string, hexData: string): PacketProcessResult {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ProtocolType } from '@onekeyfe/hd-transport';
|
|
2
|
+
|
|
3
|
+
const HIGH_VOLUME_CALLS = new Set(['FileWrite', 'FilesystemFileWrite', 'EmmcFileWrite']);
|
|
4
|
+
|
|
5
|
+
export function shouldSuppressHighVolumeCallLog(name: string) {
|
|
6
|
+
return HIGH_VOLUME_CALLS.has(name);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function createTransportCallLog(name: string, protocol: ProtocolType) {
|
|
10
|
+
return { name, protocol };
|
|
11
|
+
}
|