@onekeyfe/hd-transport-react-native 1.1.34-alpha.3 → 1.1.34-alpha.4
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/BleManager.d.ts.map +1 -1
- package/dist/BleTransport.d.ts +0 -2
- package/dist/BleTransport.d.ts.map +1 -1
- package/dist/constants.d.ts +0 -4
- package/dist/constants.d.ts.map +1 -1
- package/dist/index.d.ts +6 -62
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +150 -799
- package/dist/subscribeBleOn.d.ts.map +1 -1
- package/dist/types.d.ts +0 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/BleManager.ts +14 -11
- package/src/BleTransport.ts +5 -9
- package/src/constants.ts +1 -33
- package/src/index.ts +91 -836
- package/src/subscribeBleOn.ts +2 -0
- package/src/types.ts +0 -3
- package/src/utils/validateNotify.ts +4 -4
- package/dist/bleStrategy.d.ts +0 -13
- package/dist/bleStrategy.d.ts.map +0 -1
- package/dist/logger.d.ts +0 -14
- package/dist/logger.d.ts.map +0 -1
- package/dist/transportLog.d.ts +0 -13
- package/dist/transportLog.d.ts.map +0 -1
- package/src/__tests__/bleStrategy.test.ts +0 -42
- package/src/__tests__/constants.test.ts +0 -18
- package/src/__tests__/protocolV2Link.test.ts +0 -243
- package/src/bleStrategy.ts +0 -35
- package/src/logger.ts +0 -19
- package/src/transportLog.ts +0 -18
package/src/index.ts
CHANGED
|
@@ -9,44 +9,33 @@ import {
|
|
|
9
9
|
} from 'react-native-ble-plx';
|
|
10
10
|
import ByteBuffer from 'bytebuffer';
|
|
11
11
|
import transport, {
|
|
12
|
+
COMMON_HEADER_SIZE,
|
|
12
13
|
LogBlockCommand,
|
|
13
14
|
type OneKeyDeviceInfoBase,
|
|
14
|
-
PROTOCOL_V1_MESSAGE_HEADER_SIZE,
|
|
15
|
-
PROTOCOL_V2_BLE_FRAME_MAX_BYTES,
|
|
16
|
-
PROTOCOL_V2_CHANNEL_BLE_UART,
|
|
17
|
-
type ProtocolType,
|
|
18
|
-
ProtocolV2FrameAssembler,
|
|
19
|
-
ProtocolV2LinkManager,
|
|
20
|
-
type TransportCallOptions,
|
|
21
|
-
probeProtocolV2 as probeProtocolV2Helper,
|
|
22
15
|
} from '@onekeyfe/hd-transport';
|
|
23
16
|
import { ERRORS, HardwareErrorCode, createDeferred, isOnekeyDevice } from '@onekeyfe/hd-shared';
|
|
17
|
+
import { LoggerNames, getLogger } from '@onekeyfe/hd-core';
|
|
24
18
|
|
|
25
19
|
import { getConnectedDeviceIds, onDeviceBondState, pairDevice } from './BleManager';
|
|
26
|
-
import { hasWritableCapability, resolveProtocolV2PacketCapacity } from './bleStrategy';
|
|
27
20
|
import { subscribeBleOn } from './subscribeBleOn';
|
|
28
21
|
import {
|
|
29
22
|
ANDROID_PACKET_LENGTH,
|
|
30
23
|
IOS_PACKET_LENGTH,
|
|
31
|
-
getBleUuidKey,
|
|
32
24
|
getBluetoothServiceUuids,
|
|
33
25
|
getInfosForServiceUuid,
|
|
34
|
-
isSameBleUuid,
|
|
35
26
|
} from './constants';
|
|
36
27
|
import { isHeaderChunk } from './utils/validateNotify';
|
|
37
28
|
import BleTransport from './BleTransport';
|
|
38
29
|
import timer from './utils/timer';
|
|
39
|
-
import { bleLogger, setBleLogger } from './logger';
|
|
40
|
-
import { createTransportCallLog } from './transportLog';
|
|
41
30
|
|
|
42
31
|
import type { Deferred } from '@onekeyfe/hd-shared';
|
|
43
32
|
import type { Characteristic, Device, Subscription } from 'react-native-ble-plx';
|
|
44
33
|
import type EventEmitter from 'events';
|
|
45
34
|
import type { BleAcquireInput, TransportOptions } from './types';
|
|
46
35
|
|
|
47
|
-
const { check,
|
|
36
|
+
const { check, buildBuffers, receiveOne, parseConfigure } = transport;
|
|
48
37
|
|
|
49
|
-
const Log =
|
|
38
|
+
const Log = getLogger(LoggerNames.HdBleTransport);
|
|
50
39
|
|
|
51
40
|
const transportCache: Record<string, BleTransport> = {};
|
|
52
41
|
const FIRMWARE_UPLOAD_WRITE_BURST_SIZE = Platform.OS === 'ios' ? 4 : 5;
|
|
@@ -65,6 +54,11 @@ type ResolvedBleCharacteristics = {
|
|
|
65
54
|
notifyCharacteristic: Characteristic;
|
|
66
55
|
};
|
|
67
56
|
|
|
57
|
+
const getBleIdentityName = (device?: { name?: string | null } | null): string | null => {
|
|
58
|
+
const localName = (device as { localName?: string | null } | undefined)?.localName;
|
|
59
|
+
return device?.name ?? localName ?? null;
|
|
60
|
+
};
|
|
61
|
+
|
|
68
62
|
const delay = (ms: number) =>
|
|
69
63
|
new Promise<void>(resolve => {
|
|
70
64
|
setTimeout(resolve, ms);
|
|
@@ -103,76 +97,9 @@ const getFirmwareUploadWriteRetryType = (error: unknown): FirmwareUploadWriteRet
|
|
|
103
97
|
|
|
104
98
|
const resolveFirmwareUploadRetryDelay = (attempt: number, baseDelayMs = 200, maxDelayMs = 1200) =>
|
|
105
99
|
Math.min(baseDelayMs * 2 ** attempt, maxDelayMs);
|
|
106
|
-
const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
|
|
107
|
-
const PROTOCOL_V2_PROBE_TIMEOUT_MS = 10_000;
|
|
108
|
-
const DEVICE_SCAN_TIMEOUT_MS = 8000;
|
|
109
|
-
const IOS_NOTIFY_READY_DELAY_MS = 150;
|
|
110
|
-
const ANDROID_NOTIFY_READY_DELAY_MS = 300;
|
|
111
|
-
export type ProtocolV2BleTuning = {
|
|
112
|
-
iosPacketLength?: number;
|
|
113
|
-
androidPacketLength?: number;
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
type ResolvedProtocolV2BleTuning = Required<ProtocolV2BleTuning>;
|
|
117
|
-
|
|
118
|
-
const DEFAULT_PROTOCOL_V2_BLE_TUNING: ResolvedProtocolV2BleTuning = {
|
|
119
|
-
iosPacketLength: IOS_PACKET_LENGTH,
|
|
120
|
-
androidPacketLength: ANDROID_PACKET_LENGTH,
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
let protocolV2BleTuning: ResolvedProtocolV2BleTuning = { ...DEFAULT_PROTOCOL_V2_BLE_TUNING };
|
|
124
|
-
|
|
125
|
-
const normalizePositiveInteger = (value: unknown, fallback: number) => {
|
|
126
|
-
const normalized = Number(value);
|
|
127
|
-
if (!Number.isFinite(normalized) || normalized <= 0) return fallback;
|
|
128
|
-
return Math.floor(normalized);
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
export function configureProtocolV2BleTuning(tuning: ProtocolV2BleTuning = {}) {
|
|
132
|
-
protocolV2BleTuning = {
|
|
133
|
-
iosPacketLength: normalizePositiveInteger(
|
|
134
|
-
tuning.iosPacketLength,
|
|
135
|
-
protocolV2BleTuning.iosPacketLength
|
|
136
|
-
),
|
|
137
|
-
androidPacketLength: normalizePositiveInteger(
|
|
138
|
-
tuning.androidPacketLength,
|
|
139
|
-
protocolV2BleTuning.androidPacketLength
|
|
140
|
-
),
|
|
141
|
-
};
|
|
142
|
-
Log?.debug('[ReactNativeBleTransport] BLE tuning configured', protocolV2BleTuning);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export function resetProtocolV2BleTuning() {
|
|
146
|
-
protocolV2BleTuning = { ...DEFAULT_PROTOCOL_V2_BLE_TUNING };
|
|
147
|
-
Log?.debug('[ReactNativeBleTransport] BLE tuning reset', protocolV2BleTuning);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export function getProtocolV2BleTuning() {
|
|
151
|
-
return { ...protocolV2BleTuning };
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function inferProtocolHintFromDeviceName(name?: string | null): ProtocolType | undefined {
|
|
155
|
-
return /\bpro\s*2\b/i.test(name ?? '') ? 'V2' : undefined;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function getDeviceDisplayName(device?: Device | null) {
|
|
159
|
-
return device?.name || device?.localName || null;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function isGenericBleService(uuid?: string | null) {
|
|
163
|
-
return ['1800', '1801', '180a'].includes(getBleUuidKey(uuid));
|
|
164
|
-
}
|
|
165
100
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
getInfosForServiceUuid(serviceUuid, 'classic')
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const ANDROID_REQUEST_MTU = 256;
|
|
173
|
-
|
|
174
|
-
const connectOptions: Record<string, unknown> = {
|
|
175
|
-
requestMTU: ANDROID_REQUEST_MTU,
|
|
101
|
+
let connectOptions: Record<string, unknown> = {
|
|
102
|
+
requestMTU: 256,
|
|
176
103
|
timeout: 3000,
|
|
177
104
|
refreshGatt: 'OnConnected',
|
|
178
105
|
};
|
|
@@ -181,30 +108,12 @@ export type IOneKeyDevice = OneKeyDeviceInfoBase & Device;
|
|
|
181
108
|
|
|
182
109
|
const tryToGetConfiguration = (device: Device) => {
|
|
183
110
|
if (!device || !device.serviceUUIDs) return null;
|
|
184
|
-
const serviceUUID = device.serviceUUIDs
|
|
185
|
-
if (!serviceUUID) return null;
|
|
111
|
+
const [serviceUUID] = device.serviceUUIDs;
|
|
186
112
|
const infos = getInfosForServiceUuid(serviceUUID, 'classic');
|
|
187
113
|
if (!infos) return null;
|
|
188
114
|
return infos;
|
|
189
115
|
};
|
|
190
116
|
|
|
191
|
-
const requestAndroidMtu = async (device: Device) => {
|
|
192
|
-
if (Platform.OS !== 'android') return device;
|
|
193
|
-
|
|
194
|
-
try {
|
|
195
|
-
const mtuDevice = await device.requestMTU(ANDROID_REQUEST_MTU);
|
|
196
|
-
Log?.debug('[ReactNativeBleTransport] MTU configured', {
|
|
197
|
-
deviceId: device.id,
|
|
198
|
-
requested: ANDROID_REQUEST_MTU,
|
|
199
|
-
actual: mtuDevice.mtu,
|
|
200
|
-
});
|
|
201
|
-
return mtuDevice;
|
|
202
|
-
} catch (error) {
|
|
203
|
-
Log?.debug('[ReactNativeBleTransport] Android MTU request failed:', error);
|
|
204
|
-
return device;
|
|
205
|
-
}
|
|
206
|
-
};
|
|
207
|
-
|
|
208
117
|
type IOBleErrorRemap = Error | BleError | null | undefined;
|
|
209
118
|
|
|
210
119
|
function remapError(error: IOBleErrorRemap) {
|
|
@@ -242,15 +151,13 @@ export default class ReactNativeBleTransport {
|
|
|
242
151
|
|
|
243
152
|
_messages: ReturnType<typeof transport.parseConfigure> | undefined;
|
|
244
153
|
|
|
245
|
-
_messagesV2: ReturnType<typeof transport.parseConfigure> | undefined;
|
|
246
|
-
|
|
247
154
|
name = 'ReactNativeBleTransport';
|
|
248
155
|
|
|
249
156
|
configured = false;
|
|
250
157
|
|
|
251
158
|
stopped = false;
|
|
252
159
|
|
|
253
|
-
scanTimeout =
|
|
160
|
+
scanTimeout = 3000;
|
|
254
161
|
|
|
255
162
|
runPromise: Deferred<any> | null = null;
|
|
256
163
|
|
|
@@ -258,48 +165,11 @@ export default class ReactNativeBleTransport {
|
|
|
258
165
|
|
|
259
166
|
firmwareUploadWriteRecoveryIds = new Set<string>();
|
|
260
167
|
|
|
261
|
-
/** Per-device protocol type detected by active wire-level probe after connect. */
|
|
262
|
-
private deviceProtocol: Map<string, ProtocolType> = new Map();
|
|
263
|
-
|
|
264
|
-
private deviceProtocolHints: Map<string, ProtocolType> = new Map();
|
|
265
|
-
|
|
266
|
-
private protocolV2Assemblers: Map<string, ProtocolV2FrameAssembler> = new Map();
|
|
267
|
-
|
|
268
|
-
private protocolV2FrameQueues: Map<string, Uint8Array[]> = new Map();
|
|
269
|
-
|
|
270
|
-
private protocolV2FramePromises: Map<string, Deferred<Uint8Array>> = new Map();
|
|
271
|
-
|
|
272
|
-
private protocolV2Links = new ProtocolV2LinkManager<string>({
|
|
273
|
-
getSchemas: () => {
|
|
274
|
-
if (!this._messages || !this._messagesV2) {
|
|
275
|
-
throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
|
|
276
|
-
}
|
|
277
|
-
return {
|
|
278
|
-
protocolV1: this._messages,
|
|
279
|
-
protocolV2: this._messagesV2,
|
|
280
|
-
};
|
|
281
|
-
},
|
|
282
|
-
classifyError: () => 'link-fatal',
|
|
283
|
-
onLinkInvalidated: async (uuid, reason) => {
|
|
284
|
-
this.protocolV2Assemblers.get(uuid)?.reset();
|
|
285
|
-
this.rejectProtocolV2Frames(uuid, new Error(reason));
|
|
286
|
-
Log?.debug('[ReactNativeBleTransport] Protocol V2 link invalidated:', uuid, reason);
|
|
287
|
-
if (reason.startsWith('Protocol V2 link-fatal error:')) {
|
|
288
|
-
await this.release(uuid, true);
|
|
289
|
-
}
|
|
290
|
-
},
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
private monitorTokens: Map<string, number> = new Map();
|
|
294
|
-
|
|
295
|
-
private nextMonitorToken = 1;
|
|
296
|
-
|
|
297
168
|
constructor(options: TransportOptions) {
|
|
298
|
-
this.scanTimeout = options.scanTimeout ??
|
|
169
|
+
this.scanTimeout = options.scanTimeout ?? 3000;
|
|
299
170
|
}
|
|
300
171
|
|
|
301
|
-
init(
|
|
302
|
-
setBleLogger(logger);
|
|
172
|
+
init(_logger: any, emitter: EventEmitter) {
|
|
303
173
|
this.emitter = emitter;
|
|
304
174
|
}
|
|
305
175
|
|
|
@@ -309,13 +179,6 @@ export default class ReactNativeBleTransport {
|
|
|
309
179
|
this._messages = messages;
|
|
310
180
|
}
|
|
311
181
|
|
|
312
|
-
configureProtocolV2(signedData: any) {
|
|
313
|
-
this._messagesV2 = parseConfigure(signedData);
|
|
314
|
-
this.protocolV2Links
|
|
315
|
-
.invalidateAllLinks('Protocol V2 schema reconfigured')
|
|
316
|
-
.catch(error => Log?.debug('Protocol V2 schema link cleanup failed:', error));
|
|
317
|
-
}
|
|
318
|
-
|
|
319
182
|
listen() {
|
|
320
183
|
// empty
|
|
321
184
|
}
|
|
@@ -343,29 +206,7 @@ export default class ReactNativeBleTransport {
|
|
|
343
206
|
}
|
|
344
207
|
}
|
|
345
208
|
|
|
346
|
-
let fallbackServiceUuid: string | undefined;
|
|
347
|
-
|
|
348
209
|
if (!infos) {
|
|
349
|
-
const services = await device.services();
|
|
350
|
-
Log?.debug(
|
|
351
|
-
'[ReactNativeBleTransport] Known OneKey service UUID not found, discovered services:',
|
|
352
|
-
services?.map(service => service.uuid)
|
|
353
|
-
);
|
|
354
|
-
|
|
355
|
-
const knownService = services.find(service =>
|
|
356
|
-
getInfosForServiceUuid(service.uuid, 'classic')
|
|
357
|
-
);
|
|
358
|
-
const fallbackService =
|
|
359
|
-
knownService ?? services.find(service => !isGenericBleService(service.uuid)) ?? services[0];
|
|
360
|
-
|
|
361
|
-
if (fallbackService) {
|
|
362
|
-
fallbackServiceUuid = fallbackService.uuid;
|
|
363
|
-
characteristics = await device.characteristicsForService(fallbackService.uuid);
|
|
364
|
-
Log?.debug('[ReactNativeBleTransport] Using fallback BLE service:', fallbackService.uuid);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
if (!infos && !fallbackServiceUuid) {
|
|
369
210
|
try {
|
|
370
211
|
Log?.debug('cancel connection when service not found');
|
|
371
212
|
await device.cancelConnection();
|
|
@@ -375,13 +216,7 @@ export default class ReactNativeBleTransport {
|
|
|
375
216
|
throw ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound);
|
|
376
217
|
}
|
|
377
218
|
|
|
378
|
-
const serviceUuid = infos
|
|
379
|
-
const writeUuid = infos?.writeUuid ?? '00000002-0000-1000-8000-00805f9b34fb';
|
|
380
|
-
const notifyUuid = infos?.notifyUuid ?? '00000003-0000-1000-8000-00805f9b34fb';
|
|
381
|
-
|
|
382
|
-
if (!serviceUuid) {
|
|
383
|
-
throw ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound);
|
|
384
|
-
}
|
|
219
|
+
const { serviceUuid, writeUuid, notifyUuid } = infos;
|
|
385
220
|
|
|
386
221
|
if (!characteristics) {
|
|
387
222
|
characteristics = await device.characteristicsForService(serviceUuid);
|
|
@@ -394,9 +229,9 @@ export default class ReactNativeBleTransport {
|
|
|
394
229
|
let writeCharacteristic;
|
|
395
230
|
let notifyCharacteristic;
|
|
396
231
|
for (const c of characteristics) {
|
|
397
|
-
if (
|
|
232
|
+
if (c.uuid === writeUuid) {
|
|
398
233
|
writeCharacteristic = c;
|
|
399
|
-
} else if (
|
|
234
|
+
} else if (c.uuid === notifyUuid) {
|
|
400
235
|
notifyCharacteristic = c;
|
|
401
236
|
}
|
|
402
237
|
}
|
|
@@ -409,7 +244,7 @@ export default class ReactNativeBleTransport {
|
|
|
409
244
|
throw ERRORS.TypedError('BLECharacteristicNotFound: notify characteristic not found');
|
|
410
245
|
}
|
|
411
246
|
|
|
412
|
-
if (!
|
|
247
|
+
if (!writeCharacteristic.isWritableWithResponse) {
|
|
413
248
|
throw ERRORS.TypedError('BLECharacteristicNotWritable: write characteristic not writable');
|
|
414
249
|
}
|
|
415
250
|
|
|
@@ -432,10 +267,6 @@ export default class ReactNativeBleTransport {
|
|
|
432
267
|
Log?.debug('device disconnect ignored during FirmwareUpload write recovery: ', uuid);
|
|
433
268
|
return;
|
|
434
269
|
}
|
|
435
|
-
if (transportCache[uuid] !== transport) {
|
|
436
|
-
Log?.debug('device disconnect ignored for stale transport: ', device?.id);
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
270
|
|
|
440
271
|
try {
|
|
441
272
|
Log?.debug('device disconnect: ', device?.id);
|
|
@@ -445,14 +276,12 @@ export default class ReactNativeBleTransport {
|
|
|
445
276
|
connectId: device?.id,
|
|
446
277
|
});
|
|
447
278
|
if (this.runPromise) {
|
|
448
|
-
|
|
449
|
-
this.runPromise.reject(error);
|
|
450
|
-
this.rejectAllProtocolV2Frames(error);
|
|
279
|
+
this.runPromise.reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError));
|
|
451
280
|
}
|
|
452
281
|
} catch (e) {
|
|
453
282
|
Log?.debug('device disconnect error: ', e);
|
|
454
283
|
} finally {
|
|
455
|
-
this.release(uuid
|
|
284
|
+
this.release(uuid);
|
|
456
285
|
}
|
|
457
286
|
});
|
|
458
287
|
}
|
|
@@ -475,6 +304,7 @@ export default class ReactNativeBleTransport {
|
|
|
475
304
|
e.errorCode === BleErrorCode.DeviceMTUChangeFailed ||
|
|
476
305
|
e.errorCode === BleErrorCode.OperationCancelled
|
|
477
306
|
) {
|
|
307
|
+
connectOptions = {};
|
|
478
308
|
device = await device.connect();
|
|
479
309
|
} else if (e.errorCode !== BleErrorCode.DeviceAlreadyConnected) {
|
|
480
310
|
throw e;
|
|
@@ -489,18 +319,7 @@ export default class ReactNativeBleTransport {
|
|
|
489
319
|
transport.device = device;
|
|
490
320
|
transport.writeCharacteristic = writeCharacteristic;
|
|
491
321
|
transport.notifyCharacteristic = notifyCharacteristic;
|
|
492
|
-
|
|
493
|
-
this.nextMonitorToken += 1;
|
|
494
|
-
const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
|
|
495
|
-
transport.monitorToken = monitorToken;
|
|
496
|
-
transport.notifyTransactionId = notifyTransactionId;
|
|
497
|
-
this.monitorTokens.set(uuid, monitorToken);
|
|
498
|
-
transport.notifySubscription = this._monitorCharacteristic(
|
|
499
|
-
notifyCharacteristic,
|
|
500
|
-
uuid,
|
|
501
|
-
monitorToken,
|
|
502
|
-
notifyTransactionId
|
|
503
|
-
);
|
|
322
|
+
transport.notifySubscription = this._monitorCharacteristic(notifyCharacteristic, uuid);
|
|
504
323
|
this.attachDisconnectSubscription(transport, device, uuid);
|
|
505
324
|
} finally {
|
|
506
325
|
this.firmwareUploadWriteRecoveryIds.delete(uuid);
|
|
@@ -546,11 +365,11 @@ export default class ReactNativeBleTransport {
|
|
|
546
365
|
blePlxManager.startDeviceScan(
|
|
547
366
|
getBluetoothServiceUuids(),
|
|
548
367
|
{
|
|
549
|
-
allowDuplicates: true,
|
|
550
368
|
scanMode: ScanMode.LowLatency,
|
|
551
369
|
},
|
|
552
370
|
(error, device) => {
|
|
553
371
|
if (error) {
|
|
372
|
+
Log?.debug('ble scan manager: ', blePlxManager);
|
|
554
373
|
Log?.debug('ble scan error: ', error);
|
|
555
374
|
if (
|
|
556
375
|
[BleErrorCode.BluetoothPoweredOff, BleErrorCode.BluetoothInUnknownState].includes(
|
|
@@ -573,20 +392,14 @@ export default class ReactNativeBleTransport {
|
|
|
573
392
|
return;
|
|
574
393
|
}
|
|
575
394
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
395
|
+
if (isOnekeyDevice(getBleIdentityName(device), device?.id)) {
|
|
396
|
+
Log?.debug('search device start ======================');
|
|
397
|
+
const { name, localName, id } = device ?? {};
|
|
398
|
+
Log?.debug(
|
|
399
|
+
`device name: ${name ?? ''}\nlocalName: ${localName ?? ''}\nid: ${id ?? ''}`
|
|
400
|
+
);
|
|
582
401
|
addDevice(device as unknown as Device);
|
|
583
|
-
|
|
584
|
-
Log?.debug('[ReactNativeBleTransport] Pro2-like BLE device was not accepted:', {
|
|
585
|
-
name: device?.name,
|
|
586
|
-
localName: device?.localName,
|
|
587
|
-
id: device?.id,
|
|
588
|
-
serviceUUIDs: device?.serviceUUIDs,
|
|
589
|
-
});
|
|
402
|
+
Log?.debug('search device end ======================\n');
|
|
590
403
|
}
|
|
591
404
|
}
|
|
592
405
|
);
|
|
@@ -598,6 +411,7 @@ export default class ReactNativeBleTransport {
|
|
|
598
411
|
const hasCachedServiceUuid = Boolean(serviceUUIDs?.length);
|
|
599
412
|
const keepDevice = Platform.OS === 'ios' || hasCachedServiceUuid;
|
|
600
413
|
if (keepDevice) {
|
|
414
|
+
Log?.debug('search connected peripheral: ', device.id);
|
|
601
415
|
addDevice(device as unknown as Device);
|
|
602
416
|
}
|
|
603
417
|
}
|
|
@@ -606,22 +420,7 @@ export default class ReactNativeBleTransport {
|
|
|
606
420
|
|
|
607
421
|
const addDevice = (device: Device) => {
|
|
608
422
|
if (deviceList.every(d => d.id !== device.id)) {
|
|
609
|
-
|
|
610
|
-
const protocolHint = inferProtocolHintFromDeviceName(displayName);
|
|
611
|
-
if (protocolHint) {
|
|
612
|
-
this.deviceProtocolHints.set(device.id, protocolHint);
|
|
613
|
-
}
|
|
614
|
-
deviceList.push({
|
|
615
|
-
...device,
|
|
616
|
-
name: displayName,
|
|
617
|
-
commType: 'ble',
|
|
618
|
-
} as IOneKeyDevice);
|
|
619
|
-
Log?.debug('[ReactNativeBleTransport] OneKey BLE device discovered', {
|
|
620
|
-
deviceId: device.id,
|
|
621
|
-
name: displayName,
|
|
622
|
-
serviceUUIDs: device.serviceUUIDs,
|
|
623
|
-
protocolHint,
|
|
624
|
-
});
|
|
423
|
+
deviceList.push({ ...device, commType: 'ble' } as IOneKeyDevice);
|
|
625
424
|
}
|
|
626
425
|
};
|
|
627
426
|
|
|
@@ -633,40 +432,25 @@ export default class ReactNativeBleTransport {
|
|
|
633
432
|
}
|
|
634
433
|
|
|
635
434
|
async acquire(input: BleAcquireInput) {
|
|
636
|
-
const { uuid, forceCleanRunPromise
|
|
435
|
+
const { uuid, forceCleanRunPromise } = input;
|
|
637
436
|
|
|
638
437
|
if (!uuid) {
|
|
639
438
|
throw ERRORS.TypedError(HardwareErrorCode.BleRequiredUUID);
|
|
640
439
|
}
|
|
641
440
|
|
|
642
|
-
|
|
643
|
-
if (cachedTransport) {
|
|
644
|
-
const cachedProtocol = this.deviceProtocol.get(uuid);
|
|
645
|
-
const isCachedDeviceConnected = await cachedTransport.device.isConnected().catch(() => false);
|
|
646
|
-
if (
|
|
647
|
-
isCachedDeviceConnected &&
|
|
648
|
-
cachedProtocol &&
|
|
649
|
-
(!expectedProtocol || cachedProtocol === expectedProtocol)
|
|
650
|
-
) {
|
|
651
|
-
Log?.debug('[ReactNativeBleTransport] reuse cached BLE transport:', uuid, cachedProtocol);
|
|
652
|
-
return { uuid, protocolType: cachedProtocol };
|
|
653
|
-
}
|
|
441
|
+
let device: Device | null = null;
|
|
654
442
|
|
|
443
|
+
if (transportCache[uuid]) {
|
|
655
444
|
/**
|
|
656
|
-
* If the transport is not
|
|
657
|
-
*
|
|
445
|
+
* If the transport is not released due to an exception operation
|
|
446
|
+
* it will be handled again here
|
|
658
447
|
*/
|
|
659
|
-
Log?.debug('transport not
|
|
660
|
-
await this.release(uuid
|
|
448
|
+
Log?.debug('transport not be released, will release: ', uuid);
|
|
449
|
+
await this.release(uuid);
|
|
661
450
|
}
|
|
662
451
|
|
|
663
|
-
let device: Device | null = null;
|
|
664
|
-
|
|
665
452
|
if (forceCleanRunPromise && this.runPromise) {
|
|
666
|
-
|
|
667
|
-
this.runPromise.reject(error);
|
|
668
|
-
this.rejectAllProtocolV2Frames(error);
|
|
669
|
-
this.runPromise = null;
|
|
453
|
+
this.runPromise.reject(ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise));
|
|
670
454
|
Log?.debug('Force clean Bluetooth run promise, forceCleanRunPromise: ', forceCleanRunPromise);
|
|
671
455
|
}
|
|
672
456
|
|
|
@@ -678,12 +462,11 @@ export default class ReactNativeBleTransport {
|
|
|
678
462
|
throw error;
|
|
679
463
|
}
|
|
680
464
|
|
|
465
|
+
// check device is bonded
|
|
681
466
|
if (Platform.OS === 'android') {
|
|
682
467
|
const bondState = await pairDevice(uuid);
|
|
683
468
|
if (bondState.bonding) {
|
|
684
469
|
await onDeviceBondState(uuid);
|
|
685
|
-
} else if (!bondState.bonded) {
|
|
686
|
-
throw ERRORS.TypedError(HardwareErrorCode.BleDeviceNotBonded, 'device is not bonded');
|
|
687
470
|
}
|
|
688
471
|
}
|
|
689
472
|
|
|
@@ -709,6 +492,7 @@ export default class ReactNativeBleTransport {
|
|
|
709
492
|
e.errorCode === BleErrorCode.DeviceMTUChangeFailed ||
|
|
710
493
|
e.errorCode === BleErrorCode.OperationCancelled
|
|
711
494
|
) {
|
|
495
|
+
connectOptions = {};
|
|
712
496
|
Log?.debug('first try to reconnect without params');
|
|
713
497
|
device = await blePlxManager.connectToDevice(uuid);
|
|
714
498
|
} else if (e.errorCode === BleErrorCode.DeviceAlreadyConnected) {
|
|
@@ -728,16 +512,17 @@ export default class ReactNativeBleTransport {
|
|
|
728
512
|
Log?.debug('not connected, try to connect to device: ', uuid);
|
|
729
513
|
|
|
730
514
|
try {
|
|
731
|
-
|
|
515
|
+
await device.connect(connectOptions);
|
|
732
516
|
} catch (e) {
|
|
733
517
|
Log?.debug('not connected, try to connect to device has error: ', e);
|
|
734
518
|
if (
|
|
735
519
|
e.errorCode === BleErrorCode.DeviceMTUChangeFailed ||
|
|
736
520
|
e.errorCode === BleErrorCode.OperationCancelled
|
|
737
521
|
) {
|
|
522
|
+
connectOptions = {};
|
|
738
523
|
Log?.debug('second try to reconnect without params');
|
|
739
524
|
try {
|
|
740
|
-
|
|
525
|
+
await device.connect();
|
|
741
526
|
} catch (e) {
|
|
742
527
|
Log?.debug('last try to reconnect error: ', e);
|
|
743
528
|
// last try to reconnect device if this issue exists
|
|
@@ -745,7 +530,7 @@ export default class ReactNativeBleTransport {
|
|
|
745
530
|
if (e.errorCode === BleErrorCode.OperationCancelled) {
|
|
746
531
|
Log?.debug('last try to reconnect');
|
|
747
532
|
await device.cancelConnection();
|
|
748
|
-
|
|
533
|
+
await device.connect();
|
|
749
534
|
}
|
|
750
535
|
}
|
|
751
536
|
} else {
|
|
@@ -754,50 +539,18 @@ export default class ReactNativeBleTransport {
|
|
|
754
539
|
}
|
|
755
540
|
}
|
|
756
541
|
|
|
757
|
-
device = await requestAndroidMtu(device);
|
|
758
542
|
const { writeCharacteristic, notifyCharacteristic } = await this.resolveCharacteristics(device);
|
|
759
543
|
|
|
760
|
-
const protocolHint = expectedProtocol
|
|
761
|
-
? undefined
|
|
762
|
-
: this.deviceProtocolHints.get(uuid) ??
|
|
763
|
-
inferProtocolHintFromDeviceName(getDeviceDisplayName(device));
|
|
764
|
-
|
|
765
544
|
// release transport before new transport instance
|
|
766
|
-
await this.release(uuid
|
|
767
|
-
if (protocolHint) {
|
|
768
|
-
this.deviceProtocolHints.set(uuid, protocolHint);
|
|
769
|
-
}
|
|
545
|
+
await this.release(uuid);
|
|
770
546
|
|
|
771
547
|
const transport = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
|
|
772
|
-
if (Platform.OS === 'android') {
|
|
773
|
-
transport.mtuSize = typeof device.mtu === 'number' ? device.mtu : transport.mtuSize;
|
|
774
|
-
}
|
|
775
|
-
const monitorToken = this.nextMonitorToken;
|
|
776
|
-
this.nextMonitorToken += 1;
|
|
777
|
-
const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
|
|
778
|
-
transport.monitorToken = monitorToken;
|
|
779
|
-
transport.notifyTransactionId = notifyTransactionId;
|
|
780
|
-
this.monitorTokens.set(uuid, monitorToken);
|
|
781
548
|
transport.notifySubscription = this._monitorCharacteristic(
|
|
782
549
|
transport.notifyCharacteristic,
|
|
783
|
-
uuid
|
|
784
|
-
monitorToken,
|
|
785
|
-
notifyTransactionId
|
|
550
|
+
uuid
|
|
786
551
|
);
|
|
787
552
|
transportCache[uuid] = transport;
|
|
788
553
|
|
|
789
|
-
this.protocolV2Assemblers.set(uuid, new ProtocolV2FrameAssembler());
|
|
790
|
-
|
|
791
|
-
if (Platform.OS === 'ios') {
|
|
792
|
-
await new Promise<void>(resolve => {
|
|
793
|
-
setTimeout(resolve, IOS_NOTIFY_READY_DELAY_MS);
|
|
794
|
-
});
|
|
795
|
-
} else if (Platform.OS === 'android') {
|
|
796
|
-
await delay(ANDROID_NOTIFY_READY_DELAY_MS);
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
const protocolType = await this.detectProtocol(uuid, expectedProtocol, protocolHint);
|
|
800
|
-
|
|
801
554
|
this.emitter?.emit('device-connect', {
|
|
802
555
|
name: device.name,
|
|
803
556
|
id: device.id,
|
|
@@ -806,19 +559,13 @@ export default class ReactNativeBleTransport {
|
|
|
806
559
|
|
|
807
560
|
this.attachDisconnectSubscription(transport, device, uuid);
|
|
808
561
|
|
|
809
|
-
return { uuid
|
|
562
|
+
return { uuid };
|
|
810
563
|
}
|
|
811
564
|
|
|
812
|
-
_monitorCharacteristic(
|
|
813
|
-
characteristic: Characteristic,
|
|
814
|
-
uuid: string,
|
|
815
|
-
monitorToken: number,
|
|
816
|
-
notifyTransactionId: string
|
|
817
|
-
): Subscription {
|
|
565
|
+
_monitorCharacteristic(characteristic: Characteristic, uuid: string): Subscription {
|
|
818
566
|
let bufferLength = 0;
|
|
819
567
|
let buffer: any[] = [];
|
|
820
568
|
const subscription = characteristic.monitor((error, c) => {
|
|
821
|
-
const isCurrentMonitor = this.monitorTokens.get(uuid) === monitorToken;
|
|
822
569
|
if (error) {
|
|
823
570
|
Log?.debug(
|
|
824
571
|
`error monitor ${characteristic.uuid}, deviceId: ${characteristic.deviceID}: ${
|
|
@@ -829,33 +576,6 @@ export default class ReactNativeBleTransport {
|
|
|
829
576
|
Log?.debug('notify error ignored during FirmwareUpload write recovery: ', uuid);
|
|
830
577
|
return;
|
|
831
578
|
}
|
|
832
|
-
if (!isCurrentMonitor) {
|
|
833
|
-
Log?.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
|
|
834
|
-
return;
|
|
835
|
-
}
|
|
836
|
-
if (this.deviceProtocol.get(uuid) === 'V2') {
|
|
837
|
-
let errorCode:
|
|
838
|
-
| typeof HardwareErrorCode.BleDeviceBondError
|
|
839
|
-
| typeof HardwareErrorCode.BleCharacteristicNotifyError
|
|
840
|
-
| typeof HardwareErrorCode.BleCharacteristicNotifyChangeFailure
|
|
841
|
-
| typeof HardwareErrorCode.BleTimeoutError =
|
|
842
|
-
HardwareErrorCode.BleCharacteristicNotifyError;
|
|
843
|
-
if (error.reason?.includes('The connection has timed out unexpectedly')) {
|
|
844
|
-
errorCode = HardwareErrorCode.BleTimeoutError;
|
|
845
|
-
} else if (error.reason?.includes('Encryption is insufficient')) {
|
|
846
|
-
errorCode = HardwareErrorCode.BleDeviceBondError;
|
|
847
|
-
} else if (
|
|
848
|
-
error.reason?.includes('Cannot write client characteristic config descriptor') ||
|
|
849
|
-
error.reason?.includes('Cannot find client characteristic config descriptor') ||
|
|
850
|
-
error.reason?.includes('The handle is invalid') ||
|
|
851
|
-
error.reason?.includes('Writing is not permitted') ||
|
|
852
|
-
error.reason?.includes('notify change failed for device')
|
|
853
|
-
) {
|
|
854
|
-
errorCode = HardwareErrorCode.BleCharacteristicNotifyChangeFailure;
|
|
855
|
-
}
|
|
856
|
-
this.rejectProtocolV2Frames(uuid, ERRORS.TypedError(errorCode));
|
|
857
|
-
return;
|
|
858
|
-
}
|
|
859
579
|
if (this.runPromise) {
|
|
860
580
|
let ERROR:
|
|
861
581
|
| typeof HardwareErrorCode.BleDeviceBondError
|
|
@@ -875,45 +595,27 @@ export default class ReactNativeBleTransport {
|
|
|
875
595
|
error.reason?.includes('Writing is not permitted') || // pro firmware 2.3.4 upgrade
|
|
876
596
|
error.reason?.includes('notify change failed for device')
|
|
877
597
|
) {
|
|
878
|
-
|
|
879
|
-
HardwareErrorCode.BleCharacteristicNotifyChangeFailure
|
|
598
|
+
this.runPromise.reject(
|
|
599
|
+
ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotifyChangeFailure)
|
|
880
600
|
);
|
|
881
|
-
this.runPromise.reject(notifyError);
|
|
882
|
-
this.rejectAllProtocolV2Frames(notifyError);
|
|
883
601
|
Log?.debug(
|
|
884
602
|
`${HardwareErrorCode.BleCharacteristicNotifyChangeFailure} ${error.message} ${error.reason}`
|
|
885
603
|
);
|
|
886
604
|
return;
|
|
887
605
|
}
|
|
888
|
-
|
|
889
|
-
this.runPromise.reject(notifyError);
|
|
890
|
-
this.rejectAllProtocolV2Frames(notifyError);
|
|
606
|
+
this.runPromise.reject(ERRORS.TypedError(ERROR));
|
|
891
607
|
Log?.debug(': monitor notify error, and has unreleased Promise', Error);
|
|
892
608
|
}
|
|
893
609
|
|
|
894
610
|
return;
|
|
895
611
|
}
|
|
896
612
|
|
|
897
|
-
if (!isCurrentMonitor) {
|
|
898
|
-
Log?.debug('monitor data ignored for stale transport: ', uuid, notifyTransactionId);
|
|
899
|
-
return;
|
|
900
|
-
}
|
|
901
|
-
|
|
902
613
|
if (!c) {
|
|
903
614
|
throw ERRORS.TypedError(HardwareErrorCode.BleMonitorError);
|
|
904
615
|
}
|
|
905
616
|
|
|
906
617
|
try {
|
|
907
618
|
const data = Buffer.from(c.value as string, 'base64');
|
|
908
|
-
const protocol = this.deviceProtocol.get(uuid);
|
|
909
|
-
if (!protocol) {
|
|
910
|
-
Log?.debug('monitor data ignored before protocol detection: ', uuid);
|
|
911
|
-
return;
|
|
912
|
-
}
|
|
913
|
-
if (protocol === 'V2') {
|
|
914
|
-
this.handleProtocolV2Notification(uuid, monitorToken, new Uint8Array(data));
|
|
915
|
-
return;
|
|
916
|
-
}
|
|
917
619
|
// console.log('[hd-transport-react-native] Received a packet, ', 'buffer: ', data);
|
|
918
620
|
if (isHeaderChunk(data)) {
|
|
919
621
|
bufferLength = data.readInt32BE(5);
|
|
@@ -922,7 +624,7 @@ export default class ReactNativeBleTransport {
|
|
|
922
624
|
buffer = buffer.concat([...data]);
|
|
923
625
|
}
|
|
924
626
|
|
|
925
|
-
if (buffer.length -
|
|
627
|
+
if (buffer.length - COMMON_HEADER_SIZE >= bufferLength) {
|
|
926
628
|
const value = Buffer.from(buffer);
|
|
927
629
|
// console.log(
|
|
928
630
|
// '[hd-transport-react-native] Received a complete packet of data, resolve Promise, this.runPromise: ',
|
|
@@ -936,41 +638,17 @@ export default class ReactNativeBleTransport {
|
|
|
936
638
|
}
|
|
937
639
|
} catch (error) {
|
|
938
640
|
Log?.debug('monitor data error: ', error);
|
|
939
|
-
|
|
940
|
-
if (this.deviceProtocol.get(uuid) === 'V2') {
|
|
941
|
-
this.rejectProtocolV2Frames(uuid, notifyError);
|
|
942
|
-
} else {
|
|
943
|
-
this.runPromise?.reject(notifyError);
|
|
944
|
-
}
|
|
641
|
+
this.runPromise?.reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError));
|
|
945
642
|
}
|
|
946
|
-
},
|
|
643
|
+
}, uuid);
|
|
947
644
|
|
|
948
645
|
return subscription;
|
|
949
646
|
}
|
|
950
647
|
|
|
951
|
-
async release(uuid: string
|
|
648
|
+
async release(uuid: string) {
|
|
952
649
|
const transport = transportCache[uuid];
|
|
953
|
-
await this.protocolV2Links.invalidateLink(uuid, 'React Native BLE transport released');
|
|
954
|
-
if (this.runPromise) {
|
|
955
|
-
const error = ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise);
|
|
956
|
-
this.runPromise.reject(error);
|
|
957
|
-
this.runPromise = null;
|
|
958
|
-
this.rejectAllProtocolV2Frames(error);
|
|
959
|
-
} else {
|
|
960
|
-
this.resetProtocolV2Frames(uuid);
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
if (Platform.OS === 'android' && !onclose && transport) {
|
|
964
|
-
this.protocolV2Assemblers.get(uuid)?.reset();
|
|
965
|
-
this.resetProtocolV2Frames(uuid);
|
|
966
|
-
return Promise.resolve(true);
|
|
967
|
-
}
|
|
968
650
|
|
|
969
651
|
if (transport) {
|
|
970
|
-
if (this.monitorTokens.get(uuid) === transport.monitorToken) {
|
|
971
|
-
this.monitorTokens.delete(uuid);
|
|
972
|
-
}
|
|
973
|
-
|
|
974
652
|
// Clean up disconnect subscription first to prevent callbacks on released transport
|
|
975
653
|
Log?.debug('release: removing disconnect subscription for device: ', uuid);
|
|
976
654
|
transport.disconnectSubscription?.remove();
|
|
@@ -984,27 +662,12 @@ export default class ReactNativeBleTransport {
|
|
|
984
662
|
transport.notifySubscription?.remove();
|
|
985
663
|
transport.notifySubscription = undefined;
|
|
986
664
|
|
|
987
|
-
if (transport.notifyTransactionId) {
|
|
988
|
-
try {
|
|
989
|
-
await this.blePlxManager?.cancelTransaction(transport.notifyTransactionId);
|
|
990
|
-
} catch (e) {
|
|
991
|
-
Log?.debug('release: cancel notify transaction error (ignored): ', e?.message || e);
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
|
|
995
665
|
delete transportCache[uuid];
|
|
996
|
-
}
|
|
997
666
|
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
this.resetProtocolV2Frames(uuid);
|
|
1003
|
-
|
|
1004
|
-
try {
|
|
1005
|
-
await this.blePlxManager?.cancelTransaction(uuid);
|
|
1006
|
-
} catch (e) {
|
|
1007
|
-
Log?.debug('release: cancel transaction error (ignored): ', e?.message || e);
|
|
667
|
+
// Temporary close the Android disconnect after each request
|
|
668
|
+
if (Platform.OS === 'android') {
|
|
669
|
+
// await this.blePlxManager?.cancelDeviceConnection(uuid);
|
|
670
|
+
}
|
|
1008
671
|
}
|
|
1009
672
|
|
|
1010
673
|
return Promise.resolve(true);
|
|
@@ -1014,12 +677,7 @@ export default class ReactNativeBleTransport {
|
|
|
1014
677
|
await this.call(session, name, data);
|
|
1015
678
|
}
|
|
1016
679
|
|
|
1017
|
-
async call(
|
|
1018
|
-
uuid: string,
|
|
1019
|
-
name: string,
|
|
1020
|
-
data: Record<string, unknown>,
|
|
1021
|
-
options?: TransportCallOptions
|
|
1022
|
-
) {
|
|
680
|
+
async call(uuid: string, name: string, data: Record<string, unknown>) {
|
|
1023
681
|
if (this.stopped) {
|
|
1024
682
|
// eslint-disable-next-line prefer-promise-reject-errors
|
|
1025
683
|
return Promise.reject(ERRORS.TypedError('Transport stopped.'));
|
|
@@ -1028,44 +686,33 @@ export default class ReactNativeBleTransport {
|
|
|
1028
686
|
throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
|
|
1029
687
|
}
|
|
1030
688
|
|
|
1031
|
-
const protocol = this.getProtocolType(uuid);
|
|
1032
|
-
if (!protocol) {
|
|
1033
|
-
throw ERRORS.TypedError(
|
|
1034
|
-
HardwareErrorCode.RuntimeError,
|
|
1035
|
-
`Device protocol has not been detected for ${uuid}`
|
|
1036
|
-
);
|
|
1037
|
-
}
|
|
1038
|
-
Log?.debug('transport call', createTransportCallLog(name, protocol, data));
|
|
1039
|
-
|
|
1040
|
-
if (protocol === 'V2') {
|
|
1041
|
-
return this.callProtocolV2(uuid, name, data, options);
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
689
|
const forceRun = name === 'Initialize' || name === 'Cancel';
|
|
690
|
+
|
|
691
|
+
Log?.debug('transport-react-native call this.runPromise', this.runPromise);
|
|
1045
692
|
if (this.runPromise && !forceRun) {
|
|
1046
693
|
throw ERRORS.TypedError(HardwareErrorCode.TransportCallInProgress);
|
|
1047
694
|
}
|
|
1048
695
|
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
private async callProtocolV1(
|
|
1053
|
-
uuid: string,
|
|
1054
|
-
name: string,
|
|
1055
|
-
data: Record<string, unknown>,
|
|
1056
|
-
options?: TransportCallOptions
|
|
1057
|
-
) {
|
|
1058
|
-
if (!this._messages) {
|
|
1059
|
-
throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
|
|
696
|
+
const transport = transportCache[uuid];
|
|
697
|
+
if (!transport) {
|
|
698
|
+
throw ERRORS.TypedError(HardwareErrorCode.TransportNotFound);
|
|
1060
699
|
}
|
|
1061
700
|
|
|
1062
|
-
|
|
1063
|
-
const runPromise = createDeferred<string>();
|
|
1064
|
-
runPromise.promise.catch(() => undefined);
|
|
1065
|
-
this.runPromise = runPromise;
|
|
701
|
+
this.runPromise = createDeferred();
|
|
1066
702
|
const messages = this._messages;
|
|
1067
|
-
|
|
1068
|
-
|
|
703
|
+
// Upload resources on low-end phones may OOM
|
|
704
|
+
if (name === 'ResourceUpdate' || name === 'ResourceAck') {
|
|
705
|
+
Log?.debug('transport-react-native', 'call-', ' name: ', name, ' data: ', {
|
|
706
|
+
file_name: data?.file_name,
|
|
707
|
+
hash: data?.hash,
|
|
708
|
+
});
|
|
709
|
+
} else if (LogBlockCommand.has(name)) {
|
|
710
|
+
Log?.debug('transport-react-native', 'call-', ' name: ', name);
|
|
711
|
+
} else {
|
|
712
|
+
Log?.debug('transport-react-native', 'call-', ' name: ', name, ' data: ', data);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
const buffers = buildBuffers(messages, name, data);
|
|
1069
716
|
|
|
1070
717
|
async function writeChunkedData(
|
|
1071
718
|
buffers: ByteBuffer[],
|
|
@@ -1139,13 +786,14 @@ export default class ReactNativeBleTransport {
|
|
|
1139
786
|
}
|
|
1140
787
|
);
|
|
1141
788
|
} else if (name === 'FirmwareUpload') {
|
|
1142
|
-
Log?.debug('[ReactNativeBleTransport]
|
|
789
|
+
Log?.debug('[ReactNativeBleTransport] FirmwareUpload write uses throttled BLE packets:', {
|
|
1143
790
|
packetCapacity: FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY,
|
|
1144
791
|
burstSize: FIRMWARE_UPLOAD_WRITE_BURST_SIZE,
|
|
1145
792
|
pauseMs: FIRMWARE_UPLOAD_WRITE_PAUSE_MS,
|
|
1146
793
|
flushDelayMs: FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS,
|
|
1147
794
|
maxRetries: FIRMWARE_UPLOAD_WRITE_MAX_RETRIES,
|
|
1148
795
|
});
|
|
796
|
+
|
|
1149
797
|
await writeFirmwareUploadChunkedData(
|
|
1150
798
|
buffers,
|
|
1151
799
|
async data => {
|
|
@@ -1199,6 +847,7 @@ export default class ReactNativeBleTransport {
|
|
|
1199
847
|
for (const o of buffers) {
|
|
1200
848
|
const outData = o.toString('base64');
|
|
1201
849
|
// Upload resources on low-end phones may OOM
|
|
850
|
+
// this.Log.debug('send hex strting: ', o.toString('hex'));
|
|
1202
851
|
try {
|
|
1203
852
|
await transport.writeCharacteristic.writeWithoutResponse(outData);
|
|
1204
853
|
} catch (e) {
|
|
@@ -1216,40 +865,20 @@ export default class ReactNativeBleTransport {
|
|
|
1216
865
|
}
|
|
1217
866
|
|
|
1218
867
|
try {
|
|
1219
|
-
const response = await
|
|
1220
|
-
runPromise.promise,
|
|
1221
|
-
new Promise<never>((_, reject) => {
|
|
1222
|
-
if (options?.timeoutMs) {
|
|
1223
|
-
timeout = setTimeout(() => {
|
|
1224
|
-
const error = ERRORS.TypedError(
|
|
1225
|
-
HardwareErrorCode.BleTimeoutError,
|
|
1226
|
-
`BLE response timeout after ${options.timeoutMs}ms for ${name}`
|
|
1227
|
-
);
|
|
1228
|
-
runPromise.reject(error);
|
|
1229
|
-
reject(error);
|
|
1230
|
-
}, options.timeoutMs);
|
|
1231
|
-
}
|
|
1232
|
-
}),
|
|
1233
|
-
]);
|
|
868
|
+
const response = await this.runPromise.promise;
|
|
1234
869
|
|
|
1235
870
|
if (typeof response !== 'string') {
|
|
1236
871
|
throw new Error('Returning data is not string.');
|
|
1237
872
|
}
|
|
1238
873
|
|
|
1239
|
-
|
|
874
|
+
Log?.debug('receive data: ', response);
|
|
875
|
+
const jsonData = receiveOne(messages, response);
|
|
1240
876
|
return check.call(jsonData);
|
|
1241
877
|
} catch (e) {
|
|
1242
|
-
|
|
1243
|
-
Log?.debug('[ReactNativeBleTransport] Protocol V1 Initialize probe call failed:', e);
|
|
1244
|
-
} else {
|
|
1245
|
-
Log?.error('call error: ', e);
|
|
1246
|
-
}
|
|
878
|
+
Log?.error('call error: ', e);
|
|
1247
879
|
throw e;
|
|
1248
880
|
} finally {
|
|
1249
|
-
|
|
1250
|
-
if (this.runPromise === runPromise) {
|
|
1251
|
-
this.runPromise = null;
|
|
1252
|
-
}
|
|
881
|
+
this.runPromise = null;
|
|
1253
882
|
}
|
|
1254
883
|
}
|
|
1255
884
|
|
|
@@ -1258,7 +887,7 @@ export default class ReactNativeBleTransport {
|
|
|
1258
887
|
}
|
|
1259
888
|
|
|
1260
889
|
async disconnect(session: string) {
|
|
1261
|
-
|
|
890
|
+
Log?.debug('transport-react-native transport resetSession: ', session);
|
|
1262
891
|
const transport = transportCache[session];
|
|
1263
892
|
|
|
1264
893
|
// Clean up disconnect subscription first to prevent onDisconnected callback
|
|
@@ -1316,10 +945,6 @@ export default class ReactNativeBleTransport {
|
|
|
1316
945
|
if (transportCache[session]) {
|
|
1317
946
|
delete transportCache[session];
|
|
1318
947
|
}
|
|
1319
|
-
this.deviceProtocol.delete(session);
|
|
1320
|
-
this.deviceProtocolHints.delete(session);
|
|
1321
|
-
this.protocolV2Assemblers.delete(session);
|
|
1322
|
-
this.resetProtocolV2Frames(session);
|
|
1323
948
|
|
|
1324
949
|
// emit the disconnect event
|
|
1325
950
|
try {
|
|
@@ -1342,374 +967,4 @@ export default class ReactNativeBleTransport {
|
|
|
1342
967
|
}
|
|
1343
968
|
this.runPromise = null;
|
|
1344
969
|
}
|
|
1345
|
-
|
|
1346
|
-
private getCachedTransport(uuid: string) {
|
|
1347
|
-
const transport = transportCache[uuid];
|
|
1348
|
-
if (!transport) {
|
|
1349
|
-
throw ERRORS.TypedError(HardwareErrorCode.TransportNotFound);
|
|
1350
|
-
}
|
|
1351
|
-
return transport;
|
|
1352
|
-
}
|
|
1353
|
-
|
|
1354
|
-
private createProtocolMismatchError(expected: ProtocolType) {
|
|
1355
|
-
return ERRORS.TypedError(
|
|
1356
|
-
HardwareErrorCode.RuntimeError,
|
|
1357
|
-
`Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`
|
|
1358
|
-
);
|
|
1359
|
-
}
|
|
1360
|
-
|
|
1361
|
-
private createProtocolDetectionError() {
|
|
1362
|
-
return ERRORS.TypedError(
|
|
1363
|
-
HardwareErrorCode.BleTimeoutError,
|
|
1364
|
-
'Unable to detect BLE protocol: device did not respond to Protocol V1 Initialize or Protocol V2 Ping'
|
|
1365
|
-
);
|
|
1366
|
-
}
|
|
1367
|
-
|
|
1368
|
-
private clearProbeProtocol(uuid: string, protocol: ProtocolType) {
|
|
1369
|
-
if (this.deviceProtocol.get(uuid) === protocol) {
|
|
1370
|
-
this.deviceProtocol.delete(uuid);
|
|
1371
|
-
}
|
|
1372
|
-
}
|
|
1373
|
-
|
|
1374
|
-
private async detectProtocol(
|
|
1375
|
-
uuid: string,
|
|
1376
|
-
expectedProtocol?: ProtocolType,
|
|
1377
|
-
protocolHint?: ProtocolType
|
|
1378
|
-
): Promise<ProtocolType> {
|
|
1379
|
-
if (expectedProtocol === 'V1') {
|
|
1380
|
-
if (await this.probeProtocolV1(uuid)) {
|
|
1381
|
-
this.deviceProtocol.set(uuid, 'V1');
|
|
1382
|
-
Log?.debug('[ReactNativeBleTransport] protocol detected', {
|
|
1383
|
-
deviceId: uuid,
|
|
1384
|
-
protocol: 'V1',
|
|
1385
|
-
source: 'expected',
|
|
1386
|
-
});
|
|
1387
|
-
return 'V1';
|
|
1388
|
-
}
|
|
1389
|
-
throw this.createProtocolMismatchError(expectedProtocol);
|
|
1390
|
-
}
|
|
1391
|
-
|
|
1392
|
-
if (expectedProtocol === 'V2') {
|
|
1393
|
-
// Skip probing when the caller explicitly confirms V2, such as reconnect after a
|
|
1394
|
-
// firmware reboot where expectedProtocol carries the previously probed result.
|
|
1395
|
-
this.deviceProtocol.set(uuid, 'V2');
|
|
1396
|
-
Log?.debug('[ReactNativeBleTransport] protocol detected', {
|
|
1397
|
-
deviceId: uuid,
|
|
1398
|
-
protocol: 'V2',
|
|
1399
|
-
source: 'expected',
|
|
1400
|
-
});
|
|
1401
|
-
return 'V2';
|
|
1402
|
-
}
|
|
1403
|
-
|
|
1404
|
-
// Protocol must be actively probed after connection. Name, PID, and descriptors only
|
|
1405
|
-
// influence probe order; a V2 hint probes V2 first and falls back to V1.
|
|
1406
|
-
const probeOrder: ProtocolType[] =
|
|
1407
|
-
protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
|
|
1408
|
-
|
|
1409
|
-
for (let i = 0; i < probeOrder.length; i += 1) {
|
|
1410
|
-
const protocol = probeOrder[i];
|
|
1411
|
-
if (i > 0) {
|
|
1412
|
-
// Reset subscriptions and buffers after a failed probe before trying another protocol.
|
|
1413
|
-
await this.resetProbeStateAfterProtocolProbe(uuid, probeOrder[i - 1]);
|
|
1414
|
-
}
|
|
1415
|
-
const detected =
|
|
1416
|
-
protocol === 'V1' ? await this.probeProtocolV1(uuid) : await this.probeProtocolV2(uuid);
|
|
1417
|
-
if (detected) {
|
|
1418
|
-
this.deviceProtocol.set(uuid, protocol);
|
|
1419
|
-
Log?.debug('[ReactNativeBleTransport] protocol detected', {
|
|
1420
|
-
deviceId: uuid,
|
|
1421
|
-
protocol,
|
|
1422
|
-
source: 'probe',
|
|
1423
|
-
});
|
|
1424
|
-
return protocol;
|
|
1425
|
-
}
|
|
1426
|
-
}
|
|
1427
|
-
|
|
1428
|
-
this.deviceProtocol.delete(uuid);
|
|
1429
|
-
throw this.createProtocolDetectionError();
|
|
1430
|
-
}
|
|
1431
|
-
|
|
1432
|
-
private async resetProbeStateAfterProtocolProbe(uuid: string, protocol: ProtocolType) {
|
|
1433
|
-
const transport = transportCache[uuid];
|
|
1434
|
-
await this.protocolV2Links.invalidateLink(
|
|
1435
|
-
uuid,
|
|
1436
|
-
`Reset notify state after Protocol ${protocol} probe`
|
|
1437
|
-
);
|
|
1438
|
-
this.protocolV2Assemblers.get(uuid)?.reset();
|
|
1439
|
-
this.resetProtocolV2Frames(uuid);
|
|
1440
|
-
if (this.runPromise) {
|
|
1441
|
-
const error = ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise);
|
|
1442
|
-
this.runPromise.reject(error);
|
|
1443
|
-
this.runPromise = null;
|
|
1444
|
-
}
|
|
1445
|
-
|
|
1446
|
-
if (!transport) return;
|
|
1447
|
-
|
|
1448
|
-
const previousNotifyTransactionId = transport.notifyTransactionId;
|
|
1449
|
-
if (this.monitorTokens.get(uuid) === transport.monitorToken) {
|
|
1450
|
-
this.monitorTokens.delete(uuid);
|
|
1451
|
-
}
|
|
1452
|
-
transport.notifySubscription?.remove();
|
|
1453
|
-
transport.notifySubscription = undefined;
|
|
1454
|
-
if (previousNotifyTransactionId) {
|
|
1455
|
-
try {
|
|
1456
|
-
await this.blePlxManager?.cancelTransaction(previousNotifyTransactionId);
|
|
1457
|
-
} catch (error) {
|
|
1458
|
-
Log?.debug(
|
|
1459
|
-
`[ReactNativeBleTransport] cancel notify after Protocol ${protocol} probe failed:`,
|
|
1460
|
-
error?.message || error
|
|
1461
|
-
);
|
|
1462
|
-
}
|
|
1463
|
-
}
|
|
1464
|
-
|
|
1465
|
-
const monitorToken = this.nextMonitorToken;
|
|
1466
|
-
this.nextMonitorToken += 1;
|
|
1467
|
-
const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
|
|
1468
|
-
transport.monitorToken = monitorToken;
|
|
1469
|
-
transport.notifyTransactionId = notifyTransactionId;
|
|
1470
|
-
this.monitorTokens.set(uuid, monitorToken);
|
|
1471
|
-
transport.notifySubscription = this._monitorCharacteristic(
|
|
1472
|
-
transport.notifyCharacteristic,
|
|
1473
|
-
uuid,
|
|
1474
|
-
monitorToken,
|
|
1475
|
-
notifyTransactionId
|
|
1476
|
-
);
|
|
1477
|
-
if (Platform.OS === 'ios') {
|
|
1478
|
-
await new Promise<void>(resolve => {
|
|
1479
|
-
setTimeout(resolve, IOS_NOTIFY_READY_DELAY_MS);
|
|
1480
|
-
});
|
|
1481
|
-
}
|
|
1482
|
-
}
|
|
1483
|
-
|
|
1484
|
-
private async probeProtocolV1(uuid: string) {
|
|
1485
|
-
if (!this._messages) {
|
|
1486
|
-
return false;
|
|
1487
|
-
}
|
|
1488
|
-
|
|
1489
|
-
try {
|
|
1490
|
-
this.deviceProtocol.set(uuid, 'V1');
|
|
1491
|
-
await this.callProtocolV1(uuid, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
|
|
1492
|
-
return true;
|
|
1493
|
-
} catch (error) {
|
|
1494
|
-
this.clearProbeProtocol(uuid, 'V1');
|
|
1495
|
-
Log?.debug('[ReactNativeBleTransport] Protocol V1 Initialize probe failed:', error);
|
|
1496
|
-
return false;
|
|
1497
|
-
}
|
|
1498
|
-
}
|
|
1499
|
-
|
|
1500
|
-
private async probeProtocolV2(uuid: string) {
|
|
1501
|
-
if (!this._messages || !this._messagesV2) {
|
|
1502
|
-
return false;
|
|
1503
|
-
}
|
|
1504
|
-
|
|
1505
|
-
this.deviceProtocol.set(uuid, 'V2');
|
|
1506
|
-
this.protocolV2Assemblers.get(uuid)?.reset();
|
|
1507
|
-
const detected = await probeProtocolV2Helper({
|
|
1508
|
-
call: (name: string, data: Record<string, unknown>, options?: TransportCallOptions) =>
|
|
1509
|
-
this.callProtocolV2(uuid, name, data, options),
|
|
1510
|
-
timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT_MS,
|
|
1511
|
-
logger: Log,
|
|
1512
|
-
logPrefix: 'ProtocolV2 RN-BLE',
|
|
1513
|
-
onProbeFailed: () => {
|
|
1514
|
-
this.protocolV2Assemblers.get(uuid)?.reset();
|
|
1515
|
-
this.resetProtocolV2Frames(uuid);
|
|
1516
|
-
},
|
|
1517
|
-
});
|
|
1518
|
-
if (!detected) {
|
|
1519
|
-
this.clearProbeProtocol(uuid, 'V2');
|
|
1520
|
-
}
|
|
1521
|
-
return detected;
|
|
1522
|
-
}
|
|
1523
|
-
|
|
1524
|
-
private handleProtocolV2Notification(uuid: string, monitorToken: number, data: Uint8Array) {
|
|
1525
|
-
try {
|
|
1526
|
-
if (this.monitorTokens.get(uuid) !== monitorToken) return;
|
|
1527
|
-
|
|
1528
|
-
if (data.length === 0) return;
|
|
1529
|
-
|
|
1530
|
-
const assembler = this.protocolV2Assemblers.get(uuid);
|
|
1531
|
-
if (!assembler) return;
|
|
1532
|
-
|
|
1533
|
-
for (const frameData of assembler.drain(data)) {
|
|
1534
|
-
this.resolveProtocolV2Frame(uuid, frameData);
|
|
1535
|
-
}
|
|
1536
|
-
} catch (error) {
|
|
1537
|
-
Log?.debug('[ReactNativeBleTransport] Protocol V2 notification error:', error);
|
|
1538
|
-
const notifyError = ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError);
|
|
1539
|
-
this.rejectProtocolV2Frames(uuid, notifyError);
|
|
1540
|
-
this.protocolV2Links
|
|
1541
|
-
.invalidateLink(uuid, `Protocol V2 notification error: ${error}`)
|
|
1542
|
-
.catch(invalidateError =>
|
|
1543
|
-
Log?.debug(
|
|
1544
|
-
'[ReactNativeBleTransport] Protocol V2 notify cleanup failed:',
|
|
1545
|
-
invalidateError
|
|
1546
|
-
)
|
|
1547
|
-
);
|
|
1548
|
-
}
|
|
1549
|
-
}
|
|
1550
|
-
|
|
1551
|
-
private getProtocolV2FrameQueue(uuid: string) {
|
|
1552
|
-
let queue = this.protocolV2FrameQueues.get(uuid);
|
|
1553
|
-
if (!queue) {
|
|
1554
|
-
queue = [];
|
|
1555
|
-
this.protocolV2FrameQueues.set(uuid, queue);
|
|
1556
|
-
}
|
|
1557
|
-
return queue;
|
|
1558
|
-
}
|
|
1559
|
-
|
|
1560
|
-
private resolveProtocolV2Frame(uuid: string, frame: Uint8Array) {
|
|
1561
|
-
const framePromise = this.protocolV2FramePromises.get(uuid);
|
|
1562
|
-
if (framePromise) {
|
|
1563
|
-
framePromise.resolve(frame);
|
|
1564
|
-
this.protocolV2FramePromises.delete(uuid);
|
|
1565
|
-
return;
|
|
1566
|
-
}
|
|
1567
|
-
this.getProtocolV2FrameQueue(uuid).push(frame);
|
|
1568
|
-
}
|
|
1569
|
-
|
|
1570
|
-
private rejectAllProtocolV2Frames(error: Error) {
|
|
1571
|
-
this.protocolV2FrameQueues.clear();
|
|
1572
|
-
for (const framePromise of this.protocolV2FramePromises.values()) {
|
|
1573
|
-
framePromise.reject(error);
|
|
1574
|
-
}
|
|
1575
|
-
this.protocolV2FramePromises.clear();
|
|
1576
|
-
}
|
|
1577
|
-
|
|
1578
|
-
private resetProtocolV2Frames(uuid: string) {
|
|
1579
|
-
this.protocolV2FrameQueues.delete(uuid);
|
|
1580
|
-
this.protocolV2FramePromises.delete(uuid);
|
|
1581
|
-
}
|
|
1582
|
-
|
|
1583
|
-
private rejectProtocolV2Frames(uuid: string, error: Error) {
|
|
1584
|
-
this.protocolV2FrameQueues.delete(uuid);
|
|
1585
|
-
const framePromise = this.protocolV2FramePromises.get(uuid);
|
|
1586
|
-
if (framePromise) {
|
|
1587
|
-
this.protocolV2FramePromises.delete(uuid);
|
|
1588
|
-
framePromise.reject(error);
|
|
1589
|
-
}
|
|
1590
|
-
}
|
|
1591
|
-
|
|
1592
|
-
private async readProtocolV2Frame(uuid: string) {
|
|
1593
|
-
const queuedFrame = this.getProtocolV2FrameQueue(uuid).shift();
|
|
1594
|
-
if (queuedFrame) {
|
|
1595
|
-
return queuedFrame;
|
|
1596
|
-
}
|
|
1597
|
-
|
|
1598
|
-
const framePromise = createDeferred<Uint8Array>();
|
|
1599
|
-
this.protocolV2FramePromises.set(uuid, framePromise);
|
|
1600
|
-
try {
|
|
1601
|
-
return await framePromise.promise;
|
|
1602
|
-
} finally {
|
|
1603
|
-
if (this.protocolV2FramePromises.get(uuid) === framePromise) {
|
|
1604
|
-
this.protocolV2FramePromises.delete(uuid);
|
|
1605
|
-
}
|
|
1606
|
-
}
|
|
1607
|
-
}
|
|
1608
|
-
|
|
1609
|
-
private async writeProtocolV2Frame(transport: BleTransport, frame: Uint8Array) {
|
|
1610
|
-
const tuning = getProtocolV2BleTuning();
|
|
1611
|
-
const packetCapacity = resolveProtocolV2PacketCapacity({
|
|
1612
|
-
platform: Platform.OS,
|
|
1613
|
-
iosPacketLength: tuning.iosPacketLength,
|
|
1614
|
-
androidPacketLength: tuning.androidPacketLength,
|
|
1615
|
-
mtu: Platform.OS === 'android' ? transport.mtuSize : undefined,
|
|
1616
|
-
});
|
|
1617
|
-
for (let offset = 0; offset < frame.length; offset += packetCapacity) {
|
|
1618
|
-
const chunk = frame.slice(offset, offset + packetCapacity);
|
|
1619
|
-
const base64 = Buffer.from(chunk).toString('base64');
|
|
1620
|
-
await transport.writeCharacteristic.writeWithoutResponse(base64);
|
|
1621
|
-
}
|
|
1622
|
-
}
|
|
1623
|
-
|
|
1624
|
-
private async callProtocolV2(
|
|
1625
|
-
uuid: string,
|
|
1626
|
-
name: string,
|
|
1627
|
-
data: Record<string, unknown>,
|
|
1628
|
-
options?: TransportCallOptions
|
|
1629
|
-
) {
|
|
1630
|
-
if (!this._messages || !this._messagesV2) {
|
|
1631
|
-
throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
|
|
1632
|
-
}
|
|
1633
|
-
|
|
1634
|
-
const callOptions = {
|
|
1635
|
-
...options,
|
|
1636
|
-
// Align with V1 BLE and the USB V2 base transport: only arm a watchdog when the caller
|
|
1637
|
-
// explicitly passes timeoutMs. Interactive acks (ButtonAck/PinMatrixAck/PassphraseAck)
|
|
1638
|
-
// have their timeoutMs removed by DeviceCommands.stripInteractiveAckTimeout, so we must
|
|
1639
|
-
// not fill in a 30s default here — that would hard-cap the time a user spends confirming
|
|
1640
|
-
// a transaction or entering a PIN/passphrase on the device and tear down the BLE link.
|
|
1641
|
-
timeoutMs: options?.timeoutMs,
|
|
1642
|
-
};
|
|
1643
|
-
const highVolumeWrite = LogBlockCommand.has(name);
|
|
1644
|
-
|
|
1645
|
-
if (highVolumeWrite) {
|
|
1646
|
-
const tuning = getProtocolV2BleTuning();
|
|
1647
|
-
Log?.debug('[ReactNativeBleTransport] Protocol V2 high-volume write configured', {
|
|
1648
|
-
name,
|
|
1649
|
-
writeMode: 'withoutResponse',
|
|
1650
|
-
packetCapacity: Platform.OS === 'ios' ? tuning.iosPacketLength : tuning.androidPacketLength,
|
|
1651
|
-
});
|
|
1652
|
-
}
|
|
1653
|
-
|
|
1654
|
-
try {
|
|
1655
|
-
return await this.protocolV2Links.call(
|
|
1656
|
-
uuid,
|
|
1657
|
-
() => this.createProtocolV2Adapter(uuid),
|
|
1658
|
-
name,
|
|
1659
|
-
data,
|
|
1660
|
-
callOptions
|
|
1661
|
-
);
|
|
1662
|
-
} catch (e) {
|
|
1663
|
-
Log?.error('[ReactNativeBleTransport] Protocol V2 call error:', e);
|
|
1664
|
-
throw e;
|
|
1665
|
-
}
|
|
1666
|
-
}
|
|
1667
|
-
|
|
1668
|
-
private createProtocolV2Adapter(uuid: string) {
|
|
1669
|
-
const generation = this.monitorTokens.get(uuid) ?? 0;
|
|
1670
|
-
const assertCurrentGeneration = () => {
|
|
1671
|
-
if (this.monitorTokens.get(uuid) !== generation) {
|
|
1672
|
-
throw new Error(`Protocol V2 monitor generation changed for ${uuid}`);
|
|
1673
|
-
}
|
|
1674
|
-
};
|
|
1675
|
-
|
|
1676
|
-
return {
|
|
1677
|
-
router: PROTOCOL_V2_CHANNEL_BLE_UART,
|
|
1678
|
-
maxFrameBytes: PROTOCOL_V2_BLE_FRAME_MAX_BYTES,
|
|
1679
|
-
generation,
|
|
1680
|
-
prepareCall: () => {
|
|
1681
|
-
assertCurrentGeneration();
|
|
1682
|
-
this.protocolV2Assemblers.get(uuid)?.reset();
|
|
1683
|
-
this.resetProtocolV2Frames(uuid);
|
|
1684
|
-
},
|
|
1685
|
-
writeFrame: async (frame: Uint8Array) => {
|
|
1686
|
-
assertCurrentGeneration();
|
|
1687
|
-
const currentTransport = this.getCachedTransport(uuid);
|
|
1688
|
-
await this.writeProtocolV2Frame(currentTransport, frame);
|
|
1689
|
-
},
|
|
1690
|
-
readFrame: async () => {
|
|
1691
|
-
assertCurrentGeneration();
|
|
1692
|
-
const rxFrame = await this.readProtocolV2Frame(uuid);
|
|
1693
|
-
if (!(rxFrame instanceof Uint8Array)) {
|
|
1694
|
-
throw new Error('Protocol V2 response is not Uint8Array');
|
|
1695
|
-
}
|
|
1696
|
-
return rxFrame;
|
|
1697
|
-
},
|
|
1698
|
-
reset: (reason: string) => {
|
|
1699
|
-
this.protocolV2Assemblers.get(uuid)?.reset();
|
|
1700
|
-
this.rejectProtocolV2Frames(uuid, new Error(reason));
|
|
1701
|
-
},
|
|
1702
|
-
logger: Log,
|
|
1703
|
-
logPrefix: 'ProtocolV2 RN-BLE',
|
|
1704
|
-
createTimeoutError: (messageName: string, timeout: number) =>
|
|
1705
|
-
ERRORS.TypedError(
|
|
1706
|
-
HardwareErrorCode.BleTimeoutError,
|
|
1707
|
-
`BLE response timeout after ${timeout}ms for ${messageName}`
|
|
1708
|
-
),
|
|
1709
|
-
};
|
|
1710
|
-
}
|
|
1711
|
-
|
|
1712
|
-
getProtocolType(path: string): ProtocolType | undefined {
|
|
1713
|
-
return this.deviceProtocol.get(path);
|
|
1714
|
-
}
|
|
1715
970
|
}
|