@onekeyfe/hd-transport-react-native 1.1.26-alpha.12 → 1.1.26-alpha.30

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