@onekeyfe/hd-transport-react-native 1.1.26 → 1.1.27-alpha.31

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/index.js CHANGED
@@ -1,12 +1,13 @@
1
1
  'use strict';
2
2
 
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
3
5
  var reactNative = require('react-native');
4
6
  var buffer = require('buffer');
5
7
  var reactNativeBlePlx = require('react-native-ble-plx');
6
8
  var ByteBuffer = require('bytebuffer');
7
9
  var transport = require('@onekeyfe/hd-transport');
8
10
  var hdShared = require('@onekeyfe/hd-shared');
9
- var hdCore = require('@onekeyfe/hd-core');
10
11
  var BleUtils = require('@onekeyfe/react-native-ble-utils');
11
12
 
12
13
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
@@ -45,7 +46,17 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
45
46
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
46
47
  };
47
48
 
48
- const Logger = hdCore.getLogger(hdCore.LoggerNames.HdBleTransport);
49
+ let activeLogger;
50
+ const setBleLogger = (logger) => {
51
+ activeLogger = logger;
52
+ };
53
+ const bleLogger = {
54
+ debug: (...args) => { var _a; return (_a = activeLogger === null || activeLogger === void 0 ? void 0 : activeLogger.debug) === null || _a === void 0 ? void 0 : _a.call(activeLogger, ...args); },
55
+ error: (...args) => { var _a; return (_a = activeLogger === null || activeLogger === void 0 ? void 0 : activeLogger.error) === null || _a === void 0 ? void 0 : _a.call(activeLogger, ...args); },
56
+ warn: (...args) => { var _a; return (_a = activeLogger === null || activeLogger === void 0 ? void 0 : activeLogger.warn) === null || _a === void 0 ? void 0 : _a.call(activeLogger, ...args); },
57
+ };
58
+
59
+ const Logger = bleLogger;
49
60
  const getConnectedDeviceIds = (serviceUuids) => BleUtils__default["default"].getConnectedPeripherals(serviceUuids);
50
61
  const pairDevice = (macAddress) => BleUtils__default["default"].pairDevice(macAddress);
51
62
  const onDeviceBondState = (bleMacAddress) => new Promise((resolve, reject) => {
@@ -145,30 +156,43 @@ for (const deviceType of Object.keys(OneKeyServices)) {
145
156
  }
146
157
  const getBluetoothServiceUuids = () => bluetoothServices;
147
158
  const getInfosForServiceUuid = (serviceUuid, deviceType) => {
159
+ var _a;
148
160
  const services = OneKeyServices[deviceType];
149
161
  if (!services) {
150
162
  return null;
151
163
  }
152
- const service = services[serviceUuid];
164
+ const normalizedServiceUuid = normalizeBleUuid(serviceUuid);
165
+ const service = (_a = services[serviceUuid]) !== null && _a !== void 0 ? _a : Object.values(services).find(item => normalizeBleUuid(item.serviceUuid) === normalizedServiceUuid);
153
166
  if (!service) {
154
167
  return null;
155
168
  }
156
169
  return service;
157
170
  };
171
+ const normalizeBleUuid = (uuid) => (uuid !== null && uuid !== void 0 ? uuid : '').replace(/-/g, '').toLowerCase();
172
+ const getBleUuidKey = (uuid) => {
173
+ const normalized = normalizeBleUuid(uuid);
174
+ return normalized.length >= 8 ? normalized.substring(4, 8) : normalized;
175
+ };
176
+ const isSameBleUuid = (left, right) => {
177
+ const normalizedLeft = normalizeBleUuid(left);
178
+ const normalizedRight = normalizeBleUuid(right);
179
+ return (normalizedLeft === normalizedRight ||
180
+ (getBleUuidKey(left) !== '' && getBleUuidKey(left) === getBleUuidKey(right)));
181
+ };
158
182
 
159
183
  const isHeaderChunk = (chunk) => {
160
184
  if (chunk.length < 9)
161
185
  return false;
162
186
  const [MagicQuestionMark, sharp1, sharp2] = chunk;
163
- if (String.fromCharCode(MagicQuestionMark) === String.fromCharCode(transport.MESSAGE_TOP_CHAR) &&
164
- String.fromCharCode(sharp1) === String.fromCharCode(transport.MESSAGE_HEADER_BYTE) &&
165
- String.fromCharCode(sharp2) === String.fromCharCode(transport.MESSAGE_HEADER_BYTE)) {
187
+ if (String.fromCharCode(MagicQuestionMark) === String.fromCharCode(transport.PROTOCOL_V1_REPORT_ID) &&
188
+ String.fromCharCode(sharp1) === String.fromCharCode(transport.PROTOCOL_V1_HEADER_BYTE) &&
189
+ String.fromCharCode(sharp2) === String.fromCharCode(transport.PROTOCOL_V1_HEADER_BYTE)) {
166
190
  return true;
167
191
  }
168
192
  return false;
169
193
  };
170
194
 
171
- const Log$1 = hdCore.getLogger(hdCore.LoggerNames.HdBleTransport);
195
+ const Log$1 = bleLogger;
172
196
  class BleTransport {
173
197
  constructor(device, writeCharacteristic, notifyCharacteristic) {
174
198
  this.name = 'ReactNativeBleTransport';
@@ -187,7 +211,7 @@ class BleTransport {
187
211
  catch (error) {
188
212
  Log$1 === null || Log$1 === void 0 ? void 0 : Log$1.debug(`Write retry attempt ${BleTransport.MAX_RETRIES - retryCount + 1}, error: ${error}`);
189
213
  if (retryCount > 0) {
190
- yield hdCore.wait(BleTransport.RETRY_DELAY);
214
+ yield hdShared.wait(BleTransport.RETRY_DELAY);
191
215
  if (error.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected ||
192
216
  error.errorCode === reactNativeBlePlx.BleErrorCode.CharacteristicNotFound) {
193
217
  try {
@@ -211,9 +235,67 @@ class BleTransport {
211
235
  BleTransport.MAX_RETRIES = 5;
212
236
  BleTransport.RETRY_DELAY = 2000;
213
237
 
214
- const { check, buildBuffers, receiveOne, parseConfigure } = transport__default["default"];
215
- const Log = hdCore.getLogger(hdCore.LoggerNames.HdBleTransport);
238
+ const { check, ProtocolV1, parseConfigure } = transport__default["default"];
239
+ const Log = bleLogger;
216
240
  const transportCache = {};
241
+ const BLE_RESPONSE_TIMEOUT_MS = 30000;
242
+ const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
243
+ const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
244
+ const DEVICE_SCAN_TIMEOUT_MS = 8000;
245
+ const IOS_NOTIFY_READY_DELAY_MS = 150;
246
+ const HIGH_VOLUME_WRITE_BURST_SIZE = reactNative.Platform.OS === 'ios' ? 4 : 6;
247
+ const HIGH_VOLUME_WRITE_PAUSE_MS = reactNative.Platform.OS === 'ios' ? 6 : 2;
248
+ const HIGH_VOLUME_WRITE_FLUSH_DELAY_MS = reactNative.Platform.OS === 'ios' ? 20 : 8;
249
+ const delay = (ms) => new Promise(resolve => {
250
+ setTimeout(resolve, ms);
251
+ });
252
+ const DEFAULT_PROTOCOL_V2_BLE_TUNING = {
253
+ iosPacketLength: IOS_PACKET_LENGTH,
254
+ androidPacketLength: ANDROID_PACKET_LENGTH,
255
+ highVolumeWriteBurstSize: HIGH_VOLUME_WRITE_BURST_SIZE,
256
+ highVolumeWritePauseMs: HIGH_VOLUME_WRITE_PAUSE_MS,
257
+ highVolumeWriteFlushDelayMs: HIGH_VOLUME_WRITE_FLUSH_DELAY_MS,
258
+ highVolumeWriteWithResponse: false,
259
+ };
260
+ let protocolV2BleTuning = Object.assign({}, DEFAULT_PROTOCOL_V2_BLE_TUNING);
261
+ const normalizePositiveInteger = (value, fallback) => {
262
+ const normalized = Number(value);
263
+ if (!Number.isFinite(normalized) || normalized <= 0)
264
+ return fallback;
265
+ return Math.floor(normalized);
266
+ };
267
+ function configureProtocolV2BleTuning(tuning = {}) {
268
+ var _a;
269
+ protocolV2BleTuning = {
270
+ iosPacketLength: normalizePositiveInteger(tuning.iosPacketLength, protocolV2BleTuning.iosPacketLength),
271
+ androidPacketLength: normalizePositiveInteger(tuning.androidPacketLength, protocolV2BleTuning.androidPacketLength),
272
+ highVolumeWriteBurstSize: normalizePositiveInteger(tuning.highVolumeWriteBurstSize, protocolV2BleTuning.highVolumeWriteBurstSize),
273
+ highVolumeWritePauseMs: normalizePositiveInteger(tuning.highVolumeWritePauseMs, protocolV2BleTuning.highVolumeWritePauseMs),
274
+ highVolumeWriteFlushDelayMs: normalizePositiveInteger(tuning.highVolumeWriteFlushDelayMs, protocolV2BleTuning.highVolumeWriteFlushDelayMs),
275
+ highVolumeWriteWithResponse: (_a = tuning.highVolumeWriteWithResponse) !== null && _a !== void 0 ? _a : protocolV2BleTuning.highVolumeWriteWithResponse,
276
+ };
277
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 BLE tuning configured:', protocolV2BleTuning);
278
+ }
279
+ function resetProtocolV2BleTuning() {
280
+ protocolV2BleTuning = Object.assign({}, DEFAULT_PROTOCOL_V2_BLE_TUNING);
281
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 BLE tuning reset:', protocolV2BleTuning);
282
+ }
283
+ function getProtocolV2BleTuning() {
284
+ return Object.assign({}, protocolV2BleTuning);
285
+ }
286
+ function inferProtocolTypeFromDeviceName(name) {
287
+ return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
288
+ }
289
+ function getDeviceDisplayName(device) {
290
+ return (device === null || device === void 0 ? void 0 : device.name) || (device === null || device === void 0 ? void 0 : device.localName) || null;
291
+ }
292
+ function isGenericBleService(uuid) {
293
+ return ['1800', '1801', '180a'].includes(getBleUuidKey(uuid));
294
+ }
295
+ function hasKnownOneKeyService(device) {
296
+ var _a;
297
+ return ((_a = device === null || device === void 0 ? void 0 : device.serviceUUIDs) !== null && _a !== void 0 ? _a : []).some(serviceUuid => getInfosForServiceUuid(serviceUuid, 'classic'));
298
+ }
217
299
  let connectOptions = {
218
300
  requestMTU: 256,
219
301
  timeout: 3000,
@@ -222,7 +304,9 @@ let connectOptions = {
222
304
  const tryToGetConfiguration = (device) => {
223
305
  if (!device || !device.serviceUUIDs)
224
306
  return null;
225
- const [serviceUUID] = device.serviceUUIDs;
307
+ const serviceUUID = device.serviceUUIDs.find(uuid => getInfosForServiceUuid(uuid, 'classic'));
308
+ if (!serviceUUID)
309
+ return null;
226
310
  const infos = getInfosForServiceUuid(serviceUUID, 'classic');
227
311
  if (!infos)
228
312
  return null;
@@ -252,11 +336,18 @@ class ReactNativeBleTransport {
252
336
  this.name = 'ReactNativeBleTransport';
253
337
  this.configured = false;
254
338
  this.stopped = false;
255
- this.scanTimeout = 3000;
339
+ this.scanTimeout = DEVICE_SCAN_TIMEOUT_MS;
256
340
  this.runPromise = null;
257
- this.scanTimeout = (_a = options.scanTimeout) !== null && _a !== void 0 ? _a : 3000;
341
+ this.deviceProtocol = new Map();
342
+ this.protocolV2Assemblers = new Map();
343
+ this.protocolV2FrameQueue = [];
344
+ this.protocolV2FramePromise = null;
345
+ this.monitorTokens = new Map();
346
+ this.nextMonitorToken = 1;
347
+ this.scanTimeout = (_a = options.scanTimeout) !== null && _a !== void 0 ? _a : DEVICE_SCAN_TIMEOUT_MS;
258
348
  }
259
- init(_logger, emitter) {
349
+ init(logger, emitter) {
350
+ setBleLogger(logger);
260
351
  this.emitter = emitter;
261
352
  }
262
353
  configure(signedData) {
@@ -264,6 +355,10 @@ class ReactNativeBleTransport {
264
355
  this.configured = true;
265
356
  this._messages = messages;
266
357
  }
358
+ configureProtocolV2(signedData) {
359
+ this._messagesV2 = parseConfigure(signedData);
360
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 schema configured');
361
+ }
267
362
  listen() {
268
363
  }
269
364
  getPlxManager() {
@@ -299,9 +394,10 @@ class ReactNativeBleTransport {
299
394
  }
300
395
  }
301
396
  blePlxManager.startDeviceScan(null, {
397
+ allowDuplicates: true,
302
398
  scanMode: reactNativeBlePlx.ScanMode.LowLatency,
303
399
  }, (error, device) => {
304
- var _a, _b;
400
+ var _a, _b, _c;
305
401
  if (error) {
306
402
  Log === null || Log === void 0 ? void 0 : Log.debug('ble scan manager: ', blePlxManager);
307
403
  Log === null || Log === void 0 ? void 0 : Log.debug('ble scan error: ', error);
@@ -322,13 +418,35 @@ class ReactNativeBleTransport {
322
418
  }
323
419
  return;
324
420
  }
325
- if (hdShared.isOnekeyDevice((_b = device === null || device === void 0 ? void 0 : device.name) !== null && _b !== void 0 ? _b : null, device === null || device === void 0 ? void 0 : device.id)) {
421
+ const displayName = getDeviceDisplayName(device);
422
+ const isOneKey = hdShared.isOnekeyDevice((_b = device === null || device === void 0 ? void 0 : device.name) !== null && _b !== void 0 ? _b : null, device === null || device === void 0 ? void 0 : device.id) ||
423
+ hdShared.isOnekeyDevice((_c = device === null || device === void 0 ? void 0 : device.localName) !== null && _c !== void 0 ? _c : null, device === null || device === void 0 ? void 0 : device.id) ||
424
+ hasKnownOneKeyService(device);
425
+ const shouldTraceCandidate = !!displayName && /onekey|bixinkey|pro\s*2|pro\b|touch|^k\d|^t\d/i.test(displayName);
426
+ if (shouldTraceCandidate) {
427
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] scan candidate', {
428
+ name: device === null || device === void 0 ? void 0 : device.name,
429
+ localName: device === null || device === void 0 ? void 0 : device.localName,
430
+ id: device === null || device === void 0 ? void 0 : device.id,
431
+ serviceUUIDs: device === null || device === void 0 ? void 0 : device.serviceUUIDs,
432
+ accepted: isOneKey,
433
+ });
434
+ }
435
+ if (isOneKey) {
326
436
  Log === null || Log === void 0 ? void 0 : Log.debug('search device start ======================');
327
- const { name, localName, id } = device !== null && device !== void 0 ? device : {};
328
- Log === null || Log === void 0 ? void 0 : Log.debug(`device name: ${name !== null && name !== void 0 ? name : ''}\nlocalName: ${localName !== null && localName !== void 0 ? localName : ''}\nid: ${id !== null && id !== void 0 ? id : ''}`);
437
+ const { name, localName, id, serviceUUIDs } = device !== null && device !== void 0 ? device : {};
438
+ Log === null || Log === void 0 ? void 0 : Log.debug(`device name: ${name !== null && name !== void 0 ? name : ''}\nlocalName: ${localName !== null && localName !== void 0 ? localName : ''}\nid: ${id !== null && id !== void 0 ? id : ''}\nserviceUUIDs: ${(serviceUUIDs !== null && serviceUUIDs !== void 0 ? serviceUUIDs : []).join(',')}`);
329
439
  addDevice(device);
330
440
  Log === null || Log === void 0 ? void 0 : Log.debug('search device end ======================\n');
331
441
  }
442
+ else if (displayName && /\bpro\s*2\b/i.test(displayName)) {
443
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Pro2-like BLE device was not accepted:', {
444
+ name: device === null || device === void 0 ? void 0 : device.name,
445
+ localName: device === null || device === void 0 ? void 0 : device.localName,
446
+ id: device === null || device === void 0 ? void 0 : device.id,
447
+ serviceUUIDs: device === null || device === void 0 ? void 0 : device.serviceUUIDs,
448
+ });
449
+ }
332
450
  });
333
451
  getConnectedDeviceIds(getBluetoothServiceUuids()).then(devices => {
334
452
  for (const device of devices) {
@@ -337,8 +455,11 @@ class ReactNativeBleTransport {
337
455
  }
338
456
  });
339
457
  const addDevice = (device) => {
458
+ var _a;
340
459
  if (deviceList.every(d => d.id !== device.id)) {
341
- deviceList.push(Object.assign(Object.assign({}, device), { commType: 'ble' }));
460
+ const displayName = (_a = getDeviceDisplayName(device)) !== null && _a !== void 0 ? _a : 'Unknown BLE Device';
461
+ const protocolType = inferProtocolTypeFromDeviceName(displayName);
462
+ deviceList.push(Object.assign(Object.assign(Object.assign({}, device), { name: displayName, commType: 'ble' }), (protocolType ? { protocolType } : {})));
342
463
  }
343
464
  };
344
465
  timer.timeout(() => {
@@ -349,9 +470,9 @@ class ReactNativeBleTransport {
349
470
  });
350
471
  }
351
472
  acquire(input) {
352
- var _a;
473
+ var _a, _b, _c, _d, _e;
353
474
  return __awaiter(this, void 0, void 0, function* () {
354
- const { uuid, forceCleanRunPromise } = input;
475
+ const { uuid, forceCleanRunPromise, expectedProtocol } = input;
355
476
  if (!uuid) {
356
477
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleRequiredUUID);
357
478
  }
@@ -361,7 +482,10 @@ class ReactNativeBleTransport {
361
482
  yield this.release(uuid);
362
483
  }
363
484
  if (forceCleanRunPromise && this.runPromise) {
364
- this.runPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise));
485
+ const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
486
+ this.runPromise.reject(error);
487
+ this.rejectProtocolV2Frame(error);
488
+ this.runPromise = null;
365
489
  Log === null || Log === void 0 ? void 0 : Log.debug('Force clean Bluetooth run promise, forceCleanRunPromise: ', forceCleanRunPromise);
366
490
  }
367
491
  const blePlxManager = yield this.getPlxManager();
@@ -416,7 +540,7 @@ class ReactNativeBleTransport {
416
540
  if (!(yield device.isConnected())) {
417
541
  Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device: ', uuid);
418
542
  try {
419
- yield device.connect(connectOptions);
543
+ device = yield device.connect(connectOptions);
420
544
  }
421
545
  catch (e) {
422
546
  Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device has error: ', e);
@@ -425,14 +549,14 @@ class ReactNativeBleTransport {
425
549
  connectOptions = {};
426
550
  Log === null || Log === void 0 ? void 0 : Log.debug('second try to reconnect without params');
427
551
  try {
428
- yield device.connect();
552
+ device = yield device.connect();
429
553
  }
430
554
  catch (e) {
431
555
  Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect error: ', e);
432
556
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
433
557
  Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect');
434
558
  yield device.cancelConnection();
435
- yield device.connect();
559
+ device = yield device.connect();
436
560
  }
437
561
  }
438
562
  }
@@ -444,6 +568,7 @@ class ReactNativeBleTransport {
444
568
  yield device.discoverAllServicesAndCharacteristics();
445
569
  let infos = tryToGetConfiguration(device);
446
570
  let characteristics;
571
+ let fallbackServiceUuid;
447
572
  if (!infos) {
448
573
  for (const serviceUuid of getBluetoothServiceUuids()) {
449
574
  try {
@@ -457,16 +582,34 @@ class ReactNativeBleTransport {
457
582
  }
458
583
  }
459
584
  if (!infos) {
460
- try {
461
- Log === null || Log === void 0 ? void 0 : Log.debug('cancel connection when service not found');
462
- yield device.cancelConnection();
585
+ const services = yield device.services();
586
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Known OneKey service UUID not found, discovered services:', services === null || services === void 0 ? void 0 : services.map(service => service.uuid));
587
+ const knownService = services.find(service => getInfosForServiceUuid(service.uuid, 'classic'));
588
+ const fallbackService = (_a = knownService !== null && knownService !== void 0 ? knownService : services.find(service => !isGenericBleService(service.uuid))) !== null && _a !== void 0 ? _a : services[0];
589
+ if (fallbackService) {
590
+ fallbackServiceUuid = fallbackService.uuid;
591
+ characteristics = yield device.characteristicsForService(fallbackService.uuid);
592
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Using fallback BLE service:', fallbackService.uuid);
463
593
  }
464
- catch (e) {
465
- Log === null || Log === void 0 ? void 0 : Log.debug('cancel connection error when service not found: ', e.message || e.reason);
594
+ }
595
+ if (!infos) {
596
+ if (!fallbackServiceUuid) {
597
+ try {
598
+ Log === null || Log === void 0 ? void 0 : Log.debug('cancel connection when service not found');
599
+ yield device.cancelConnection();
600
+ }
601
+ catch (e) {
602
+ Log === null || Log === void 0 ? void 0 : Log.debug('cancel connection error when service not found: ', e.message || e.reason);
603
+ }
604
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
466
605
  }
606
+ }
607
+ const serviceUuid = (_b = infos === null || infos === void 0 ? void 0 : infos.serviceUuid) !== null && _b !== void 0 ? _b : fallbackServiceUuid;
608
+ const writeUuid = (_c = infos === null || infos === void 0 ? void 0 : infos.writeUuid) !== null && _c !== void 0 ? _c : '00000002-0000-1000-8000-00805f9b34fb';
609
+ const notifyUuid = (_d = infos === null || infos === void 0 ? void 0 : infos.notifyUuid) !== null && _d !== void 0 ? _d : '00000003-0000-1000-8000-00805f9b34fb';
610
+ if (!serviceUuid) {
467
611
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
468
612
  }
469
- const { serviceUuid, writeUuid, notifyUuid } = infos;
470
613
  if (!characteristics) {
471
614
  characteristics = yield device.characteristicsForService(serviceUuid);
472
615
  }
@@ -476,10 +619,10 @@ class ReactNativeBleTransport {
476
619
  let writeCharacteristic;
477
620
  let notifyCharacteristic;
478
621
  for (const c of characteristics) {
479
- if (c.uuid === writeUuid) {
622
+ if (isSameBleUuid(c.uuid, writeUuid)) {
480
623
  writeCharacteristic = c;
481
624
  }
482
- else if (c.uuid === notifyUuid) {
625
+ else if (isSameBleUuid(c.uuid, notifyUuid)) {
483
626
  notifyCharacteristic = c;
484
627
  }
485
628
  }
@@ -496,16 +639,33 @@ class ReactNativeBleTransport {
496
639
  throw hdShared.ERRORS.TypedError('BLECharacteristicNotNotifiable: notify characteristic not notifiable');
497
640
  }
498
641
  yield this.release(uuid);
499
- const transport = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
500
- transport.notifySubscription = this._monitorCharacteristic(transport.notifyCharacteristic, uuid);
501
- transportCache[uuid] = transport;
502
- (_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit('device-connect', {
642
+ const transport$1 = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
643
+ const monitorToken = this.nextMonitorToken;
644
+ this.nextMonitorToken += 1;
645
+ const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
646
+ transport$1.monitorToken = monitorToken;
647
+ transport$1.notifyTransactionId = notifyTransactionId;
648
+ this.monitorTokens.set(uuid, monitorToken);
649
+ transport$1.notifySubscription = this._monitorCharacteristic(transport$1.notifyCharacteristic, uuid, monitorToken, notifyTransactionId);
650
+ transportCache[uuid] = transport$1;
651
+ this.protocolV2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler());
652
+ if (reactNative.Platform.OS === 'ios') {
653
+ yield new Promise(resolve => {
654
+ setTimeout(resolve, IOS_NOTIFY_READY_DELAY_MS);
655
+ });
656
+ }
657
+ const protocolType = yield this.detectProtocol(uuid, expectedProtocol);
658
+ (_e = this.emitter) === null || _e === void 0 ? void 0 : _e.emit('device-connect', {
503
659
  name: device.name,
504
660
  id: device.id,
505
661
  connectId: device.id,
506
662
  });
507
- transport.disconnectSubscription = device.onDisconnected(() => {
663
+ transport$1.disconnectSubscription = device.onDisconnected(() => {
508
664
  var _a;
665
+ if (transportCache[uuid] !== transport$1) {
666
+ Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored for stale transport: ', device === null || device === void 0 ? void 0 : device.id);
667
+ return;
668
+ }
509
669
  try {
510
670
  Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect: ', device === null || device === void 0 ? void 0 : device.id);
511
671
  (_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit('device-disconnect', {
@@ -514,7 +674,9 @@ class ReactNativeBleTransport {
514
674
  connectId: device === null || device === void 0 ? void 0 : device.id,
515
675
  });
516
676
  if (this.runPromise) {
517
- this.runPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError));
677
+ const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError);
678
+ this.runPromise.reject(error);
679
+ this.rejectProtocolV2Frame(error);
518
680
  }
519
681
  }
520
682
  catch (e) {
@@ -524,16 +686,21 @@ class ReactNativeBleTransport {
524
686
  this.release(uuid);
525
687
  }
526
688
  });
527
- return { uuid };
689
+ return { uuid, protocolType };
528
690
  });
529
691
  }
530
- _monitorCharacteristic(characteristic, uuid) {
692
+ _monitorCharacteristic(characteristic, uuid, monitorToken, notifyTransactionId) {
531
693
  let bufferLength = 0;
532
694
  let buffer$1 = [];
533
695
  const subscription = characteristic.monitor((error, c) => {
534
696
  var _a, _b, _c, _d, _e, _f, _g, _h, _j;
697
+ const isCurrentMonitor = this.monitorTokens.get(uuid) === monitorToken;
535
698
  if (error) {
536
699
  Log === null || Log === void 0 ? void 0 : Log.debug(`error monitor ${characteristic.uuid}, deviceId: ${characteristic.deviceID}: ${error}`);
700
+ if (!isCurrentMonitor) {
701
+ Log === null || Log === void 0 ? void 0 : Log.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
702
+ return;
703
+ }
537
704
  if (this.runPromise) {
538
705
  let ERROR = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
539
706
  if ((_a = error.reason) === null || _a === void 0 ? void 0 : _a.includes('The connection has timed out unexpectedly')) {
@@ -547,20 +714,32 @@ class ReactNativeBleTransport {
547
714
  ((_e = error.reason) === null || _e === void 0 ? void 0 : _e.includes('The handle is invalid')) ||
548
715
  ((_f = error.reason) === null || _f === void 0 ? void 0 : _f.includes('Writing is not permitted')) ||
549
716
  ((_g = error.reason) === null || _g === void 0 ? void 0 : _g.includes('notify change failed for device'))) {
550
- this.runPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure));
717
+ const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure);
718
+ this.runPromise.reject(notifyError);
719
+ this.rejectProtocolV2Frame(notifyError);
551
720
  Log === null || Log === void 0 ? void 0 : Log.debug(`${hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure} ${error.message} ${error.reason}`);
552
721
  return;
553
722
  }
554
- this.runPromise.reject(hdShared.ERRORS.TypedError(ERROR));
723
+ const notifyError = hdShared.ERRORS.TypedError(ERROR);
724
+ this.runPromise.reject(notifyError);
725
+ this.rejectProtocolV2Frame(notifyError);
555
726
  Log === null || Log === void 0 ? void 0 : Log.debug(': monitor notify error, and has unreleased Promise', Error);
556
727
  }
557
728
  return;
558
729
  }
730
+ if (!isCurrentMonitor) {
731
+ Log === null || Log === void 0 ? void 0 : Log.debug('monitor data ignored for stale transport: ', uuid, notifyTransactionId);
732
+ return;
733
+ }
559
734
  if (!c) {
560
735
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleMonitorError);
561
736
  }
562
737
  try {
563
738
  const data = buffer.Buffer.from(c.value, 'base64');
739
+ if (this.deviceProtocol.get(uuid) === 'V2') {
740
+ this.handleProtocolV2Notification(uuid, new Uint8Array(data));
741
+ return;
742
+ }
564
743
  if (isHeaderChunk(data)) {
565
744
  bufferLength = data.readInt32BE(5);
566
745
  buffer$1 = [...data.subarray(3)];
@@ -568,7 +747,7 @@ class ReactNativeBleTransport {
568
747
  else {
569
748
  buffer$1 = buffer$1.concat([...data]);
570
749
  }
571
- if (buffer$1.length - transport.COMMON_HEADER_SIZE >= bufferLength) {
750
+ if (buffer$1.length - transport.PROTOCOL_V1_MESSAGE_HEADER_SIZE >= bufferLength) {
572
751
  const value = buffer.Buffer.from(buffer$1);
573
752
  bufferLength = 0;
574
753
  buffer$1 = [];
@@ -577,24 +756,54 @@ class ReactNativeBleTransport {
577
756
  }
578
757
  catch (error) {
579
758
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor data error: ', error);
580
- (_j = this.runPromise) === null || _j === void 0 ? void 0 : _j.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError));
759
+ const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
760
+ (_j = this.runPromise) === null || _j === void 0 ? void 0 : _j.reject(notifyError);
761
+ this.rejectProtocolV2Frame(notifyError);
581
762
  }
582
- }, uuid);
763
+ }, notifyTransactionId);
583
764
  return subscription;
584
765
  }
585
766
  release(uuid) {
586
- var _a, _b, _c;
767
+ var _a, _b, _c, _d, _e, _f;
587
768
  return __awaiter(this, void 0, void 0, function* () {
588
769
  const transport = transportCache[uuid];
770
+ if (this.runPromise) {
771
+ const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
772
+ this.runPromise.reject(error);
773
+ this.runPromise = null;
774
+ this.rejectProtocolV2Frame(error);
775
+ }
776
+ else {
777
+ this.resetProtocolV2Frames();
778
+ }
589
779
  if (transport) {
780
+ if (this.monitorTokens.get(uuid) === transport.monitorToken) {
781
+ this.monitorTokens.delete(uuid);
782
+ }
590
783
  Log === null || Log === void 0 ? void 0 : Log.debug('release: removing disconnect subscription for device: ', uuid);
591
784
  (_a = transport.disconnectSubscription) === null || _a === void 0 ? void 0 : _a.remove();
592
785
  transport.disconnectSubscription = undefined;
593
786
  Log === null || Log === void 0 ? void 0 : Log.debug('release: removing notify subscription, characteristic: ', (_b = transport.notifyCharacteristic) === null || _b === void 0 ? void 0 : _b.uuid);
594
787
  (_c = transport.notifySubscription) === null || _c === void 0 ? void 0 : _c.remove();
595
788
  transport.notifySubscription = undefined;
789
+ if (transport.notifyTransactionId) {
790
+ try {
791
+ yield ((_d = this.blePlxManager) === null || _d === void 0 ? void 0 : _d.cancelTransaction(transport.notifyTransactionId));
792
+ }
793
+ catch (e) {
794
+ Log === null || Log === void 0 ? void 0 : Log.debug('release: cancel notify transaction error (ignored): ', (e === null || e === void 0 ? void 0 : e.message) || e);
795
+ }
796
+ }
596
797
  delete transportCache[uuid];
597
- if (reactNative.Platform.OS === 'android') ;
798
+ this.deviceProtocol.delete(uuid);
799
+ }
800
+ (_e = this.protocolV2Assemblers.get(uuid)) === null || _e === void 0 ? void 0 : _e.reset();
801
+ this.protocolV2Assemblers.delete(uuid);
802
+ try {
803
+ yield ((_f = this.blePlxManager) === null || _f === void 0 ? void 0 : _f.cancelTransaction(uuid));
804
+ }
805
+ catch (e) {
806
+ Log === null || Log === void 0 ? void 0 : Log.debug('release: cancel transaction error (ignored): ', (e === null || e === void 0 ? void 0 : e.message) || e);
598
807
  }
599
808
  return Promise.resolve(true);
600
809
  });
@@ -604,7 +813,7 @@ class ReactNativeBleTransport {
604
813
  yield this.call(session, name, data);
605
814
  });
606
815
  }
607
- call(uuid, name, data) {
816
+ call(uuid, name, data, options) {
608
817
  return __awaiter(this, void 0, void 0, function* () {
609
818
  if (this.stopped) {
610
819
  return Promise.reject(hdShared.ERRORS.TypedError('Transport stopped.'));
@@ -617,12 +826,7 @@ class ReactNativeBleTransport {
617
826
  if (this.runPromise && !forceRun) {
618
827
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportCallInProgress);
619
828
  }
620
- const transport$1 = transportCache[uuid];
621
- if (!transport$1) {
622
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
623
- }
624
- this.runPromise = hdShared.createDeferred();
625
- const messages = this._messages;
829
+ const protocol = this.getProtocolType(uuid);
626
830
  if (name === 'ResourceUpdate' || name === 'ResourceAck') {
627
831
  Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native', 'call-', ' name: ', name, ' data: ', {
628
832
  file_name: data === null || data === void 0 ? void 0 : data.file_name,
@@ -630,12 +834,29 @@ class ReactNativeBleTransport {
630
834
  });
631
835
  }
632
836
  else if (transport.LogBlockCommand.has(name)) {
633
- Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native', 'call-', ' name: ', name);
837
+ Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native', 'call-', ' name: ', name, ' protocol: ', protocol);
634
838
  }
635
839
  else {
636
- Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native', 'call-', ' name: ', name, ' data: ', data);
840
+ Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native', 'call-', ' name: ', name, ' data: ', data, ' protocol: ', protocol);
841
+ }
842
+ if (protocol === 'V2') {
843
+ return this.callProtocolV2(uuid, name, data, options);
637
844
  }
638
- const buffers = buildBuffers(messages, name, data);
845
+ return this.callProtocolV1(uuid, name, data, options);
846
+ });
847
+ }
848
+ callProtocolV1(uuid, name, data, options) {
849
+ return __awaiter(this, void 0, void 0, function* () {
850
+ if (!this._messages) {
851
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
852
+ }
853
+ const transport = this.getCachedTransport(uuid);
854
+ const runPromise = hdShared.createDeferred();
855
+ runPromise.promise.catch(() => undefined);
856
+ this.runPromise = runPromise;
857
+ const messages = this._messages;
858
+ const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
859
+ let timeout;
639
860
  function writeChunkedData(buffers, writeFunction, onError) {
640
861
  return __awaiter(this, void 0, void 0, function* () {
641
862
  const packetCapacity = reactNative.Platform.OS === 'ios' ? IOS_PACKET_LENGTH : ANDROID_PACKET_LENGTH;
@@ -660,14 +881,14 @@ class ReactNativeBleTransport {
660
881
  });
661
882
  }
662
883
  if (name === 'EmmcFileWrite') {
663
- yield writeChunkedData(buffers, data => transport$1.writeWithRetry(data), e => {
884
+ yield writeChunkedData(buffers, data => transport.writeWithRetry(data), e => {
664
885
  this.runPromise = null;
665
886
  Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
666
887
  });
667
888
  }
668
889
  else if (name === 'FirmwareUpload') {
669
890
  yield writeChunkedData(buffers, (data) => __awaiter(this, void 0, void 0, function* () {
670
- yield transport$1.writeCharacteristic.writeWithoutResponse(data);
891
+ yield transport.writeCharacteristic.writeWithoutResponse(data);
671
892
  }), e => {
672
893
  this.runPromise = null;
673
894
  Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
@@ -677,7 +898,7 @@ class ReactNativeBleTransport {
677
898
  for (const o of buffers) {
678
899
  const outData = o.toString('base64');
679
900
  try {
680
- yield transport$1.writeCharacteristic.writeWithoutResponse(outData);
901
+ yield transport.writeCharacteristic.writeWithoutResponse(outData);
681
902
  }
682
903
  catch (e) {
683
904
  Log === null || Log === void 0 ? void 0 : Log.debug('writeCharacteristic write error: ', e);
@@ -695,20 +916,40 @@ class ReactNativeBleTransport {
695
916
  }
696
917
  }
697
918
  try {
698
- const response = yield this.runPromise.promise;
919
+ const response = yield Promise.race([
920
+ runPromise.promise,
921
+ new Promise((_, reject) => {
922
+ if (options === null || options === void 0 ? void 0 : options.timeoutMs) {
923
+ timeout = setTimeout(() => {
924
+ const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, `BLE response timeout after ${options.timeoutMs}ms for ${name}`);
925
+ runPromise.reject(error);
926
+ reject(error);
927
+ }, options.timeoutMs);
928
+ }
929
+ }),
930
+ ]);
699
931
  if (typeof response !== 'string') {
700
932
  throw new Error('Returning data is not string.');
701
933
  }
702
934
  Log === null || Log === void 0 ? void 0 : Log.debug('receive data: ', response);
703
- const jsonData = receiveOne(messages, response);
935
+ const jsonData = ProtocolV1.decodeMessage(messages, response);
704
936
  return check.call(jsonData);
705
937
  }
706
938
  catch (e) {
707
- Log === null || Log === void 0 ? void 0 : Log.error('call error: ', e);
939
+ if (name === 'Initialize' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS) {
940
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 Initialize probe call failed:', e);
941
+ }
942
+ else {
943
+ Log === null || Log === void 0 ? void 0 : Log.error('call error: ', e);
944
+ }
708
945
  throw e;
709
946
  }
710
947
  finally {
711
- this.runPromise = null;
948
+ if (timeout)
949
+ clearTimeout(timeout);
950
+ if (this.runPromise === runPromise) {
951
+ this.runPromise = null;
952
+ }
712
953
  }
713
954
  });
714
955
  }
@@ -765,6 +1006,8 @@ class ReactNativeBleTransport {
765
1006
  if (transportCache[session]) {
766
1007
  delete transportCache[session];
767
1008
  }
1009
+ this.deviceProtocol.delete(session);
1010
+ this.protocolV2Assemblers.delete(session);
768
1011
  try {
769
1012
  (_d = this.emitter) === null || _d === void 0 ? void 0 : _d.emit('device-disconnect', {
770
1013
  name: (_e = transport === null || transport === void 0 ? void 0 : transport.device) === null || _e === void 0 ? void 0 : _e.name,
@@ -783,6 +1026,271 @@ class ReactNativeBleTransport {
783
1026
  if (this.runPromise) ;
784
1027
  this.runPromise = null;
785
1028
  }
1029
+ getCachedTransport(uuid) {
1030
+ const transport = transportCache[uuid];
1031
+ if (!transport) {
1032
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
1033
+ }
1034
+ return transport;
1035
+ }
1036
+ createProtocolMismatchError(expected) {
1037
+ return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
1038
+ }
1039
+ detectProtocol(uuid, expectedProtocol) {
1040
+ return __awaiter(this, void 0, void 0, function* () {
1041
+ if (expectedProtocol === 'V1') {
1042
+ if (yield this.probeProtocolV1(uuid)) {
1043
+ this.deviceProtocol.set(uuid, 'V1');
1044
+ Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> V1 (expected)`);
1045
+ return 'V1';
1046
+ }
1047
+ throw this.createProtocolMismatchError(expectedProtocol);
1048
+ }
1049
+ if (expectedProtocol === 'V2') {
1050
+ if (yield this.probeProtocolV2(uuid)) {
1051
+ this.deviceProtocol.set(uuid, 'V2');
1052
+ Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> V2 (expected)`);
1053
+ return 'V2';
1054
+ }
1055
+ throw this.createProtocolMismatchError(expectedProtocol);
1056
+ }
1057
+ if (this.deviceProtocol.get(uuid) === 'V2' && (yield this.probeProtocolV2(uuid))) {
1058
+ this.deviceProtocol.set(uuid, 'V2');
1059
+ Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> V2 (cached)`);
1060
+ return 'V2';
1061
+ }
1062
+ let protocol = 'V1';
1063
+ if (!(yield this.probeProtocolV1(uuid)) && (yield this.probeProtocolV2(uuid))) {
1064
+ protocol = 'V2';
1065
+ }
1066
+ this.deviceProtocol.set(uuid, protocol);
1067
+ Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> ${protocol}`);
1068
+ return protocol;
1069
+ });
1070
+ }
1071
+ probeProtocolV1(uuid) {
1072
+ return __awaiter(this, void 0, void 0, function* () {
1073
+ if (!this._messages) {
1074
+ return false;
1075
+ }
1076
+ try {
1077
+ this.deviceProtocol.set(uuid, 'V1');
1078
+ yield this.callProtocolV1(uuid, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
1079
+ return true;
1080
+ }
1081
+ catch (error) {
1082
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 Initialize probe failed:', error);
1083
+ return false;
1084
+ }
1085
+ });
1086
+ }
1087
+ probeProtocolV2(uuid) {
1088
+ var _a;
1089
+ return __awaiter(this, void 0, void 0, function* () {
1090
+ if (!this._messages || !this._messagesV2) {
1091
+ return false;
1092
+ }
1093
+ this.deviceProtocol.set(uuid, 'V2');
1094
+ (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1095
+ return transport.probeProtocolV2({
1096
+ call: (name, data, options) => this.callProtocolV2(uuid, name, data, options),
1097
+ timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT_MS,
1098
+ logger: Log,
1099
+ logPrefix: 'ProtocolV2 RN-BLE',
1100
+ onProbeFailed: () => {
1101
+ var _a;
1102
+ (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1103
+ this.resetProtocolV2Frames();
1104
+ },
1105
+ });
1106
+ });
1107
+ }
1108
+ handleProtocolV2Notification(uuid, data) {
1109
+ var _a, _b;
1110
+ try {
1111
+ if (!this.runPromise) {
1112
+ (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1113
+ this.resetProtocolV2Frames();
1114
+ return;
1115
+ }
1116
+ if (data.length === 0)
1117
+ return;
1118
+ const assembler = this.protocolV2Assemblers.get(uuid);
1119
+ if (!assembler)
1120
+ return;
1121
+ let frameData = assembler.push(data);
1122
+ while (frameData) {
1123
+ this.resolveProtocolV2Frame(frameData);
1124
+ frameData = assembler.push(new Uint8Array(0));
1125
+ }
1126
+ }
1127
+ catch (error) {
1128
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 notification error:', error);
1129
+ const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1130
+ (_b = this.runPromise) === null || _b === void 0 ? void 0 : _b.reject(notifyError);
1131
+ this.rejectProtocolV2Frame(notifyError);
1132
+ }
1133
+ }
1134
+ resolveProtocolV2Frame(frame) {
1135
+ if (this.protocolV2FramePromise) {
1136
+ this.protocolV2FramePromise.resolve(frame);
1137
+ this.protocolV2FramePromise = null;
1138
+ return;
1139
+ }
1140
+ this.protocolV2FrameQueue.push(frame);
1141
+ }
1142
+ rejectProtocolV2Frame(error) {
1143
+ this.protocolV2FrameQueue = [];
1144
+ if (this.protocolV2FramePromise) {
1145
+ this.protocolV2FramePromise.reject(error);
1146
+ this.protocolV2FramePromise = null;
1147
+ }
1148
+ }
1149
+ resetProtocolV2Frames() {
1150
+ this.protocolV2FrameQueue = [];
1151
+ this.protocolV2FramePromise = null;
1152
+ }
1153
+ readProtocolV2Frame() {
1154
+ return __awaiter(this, void 0, void 0, function* () {
1155
+ const queuedFrame = this.protocolV2FrameQueue.shift();
1156
+ if (queuedFrame) {
1157
+ return queuedFrame;
1158
+ }
1159
+ const framePromise = hdShared.createDeferred();
1160
+ this.protocolV2FramePromise = framePromise;
1161
+ try {
1162
+ return yield framePromise.promise;
1163
+ }
1164
+ finally {
1165
+ if (this.protocolV2FramePromise === framePromise) {
1166
+ this.protocolV2FramePromise = null;
1167
+ }
1168
+ }
1169
+ });
1170
+ }
1171
+ writeProtocolV2Frame(transport, frame, options) {
1172
+ return __awaiter(this, void 0, void 0, function* () {
1173
+ const tuning = getProtocolV2BleTuning();
1174
+ const packetCapacity = reactNative.Platform.OS === 'ios' ? tuning.iosPacketLength : tuning.androidPacketLength;
1175
+ const writeWithResponse = !!(options === null || options === void 0 ? void 0 : options.writeWithResponse) || (!!(options === null || options === void 0 ? void 0 : options.highVolume) && tuning.highVolumeWriteWithResponse);
1176
+ const shouldThrottle = !!(options === null || options === void 0 ? void 0 : options.highVolume) && !writeWithResponse;
1177
+ let packetsWritten = 0;
1178
+ try {
1179
+ for (let offset = 0; offset < frame.length; offset += packetCapacity) {
1180
+ const chunk = frame.slice(offset, offset + packetCapacity);
1181
+ const base64 = buffer.Buffer.from(chunk).toString('base64');
1182
+ if (writeWithResponse) {
1183
+ yield transport.writeCharacteristic.writeWithResponse(base64);
1184
+ }
1185
+ else {
1186
+ yield transport.writeCharacteristic.writeWithoutResponse(base64);
1187
+ }
1188
+ packetsWritten += 1;
1189
+ if (shouldThrottle &&
1190
+ packetsWritten % tuning.highVolumeWriteBurstSize === 0 &&
1191
+ offset + packetCapacity < frame.length) {
1192
+ yield delay(tuning.highVolumeWritePauseMs);
1193
+ }
1194
+ }
1195
+ if (shouldThrottle) {
1196
+ yield delay(tuning.highVolumeWriteFlushDelayMs);
1197
+ }
1198
+ }
1199
+ catch (error) {
1200
+ if ((options === null || options === void 0 ? void 0 : options.highVolume) && !writeWithResponse && packetsWritten === 0) {
1201
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume writeWithoutResponse failed before data was sent, fallback to writeWithResponse:', error);
1202
+ yield this.writeProtocolV2Frame(transport, frame, {
1203
+ highVolume: true,
1204
+ writeWithResponse: true,
1205
+ });
1206
+ return;
1207
+ }
1208
+ throw error;
1209
+ }
1210
+ });
1211
+ }
1212
+ callProtocolV2(uuid, name, data, options) {
1213
+ var _a, _b, _c, _d;
1214
+ return __awaiter(this, void 0, void 0, function* () {
1215
+ if (!this._messages || !this._messagesV2) {
1216
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
1217
+ }
1218
+ const forceRun = name === 'Initialize' || name === 'Cancel' || name === 'GetProtoVersion';
1219
+ if (this.runPromise) {
1220
+ if (!forceRun) {
1221
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportCallInProgress);
1222
+ }
1223
+ const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
1224
+ this.runPromise.reject(error);
1225
+ this.rejectProtocolV2Frame(error);
1226
+ this.runPromise = null;
1227
+ }
1228
+ const transport$1 = this.getCachedTransport(uuid);
1229
+ const runPromise = hdShared.createDeferred();
1230
+ runPromise.promise.catch(() => undefined);
1231
+ this.runPromise = runPromise;
1232
+ (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1233
+ this.resetProtocolV2Frames();
1234
+ let completed = false;
1235
+ const callOptions = Object.assign(Object.assign({}, options), { timeoutMs: (_b = options === null || options === void 0 ? void 0 : options.timeoutMs) !== null && _b !== void 0 ? _b : BLE_RESPONSE_TIMEOUT_MS });
1236
+ const highVolumeWrite = transport.LogBlockCommand.has(name);
1237
+ if (highVolumeWrite) {
1238
+ const tuning = getProtocolV2BleTuning();
1239
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume write uses throttled writeWithoutResponse:', name, {
1240
+ packetCapacity: reactNative.Platform.OS === 'ios' ? tuning.iosPacketLength : tuning.androidPacketLength,
1241
+ burstSize: tuning.highVolumeWriteBurstSize,
1242
+ pauseMs: tuning.highVolumeWritePauseMs,
1243
+ flushDelayMs: tuning.highVolumeWriteFlushDelayMs,
1244
+ writeWithResponse: tuning.highVolumeWriteWithResponse,
1245
+ });
1246
+ }
1247
+ try {
1248
+ const session = new transport.ProtocolV2Session({
1249
+ schemas: {
1250
+ protocolV1: this._messages,
1251
+ protocolV2: this._messagesV2,
1252
+ },
1253
+ router: transport.PROTOCOL_V2_CHANNEL_BLE_UART,
1254
+ writeFrame: (frame) => this.writeProtocolV2Frame(transport$1, frame, { highVolume: highVolumeWrite }),
1255
+ readFrame: () => __awaiter(this, void 0, void 0, function* () {
1256
+ const rxFrame = yield this.readProtocolV2Frame();
1257
+ if (!(rxFrame instanceof Uint8Array)) {
1258
+ throw new Error('Protocol V2 response is not Uint8Array');
1259
+ }
1260
+ return rxFrame;
1261
+ }),
1262
+ logger: Log,
1263
+ logPrefix: 'ProtocolV2 RN-BLE',
1264
+ createTimeoutError: (_messageName, timeout) => hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, `BLE response timeout after ${timeout}ms for ${name}`),
1265
+ });
1266
+ const result = yield session.call(name, data, callOptions);
1267
+ completed = true;
1268
+ return result;
1269
+ }
1270
+ catch (e) {
1271
+ (_c = this.protocolV2Assemblers.get(uuid)) === null || _c === void 0 ? void 0 : _c.reset();
1272
+ this.resetProtocolV2Frames();
1273
+ Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] Protocol V2 call error:', e);
1274
+ throw e;
1275
+ }
1276
+ finally {
1277
+ if (!completed) {
1278
+ (_d = this.protocolV2Assemblers.get(uuid)) === null || _d === void 0 ? void 0 : _d.reset();
1279
+ }
1280
+ this.resetProtocolV2Frames();
1281
+ if (this.runPromise === runPromise) {
1282
+ this.runPromise = null;
1283
+ }
1284
+ }
1285
+ });
1286
+ }
1287
+ getProtocolType(path) {
1288
+ var _a;
1289
+ return (_a = this.deviceProtocol.get(path)) !== null && _a !== void 0 ? _a : 'V1';
1290
+ }
786
1291
  }
787
1292
 
788
- module.exports = ReactNativeBleTransport;
1293
+ exports.configureProtocolV2BleTuning = configureProtocolV2BleTuning;
1294
+ exports["default"] = ReactNativeBleTransport;
1295
+ exports.getProtocolV2BleTuning = getProtocolV2BleTuning;
1296
+ exports.resetProtocolV2BleTuning = resetProtocolV2BleTuning;