@onekeyfe/hd-transport-react-native 1.1.27-alpha.34 → 1.1.27-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/src/index.ts CHANGED
@@ -9,139 +9,35 @@ 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_CHANNEL_BLE_UART,
16
- type ProtocolType,
17
- ProtocolV2FrameAssembler,
18
- ProtocolV2Session,
19
- type TransportCallOptions,
20
- probeProtocolV2 as probeProtocolV2Helper,
21
15
  } from '@onekeyfe/hd-transport';
22
16
  import { ERRORS, HardwareErrorCode, createDeferred, isOnekeyDevice } from '@onekeyfe/hd-shared';
17
+ import { LoggerNames, getLogger } from '@onekeyfe/hd-core';
23
18
 
24
19
  import { getConnectedDeviceIds, onDeviceBondState, pairDevice } from './BleManager';
25
20
  import { subscribeBleOn } from './subscribeBleOn';
26
21
  import {
27
22
  ANDROID_PACKET_LENGTH,
28
23
  IOS_PACKET_LENGTH,
29
- getBleUuidKey,
30
24
  getBluetoothServiceUuids,
31
25
  getInfosForServiceUuid,
32
- isSameBleUuid,
33
26
  } from './constants';
34
27
  import { isHeaderChunk } from './utils/validateNotify';
35
28
  import BleTransport from './BleTransport';
36
29
  import timer from './utils/timer';
37
- import { bleLogger, setBleLogger } from './logger';
38
30
 
39
31
  import type { Deferred } from '@onekeyfe/hd-shared';
40
32
  import type { Characteristic, Device, Subscription } from 'react-native-ble-plx';
41
33
  import type EventEmitter from 'events';
42
34
  import type { BleAcquireInput, TransportOptions } from './types';
43
35
 
44
- const { check, ProtocolV1, parseConfigure } = transport;
36
+ const { check, buildBuffers, receiveOne, parseConfigure } = transport;
45
37
 
46
- const Log = bleLogger;
38
+ const Log = getLogger(LoggerNames.HdBleTransport);
47
39
 
48
40
  const transportCache: Record<string, BleTransport> = {};
49
- const BLE_RESPONSE_TIMEOUT_MS = 30_000;
50
- const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
51
- const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
52
- const DEVICE_SCAN_TIMEOUT_MS = 8000;
53
- const IOS_NOTIFY_READY_DELAY_MS = 150;
54
- const HIGH_VOLUME_WRITE_BURST_SIZE = Platform.OS === 'ios' ? 4 : 6;
55
- const HIGH_VOLUME_WRITE_PAUSE_MS = Platform.OS === 'ios' ? 6 : 2;
56
- const HIGH_VOLUME_WRITE_FLUSH_DELAY_MS = Platform.OS === 'ios' ? 20 : 8;
57
-
58
- const delay = (ms: number) =>
59
- new Promise<void>(resolve => {
60
- setTimeout(resolve, ms);
61
- });
62
-
63
- export type ProtocolV2BleTuning = {
64
- iosPacketLength?: number;
65
- androidPacketLength?: number;
66
- highVolumeWriteBurstSize?: number;
67
- highVolumeWritePauseMs?: number;
68
- highVolumeWriteFlushDelayMs?: number;
69
- highVolumeWriteWithResponse?: boolean;
70
- };
71
-
72
- type ResolvedProtocolV2BleTuning = Required<ProtocolV2BleTuning>;
73
-
74
- const DEFAULT_PROTOCOL_V2_BLE_TUNING: ResolvedProtocolV2BleTuning = {
75
- iosPacketLength: IOS_PACKET_LENGTH,
76
- androidPacketLength: ANDROID_PACKET_LENGTH,
77
- highVolumeWriteBurstSize: HIGH_VOLUME_WRITE_BURST_SIZE,
78
- highVolumeWritePauseMs: HIGH_VOLUME_WRITE_PAUSE_MS,
79
- highVolumeWriteFlushDelayMs: HIGH_VOLUME_WRITE_FLUSH_DELAY_MS,
80
- highVolumeWriteWithResponse: false,
81
- };
82
-
83
- let protocolV2BleTuning: ResolvedProtocolV2BleTuning = { ...DEFAULT_PROTOCOL_V2_BLE_TUNING };
84
-
85
- const normalizePositiveInteger = (value: unknown, fallback: number) => {
86
- const normalized = Number(value);
87
- if (!Number.isFinite(normalized) || normalized <= 0) return fallback;
88
- return Math.floor(normalized);
89
- };
90
-
91
- export function configureProtocolV2BleTuning(tuning: ProtocolV2BleTuning = {}) {
92
- protocolV2BleTuning = {
93
- iosPacketLength: normalizePositiveInteger(
94
- tuning.iosPacketLength,
95
- protocolV2BleTuning.iosPacketLength
96
- ),
97
- androidPacketLength: normalizePositiveInteger(
98
- tuning.androidPacketLength,
99
- protocolV2BleTuning.androidPacketLength
100
- ),
101
- highVolumeWriteBurstSize: normalizePositiveInteger(
102
- tuning.highVolumeWriteBurstSize,
103
- protocolV2BleTuning.highVolumeWriteBurstSize
104
- ),
105
- highVolumeWritePauseMs: normalizePositiveInteger(
106
- tuning.highVolumeWritePauseMs,
107
- protocolV2BleTuning.highVolumeWritePauseMs
108
- ),
109
- highVolumeWriteFlushDelayMs: normalizePositiveInteger(
110
- tuning.highVolumeWriteFlushDelayMs,
111
- protocolV2BleTuning.highVolumeWriteFlushDelayMs
112
- ),
113
- highVolumeWriteWithResponse:
114
- tuning.highVolumeWriteWithResponse ?? protocolV2BleTuning.highVolumeWriteWithResponse,
115
- };
116
- Log?.debug('[ReactNativeBleTransport] Protocol V2 BLE tuning configured:', protocolV2BleTuning);
117
- }
118
-
119
- export function resetProtocolV2BleTuning() {
120
- protocolV2BleTuning = { ...DEFAULT_PROTOCOL_V2_BLE_TUNING };
121
- Log?.debug('[ReactNativeBleTransport] Protocol V2 BLE tuning reset:', protocolV2BleTuning);
122
- }
123
-
124
- export function getProtocolV2BleTuning() {
125
- return { ...protocolV2BleTuning };
126
- }
127
-
128
- function inferProtocolHintFromDeviceName(name?: string | null): ProtocolType | undefined {
129
- return /\bpro\s*2\b/i.test(name ?? '') ? 'V2' : undefined;
130
- }
131
-
132
- function getDeviceDisplayName(device?: Device | null) {
133
- return device?.name || device?.localName || null;
134
- }
135
-
136
- function isGenericBleService(uuid?: string | null) {
137
- return ['1800', '1801', '180a'].includes(getBleUuidKey(uuid));
138
- }
139
-
140
- function hasKnownOneKeyService(device?: Device | null) {
141
- return (device?.serviceUUIDs ?? []).some(serviceUuid =>
142
- getInfosForServiceUuid(serviceUuid, 'classic')
143
- );
144
- }
145
41
 
146
42
  let connectOptions: Record<string, unknown> = {
147
43
  requestMTU: 256,
@@ -153,8 +49,7 @@ export type IOneKeyDevice = OneKeyDeviceInfoBase & Device;
153
49
 
154
50
  const tryToGetConfiguration = (device: Device) => {
155
51
  if (!device || !device.serviceUUIDs) return null;
156
- const serviceUUID = device.serviceUUIDs.find(uuid => getInfosForServiceUuid(uuid, 'classic'));
157
- if (!serviceUUID) return null;
52
+ const [serviceUUID] = device.serviceUUIDs;
158
53
  const infos = getInfosForServiceUuid(serviceUUID, 'classic');
159
54
  if (!infos) return null;
160
55
  return infos;
@@ -197,45 +92,23 @@ export default class ReactNativeBleTransport {
197
92
 
198
93
  _messages: ReturnType<typeof transport.parseConfigure> | undefined;
199
94
 
200
- _messagesV2: ReturnType<typeof transport.parseConfigure> | undefined;
201
-
202
95
  name = 'ReactNativeBleTransport';
203
96
 
204
97
  configured = false;
205
98
 
206
99
  stopped = false;
207
100
 
208
- scanTimeout = DEVICE_SCAN_TIMEOUT_MS;
101
+ scanTimeout = 3000;
209
102
 
210
103
  runPromise: Deferred<any> | null = null;
211
104
 
212
105
  emitter?: EventEmitter;
213
106
 
214
- /** Per-device protocol type detected by active wire-level probe after connect. */
215
- private deviceProtocol: Map<string, ProtocolType> = new Map();
216
-
217
- private deviceProtocolHints: Map<string, ProtocolType> = new Map();
218
-
219
- private protocolV2Assemblers: Map<string, ProtocolV2FrameAssembler> = new Map();
220
-
221
- private protocolV2FrameQueues: Map<string, Uint8Array[]> = new Map();
222
-
223
- private protocolV2FramePromises: Map<string, Deferred<Uint8Array>> = new Map();
224
-
225
- private activeProtocolV2Call: { uuid: string; token: number } | null = null;
226
-
227
- private nextProtocolV2CallToken = 1;
228
-
229
- private monitorTokens: Map<string, number> = new Map();
230
-
231
- private nextMonitorToken = 1;
232
-
233
107
  constructor(options: TransportOptions) {
234
- this.scanTimeout = options.scanTimeout ?? DEVICE_SCAN_TIMEOUT_MS;
108
+ this.scanTimeout = options.scanTimeout ?? 3000;
235
109
  }
236
110
 
237
- init(logger: any, emitter: EventEmitter) {
238
- setBleLogger(logger);
111
+ init(_logger: any, emitter: EventEmitter) {
239
112
  this.emitter = emitter;
240
113
  }
241
114
 
@@ -245,11 +118,6 @@ export default class ReactNativeBleTransport {
245
118
  this._messages = messages;
246
119
  }
247
120
 
248
- configureProtocolV2(signedData: any) {
249
- this._messagesV2 = parseConfigure(signedData);
250
- Log?.debug('[ReactNativeBleTransport] Protocol V2 schema configured');
251
- }
252
-
253
121
  listen() {
254
122
  // empty
255
123
  }
@@ -299,7 +167,6 @@ export default class ReactNativeBleTransport {
299
167
  blePlxManager.startDeviceScan(
300
168
  null,
301
169
  {
302
- allowDuplicates: true,
303
170
  scanMode: ScanMode.LowLatency,
304
171
  },
305
172
  (error, device) => {
@@ -327,41 +194,14 @@ export default class ReactNativeBleTransport {
327
194
  return;
328
195
  }
329
196
 
330
- const displayName = getDeviceDisplayName(device);
331
- const isOneKey =
332
- isOnekeyDevice(device?.name ?? null, device?.id) ||
333
- isOnekeyDevice(device?.localName ?? null, device?.id) ||
334
- hasKnownOneKeyService(device);
335
- const shouldTraceCandidate =
336
- !!displayName && /onekey|bixinkey|pro\s*2|pro\b|touch|^k\d|^t\d/i.test(displayName);
337
-
338
- if (shouldTraceCandidate) {
339
- Log?.debug('[ReactNativeBleTransport] scan candidate', {
340
- name: device?.name,
341
- localName: device?.localName,
342
- id: device?.id,
343
- serviceUUIDs: device?.serviceUUIDs,
344
- accepted: isOneKey,
345
- });
346
- }
347
-
348
- if (isOneKey) {
197
+ if (isOnekeyDevice(device?.name ?? null, device?.id)) {
349
198
  Log?.debug('search device start ======================');
350
- const { name, localName, id, serviceUUIDs } = device ?? {};
199
+ const { name, localName, id } = device ?? {};
351
200
  Log?.debug(
352
- `device name: ${name ?? ''}\nlocalName: ${localName ?? ''}\nid: ${
353
- id ?? ''
354
- }\nserviceUUIDs: ${(serviceUUIDs ?? []).join(',')}`
201
+ `device name: ${name ?? ''}\nlocalName: ${localName ?? ''}\nid: ${id ?? ''}`
355
202
  );
356
203
  addDevice(device as unknown as Device);
357
204
  Log?.debug('search device end ======================\n');
358
- } else if (displayName && /\bpro\s*2\b/i.test(displayName)) {
359
- Log?.debug('[ReactNativeBleTransport] Pro2-like BLE device was not accepted:', {
360
- name: device?.name,
361
- localName: device?.localName,
362
- id: device?.id,
363
- serviceUUIDs: device?.serviceUUIDs,
364
- });
365
205
  }
366
206
  }
367
207
  );
@@ -375,16 +215,7 @@ export default class ReactNativeBleTransport {
375
215
 
376
216
  const addDevice = (device: Device) => {
377
217
  if (deviceList.every(d => d.id !== device.id)) {
378
- const displayName = getDeviceDisplayName(device) ?? 'Unknown BLE Device';
379
- const protocolHint = inferProtocolHintFromDeviceName(displayName);
380
- if (protocolHint) {
381
- this.deviceProtocolHints.set(device.id, protocolHint);
382
- }
383
- deviceList.push({
384
- ...device,
385
- name: displayName,
386
- commType: 'ble',
387
- } as IOneKeyDevice);
218
+ deviceList.push({ ...device, commType: 'ble' } as IOneKeyDevice);
388
219
  }
389
220
  };
390
221
 
@@ -396,7 +227,7 @@ export default class ReactNativeBleTransport {
396
227
  }
397
228
 
398
229
  async acquire(input: BleAcquireInput) {
399
- const { uuid, forceCleanRunPromise, expectedProtocol } = input;
230
+ const { uuid, forceCleanRunPromise } = input;
400
231
 
401
232
  if (!uuid) {
402
233
  throw ERRORS.TypedError(HardwareErrorCode.BleRequiredUUID);
@@ -414,11 +245,7 @@ export default class ReactNativeBleTransport {
414
245
  }
415
246
 
416
247
  if (forceCleanRunPromise && this.runPromise) {
417
- const error = ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise);
418
- this.runPromise.reject(error);
419
- this.rejectAllProtocolV2Frames(error);
420
- this.runPromise = null;
421
- this.activeProtocolV2Call = null;
248
+ this.runPromise.reject(ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise));
422
249
  Log?.debug('Force clean Bluetooth run promise, forceCleanRunPromise: ', forceCleanRunPromise);
423
250
  }
424
251
 
@@ -480,7 +307,7 @@ export default class ReactNativeBleTransport {
480
307
  Log?.debug('not connected, try to connect to device: ', uuid);
481
308
 
482
309
  try {
483
- device = await device.connect(connectOptions);
310
+ await device.connect(connectOptions);
484
311
  } catch (e) {
485
312
  Log?.debug('not connected, try to connect to device has error: ', e);
486
313
  if (
@@ -490,7 +317,7 @@ export default class ReactNativeBleTransport {
490
317
  connectOptions = {};
491
318
  Log?.debug('second try to reconnect without params');
492
319
  try {
493
- device = await device.connect();
320
+ await device.connect();
494
321
  } catch (e) {
495
322
  Log?.debug('last try to reconnect error: ', e);
496
323
  // last try to reconnect device if this issue exists
@@ -498,7 +325,7 @@ export default class ReactNativeBleTransport {
498
325
  if (e.errorCode === BleErrorCode.OperationCancelled) {
499
326
  Log?.debug('last try to reconnect');
500
327
  await device.cancelConnection();
501
- device = await device.connect();
328
+ await device.connect();
502
329
  }
503
330
  }
504
331
  } else {
@@ -510,7 +337,6 @@ export default class ReactNativeBleTransport {
510
337
  await device.discoverAllServicesAndCharacteristics();
511
338
  let infos = tryToGetConfiguration(device);
512
339
  let characteristics;
513
- let fallbackServiceUuid: string | undefined;
514
340
 
515
341
  if (!infos) {
516
342
  for (const serviceUuid of getBluetoothServiceUuids()) {
@@ -525,45 +351,17 @@ export default class ReactNativeBleTransport {
525
351
  }
526
352
 
527
353
  if (!infos) {
528
- const services = await device.services();
529
- Log?.debug(
530
- '[ReactNativeBleTransport] Known OneKey service UUID not found, discovered services:',
531
- services?.map(service => service.uuid)
532
- );
533
-
534
- const knownService = services.find(service =>
535
- getInfosForServiceUuid(service.uuid, 'classic')
536
- );
537
- const fallbackService =
538
- knownService ?? services.find(service => !isGenericBleService(service.uuid)) ?? services[0];
539
-
540
- if (fallbackService) {
541
- fallbackServiceUuid = fallbackService.uuid;
542
- characteristics = await device.characteristicsForService(fallbackService.uuid);
543
- Log?.debug('[ReactNativeBleTransport] Using fallback BLE service:', fallbackService.uuid);
544
- }
545
- }
546
-
547
- if (!infos) {
548
- if (!fallbackServiceUuid) {
549
- try {
550
- Log?.debug('cancel connection when service not found');
551
- await device.cancelConnection();
552
- } catch (e) {
553
- Log?.debug('cancel connection error when service not found: ', e.message || e.reason);
554
- }
555
- throw ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound);
354
+ try {
355
+ Log?.debug('cancel connection when service not found');
356
+ await device.cancelConnection();
357
+ } catch (e) {
358
+ Log?.debug('cancel connection error when service not found: ', e.message || e.reason);
556
359
  }
557
- }
558
-
559
- const serviceUuid = infos?.serviceUuid ?? fallbackServiceUuid;
560
- const writeUuid = infos?.writeUuid ?? '00000002-0000-1000-8000-00805f9b34fb';
561
- const notifyUuid = infos?.notifyUuid ?? '00000003-0000-1000-8000-00805f9b34fb';
562
-
563
- if (!serviceUuid) {
564
360
  throw ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound);
565
361
  }
566
362
 
363
+ const { serviceUuid, writeUuid, notifyUuid } = infos;
364
+
567
365
  if (!characteristics) {
568
366
  characteristics = await device.characteristicsForService(serviceUuid);
569
367
  }
@@ -575,9 +373,9 @@ export default class ReactNativeBleTransport {
575
373
  let writeCharacteristic;
576
374
  let notifyCharacteristic;
577
375
  for (const c of characteristics) {
578
- if (isSameBleUuid(c.uuid, writeUuid)) {
376
+ if (c.uuid === writeUuid) {
579
377
  writeCharacteristic = c;
580
- } else if (isSameBleUuid(c.uuid, notifyUuid)) {
378
+ } else if (c.uuid === notifyUuid) {
581
379
  notifyCharacteristic = c;
582
380
  }
583
381
  }
@@ -600,42 +398,16 @@ export default class ReactNativeBleTransport {
600
398
  );
601
399
  }
602
400
 
603
- const protocolHint = expectedProtocol
604
- ? undefined
605
- : this.deviceProtocolHints.get(uuid) ??
606
- inferProtocolHintFromDeviceName(getDeviceDisplayName(device));
607
-
608
401
  // release transport before new transport instance
609
402
  await this.release(uuid);
610
- if (protocolHint) {
611
- this.deviceProtocolHints.set(uuid, protocolHint);
612
- }
613
403
 
614
404
  const transport = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
615
- const monitorToken = this.nextMonitorToken;
616
- this.nextMonitorToken += 1;
617
- const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
618
- transport.monitorToken = monitorToken;
619
- transport.notifyTransactionId = notifyTransactionId;
620
- this.monitorTokens.set(uuid, monitorToken);
621
405
  transport.notifySubscription = this._monitorCharacteristic(
622
406
  transport.notifyCharacteristic,
623
- uuid,
624
- monitorToken,
625
- notifyTransactionId
407
+ uuid
626
408
  );
627
409
  transportCache[uuid] = transport;
628
410
 
629
- this.protocolV2Assemblers.set(uuid, new ProtocolV2FrameAssembler());
630
-
631
- if (Platform.OS === 'ios') {
632
- await new Promise<void>(resolve => {
633
- setTimeout(resolve, IOS_NOTIFY_READY_DELAY_MS);
634
- });
635
- }
636
-
637
- const protocolType = await this.detectProtocol(uuid, expectedProtocol, protocolHint);
638
-
639
411
  this.emitter?.emit('device-connect', {
640
412
  name: device.name,
641
413
  id: device.id,
@@ -643,11 +415,6 @@ export default class ReactNativeBleTransport {
643
415
  });
644
416
 
645
417
  transport.disconnectSubscription = device.onDisconnected(() => {
646
- if (transportCache[uuid] !== transport) {
647
- Log?.debug('device disconnect ignored for stale transport: ', device?.id);
648
- return;
649
- }
650
-
651
418
  try {
652
419
  Log?.debug('device disconnect: ', device?.id);
653
420
  this.emitter?.emit('device-disconnect', {
@@ -656,9 +423,7 @@ export default class ReactNativeBleTransport {
656
423
  connectId: device?.id,
657
424
  });
658
425
  if (this.runPromise) {
659
- const error = ERRORS.TypedError(HardwareErrorCode.BleConnectedError);
660
- this.runPromise.reject(error);
661
- this.rejectAllProtocolV2Frames(error);
426
+ this.runPromise.reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError));
662
427
  }
663
428
  } catch (e) {
664
429
  Log?.debug('device disconnect error: ', e);
@@ -667,29 +432,19 @@ export default class ReactNativeBleTransport {
667
432
  }
668
433
  });
669
434
 
670
- return { uuid, protocolType };
435
+ return { uuid };
671
436
  }
672
437
 
673
- _monitorCharacteristic(
674
- characteristic: Characteristic,
675
- uuid: string,
676
- monitorToken: number,
677
- notifyTransactionId: string
678
- ): Subscription {
438
+ _monitorCharacteristic(characteristic: Characteristic, uuid: string): Subscription {
679
439
  let bufferLength = 0;
680
440
  let buffer: any[] = [];
681
441
  const subscription = characteristic.monitor((error, c) => {
682
- const isCurrentMonitor = this.monitorTokens.get(uuid) === monitorToken;
683
442
  if (error) {
684
443
  Log?.debug(
685
444
  `error monitor ${characteristic.uuid}, deviceId: ${characteristic.deviceID}: ${
686
445
  error as unknown as string
687
446
  }`
688
447
  );
689
- if (!isCurrentMonitor) {
690
- Log?.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
691
- return;
692
- }
693
448
  if (this.runPromise) {
694
449
  let ERROR:
695
450
  | typeof HardwareErrorCode.BleDeviceBondError
@@ -709,40 +464,27 @@ export default class ReactNativeBleTransport {
709
464
  error.reason?.includes('Writing is not permitted') || // pro firmware 2.3.4 upgrade
710
465
  error.reason?.includes('notify change failed for device')
711
466
  ) {
712
- const notifyError = ERRORS.TypedError(
713
- HardwareErrorCode.BleCharacteristicNotifyChangeFailure
467
+ this.runPromise.reject(
468
+ ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotifyChangeFailure)
714
469
  );
715
- this.runPromise.reject(notifyError);
716
- this.rejectAllProtocolV2Frames(notifyError);
717
470
  Log?.debug(
718
471
  `${HardwareErrorCode.BleCharacteristicNotifyChangeFailure} ${error.message} ${error.reason}`
719
472
  );
720
473
  return;
721
474
  }
722
- const notifyError = ERRORS.TypedError(ERROR);
723
- this.runPromise.reject(notifyError);
724
- this.rejectAllProtocolV2Frames(notifyError);
475
+ this.runPromise.reject(ERRORS.TypedError(ERROR));
725
476
  Log?.debug(': monitor notify error, and has unreleased Promise', Error);
726
477
  }
727
478
 
728
479
  return;
729
480
  }
730
481
 
731
- if (!isCurrentMonitor) {
732
- Log?.debug('monitor data ignored for stale transport: ', uuid, notifyTransactionId);
733
- return;
734
- }
735
-
736
482
  if (!c) {
737
483
  throw ERRORS.TypedError(HardwareErrorCode.BleMonitorError);
738
484
  }
739
485
 
740
486
  try {
741
487
  const data = Buffer.from(c.value as string, 'base64');
742
- if (this.deviceProtocol.get(uuid) === 'V2') {
743
- this.handleProtocolV2Notification(uuid, new Uint8Array(data));
744
- return;
745
- }
746
488
  // console.log('[hd-transport-react-native] Received a packet, ', 'buffer: ', data);
747
489
  if (isHeaderChunk(data)) {
748
490
  bufferLength = data.readInt32BE(5);
@@ -751,7 +493,7 @@ export default class ReactNativeBleTransport {
751
493
  buffer = buffer.concat([...data]);
752
494
  }
753
495
 
754
- if (buffer.length - PROTOCOL_V1_MESSAGE_HEADER_SIZE >= bufferLength) {
496
+ if (buffer.length - COMMON_HEADER_SIZE >= bufferLength) {
755
497
  const value = Buffer.from(buffer);
756
498
  // console.log(
757
499
  // '[hd-transport-react-native] Received a complete packet of data, resolve Promise, this.runPromise: ',
@@ -765,32 +507,17 @@ export default class ReactNativeBleTransport {
765
507
  }
766
508
  } catch (error) {
767
509
  Log?.debug('monitor data error: ', error);
768
- const notifyError = ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError);
769
- this.runPromise?.reject(notifyError);
770
- this.rejectAllProtocolV2Frames(notifyError);
510
+ this.runPromise?.reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError));
771
511
  }
772
- }, notifyTransactionId);
512
+ }, uuid);
773
513
 
774
514
  return subscription;
775
515
  }
776
516
 
777
517
  async release(uuid: string) {
778
518
  const transport = transportCache[uuid];
779
- if (this.runPromise) {
780
- const error = ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise);
781
- this.runPromise.reject(error);
782
- this.runPromise = null;
783
- this.rejectAllProtocolV2Frames(error);
784
- this.activeProtocolV2Call = null;
785
- } else {
786
- this.resetProtocolV2Frames(uuid);
787
- }
788
519
 
789
520
  if (transport) {
790
- if (this.monitorTokens.get(uuid) === transport.monitorToken) {
791
- this.monitorTokens.delete(uuid);
792
- }
793
-
794
521
  // Clean up disconnect subscription first to prevent callbacks on released transport
795
522
  Log?.debug('release: removing disconnect subscription for device: ', uuid);
796
523
  transport.disconnectSubscription?.remove();
@@ -804,27 +531,12 @@ export default class ReactNativeBleTransport {
804
531
  transport.notifySubscription?.remove();
805
532
  transport.notifySubscription = undefined;
806
533
 
807
- if (transport.notifyTransactionId) {
808
- try {
809
- await this.blePlxManager?.cancelTransaction(transport.notifyTransactionId);
810
- } catch (e) {
811
- Log?.debug('release: cancel notify transaction error (ignored): ', e?.message || e);
812
- }
813
- }
814
-
815
534
  delete transportCache[uuid];
816
- }
817
535
 
818
- this.deviceProtocol.delete(uuid);
819
- this.deviceProtocolHints.delete(uuid);
820
- this.protocolV2Assemblers.get(uuid)?.reset();
821
- this.protocolV2Assemblers.delete(uuid);
822
- this.resetProtocolV2Frames(uuid);
823
-
824
- try {
825
- await this.blePlxManager?.cancelTransaction(uuid);
826
- } catch (e) {
827
- Log?.debug('release: cancel transaction error (ignored): ', e?.message || e);
536
+ // Temporary close the Android disconnect after each request
537
+ if (Platform.OS === 'android') {
538
+ // await this.blePlxManager?.cancelDeviceConnection(uuid);
539
+ }
828
540
  }
829
541
 
830
542
  return Promise.resolve(true);
@@ -834,12 +546,7 @@ export default class ReactNativeBleTransport {
834
546
  await this.call(session, name, data);
835
547
  }
836
548
 
837
- async call(
838
- uuid: string,
839
- name: string,
840
- data: Record<string, unknown>,
841
- options?: TransportCallOptions
842
- ) {
549
+ async call(uuid: string, name: string, data: Record<string, unknown>) {
843
550
  if (this.stopped) {
844
551
  // eslint-disable-next-line prefer-promise-reject-errors
845
552
  return Promise.reject(ERRORS.TypedError('Transport stopped.'));
@@ -855,7 +562,13 @@ export default class ReactNativeBleTransport {
855
562
  throw ERRORS.TypedError(HardwareErrorCode.TransportCallInProgress);
856
563
  }
857
564
 
858
- const protocol = this.getProtocolType(uuid);
565
+ const transport = transportCache[uuid];
566
+ if (!transport) {
567
+ throw ERRORS.TypedError(HardwareErrorCode.TransportNotFound);
568
+ }
569
+
570
+ this.runPromise = createDeferred();
571
+ const messages = this._messages;
859
572
  // Upload resources on low-end phones may OOM
860
573
  if (name === 'ResourceUpdate' || name === 'ResourceAck') {
861
574
  Log?.debug('transport-react-native', 'call-', ' name: ', name, ' data: ', {
@@ -863,44 +576,12 @@ export default class ReactNativeBleTransport {
863
576
  hash: data?.hash,
864
577
  });
865
578
  } else if (LogBlockCommand.has(name)) {
866
- Log?.debug('transport-react-native', 'call-', ' name: ', name, ' protocol: ', protocol);
579
+ Log?.debug('transport-react-native', 'call-', ' name: ', name);
867
580
  } else {
868
- Log?.debug(
869
- 'transport-react-native',
870
- 'call-',
871
- ' name: ',
872
- name,
873
- ' data: ',
874
- data,
875
- ' protocol: ',
876
- protocol
877
- );
878
- }
879
-
880
- if (protocol === 'V2') {
881
- return this.callProtocolV2(uuid, name, data, options);
882
- }
883
-
884
- return this.callProtocolV1(uuid, name, data, options);
885
- }
886
-
887
- private async callProtocolV1(
888
- uuid: string,
889
- name: string,
890
- data: Record<string, unknown>,
891
- options?: TransportCallOptions
892
- ) {
893
- if (!this._messages) {
894
- throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
581
+ Log?.debug('transport-react-native', 'call-', ' name: ', name, ' data: ', data);
895
582
  }
896
583
 
897
- const transport = this.getCachedTransport(uuid);
898
- const runPromise = createDeferred<string>();
899
- runPromise.promise.catch(() => undefined);
900
- this.runPromise = runPromise;
901
- const messages = this._messages;
902
- const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
903
- let timeout: ReturnType<typeof setTimeout> | undefined;
584
+ const buffers = buildBuffers(messages, name, data);
904
585
 
905
586
  async function writeChunkedData(
906
587
  buffers: ByteBuffer[],
@@ -971,41 +652,20 @@ export default class ReactNativeBleTransport {
971
652
  }
972
653
 
973
654
  try {
974
- const response = await Promise.race([
975
- runPromise.promise,
976
- new Promise<never>((_, reject) => {
977
- if (options?.timeoutMs) {
978
- timeout = setTimeout(() => {
979
- const error = ERRORS.TypedError(
980
- HardwareErrorCode.BleTimeoutError,
981
- `BLE response timeout after ${options.timeoutMs}ms for ${name}`
982
- );
983
- runPromise.reject(error);
984
- reject(error);
985
- }, options.timeoutMs);
986
- }
987
- }),
988
- ]);
655
+ const response = await this.runPromise.promise;
989
656
 
990
657
  if (typeof response !== 'string') {
991
658
  throw new Error('Returning data is not string.');
992
659
  }
993
660
 
994
661
  Log?.debug('receive data: ', response);
995
- const jsonData = ProtocolV1.decodeMessage(messages, response);
662
+ const jsonData = receiveOne(messages, response);
996
663
  return check.call(jsonData);
997
664
  } catch (e) {
998
- if (name === 'Initialize' && options?.timeoutMs === PROTOCOL_PROBE_TIMEOUT_MS) {
999
- Log?.debug('[ReactNativeBleTransport] Protocol V1 Initialize probe call failed:', e);
1000
- } else {
1001
- Log?.error('call error: ', e);
1002
- }
665
+ Log?.error('call error: ', e);
1003
666
  throw e;
1004
667
  } finally {
1005
- if (timeout) clearTimeout(timeout);
1006
- if (this.runPromise === runPromise) {
1007
- this.runPromise = null;
1008
- }
668
+ this.runPromise = null;
1009
669
  }
1010
670
  }
1011
671
 
@@ -1072,13 +732,6 @@ export default class ReactNativeBleTransport {
1072
732
  if (transportCache[session]) {
1073
733
  delete transportCache[session];
1074
734
  }
1075
- this.deviceProtocol.delete(session);
1076
- this.deviceProtocolHints.delete(session);
1077
- this.protocolV2Assemblers.delete(session);
1078
- this.resetProtocolV2Frames(session);
1079
- if (this.activeProtocolV2Call?.uuid === session) {
1080
- this.activeProtocolV2Call = null;
1081
- }
1082
735
 
1083
736
  // emit the disconnect event
1084
737
  try {
@@ -1101,390 +754,4 @@ export default class ReactNativeBleTransport {
1101
754
  }
1102
755
  this.runPromise = null;
1103
756
  }
1104
-
1105
- private getCachedTransport(uuid: string) {
1106
- const transport = transportCache[uuid];
1107
- if (!transport) {
1108
- throw ERRORS.TypedError(HardwareErrorCode.TransportNotFound);
1109
- }
1110
- return transport;
1111
- }
1112
-
1113
- private createProtocolMismatchError(expected: ProtocolType) {
1114
- return ERRORS.TypedError(
1115
- HardwareErrorCode.RuntimeError,
1116
- `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`
1117
- );
1118
- }
1119
-
1120
- private async detectProtocol(
1121
- uuid: string,
1122
- expectedProtocol?: ProtocolType,
1123
- protocolHint?: ProtocolType
1124
- ): Promise<ProtocolType> {
1125
- if (expectedProtocol === 'V1') {
1126
- if (await this.probeProtocolV1(uuid)) {
1127
- this.deviceProtocol.set(uuid, 'V1');
1128
- Log?.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> V1 (expected)`);
1129
- return 'V1';
1130
- }
1131
- throw this.createProtocolMismatchError(expectedProtocol);
1132
- }
1133
-
1134
- if (expectedProtocol === 'V2') {
1135
- if (await this.probeProtocolV2(uuid)) {
1136
- this.deviceProtocol.set(uuid, 'V2');
1137
- Log?.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> V2 (expected)`);
1138
- return 'V2';
1139
- }
1140
- throw this.createProtocolMismatchError(expectedProtocol);
1141
- }
1142
-
1143
- if (protocolHint === 'V2' && (await this.probeProtocolV2(uuid))) {
1144
- this.deviceProtocol.set(uuid, 'V2');
1145
- Log?.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> V2 (hint)`);
1146
- return 'V2';
1147
- }
1148
-
1149
- if (this.deviceProtocol.get(uuid) === 'V2' && (await this.probeProtocolV2(uuid))) {
1150
- this.deviceProtocol.set(uuid, 'V2');
1151
- Log?.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> V2 (cached)`);
1152
- return 'V2';
1153
- }
1154
-
1155
- let protocol: ProtocolType = 'V1';
1156
- const protocolV1Detected = await this.probeProtocolV1(uuid);
1157
- if (!protocolV1Detected) {
1158
- await this.resetProbeStateAfterProtocolProbe(uuid, 'V1');
1159
- if (await this.probeProtocolV2(uuid)) {
1160
- protocol = 'V2';
1161
- }
1162
- }
1163
- this.deviceProtocol.set(uuid, protocol);
1164
- Log?.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> ${protocol}`);
1165
- return protocol;
1166
- }
1167
-
1168
- private async resetProbeStateAfterProtocolProbe(uuid: string, protocol: ProtocolType) {
1169
- const transport = transportCache[uuid];
1170
- this.protocolV2Assemblers.get(uuid)?.reset();
1171
- this.resetProtocolV2Frames(uuid);
1172
- if (this.activeProtocolV2Call?.uuid === uuid) {
1173
- this.activeProtocolV2Call = null;
1174
- }
1175
- if (this.runPromise) {
1176
- const error = ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise);
1177
- this.runPromise.reject(error);
1178
- this.runPromise = null;
1179
- }
1180
-
1181
- if (!transport) return;
1182
-
1183
- const previousNotifyTransactionId = transport.notifyTransactionId;
1184
- if (this.monitorTokens.get(uuid) === transport.monitorToken) {
1185
- this.monitorTokens.delete(uuid);
1186
- }
1187
- transport.notifySubscription?.remove();
1188
- transport.notifySubscription = undefined;
1189
- if (previousNotifyTransactionId) {
1190
- try {
1191
- await this.blePlxManager?.cancelTransaction(previousNotifyTransactionId);
1192
- } catch (error) {
1193
- Log?.debug(
1194
- `[ReactNativeBleTransport] cancel notify after Protocol ${protocol} probe failed:`,
1195
- error?.message || error
1196
- );
1197
- }
1198
- }
1199
-
1200
- const monitorToken = this.nextMonitorToken;
1201
- this.nextMonitorToken += 1;
1202
- const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
1203
- transport.monitorToken = monitorToken;
1204
- transport.notifyTransactionId = notifyTransactionId;
1205
- this.monitorTokens.set(uuid, monitorToken);
1206
- transport.notifySubscription = this._monitorCharacteristic(
1207
- transport.notifyCharacteristic,
1208
- uuid,
1209
- monitorToken,
1210
- notifyTransactionId
1211
- );
1212
- if (Platform.OS === 'ios') {
1213
- await new Promise<void>(resolve => {
1214
- setTimeout(resolve, IOS_NOTIFY_READY_DELAY_MS);
1215
- });
1216
- }
1217
- }
1218
-
1219
- private async probeProtocolV1(uuid: string) {
1220
- if (!this._messages) {
1221
- return false;
1222
- }
1223
-
1224
- try {
1225
- this.deviceProtocol.set(uuid, 'V1');
1226
- await this.callProtocolV1(uuid, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
1227
- return true;
1228
- } catch (error) {
1229
- Log?.debug('[ReactNativeBleTransport] Protocol V1 Initialize probe failed:', error);
1230
- return false;
1231
- }
1232
- }
1233
-
1234
- private async probeProtocolV2(uuid: string) {
1235
- if (!this._messages || !this._messagesV2) {
1236
- return false;
1237
- }
1238
-
1239
- this.deviceProtocol.set(uuid, 'V2');
1240
- this.protocolV2Assemblers.get(uuid)?.reset();
1241
- return probeProtocolV2Helper({
1242
- call: (name: string, data: Record<string, unknown>, options?: TransportCallOptions) =>
1243
- this.callProtocolV2(uuid, name, data, options),
1244
- timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT_MS,
1245
- logger: Log,
1246
- logPrefix: 'ProtocolV2 RN-BLE',
1247
- onProbeFailed: () => {
1248
- this.protocolV2Assemblers.get(uuid)?.reset();
1249
- this.resetProtocolV2Frames(uuid);
1250
- },
1251
- });
1252
- }
1253
-
1254
- private handleProtocolV2Notification(uuid: string, data: Uint8Array) {
1255
- try {
1256
- if (!this.runPromise || this.activeProtocolV2Call?.uuid !== uuid) {
1257
- this.protocolV2Assemblers.get(uuid)?.reset();
1258
- this.resetProtocolV2Frames(uuid);
1259
- return;
1260
- }
1261
-
1262
- if (data.length === 0) return;
1263
-
1264
- const assembler = this.protocolV2Assemblers.get(uuid);
1265
- if (!assembler) return;
1266
-
1267
- let frameData = assembler.push(data);
1268
- while (frameData) {
1269
- this.resolveProtocolV2Frame(uuid, frameData);
1270
- frameData = assembler.push(new Uint8Array(0));
1271
- }
1272
- } catch (error) {
1273
- Log?.debug('[ReactNativeBleTransport] Protocol V2 notification error:', error);
1274
- const notifyError = ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError);
1275
- this.runPromise?.reject(notifyError);
1276
- this.rejectAllProtocolV2Frames(notifyError);
1277
- }
1278
- }
1279
-
1280
- private getProtocolV2FrameQueue(uuid: string) {
1281
- let queue = this.protocolV2FrameQueues.get(uuid);
1282
- if (!queue) {
1283
- queue = [];
1284
- this.protocolV2FrameQueues.set(uuid, queue);
1285
- }
1286
- return queue;
1287
- }
1288
-
1289
- private resolveProtocolV2Frame(uuid: string, frame: Uint8Array) {
1290
- const framePromise = this.protocolV2FramePromises.get(uuid);
1291
- if (framePromise) {
1292
- framePromise.resolve(frame);
1293
- this.protocolV2FramePromises.delete(uuid);
1294
- return;
1295
- }
1296
- this.getProtocolV2FrameQueue(uuid).push(frame);
1297
- }
1298
-
1299
- private rejectAllProtocolV2Frames(error: Error) {
1300
- this.protocolV2FrameQueues.clear();
1301
- for (const framePromise of this.protocolV2FramePromises.values()) {
1302
- framePromise.reject(error);
1303
- }
1304
- this.protocolV2FramePromises.clear();
1305
- }
1306
-
1307
- private resetProtocolV2Frames(uuid: string) {
1308
- this.protocolV2FrameQueues.delete(uuid);
1309
- this.protocolV2FramePromises.delete(uuid);
1310
- }
1311
-
1312
- private isActiveProtocolV2Call(uuid: string, token: number) {
1313
- return this.activeProtocolV2Call?.uuid === uuid && this.activeProtocolV2Call.token === token;
1314
- }
1315
-
1316
- private async readProtocolV2Frame(uuid: string) {
1317
- const queuedFrame = this.getProtocolV2FrameQueue(uuid).shift();
1318
- if (queuedFrame) {
1319
- return queuedFrame;
1320
- }
1321
-
1322
- const framePromise = createDeferred<Uint8Array>();
1323
- this.protocolV2FramePromises.set(uuid, framePromise);
1324
- try {
1325
- return await framePromise.promise;
1326
- } finally {
1327
- if (this.protocolV2FramePromises.get(uuid) === framePromise) {
1328
- this.protocolV2FramePromises.delete(uuid);
1329
- }
1330
- }
1331
- }
1332
-
1333
- private async writeProtocolV2Frame(
1334
- transport: BleTransport,
1335
- frame: Uint8Array,
1336
- options?: { highVolume?: boolean; writeWithResponse?: boolean }
1337
- ) {
1338
- const tuning = getProtocolV2BleTuning();
1339
- const packetCapacity =
1340
- Platform.OS === 'ios' ? tuning.iosPacketLength : tuning.androidPacketLength;
1341
- const writeWithResponse =
1342
- !!options?.writeWithResponse || (!!options?.highVolume && tuning.highVolumeWriteWithResponse);
1343
- const shouldThrottle = !!options?.highVolume && !writeWithResponse;
1344
- let packetsWritten = 0;
1345
-
1346
- try {
1347
- for (let offset = 0; offset < frame.length; offset += packetCapacity) {
1348
- const chunk = frame.slice(offset, offset + packetCapacity);
1349
- const base64 = Buffer.from(chunk).toString('base64');
1350
- if (writeWithResponse) {
1351
- await transport.writeCharacteristic.writeWithResponse(base64);
1352
- } else {
1353
- await transport.writeCharacteristic.writeWithoutResponse(base64);
1354
- }
1355
- packetsWritten += 1;
1356
-
1357
- if (
1358
- shouldThrottle &&
1359
- packetsWritten % tuning.highVolumeWriteBurstSize === 0 &&
1360
- offset + packetCapacity < frame.length
1361
- ) {
1362
- await delay(tuning.highVolumeWritePauseMs);
1363
- }
1364
- }
1365
-
1366
- if (shouldThrottle) {
1367
- await delay(tuning.highVolumeWriteFlushDelayMs);
1368
- }
1369
- } catch (error) {
1370
- if (options?.highVolume && !writeWithResponse && packetsWritten === 0) {
1371
- Log?.debug(
1372
- '[ReactNativeBleTransport] Protocol V2 high-volume writeWithoutResponse failed before data was sent, fallback to writeWithResponse:',
1373
- error
1374
- );
1375
- await this.writeProtocolV2Frame(transport, frame, {
1376
- highVolume: true,
1377
- writeWithResponse: true,
1378
- });
1379
- return;
1380
- }
1381
- throw error;
1382
- }
1383
- }
1384
-
1385
- private async callProtocolV2(
1386
- uuid: string,
1387
- name: string,
1388
- data: Record<string, unknown>,
1389
- options?: TransportCallOptions
1390
- ) {
1391
- if (!this._messages || !this._messagesV2) {
1392
- throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
1393
- }
1394
-
1395
- const forceRun = name === 'Initialize' || name === 'Cancel' || name === 'GetProtoVersion';
1396
- if (this.runPromise) {
1397
- if (!forceRun) {
1398
- throw ERRORS.TypedError(HardwareErrorCode.TransportCallInProgress);
1399
- }
1400
- const error = ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise);
1401
- this.runPromise.reject(error);
1402
- this.rejectAllProtocolV2Frames(error);
1403
- this.runPromise = null;
1404
- this.activeProtocolV2Call = null;
1405
- }
1406
-
1407
- const transport = this.getCachedTransport(uuid);
1408
- const runPromise = createDeferred<Uint8Array>();
1409
- runPromise.promise.catch(() => undefined);
1410
- this.runPromise = runPromise;
1411
- const callToken = this.nextProtocolV2CallToken++;
1412
- this.activeProtocolV2Call = { uuid, token: callToken };
1413
- this.protocolV2Assemblers.get(uuid)?.reset();
1414
- this.resetProtocolV2Frames(uuid);
1415
- let completed = false;
1416
- const callOptions = {
1417
- ...options,
1418
- timeoutMs: options?.timeoutMs ?? BLE_RESPONSE_TIMEOUT_MS,
1419
- };
1420
- const highVolumeWrite = LogBlockCommand.has(name);
1421
-
1422
- if (highVolumeWrite) {
1423
- const tuning = getProtocolV2BleTuning();
1424
- Log?.debug(
1425
- '[ReactNativeBleTransport] Protocol V2 high-volume write uses throttled writeWithoutResponse:',
1426
- name,
1427
- {
1428
- packetCapacity:
1429
- Platform.OS === 'ios' ? tuning.iosPacketLength : tuning.androidPacketLength,
1430
- burstSize: tuning.highVolumeWriteBurstSize,
1431
- pauseMs: tuning.highVolumeWritePauseMs,
1432
- flushDelayMs: tuning.highVolumeWriteFlushDelayMs,
1433
- writeWithResponse: tuning.highVolumeWriteWithResponse,
1434
- }
1435
- );
1436
- }
1437
-
1438
- try {
1439
- const session = new ProtocolV2Session({
1440
- schemas: {
1441
- protocolV1: this._messages,
1442
- protocolV2: this._messagesV2,
1443
- },
1444
- router: PROTOCOL_V2_CHANNEL_BLE_UART,
1445
- writeFrame: (frame: Uint8Array) =>
1446
- this.writeProtocolV2Frame(transport, frame, { highVolume: highVolumeWrite }),
1447
- readFrame: async () => {
1448
- const rxFrame = await this.readProtocolV2Frame(uuid);
1449
- if (!(rxFrame instanceof Uint8Array)) {
1450
- throw new Error('Protocol V2 response is not Uint8Array');
1451
- }
1452
- return rxFrame;
1453
- },
1454
- logger: Log,
1455
- logPrefix: 'ProtocolV2 RN-BLE',
1456
- createTimeoutError: (_messageName: string, timeout: number) =>
1457
- ERRORS.TypedError(
1458
- HardwareErrorCode.BleTimeoutError,
1459
- `BLE response timeout after ${timeout}ms for ${name}`
1460
- ),
1461
- });
1462
-
1463
- const result = await session.call(name, data, callOptions);
1464
- completed = true;
1465
- return result;
1466
- } catch (e) {
1467
- if (this.isActiveProtocolV2Call(uuid, callToken)) {
1468
- this.protocolV2Assemblers.get(uuid)?.reset();
1469
- this.resetProtocolV2Frames(uuid);
1470
- }
1471
- Log?.error('[ReactNativeBleTransport] Protocol V2 call error:', e);
1472
- throw e;
1473
- } finally {
1474
- if (this.isActiveProtocolV2Call(uuid, callToken)) {
1475
- if (!completed) {
1476
- this.protocolV2Assemblers.get(uuid)?.reset();
1477
- }
1478
- this.resetProtocolV2Frames(uuid);
1479
- this.activeProtocolV2Call = null;
1480
- }
1481
- if (this.runPromise === runPromise) {
1482
- this.runPromise = null;
1483
- }
1484
- }
1485
- }
1486
-
1487
- getProtocolType(path: string): ProtocolType {
1488
- return this.deviceProtocol.get(path) ?? 'V1';
1489
- }
1490
757
  }