@onekeyfe/hd-transport-react-native 1.2.0-alpha.2 → 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/dist/bleStrategy.d.ts +0 -2
- package/dist/bleStrategy.d.ts.map +1 -1
- package/dist/index.d.ts +15 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +191 -228
- package/dist/subscribeBleOn.d.ts.map +1 -1
- package/dist/transportLog.d.ts +13 -0
- package/dist/transportLog.d.ts.map +1 -0
- package/package.json +6 -6
- package/src/__tests__/bleStrategy.test.ts +1 -6
- package/src/__tests__/protocolV2Link.test.ts +243 -0
- package/src/bleStrategy.ts +0 -25
- package/src/index.ts +192 -261
- package/src/subscribeBleOn.ts +0 -2
- package/src/transportLog.ts +18 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subscribeBleOn.d.ts","sourceRoot":"","sources":["../src/subscribeBleOn.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C,eAAO,MAAM,cAAc,eAAgB,aAAa,kBAAc,QAAQ,IAAI,
|
|
1
|
+
{"version":3,"file":"subscribeBleOn.d.ts","sourceRoot":"","sources":["../src/subscribeBleOn.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C,eAAO,MAAM,cAAc,eAAgB,aAAa,kBAAc,QAAQ,IAAI,CAmB9E,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ProtocolType } from '@onekeyfe/hd-transport';
|
|
2
|
+
export declare function createTransportCallLog(name: string, protocol: ProtocolType, data: Record<string, unknown>): {
|
|
3
|
+
name: string;
|
|
4
|
+
protocol: ProtocolType;
|
|
5
|
+
file_name: unknown;
|
|
6
|
+
hash: unknown;
|
|
7
|
+
} | {
|
|
8
|
+
name: string;
|
|
9
|
+
protocol: ProtocolType;
|
|
10
|
+
file_name?: undefined;
|
|
11
|
+
hash?: undefined;
|
|
12
|
+
};
|
|
13
|
+
//# 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;AAE3D,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,YAAY,EACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;;EAY9B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-transport-react-native",
|
|
3
|
-
"version": "1.2.0-alpha.
|
|
3
|
+
"version": "1.2.0-alpha.21",
|
|
4
4
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -19,11 +19,11 @@
|
|
|
19
19
|
"lint:fix": "eslint . --fix"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@onekeyfe/hd-core": "1.2.0-alpha.
|
|
23
|
-
"@onekeyfe/hd-shared": "1.2.0-alpha.
|
|
24
|
-
"@onekeyfe/hd-transport": "1.2.0-alpha.
|
|
25
|
-
"@onekeyfe/react-native-ble-utils": "^0.1.
|
|
22
|
+
"@onekeyfe/hd-core": "1.2.0-alpha.21",
|
|
23
|
+
"@onekeyfe/hd-shared": "1.2.0-alpha.21",
|
|
24
|
+
"@onekeyfe/hd-transport": "1.2.0-alpha.21",
|
|
25
|
+
"@onekeyfe/react-native-ble-utils": "^0.1.6",
|
|
26
26
|
"react-native-ble-plx": "3.5.1"
|
|
27
27
|
},
|
|
28
|
-
"gitHead": "
|
|
28
|
+
"gitHead": "efe594367fb3b196a5126baee7e2aba3e94986cd"
|
|
29
29
|
}
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
hasWritableCapability,
|
|
3
|
-
resolveBleWriteMode,
|
|
4
|
-
resolveProtocolV2PacketCapacity,
|
|
5
|
-
} from '../bleStrategy';
|
|
1
|
+
import { hasWritableCapability, resolveProtocolV2PacketCapacity } from '../bleStrategy';
|
|
6
2
|
|
|
7
3
|
describe('React Native BLE strategy', () => {
|
|
8
4
|
test('accepts writeWithoutResponse-only characteristics', () => {
|
|
@@ -12,7 +8,6 @@ describe('React Native BLE strategy', () => {
|
|
|
12
8
|
};
|
|
13
9
|
|
|
14
10
|
expect(hasWritableCapability(characteristic)).toBe(true);
|
|
15
|
-
expect(resolveBleWriteMode(characteristic)).toBe('withoutResponse');
|
|
16
11
|
});
|
|
17
12
|
|
|
18
13
|
test('falls back to Android default ATT payload when MTU is unavailable', () => {
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import transportPackage, {
|
|
3
|
+
PROTOCOL_V2_CHANNEL_BLE_UART,
|
|
4
|
+
ProtocolV2,
|
|
5
|
+
bytesToHex,
|
|
6
|
+
} from '@onekeyfe/hd-transport';
|
|
7
|
+
import { HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
8
|
+
|
|
9
|
+
import ReactNativeBleTransport from '../index';
|
|
10
|
+
|
|
11
|
+
jest.mock(
|
|
12
|
+
'react-native',
|
|
13
|
+
() => ({
|
|
14
|
+
PermissionsAndroid: {},
|
|
15
|
+
Platform: { OS: 'ios' },
|
|
16
|
+
}),
|
|
17
|
+
{ virtual: true }
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
jest.mock('react-native-ble-plx', () => ({
|
|
21
|
+
BleATTErrorCode: { UnlikelyError: 14 },
|
|
22
|
+
BleError: class BleError extends Error {},
|
|
23
|
+
BleErrorCode: {
|
|
24
|
+
DeviceAlreadyConnected: 203,
|
|
25
|
+
DeviceDisconnected: 205,
|
|
26
|
+
DeviceMTUChangeFailed: 206,
|
|
27
|
+
OperationCancelled: 2,
|
|
28
|
+
CharacteristicNotFound: 404,
|
|
29
|
+
},
|
|
30
|
+
BleManager: jest.fn(),
|
|
31
|
+
ScanMode: { LowLatency: 2 },
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
jest.mock('../BleManager', () => ({
|
|
35
|
+
getConnectedDeviceIds: jest.fn(() => Promise.resolve([])),
|
|
36
|
+
onDeviceBondState: jest.fn(() => Promise.resolve()),
|
|
37
|
+
pairDevice: jest.fn(() => Promise.resolve({ bonded: true, bonding: false })),
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
jest.mock('../subscribeBleOn', () => ({
|
|
41
|
+
subscribeBleOn: jest.fn(() => Promise.resolve()),
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
const { parseConfigure } = transportPackage;
|
|
45
|
+
|
|
46
|
+
const protocolV1Schema = {
|
|
47
|
+
nested: {
|
|
48
|
+
Initialize: { fields: {} },
|
|
49
|
+
Success: {
|
|
50
|
+
fields: {
|
|
51
|
+
message: { type: 'string', id: 1 },
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
MessageType: {
|
|
55
|
+
values: {
|
|
56
|
+
MessageType_Initialize: 1,
|
|
57
|
+
MessageType_Success: 2,
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const protocolV2Schema = {
|
|
64
|
+
nested: {
|
|
65
|
+
Ping: {
|
|
66
|
+
fields: {
|
|
67
|
+
message: { type: 'string', id: 1 },
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
FileWrite: { fields: {} },
|
|
71
|
+
Success: {
|
|
72
|
+
fields: {
|
|
73
|
+
message: { type: 'string', id: 1 },
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
MessageType: {
|
|
77
|
+
values: {
|
|
78
|
+
MessageType_Ping: 60206,
|
|
79
|
+
MessageType_Success: 60207,
|
|
80
|
+
MessageType_FileWrite: 60805,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const schemas = {
|
|
87
|
+
protocolV1: parseConfigure(protocolV1Schema),
|
|
88
|
+
protocolV2: parseConfigure(protocolV2Schema),
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const createHarness = () => {
|
|
92
|
+
const uuid = 'rn-pro2-id';
|
|
93
|
+
const sentSeqs: number[] = [];
|
|
94
|
+
let shouldRespond = true;
|
|
95
|
+
let notifyCallback:
|
|
96
|
+
| ((
|
|
97
|
+
error: (Error & { reason?: string }) | null,
|
|
98
|
+
characteristic: { value: string } | null
|
|
99
|
+
) => void)
|
|
100
|
+
| undefined;
|
|
101
|
+
const notifyCharacteristic = {
|
|
102
|
+
uuid: '0003',
|
|
103
|
+
deviceID: uuid,
|
|
104
|
+
isNotifiable: true,
|
|
105
|
+
monitor: jest.fn(callback => {
|
|
106
|
+
notifyCallback = callback;
|
|
107
|
+
return { remove: jest.fn() };
|
|
108
|
+
}),
|
|
109
|
+
};
|
|
110
|
+
const handleWrite = (base64: string) => {
|
|
111
|
+
const frame = Buffer.from(base64, 'base64');
|
|
112
|
+
sentSeqs.push(frame[6]);
|
|
113
|
+
if (shouldRespond) {
|
|
114
|
+
const response = ProtocolV2.encodeFrame(
|
|
115
|
+
schemas,
|
|
116
|
+
'Success',
|
|
117
|
+
{ message: 'ok' },
|
|
118
|
+
{ router: PROTOCOL_V2_CHANNEL_BLE_UART }
|
|
119
|
+
);
|
|
120
|
+
notifyCallback?.(null, { value: Buffer.from(response).toString('base64') });
|
|
121
|
+
}
|
|
122
|
+
return Promise.resolve();
|
|
123
|
+
};
|
|
124
|
+
const writeCharacteristic = {
|
|
125
|
+
uuid: '0002',
|
|
126
|
+
deviceID: uuid,
|
|
127
|
+
isWritableWithResponse: true,
|
|
128
|
+
isWritableWithoutResponse: true,
|
|
129
|
+
writeWithResponse: jest.fn(handleWrite),
|
|
130
|
+
writeWithoutResponse: jest.fn(handleWrite),
|
|
131
|
+
};
|
|
132
|
+
const device = {
|
|
133
|
+
id: uuid,
|
|
134
|
+
name: 'OneKey Pro 2',
|
|
135
|
+
localName: 'OneKey Pro 2',
|
|
136
|
+
serviceUUIDs: ['fffd'],
|
|
137
|
+
isConnected: jest.fn(() => Promise.resolve(true)),
|
|
138
|
+
onDisconnected: jest.fn(() => ({ remove: jest.fn() })),
|
|
139
|
+
};
|
|
140
|
+
const bleManager = {
|
|
141
|
+
devices: jest.fn(() => Promise.resolve([device])),
|
|
142
|
+
connectedDevices: jest.fn(() => Promise.resolve([])),
|
|
143
|
+
cancelTransaction: jest.fn(() => Promise.resolve()),
|
|
144
|
+
};
|
|
145
|
+
const transport = new ReactNativeBleTransport({ scanTimeout: 1 });
|
|
146
|
+
transport.blePlxManager = bleManager;
|
|
147
|
+
transport.resolveCharacteristics = jest.fn(() =>
|
|
148
|
+
Promise.resolve({ writeCharacteristic, notifyCharacteristic })
|
|
149
|
+
);
|
|
150
|
+
transport.init({ debug: jest.fn(), error: jest.fn() }, new EventEmitter());
|
|
151
|
+
transport.configure(protocolV1Schema);
|
|
152
|
+
transport.configureProtocolV2(protocolV2Schema);
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
transport,
|
|
156
|
+
uuid,
|
|
157
|
+
sentSeqs,
|
|
158
|
+
writeCharacteristic,
|
|
159
|
+
setShouldRespond(value: boolean) {
|
|
160
|
+
shouldRespond = value;
|
|
161
|
+
},
|
|
162
|
+
emitMonitorError(error: Error & { reason?: string }) {
|
|
163
|
+
notifyCallback?.(error, null);
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
169
|
+
test('keeps the Protocol V2 sequence across probe and the next call', async () => {
|
|
170
|
+
const { transport, uuid, sentSeqs } = createHarness();
|
|
171
|
+
|
|
172
|
+
await transport.acquire({ uuid });
|
|
173
|
+
await transport.call(uuid, 'Ping', { message: 'after-probe' });
|
|
174
|
+
|
|
175
|
+
expect(sentSeqs).toEqual([1, 2]);
|
|
176
|
+
expect(bytesToHex(new Uint8Array([sentSeqs[0], sentSeqs[1]]))).toBe('0102');
|
|
177
|
+
await transport.release(uuid, true);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test('rejects the active Protocol V2 reader when the current monitor errors', async () => {
|
|
181
|
+
const harness = createHarness();
|
|
182
|
+
const { transport, uuid, sentSeqs } = harness;
|
|
183
|
+
await transport.acquire({ uuid });
|
|
184
|
+
harness.setShouldRespond(false);
|
|
185
|
+
|
|
186
|
+
const call = transport.call(uuid, 'Ping', { message: 'wait-for-monitor' }, { timeoutMs: 50 });
|
|
187
|
+
while (sentSeqs.length < 2) {
|
|
188
|
+
await Promise.resolve();
|
|
189
|
+
}
|
|
190
|
+
await new Promise(resolve => {
|
|
191
|
+
setTimeout(resolve, 0);
|
|
192
|
+
});
|
|
193
|
+
harness.emitMonitorError(Object.assign(new Error('monitor failed'), { reason: 'link lost' }));
|
|
194
|
+
|
|
195
|
+
await expect(call).rejects.toMatchObject({
|
|
196
|
+
errorCode: HardwareErrorCode.BleCharacteristicNotifyError,
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test('retains the sequence cursor when a new monitor generation is acquired', async () => {
|
|
201
|
+
const { transport, uuid, sentSeqs } = createHarness();
|
|
202
|
+
|
|
203
|
+
await transport.acquire({ uuid });
|
|
204
|
+
await transport.release(uuid, true);
|
|
205
|
+
await transport.acquire({ uuid });
|
|
206
|
+
|
|
207
|
+
expect(sentSeqs).toEqual([1, 2]);
|
|
208
|
+
await transport.release(uuid, true);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test('uses withoutResponse for normal and high-volume calls', async () => {
|
|
212
|
+
const { transport, uuid, writeCharacteristic } = createHarness();
|
|
213
|
+
|
|
214
|
+
await transport.acquire({ uuid });
|
|
215
|
+
expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalledTimes(1);
|
|
216
|
+
expect(writeCharacteristic.writeWithResponse).not.toHaveBeenCalled();
|
|
217
|
+
|
|
218
|
+
await transport.call(uuid, 'Ping', { message: 'normal' });
|
|
219
|
+
expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalledTimes(2);
|
|
220
|
+
expect(writeCharacteristic.writeWithResponse).not.toHaveBeenCalled();
|
|
221
|
+
|
|
222
|
+
await transport.call(uuid, 'FileWrite', {});
|
|
223
|
+
expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalledTimes(3);
|
|
224
|
+
expect(writeCharacteristic.writeWithResponse).not.toHaveBeenCalled();
|
|
225
|
+
await transport.release(uuid, true);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test('rejects an active Protocol V2 reader when disconnect resets the link', async () => {
|
|
229
|
+
const harness = createHarness();
|
|
230
|
+
const { transport, uuid, sentSeqs } = harness;
|
|
231
|
+
await transport.acquire({ uuid });
|
|
232
|
+
harness.setShouldRespond(false);
|
|
233
|
+
|
|
234
|
+
const call = transport.call(uuid, 'Ping', { message: 'disconnect' }, { timeoutMs: 50 });
|
|
235
|
+
while (sentSeqs.length < 2) {
|
|
236
|
+
await Promise.resolve();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const rejection = expect(call).rejects.toThrow('React Native BLE transport disconnected');
|
|
240
|
+
await transport.disconnect(uuid);
|
|
241
|
+
await rejection;
|
|
242
|
+
});
|
|
243
|
+
});
|
package/src/bleStrategy.ts
CHANGED
|
@@ -7,35 +7,10 @@ export type BleWriteCapability = {
|
|
|
7
7
|
isWritableWithoutResponse?: boolean | null;
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
-
export type BleWriteMode = 'withResponse' | 'withoutResponse';
|
|
11
|
-
|
|
12
10
|
export function hasWritableCapability(characteristic: BleWriteCapability) {
|
|
13
11
|
return !!(characteristic.isWritableWithResponse || characteristic.isWritableWithoutResponse);
|
|
14
12
|
}
|
|
15
13
|
|
|
16
|
-
export function resolveBleWriteMode(
|
|
17
|
-
characteristic: BleWriteCapability,
|
|
18
|
-
preferredMode: BleWriteMode = 'withoutResponse'
|
|
19
|
-
): BleWriteMode {
|
|
20
|
-
if (preferredMode === 'withoutResponse' && characteristic.isWritableWithoutResponse) {
|
|
21
|
-
return 'withoutResponse';
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (preferredMode === 'withResponse' && characteristic.isWritableWithResponse) {
|
|
25
|
-
return 'withResponse';
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (characteristic.isWritableWithoutResponse) {
|
|
29
|
-
return 'withoutResponse';
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (characteristic.isWritableWithResponse) {
|
|
33
|
-
return 'withResponse';
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return preferredMode;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
14
|
export function resolveProtocolV2PacketCapacity({
|
|
40
15
|
platform,
|
|
41
16
|
iosPacketLength = IOS_PACKET_LENGTH,
|