@onekeyfe/hd-transport-react-native 1.1.27-patch.1 → 1.2.0-alpha.0

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,44 +46,124 @@ 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) => {
52
- let timeout;
53
- const cleanup = (cleanupListener) => {
63
+ const cleanup = () => {
54
64
  if (timeout) {
55
65
  clearTimeout(timeout);
56
66
  }
57
67
  if (cleanupListener)
58
68
  cleanupListener();
59
69
  };
70
+ const timeout = setTimeout(() => {
71
+ cleanup();
72
+ reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'device is not bonded'));
73
+ }, 60 * 1000);
60
74
  const cleanupListener = BleUtils__default["default"].onDeviceBondState(peripheral => {
61
75
  var _a;
62
76
  if (((_a = peripheral.id) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== bleMacAddress.toLowerCase()) {
63
77
  return;
64
78
  }
65
79
  const { bondState } = peripheral;
66
- if (bondState.preState === 'BOND_NONE' && bondState.state === 'BOND_BONDING') {
67
- timeout = setTimeout(() => {
68
- cleanup(cleanupListener);
69
- reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'device is not bonded'));
70
- }, 60 * 1000);
71
- }
72
80
  const hasBonded = bondState.preState === 'BOND_BONDING' && bondState.state === 'BOND_BONDED';
73
81
  const hasCanceled = bondState.preState === 'BOND_BONDING' && bondState.state === 'BOND_NONE';
74
82
  Logger.debug('onDeviceBondState bondState:', bondState);
75
83
  if (hasBonded) {
76
- cleanup(cleanupListener);
84
+ cleanup();
77
85
  resolve(peripheral);
78
86
  }
79
87
  else if (hasCanceled) {
80
- cleanup(cleanupListener);
88
+ cleanup();
81
89
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceBondedCanceled, 'bonding canceled'));
82
90
  }
83
91
  });
84
92
  });
85
93
 
94
+ const IOS_PACKET_LENGTH = 128;
95
+ const ANDROID_PACKET_LENGTH = 192;
96
+ const ANDROID_DEFAULT_MTU = 23;
97
+ const ClassicServiceUUID = '00000001-0000-1000-8000-00805f9b34fb';
98
+ const OneKeyServices = {
99
+ classic: {
100
+ [ClassicServiceUUID]: {
101
+ serviceUuid: ClassicServiceUUID,
102
+ writeUuid: '00000002-0000-1000-8000-00805f9b34fb',
103
+ notifyUuid: '00000003-0000-1000-8000-00805f9b34fb',
104
+ },
105
+ },
106
+ };
107
+ const bluetoothServices = [];
108
+ for (const deviceType of Object.keys(OneKeyServices)) {
109
+ const services = OneKeyServices[deviceType];
110
+ bluetoothServices.push(...Object.keys(services));
111
+ }
112
+ const getBluetoothServiceUuids = () => bluetoothServices;
113
+ const getInfosForServiceUuid = (serviceUuid, deviceType) => {
114
+ var _a;
115
+ const services = OneKeyServices[deviceType];
116
+ if (!services) {
117
+ return null;
118
+ }
119
+ const normalizedServiceUuid = normalizeBleUuid(serviceUuid);
120
+ const service = (_a = services[serviceUuid]) !== null && _a !== void 0 ? _a : Object.values(services).find(item => normalizeBleUuid(item.serviceUuid) === normalizedServiceUuid);
121
+ if (!service) {
122
+ return null;
123
+ }
124
+ return service;
125
+ };
126
+ const normalizeBleUuid = (uuid) => (uuid !== null && uuid !== void 0 ? uuid : '').replace(/-/g, '').toLowerCase();
127
+ const getBleUuidKey = (uuid) => {
128
+ const normalized = normalizeBleUuid(uuid);
129
+ return normalized.length >= 8 ? normalized.substring(4, 8) : normalized;
130
+ };
131
+ const isSameBleUuid = (left, right) => {
132
+ const normalizedLeft = normalizeBleUuid(left);
133
+ const normalizedRight = normalizeBleUuid(right);
134
+ return (normalizedLeft === normalizedRight ||
135
+ (getBleUuidKey(left) !== '' && getBleUuidKey(left) === getBleUuidKey(right)));
136
+ };
137
+
138
+ function hasWritableCapability(characteristic) {
139
+ return !!(characteristic.isWritableWithResponse || characteristic.isWritableWithoutResponse);
140
+ }
141
+ function resolveBleWriteMode(characteristic, preferredMode = 'withoutResponse') {
142
+ if (preferredMode === 'withoutResponse' && characteristic.isWritableWithoutResponse) {
143
+ return 'withoutResponse';
144
+ }
145
+ if (preferredMode === 'withResponse' && characteristic.isWritableWithResponse) {
146
+ return 'withResponse';
147
+ }
148
+ if (characteristic.isWritableWithoutResponse) {
149
+ return 'withoutResponse';
150
+ }
151
+ if (characteristic.isWritableWithResponse) {
152
+ return 'withResponse';
153
+ }
154
+ return preferredMode;
155
+ }
156
+ function resolveProtocolV2PacketCapacity({ platform, iosPacketLength = IOS_PACKET_LENGTH, androidPacketLength = ANDROID_PACKET_LENGTH, mtu, }) {
157
+ if (platform === 'ios') {
158
+ return iosPacketLength;
159
+ }
160
+ if (platform === 'android') {
161
+ const payloadLength = Math.max((mtu !== null && mtu !== void 0 ? mtu : ANDROID_DEFAULT_MTU) - 3, 1);
162
+ return Math.min(androidPacketLength, payloadLength);
163
+ }
164
+ return androidPacketLength;
165
+ }
166
+
86
167
  const timer = process.env.NODE_ENV === 'development'
87
168
  ? {
88
169
  timeout: (fn, ms) => {
@@ -126,58 +207,27 @@ const subscribeBleOn = (bleManager, ms = 1000) => new Promise((resolve, reject)
126
207
  }, ms);
127
208
  });
128
209
 
129
- const IOS_PACKET_LENGTH = 128;
130
- const ANDROID_PACKET_LENGTH = 192;
131
- const ClassicServiceUUID = '00000001-0000-1000-8000-00805f9b34fb';
132
- const OneKeyServices = {
133
- classic: {
134
- [ClassicServiceUUID]: {
135
- serviceUuid: ClassicServiceUUID,
136
- writeUuid: '00000002-0000-1000-8000-00805f9b34fb',
137
- notifyUuid: '00000003-0000-1000-8000-00805f9b34fb',
138
- },
139
- },
140
- };
141
- const bluetoothServices = [];
142
- for (const deviceType of Object.keys(OneKeyServices)) {
143
- const services = OneKeyServices[deviceType];
144
- bluetoothServices.push(...Object.keys(services));
145
- }
146
- const getBluetoothServiceUuids = () => bluetoothServices;
147
- const getInfosForServiceUuid = (serviceUuid, deviceType) => {
148
- const services = OneKeyServices[deviceType];
149
- if (!services) {
150
- return null;
151
- }
152
- const service = services[serviceUuid];
153
- if (!service) {
154
- return null;
155
- }
156
- return service;
157
- };
158
-
159
210
  const isHeaderChunk = (chunk) => {
160
211
  if (chunk.length < 9)
161
212
  return false;
162
213
  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)) {
214
+ if (String.fromCharCode(MagicQuestionMark) === String.fromCharCode(transport.PROTOCOL_V1_REPORT_ID) &&
215
+ String.fromCharCode(sharp1) === String.fromCharCode(transport.PROTOCOL_V1_HEADER_BYTE) &&
216
+ String.fromCharCode(sharp2) === String.fromCharCode(transport.PROTOCOL_V1_HEADER_BYTE)) {
166
217
  return true;
167
218
  }
168
219
  return false;
169
220
  };
170
221
 
171
- const Log$1 = hdCore.getLogger(hdCore.LoggerNames.HdBleTransport);
222
+ const Log$1 = bleLogger;
172
223
  class BleTransport {
173
224
  constructor(device, writeCharacteristic, notifyCharacteristic) {
174
225
  this.name = 'ReactNativeBleTransport';
175
- this.mtuSize = 20;
226
+ this.mtuSize = 23;
176
227
  this.id = device.id;
177
228
  this.device = device;
178
229
  this.writeCharacteristic = writeCharacteristic;
179
230
  this.notifyCharacteristic = notifyCharacteristic;
180
- console.log(`BleTransport(${String(this.id)}) new instance`);
181
231
  }
182
232
  writeWithRetry(data, retryCount = BleTransport.MAX_RETRIES) {
183
233
  return __awaiter(this, void 0, void 0, function* () {
@@ -187,7 +237,7 @@ class BleTransport {
187
237
  catch (error) {
188
238
  Log$1 === null || Log$1 === void 0 ? void 0 : Log$1.debug(`Write retry attempt ${BleTransport.MAX_RETRIES - retryCount + 1}, error: ${error}`);
189
239
  if (retryCount > 0) {
190
- yield hdCore.wait(BleTransport.RETRY_DELAY);
240
+ yield hdShared.wait(BleTransport.RETRY_DELAY);
191
241
  if (error.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected ||
192
242
  error.errorCode === reactNativeBlePlx.BleErrorCode.CharacteristicNotFound) {
193
243
  try {
@@ -211,8 +261,8 @@ class BleTransport {
211
261
  BleTransport.MAX_RETRIES = 5;
212
262
  BleTransport.RETRY_DELAY = 2000;
213
263
 
214
- const { check, buildBuffers, receiveOne, parseConfigure } = transport__default["default"];
215
- const Log = hdCore.getLogger(hdCore.LoggerNames.HdBleTransport);
264
+ const { check, ProtocolV1, parseConfigure } = transport__default["default"];
265
+ const Log = bleLogger;
216
266
  const transportCache = {};
217
267
  const FIRMWARE_UPLOAD_WRITE_BURST_SIZE = reactNative.Platform.OS === 'ios' ? 4 : 5;
218
268
  const FIRMWARE_UPLOAD_WRITE_PAUSE_MS = reactNative.Platform.OS === 'ios' ? 8 : 10;
@@ -243,20 +293,95 @@ const getFirmwareUploadWriteRetryType = (error) => {
243
293
  return /GATT_CONGESTED|status\s*[:=]?\s*143/.test(text) ? 'congested' : null;
244
294
  };
245
295
  const resolveFirmwareUploadRetryDelay = (attempt, baseDelayMs = 200, maxDelayMs = 1200) => Math.min(baseDelayMs * Math.pow(2, attempt), maxDelayMs);
246
- let connectOptions = {
247
- requestMTU: 256,
296
+ const BLE_RESPONSE_TIMEOUT_MS = 30000;
297
+ const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
298
+ const PROTOCOL_V2_PROBE_TIMEOUT_MS = 10000;
299
+ const DEVICE_SCAN_TIMEOUT_MS = 8000;
300
+ const IOS_NOTIFY_READY_DELAY_MS = 150;
301
+ const ANDROID_NOTIFY_READY_DELAY_MS = 300;
302
+ const HIGH_VOLUME_WRITE_BURST_SIZE = reactNative.Platform.OS === 'ios' ? 4 : 6;
303
+ const HIGH_VOLUME_WRITE_PAUSE_MS = reactNative.Platform.OS === 'ios' ? 6 : 2;
304
+ const HIGH_VOLUME_WRITE_FLUSH_DELAY_MS = reactNative.Platform.OS === 'ios' ? 20 : 8;
305
+ const DEFAULT_PROTOCOL_V2_BLE_TUNING = {
306
+ iosPacketLength: IOS_PACKET_LENGTH,
307
+ androidPacketLength: ANDROID_PACKET_LENGTH,
308
+ highVolumeWriteBurstSize: HIGH_VOLUME_WRITE_BURST_SIZE,
309
+ highVolumeWritePauseMs: HIGH_VOLUME_WRITE_PAUSE_MS,
310
+ highVolumeWriteFlushDelayMs: HIGH_VOLUME_WRITE_FLUSH_DELAY_MS,
311
+ highVolumeWriteWithResponse: false,
312
+ };
313
+ let protocolV2BleTuning = Object.assign({}, DEFAULT_PROTOCOL_V2_BLE_TUNING);
314
+ const normalizePositiveInteger = (value, fallback) => {
315
+ const normalized = Number(value);
316
+ if (!Number.isFinite(normalized) || normalized <= 0)
317
+ return fallback;
318
+ return Math.floor(normalized);
319
+ };
320
+ function configureProtocolV2BleTuning(tuning = {}) {
321
+ var _a;
322
+ protocolV2BleTuning = {
323
+ iosPacketLength: normalizePositiveInteger(tuning.iosPacketLength, protocolV2BleTuning.iosPacketLength),
324
+ androidPacketLength: normalizePositiveInteger(tuning.androidPacketLength, protocolV2BleTuning.androidPacketLength),
325
+ highVolumeWriteBurstSize: normalizePositiveInteger(tuning.highVolumeWriteBurstSize, protocolV2BleTuning.highVolumeWriteBurstSize),
326
+ highVolumeWritePauseMs: normalizePositiveInteger(tuning.highVolumeWritePauseMs, protocolV2BleTuning.highVolumeWritePauseMs),
327
+ highVolumeWriteFlushDelayMs: normalizePositiveInteger(tuning.highVolumeWriteFlushDelayMs, protocolV2BleTuning.highVolumeWriteFlushDelayMs),
328
+ highVolumeWriteWithResponse: (_a = tuning.highVolumeWriteWithResponse) !== null && _a !== void 0 ? _a : protocolV2BleTuning.highVolumeWriteWithResponse,
329
+ };
330
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 BLE tuning configured:', protocolV2BleTuning);
331
+ }
332
+ function resetProtocolV2BleTuning() {
333
+ protocolV2BleTuning = Object.assign({}, DEFAULT_PROTOCOL_V2_BLE_TUNING);
334
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 BLE tuning reset:', protocolV2BleTuning);
335
+ }
336
+ function getProtocolV2BleTuning() {
337
+ return Object.assign({}, protocolV2BleTuning);
338
+ }
339
+ function inferProtocolHintFromDeviceName(name) {
340
+ return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
341
+ }
342
+ function getDeviceDisplayName(device) {
343
+ return (device === null || device === void 0 ? void 0 : device.name) || (device === null || device === void 0 ? void 0 : device.localName) || null;
344
+ }
345
+ function isGenericBleService(uuid) {
346
+ return ['1800', '1801', '180a'].includes(getBleUuidKey(uuid));
347
+ }
348
+ function hasKnownOneKeyService(device) {
349
+ var _a;
350
+ return ((_a = device === null || device === void 0 ? void 0 : device.serviceUUIDs) !== null && _a !== void 0 ? _a : []).some(serviceUuid => getInfosForServiceUuid(serviceUuid, 'classic'));
351
+ }
352
+ const ANDROID_REQUEST_MTU = 256;
353
+ const connectOptions = {
354
+ requestMTU: ANDROID_REQUEST_MTU,
248
355
  timeout: 3000,
249
356
  refreshGatt: 'OnConnected',
250
357
  };
251
358
  const tryToGetConfiguration = (device) => {
252
359
  if (!device || !device.serviceUUIDs)
253
360
  return null;
254
- const [serviceUUID] = device.serviceUUIDs;
361
+ const serviceUUID = device.serviceUUIDs.find(uuid => getInfosForServiceUuid(uuid, 'classic'));
362
+ if (!serviceUUID)
363
+ return null;
255
364
  const infos = getInfosForServiceUuid(serviceUUID, 'classic');
256
365
  if (!infos)
257
366
  return null;
258
367
  return infos;
259
368
  };
369
+ const requestAndroidMtu = (device) => __awaiter(void 0, void 0, void 0, function* () {
370
+ if (reactNative.Platform.OS !== 'android')
371
+ return device;
372
+ try {
373
+ const mtuDevice = yield device.requestMTU(ANDROID_REQUEST_MTU);
374
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android MTU requested:', {
375
+ requested: ANDROID_REQUEST_MTU,
376
+ mtu: mtuDevice.mtu,
377
+ });
378
+ return mtuDevice;
379
+ }
380
+ catch (error) {
381
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android MTU request failed:', error);
382
+ return device;
383
+ }
384
+ });
260
385
  function remapError(error) {
261
386
  var _a;
262
387
  if (error instanceof reactNativeBlePlx.BleError) {
@@ -281,12 +406,22 @@ class ReactNativeBleTransport {
281
406
  this.name = 'ReactNativeBleTransport';
282
407
  this.configured = false;
283
408
  this.stopped = false;
284
- this.scanTimeout = 3000;
409
+ this.scanTimeout = DEVICE_SCAN_TIMEOUT_MS;
285
410
  this.runPromise = null;
286
411
  this.firmwareUploadWriteRecoveryIds = new Set();
287
- this.scanTimeout = (_a = options.scanTimeout) !== null && _a !== void 0 ? _a : 3000;
412
+ this.deviceProtocol = new Map();
413
+ this.deviceProtocolHints = new Map();
414
+ this.protocolV2Assemblers = new Map();
415
+ this.protocolV2FrameQueues = new Map();
416
+ this.protocolV2FramePromises = new Map();
417
+ this.activeProtocolV2Call = null;
418
+ this.nextProtocolV2CallToken = 1;
419
+ this.monitorTokens = new Map();
420
+ this.nextMonitorToken = 1;
421
+ this.scanTimeout = (_a = options.scanTimeout) !== null && _a !== void 0 ? _a : DEVICE_SCAN_TIMEOUT_MS;
288
422
  }
289
- init(_logger, emitter) {
423
+ init(logger, emitter) {
424
+ setBleLogger(logger);
290
425
  this.emitter = emitter;
291
426
  }
292
427
  configure(signedData) {
@@ -294,6 +429,10 @@ class ReactNativeBleTransport {
294
429
  this.configured = true;
295
430
  this._messages = messages;
296
431
  }
432
+ configureProtocolV2(signedData) {
433
+ this._messagesV2 = parseConfigure(signedData);
434
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 schema configured');
435
+ }
297
436
  listen() {
298
437
  }
299
438
  getPlxManager() {
@@ -303,6 +442,7 @@ class ReactNativeBleTransport {
303
442
  return Promise.resolve(this.blePlxManager);
304
443
  }
305
444
  resolveCharacteristics(device) {
445
+ var _a, _b, _c, _d;
306
446
  return __awaiter(this, void 0, void 0, function* () {
307
447
  yield device.discoverAllServicesAndCharacteristics();
308
448
  let infos = tryToGetConfiguration(device);
@@ -319,7 +459,19 @@ class ReactNativeBleTransport {
319
459
  }
320
460
  }
321
461
  }
462
+ let fallbackServiceUuid;
322
463
  if (!infos) {
464
+ const services = yield device.services();
465
+ 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));
466
+ const knownService = services.find(service => getInfosForServiceUuid(service.uuid, 'classic'));
467
+ const fallbackService = (_a = knownService !== null && knownService !== void 0 ? knownService : services.find(service => !isGenericBleService(service.uuid))) !== null && _a !== void 0 ? _a : services[0];
468
+ if (fallbackService) {
469
+ fallbackServiceUuid = fallbackService.uuid;
470
+ characteristics = yield device.characteristicsForService(fallbackService.uuid);
471
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Using fallback BLE service:', fallbackService.uuid);
472
+ }
473
+ }
474
+ if (!infos && !fallbackServiceUuid) {
323
475
  try {
324
476
  Log === null || Log === void 0 ? void 0 : Log.debug('cancel connection when service not found');
325
477
  yield device.cancelConnection();
@@ -329,7 +481,12 @@ class ReactNativeBleTransport {
329
481
  }
330
482
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
331
483
  }
332
- const { serviceUuid, writeUuid, notifyUuid } = infos;
484
+ const serviceUuid = (_b = infos === null || infos === void 0 ? void 0 : infos.serviceUuid) !== null && _b !== void 0 ? _b : fallbackServiceUuid;
485
+ const writeUuid = (_c = infos === null || infos === void 0 ? void 0 : infos.writeUuid) !== null && _c !== void 0 ? _c : '00000002-0000-1000-8000-00805f9b34fb';
486
+ const notifyUuid = (_d = infos === null || infos === void 0 ? void 0 : infos.notifyUuid) !== null && _d !== void 0 ? _d : '00000003-0000-1000-8000-00805f9b34fb';
487
+ if (!serviceUuid) {
488
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
489
+ }
333
490
  if (!characteristics) {
334
491
  characteristics = yield device.characteristicsForService(serviceUuid);
335
492
  }
@@ -339,10 +496,10 @@ class ReactNativeBleTransport {
339
496
  let writeCharacteristic;
340
497
  let notifyCharacteristic;
341
498
  for (const c of characteristics) {
342
- if (c.uuid === writeUuid) {
499
+ if (isSameBleUuid(c.uuid, writeUuid)) {
343
500
  writeCharacteristic = c;
344
501
  }
345
- else if (c.uuid === notifyUuid) {
502
+ else if (isSameBleUuid(c.uuid, notifyUuid)) {
346
503
  notifyCharacteristic = c;
347
504
  }
348
505
  }
@@ -352,7 +509,7 @@ class ReactNativeBleTransport {
352
509
  if (!notifyCharacteristic) {
353
510
  throw hdShared.ERRORS.TypedError('BLECharacteristicNotFound: notify characteristic not found');
354
511
  }
355
- if (!writeCharacteristic.isWritableWithResponse) {
512
+ if (!hasWritableCapability(writeCharacteristic)) {
356
513
  throw hdShared.ERRORS.TypedError('BLECharacteristicNotWritable: write characteristic not writable');
357
514
  }
358
515
  if (!notifyCharacteristic.isNotifiable) {
@@ -373,6 +530,10 @@ class ReactNativeBleTransport {
373
530
  Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored during FirmwareUpload write recovery: ', uuid);
374
531
  return;
375
532
  }
533
+ if (transportCache[uuid] !== transport) {
534
+ Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored for stale transport: ', device === null || device === void 0 ? void 0 : device.id);
535
+ return;
536
+ }
376
537
  try {
377
538
  Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect: ', device === null || device === void 0 ? void 0 : device.id);
378
539
  (_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit('device-disconnect', {
@@ -381,14 +542,16 @@ class ReactNativeBleTransport {
381
542
  connectId: device === null || device === void 0 ? void 0 : device.id,
382
543
  });
383
544
  if (this.runPromise) {
384
- this.runPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError));
545
+ const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError);
546
+ this.runPromise.reject(error);
547
+ this.rejectAllProtocolV2Frames(error);
385
548
  }
386
549
  }
387
550
  catch (e) {
388
551
  Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect error: ', e);
389
552
  }
390
553
  finally {
391
- this.release(uuid);
554
+ this.release(uuid, true);
392
555
  }
393
556
  });
394
557
  }
@@ -410,7 +573,6 @@ class ReactNativeBleTransport {
410
573
  catch (e) {
411
574
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
412
575
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
413
- connectOptions = {};
414
576
  device = yield device.connect();
415
577
  }
416
578
  else if (e.errorCode !== reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
@@ -422,7 +584,13 @@ class ReactNativeBleTransport {
422
584
  transport.device = device;
423
585
  transport.writeCharacteristic = writeCharacteristic;
424
586
  transport.notifyCharacteristic = notifyCharacteristic;
425
- transport.notifySubscription = this._monitorCharacteristic(notifyCharacteristic, uuid);
587
+ const monitorToken = this.nextMonitorToken;
588
+ this.nextMonitorToken += 1;
589
+ const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
590
+ transport.monitorToken = monitorToken;
591
+ transport.notifyTransactionId = notifyTransactionId;
592
+ this.monitorTokens.set(uuid, monitorToken);
593
+ transport.notifySubscription = this._monitorCharacteristic(notifyCharacteristic, uuid, monitorToken, notifyTransactionId);
426
594
  this.attachDisconnectSubscription(transport, device, uuid);
427
595
  }
428
596
  finally {
@@ -457,9 +625,10 @@ class ReactNativeBleTransport {
457
625
  }
458
626
  }
459
627
  blePlxManager.startDeviceScan(null, {
628
+ allowDuplicates: true,
460
629
  scanMode: reactNativeBlePlx.ScanMode.LowLatency,
461
630
  }, (error, device) => {
462
- var _a, _b;
631
+ var _a, _b, _c;
463
632
  if (error) {
464
633
  Log === null || Log === void 0 ? void 0 : Log.debug('ble scan manager: ', blePlxManager);
465
634
  Log === null || Log === void 0 ? void 0 : Log.debug('ble scan error: ', error);
@@ -480,13 +649,35 @@ class ReactNativeBleTransport {
480
649
  }
481
650
  return;
482
651
  }
483
- 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)) {
652
+ const displayName = getDeviceDisplayName(device);
653
+ 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) ||
654
+ 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) ||
655
+ hasKnownOneKeyService(device);
656
+ const shouldTraceCandidate = !!displayName && /onekey|bixinkey|pro\s*2|pro\b|touch|^k\d|^t\d/i.test(displayName);
657
+ if (shouldTraceCandidate) {
658
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] scan candidate', {
659
+ name: device === null || device === void 0 ? void 0 : device.name,
660
+ localName: device === null || device === void 0 ? void 0 : device.localName,
661
+ id: device === null || device === void 0 ? void 0 : device.id,
662
+ serviceUUIDs: device === null || device === void 0 ? void 0 : device.serviceUUIDs,
663
+ accepted: isOneKey,
664
+ });
665
+ }
666
+ if (isOneKey) {
484
667
  Log === null || Log === void 0 ? void 0 : Log.debug('search device start ======================');
485
- const { name, localName, id } = device !== null && device !== void 0 ? device : {};
486
- 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 : ''}`);
668
+ const { name, localName, id, serviceUUIDs } = device !== null && device !== void 0 ? device : {};
669
+ 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(',')}`);
487
670
  addDevice(device);
488
671
  Log === null || Log === void 0 ? void 0 : Log.debug('search device end ======================\n');
489
672
  }
673
+ else if (displayName && /\bpro\s*2\b/i.test(displayName)) {
674
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Pro2-like BLE device was not accepted:', {
675
+ name: device === null || device === void 0 ? void 0 : device.name,
676
+ localName: device === null || device === void 0 ? void 0 : device.localName,
677
+ id: device === null || device === void 0 ? void 0 : device.id,
678
+ serviceUUIDs: device === null || device === void 0 ? void 0 : device.serviceUUIDs,
679
+ });
680
+ }
490
681
  });
491
682
  getConnectedDeviceIds(getBluetoothServiceUuids()).then(devices => {
492
683
  for (const device of devices) {
@@ -495,8 +686,14 @@ class ReactNativeBleTransport {
495
686
  }
496
687
  });
497
688
  const addDevice = (device) => {
689
+ var _a;
498
690
  if (deviceList.every(d => d.id !== device.id)) {
499
- deviceList.push(Object.assign(Object.assign({}, device), { commType: 'ble' }));
691
+ const displayName = (_a = getDeviceDisplayName(device)) !== null && _a !== void 0 ? _a : 'Unknown BLE Device';
692
+ const protocolHint = inferProtocolHintFromDeviceName(displayName);
693
+ if (protocolHint) {
694
+ this.deviceProtocolHints.set(device.id, protocolHint);
695
+ }
696
+ deviceList.push(Object.assign(Object.assign({}, device), { name: displayName, commType: 'ble' }));
500
697
  }
501
698
  };
502
699
  timer.timeout(() => {
@@ -507,19 +704,32 @@ class ReactNativeBleTransport {
507
704
  });
508
705
  }
509
706
  acquire(input) {
510
- var _a;
707
+ var _a, _b;
511
708
  return __awaiter(this, void 0, void 0, function* () {
512
- const { uuid, forceCleanRunPromise } = input;
709
+ const { uuid, forceCleanRunPromise, expectedProtocol } = input;
513
710
  if (!uuid) {
514
711
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleRequiredUUID);
515
712
  }
516
- let device = null;
517
- if (transportCache[uuid]) {
518
- Log === null || Log === void 0 ? void 0 : Log.debug('transport not be released, will release: ', uuid);
519
- yield this.release(uuid);
713
+ const cachedTransport = transportCache[uuid];
714
+ if (cachedTransport) {
715
+ const cachedProtocol = this.deviceProtocol.get(uuid);
716
+ const isCachedDeviceConnected = yield cachedTransport.device.isConnected().catch(() => false);
717
+ if (isCachedDeviceConnected &&
718
+ cachedProtocol &&
719
+ (!expectedProtocol || cachedProtocol === expectedProtocol)) {
720
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] reuse cached BLE transport:', uuid, cachedProtocol);
721
+ return { uuid, protocolType: cachedProtocol };
722
+ }
723
+ Log === null || Log === void 0 ? void 0 : Log.debug('transport not reusable, will release: ', uuid);
724
+ yield this.release(uuid, true);
520
725
  }
726
+ let device = null;
521
727
  if (forceCleanRunPromise && this.runPromise) {
522
- this.runPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise));
728
+ const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
729
+ this.runPromise.reject(error);
730
+ this.rejectAllProtocolV2Frames(error);
731
+ this.runPromise = null;
732
+ this.activeProtocolV2Call = null;
523
733
  Log === null || Log === void 0 ? void 0 : Log.debug('Force clean Bluetooth run promise, forceCleanRunPromise: ', forceCleanRunPromise);
524
734
  }
525
735
  const blePlxManager = yield this.getPlxManager();
@@ -535,6 +745,9 @@ class ReactNativeBleTransport {
535
745
  if (bondState.bonding) {
536
746
  yield onDeviceBondState(uuid);
537
747
  }
748
+ else if (!bondState.bonded) {
749
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'device is not bonded');
750
+ }
538
751
  }
539
752
  if (!device) {
540
753
  const devices = yield blePlxManager.devices([uuid]);
@@ -555,7 +768,6 @@ class ReactNativeBleTransport {
555
768
  Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device has error: ', e);
556
769
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
557
770
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
558
- connectOptions = {};
559
771
  Log === null || Log === void 0 ? void 0 : Log.debug('first try to reconnect without params');
560
772
  device = yield blePlxManager.connectToDevice(uuid);
561
773
  }
@@ -574,23 +786,22 @@ class ReactNativeBleTransport {
574
786
  if (!(yield device.isConnected())) {
575
787
  Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device: ', uuid);
576
788
  try {
577
- yield device.connect(connectOptions);
789
+ device = yield device.connect(connectOptions);
578
790
  }
579
791
  catch (e) {
580
792
  Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device has error: ', e);
581
793
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
582
794
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
583
- connectOptions = {};
584
795
  Log === null || Log === void 0 ? void 0 : Log.debug('second try to reconnect without params');
585
796
  try {
586
- yield device.connect();
797
+ device = yield device.connect();
587
798
  }
588
799
  catch (e) {
589
800
  Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect error: ', e);
590
801
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
591
802
  Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect');
592
803
  yield device.cancelConnection();
593
- yield device.connect();
804
+ device = yield device.connect();
594
805
  }
595
806
  }
596
807
  }
@@ -599,31 +810,62 @@ class ReactNativeBleTransport {
599
810
  }
600
811
  }
601
812
  }
813
+ device = yield requestAndroidMtu(device);
602
814
  const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristics(device);
603
- yield this.release(uuid);
604
- const transport = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
605
- transport.notifySubscription = this._monitorCharacteristic(transport.notifyCharacteristic, uuid);
606
- transportCache[uuid] = transport;
607
- (_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit('device-connect', {
815
+ const protocolHint = expectedProtocol
816
+ ? undefined
817
+ : (_a = this.deviceProtocolHints.get(uuid)) !== null && _a !== void 0 ? _a : inferProtocolHintFromDeviceName(getDeviceDisplayName(device));
818
+ yield this.release(uuid, true);
819
+ if (protocolHint) {
820
+ this.deviceProtocolHints.set(uuid, protocolHint);
821
+ }
822
+ const transport$1 = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
823
+ if (reactNative.Platform.OS === 'android') {
824
+ transport$1.mtuSize = typeof device.mtu === 'number' ? device.mtu : transport$1.mtuSize;
825
+ }
826
+ const monitorToken = this.nextMonitorToken;
827
+ this.nextMonitorToken += 1;
828
+ const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
829
+ transport$1.monitorToken = monitorToken;
830
+ transport$1.notifyTransactionId = notifyTransactionId;
831
+ this.monitorTokens.set(uuid, monitorToken);
832
+ transport$1.notifySubscription = this._monitorCharacteristic(transport$1.notifyCharacteristic, uuid, monitorToken, notifyTransactionId);
833
+ transportCache[uuid] = transport$1;
834
+ this.protocolV2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler());
835
+ if (reactNative.Platform.OS === 'ios') {
836
+ yield new Promise(resolve => {
837
+ setTimeout(resolve, IOS_NOTIFY_READY_DELAY_MS);
838
+ });
839
+ }
840
+ else if (reactNative.Platform.OS === 'android') {
841
+ yield delay(ANDROID_NOTIFY_READY_DELAY_MS);
842
+ }
843
+ const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint);
844
+ (_b = this.emitter) === null || _b === void 0 ? void 0 : _b.emit('device-connect', {
608
845
  name: device.name,
609
846
  id: device.id,
610
847
  connectId: device.id,
611
848
  });
612
- this.attachDisconnectSubscription(transport, device, uuid);
613
- return { uuid };
849
+ this.attachDisconnectSubscription(transport$1, device, uuid);
850
+ return { uuid, protocolType };
614
851
  });
615
852
  }
616
- _monitorCharacteristic(characteristic, uuid) {
853
+ _monitorCharacteristic(characteristic, uuid, monitorToken, notifyTransactionId) {
617
854
  let bufferLength = 0;
618
855
  let buffer$1 = [];
619
856
  const subscription = characteristic.monitor((error, c) => {
620
857
  var _a, _b, _c, _d, _e, _f, _g, _h, _j;
858
+ const isCurrentMonitor = this.monitorTokens.get(uuid) === monitorToken;
621
859
  if (error) {
622
860
  Log === null || Log === void 0 ? void 0 : Log.debug(`error monitor ${characteristic.uuid}, deviceId: ${characteristic.deviceID}: ${error}`);
623
861
  if (this.firmwareUploadWriteRecoveryIds.has(uuid)) {
624
862
  Log === null || Log === void 0 ? void 0 : Log.debug('notify error ignored during FirmwareUpload write recovery: ', uuid);
625
863
  return;
626
864
  }
865
+ if (!isCurrentMonitor) {
866
+ Log === null || Log === void 0 ? void 0 : Log.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
867
+ return;
868
+ }
627
869
  if (this.runPromise) {
628
870
  let ERROR = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
629
871
  if ((_a = error.reason) === null || _a === void 0 ? void 0 : _a.includes('The connection has timed out unexpectedly')) {
@@ -637,20 +879,37 @@ class ReactNativeBleTransport {
637
879
  ((_e = error.reason) === null || _e === void 0 ? void 0 : _e.includes('The handle is invalid')) ||
638
880
  ((_f = error.reason) === null || _f === void 0 ? void 0 : _f.includes('Writing is not permitted')) ||
639
881
  ((_g = error.reason) === null || _g === void 0 ? void 0 : _g.includes('notify change failed for device'))) {
640
- this.runPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure));
882
+ const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure);
883
+ this.runPromise.reject(notifyError);
884
+ this.rejectAllProtocolV2Frames(notifyError);
641
885
  Log === null || Log === void 0 ? void 0 : Log.debug(`${hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure} ${error.message} ${error.reason}`);
642
886
  return;
643
887
  }
644
- this.runPromise.reject(hdShared.ERRORS.TypedError(ERROR));
888
+ const notifyError = hdShared.ERRORS.TypedError(ERROR);
889
+ this.runPromise.reject(notifyError);
890
+ this.rejectAllProtocolV2Frames(notifyError);
645
891
  Log === null || Log === void 0 ? void 0 : Log.debug(': monitor notify error, and has unreleased Promise', Error);
646
892
  }
647
893
  return;
648
894
  }
895
+ if (!isCurrentMonitor) {
896
+ Log === null || Log === void 0 ? void 0 : Log.debug('monitor data ignored for stale transport: ', uuid, notifyTransactionId);
897
+ return;
898
+ }
649
899
  if (!c) {
650
900
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleMonitorError);
651
901
  }
652
902
  try {
653
903
  const data = buffer.Buffer.from(c.value, 'base64');
904
+ const protocol = this.deviceProtocol.get(uuid);
905
+ if (!protocol) {
906
+ Log === null || Log === void 0 ? void 0 : Log.debug('monitor data ignored before protocol detection: ', uuid);
907
+ return;
908
+ }
909
+ if (protocol === 'V2') {
910
+ this.handleProtocolV2Notification(uuid, new Uint8Array(data));
911
+ return;
912
+ }
654
913
  if (isHeaderChunk(data)) {
655
914
  bufferLength = data.readInt32BE(5);
656
915
  buffer$1 = [...data.subarray(3)];
@@ -658,7 +917,7 @@ class ReactNativeBleTransport {
658
917
  else {
659
918
  buffer$1 = buffer$1.concat([...data]);
660
919
  }
661
- if (buffer$1.length - transport.COMMON_HEADER_SIZE >= bufferLength) {
920
+ if (buffer$1.length - transport.PROTOCOL_V1_MESSAGE_HEADER_SIZE >= bufferLength) {
662
921
  const value = buffer.Buffer.from(buffer$1);
663
922
  bufferLength = 0;
664
923
  buffer$1 = [];
@@ -667,24 +926,65 @@ class ReactNativeBleTransport {
667
926
  }
668
927
  catch (error) {
669
928
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor data error: ', error);
670
- (_j = this.runPromise) === null || _j === void 0 ? void 0 : _j.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError));
929
+ const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
930
+ (_j = this.runPromise) === null || _j === void 0 ? void 0 : _j.reject(notifyError);
931
+ this.rejectAllProtocolV2Frames(notifyError);
671
932
  }
672
- }, uuid);
933
+ }, notifyTransactionId);
673
934
  return subscription;
674
935
  }
675
- release(uuid) {
676
- var _a, _b, _c;
936
+ release(uuid, onclose = false) {
937
+ var _a, _b, _c, _d, _e, _f, _g, _h;
677
938
  return __awaiter(this, void 0, void 0, function* () {
678
939
  const transport = transportCache[uuid];
940
+ if (this.runPromise) {
941
+ const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
942
+ this.runPromise.reject(error);
943
+ this.runPromise = null;
944
+ this.rejectAllProtocolV2Frames(error);
945
+ this.activeProtocolV2Call = null;
946
+ }
947
+ else {
948
+ this.resetProtocolV2Frames(uuid);
949
+ }
950
+ if (reactNative.Platform.OS === 'android' && !onclose && transport) {
951
+ (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
952
+ this.resetProtocolV2Frames(uuid);
953
+ if (((_b = this.activeProtocolV2Call) === null || _b === void 0 ? void 0 : _b.uuid) === uuid) {
954
+ this.activeProtocolV2Call = null;
955
+ }
956
+ return Promise.resolve(true);
957
+ }
679
958
  if (transport) {
959
+ if (this.monitorTokens.get(uuid) === transport.monitorToken) {
960
+ this.monitorTokens.delete(uuid);
961
+ }
680
962
  Log === null || Log === void 0 ? void 0 : Log.debug('release: removing disconnect subscription for device: ', uuid);
681
- (_a = transport.disconnectSubscription) === null || _a === void 0 ? void 0 : _a.remove();
963
+ (_c = transport.disconnectSubscription) === null || _c === void 0 ? void 0 : _c.remove();
682
964
  transport.disconnectSubscription = undefined;
683
- 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);
684
- (_c = transport.notifySubscription) === null || _c === void 0 ? void 0 : _c.remove();
965
+ Log === null || Log === void 0 ? void 0 : Log.debug('release: removing notify subscription, characteristic: ', (_d = transport.notifyCharacteristic) === null || _d === void 0 ? void 0 : _d.uuid);
966
+ (_e = transport.notifySubscription) === null || _e === void 0 ? void 0 : _e.remove();
685
967
  transport.notifySubscription = undefined;
968
+ if (transport.notifyTransactionId) {
969
+ try {
970
+ yield ((_f = this.blePlxManager) === null || _f === void 0 ? void 0 : _f.cancelTransaction(transport.notifyTransactionId));
971
+ }
972
+ catch (e) {
973
+ 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);
974
+ }
975
+ }
686
976
  delete transportCache[uuid];
687
- if (reactNative.Platform.OS === 'android') ;
977
+ }
978
+ this.deviceProtocol.delete(uuid);
979
+ this.deviceProtocolHints.delete(uuid);
980
+ (_g = this.protocolV2Assemblers.get(uuid)) === null || _g === void 0 ? void 0 : _g.reset();
981
+ this.protocolV2Assemblers.delete(uuid);
982
+ this.resetProtocolV2Frames(uuid);
983
+ try {
984
+ yield ((_h = this.blePlxManager) === null || _h === void 0 ? void 0 : _h.cancelTransaction(uuid));
985
+ }
986
+ catch (e) {
987
+ Log === null || Log === void 0 ? void 0 : Log.debug('release: cancel transaction error (ignored): ', (e === null || e === void 0 ? void 0 : e.message) || e);
688
988
  }
689
989
  return Promise.resolve(true);
690
990
  });
@@ -694,7 +994,7 @@ class ReactNativeBleTransport {
694
994
  yield this.call(session, name, data);
695
995
  });
696
996
  }
697
- call(uuid, name, data) {
997
+ call(uuid, name, data, options) {
698
998
  return __awaiter(this, void 0, void 0, function* () {
699
999
  if (this.stopped) {
700
1000
  return Promise.reject(hdShared.ERRORS.TypedError('Transport stopped.'));
@@ -707,12 +1007,10 @@ class ReactNativeBleTransport {
707
1007
  if (this.runPromise && !forceRun) {
708
1008
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportCallInProgress);
709
1009
  }
710
- const transport$1 = transportCache[uuid];
711
- if (!transport$1) {
712
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
1010
+ const protocol = this.getProtocolType(uuid);
1011
+ if (!protocol) {
1012
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${uuid}`);
713
1013
  }
714
- this.runPromise = hdShared.createDeferred();
715
- const messages = this._messages;
716
1014
  if (name === 'ResourceUpdate' || name === 'ResourceAck') {
717
1015
  Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native', 'call-', ' name: ', name, ' data: ', {
718
1016
  file_name: data === null || data === void 0 ? void 0 : data.file_name,
@@ -720,12 +1018,29 @@ class ReactNativeBleTransport {
720
1018
  });
721
1019
  }
722
1020
  else if (transport.LogBlockCommand.has(name)) {
723
- Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native', 'call-', ' name: ', name);
1021
+ Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native', 'call-', ' name: ', name, ' protocol: ', protocol);
724
1022
  }
725
1023
  else {
726
- Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native', 'call-', ' name: ', name, ' data: ', data);
1024
+ Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native', 'call-', ' name: ', name, ' data: ', data, ' protocol: ', protocol);
1025
+ }
1026
+ if (protocol === 'V2') {
1027
+ return this.callProtocolV2(uuid, name, data, options);
1028
+ }
1029
+ return this.callProtocolV1(uuid, name, data, options);
1030
+ });
1031
+ }
1032
+ callProtocolV1(uuid, name, data, options) {
1033
+ return __awaiter(this, void 0, void 0, function* () {
1034
+ if (!this._messages) {
1035
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
727
1036
  }
728
- const buffers = buildBuffers(messages, name, data);
1037
+ const transport = this.getCachedTransport(uuid);
1038
+ const runPromise = hdShared.createDeferred();
1039
+ runPromise.promise.catch(() => undefined);
1040
+ this.runPromise = runPromise;
1041
+ const messages = this._messages;
1042
+ const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
1043
+ let timeout;
729
1044
  function writeChunkedData(buffers, writeFunction, onError) {
730
1045
  return __awaiter(this, void 0, void 0, function* () {
731
1046
  const packetCapacity = reactNative.Platform.OS === 'ios' ? IOS_PACKET_LENGTH : ANDROID_PACKET_LENGTH;
@@ -780,7 +1095,7 @@ class ReactNativeBleTransport {
780
1095
  });
781
1096
  }
782
1097
  if (name === 'EmmcFileWrite') {
783
- yield writeChunkedData(buffers, data => transport$1.writeWithRetry(data), e => {
1098
+ yield writeChunkedData(buffers, data => transport.writeWithRetry(data), e => {
784
1099
  this.runPromise = null;
785
1100
  Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
786
1101
  });
@@ -797,7 +1112,7 @@ class ReactNativeBleTransport {
797
1112
  let attempt = 0;
798
1113
  while (true) {
799
1114
  try {
800
- yield transport$1.writeCharacteristic.writeWithoutResponse(data);
1115
+ yield transport.writeCharacteristic.writeWithoutResponse(data);
801
1116
  return;
802
1117
  }
803
1118
  catch (error) {
@@ -822,7 +1137,7 @@ class ReactNativeBleTransport {
822
1137
  attempt += 1;
823
1138
  if (shouldReconnect) {
824
1139
  try {
825
- yield this.reconnectFirmwareUploadTransport(uuid, transport$1);
1140
+ yield this.reconnectFirmwareUploadTransport(uuid, transport);
826
1141
  }
827
1142
  catch (e) {
828
1143
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] FirmwareUpload reconnect error:', e);
@@ -842,7 +1157,7 @@ class ReactNativeBleTransport {
842
1157
  for (const o of buffers) {
843
1158
  const outData = o.toString('base64');
844
1159
  try {
845
- yield transport$1.writeCharacteristic.writeWithoutResponse(outData);
1160
+ yield transport.writeCharacteristic.writeWithoutResponse(outData);
846
1161
  }
847
1162
  catch (e) {
848
1163
  Log === null || Log === void 0 ? void 0 : Log.debug('writeCharacteristic write error: ', e);
@@ -860,20 +1175,40 @@ class ReactNativeBleTransport {
860
1175
  }
861
1176
  }
862
1177
  try {
863
- const response = yield this.runPromise.promise;
1178
+ const response = yield Promise.race([
1179
+ runPromise.promise,
1180
+ new Promise((_, reject) => {
1181
+ if (options === null || options === void 0 ? void 0 : options.timeoutMs) {
1182
+ timeout = setTimeout(() => {
1183
+ const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, `BLE response timeout after ${options.timeoutMs}ms for ${name}`);
1184
+ runPromise.reject(error);
1185
+ reject(error);
1186
+ }, options.timeoutMs);
1187
+ }
1188
+ }),
1189
+ ]);
864
1190
  if (typeof response !== 'string') {
865
1191
  throw new Error('Returning data is not string.');
866
1192
  }
867
1193
  Log === null || Log === void 0 ? void 0 : Log.debug('receive data: ', response);
868
- const jsonData = receiveOne(messages, response);
1194
+ const jsonData = ProtocolV1.decodeMessage(messages, response);
869
1195
  return check.call(jsonData);
870
1196
  }
871
1197
  catch (e) {
872
- Log === null || Log === void 0 ? void 0 : Log.error('call error: ', e);
1198
+ if (name === 'Initialize' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS) {
1199
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 Initialize probe call failed:', e);
1200
+ }
1201
+ else {
1202
+ Log === null || Log === void 0 ? void 0 : Log.error('call error: ', e);
1203
+ }
873
1204
  throw e;
874
1205
  }
875
1206
  finally {
876
- this.runPromise = null;
1207
+ if (timeout)
1208
+ clearTimeout(timeout);
1209
+ if (this.runPromise === runPromise) {
1210
+ this.runPromise = null;
1211
+ }
877
1212
  }
878
1213
  });
879
1214
  }
@@ -881,7 +1216,7 @@ class ReactNativeBleTransport {
881
1216
  this.stopped = true;
882
1217
  }
883
1218
  disconnect(session) {
884
- var _a, _b, _c, _d, _e;
1219
+ var _a, _b, _c, _d, _e, _f;
885
1220
  return __awaiter(this, void 0, void 0, function* () {
886
1221
  Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native transport resetSession: ', session);
887
1222
  const transport = transportCache[session];
@@ -930,9 +1265,16 @@ class ReactNativeBleTransport {
930
1265
  if (transportCache[session]) {
931
1266
  delete transportCache[session];
932
1267
  }
1268
+ this.deviceProtocol.delete(session);
1269
+ this.deviceProtocolHints.delete(session);
1270
+ this.protocolV2Assemblers.delete(session);
1271
+ this.resetProtocolV2Frames(session);
1272
+ if (((_d = this.activeProtocolV2Call) === null || _d === void 0 ? void 0 : _d.uuid) === session) {
1273
+ this.activeProtocolV2Call = null;
1274
+ }
933
1275
  try {
934
- (_d = this.emitter) === null || _d === void 0 ? void 0 : _d.emit('device-disconnect', {
935
- name: (_e = transport === null || transport === void 0 ? void 0 : transport.device) === null || _e === void 0 ? void 0 : _e.name,
1276
+ (_e = this.emitter) === null || _e === void 0 ? void 0 : _e.emit('device-disconnect', {
1277
+ name: (_f = transport === null || transport === void 0 ? void 0 : transport.device) === null || _f === void 0 ? void 0 : _f.name,
936
1278
  id: session,
937
1279
  connectId: session,
938
1280
  });
@@ -948,6 +1290,356 @@ class ReactNativeBleTransport {
948
1290
  if (this.runPromise) ;
949
1291
  this.runPromise = null;
950
1292
  }
1293
+ getCachedTransport(uuid) {
1294
+ const transport = transportCache[uuid];
1295
+ if (!transport) {
1296
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
1297
+ }
1298
+ return transport;
1299
+ }
1300
+ createProtocolMismatchError(expected) {
1301
+ return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
1302
+ }
1303
+ createProtocolDetectionError() {
1304
+ return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1 Initialize or Protocol V2 Ping');
1305
+ }
1306
+ clearProbeProtocol(uuid, protocol) {
1307
+ if (this.deviceProtocol.get(uuid) === protocol) {
1308
+ this.deviceProtocol.delete(uuid);
1309
+ }
1310
+ }
1311
+ detectProtocol(uuid, expectedProtocol, protocolHint) {
1312
+ return __awaiter(this, void 0, void 0, function* () {
1313
+ if (expectedProtocol === 'V1') {
1314
+ if (yield this.probeProtocolV1(uuid)) {
1315
+ this.deviceProtocol.set(uuid, 'V1');
1316
+ Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> V1 (expected)`);
1317
+ return 'V1';
1318
+ }
1319
+ throw this.createProtocolMismatchError(expectedProtocol);
1320
+ }
1321
+ if (expectedProtocol === 'V2') {
1322
+ this.deviceProtocol.set(uuid, 'V2');
1323
+ Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> V2 (expected)`);
1324
+ return 'V2';
1325
+ }
1326
+ const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
1327
+ for (let i = 0; i < probeOrder.length; i += 1) {
1328
+ const protocol = probeOrder[i];
1329
+ if (i > 0) {
1330
+ yield this.resetProbeStateAfterProtocolProbe(uuid, probeOrder[i - 1]);
1331
+ }
1332
+ const detected = protocol === 'V1' ? yield this.probeProtocolV1(uuid) : yield this.probeProtocolV2(uuid);
1333
+ if (detected) {
1334
+ this.deviceProtocol.set(uuid, protocol);
1335
+ Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> ${protocol}`);
1336
+ return protocol;
1337
+ }
1338
+ }
1339
+ this.deviceProtocol.delete(uuid);
1340
+ throw this.createProtocolDetectionError();
1341
+ });
1342
+ }
1343
+ resetProbeStateAfterProtocolProbe(uuid, protocol) {
1344
+ var _a, _b, _c, _d;
1345
+ return __awaiter(this, void 0, void 0, function* () {
1346
+ const transport = transportCache[uuid];
1347
+ (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1348
+ this.resetProtocolV2Frames(uuid);
1349
+ if (((_b = this.activeProtocolV2Call) === null || _b === void 0 ? void 0 : _b.uuid) === uuid) {
1350
+ this.activeProtocolV2Call = null;
1351
+ }
1352
+ if (this.runPromise) {
1353
+ const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
1354
+ this.runPromise.reject(error);
1355
+ this.runPromise = null;
1356
+ }
1357
+ if (!transport)
1358
+ return;
1359
+ const previousNotifyTransactionId = transport.notifyTransactionId;
1360
+ if (this.monitorTokens.get(uuid) === transport.monitorToken) {
1361
+ this.monitorTokens.delete(uuid);
1362
+ }
1363
+ (_c = transport.notifySubscription) === null || _c === void 0 ? void 0 : _c.remove();
1364
+ transport.notifySubscription = undefined;
1365
+ if (previousNotifyTransactionId) {
1366
+ try {
1367
+ yield ((_d = this.blePlxManager) === null || _d === void 0 ? void 0 : _d.cancelTransaction(previousNotifyTransactionId));
1368
+ }
1369
+ catch (error) {
1370
+ Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] cancel notify after Protocol ${protocol} probe failed:`, (error === null || error === void 0 ? void 0 : error.message) || error);
1371
+ }
1372
+ }
1373
+ const monitorToken = this.nextMonitorToken;
1374
+ this.nextMonitorToken += 1;
1375
+ const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
1376
+ transport.monitorToken = monitorToken;
1377
+ transport.notifyTransactionId = notifyTransactionId;
1378
+ this.monitorTokens.set(uuid, monitorToken);
1379
+ transport.notifySubscription = this._monitorCharacteristic(transport.notifyCharacteristic, uuid, monitorToken, notifyTransactionId);
1380
+ if (reactNative.Platform.OS === 'ios') {
1381
+ yield new Promise(resolve => {
1382
+ setTimeout(resolve, IOS_NOTIFY_READY_DELAY_MS);
1383
+ });
1384
+ }
1385
+ });
1386
+ }
1387
+ probeProtocolV1(uuid) {
1388
+ return __awaiter(this, void 0, void 0, function* () {
1389
+ if (!this._messages) {
1390
+ return false;
1391
+ }
1392
+ try {
1393
+ this.deviceProtocol.set(uuid, 'V1');
1394
+ yield this.callProtocolV1(uuid, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
1395
+ return true;
1396
+ }
1397
+ catch (error) {
1398
+ this.clearProbeProtocol(uuid, 'V1');
1399
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 Initialize probe failed:', error);
1400
+ return false;
1401
+ }
1402
+ });
1403
+ }
1404
+ probeProtocolV2(uuid) {
1405
+ var _a;
1406
+ return __awaiter(this, void 0, void 0, function* () {
1407
+ if (!this._messages || !this._messagesV2) {
1408
+ return false;
1409
+ }
1410
+ this.deviceProtocol.set(uuid, 'V2');
1411
+ (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1412
+ const detected = yield transport.probeProtocolV2({
1413
+ call: (name, data, options) => this.callProtocolV2(uuid, name, data, options),
1414
+ timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT_MS,
1415
+ logger: Log,
1416
+ logPrefix: 'ProtocolV2 RN-BLE',
1417
+ onProbeFailed: () => {
1418
+ var _a;
1419
+ (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1420
+ this.resetProtocolV2Frames(uuid);
1421
+ },
1422
+ });
1423
+ if (!detected) {
1424
+ this.clearProbeProtocol(uuid, 'V2');
1425
+ }
1426
+ return detected;
1427
+ });
1428
+ }
1429
+ handleProtocolV2Notification(uuid, data) {
1430
+ var _a, _b, _c;
1431
+ try {
1432
+ if (!this.runPromise || ((_a = this.activeProtocolV2Call) === null || _a === void 0 ? void 0 : _a.uuid) !== uuid) {
1433
+ (_b = this.protocolV2Assemblers.get(uuid)) === null || _b === void 0 ? void 0 : _b.reset();
1434
+ this.resetProtocolV2Frames(uuid);
1435
+ return;
1436
+ }
1437
+ if (data.length === 0)
1438
+ return;
1439
+ const assembler = this.protocolV2Assemblers.get(uuid);
1440
+ if (!assembler)
1441
+ return;
1442
+ for (const frameData of assembler.drain(data)) {
1443
+ this.resolveProtocolV2Frame(uuid, frameData);
1444
+ }
1445
+ }
1446
+ catch (error) {
1447
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 notification error:', error);
1448
+ const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1449
+ (_c = this.runPromise) === null || _c === void 0 ? void 0 : _c.reject(notifyError);
1450
+ this.rejectAllProtocolV2Frames(notifyError);
1451
+ }
1452
+ }
1453
+ getProtocolV2FrameQueue(uuid) {
1454
+ let queue = this.protocolV2FrameQueues.get(uuid);
1455
+ if (!queue) {
1456
+ queue = [];
1457
+ this.protocolV2FrameQueues.set(uuid, queue);
1458
+ }
1459
+ return queue;
1460
+ }
1461
+ resolveProtocolV2Frame(uuid, frame) {
1462
+ const framePromise = this.protocolV2FramePromises.get(uuid);
1463
+ if (framePromise) {
1464
+ framePromise.resolve(frame);
1465
+ this.protocolV2FramePromises.delete(uuid);
1466
+ return;
1467
+ }
1468
+ this.getProtocolV2FrameQueue(uuid).push(frame);
1469
+ }
1470
+ rejectAllProtocolV2Frames(error) {
1471
+ this.protocolV2FrameQueues.clear();
1472
+ for (const framePromise of this.protocolV2FramePromises.values()) {
1473
+ framePromise.reject(error);
1474
+ }
1475
+ this.protocolV2FramePromises.clear();
1476
+ }
1477
+ resetProtocolV2Frames(uuid) {
1478
+ this.protocolV2FrameQueues.delete(uuid);
1479
+ this.protocolV2FramePromises.delete(uuid);
1480
+ }
1481
+ isActiveProtocolV2Call(uuid, token) {
1482
+ var _a;
1483
+ return ((_a = this.activeProtocolV2Call) === null || _a === void 0 ? void 0 : _a.uuid) === uuid && this.activeProtocolV2Call.token === token;
1484
+ }
1485
+ readProtocolV2Frame(uuid) {
1486
+ return __awaiter(this, void 0, void 0, function* () {
1487
+ const queuedFrame = this.getProtocolV2FrameQueue(uuid).shift();
1488
+ if (queuedFrame) {
1489
+ return queuedFrame;
1490
+ }
1491
+ const framePromise = hdShared.createDeferred();
1492
+ this.protocolV2FramePromises.set(uuid, framePromise);
1493
+ try {
1494
+ return yield framePromise.promise;
1495
+ }
1496
+ finally {
1497
+ if (this.protocolV2FramePromises.get(uuid) === framePromise) {
1498
+ this.protocolV2FramePromises.delete(uuid);
1499
+ }
1500
+ }
1501
+ });
1502
+ }
1503
+ writeProtocolV2Frame(transport, frame, options) {
1504
+ return __awaiter(this, void 0, void 0, function* () {
1505
+ const tuning = getProtocolV2BleTuning();
1506
+ const packetCapacity = resolveProtocolV2PacketCapacity({
1507
+ platform: reactNative.Platform.OS,
1508
+ iosPacketLength: tuning.iosPacketLength,
1509
+ androidPacketLength: tuning.androidPacketLength,
1510
+ mtu: reactNative.Platform.OS === 'android' ? transport.mtuSize : undefined,
1511
+ });
1512
+ const writeWithResponse = !!(options === null || options === void 0 ? void 0 : options.writeWithResponse) || (!!(options === null || options === void 0 ? void 0 : options.highVolume) && tuning.highVolumeWriteWithResponse);
1513
+ const writeMode = resolveBleWriteMode(transport.writeCharacteristic, writeWithResponse ? 'withResponse' : 'withoutResponse');
1514
+ const shouldThrottle = !!(options === null || options === void 0 ? void 0 : options.highVolume) && writeMode === 'withoutResponse';
1515
+ let packetsWritten = 0;
1516
+ try {
1517
+ for (let offset = 0; offset < frame.length; offset += packetCapacity) {
1518
+ const chunk = frame.slice(offset, offset + packetCapacity);
1519
+ const base64 = buffer.Buffer.from(chunk).toString('base64');
1520
+ if (writeMode === 'withResponse') {
1521
+ yield transport.writeCharacteristic.writeWithResponse(base64);
1522
+ }
1523
+ else {
1524
+ yield transport.writeCharacteristic.writeWithoutResponse(base64);
1525
+ }
1526
+ packetsWritten += 1;
1527
+ if (shouldThrottle &&
1528
+ packetsWritten % tuning.highVolumeWriteBurstSize === 0 &&
1529
+ offset + packetCapacity < frame.length) {
1530
+ yield delay(tuning.highVolumeWritePauseMs);
1531
+ }
1532
+ }
1533
+ if (shouldThrottle) {
1534
+ yield delay(tuning.highVolumeWriteFlushDelayMs);
1535
+ }
1536
+ }
1537
+ catch (error) {
1538
+ if ((options === null || options === void 0 ? void 0 : options.highVolume) && !writeWithResponse && packetsWritten === 0) {
1539
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume writeWithoutResponse failed before data was sent, fallback to writeWithResponse:', error);
1540
+ yield this.writeProtocolV2Frame(transport, frame, {
1541
+ highVolume: true,
1542
+ writeWithResponse: true,
1543
+ });
1544
+ return;
1545
+ }
1546
+ throw error;
1547
+ }
1548
+ });
1549
+ }
1550
+ callProtocolV2(uuid, name, data, options) {
1551
+ var _a, _b, _c, _d;
1552
+ return __awaiter(this, void 0, void 0, function* () {
1553
+ if (!this._messages || !this._messagesV2) {
1554
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
1555
+ }
1556
+ const forceRun = name === 'Initialize' || name === 'Cancel' || name === 'GetProtoVersion';
1557
+ if (this.runPromise) {
1558
+ if (!forceRun) {
1559
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportCallInProgress);
1560
+ }
1561
+ const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
1562
+ this.runPromise.reject(error);
1563
+ this.rejectAllProtocolV2Frames(error);
1564
+ this.runPromise = null;
1565
+ this.activeProtocolV2Call = null;
1566
+ }
1567
+ const transport$1 = this.getCachedTransport(uuid);
1568
+ const runPromise = hdShared.createDeferred();
1569
+ runPromise.promise.catch(() => undefined);
1570
+ this.runPromise = runPromise;
1571
+ const callToken = this.nextProtocolV2CallToken++;
1572
+ this.activeProtocolV2Call = { uuid, token: callToken };
1573
+ (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1574
+ this.resetProtocolV2Frames(uuid);
1575
+ let completed = false;
1576
+ 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 });
1577
+ const highVolumeWrite = transport.LogBlockCommand.has(name);
1578
+ if (highVolumeWrite) {
1579
+ const tuning = getProtocolV2BleTuning();
1580
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume write uses throttled writeWithoutResponse:', name, {
1581
+ packetCapacity: reactNative.Platform.OS === 'ios' ? tuning.iosPacketLength : tuning.androidPacketLength,
1582
+ burstSize: tuning.highVolumeWriteBurstSize,
1583
+ pauseMs: tuning.highVolumeWritePauseMs,
1584
+ flushDelayMs: tuning.highVolumeWriteFlushDelayMs,
1585
+ writeWithResponse: tuning.highVolumeWriteWithResponse,
1586
+ });
1587
+ }
1588
+ try {
1589
+ const session = new transport.ProtocolV2Session({
1590
+ schemas: {
1591
+ protocolV1: this._messages,
1592
+ protocolV2: this._messagesV2,
1593
+ },
1594
+ router: transport.PROTOCOL_V2_CHANNEL_BLE_UART,
1595
+ writeFrame: (frame) => __awaiter(this, void 0, void 0, function* () {
1596
+ yield this.writeProtocolV2Frame(transport$1, frame, {
1597
+ highVolume: highVolumeWrite,
1598
+ });
1599
+ }),
1600
+ readFrame: () => __awaiter(this, void 0, void 0, function* () {
1601
+ const rxFrame = yield this.readProtocolV2Frame(uuid);
1602
+ if (!(rxFrame instanceof Uint8Array)) {
1603
+ throw new Error('Protocol V2 response is not Uint8Array');
1604
+ }
1605
+ return rxFrame;
1606
+ }),
1607
+ logger: Log,
1608
+ logPrefix: 'ProtocolV2 RN-BLE',
1609
+ createTimeoutError: (_messageName, timeout) => hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, `BLE response timeout after ${timeout}ms for ${name}`),
1610
+ });
1611
+ const result = yield session.call(name, data, callOptions);
1612
+ completed = true;
1613
+ return result;
1614
+ }
1615
+ catch (e) {
1616
+ if (this.isActiveProtocolV2Call(uuid, callToken)) {
1617
+ (_c = this.protocolV2Assemblers.get(uuid)) === null || _c === void 0 ? void 0 : _c.reset();
1618
+ this.resetProtocolV2Frames(uuid);
1619
+ }
1620
+ Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] Protocol V2 call error:', e);
1621
+ throw e;
1622
+ }
1623
+ finally {
1624
+ if (this.isActiveProtocolV2Call(uuid, callToken)) {
1625
+ if (!completed) {
1626
+ (_d = this.protocolV2Assemblers.get(uuid)) === null || _d === void 0 ? void 0 : _d.reset();
1627
+ }
1628
+ this.resetProtocolV2Frames(uuid);
1629
+ this.activeProtocolV2Call = null;
1630
+ }
1631
+ if (this.runPromise === runPromise) {
1632
+ this.runPromise = null;
1633
+ }
1634
+ }
1635
+ });
1636
+ }
1637
+ getProtocolType(path) {
1638
+ return this.deviceProtocol.get(path);
1639
+ }
951
1640
  }
952
1641
 
953
- module.exports = ReactNativeBleTransport;
1642
+ exports.configureProtocolV2BleTuning = configureProtocolV2BleTuning;
1643
+ exports["default"] = ReactNativeBleTransport;
1644
+ exports.getProtocolV2BleTuning = getProtocolV2BleTuning;
1645
+ exports.resetProtocolV2BleTuning = resetProtocolV2BleTuning;