@onekeyfe/hd-transport-react-native 1.2.0-alpha.13 → 1.2.0-alpha.130

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
@@ -93,7 +93,8 @@ const onDeviceBondState = (bleMacAddress) => new Promise((resolve, reject) => {
93
93
 
94
94
  const IOS_PACKET_LENGTH = 128;
95
95
  const ANDROID_PACKET_LENGTH = 192;
96
- const ANDROID_DEFAULT_MTU = 23;
96
+ const IOS_PROTOCOL_V2_PACKET_LENGTH = 244;
97
+ const ANDROID_PROTOCOL_V2_PACKET_LENGTH = 244;
97
98
  const ClassicServiceUUID = '00000001-0000-1000-8000-00805f9b34fb';
98
99
  const OneKeyServices = {
99
100
  classic: {
@@ -117,36 +118,38 @@ const getInfosForServiceUuid = (serviceUuid, deviceType) => {
117
118
  return null;
118
119
  }
119
120
  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
+ const service = (_a = services[serviceUuid]) !== null && _a !== void 0 ? _a : Object.values(services).find(item => normalizeBleUuid(item.serviceUuid) === normalizedServiceUuid ||
122
+ hdShared.matchesKnownBleUuid(serviceUuid, hdShared.createKnownBleUuidAliases(item.serviceUuid)));
121
123
  if (!service) {
122
124
  return null;
123
125
  }
124
126
  return service;
125
127
  };
126
128
  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
129
  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)));
130
+ if (!left || !right)
131
+ return false;
132
+ return hdShared.matchesKnownBleUuid(left, hdShared.createKnownBleUuidAliases(right));
136
133
  };
137
134
 
138
135
  function hasWritableCapability(characteristic) {
139
136
  return !!(characteristic.isWritableWithResponse || characteristic.isWritableWithoutResponse);
140
137
  }
141
- function resolveProtocolV2PacketCapacity({ platform, iosPacketLength = IOS_PACKET_LENGTH, androidPacketLength = ANDROID_PACKET_LENGTH, mtu, }) {
142
- if (platform === 'ios') {
143
- return iosPacketLength;
144
- }
145
- if (platform === 'android') {
146
- const payloadLength = Math.max((mtu !== null && mtu !== void 0 ? mtu : ANDROID_DEFAULT_MTU) - 3, 1);
147
- return Math.min(androidPacketLength, payloadLength);
148
- }
149
- return androidPacketLength;
138
+ function resolveProtocolV2PacketCapacity({ platform, iosPacketLength = IOS_PROTOCOL_V2_PACKET_LENGTH, androidPacketLength = ANDROID_PROTOCOL_V2_PACKET_LENGTH, mtu, }) {
139
+ const negotiatedMtu = typeof mtu === 'number' && Number.isFinite(mtu) && mtu > 3 ? Math.floor(mtu) : 23;
140
+ const payloadLength = negotiatedMtu - 3;
141
+ const configuredPacketLength = platform === 'ios' ? iosPacketLength : androidPacketLength;
142
+ return Math.min(configuredPacketLength, payloadLength);
143
+ }
144
+ function shouldRefreshNegotiatedMtu(mtu) {
145
+ return typeof mtu !== 'number' || !Number.isFinite(mtu) || mtu <= 23;
146
+ }
147
+ function shouldWriteProtocolV2WithResponse({ platform, highThroughput, requestedWithResponse, characteristic, }) {
148
+ if (!characteristic.isWritableWithResponse)
149
+ return false;
150
+ if (!characteristic.isWritableWithoutResponse)
151
+ return true;
152
+ return requestedWithResponse === true || (platform === 'ios' && !highThroughput);
150
153
  }
151
154
 
152
155
  const timer = process.env.NODE_ENV === 'development'
@@ -203,59 +206,20 @@ const isHeaderChunk = (chunk) => {
203
206
  return false;
204
207
  };
205
208
 
206
- const Log$1 = bleLogger;
207
209
  class BleTransport {
208
210
  constructor(device, writeCharacteristic, notifyCharacteristic) {
209
211
  this.name = 'ReactNativeBleTransport';
210
- this.mtuSize = 23;
211
212
  this.id = device.id;
212
213
  this.device = device;
213
214
  this.writeCharacteristic = writeCharacteristic;
214
215
  this.notifyCharacteristic = notifyCharacteristic;
215
216
  }
216
- writeWithRetry(data, retryCount = BleTransport.MAX_RETRIES) {
217
+ writeWithRetry(data) {
217
218
  return __awaiter(this, void 0, void 0, function* () {
218
- try {
219
- yield this.writeCharacteristic.writeWithoutResponse(data);
220
- }
221
- catch (error) {
222
- Log$1 === null || Log$1 === void 0 ? void 0 : Log$1.debug(`Write retry attempt ${BleTransport.MAX_RETRIES - retryCount + 1}, error: ${error}`);
223
- if (retryCount > 0) {
224
- yield hdShared.wait(BleTransport.RETRY_DELAY);
225
- if (error.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected ||
226
- error.errorCode === reactNativeBlePlx.BleErrorCode.CharacteristicNotFound) {
227
- try {
228
- yield this.device.connect();
229
- yield this.device.discoverAllServicesAndCharacteristics();
230
- }
231
- catch (e) {
232
- Log$1 === null || Log$1 === void 0 ? void 0 : Log$1.debug(`Connect or discoverAllServicesAndCharacteristics error: ${e}`);
233
- }
234
- }
235
- else {
236
- Log$1 === null || Log$1 === void 0 ? void 0 : Log$1.debug(`writeCharacteristic error: ${error}`);
237
- }
238
- return this.writeWithRetry(data, retryCount - 1);
239
- }
240
- throw error;
241
- }
219
+ yield this.writeCharacteristic.writeWithoutResponse(data);
242
220
  });
243
221
  }
244
222
  }
245
- BleTransport.MAX_RETRIES = 5;
246
- BleTransport.RETRY_DELAY = 2000;
247
-
248
- function createTransportCallLog(name, protocol, data) {
249
- if (name === 'ResourceUpdate' || name === 'ResourceAck') {
250
- return {
251
- name,
252
- protocol,
253
- file_name: data.file_name,
254
- hash: data.hash,
255
- };
256
- }
257
- return { name, protocol };
258
- }
259
223
 
260
224
  const { check, ProtocolV1, parseConfigure } = transport__default["default"];
261
225
  const Log = bleLogger;
@@ -264,10 +228,35 @@ const FIRMWARE_UPLOAD_WRITE_BURST_SIZE = reactNative.Platform.OS === 'ios' ? 4 :
264
228
  const FIRMWARE_UPLOAD_WRITE_PAUSE_MS = reactNative.Platform.OS === 'ios' ? 8 : 10;
265
229
  const FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS = reactNative.Platform.OS === 'ios' ? 24 : 30;
266
230
  const FIRMWARE_UPLOAD_WRITE_MAX_RETRIES = 8;
267
- const FIRMWARE_UPLOAD_RECONNECT_RETRY_DELAY_MS = 2000;
268
231
  const ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH = 192;
269
232
  const FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY = reactNative.Platform.OS === 'ios' ? IOS_PACKET_LENGTH : ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH;
270
233
  const ANDROID_GATT_CONGESTED_STATUS = 143;
234
+ const isAsciiWhitespace = (code) => code === 0x09 ||
235
+ code === 0x0a ||
236
+ code === 0x0b ||
237
+ code === 0x0c ||
238
+ code === 0x0d ||
239
+ code === 0x20;
240
+ const hasGattCongestedStatus = (text) => {
241
+ let searchFrom = 0;
242
+ while (searchFrom < text.length) {
243
+ const statusIndex = text.indexOf('status', searchFrom);
244
+ if (statusIndex < 0)
245
+ return false;
246
+ let cursor = statusIndex + 'status'.length;
247
+ while (cursor < text.length && isAsciiWhitespace(text.charCodeAt(cursor)))
248
+ cursor += 1;
249
+ if (text[cursor] === ':' || text[cursor] === '=') {
250
+ cursor += 1;
251
+ while (cursor < text.length && isAsciiWhitespace(text.charCodeAt(cursor)))
252
+ cursor += 1;
253
+ }
254
+ if (text.startsWith(String(ANDROID_GATT_CONGESTED_STATUS), cursor))
255
+ return true;
256
+ searchFrom = statusIndex + 'status'.length;
257
+ }
258
+ return false;
259
+ };
271
260
  const delay = (ms) => new Promise(resolve => {
272
261
  setTimeout(resolve, ms);
273
262
  });
@@ -275,10 +264,6 @@ const getFirmwareUploadWriteRetryType = (error) => {
275
264
  if (!error || typeof error !== 'object')
276
265
  return null;
277
266
  const bleWriteError = error;
278
- if (bleWriteError.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected ||
279
- bleWriteError.errorCode === reactNativeBlePlx.BleErrorCode.CharacteristicNotFound) {
280
- return 'reconnectable';
281
- }
282
267
  if (bleWriteError.androidErrorCode === ANDROID_GATT_CONGESTED_STATUS ||
283
268
  bleWriteError.status === ANDROID_GATT_CONGESTED_STATUS) {
284
269
  return 'congested';
@@ -286,18 +271,23 @@ const getFirmwareUploadWriteRetryType = (error) => {
286
271
  const text = [bleWriteError.reason, bleWriteError.message, bleWriteError.name]
287
272
  .filter(value => typeof value === 'string')
288
273
  .join(' ');
289
- return /GATT_CONGESTED|status\s*[:=]?\s*143/.test(text) ? 'congested' : null;
274
+ return text.includes('GATT_CONGESTED') || hasGattCongestedStatus(text) ? 'congested' : null;
290
275
  };
291
276
  const resolveFirmwareUploadRetryDelay = (attempt, baseDelayMs = 200, maxDelayMs = 1200) => Math.min(baseDelayMs * Math.pow(2, attempt), maxDelayMs);
292
- const BLE_RESPONSE_TIMEOUT_MS = 30000;
293
- const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
277
+ const PROTOCOL_PROBE_TIMEOUT_MS = 3000;
294
278
  const PROTOCOL_V2_PROBE_TIMEOUT_MS = 10000;
295
- const DEVICE_SCAN_TIMEOUT_MS = 8000;
279
+ const BLE_WRITE_PACKET_TIMEOUT_MS = 10000;
280
+ const WEDGED_WRITE_MESSAGE = 'BLE write timeout after';
281
+ const isWedgedWriteError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleWriteCharacteristicError &&
282
+ typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' &&
283
+ error.message.startsWith(WEDGED_WRITE_MESSAGE);
284
+ const BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
285
+ const DEVICE_SCAN_TIMEOUT_MS = 3000;
296
286
  const IOS_NOTIFY_READY_DELAY_MS = 150;
297
287
  const ANDROID_NOTIFY_READY_DELAY_MS = 300;
298
288
  const DEFAULT_PROTOCOL_V2_BLE_TUNING = {
299
- iosPacketLength: IOS_PACKET_LENGTH,
300
- androidPacketLength: ANDROID_PACKET_LENGTH,
289
+ iosPacketLength: IOS_PROTOCOL_V2_PACKET_LENGTH,
290
+ androidPacketLength: ANDROID_PROTOCOL_V2_PACKET_LENGTH,
301
291
  };
302
292
  let protocolV2BleTuning = Object.assign({}, DEFAULT_PROTOCOL_V2_BLE_TUNING);
303
293
  const normalizePositiveInteger = (value, fallback) => {
@@ -326,19 +316,29 @@ function inferProtocolHintFromDeviceName(name) {
326
316
  function getDeviceDisplayName(device) {
327
317
  return (device === null || device === void 0 ? void 0 : device.name) || (device === null || device === void 0 ? void 0 : device.localName) || null;
328
318
  }
329
- function isGenericBleService(uuid) {
330
- return ['1800', '1801', '180a'].includes(getBleUuidKey(uuid));
331
- }
332
- function hasKnownOneKeyService(device) {
333
- var _a;
334
- return ((_a = device === null || device === void 0 ? void 0 : device.serviceUUIDs) !== null && _a !== void 0 ? _a : []).some(serviceUuid => getInfosForServiceUuid(serviceUuid, 'classic'));
335
- }
336
- const ANDROID_REQUEST_MTU = 256;
319
+ const IOS_REQUEST_MTU = 247;
320
+ const ANDROID_REQUEST_MTU = 517;
321
+ const BLE_MTU_REFRESH_RETRY_DELAY_MS = 200;
322
+ const ANDROID_HIGH_PRIORITY_IDLE_MS = 1000;
323
+ const getRequestedBleMtu = () => reactNative.Platform.OS === 'android' ? ANDROID_REQUEST_MTU : IOS_REQUEST_MTU;
324
+ const BLE_NATIVE_CONNECT_TIMEOUT_MS = 3000;
337
325
  const connectOptions = {
338
- requestMTU: ANDROID_REQUEST_MTU,
339
- timeout: 3000,
326
+ requestMTU: getRequestedBleMtu(),
327
+ timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
340
328
  refreshGatt: 'OnConnected',
341
329
  };
330
+ const fallbackConnectOptions = {
331
+ timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
332
+ };
333
+ const BLE_CONNECT_TIMEOUT_MS = BLE_NATIVE_CONNECT_TIMEOUT_MS * 2 + 2000;
334
+ const BLE_GATT_SETUP_TIMEOUT_MS = 10000;
335
+ const PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = 3;
336
+ const BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
337
+ const CONNECT_TIMEOUT_MESSAGE = 'BLE connect timeout after';
338
+ const isConnectTimeoutError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleConnectedError &&
339
+ typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' &&
340
+ error.message.startsWith(CONNECT_TIMEOUT_MESSAGE);
341
+ const isNativeOperationTimeoutError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === reactNativeBlePlx.BleErrorCode.OperationTimedOut;
342
342
  const tryToGetConfiguration = (device) => {
343
343
  if (!device || !device.serviceUUIDs)
344
344
  return null;
@@ -350,23 +350,25 @@ const tryToGetConfiguration = (device) => {
350
350
  return null;
351
351
  return infos;
352
352
  };
353
- const requestAndroidMtu = (device) => __awaiter(void 0, void 0, void 0, function* () {
354
- if (reactNative.Platform.OS !== 'android')
353
+ const requestNegotiatedMtu = (device, stage, attempt) => __awaiter(void 0, void 0, void 0, function* () {
354
+ if (reactNative.Platform.OS !== 'ios' && reactNative.Platform.OS !== 'android')
355
355
  return device;
356
356
  try {
357
- const mtuDevice = yield device.requestMTU(ANDROID_REQUEST_MTU);
358
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] MTU configured', {
359
- deviceId: device.id,
360
- requested: ANDROID_REQUEST_MTU,
361
- actual: mtuDevice.mtu,
362
- });
357
+ const mtuDevice = yield device.requestMTU(getRequestedBleMtu());
363
358
  return mtuDevice;
364
359
  }
365
360
  catch (error) {
366
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android MTU request failed:', error);
361
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] MTU refresh failed, continuing with current value', {
362
+ platform: reactNative.Platform.OS,
363
+ stage,
364
+ attempt,
365
+ actual: device.mtu,
366
+ error: error instanceof Error ? error.message : String(error),
367
+ });
367
368
  return device;
368
369
  }
369
370
  });
371
+ const resolveNegotiatedMtu = (device) => requestNegotiatedMtu(device, 'connected', 0);
370
372
  function remapError(error) {
371
373
  var _a;
372
374
  if (error instanceof reactNativeBlePlx.BleError) {
@@ -393,9 +395,15 @@ class ReactNativeBleTransport {
393
395
  this.stopped = false;
394
396
  this.scanTimeout = DEVICE_SCAN_TIMEOUT_MS;
395
397
  this.runPromise = null;
398
+ this.runPromiseDeviceId = null;
396
399
  this.firmwareUploadWriteRecoveryIds = new Set();
397
400
  this.deviceProtocol = new Map();
401
+ this.probingProtocols = new Map();
402
+ this.writeTimeoutCounts = new Map();
403
+ this.connectionSetupTimeoutCounts = new Map();
398
404
  this.deviceProtocolHints = new Map();
405
+ this.sessionProtocols = new Map();
406
+ this.protocolReprobeFailures = new Map();
399
407
  this.protocolV2Assemblers = new Map();
400
408
  this.protocolV2FrameQueues = new Map();
401
409
  this.protocolV2FramePromises = new Map();
@@ -416,12 +424,17 @@ class ReactNativeBleTransport {
416
424
  this.rejectProtocolV2Frames(uuid, new Error(reason));
417
425
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 link invalidated:', uuid, reason);
418
426
  if (reason.startsWith('Protocol V2 link-fatal error:')) {
419
- yield this.release(uuid, true);
427
+ yield this.releaseNative(uuid, true);
420
428
  }
421
429
  }),
422
430
  });
423
431
  this.monitorTokens = new Map();
432
+ this.disconnectEventTokens = new Map();
433
+ this.protocolV2HighVolumeLogSignatures = new Map();
434
+ this.androidHighPriorityDevices = new Set();
435
+ this.androidPriorityResetTimers = new Map();
424
436
  this.nextMonitorToken = 1;
437
+ this.lifecycleOperations = new Map();
425
438
  this.scanTimeout = (_a = options.scanTimeout) !== null && _a !== void 0 ? _a : DEVICE_SCAN_TIMEOUT_MS;
426
439
  }
427
440
  init(logger, emitter) {
@@ -434,10 +447,18 @@ class ReactNativeBleTransport {
434
447
  this._messages = messages;
435
448
  }
436
449
  configureProtocolV2(signedData) {
450
+ const configuration = typeof signedData === 'string' ? signedData : JSON.stringify(signedData);
451
+ if (this.protocolV2SchemaConfiguration === configuration) {
452
+ return;
453
+ }
454
+ const isReconfiguration = this.protocolV2SchemaConfiguration !== undefined;
437
455
  this._messagesV2 = parseConfigure(signedData);
438
- this.protocolV2Links
439
- .invalidateAllLinks('Protocol V2 schema reconfigured')
440
- .catch(error => Log === null || Log === void 0 ? void 0 : Log.debug('Protocol V2 schema link cleanup failed:', error));
456
+ this.protocolV2SchemaConfiguration = configuration;
457
+ if (isReconfiguration) {
458
+ this.protocolV2Links
459
+ .invalidateAllLinks('Protocol V2 schema reconfigured')
460
+ .catch(error => Log === null || Log === void 0 ? void 0 : Log.debug('Protocol V2 schema link cleanup failed:', error));
461
+ }
441
462
  }
442
463
  listen() {
443
464
  }
@@ -448,7 +469,6 @@ class ReactNativeBleTransport {
448
469
  return Promise.resolve(this.blePlxManager);
449
470
  }
450
471
  resolveCharacteristics(device) {
451
- var _a, _b, _c, _d;
452
472
  return __awaiter(this, void 0, void 0, function* () {
453
473
  yield device.discoverAllServicesAndCharacteristics();
454
474
  let infos = tryToGetConfiguration(device);
@@ -465,19 +485,11 @@ class ReactNativeBleTransport {
465
485
  }
466
486
  }
467
487
  }
468
- let fallbackServiceUuid;
469
488
  if (!infos) {
470
489
  const services = yield device.services();
471
490
  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));
472
- const knownService = services.find(service => getInfosForServiceUuid(service.uuid, 'classic'));
473
- const fallbackService = (_a = knownService !== null && knownService !== void 0 ? knownService : services.find(service => !isGenericBleService(service.uuid))) !== null && _a !== void 0 ? _a : services[0];
474
- if (fallbackService) {
475
- fallbackServiceUuid = fallbackService.uuid;
476
- characteristics = yield device.characteristicsForService(fallbackService.uuid);
477
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Using fallback BLE service:', fallbackService.uuid);
478
- }
479
491
  }
480
- if (!infos && !fallbackServiceUuid) {
492
+ if (!infos) {
481
493
  try {
482
494
  Log === null || Log === void 0 ? void 0 : Log.debug('cancel connection when service not found');
483
495
  yield device.cancelConnection();
@@ -487,9 +499,7 @@ class ReactNativeBleTransport {
487
499
  }
488
500
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
489
501
  }
490
- const serviceUuid = (_b = infos === null || infos === void 0 ? void 0 : infos.serviceUuid) !== null && _b !== void 0 ? _b : fallbackServiceUuid;
491
- const writeUuid = (_c = infos === null || infos === void 0 ? void 0 : infos.writeUuid) !== null && _c !== void 0 ? _c : '00000002-0000-1000-8000-00805f9b34fb';
492
- const notifyUuid = (_d = infos === null || infos === void 0 ? void 0 : infos.notifyUuid) !== null && _d !== void 0 ? _d : '00000003-0000-1000-8000-00805f9b34fb';
502
+ const { serviceUuid, writeUuid, notifyUuid } = infos;
493
503
  if (!serviceUuid) {
494
504
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
495
505
  }
@@ -530,8 +540,8 @@ class ReactNativeBleTransport {
530
540
  attachDisconnectSubscription(transport, device, uuid) {
531
541
  var _a;
532
542
  (_a = transport.disconnectSubscription) === null || _a === void 0 ? void 0 : _a.remove();
543
+ const { monitorToken } = transport;
533
544
  transport.disconnectSubscription = device.onDisconnected(() => {
534
- var _a;
535
545
  if (this.firmwareUploadWriteRecoveryIds.has(uuid)) {
536
546
  Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored during FirmwareUpload write recovery: ', uuid);
537
547
  return;
@@ -540,17 +550,16 @@ class ReactNativeBleTransport {
540
550
  Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored for stale transport: ', device === null || device === void 0 ? void 0 : device.id);
541
551
  return;
542
552
  }
553
+ if (this.monitorTokens.get(uuid) !== monitorToken) {
554
+ Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored for stale generation: ', device === null || device === void 0 ? void 0 : device.id);
555
+ return;
556
+ }
543
557
  try {
544
558
  Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect: ', device === null || device === void 0 ? void 0 : device.id);
545
- (_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit('device-disconnect', {
546
- name: device === null || device === void 0 ? void 0 : device.name,
547
- id: device === null || device === void 0 ? void 0 : device.id,
548
- connectId: device === null || device === void 0 ? void 0 : device.id,
549
- });
550
- if (this.runPromise) {
559
+ this.emitDeviceDisconnect(uuid, device === null || device === void 0 ? void 0 : device.name, monitorToken);
560
+ if (this.runPromise && this.runPromiseDeviceId === uuid) {
551
561
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError);
552
562
  this.runPromise.reject(error);
553
- this.rejectAllProtocolV2Frames(error);
554
563
  }
555
564
  }
556
565
  catch (e) {
@@ -561,6 +570,22 @@ class ReactNativeBleTransport {
561
570
  }
562
571
  });
563
572
  }
573
+ emitDeviceDisconnect(uuid, name, token) {
574
+ var _a;
575
+ if (token === undefined || this.disconnectEventTokens.get(uuid) === token) {
576
+ return;
577
+ }
578
+ if (this.monitorTokens.get(uuid) !== token) {
579
+ Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect event ignored for stale generation: ', uuid);
580
+ return;
581
+ }
582
+ this.disconnectEventTokens.set(uuid, token);
583
+ (_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit(transport.TRANSPORT_EVENT.DEVICE_DISCONNECT, {
584
+ name,
585
+ id: uuid,
586
+ connectId: uuid,
587
+ });
588
+ }
564
589
  reconnectFirmwareUploadTransport(uuid, transport) {
565
590
  var _a, _b;
566
591
  return __awaiter(this, void 0, void 0, function* () {
@@ -574,19 +599,19 @@ class ReactNativeBleTransport {
574
599
  const isConnected = yield device.isConnected().catch(() => false);
575
600
  if (!isConnected) {
576
601
  try {
577
- device = yield device.connect(connectOptions);
602
+ device = yield this.connectWithTimeout(uuid, () => device.connect(connectOptions));
578
603
  }
579
604
  catch (e) {
580
605
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
581
606
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
582
- device = yield device.connect();
607
+ device = yield this.connectWithTimeout(uuid, () => device.connect());
583
608
  }
584
609
  else if (e.errorCode !== reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
585
610
  throw e;
586
611
  }
587
612
  }
588
613
  }
589
- const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristics(device);
614
+ const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, device);
590
615
  transport.device = device;
591
616
  transport.writeCharacteristic = writeCharacteristic;
592
617
  transport.notifyCharacteristic = notifyCharacteristic;
@@ -634,7 +659,7 @@ class ReactNativeBleTransport {
634
659
  allowDuplicates: true,
635
660
  scanMode: reactNativeBlePlx.ScanMode.LowLatency,
636
661
  }, (error, device) => {
637
- var _a, _b, _c;
662
+ var _a, _b;
638
663
  if (error) {
639
664
  Log === null || Log === void 0 ? void 0 : Log.debug('ble scan error: ', error);
640
665
  if ([reactNativeBlePlx.BleErrorCode.BluetoothPoweredOff, reactNativeBlePlx.BleErrorCode.BluetoothInUnknownState].includes(error.errorCode)) {
@@ -655,9 +680,14 @@ class ReactNativeBleTransport {
655
680
  return;
656
681
  }
657
682
  const displayName = getDeviceDisplayName(device);
658
- 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) ||
659
- 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) ||
660
- hasKnownOneKeyService(device);
683
+ const isUnnamedIOSPeripheral = reactNative.Platform.OS === 'ios' && !(displayName === null || displayName === void 0 ? void 0 : displayName.trim());
684
+ const isOneKey = !isUnnamedIOSPeripheral &&
685
+ hdShared.isOnekeyBluetoothDevice({
686
+ id: device === null || device === void 0 ? void 0 : device.id,
687
+ name: device === null || device === void 0 ? void 0 : device.name,
688
+ localName: device === null || device === void 0 ? void 0 : device.localName,
689
+ serviceUuids: (_b = device === null || device === void 0 ? void 0 : device.serviceUUIDs) !== null && _b !== void 0 ? _b : getBluetoothServiceUuids(),
690
+ });
661
691
  if (isOneKey) {
662
692
  addDevice(device);
663
693
  }
@@ -672,10 +702,15 @@ class ReactNativeBleTransport {
672
702
  });
673
703
  getConnectedDeviceIds(reactNative.Platform.OS === 'ios' ? getBluetoothServiceUuids() : []).then(devices => {
674
704
  for (const device of devices) {
675
- const { serviceUUIDs } = device;
676
- const hasCachedServiceUuid = Boolean(serviceUUIDs === null || serviceUUIDs === void 0 ? void 0 : serviceUUIDs.length);
677
- const keepDevice = reactNative.Platform.OS === 'ios' || hasCachedServiceUuid;
678
- if (keepDevice) {
705
+ const localName = 'localName' in device && typeof device.localName === 'string'
706
+ ? device.localName
707
+ : null;
708
+ if (hdShared.isOnekeyBluetoothDevice({
709
+ id: device.id,
710
+ name: device.name,
711
+ localName,
712
+ serviceUuids: device.serviceUUIDs,
713
+ })) {
679
714
  Log === null || Log === void 0 ? void 0 : Log.debug('search connected peripheral: ', device.id);
680
715
  addDevice(device);
681
716
  }
@@ -705,13 +740,70 @@ class ReactNativeBleTransport {
705
740
  }));
706
741
  });
707
742
  }
743
+ installTransportForAcquire(uuid, device, characteristics) {
744
+ return __awaiter(this, void 0, void 0, function* () {
745
+ const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristicsWithTimeout(uuid, device));
746
+ const transport$1 = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
747
+ transport$1.mtuSize = typeof device.mtu === 'number' ? device.mtu : undefined;
748
+ const monitorToken = this.nextMonitorToken;
749
+ this.nextMonitorToken += 1;
750
+ const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
751
+ transport$1.monitorToken = monitorToken;
752
+ transport$1.notifyTransactionId = notifyTransactionId;
753
+ this.monitorTokens.set(uuid, monitorToken);
754
+ transport$1.notifySubscription = this._monitorCharacteristic(transport$1.notifyCharacteristic, uuid, monitorToken, notifyTransactionId);
755
+ transportCache[uuid] = transport$1;
756
+ this.protocolV2HighVolumeLogSignatures.set(uuid, new Set());
757
+ this.protocolV2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES));
758
+ if (reactNative.Platform.OS === 'ios') {
759
+ yield new Promise(resolve => {
760
+ setTimeout(resolve, IOS_NOTIFY_READY_DELAY_MS);
761
+ });
762
+ }
763
+ else if (reactNative.Platform.OS === 'android') {
764
+ yield delay(ANDROID_NOTIFY_READY_DELAY_MS);
765
+ }
766
+ const initialMtu = transport$1.mtuSize;
767
+ let refreshAttempts = 0;
768
+ if ((reactNative.Platform.OS === 'ios' || reactNative.Platform.OS === 'android') &&
769
+ shouldRefreshNegotiatedMtu(transport$1.mtuSize)) {
770
+ refreshAttempts += 1;
771
+ let refreshedDevice = yield requestNegotiatedMtu(transport$1.device, 'servicesAndNotifyReady', 1);
772
+ transport$1.device = refreshedDevice;
773
+ transport$1.mtuSize =
774
+ typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport$1.mtuSize;
775
+ if (shouldRefreshNegotiatedMtu(transport$1.mtuSize)) {
776
+ yield delay(BLE_MTU_REFRESH_RETRY_DELAY_MS);
777
+ refreshAttempts += 1;
778
+ refreshedDevice = yield requestNegotiatedMtu(transport$1.device, 'servicesAndNotifyReady', 2);
779
+ transport$1.device = refreshedDevice;
780
+ transport$1.mtuSize =
781
+ typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport$1.mtuSize;
782
+ }
783
+ }
784
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE MTU ready', {
785
+ platform: reactNative.Platform.OS,
786
+ requested: getRequestedBleMtu(),
787
+ initial: initialMtu,
788
+ actual: transport$1.mtuSize,
789
+ refreshAttempts,
790
+ });
791
+ return transport$1;
792
+ });
793
+ }
708
794
  acquire(input) {
709
- var _a, _b;
710
795
  return __awaiter(this, void 0, void 0, function* () {
711
- const { uuid, forceCleanRunPromise, expectedProtocol } = input;
796
+ const { uuid } = input;
712
797
  if (!uuid) {
713
798
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleRequiredUUID);
714
799
  }
800
+ return this.runLifecycleOperation(uuid, () => this.acquireUnlocked(input));
801
+ });
802
+ }
803
+ acquireUnlocked(input) {
804
+ var _a, _b;
805
+ return __awaiter(this, void 0, void 0, function* () {
806
+ const { uuid, forceCleanRunPromise, expectedProtocol } = input;
715
807
  const cachedTransport = transportCache[uuid];
716
808
  if (cachedTransport) {
717
809
  const cachedProtocol = this.deviceProtocol.get(uuid);
@@ -723,14 +815,14 @@ class ReactNativeBleTransport {
723
815
  return { uuid, protocolType: cachedProtocol };
724
816
  }
725
817
  Log === null || Log === void 0 ? void 0 : Log.debug('transport not reusable, will release: ', uuid);
726
- yield this.release(uuid, true);
818
+ yield this.releaseUnlocked(uuid, true);
727
819
  }
728
820
  let device = null;
729
821
  if (forceCleanRunPromise && this.runPromise) {
730
822
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
731
823
  this.runPromise.reject(error);
732
- this.rejectAllProtocolV2Frames(error);
733
824
  this.runPromise = null;
825
+ this.runPromiseDeviceId = null;
734
826
  Log === null || Log === void 0 ? void 0 : Log.debug('Force clean Bluetooth run promise, forceCleanRunPromise: ', forceCleanRunPromise);
735
827
  }
736
828
  const blePlxManager = yield this.getPlxManager();
@@ -763,14 +855,17 @@ class ReactNativeBleTransport {
763
855
  if (!device) {
764
856
  Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device: ', uuid);
765
857
  try {
766
- device = yield blePlxManager.connectToDevice(uuid, connectOptions);
858
+ device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, connectOptions));
767
859
  }
768
860
  catch (e) {
769
861
  Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device has error: ', e);
862
+ if (isConnectTimeoutError(e)) {
863
+ throw e;
864
+ }
770
865
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
771
866
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
772
867
  Log === null || Log === void 0 ? void 0 : Log.debug('first try to reconnect without params');
773
- device = yield blePlxManager.connectToDevice(uuid);
868
+ device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, fallbackConnectOptions));
774
869
  }
775
870
  else if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
776
871
  Log === null || Log === void 0 ? void 0 : Log.debug('device already connected');
@@ -786,23 +881,27 @@ class ReactNativeBleTransport {
786
881
  }
787
882
  if (!(yield device.isConnected())) {
788
883
  Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device: ', uuid);
884
+ const disconnectedDevice = device;
789
885
  try {
790
- device = yield device.connect(connectOptions);
886
+ device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(connectOptions));
791
887
  }
792
888
  catch (e) {
793
889
  Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device has error: ', e);
890
+ if (isConnectTimeoutError(e)) {
891
+ throw e;
892
+ }
794
893
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
795
894
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
796
895
  Log === null || Log === void 0 ? void 0 : Log.debug('second try to reconnect without params');
797
896
  try {
798
- device = yield device.connect();
897
+ device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
799
898
  }
800
899
  catch (e) {
801
900
  Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect error: ', e);
802
901
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
803
902
  Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect');
804
- yield device.cancelConnection();
805
- device = yield device.connect();
903
+ yield disconnectedDevice.cancelConnection();
904
+ device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
806
905
  }
807
906
  }
808
907
  }
@@ -811,44 +910,35 @@ class ReactNativeBleTransport {
811
910
  }
812
911
  }
813
912
  }
814
- device = yield requestAndroidMtu(device);
815
- const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristics(device);
913
+ device = yield resolveNegotiatedMtu(device);
914
+ const acquiredDevice = device;
915
+ const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, acquiredDevice);
816
916
  const protocolHint = expectedProtocol
817
917
  ? undefined
818
- : (_a = this.deviceProtocolHints.get(uuid)) !== null && _a !== void 0 ? _a : inferProtocolHintFromDeviceName(getDeviceDisplayName(device));
819
- yield this.release(uuid, true);
918
+ : (_b = (_a = input.protocolHint) !== null && _a !== void 0 ? _a : this.deviceProtocolHints.get(uuid)) !== null && _b !== void 0 ? _b : inferProtocolHintFromDeviceName(getDeviceDisplayName(acquiredDevice));
919
+ yield this.releaseUnlocked(uuid, true);
820
920
  if (protocolHint) {
821
921
  this.deviceProtocolHints.set(uuid, protocolHint);
822
922
  }
823
- const transport$1 = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
824
- if (reactNative.Platform.OS === 'android') {
825
- transport$1.mtuSize = typeof device.mtu === 'number' ? device.mtu : transport$1.mtuSize;
826
- }
827
- const monitorToken = this.nextMonitorToken;
828
- this.nextMonitorToken += 1;
829
- const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
830
- transport$1.monitorToken = monitorToken;
831
- transport$1.notifyTransactionId = notifyTransactionId;
832
- this.monitorTokens.set(uuid, monitorToken);
833
- transport$1.notifySubscription = this._monitorCharacteristic(transport$1.notifyCharacteristic, uuid, monitorToken, notifyTransactionId);
834
- transportCache[uuid] = transport$1;
835
- this.protocolV2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler());
836
- if (reactNative.Platform.OS === 'ios') {
837
- yield new Promise(resolve => {
838
- setTimeout(resolve, IOS_NOTIFY_READY_DELAY_MS);
839
- });
923
+ yield this.installTransportForAcquire(uuid, acquiredDevice, {
924
+ writeCharacteristic,
925
+ notifyCharacteristic,
926
+ });
927
+ try {
928
+ const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint, () => __awaiter(this, void 0, void 0, function* () {
929
+ yield this.installTransportForAcquire(uuid, acquiredDevice);
930
+ }));
931
+ const currentTransport = transportCache[uuid];
932
+ if (!currentTransport) {
933
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
934
+ }
935
+ this.attachDisconnectSubscription(currentTransport, currentTransport.device, uuid);
936
+ return { uuid, protocolType };
840
937
  }
841
- else if (reactNative.Platform.OS === 'android') {
842
- yield delay(ANDROID_NOTIFY_READY_DELAY_MS);
938
+ catch (error) {
939
+ yield this.releaseUnlocked(uuid, true);
940
+ throw error;
843
941
  }
844
- const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint);
845
- (_b = this.emitter) === null || _b === void 0 ? void 0 : _b.emit('device-connect', {
846
- name: device.name,
847
- id: device.id,
848
- connectId: device.id,
849
- });
850
- this.attachDisconnectSubscription(transport$1, device, uuid);
851
- return { uuid, protocolType };
852
942
  });
853
943
  }
854
944
  _monitorCharacteristic(characteristic, uuid, monitorToken, notifyTransactionId) {
@@ -867,7 +957,7 @@ class ReactNativeBleTransport {
867
957
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
868
958
  return;
869
959
  }
870
- if (this.deviceProtocol.get(uuid) === 'V2') {
960
+ if (this.getActiveProtocol(uuid) === 'V2') {
871
961
  let errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
872
962
  if ((_a = error.reason) === null || _a === void 0 ? void 0 : _a.includes('The connection has timed out unexpectedly')) {
873
963
  errorCode = hdShared.HardwareErrorCode.BleTimeoutError;
@@ -885,7 +975,7 @@ class ReactNativeBleTransport {
885
975
  this.rejectProtocolV2Frames(uuid, hdShared.ERRORS.TypedError(errorCode));
886
976
  return;
887
977
  }
888
- if (this.runPromise) {
978
+ if (this.runPromise && this.runPromiseDeviceId === uuid) {
889
979
  let ERROR = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
890
980
  if ((_h = error.reason) === null || _h === void 0 ? void 0 : _h.includes('The connection has timed out unexpectedly')) {
891
981
  ERROR = hdShared.HardwareErrorCode.BleTimeoutError;
@@ -900,13 +990,11 @@ class ReactNativeBleTransport {
900
990
  ((_p = error.reason) === null || _p === void 0 ? void 0 : _p.includes('notify change failed for device'))) {
901
991
  const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure);
902
992
  this.runPromise.reject(notifyError);
903
- this.rejectAllProtocolV2Frames(notifyError);
904
993
  Log === null || Log === void 0 ? void 0 : Log.debug(`${hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure} ${error.message} ${error.reason}`);
905
994
  return;
906
995
  }
907
996
  const notifyError = hdShared.ERRORS.TypedError(ERROR);
908
997
  this.runPromise.reject(notifyError);
909
- this.rejectAllProtocolV2Frames(notifyError);
910
998
  Log === null || Log === void 0 ? void 0 : Log.debug(': monitor notify error, and has unreleased Promise', Error);
911
999
  }
912
1000
  return;
@@ -920,7 +1008,7 @@ class ReactNativeBleTransport {
920
1008
  }
921
1009
  try {
922
1010
  const data = buffer.Buffer.from(c.value, 'base64');
923
- const protocol = this.deviceProtocol.get(uuid);
1011
+ const protocol = this.getActiveProtocol(uuid);
924
1012
  if (!protocol) {
925
1013
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor data ignored before protocol detection: ', uuid);
926
1014
  return;
@@ -940,16 +1028,18 @@ class ReactNativeBleTransport {
940
1028
  const value = buffer.Buffer.from(buffer$1);
941
1029
  bufferLength = 0;
942
1030
  buffer$1 = [];
943
- (_q = this.runPromise) === null || _q === void 0 ? void 0 : _q.resolve(value.toString('hex'));
1031
+ if (this.runPromiseDeviceId === uuid) {
1032
+ (_q = this.runPromise) === null || _q === void 0 ? void 0 : _q.resolve(value.toString('hex'));
1033
+ }
944
1034
  }
945
1035
  }
946
1036
  catch (error) {
947
1037
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor data error: ', error);
948
1038
  const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
949
- if (this.deviceProtocol.get(uuid) === 'V2') {
1039
+ if (this.getActiveProtocol(uuid) === 'V2') {
950
1040
  this.rejectProtocolV2Frames(uuid, notifyError);
951
1041
  }
952
- else {
1042
+ else if (this.runPromiseDeviceId === uuid) {
953
1043
  (_r = this.runPromise) === null || _r === void 0 ? void 0 : _r.reject(notifyError);
954
1044
  }
955
1045
  }
@@ -957,15 +1047,26 @@ class ReactNativeBleTransport {
957
1047
  return subscription;
958
1048
  }
959
1049
  release(uuid, onclose = false) {
1050
+ return __awaiter(this, void 0, void 0, function* () {
1051
+ return this.runLifecycleOperation(uuid, () => this.releaseUnlocked(uuid, onclose));
1052
+ });
1053
+ }
1054
+ releaseUnlocked(uuid, onclose = false) {
1055
+ return __awaiter(this, void 0, void 0, function* () {
1056
+ yield this.protocolV2Links.invalidateLink(uuid, 'React Native BLE transport released');
1057
+ return this.releaseNative(uuid, onclose);
1058
+ });
1059
+ }
1060
+ releaseNative(uuid, onclose = false) {
960
1061
  var _a, _b, _c, _d, _e, _f, _g;
961
1062
  return __awaiter(this, void 0, void 0, function* () {
962
1063
  const transport = transportCache[uuid];
963
- yield this.protocolV2Links.invalidateLink(uuid, 'React Native BLE transport released');
964
- if (this.runPromise) {
1064
+ if (this.runPromise && this.runPromiseDeviceId === uuid) {
965
1065
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
966
1066
  this.runPromise.reject(error);
967
1067
  this.runPromise = null;
968
- this.rejectAllProtocolV2Frames(error);
1068
+ this.runPromiseDeviceId = null;
1069
+ this.rejectProtocolV2Frames(uuid, error);
969
1070
  }
970
1071
  else {
971
1072
  this.resetProtocolV2Frames(uuid);
@@ -975,6 +1076,7 @@ class ReactNativeBleTransport {
975
1076
  this.resetProtocolV2Frames(uuid);
976
1077
  return Promise.resolve(true);
977
1078
  }
1079
+ yield this.restoreAndroidConnectionPriority(uuid, transport);
978
1080
  if (transport) {
979
1081
  if (this.monitorTokens.get(uuid) === transport.monitorToken) {
980
1082
  this.monitorTokens.delete(uuid);
@@ -995,7 +1097,9 @@ class ReactNativeBleTransport {
995
1097
  }
996
1098
  delete transportCache[uuid];
997
1099
  }
1100
+ this.protocolV2HighVolumeLogSignatures.delete(uuid);
998
1101
  this.deviceProtocol.delete(uuid);
1102
+ this.probingProtocols.delete(uuid);
999
1103
  (_f = this.protocolV2Assemblers.get(uuid)) === null || _f === void 0 ? void 0 : _f.reset();
1000
1104
  this.protocolV2Assemblers.delete(uuid);
1001
1105
  this.resetProtocolV2Frames(uuid);
@@ -1010,6 +1114,10 @@ class ReactNativeBleTransport {
1010
1114
  }
1011
1115
  post(session, name, data) {
1012
1116
  return __awaiter(this, void 0, void 0, function* () {
1117
+ if (this.getProtocolType(session) === 'V2') {
1118
+ yield this.protocolV2Links.sendFlowControl(session, () => this.createProtocolV2Adapter(session), name, data);
1119
+ return;
1120
+ }
1013
1121
  yield this.call(session, name, data);
1014
1122
  });
1015
1123
  }
@@ -1025,7 +1133,6 @@ class ReactNativeBleTransport {
1025
1133
  if (!protocol) {
1026
1134
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${uuid}`);
1027
1135
  }
1028
- Log === null || Log === void 0 ? void 0 : Log.debug('transport call', createTransportCallLog(name, protocol, data));
1029
1136
  if (protocol === 'V2') {
1030
1137
  return this.callProtocolV2(uuid, name, data, options);
1031
1138
  }
@@ -1044,7 +1151,19 @@ class ReactNativeBleTransport {
1044
1151
  const transport = this.getCachedTransport(uuid);
1045
1152
  const runPromise = hdShared.createDeferred();
1046
1153
  runPromise.promise.catch(() => undefined);
1154
+ const supersededRunPromise = this.runPromise;
1155
+ if (supersededRunPromise) {
1156
+ supersededRunPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise));
1157
+ }
1047
1158
  this.runPromise = runPromise;
1159
+ this.runPromiseDeviceId = uuid;
1160
+ const releaseOwnershipIfCurrent = () => {
1161
+ if (this.runPromise === runPromise) {
1162
+ this.runPromise = null;
1163
+ this.runPromiseDeviceId = null;
1164
+ }
1165
+ };
1166
+ const isCurrentOwner = () => this.runPromise === runPromise;
1048
1167
  const messages = this._messages;
1049
1168
  const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
1050
1169
  let timeout;
@@ -1065,6 +1184,9 @@ class ReactNativeBleTransport {
1065
1184
  }
1066
1185
  catch (e) {
1067
1186
  onError(e);
1187
+ if (isWedgedWriteError(e)) {
1188
+ throw e;
1189
+ }
1068
1190
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1069
1191
  }
1070
1192
  }
@@ -1092,6 +1214,9 @@ class ReactNativeBleTransport {
1092
1214
  }
1093
1215
  catch (e) {
1094
1216
  onError(e);
1217
+ if (isWedgedWriteError(e)) {
1218
+ throw e;
1219
+ }
1095
1220
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1096
1221
  }
1097
1222
  }
@@ -1102,8 +1227,8 @@ class ReactNativeBleTransport {
1102
1227
  });
1103
1228
  }
1104
1229
  if (name === 'EmmcFileWrite') {
1105
- yield writeChunkedData(buffers, data => transport.writeWithRetry(data), e => {
1106
- this.runPromise = null;
1230
+ yield writeChunkedData(buffers, data => this.writeBlePacket(uuid, data, payload => transport.writeWithRetry(payload), isCurrentOwner), e => {
1231
+ releaseOwnershipIfCurrent();
1107
1232
  Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
1108
1233
  });
1109
1234
  }
@@ -1119,7 +1244,7 @@ class ReactNativeBleTransport {
1119
1244
  let attempt = 0;
1120
1245
  while (true) {
1121
1246
  try {
1122
- yield transport.writeCharacteristic.writeWithoutResponse(data);
1247
+ yield this.writeBlePacket(uuid, data, payload => transport.writeWithRetry(payload), isCurrentOwner);
1123
1248
  return;
1124
1249
  }
1125
1250
  catch (error) {
@@ -1127,36 +1252,18 @@ class ReactNativeBleTransport {
1127
1252
  if (!retryType || attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
1128
1253
  throw error;
1129
1254
  }
1130
- const shouldReconnect = retryType === 'reconnectable';
1131
- const delayMs = shouldReconnect
1132
- ? FIRMWARE_UPLOAD_RECONNECT_RETRY_DELAY_MS
1133
- : resolveFirmwareUploadRetryDelay(attempt);
1255
+ const delayMs = resolveFirmwareUploadRetryDelay(attempt);
1134
1256
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] FirmwareUpload write retry:', {
1135
1257
  attempt: attempt + 1,
1136
1258
  delayMs,
1137
- reconnect: shouldReconnect,
1138
1259
  error,
1139
1260
  });
1140
- if (shouldReconnect) {
1141
- this.firmwareUploadWriteRecoveryIds.add(uuid);
1142
- }
1143
1261
  yield delay(delayMs);
1144
1262
  attempt += 1;
1145
- if (shouldReconnect) {
1146
- try {
1147
- yield this.reconnectFirmwareUploadTransport(uuid, transport);
1148
- }
1149
- catch (e) {
1150
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] FirmwareUpload reconnect error:', e);
1151
- if (attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
1152
- throw e;
1153
- }
1154
- }
1155
- }
1156
1263
  }
1157
1264
  }
1158
1265
  }), e => {
1159
- this.runPromise = null;
1266
+ releaseOwnershipIfCurrent();
1160
1267
  Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
1161
1268
  });
1162
1269
  }
@@ -1164,11 +1271,17 @@ class ReactNativeBleTransport {
1164
1271
  for (const o of buffers) {
1165
1272
  const outData = o.toString('base64');
1166
1273
  try {
1167
- yield transport.writeCharacteristic.writeWithoutResponse(outData);
1274
+ const shouldUseWriteWithResponse = reactNative.Platform.OS === 'ios' && transport.writeCharacteristic.isWritableWithResponse;
1275
+ yield this.writeBlePacket(uuid, outData, payload => shouldUseWriteWithResponse
1276
+ ? transport.writeCharacteristic.writeWithResponse(payload)
1277
+ : transport.writeCharacteristic.writeWithoutResponse(payload), isCurrentOwner);
1168
1278
  }
1169
1279
  catch (e) {
1170
1280
  Log === null || Log === void 0 ? void 0 : Log.debug('writeCharacteristic write error: ', e);
1171
- this.runPromise = null;
1281
+ releaseOwnershipIfCurrent();
1282
+ if (isWedgedWriteError(e)) {
1283
+ throw e;
1284
+ }
1172
1285
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected) {
1173
1286
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded);
1174
1287
  }
@@ -1201,12 +1314,19 @@ class ReactNativeBleTransport {
1201
1314
  return check.call(jsonData);
1202
1315
  }
1203
1316
  catch (e) {
1204
- if (name === 'Initialize' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS) {
1205
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 Initialize probe call failed:', e);
1317
+ if (name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS) {
1318
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe call failed:', e);
1206
1319
  }
1207
1320
  else {
1208
1321
  Log === null || Log === void 0 ? void 0 : Log.error('call error: ', e);
1209
1322
  }
1323
+ const isProbeTimeout = name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS;
1324
+ const isStaleCall = this.runPromise !== runPromise;
1325
+ if (!isProbeTimeout &&
1326
+ !isStaleCall &&
1327
+ (e === null || e === void 0 ? void 0 : e.errorCode) === hdShared.HardwareErrorCode.BleTimeoutError) {
1328
+ yield this.disconnect(uuid);
1329
+ }
1210
1330
  throw e;
1211
1331
  }
1212
1332
  finally {
@@ -1214,6 +1334,7 @@ class ReactNativeBleTransport {
1214
1334
  clearTimeout(timeout);
1215
1335
  if (this.runPromise === runPromise) {
1216
1336
  this.runPromise = null;
1337
+ this.runPromiseDeviceId = null;
1217
1338
  }
1218
1339
  }
1219
1340
  });
@@ -1222,10 +1343,16 @@ class ReactNativeBleTransport {
1222
1343
  this.stopped = true;
1223
1344
  }
1224
1345
  disconnect(session) {
1346
+ return __awaiter(this, void 0, void 0, function* () {
1347
+ return this.runLifecycleOperation(session, () => this.disconnectUnlocked(session));
1348
+ });
1349
+ }
1350
+ disconnectUnlocked(session) {
1225
1351
  var _a, _b, _c, _d, _e;
1226
1352
  return __awaiter(this, void 0, void 0, function* () {
1227
1353
  yield this.protocolV2Links.invalidateLink(session, 'React Native BLE transport disconnected');
1228
1354
  const transport = transportCache[session];
1355
+ const monitorToken = (_a = transport === null || transport === void 0 ? void 0 : transport.monitorToken) !== null && _a !== void 0 ? _a : this.monitorTokens.get(session);
1229
1356
  if (transport === null || transport === void 0 ? void 0 : transport.disconnectSubscription) {
1230
1357
  try {
1231
1358
  Log === null || Log === void 0 ? void 0 : Log.debug('disconnect: removing disconnect subscription');
@@ -1238,7 +1365,7 @@ class ReactNativeBleTransport {
1238
1365
  }
1239
1366
  if (transport === null || transport === void 0 ? void 0 : transport.notifySubscription) {
1240
1367
  try {
1241
- Log === null || Log === void 0 ? void 0 : Log.debug('disconnect: removing notify subscription, characteristic: ', (_a = transport.notifyCharacteristic) === null || _a === void 0 ? void 0 : _a.uuid);
1368
+ Log === null || Log === void 0 ? void 0 : Log.debug('disconnect: removing notify subscription, characteristic: ', (_b = transport.notifyCharacteristic) === null || _b === void 0 ? void 0 : _b.uuid);
1242
1369
  transport.notifySubscription.remove();
1243
1370
  transport.notifySubscription = undefined;
1244
1371
  }
@@ -1248,7 +1375,7 @@ class ReactNativeBleTransport {
1248
1375
  }
1249
1376
  if (session) {
1250
1377
  try {
1251
- yield ((_b = this.blePlxManager) === null || _b === void 0 ? void 0 : _b.cancelTransaction(session));
1378
+ yield ((_c = this.blePlxManager) === null || _c === void 0 ? void 0 : _c.cancelTransaction(session));
1252
1379
  }
1253
1380
  catch (e) {
1254
1381
  Log === null || Log === void 0 ? void 0 : Log.debug('resetSession: cancel transaction error (ignored): ', (e === null || e === void 0 ? void 0 : e.message) || e);
@@ -1263,7 +1390,7 @@ class ReactNativeBleTransport {
1263
1390
  }
1264
1391
  }
1265
1392
  try {
1266
- yield ((_c = this.blePlxManager) === null || _c === void 0 ? void 0 : _c.cancelDeviceConnection(session));
1393
+ yield ((_d = this.blePlxManager) === null || _d === void 0 ? void 0 : _d.cancelDeviceConnection(session));
1267
1394
  }
1268
1395
  catch (e) {
1269
1396
  Log === null || Log === void 0 ? void 0 : Log.debug('resetSession: manager.cancelDeviceConnection error (ignored): ', (e === null || e === void 0 ? void 0 : e.message) || e);
@@ -1272,26 +1399,136 @@ class ReactNativeBleTransport {
1272
1399
  delete transportCache[session];
1273
1400
  }
1274
1401
  this.deviceProtocol.delete(session);
1402
+ this.probingProtocols.delete(session);
1275
1403
  this.deviceProtocolHints.delete(session);
1404
+ this.sessionProtocols.delete(session);
1405
+ this.protocolReprobeFailures.delete(session);
1276
1406
  this.protocolV2Assemblers.delete(session);
1277
1407
  this.resetProtocolV2Frames(session);
1278
1408
  try {
1279
- (_d = this.emitter) === null || _d === void 0 ? void 0 : _d.emit('device-disconnect', {
1280
- name: (_e = transport === null || transport === void 0 ? void 0 : transport.device) === null || _e === void 0 ? void 0 : _e.name,
1281
- id: session,
1282
- connectId: session,
1283
- });
1409
+ this.emitDeviceDisconnect(session, (_e = transport === null || transport === void 0 ? void 0 : transport.device) === null || _e === void 0 ? void 0 : _e.name, monitorToken);
1284
1410
  }
1285
1411
  catch (e) {
1286
1412
  Log === null || Log === void 0 ? void 0 : Log.error('resetSession: emit disconnect event error: ', e);
1287
1413
  }
1414
+ if (monitorToken !== undefined && this.monitorTokens.get(session) === monitorToken) {
1415
+ this.monitorTokens.delete(session);
1416
+ }
1288
1417
  yield new Promise(resolve => setTimeout(() => resolve(), 100));
1289
1418
  });
1290
1419
  }
1420
+ runLifecycleOperation(uuid, operation) {
1421
+ var _a;
1422
+ return __awaiter(this, void 0, void 0, function* () {
1423
+ const previousOperation = (_a = this.lifecycleOperations.get(uuid)) !== null && _a !== void 0 ? _a : Promise.resolve();
1424
+ let completeOperation;
1425
+ const operationGate = new Promise(resolve => {
1426
+ completeOperation = resolve;
1427
+ });
1428
+ const operationTail = previousOperation.catch(() => undefined).then(() => operationGate);
1429
+ this.lifecycleOperations.set(uuid, operationTail);
1430
+ yield previousOperation.catch(() => undefined);
1431
+ try {
1432
+ return yield operation();
1433
+ }
1434
+ finally {
1435
+ completeOperation();
1436
+ if (this.lifecycleOperations.get(uuid) === operationTail) {
1437
+ this.lifecycleOperations.delete(uuid);
1438
+ }
1439
+ }
1440
+ });
1441
+ }
1291
1442
  cancel() {
1292
1443
  Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native transport cancel');
1293
1444
  if (this.runPromise) ;
1294
1445
  this.runPromise = null;
1446
+ this.runPromiseDeviceId = null;
1447
+ }
1448
+ connectWithTimeout(uuid, connect) {
1449
+ return __awaiter(this, void 0, void 0, function* () {
1450
+ let timer;
1451
+ let timedOut = false;
1452
+ const pending = connect();
1453
+ pending.catch(() => undefined);
1454
+ try {
1455
+ const result = yield Promise.race([
1456
+ pending,
1457
+ new Promise((_, reject) => {
1458
+ timer = setTimeout(() => {
1459
+ timedOut = true;
1460
+ reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `BLE connect timeout after ${BLE_CONNECT_TIMEOUT_MS}ms for ${uuid}`));
1461
+ }, BLE_CONNECT_TIMEOUT_MS);
1462
+ }),
1463
+ ]);
1464
+ return result;
1465
+ }
1466
+ catch (error) {
1467
+ if (timedOut || isNativeOperationTimeoutError(error)) {
1468
+ this.abandonStalledConnection(uuid, timedOut ? 'connect-backstop' : 'connect-native');
1469
+ }
1470
+ throw error;
1471
+ }
1472
+ finally {
1473
+ if (timer)
1474
+ clearTimeout(timer);
1475
+ }
1476
+ });
1477
+ }
1478
+ resolveCharacteristicsWithTimeout(uuid, device) {
1479
+ return __awaiter(this, void 0, void 0, function* () {
1480
+ let timer;
1481
+ let timedOut = false;
1482
+ const pending = this.resolveCharacteristics(device);
1483
+ pending.catch(() => undefined);
1484
+ try {
1485
+ const result = yield Promise.race([
1486
+ pending,
1487
+ new Promise((_, reject) => {
1488
+ timer = setTimeout(() => {
1489
+ timedOut = true;
1490
+ reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `BLE GATT setup timeout after ${BLE_GATT_SETUP_TIMEOUT_MS}ms for ${uuid}`));
1491
+ }, BLE_GATT_SETUP_TIMEOUT_MS);
1492
+ }),
1493
+ ]);
1494
+ this.connectionSetupTimeoutCounts.delete(uuid);
1495
+ return result;
1496
+ }
1497
+ catch (error) {
1498
+ if (timedOut || isNativeOperationTimeoutError(error)) {
1499
+ this.abandonStalledConnection(uuid, timedOut ? 'gatt-backstop' : 'gatt-native');
1500
+ }
1501
+ throw error;
1502
+ }
1503
+ finally {
1504
+ if (timer)
1505
+ clearTimeout(timer);
1506
+ }
1507
+ });
1508
+ }
1509
+ abandonStalledConnection(uuid, stage) {
1510
+ var _a, _b;
1511
+ const timeouts = ((_a = this.connectionSetupTimeoutCounts.get(uuid)) !== null && _a !== void 0 ? _a : 0) + 1;
1512
+ this.connectionSetupTimeoutCounts.set(uuid, timeouts);
1513
+ Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE setup timed out:', uuid, {
1514
+ stage,
1515
+ setupTimeoutsSinceSuccess: timeouts,
1516
+ });
1517
+ (_b = this.blePlxManager) === null || _b === void 0 ? void 0 : _b.cancelDeviceConnection(uuid).catch(() => {
1518
+ });
1519
+ const stalled = transportCache[uuid];
1520
+ if (stalled) {
1521
+ delete transportCache[uuid];
1522
+ }
1523
+ this.deviceProtocol.delete(uuid);
1524
+ this.probingProtocols.delete(uuid);
1525
+ this.protocolV2Assemblers.delete(uuid);
1526
+ this.resetProtocolV2Frames(uuid);
1527
+ if (timeouts >= BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD) {
1528
+ Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE setup wedged repeatedly, resetting BLE manager');
1529
+ this.resetPlxManager();
1530
+ this.connectionSetupTimeoutCounts.delete(uuid);
1531
+ }
1295
1532
  }
1296
1533
  getCachedTransport(uuid) {
1297
1534
  const transport = transportCache[uuid];
@@ -1300,22 +1537,118 @@ class ReactNativeBleTransport {
1300
1537
  }
1301
1538
  return transport;
1302
1539
  }
1540
+ writeBlePacket(uuid, data, write, isCurrentOwner) {
1541
+ return __awaiter(this, void 0, void 0, function* () {
1542
+ let timer;
1543
+ let timedOut = false;
1544
+ try {
1545
+ yield Promise.race([
1546
+ write(data),
1547
+ new Promise((_, reject) => {
1548
+ timer = setTimeout(() => {
1549
+ timedOut = true;
1550
+ reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError, `BLE write timeout after ${BLE_WRITE_PACKET_TIMEOUT_MS}ms`));
1551
+ }, BLE_WRITE_PACKET_TIMEOUT_MS);
1552
+ }),
1553
+ ]);
1554
+ this.writeTimeoutCounts.delete(uuid);
1555
+ }
1556
+ catch (error) {
1557
+ if (timedOut) {
1558
+ if (isCurrentOwner && !isCurrentOwner()) {
1559
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] stale BLE write timed out, link kept:', uuid);
1560
+ }
1561
+ else {
1562
+ this.tearDownWedgedLink(uuid);
1563
+ }
1564
+ }
1565
+ throw error;
1566
+ }
1567
+ finally {
1568
+ if (timer)
1569
+ clearTimeout(timer);
1570
+ }
1571
+ });
1572
+ }
1573
+ tearDownWedgedLink(uuid) {
1574
+ var _a;
1575
+ const timeouts = ((_a = this.writeTimeoutCounts.get(uuid)) !== null && _a !== void 0 ? _a : 0) + 1;
1576
+ this.writeTimeoutCounts.set(uuid, timeouts);
1577
+ Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE write timed out, tearing down link:', uuid, {
1578
+ consecutiveWriteTimeouts: timeouts,
1579
+ });
1580
+ const wedged = transportCache[uuid];
1581
+ this.disconnect(uuid).catch(error => {
1582
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] wedged link teardown failed (ignored):', error);
1583
+ });
1584
+ if (wedged && transportCache[uuid] === wedged) {
1585
+ delete transportCache[uuid];
1586
+ }
1587
+ this.deviceProtocol.delete(uuid);
1588
+ this.probingProtocols.delete(uuid);
1589
+ this.protocolV2Assemblers.delete(uuid);
1590
+ this.resetProtocolV2Frames(uuid);
1591
+ if (timeouts >= BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD) {
1592
+ Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE writes wedged repeatedly, resetting BLE manager');
1593
+ this.resetPlxManager();
1594
+ this.writeTimeoutCounts.delete(uuid);
1595
+ }
1596
+ }
1597
+ resetPlxManager() {
1598
+ const manager = this.blePlxManager;
1599
+ this.blePlxManager = undefined;
1600
+ Object.keys(transportCache).forEach(key => {
1601
+ delete transportCache[key];
1602
+ });
1603
+ this.deviceProtocol.clear();
1604
+ this.probingProtocols.clear();
1605
+ this.sessionProtocols.clear();
1606
+ this.protocolReprobeFailures.clear();
1607
+ this.writeTimeoutCounts.clear();
1608
+ this.connectionSetupTimeoutCounts.clear();
1609
+ this.monitorTokens.clear();
1610
+ this.protocolV2Assemblers.clear();
1611
+ try {
1612
+ manager === null || manager === void 0 ? void 0 : manager.destroy();
1613
+ }
1614
+ catch (error) {
1615
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE manager destroy failed (ignored):', error);
1616
+ }
1617
+ }
1303
1618
  createProtocolMismatchError(expected) {
1304
1619
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
1305
1620
  }
1306
1621
  createProtocolDetectionError() {
1307
- return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1 Initialize or Protocol V2 Ping');
1622
+ return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1 GetFeatures or Protocol V2 Ping');
1308
1623
  }
1309
1624
  clearProbeProtocol(uuid, protocol) {
1625
+ if (this.probingProtocols.get(uuid) === protocol) {
1626
+ this.probingProtocols.delete(uuid);
1627
+ }
1310
1628
  if (this.deviceProtocol.get(uuid) === protocol) {
1311
1629
  this.deviceProtocol.delete(uuid);
1312
1630
  }
1313
1631
  }
1314
- detectProtocol(uuid, expectedProtocol, protocolHint) {
1632
+ getActiveProtocol(uuid) {
1633
+ var _a;
1634
+ return (_a = this.deviceProtocol.get(uuid)) !== null && _a !== void 0 ? _a : this.probingProtocols.get(uuid);
1635
+ }
1636
+ detectProtocol(uuid, expectedProtocol, protocolHint, rebuildTransport) {
1637
+ var _a;
1315
1638
  return __awaiter(this, void 0, void 0, function* () {
1639
+ if (reactNative.Platform.OS === 'ios' && expectedProtocol === 'V1') {
1640
+ this.deviceProtocol.set(uuid, expectedProtocol);
1641
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol selected', {
1642
+ deviceId: uuid,
1643
+ protocol: expectedProtocol,
1644
+ source: 'expected',
1645
+ });
1646
+ return expectedProtocol;
1647
+ }
1316
1648
  if (expectedProtocol === 'V1') {
1317
1649
  if (yield this.probeProtocolV1(uuid)) {
1318
1650
  this.deviceProtocol.set(uuid, 'V1');
1651
+ this.sessionProtocols.set(uuid, 'V1');
1319
1652
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1320
1653
  deviceId: uuid,
1321
1654
  protocol: 'V1',
@@ -1326,23 +1659,41 @@ class ReactNativeBleTransport {
1326
1659
  throw this.createProtocolMismatchError(expectedProtocol);
1327
1660
  }
1328
1661
  if (expectedProtocol === 'V2') {
1329
- this.deviceProtocol.set(uuid, 'V2');
1330
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1331
- deviceId: uuid,
1332
- protocol: 'V2',
1333
- source: 'expected',
1334
- });
1335
- return 'V2';
1662
+ if (yield this.probeProtocolV2(uuid)) {
1663
+ this.deviceProtocol.set(uuid, 'V2');
1664
+ this.sessionProtocols.set(uuid, 'V2');
1665
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1666
+ deviceId: uuid,
1667
+ protocol: 'V2',
1668
+ source: 'expected',
1669
+ });
1670
+ return 'V2';
1671
+ }
1672
+ throw this.createProtocolMismatchError(expectedProtocol);
1336
1673
  }
1337
- const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
1674
+ const sessionProtocol = this.sessionProtocols.get(uuid);
1675
+ const reprobeFailures = (_a = this.protocolReprobeFailures.get(uuid)) !== null && _a !== void 0 ? _a : 0;
1676
+ const fullProbeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
1677
+ const trustSessionProtocol = sessionProtocol !== undefined &&
1678
+ !protocolHint &&
1679
+ reprobeFailures < PROTOCOL_REPROBE_FALLBACK_ATTEMPTS;
1680
+ const probeOrder = trustSessionProtocol ? [sessionProtocol] : fullProbeOrder;
1338
1681
  for (let i = 0; i < probeOrder.length; i += 1) {
1339
1682
  const protocol = probeOrder[i];
1340
1683
  if (i > 0) {
1341
1684
  yield this.resetProbeStateAfterProtocolProbe(uuid, probeOrder[i - 1]);
1685
+ if (!transportCache[uuid]) {
1686
+ if (!rebuildTransport) {
1687
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
1688
+ }
1689
+ yield rebuildTransport();
1690
+ }
1342
1691
  }
1343
1692
  const detected = protocol === 'V1' ? yield this.probeProtocolV1(uuid) : yield this.probeProtocolV2(uuid);
1344
1693
  if (detected) {
1345
1694
  this.deviceProtocol.set(uuid, protocol);
1695
+ this.sessionProtocols.set(uuid, protocol);
1696
+ this.protocolReprobeFailures.delete(uuid);
1346
1697
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1347
1698
  deviceId: uuid,
1348
1699
  protocol,
@@ -1351,7 +1702,14 @@ class ReactNativeBleTransport {
1351
1702
  return protocol;
1352
1703
  }
1353
1704
  }
1705
+ if (trustSessionProtocol) {
1706
+ this.protocolReprobeFailures.set(uuid, reprobeFailures + 1);
1707
+ }
1708
+ else {
1709
+ this.protocolReprobeFailures.delete(uuid);
1710
+ }
1354
1711
  this.deviceProtocol.delete(uuid);
1712
+ this.probingProtocols.delete(uuid);
1355
1713
  throw this.createProtocolDetectionError();
1356
1714
  });
1357
1715
  }
@@ -1403,13 +1761,17 @@ class ReactNativeBleTransport {
1403
1761
  return false;
1404
1762
  }
1405
1763
  try {
1406
- this.deviceProtocol.set(uuid, 'V1');
1407
- yield this.callProtocolV1(uuid, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
1764
+ this.probingProtocols.set(uuid, 'V1');
1765
+ yield this.callProtocolV1(uuid, 'GetFeatures', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
1766
+ this.probingProtocols.delete(uuid);
1408
1767
  return true;
1409
1768
  }
1410
1769
  catch (error) {
1411
1770
  this.clearProbeProtocol(uuid, 'V1');
1412
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 Initialize probe failed:', error);
1771
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe failed:', error);
1772
+ if (isWedgedWriteError(error)) {
1773
+ throw error;
1774
+ }
1413
1775
  return false;
1414
1776
  }
1415
1777
  });
@@ -1420,7 +1782,7 @@ class ReactNativeBleTransport {
1420
1782
  if (!this._messages || !this._messagesV2) {
1421
1783
  return false;
1422
1784
  }
1423
- this.deviceProtocol.set(uuid, 'V2');
1785
+ this.probingProtocols.set(uuid, 'V2');
1424
1786
  (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1425
1787
  const detected = yield transport.probeProtocolV2({
1426
1788
  call: (name, data, options) => this.callProtocolV2(uuid, name, data, options),
@@ -1436,6 +1798,9 @@ class ReactNativeBleTransport {
1436
1798
  if (!detected) {
1437
1799
  this.clearProbeProtocol(uuid, 'V2');
1438
1800
  }
1801
+ else {
1802
+ this.probingProtocols.delete(uuid);
1803
+ }
1439
1804
  return detected;
1440
1805
  });
1441
1806
  }
@@ -1478,16 +1843,8 @@ class ReactNativeBleTransport {
1478
1843
  }
1479
1844
  this.getProtocolV2FrameQueue(uuid).push(frame);
1480
1845
  }
1481
- rejectAllProtocolV2Frames(error) {
1482
- this.protocolV2FrameQueues.clear();
1483
- for (const framePromise of this.protocolV2FramePromises.values()) {
1484
- framePromise.reject(error);
1485
- }
1486
- this.protocolV2FramePromises.clear();
1487
- }
1488
1846
  resetProtocolV2Frames(uuid) {
1489
- this.protocolV2FrameQueues.delete(uuid);
1490
- this.protocolV2FramePromises.delete(uuid);
1847
+ this.rejectProtocolV2Frames(uuid, new Error(`Protocol V2 frame state reset for ${uuid}`));
1491
1848
  }
1492
1849
  rejectProtocolV2Frames(uuid, error) {
1493
1850
  this.protocolV2FrameQueues.delete(uuid);
@@ -1515,20 +1872,70 @@ class ReactNativeBleTransport {
1515
1872
  }
1516
1873
  });
1517
1874
  }
1518
- writeProtocolV2Frame(transport, frame) {
1875
+ writeProtocolV2Packet(uuid, transport, base64, context, assertCurrentGeneration) {
1876
+ return __awaiter(this, void 0, void 0, function* () {
1877
+ const shouldUseWriteWithResponse = shouldWriteProtocolV2WithResponse({
1878
+ platform: reactNative.Platform.OS,
1879
+ highThroughput: context.highThroughput,
1880
+ requestedWithResponse: context.writeWithResponse,
1881
+ characteristic: transport.writeCharacteristic,
1882
+ });
1883
+ let attempt = 0;
1884
+ for (;;) {
1885
+ assertCurrentGeneration();
1886
+ if (context.signal.aborted) {
1887
+ throw new Error(`Protocol V2 BLE write aborted for ${context.messageName}`);
1888
+ }
1889
+ try {
1890
+ yield this.writeBlePacket(uuid, base64, payload => shouldUseWriteWithResponse
1891
+ ? transport.writeCharacteristic.writeWithResponse(payload)
1892
+ : transport.writeCharacteristic.writeWithoutResponse(payload), () => {
1893
+ try {
1894
+ assertCurrentGeneration();
1895
+ return !context.signal.aborted;
1896
+ }
1897
+ catch (_a) {
1898
+ return false;
1899
+ }
1900
+ });
1901
+ assertCurrentGeneration();
1902
+ return;
1903
+ }
1904
+ catch (error) {
1905
+ if (getFirmwareUploadWriteRetryType(error) !== 'congested' ||
1906
+ attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
1907
+ throw error;
1908
+ }
1909
+ const delayMs = resolveFirmwareUploadRetryDelay(attempt);
1910
+ attempt += 1;
1911
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 congested write retry:', {
1912
+ name: context.messageName,
1913
+ attempt,
1914
+ delayMs,
1915
+ });
1916
+ yield delay(delayMs);
1917
+ }
1918
+ }
1919
+ });
1920
+ }
1921
+ writeProtocolV2Frame(uuid, transport$1, frame, context, assertCurrentGeneration) {
1519
1922
  return __awaiter(this, void 0, void 0, function* () {
1520
1923
  const tuning = getProtocolV2BleTuning();
1521
1924
  const packetCapacity = resolveProtocolV2PacketCapacity({
1522
1925
  platform: reactNative.Platform.OS,
1523
1926
  iosPacketLength: tuning.iosPacketLength,
1524
1927
  androidPacketLength: tuning.androidPacketLength,
1525
- mtu: reactNative.Platform.OS === 'android' ? transport.mtuSize : undefined,
1928
+ mtu: transport$1.mtuSize,
1929
+ });
1930
+ yield transport.writeProtocolV2BleFrame({
1931
+ frame,
1932
+ packetCapacity,
1933
+ assertActive: assertCurrentGeneration,
1934
+ signal: context.signal,
1935
+ abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
1936
+ wait: delay,
1937
+ writePacket: packet => this.writeProtocolV2Packet(uuid, transport$1, buffer.Buffer.from(packet).toString('base64'), context, assertCurrentGeneration),
1526
1938
  });
1527
- for (let offset = 0; offset < frame.length; offset += packetCapacity) {
1528
- const chunk = frame.slice(offset, offset + packetCapacity);
1529
- const base64 = buffer.Buffer.from(chunk).toString('base64');
1530
- yield transport.writeCharacteristic.writeWithoutResponse(base64);
1531
- }
1532
1939
  });
1533
1940
  }
1534
1941
  callProtocolV2(uuid, name, data, options) {
@@ -1537,15 +1944,40 @@ class ReactNativeBleTransport {
1537
1944
  if (!this._messages || !this._messagesV2) {
1538
1945
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
1539
1946
  }
1540
- const callOptions = Object.assign(Object.assign({}, options), { timeoutMs: (_a = options === null || options === void 0 ? void 0 : options.timeoutMs) !== null && _a !== void 0 ? _a : BLE_RESPONSE_TIMEOUT_MS });
1541
- const highVolumeWrite = transport.LogBlockCommand.has(name);
1542
- if (highVolumeWrite) {
1947
+ const callOptions = options;
1948
+ const highThroughputWrite = transport.isProtocolV2HighThroughputCall(name);
1949
+ if (highThroughputWrite) {
1950
+ yield this.ensureProtocolV2HighThroughputMtu(uuid);
1543
1951
  const tuning = getProtocolV2BleTuning();
1544
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume write configured', {
1545
- name,
1546
- writeMode: 'withoutResponse',
1547
- packetCapacity: reactNative.Platform.OS === 'ios' ? tuning.iosPacketLength : tuning.androidPacketLength,
1952
+ const currentTransport = this.getCachedTransport(uuid);
1953
+ const writeWithResponse = shouldWriteProtocolV2WithResponse({
1954
+ platform: reactNative.Platform.OS,
1955
+ highThroughput: true,
1956
+ requestedWithResponse: options === null || options === void 0 ? void 0 : options.writeWithResponse,
1957
+ characteristic: currentTransport.writeCharacteristic,
1958
+ });
1959
+ const packetCapacity = resolveProtocolV2PacketCapacity({
1960
+ platform: reactNative.Platform.OS,
1961
+ iosPacketLength: tuning.iosPacketLength,
1962
+ androidPacketLength: tuning.androidPacketLength,
1963
+ mtu: currentTransport.mtuSize,
1548
1964
  });
1965
+ const writeMode = writeWithResponse ? 'withResponse' : 'withoutResponse';
1966
+ const logSignature = `${name}:${writeMode}:${String(currentTransport.mtuSize)}:${packetCapacity}`;
1967
+ const loggedSignatures = (_a = this.protocolV2HighVolumeLogSignatures.get(uuid)) !== null && _a !== void 0 ? _a : new Set();
1968
+ if (!loggedSignatures.has(logSignature)) {
1969
+ loggedSignatures.add(logSignature);
1970
+ this.protocolV2HighVolumeLogSignatures.set(uuid, loggedSignatures);
1971
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume write configured', {
1972
+ name,
1973
+ writeMode,
1974
+ reportedMtu: currentTransport.mtuSize,
1975
+ packetCapacity,
1976
+ });
1977
+ }
1978
+ }
1979
+ if (highThroughputWrite) {
1980
+ yield this.enableAndroidHighConnectionPriority(uuid);
1549
1981
  }
1550
1982
  try {
1551
1983
  return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, callOptions);
@@ -1554,6 +1986,85 @@ class ReactNativeBleTransport {
1554
1986
  Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] Protocol V2 call error:', e);
1555
1987
  throw e;
1556
1988
  }
1989
+ finally {
1990
+ if (highThroughputWrite) {
1991
+ this.scheduleAndroidBalancedConnectionPriority(uuid);
1992
+ }
1993
+ }
1994
+ });
1995
+ }
1996
+ ensureProtocolV2HighThroughputMtu(uuid) {
1997
+ return __awaiter(this, void 0, void 0, function* () {
1998
+ const transport = this.getCachedTransport(uuid);
1999
+ if (!shouldRefreshNegotiatedMtu(transport.mtuSize))
2000
+ return;
2001
+ const refreshedDevice = yield requestNegotiatedMtu(transport.device, 'highThroughput', 1);
2002
+ transport.device = refreshedDevice;
2003
+ transport.mtuSize =
2004
+ typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport.mtuSize;
2005
+ if (shouldRefreshNegotiatedMtu(transport.mtuSize)) {
2006
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `Protocol V2 high-throughput BLE MTU unavailable: ${String(transport.mtuSize)}`);
2007
+ }
2008
+ });
2009
+ }
2010
+ clearAndroidPriorityResetTimer(uuid) {
2011
+ const timerId = this.androidPriorityResetTimers.get(uuid);
2012
+ if (timerId !== undefined) {
2013
+ clearTimeout(timerId);
2014
+ this.androidPriorityResetTimers.delete(uuid);
2015
+ }
2016
+ }
2017
+ enableAndroidHighConnectionPriority(uuid) {
2018
+ return __awaiter(this, void 0, void 0, function* () {
2019
+ if (reactNative.Platform.OS !== 'android')
2020
+ return;
2021
+ this.clearAndroidPriorityResetTimer(uuid);
2022
+ if (this.androidHighPriorityDevices.has(uuid))
2023
+ return;
2024
+ const transport = transportCache[uuid];
2025
+ if (!transport)
2026
+ return;
2027
+ try {
2028
+ transport.device = yield transport.device.requestConnectionPriority(reactNativeBlePlx.ConnectionPriority.High);
2029
+ this.androidHighPriorityDevices.add(uuid);
2030
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE connection priority changed', {
2031
+ priority: 'high',
2032
+ });
2033
+ }
2034
+ catch (error) {
2035
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE high priority request failed', {
2036
+ error: error instanceof Error ? error.message : String(error),
2037
+ });
2038
+ }
2039
+ });
2040
+ }
2041
+ scheduleAndroidBalancedConnectionPriority(uuid) {
2042
+ if (reactNative.Platform.OS !== 'android' || !this.androidHighPriorityDevices.has(uuid))
2043
+ return;
2044
+ this.clearAndroidPriorityResetTimer(uuid);
2045
+ const timerId = setTimeout(() => {
2046
+ this.androidPriorityResetTimers.delete(uuid);
2047
+ this.restoreAndroidConnectionPriority(uuid, transportCache[uuid]).catch(error => Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE priority restore failed', error));
2048
+ }, ANDROID_HIGH_PRIORITY_IDLE_MS);
2049
+ this.androidPriorityResetTimers.set(uuid, timerId);
2050
+ }
2051
+ restoreAndroidConnectionPriority(uuid, transport) {
2052
+ return __awaiter(this, void 0, void 0, function* () {
2053
+ this.clearAndroidPriorityResetTimer(uuid);
2054
+ if (reactNative.Platform.OS !== 'android' || !this.androidHighPriorityDevices.delete(uuid) || !transport) {
2055
+ return;
2056
+ }
2057
+ try {
2058
+ transport.device = yield transport.device.requestConnectionPriority(reactNativeBlePlx.ConnectionPriority.Balanced);
2059
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE connection priority changed', {
2060
+ priority: 'balanced',
2061
+ });
2062
+ }
2063
+ catch (error) {
2064
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE balanced priority request failed', {
2065
+ error: error instanceof Error ? error.message : String(error),
2066
+ });
2067
+ }
1557
2068
  });
1558
2069
  }
1559
2070
  createProtocolV2Adapter(uuid) {
@@ -1574,10 +2085,10 @@ class ReactNativeBleTransport {
1574
2085
  (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1575
2086
  this.resetProtocolV2Frames(uuid);
1576
2087
  },
1577
- writeFrame: (frame) => __awaiter(this, void 0, void 0, function* () {
2088
+ writeFrame: (frame, context) => __awaiter(this, void 0, void 0, function* () {
1578
2089
  assertCurrentGeneration();
1579
2090
  const currentTransport = this.getCachedTransport(uuid);
1580
- yield this.writeProtocolV2Frame(currentTransport, frame);
2091
+ yield this.writeProtocolV2Frame(uuid, currentTransport, frame, context, assertCurrentGeneration);
1581
2092
  }),
1582
2093
  readFrame: () => __awaiter(this, void 0, void 0, function* () {
1583
2094
  assertCurrentGeneration();
@@ -1598,11 +2109,18 @@ class ReactNativeBleTransport {
1598
2109
  };
1599
2110
  }
1600
2111
  getProtocolType(path) {
1601
- return this.deviceProtocol.get(path);
2112
+ return this.getActiveProtocol(path);
1602
2113
  }
1603
2114
  }
1604
2115
 
2116
+ exports.BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD;
2117
+ exports.BLE_CONNECT_TIMEOUT_MS = BLE_CONNECT_TIMEOUT_MS;
2118
+ exports.BLE_GATT_SETUP_TIMEOUT_MS = BLE_GATT_SETUP_TIMEOUT_MS;
2119
+ exports.BLE_WRITE_PACKET_TIMEOUT_MS = BLE_WRITE_PACKET_TIMEOUT_MS;
2120
+ exports.BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD;
2121
+ exports.PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = PROTOCOL_REPROBE_FALLBACK_ATTEMPTS;
1605
2122
  exports.configureProtocolV2BleTuning = configureProtocolV2BleTuning;
1606
2123
  exports["default"] = ReactNativeBleTransport;
2124
+ exports.getFirmwareUploadWriteRetryType = getFirmwareUploadWriteRetryType;
1607
2125
  exports.getProtocolV2BleTuning = getProtocolV2BleTuning;
1608
2126
  exports.resetProtocolV2BleTuning = resetProtocolV2BleTuning;