@onekeyfe/hd-transport-react-native 1.2.0-alpha.14 → 1.2.0-alpha.140

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,24 @@ 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 BLE_NATIVE_TEARDOWN_TIMEOUT_MS = 3000;
281
+ const WEDGED_WRITE_MESSAGE = 'BLE write timeout after';
282
+ const isWedgedWriteError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleWriteCharacteristicError &&
283
+ typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' &&
284
+ error.message.startsWith(WEDGED_WRITE_MESSAGE);
285
+ const BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
286
+ const DEVICE_SCAN_TIMEOUT_MS = 3000;
296
287
  const IOS_NOTIFY_READY_DELAY_MS = 150;
297
288
  const ANDROID_NOTIFY_READY_DELAY_MS = 300;
298
289
  const DEFAULT_PROTOCOL_V2_BLE_TUNING = {
299
- iosPacketLength: IOS_PACKET_LENGTH,
300
- androidPacketLength: ANDROID_PACKET_LENGTH,
290
+ iosPacketLength: IOS_PROTOCOL_V2_PACKET_LENGTH,
291
+ androidPacketLength: ANDROID_PROTOCOL_V2_PACKET_LENGTH,
301
292
  };
302
293
  let protocolV2BleTuning = Object.assign({}, DEFAULT_PROTOCOL_V2_BLE_TUNING);
303
294
  const normalizePositiveInteger = (value, fallback) => {
@@ -326,19 +317,29 @@ function inferProtocolHintFromDeviceName(name) {
326
317
  function getDeviceDisplayName(device) {
327
318
  return (device === null || device === void 0 ? void 0 : device.name) || (device === null || device === void 0 ? void 0 : device.localName) || null;
328
319
  }
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;
320
+ const IOS_REQUEST_MTU = 247;
321
+ const ANDROID_REQUEST_MTU = 517;
322
+ const BLE_MTU_REFRESH_RETRY_DELAY_MS = 200;
323
+ const ANDROID_HIGH_PRIORITY_IDLE_MS = 1000;
324
+ const getRequestedBleMtu = () => reactNative.Platform.OS === 'android' ? ANDROID_REQUEST_MTU : IOS_REQUEST_MTU;
325
+ const BLE_NATIVE_CONNECT_TIMEOUT_MS = 3000;
337
326
  const connectOptions = {
338
- requestMTU: ANDROID_REQUEST_MTU,
339
- timeout: 3000,
327
+ requestMTU: getRequestedBleMtu(),
328
+ timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
340
329
  refreshGatt: 'OnConnected',
341
330
  };
331
+ const fallbackConnectOptions = {
332
+ timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
333
+ };
334
+ const BLE_CONNECT_TIMEOUT_MS = BLE_NATIVE_CONNECT_TIMEOUT_MS * 2 + 2000;
335
+ const BLE_GATT_SETUP_TIMEOUT_MS = 10000;
336
+ const PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = 3;
337
+ const BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
338
+ const CONNECT_TIMEOUT_MESSAGE = 'BLE connect timeout after';
339
+ const isConnectTimeoutError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleConnectedError &&
340
+ typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' &&
341
+ error.message.startsWith(CONNECT_TIMEOUT_MESSAGE);
342
+ const isNativeOperationTimeoutError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === reactNativeBlePlx.BleErrorCode.OperationTimedOut;
342
343
  const tryToGetConfiguration = (device) => {
343
344
  if (!device || !device.serviceUUIDs)
344
345
  return null;
@@ -350,23 +351,25 @@ const tryToGetConfiguration = (device) => {
350
351
  return null;
351
352
  return infos;
352
353
  };
353
- const requestAndroidMtu = (device) => __awaiter(void 0, void 0, void 0, function* () {
354
- if (reactNative.Platform.OS !== 'android')
354
+ const requestNegotiatedMtu = (device, stage, attempt) => __awaiter(void 0, void 0, void 0, function* () {
355
+ if (reactNative.Platform.OS !== 'ios' && reactNative.Platform.OS !== 'android')
355
356
  return device;
356
357
  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
- });
358
+ const mtuDevice = yield device.requestMTU(getRequestedBleMtu());
363
359
  return mtuDevice;
364
360
  }
365
361
  catch (error) {
366
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android MTU request failed:', error);
362
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] MTU refresh failed, continuing with current value', {
363
+ platform: reactNative.Platform.OS,
364
+ stage,
365
+ attempt,
366
+ actual: device.mtu,
367
+ error: error instanceof Error ? error.message : String(error),
368
+ });
367
369
  return device;
368
370
  }
369
371
  });
372
+ const resolveNegotiatedMtu = (device) => requestNegotiatedMtu(device, 'connected', 0);
370
373
  function remapError(error) {
371
374
  var _a;
372
375
  if (error instanceof reactNativeBlePlx.BleError) {
@@ -393,9 +396,15 @@ class ReactNativeBleTransport {
393
396
  this.stopped = false;
394
397
  this.scanTimeout = DEVICE_SCAN_TIMEOUT_MS;
395
398
  this.runPromise = null;
399
+ this.runPromiseDeviceId = null;
396
400
  this.firmwareUploadWriteRecoveryIds = new Set();
397
401
  this.deviceProtocol = new Map();
402
+ this.probingProtocols = new Map();
403
+ this.writeTimeoutCounts = new Map();
404
+ this.connectionSetupTimeoutCounts = new Map();
398
405
  this.deviceProtocolHints = new Map();
406
+ this.sessionProtocols = new Map();
407
+ this.protocolReprobeFailures = new Map();
399
408
  this.protocolV2Assemblers = new Map();
400
409
  this.protocolV2FrameQueues = new Map();
401
410
  this.protocolV2FramePromises = new Map();
@@ -416,12 +425,17 @@ class ReactNativeBleTransport {
416
425
  this.rejectProtocolV2Frames(uuid, new Error(reason));
417
426
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 link invalidated:', uuid, reason);
418
427
  if (reason.startsWith('Protocol V2 link-fatal error:')) {
419
- yield this.release(uuid, true);
428
+ yield this.releaseNative(uuid, true);
420
429
  }
421
430
  }),
422
431
  });
423
432
  this.monitorTokens = new Map();
433
+ this.disconnectEventTokens = new Map();
434
+ this.protocolV2HighVolumeLogSignatures = new Map();
435
+ this.androidHighPriorityDevices = new Set();
436
+ this.androidPriorityResetTimers = new Map();
424
437
  this.nextMonitorToken = 1;
438
+ this.lifecycleOperations = new Map();
425
439
  this.scanTimeout = (_a = options.scanTimeout) !== null && _a !== void 0 ? _a : DEVICE_SCAN_TIMEOUT_MS;
426
440
  }
427
441
  init(logger, emitter) {
@@ -434,10 +448,18 @@ class ReactNativeBleTransport {
434
448
  this._messages = messages;
435
449
  }
436
450
  configureProtocolV2(signedData) {
451
+ const configuration = typeof signedData === 'string' ? signedData : JSON.stringify(signedData);
452
+ if (this.protocolV2SchemaConfiguration === configuration) {
453
+ return;
454
+ }
455
+ const isReconfiguration = this.protocolV2SchemaConfiguration !== undefined;
437
456
  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));
457
+ this.protocolV2SchemaConfiguration = configuration;
458
+ if (isReconfiguration) {
459
+ this.protocolV2Links
460
+ .invalidateAllLinks('Protocol V2 schema reconfigured')
461
+ .catch(error => Log === null || Log === void 0 ? void 0 : Log.debug('Protocol V2 schema link cleanup failed:', error));
462
+ }
441
463
  }
442
464
  listen() {
443
465
  }
@@ -448,7 +470,6 @@ class ReactNativeBleTransport {
448
470
  return Promise.resolve(this.blePlxManager);
449
471
  }
450
472
  resolveCharacteristics(device) {
451
- var _a, _b, _c, _d;
452
473
  return __awaiter(this, void 0, void 0, function* () {
453
474
  yield device.discoverAllServicesAndCharacteristics();
454
475
  let infos = tryToGetConfiguration(device);
@@ -465,19 +486,11 @@ class ReactNativeBleTransport {
465
486
  }
466
487
  }
467
488
  }
468
- let fallbackServiceUuid;
469
489
  if (!infos) {
470
490
  const services = yield device.services();
471
491
  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
492
  }
480
- if (!infos && !fallbackServiceUuid) {
493
+ if (!infos) {
481
494
  try {
482
495
  Log === null || Log === void 0 ? void 0 : Log.debug('cancel connection when service not found');
483
496
  yield device.cancelConnection();
@@ -487,9 +500,7 @@ class ReactNativeBleTransport {
487
500
  }
488
501
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
489
502
  }
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';
503
+ const { serviceUuid, writeUuid, notifyUuid } = infos;
493
504
  if (!serviceUuid) {
494
505
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
495
506
  }
@@ -530,8 +541,8 @@ class ReactNativeBleTransport {
530
541
  attachDisconnectSubscription(transport, device, uuid) {
531
542
  var _a;
532
543
  (_a = transport.disconnectSubscription) === null || _a === void 0 ? void 0 : _a.remove();
544
+ const { monitorToken } = transport;
533
545
  transport.disconnectSubscription = device.onDisconnected(() => {
534
- var _a;
535
546
  if (this.firmwareUploadWriteRecoveryIds.has(uuid)) {
536
547
  Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored during FirmwareUpload write recovery: ', uuid);
537
548
  return;
@@ -540,17 +551,16 @@ class ReactNativeBleTransport {
540
551
  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
552
  return;
542
553
  }
554
+ if (this.monitorTokens.get(uuid) !== monitorToken) {
555
+ Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored for stale generation: ', device === null || device === void 0 ? void 0 : device.id);
556
+ return;
557
+ }
543
558
  try {
544
559
  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) {
560
+ this.emitDeviceDisconnect(uuid, device === null || device === void 0 ? void 0 : device.name, monitorToken);
561
+ if (this.runPromise && this.runPromiseDeviceId === uuid) {
551
562
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError);
552
563
  this.runPromise.reject(error);
553
- this.rejectAllProtocolV2Frames(error);
554
564
  }
555
565
  }
556
566
  catch (e) {
@@ -561,6 +571,22 @@ class ReactNativeBleTransport {
561
571
  }
562
572
  });
563
573
  }
574
+ emitDeviceDisconnect(uuid, name, token) {
575
+ var _a;
576
+ if (token === undefined || this.disconnectEventTokens.get(uuid) === token) {
577
+ return;
578
+ }
579
+ if (this.monitorTokens.get(uuid) !== token) {
580
+ Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect event ignored for stale generation: ', uuid);
581
+ return;
582
+ }
583
+ this.disconnectEventTokens.set(uuid, token);
584
+ (_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit(transport.TRANSPORT_EVENT.DEVICE_DISCONNECT, {
585
+ name,
586
+ id: uuid,
587
+ connectId: uuid,
588
+ });
589
+ }
564
590
  reconnectFirmwareUploadTransport(uuid, transport) {
565
591
  var _a, _b;
566
592
  return __awaiter(this, void 0, void 0, function* () {
@@ -574,19 +600,19 @@ class ReactNativeBleTransport {
574
600
  const isConnected = yield device.isConnected().catch(() => false);
575
601
  if (!isConnected) {
576
602
  try {
577
- device = yield device.connect(connectOptions);
603
+ device = yield this.connectWithTimeout(uuid, () => device.connect(connectOptions));
578
604
  }
579
605
  catch (e) {
580
606
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
581
607
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
582
- device = yield device.connect();
608
+ device = yield this.connectWithTimeout(uuid, () => device.connect());
583
609
  }
584
610
  else if (e.errorCode !== reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
585
611
  throw e;
586
612
  }
587
613
  }
588
614
  }
589
- const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristics(device);
615
+ const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, device);
590
616
  transport.device = device;
591
617
  transport.writeCharacteristic = writeCharacteristic;
592
618
  transport.notifyCharacteristic = notifyCharacteristic;
@@ -634,7 +660,7 @@ class ReactNativeBleTransport {
634
660
  allowDuplicates: true,
635
661
  scanMode: reactNativeBlePlx.ScanMode.LowLatency,
636
662
  }, (error, device) => {
637
- var _a, _b, _c;
663
+ var _a, _b;
638
664
  if (error) {
639
665
  Log === null || Log === void 0 ? void 0 : Log.debug('ble scan error: ', error);
640
666
  if ([reactNativeBlePlx.BleErrorCode.BluetoothPoweredOff, reactNativeBlePlx.BleErrorCode.BluetoothInUnknownState].includes(error.errorCode)) {
@@ -655,9 +681,14 @@ class ReactNativeBleTransport {
655
681
  return;
656
682
  }
657
683
  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);
684
+ const isUnnamedIOSPeripheral = reactNative.Platform.OS === 'ios' && !(displayName === null || displayName === void 0 ? void 0 : displayName.trim());
685
+ const isOneKey = !isUnnamedIOSPeripheral &&
686
+ hdShared.isOnekeyBluetoothDevice({
687
+ id: device === null || device === void 0 ? void 0 : device.id,
688
+ name: device === null || device === void 0 ? void 0 : device.name,
689
+ localName: device === null || device === void 0 ? void 0 : device.localName,
690
+ serviceUuids: (_b = device === null || device === void 0 ? void 0 : device.serviceUUIDs) !== null && _b !== void 0 ? _b : getBluetoothServiceUuids(),
691
+ });
661
692
  if (isOneKey) {
662
693
  addDevice(device);
663
694
  }
@@ -672,10 +703,15 @@ class ReactNativeBleTransport {
672
703
  });
673
704
  getConnectedDeviceIds(reactNative.Platform.OS === 'ios' ? getBluetoothServiceUuids() : []).then(devices => {
674
705
  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) {
706
+ const localName = 'localName' in device && typeof device.localName === 'string'
707
+ ? device.localName
708
+ : null;
709
+ if (hdShared.isOnekeyBluetoothDevice({
710
+ id: device.id,
711
+ name: device.name,
712
+ localName,
713
+ serviceUuids: device.serviceUUIDs,
714
+ })) {
679
715
  Log === null || Log === void 0 ? void 0 : Log.debug('search connected peripheral: ', device.id);
680
716
  addDevice(device);
681
717
  }
@@ -705,13 +741,70 @@ class ReactNativeBleTransport {
705
741
  }));
706
742
  });
707
743
  }
744
+ installTransportForAcquire(uuid, device, characteristics) {
745
+ return __awaiter(this, void 0, void 0, function* () {
746
+ const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristicsWithTimeout(uuid, device));
747
+ const transport$1 = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
748
+ transport$1.mtuSize = typeof device.mtu === 'number' ? device.mtu : undefined;
749
+ const monitorToken = this.nextMonitorToken;
750
+ this.nextMonitorToken += 1;
751
+ const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
752
+ transport$1.monitorToken = monitorToken;
753
+ transport$1.notifyTransactionId = notifyTransactionId;
754
+ this.monitorTokens.set(uuid, monitorToken);
755
+ transport$1.notifySubscription = this._monitorCharacteristic(transport$1.notifyCharacteristic, uuid, monitorToken, notifyTransactionId);
756
+ transportCache[uuid] = transport$1;
757
+ this.protocolV2HighVolumeLogSignatures.set(uuid, new Set());
758
+ this.protocolV2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES));
759
+ if (reactNative.Platform.OS === 'ios') {
760
+ yield new Promise(resolve => {
761
+ setTimeout(resolve, IOS_NOTIFY_READY_DELAY_MS);
762
+ });
763
+ }
764
+ else if (reactNative.Platform.OS === 'android') {
765
+ yield delay(ANDROID_NOTIFY_READY_DELAY_MS);
766
+ }
767
+ const initialMtu = transport$1.mtuSize;
768
+ let refreshAttempts = 0;
769
+ if ((reactNative.Platform.OS === 'ios' || reactNative.Platform.OS === 'android') &&
770
+ shouldRefreshNegotiatedMtu(transport$1.mtuSize)) {
771
+ refreshAttempts += 1;
772
+ let refreshedDevice = yield requestNegotiatedMtu(transport$1.device, 'servicesAndNotifyReady', 1);
773
+ transport$1.device = refreshedDevice;
774
+ transport$1.mtuSize =
775
+ typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport$1.mtuSize;
776
+ if (shouldRefreshNegotiatedMtu(transport$1.mtuSize)) {
777
+ yield delay(BLE_MTU_REFRESH_RETRY_DELAY_MS);
778
+ refreshAttempts += 1;
779
+ refreshedDevice = yield requestNegotiatedMtu(transport$1.device, 'servicesAndNotifyReady', 2);
780
+ transport$1.device = refreshedDevice;
781
+ transport$1.mtuSize =
782
+ typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport$1.mtuSize;
783
+ }
784
+ }
785
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE MTU ready', {
786
+ platform: reactNative.Platform.OS,
787
+ requested: getRequestedBleMtu(),
788
+ initial: initialMtu,
789
+ actual: transport$1.mtuSize,
790
+ refreshAttempts,
791
+ });
792
+ return transport$1;
793
+ });
794
+ }
708
795
  acquire(input) {
709
- var _a, _b;
710
796
  return __awaiter(this, void 0, void 0, function* () {
711
- const { uuid, forceCleanRunPromise, expectedProtocol } = input;
797
+ const { uuid } = input;
712
798
  if (!uuid) {
713
799
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleRequiredUUID);
714
800
  }
801
+ return this.runLifecycleOperation(uuid, () => this.acquireUnlocked(input));
802
+ });
803
+ }
804
+ acquireUnlocked(input) {
805
+ var _a, _b;
806
+ return __awaiter(this, void 0, void 0, function* () {
807
+ const { uuid, forceCleanRunPromise, expectedProtocol } = input;
715
808
  const cachedTransport = transportCache[uuid];
716
809
  if (cachedTransport) {
717
810
  const cachedProtocol = this.deviceProtocol.get(uuid);
@@ -723,14 +816,14 @@ class ReactNativeBleTransport {
723
816
  return { uuid, protocolType: cachedProtocol };
724
817
  }
725
818
  Log === null || Log === void 0 ? void 0 : Log.debug('transport not reusable, will release: ', uuid);
726
- yield this.release(uuid, true);
819
+ yield this.releaseUnlocked(uuid, true);
727
820
  }
728
821
  let device = null;
729
822
  if (forceCleanRunPromise && this.runPromise) {
730
823
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
731
824
  this.runPromise.reject(error);
732
- this.rejectAllProtocolV2Frames(error);
733
825
  this.runPromise = null;
826
+ this.runPromiseDeviceId = null;
734
827
  Log === null || Log === void 0 ? void 0 : Log.debug('Force clean Bluetooth run promise, forceCleanRunPromise: ', forceCleanRunPromise);
735
828
  }
736
829
  const blePlxManager = yield this.getPlxManager();
@@ -763,14 +856,17 @@ class ReactNativeBleTransport {
763
856
  if (!device) {
764
857
  Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device: ', uuid);
765
858
  try {
766
- device = yield blePlxManager.connectToDevice(uuid, connectOptions);
859
+ device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, connectOptions));
767
860
  }
768
861
  catch (e) {
769
862
  Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device has error: ', e);
863
+ if (isConnectTimeoutError(e)) {
864
+ throw e;
865
+ }
770
866
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
771
867
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
772
868
  Log === null || Log === void 0 ? void 0 : Log.debug('first try to reconnect without params');
773
- device = yield blePlxManager.connectToDevice(uuid);
869
+ device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, fallbackConnectOptions));
774
870
  }
775
871
  else if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
776
872
  Log === null || Log === void 0 ? void 0 : Log.debug('device already connected');
@@ -786,23 +882,27 @@ class ReactNativeBleTransport {
786
882
  }
787
883
  if (!(yield device.isConnected())) {
788
884
  Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device: ', uuid);
885
+ const disconnectedDevice = device;
789
886
  try {
790
- device = yield device.connect(connectOptions);
887
+ device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(connectOptions));
791
888
  }
792
889
  catch (e) {
793
890
  Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device has error: ', e);
891
+ if (isConnectTimeoutError(e)) {
892
+ throw e;
893
+ }
794
894
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
795
895
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
796
896
  Log === null || Log === void 0 ? void 0 : Log.debug('second try to reconnect without params');
797
897
  try {
798
- device = yield device.connect();
898
+ device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
799
899
  }
800
900
  catch (e) {
801
901
  Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect error: ', e);
802
902
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
803
903
  Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect');
804
- yield device.cancelConnection();
805
- device = yield device.connect();
904
+ yield disconnectedDevice.cancelConnection();
905
+ device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
806
906
  }
807
907
  }
808
908
  }
@@ -811,44 +911,35 @@ class ReactNativeBleTransport {
811
911
  }
812
912
  }
813
913
  }
814
- device = yield requestAndroidMtu(device);
815
- const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristics(device);
914
+ device = yield resolveNegotiatedMtu(device);
915
+ const acquiredDevice = device;
916
+ const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, acquiredDevice);
816
917
  const protocolHint = expectedProtocol
817
918
  ? undefined
818
- : (_a = this.deviceProtocolHints.get(uuid)) !== null && _a !== void 0 ? _a : inferProtocolHintFromDeviceName(getDeviceDisplayName(device));
819
- yield this.release(uuid, true);
919
+ : (_b = (_a = input.protocolHint) !== null && _a !== void 0 ? _a : this.deviceProtocolHints.get(uuid)) !== null && _b !== void 0 ? _b : inferProtocolHintFromDeviceName(getDeviceDisplayName(acquiredDevice));
920
+ yield this.releaseUnlocked(uuid, true);
820
921
  if (protocolHint) {
821
922
  this.deviceProtocolHints.set(uuid, protocolHint);
822
923
  }
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
- });
924
+ yield this.installTransportForAcquire(uuid, acquiredDevice, {
925
+ writeCharacteristic,
926
+ notifyCharacteristic,
927
+ });
928
+ try {
929
+ const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint, () => __awaiter(this, void 0, void 0, function* () {
930
+ yield this.installTransportForAcquire(uuid, acquiredDevice);
931
+ }));
932
+ const currentTransport = transportCache[uuid];
933
+ if (!currentTransport) {
934
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
935
+ }
936
+ this.attachDisconnectSubscription(currentTransport, currentTransport.device, uuid);
937
+ return { uuid, protocolType };
840
938
  }
841
- else if (reactNative.Platform.OS === 'android') {
842
- yield delay(ANDROID_NOTIFY_READY_DELAY_MS);
939
+ catch (error) {
940
+ yield this.releaseUnlocked(uuid, true);
941
+ throw error;
843
942
  }
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
943
  });
853
944
  }
854
945
  _monitorCharacteristic(characteristic, uuid, monitorToken, notifyTransactionId) {
@@ -867,7 +958,7 @@ class ReactNativeBleTransport {
867
958
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
868
959
  return;
869
960
  }
870
- if (this.deviceProtocol.get(uuid) === 'V2') {
961
+ if (this.getActiveProtocol(uuid) === 'V2') {
871
962
  let errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
872
963
  if ((_a = error.reason) === null || _a === void 0 ? void 0 : _a.includes('The connection has timed out unexpectedly')) {
873
964
  errorCode = hdShared.HardwareErrorCode.BleTimeoutError;
@@ -885,7 +976,7 @@ class ReactNativeBleTransport {
885
976
  this.rejectProtocolV2Frames(uuid, hdShared.ERRORS.TypedError(errorCode));
886
977
  return;
887
978
  }
888
- if (this.runPromise) {
979
+ if (this.runPromise && this.runPromiseDeviceId === uuid) {
889
980
  let ERROR = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
890
981
  if ((_h = error.reason) === null || _h === void 0 ? void 0 : _h.includes('The connection has timed out unexpectedly')) {
891
982
  ERROR = hdShared.HardwareErrorCode.BleTimeoutError;
@@ -900,13 +991,11 @@ class ReactNativeBleTransport {
900
991
  ((_p = error.reason) === null || _p === void 0 ? void 0 : _p.includes('notify change failed for device'))) {
901
992
  const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure);
902
993
  this.runPromise.reject(notifyError);
903
- this.rejectAllProtocolV2Frames(notifyError);
904
994
  Log === null || Log === void 0 ? void 0 : Log.debug(`${hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure} ${error.message} ${error.reason}`);
905
995
  return;
906
996
  }
907
997
  const notifyError = hdShared.ERRORS.TypedError(ERROR);
908
998
  this.runPromise.reject(notifyError);
909
- this.rejectAllProtocolV2Frames(notifyError);
910
999
  Log === null || Log === void 0 ? void 0 : Log.debug(': monitor notify error, and has unreleased Promise', Error);
911
1000
  }
912
1001
  return;
@@ -920,7 +1009,7 @@ class ReactNativeBleTransport {
920
1009
  }
921
1010
  try {
922
1011
  const data = buffer.Buffer.from(c.value, 'base64');
923
- const protocol = this.deviceProtocol.get(uuid);
1012
+ const protocol = this.getActiveProtocol(uuid);
924
1013
  if (!protocol) {
925
1014
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor data ignored before protocol detection: ', uuid);
926
1015
  return;
@@ -940,16 +1029,18 @@ class ReactNativeBleTransport {
940
1029
  const value = buffer.Buffer.from(buffer$1);
941
1030
  bufferLength = 0;
942
1031
  buffer$1 = [];
943
- (_q = this.runPromise) === null || _q === void 0 ? void 0 : _q.resolve(value.toString('hex'));
1032
+ if (this.runPromiseDeviceId === uuid) {
1033
+ (_q = this.runPromise) === null || _q === void 0 ? void 0 : _q.resolve(value.toString('hex'));
1034
+ }
944
1035
  }
945
1036
  }
946
1037
  catch (error) {
947
1038
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor data error: ', error);
948
1039
  const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
949
- if (this.deviceProtocol.get(uuid) === 'V2') {
1040
+ if (this.getActiveProtocol(uuid) === 'V2') {
950
1041
  this.rejectProtocolV2Frames(uuid, notifyError);
951
1042
  }
952
- else {
1043
+ else if (this.runPromiseDeviceId === uuid) {
953
1044
  (_r = this.runPromise) === null || _r === void 0 ? void 0 : _r.reject(notifyError);
954
1045
  }
955
1046
  }
@@ -957,15 +1048,27 @@ class ReactNativeBleTransport {
957
1048
  return subscription;
958
1049
  }
959
1050
  release(uuid, onclose = false) {
960
- var _a, _b, _c, _d, _e, _f, _g;
961
1051
  return __awaiter(this, void 0, void 0, function* () {
962
- const transport = transportCache[uuid];
1052
+ return this.runLifecycleOperation(uuid, () => this.releaseUnlocked(uuid, onclose));
1053
+ });
1054
+ }
1055
+ releaseUnlocked(uuid, onclose = false) {
1056
+ return __awaiter(this, void 0, void 0, function* () {
963
1057
  yield this.protocolV2Links.invalidateLink(uuid, 'React Native BLE transport released');
964
- if (this.runPromise) {
1058
+ return this.releaseNative(uuid, onclose);
1059
+ });
1060
+ }
1061
+ releaseNative(uuid, onclose = false) {
1062
+ var _a, _b, _c, _d, _e;
1063
+ return __awaiter(this, void 0, void 0, function* () {
1064
+ const transport = transportCache[uuid];
1065
+ const manager = this.blePlxManager;
1066
+ if (this.runPromise && this.runPromiseDeviceId === uuid) {
965
1067
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
966
1068
  this.runPromise.reject(error);
967
1069
  this.runPromise = null;
968
- this.rejectAllProtocolV2Frames(error);
1070
+ this.runPromiseDeviceId = null;
1071
+ this.rejectProtocolV2Frames(uuid, error);
969
1072
  }
970
1073
  else {
971
1074
  this.resetProtocolV2Frames(uuid);
@@ -985,31 +1088,37 @@ class ReactNativeBleTransport {
985
1088
  Log === null || Log === void 0 ? void 0 : Log.debug('release: removing notify subscription, characteristic: ', (_c = transport.notifyCharacteristic) === null || _c === void 0 ? void 0 : _c.uuid);
986
1089
  (_d = transport.notifySubscription) === null || _d === void 0 ? void 0 : _d.remove();
987
1090
  transport.notifySubscription = undefined;
988
- if (transport.notifyTransactionId) {
989
- try {
990
- yield ((_e = this.blePlxManager) === null || _e === void 0 ? void 0 : _e.cancelTransaction(transport.notifyTransactionId));
991
- }
992
- catch (e) {
993
- 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);
994
- }
1091
+ if (transportCache[uuid] === transport) {
1092
+ delete transportCache[uuid];
995
1093
  }
996
- delete transportCache[uuid];
997
1094
  }
1095
+ this.protocolV2HighVolumeLogSignatures.delete(uuid);
998
1096
  this.deviceProtocol.delete(uuid);
999
- (_f = this.protocolV2Assemblers.get(uuid)) === null || _f === void 0 ? void 0 : _f.reset();
1097
+ this.probingProtocols.delete(uuid);
1098
+ (_e = this.protocolV2Assemblers.get(uuid)) === null || _e === void 0 ? void 0 : _e.reset();
1000
1099
  this.protocolV2Assemblers.delete(uuid);
1001
1100
  this.resetProtocolV2Frames(uuid);
1002
- try {
1003
- yield ((_g = this.blePlxManager) === null || _g === void 0 ? void 0 : _g.cancelTransaction(uuid));
1004
- }
1005
- catch (e) {
1006
- Log === null || Log === void 0 ? void 0 : Log.debug('release: cancel transaction error (ignored): ', (e === null || e === void 0 ? void 0 : e.message) || e);
1007
- }
1101
+ yield this.runNativeTeardown(uuid, manager, () => __awaiter(this, void 0, void 0, function* () {
1102
+ const operations = [
1103
+ this.runBestEffortNativeOperation('release: restore connection priority', () => this.restoreAndroidConnectionPriority(uuid, transport)),
1104
+ ];
1105
+ if ((transport === null || transport === void 0 ? void 0 : transport.notifyTransactionId) && manager) {
1106
+ operations.push(this.runBestEffortNativeOperation('release: cancel notify transaction', () => manager.cancelTransaction(transport.notifyTransactionId)));
1107
+ }
1108
+ if (manager) {
1109
+ operations.push(this.runBestEffortNativeOperation('release: cancel transaction', () => manager.cancelTransaction(uuid)));
1110
+ }
1111
+ yield Promise.all(operations);
1112
+ }));
1008
1113
  return Promise.resolve(true);
1009
1114
  });
1010
1115
  }
1011
1116
  post(session, name, data) {
1012
1117
  return __awaiter(this, void 0, void 0, function* () {
1118
+ if (this.getProtocolType(session) === 'V2') {
1119
+ yield this.protocolV2Links.sendFlowControl(session, () => this.createProtocolV2Adapter(session), name, data);
1120
+ return;
1121
+ }
1013
1122
  yield this.call(session, name, data);
1014
1123
  });
1015
1124
  }
@@ -1025,7 +1134,6 @@ class ReactNativeBleTransport {
1025
1134
  if (!protocol) {
1026
1135
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${uuid}`);
1027
1136
  }
1028
- Log === null || Log === void 0 ? void 0 : Log.debug('transport call', createTransportCallLog(name, protocol, data));
1029
1137
  if (protocol === 'V2') {
1030
1138
  return this.callProtocolV2(uuid, name, data, options);
1031
1139
  }
@@ -1044,7 +1152,19 @@ class ReactNativeBleTransport {
1044
1152
  const transport = this.getCachedTransport(uuid);
1045
1153
  const runPromise = hdShared.createDeferred();
1046
1154
  runPromise.promise.catch(() => undefined);
1155
+ const supersededRunPromise = this.runPromise;
1156
+ if (supersededRunPromise) {
1157
+ supersededRunPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise));
1158
+ }
1047
1159
  this.runPromise = runPromise;
1160
+ this.runPromiseDeviceId = uuid;
1161
+ const releaseOwnershipIfCurrent = () => {
1162
+ if (this.runPromise === runPromise) {
1163
+ this.runPromise = null;
1164
+ this.runPromiseDeviceId = null;
1165
+ }
1166
+ };
1167
+ const isCurrentOwner = () => this.runPromise === runPromise;
1048
1168
  const messages = this._messages;
1049
1169
  const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
1050
1170
  let timeout;
@@ -1065,6 +1185,9 @@ class ReactNativeBleTransport {
1065
1185
  }
1066
1186
  catch (e) {
1067
1187
  onError(e);
1188
+ if (isWedgedWriteError(e)) {
1189
+ throw e;
1190
+ }
1068
1191
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1069
1192
  }
1070
1193
  }
@@ -1092,6 +1215,9 @@ class ReactNativeBleTransport {
1092
1215
  }
1093
1216
  catch (e) {
1094
1217
  onError(e);
1218
+ if (isWedgedWriteError(e)) {
1219
+ throw e;
1220
+ }
1095
1221
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1096
1222
  }
1097
1223
  }
@@ -1102,8 +1228,8 @@ class ReactNativeBleTransport {
1102
1228
  });
1103
1229
  }
1104
1230
  if (name === 'EmmcFileWrite') {
1105
- yield writeChunkedData(buffers, data => transport.writeWithRetry(data), e => {
1106
- this.runPromise = null;
1231
+ yield writeChunkedData(buffers, data => this.writeBlePacket(uuid, data, payload => transport.writeWithRetry(payload), isCurrentOwner), e => {
1232
+ releaseOwnershipIfCurrent();
1107
1233
  Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
1108
1234
  });
1109
1235
  }
@@ -1119,7 +1245,7 @@ class ReactNativeBleTransport {
1119
1245
  let attempt = 0;
1120
1246
  while (true) {
1121
1247
  try {
1122
- yield transport.writeCharacteristic.writeWithoutResponse(data);
1248
+ yield this.writeBlePacket(uuid, data, payload => transport.writeWithRetry(payload), isCurrentOwner);
1123
1249
  return;
1124
1250
  }
1125
1251
  catch (error) {
@@ -1127,36 +1253,18 @@ class ReactNativeBleTransport {
1127
1253
  if (!retryType || attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
1128
1254
  throw error;
1129
1255
  }
1130
- const shouldReconnect = retryType === 'reconnectable';
1131
- const delayMs = shouldReconnect
1132
- ? FIRMWARE_UPLOAD_RECONNECT_RETRY_DELAY_MS
1133
- : resolveFirmwareUploadRetryDelay(attempt);
1256
+ const delayMs = resolveFirmwareUploadRetryDelay(attempt);
1134
1257
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] FirmwareUpload write retry:', {
1135
1258
  attempt: attempt + 1,
1136
1259
  delayMs,
1137
- reconnect: shouldReconnect,
1138
1260
  error,
1139
1261
  });
1140
- if (shouldReconnect) {
1141
- this.firmwareUploadWriteRecoveryIds.add(uuid);
1142
- }
1143
1262
  yield delay(delayMs);
1144
1263
  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
1264
  }
1157
1265
  }
1158
1266
  }), e => {
1159
- this.runPromise = null;
1267
+ releaseOwnershipIfCurrent();
1160
1268
  Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
1161
1269
  });
1162
1270
  }
@@ -1164,11 +1272,17 @@ class ReactNativeBleTransport {
1164
1272
  for (const o of buffers) {
1165
1273
  const outData = o.toString('base64');
1166
1274
  try {
1167
- yield transport.writeCharacteristic.writeWithoutResponse(outData);
1275
+ const shouldUseWriteWithResponse = reactNative.Platform.OS === 'ios' && transport.writeCharacteristic.isWritableWithResponse;
1276
+ yield this.writeBlePacket(uuid, outData, payload => shouldUseWriteWithResponse
1277
+ ? transport.writeCharacteristic.writeWithResponse(payload)
1278
+ : transport.writeCharacteristic.writeWithoutResponse(payload), isCurrentOwner);
1168
1279
  }
1169
1280
  catch (e) {
1170
1281
  Log === null || Log === void 0 ? void 0 : Log.debug('writeCharacteristic write error: ', e);
1171
- this.runPromise = null;
1282
+ releaseOwnershipIfCurrent();
1283
+ if (isWedgedWriteError(e)) {
1284
+ throw e;
1285
+ }
1172
1286
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected) {
1173
1287
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded);
1174
1288
  }
@@ -1201,12 +1315,19 @@ class ReactNativeBleTransport {
1201
1315
  return check.call(jsonData);
1202
1316
  }
1203
1317
  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);
1318
+ if (name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS) {
1319
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe call failed:', e);
1206
1320
  }
1207
1321
  else {
1208
1322
  Log === null || Log === void 0 ? void 0 : Log.error('call error: ', e);
1209
1323
  }
1324
+ const isProbeTimeout = name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS;
1325
+ const isStaleCall = this.runPromise !== runPromise;
1326
+ if (!isProbeTimeout &&
1327
+ !isStaleCall &&
1328
+ (e === null || e === void 0 ? void 0 : e.errorCode) === hdShared.HardwareErrorCode.BleTimeoutError) {
1329
+ yield this.disconnect(uuid);
1330
+ }
1210
1331
  throw e;
1211
1332
  }
1212
1333
  finally {
@@ -1214,6 +1335,7 @@ class ReactNativeBleTransport {
1214
1335
  clearTimeout(timeout);
1215
1336
  if (this.runPromise === runPromise) {
1216
1337
  this.runPromise = null;
1338
+ this.runPromiseDeviceId = null;
1217
1339
  }
1218
1340
  }
1219
1341
  });
@@ -1222,10 +1344,17 @@ class ReactNativeBleTransport {
1222
1344
  this.stopped = true;
1223
1345
  }
1224
1346
  disconnect(session) {
1225
- var _a, _b, _c, _d, _e;
1347
+ return __awaiter(this, void 0, void 0, function* () {
1348
+ return this.runLifecycleOperation(session, () => this.disconnectUnlocked(session));
1349
+ });
1350
+ }
1351
+ disconnectUnlocked(session) {
1352
+ var _a, _b, _c;
1226
1353
  return __awaiter(this, void 0, void 0, function* () {
1227
1354
  yield this.protocolV2Links.invalidateLink(session, 'React Native BLE transport disconnected');
1228
1355
  const transport = transportCache[session];
1356
+ const manager = this.blePlxManager;
1357
+ const monitorToken = (_a = transport === null || transport === void 0 ? void 0 : transport.monitorToken) !== null && _a !== void 0 ? _a : this.monitorTokens.get(session);
1229
1358
  if (transport === null || transport === void 0 ? void 0 : transport.disconnectSubscription) {
1230
1359
  try {
1231
1360
  Log === null || Log === void 0 ? void 0 : Log.debug('disconnect: removing disconnect subscription');
@@ -1238,7 +1367,7 @@ class ReactNativeBleTransport {
1238
1367
  }
1239
1368
  if (transport === null || transport === void 0 ? void 0 : transport.notifySubscription) {
1240
1369
  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);
1370
+ 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
1371
  transport.notifySubscription.remove();
1243
1372
  transport.notifySubscription = undefined;
1244
1373
  }
@@ -1246,52 +1375,190 @@ class ReactNativeBleTransport {
1246
1375
  Log === null || Log === void 0 ? void 0 : Log.error('disconnect: remove notify subscription error: ', e);
1247
1376
  }
1248
1377
  }
1249
- if (session) {
1250
- try {
1251
- yield ((_b = this.blePlxManager) === null || _b === void 0 ? void 0 : _b.cancelTransaction(session));
1252
- }
1253
- catch (e) {
1254
- Log === null || Log === void 0 ? void 0 : Log.debug('resetSession: cancel transaction error (ignored): ', (e === null || e === void 0 ? void 0 : e.message) || e);
1255
- }
1256
- }
1257
- if (transport === null || transport === void 0 ? void 0 : transport.device) {
1258
- try {
1259
- yield transport.device.cancelConnection();
1260
- }
1261
- catch (e) {
1262
- Log === null || Log === void 0 ? void 0 : Log.debug('resetSession: device.cancelConnection error (ignored): ', (e === null || e === void 0 ? void 0 : e.message) || e);
1263
- }
1264
- }
1265
- try {
1266
- yield ((_c = this.blePlxManager) === null || _c === void 0 ? void 0 : _c.cancelDeviceConnection(session));
1267
- }
1268
- catch (e) {
1269
- Log === null || Log === void 0 ? void 0 : Log.debug('resetSession: manager.cancelDeviceConnection error (ignored): ', (e === null || e === void 0 ? void 0 : e.message) || e);
1270
- }
1271
- if (transportCache[session]) {
1378
+ if (!transport || transportCache[session] === transport) {
1272
1379
  delete transportCache[session];
1273
1380
  }
1274
1381
  this.deviceProtocol.delete(session);
1382
+ this.probingProtocols.delete(session);
1275
1383
  this.deviceProtocolHints.delete(session);
1384
+ this.sessionProtocols.delete(session);
1385
+ this.protocolReprobeFailures.delete(session);
1276
1386
  this.protocolV2Assemblers.delete(session);
1277
1387
  this.resetProtocolV2Frames(session);
1278
1388
  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
- });
1389
+ this.emitDeviceDisconnect(session, (_c = transport === null || transport === void 0 ? void 0 : transport.device) === null || _c === void 0 ? void 0 : _c.name, monitorToken);
1284
1390
  }
1285
1391
  catch (e) {
1286
1392
  Log === null || Log === void 0 ? void 0 : Log.error('resetSession: emit disconnect event error: ', e);
1287
1393
  }
1394
+ if (monitorToken !== undefined && this.monitorTokens.get(session) === monitorToken) {
1395
+ this.monitorTokens.delete(session);
1396
+ }
1397
+ yield this.runNativeTeardown(session, manager, () => __awaiter(this, void 0, void 0, function* () {
1398
+ const operations = [];
1399
+ if (manager) {
1400
+ operations.push(this.runBestEffortNativeOperation('disconnect: cancel transaction', () => manager.cancelTransaction(session)));
1401
+ operations.push(this.runBestEffortNativeOperation('disconnect: cancel device connection', () => manager.cancelDeviceConnection(session)));
1402
+ }
1403
+ if (transport === null || transport === void 0 ? void 0 : transport.device) {
1404
+ operations.push(this.runBestEffortNativeOperation('disconnect: device cancel connection', () => transport.device.cancelConnection()));
1405
+ }
1406
+ yield Promise.all(operations);
1407
+ }));
1288
1408
  yield new Promise(resolve => setTimeout(() => resolve(), 100));
1289
1409
  });
1290
1410
  }
1411
+ runNativeTeardown(uuid, manager, teardown) {
1412
+ return __awaiter(this, void 0, void 0, function* () {
1413
+ let timer;
1414
+ let timedOut = false;
1415
+ const pending = Promise.resolve()
1416
+ .then(teardown)
1417
+ .catch(error => {
1418
+ Log === null || Log === void 0 ? void 0 : Log.debug('BLE native teardown error (ignored): ', (error === null || error === void 0 ? void 0 : error.message) || error);
1419
+ });
1420
+ try {
1421
+ yield Promise.race([
1422
+ pending,
1423
+ new Promise(resolve => {
1424
+ timer = setTimeout(() => {
1425
+ timedOut = true;
1426
+ resolve();
1427
+ }, BLE_NATIVE_TEARDOWN_TIMEOUT_MS);
1428
+ }),
1429
+ ]);
1430
+ }
1431
+ finally {
1432
+ if (timer)
1433
+ clearTimeout(timer);
1434
+ }
1435
+ if (timedOut) {
1436
+ Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE native teardown timed out:', uuid);
1437
+ if (this.blePlxManager === manager) {
1438
+ this.resetPlxManager();
1439
+ }
1440
+ }
1441
+ });
1442
+ }
1443
+ runBestEffortNativeOperation(label, operation) {
1444
+ return Promise.resolve()
1445
+ .then(operation)
1446
+ .catch(error => {
1447
+ Log === null || Log === void 0 ? void 0 : Log.debug(`${label} error (ignored): `, (error === null || error === void 0 ? void 0 : error.message) || error);
1448
+ });
1449
+ }
1450
+ runLifecycleOperation(uuid, operation) {
1451
+ var _a;
1452
+ return __awaiter(this, void 0, void 0, function* () {
1453
+ const previousOperation = (_a = this.lifecycleOperations.get(uuid)) !== null && _a !== void 0 ? _a : Promise.resolve();
1454
+ let completeOperation;
1455
+ const operationGate = new Promise(resolve => {
1456
+ completeOperation = resolve;
1457
+ });
1458
+ const operationTail = previousOperation.catch(() => undefined).then(() => operationGate);
1459
+ this.lifecycleOperations.set(uuid, operationTail);
1460
+ yield previousOperation.catch(() => undefined);
1461
+ try {
1462
+ return yield operation();
1463
+ }
1464
+ finally {
1465
+ completeOperation();
1466
+ if (this.lifecycleOperations.get(uuid) === operationTail) {
1467
+ this.lifecycleOperations.delete(uuid);
1468
+ }
1469
+ }
1470
+ });
1471
+ }
1291
1472
  cancel() {
1292
1473
  Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native transport cancel');
1293
1474
  if (this.runPromise) ;
1294
1475
  this.runPromise = null;
1476
+ this.runPromiseDeviceId = null;
1477
+ }
1478
+ connectWithTimeout(uuid, connect) {
1479
+ return __awaiter(this, void 0, void 0, function* () {
1480
+ let timer;
1481
+ let timedOut = false;
1482
+ const pending = connect();
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 connect timeout after ${BLE_CONNECT_TIMEOUT_MS}ms for ${uuid}`));
1491
+ }, BLE_CONNECT_TIMEOUT_MS);
1492
+ }),
1493
+ ]);
1494
+ return result;
1495
+ }
1496
+ catch (error) {
1497
+ if (timedOut || isNativeOperationTimeoutError(error)) {
1498
+ this.abandonStalledConnection(uuid, timedOut ? 'connect-backstop' : 'connect-native');
1499
+ }
1500
+ throw error;
1501
+ }
1502
+ finally {
1503
+ if (timer)
1504
+ clearTimeout(timer);
1505
+ }
1506
+ });
1507
+ }
1508
+ resolveCharacteristicsWithTimeout(uuid, device) {
1509
+ return __awaiter(this, void 0, void 0, function* () {
1510
+ let timer;
1511
+ let timedOut = false;
1512
+ const pending = this.resolveCharacteristics(device);
1513
+ pending.catch(() => undefined);
1514
+ try {
1515
+ const result = yield Promise.race([
1516
+ pending,
1517
+ new Promise((_, reject) => {
1518
+ timer = setTimeout(() => {
1519
+ timedOut = true;
1520
+ reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `BLE GATT setup timeout after ${BLE_GATT_SETUP_TIMEOUT_MS}ms for ${uuid}`));
1521
+ }, BLE_GATT_SETUP_TIMEOUT_MS);
1522
+ }),
1523
+ ]);
1524
+ this.connectionSetupTimeoutCounts.delete(uuid);
1525
+ return result;
1526
+ }
1527
+ catch (error) {
1528
+ if (timedOut || isNativeOperationTimeoutError(error)) {
1529
+ this.abandonStalledConnection(uuid, timedOut ? 'gatt-backstop' : 'gatt-native');
1530
+ }
1531
+ throw error;
1532
+ }
1533
+ finally {
1534
+ if (timer)
1535
+ clearTimeout(timer);
1536
+ }
1537
+ });
1538
+ }
1539
+ abandonStalledConnection(uuid, stage) {
1540
+ var _a, _b;
1541
+ const timeouts = ((_a = this.connectionSetupTimeoutCounts.get(uuid)) !== null && _a !== void 0 ? _a : 0) + 1;
1542
+ this.connectionSetupTimeoutCounts.set(uuid, timeouts);
1543
+ Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE setup timed out:', uuid, {
1544
+ stage,
1545
+ setupTimeoutsSinceSuccess: timeouts,
1546
+ });
1547
+ (_b = this.blePlxManager) === null || _b === void 0 ? void 0 : _b.cancelDeviceConnection(uuid).catch(() => {
1548
+ });
1549
+ const stalled = transportCache[uuid];
1550
+ if (stalled) {
1551
+ delete transportCache[uuid];
1552
+ }
1553
+ this.deviceProtocol.delete(uuid);
1554
+ this.probingProtocols.delete(uuid);
1555
+ this.protocolV2Assemblers.delete(uuid);
1556
+ this.resetProtocolV2Frames(uuid);
1557
+ if (timeouts >= BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD) {
1558
+ Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE setup wedged repeatedly, resetting BLE manager');
1559
+ this.resetPlxManager();
1560
+ this.connectionSetupTimeoutCounts.delete(uuid);
1561
+ }
1295
1562
  }
1296
1563
  getCachedTransport(uuid) {
1297
1564
  const transport = transportCache[uuid];
@@ -1300,22 +1567,144 @@ class ReactNativeBleTransport {
1300
1567
  }
1301
1568
  return transport;
1302
1569
  }
1570
+ writeBlePacket(uuid, data, write, isCurrentOwner) {
1571
+ return __awaiter(this, void 0, void 0, function* () {
1572
+ let timer;
1573
+ let timedOut = false;
1574
+ try {
1575
+ yield Promise.race([
1576
+ write(data),
1577
+ new Promise((_, reject) => {
1578
+ timer = setTimeout(() => {
1579
+ timedOut = true;
1580
+ reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError, `BLE write timeout after ${BLE_WRITE_PACKET_TIMEOUT_MS}ms`));
1581
+ }, BLE_WRITE_PACKET_TIMEOUT_MS);
1582
+ }),
1583
+ ]);
1584
+ this.writeTimeoutCounts.delete(uuid);
1585
+ }
1586
+ catch (error) {
1587
+ if (timedOut) {
1588
+ if (isCurrentOwner && !isCurrentOwner()) {
1589
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] stale BLE write timed out, link kept:', uuid);
1590
+ }
1591
+ else {
1592
+ this.tearDownWedgedLink(uuid);
1593
+ }
1594
+ }
1595
+ throw error;
1596
+ }
1597
+ finally {
1598
+ if (timer)
1599
+ clearTimeout(timer);
1600
+ }
1601
+ });
1602
+ }
1603
+ tearDownWedgedLink(uuid) {
1604
+ var _a;
1605
+ const timeouts = ((_a = this.writeTimeoutCounts.get(uuid)) !== null && _a !== void 0 ? _a : 0) + 1;
1606
+ this.writeTimeoutCounts.set(uuid, timeouts);
1607
+ Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE write timed out, tearing down link:', uuid, {
1608
+ consecutiveWriteTimeouts: timeouts,
1609
+ });
1610
+ const wedged = transportCache[uuid];
1611
+ this.disconnect(uuid).catch(error => {
1612
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] wedged link teardown failed (ignored):', error);
1613
+ });
1614
+ if (wedged && transportCache[uuid] === wedged) {
1615
+ delete transportCache[uuid];
1616
+ }
1617
+ this.deviceProtocol.delete(uuid);
1618
+ this.probingProtocols.delete(uuid);
1619
+ this.protocolV2Assemblers.delete(uuid);
1620
+ this.resetProtocolV2Frames(uuid);
1621
+ if (timeouts >= BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD) {
1622
+ Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE writes wedged repeatedly, resetting BLE manager');
1623
+ this.resetPlxManager();
1624
+ this.writeTimeoutCounts.delete(uuid);
1625
+ }
1626
+ }
1627
+ resetPlxManager() {
1628
+ const manager = this.blePlxManager;
1629
+ this.blePlxManager = undefined;
1630
+ const reason = 'React Native BLE manager reset';
1631
+ Object.entries(transportCache).forEach(([uuid, cachedTransport]) => {
1632
+ var _a, _b, _c, _d;
1633
+ try {
1634
+ (_a = cachedTransport.disconnectSubscription) === null || _a === void 0 ? void 0 : _a.remove();
1635
+ }
1636
+ catch (error) {
1637
+ Log === null || Log === void 0 ? void 0 : Log.debug('BLE manager reset disconnect subscription removal failed:', error);
1638
+ }
1639
+ cachedTransport.disconnectSubscription = undefined;
1640
+ try {
1641
+ (_b = cachedTransport.notifySubscription) === null || _b === void 0 ? void 0 : _b.remove();
1642
+ }
1643
+ catch (error) {
1644
+ Log === null || Log === void 0 ? void 0 : Log.debug('BLE manager reset notify subscription removal failed:', error);
1645
+ }
1646
+ cachedTransport.notifySubscription = undefined;
1647
+ this.rejectProtocolV2Frames(uuid, new Error(reason));
1648
+ try {
1649
+ this.emitDeviceDisconnect(uuid, (_c = cachedTransport.device) === null || _c === void 0 ? void 0 : _c.name, (_d = cachedTransport.monitorToken) !== null && _d !== void 0 ? _d : this.monitorTokens.get(uuid));
1650
+ }
1651
+ catch (error) {
1652
+ Log === null || Log === void 0 ? void 0 : Log.debug('BLE manager reset disconnect event failed:', error);
1653
+ }
1654
+ delete transportCache[uuid];
1655
+ });
1656
+ this.protocolV2Links.invalidateAllLinks(reason).catch(error => {
1657
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE manager link invalidation failed:', error);
1658
+ });
1659
+ this.deviceProtocol.clear();
1660
+ this.probingProtocols.clear();
1661
+ this.sessionProtocols.clear();
1662
+ this.protocolReprobeFailures.clear();
1663
+ this.writeTimeoutCounts.clear();
1664
+ this.connectionSetupTimeoutCounts.clear();
1665
+ this.monitorTokens.clear();
1666
+ this.protocolV2Assemblers.clear();
1667
+ try {
1668
+ manager === null || manager === void 0 ? void 0 : manager.destroy();
1669
+ }
1670
+ catch (error) {
1671
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE manager destroy failed (ignored):', error);
1672
+ }
1673
+ }
1303
1674
  createProtocolMismatchError(expected) {
1304
1675
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
1305
1676
  }
1306
1677
  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');
1678
+ 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
1679
  }
1309
1680
  clearProbeProtocol(uuid, protocol) {
1681
+ if (this.probingProtocols.get(uuid) === protocol) {
1682
+ this.probingProtocols.delete(uuid);
1683
+ }
1310
1684
  if (this.deviceProtocol.get(uuid) === protocol) {
1311
1685
  this.deviceProtocol.delete(uuid);
1312
1686
  }
1313
1687
  }
1314
- detectProtocol(uuid, expectedProtocol, protocolHint) {
1688
+ getActiveProtocol(uuid) {
1689
+ var _a;
1690
+ return (_a = this.deviceProtocol.get(uuid)) !== null && _a !== void 0 ? _a : this.probingProtocols.get(uuid);
1691
+ }
1692
+ detectProtocol(uuid, expectedProtocol, protocolHint, rebuildTransport) {
1693
+ var _a;
1315
1694
  return __awaiter(this, void 0, void 0, function* () {
1695
+ if (reactNative.Platform.OS === 'ios' && expectedProtocol === 'V1') {
1696
+ this.deviceProtocol.set(uuid, expectedProtocol);
1697
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol selected', {
1698
+ deviceId: uuid,
1699
+ protocol: expectedProtocol,
1700
+ source: 'expected',
1701
+ });
1702
+ return expectedProtocol;
1703
+ }
1316
1704
  if (expectedProtocol === 'V1') {
1317
1705
  if (yield this.probeProtocolV1(uuid)) {
1318
1706
  this.deviceProtocol.set(uuid, 'V1');
1707
+ this.sessionProtocols.set(uuid, 'V1');
1319
1708
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1320
1709
  deviceId: uuid,
1321
1710
  protocol: 'V1',
@@ -1326,23 +1715,41 @@ class ReactNativeBleTransport {
1326
1715
  throw this.createProtocolMismatchError(expectedProtocol);
1327
1716
  }
1328
1717
  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';
1718
+ if (yield this.probeProtocolV2(uuid)) {
1719
+ this.deviceProtocol.set(uuid, 'V2');
1720
+ this.sessionProtocols.set(uuid, 'V2');
1721
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1722
+ deviceId: uuid,
1723
+ protocol: 'V2',
1724
+ source: 'expected',
1725
+ });
1726
+ return 'V2';
1727
+ }
1728
+ throw this.createProtocolMismatchError(expectedProtocol);
1336
1729
  }
1337
- const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
1730
+ const sessionProtocol = this.sessionProtocols.get(uuid);
1731
+ const reprobeFailures = (_a = this.protocolReprobeFailures.get(uuid)) !== null && _a !== void 0 ? _a : 0;
1732
+ const fullProbeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
1733
+ const trustSessionProtocol = sessionProtocol !== undefined &&
1734
+ !protocolHint &&
1735
+ reprobeFailures < PROTOCOL_REPROBE_FALLBACK_ATTEMPTS;
1736
+ const probeOrder = trustSessionProtocol ? [sessionProtocol] : fullProbeOrder;
1338
1737
  for (let i = 0; i < probeOrder.length; i += 1) {
1339
1738
  const protocol = probeOrder[i];
1340
1739
  if (i > 0) {
1341
1740
  yield this.resetProbeStateAfterProtocolProbe(uuid, probeOrder[i - 1]);
1741
+ if (!transportCache[uuid]) {
1742
+ if (!rebuildTransport) {
1743
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
1744
+ }
1745
+ yield rebuildTransport();
1746
+ }
1342
1747
  }
1343
1748
  const detected = protocol === 'V1' ? yield this.probeProtocolV1(uuid) : yield this.probeProtocolV2(uuid);
1344
1749
  if (detected) {
1345
1750
  this.deviceProtocol.set(uuid, protocol);
1751
+ this.sessionProtocols.set(uuid, protocol);
1752
+ this.protocolReprobeFailures.delete(uuid);
1346
1753
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1347
1754
  deviceId: uuid,
1348
1755
  protocol,
@@ -1351,7 +1758,14 @@ class ReactNativeBleTransport {
1351
1758
  return protocol;
1352
1759
  }
1353
1760
  }
1761
+ if (trustSessionProtocol) {
1762
+ this.protocolReprobeFailures.set(uuid, reprobeFailures + 1);
1763
+ }
1764
+ else {
1765
+ this.protocolReprobeFailures.delete(uuid);
1766
+ }
1354
1767
  this.deviceProtocol.delete(uuid);
1768
+ this.probingProtocols.delete(uuid);
1355
1769
  throw this.createProtocolDetectionError();
1356
1770
  });
1357
1771
  }
@@ -1403,13 +1817,17 @@ class ReactNativeBleTransport {
1403
1817
  return false;
1404
1818
  }
1405
1819
  try {
1406
- this.deviceProtocol.set(uuid, 'V1');
1407
- yield this.callProtocolV1(uuid, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
1820
+ this.probingProtocols.set(uuid, 'V1');
1821
+ yield this.callProtocolV1(uuid, 'GetFeatures', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
1822
+ this.probingProtocols.delete(uuid);
1408
1823
  return true;
1409
1824
  }
1410
1825
  catch (error) {
1411
1826
  this.clearProbeProtocol(uuid, 'V1');
1412
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 Initialize probe failed:', error);
1827
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe failed:', error);
1828
+ if (isWedgedWriteError(error)) {
1829
+ throw error;
1830
+ }
1413
1831
  return false;
1414
1832
  }
1415
1833
  });
@@ -1420,7 +1838,7 @@ class ReactNativeBleTransport {
1420
1838
  if (!this._messages || !this._messagesV2) {
1421
1839
  return false;
1422
1840
  }
1423
- this.deviceProtocol.set(uuid, 'V2');
1841
+ this.probingProtocols.set(uuid, 'V2');
1424
1842
  (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1425
1843
  const detected = yield transport.probeProtocolV2({
1426
1844
  call: (name, data, options) => this.callProtocolV2(uuid, name, data, options),
@@ -1436,6 +1854,9 @@ class ReactNativeBleTransport {
1436
1854
  if (!detected) {
1437
1855
  this.clearProbeProtocol(uuid, 'V2');
1438
1856
  }
1857
+ else {
1858
+ this.probingProtocols.delete(uuid);
1859
+ }
1439
1860
  return detected;
1440
1861
  });
1441
1862
  }
@@ -1478,16 +1899,8 @@ class ReactNativeBleTransport {
1478
1899
  }
1479
1900
  this.getProtocolV2FrameQueue(uuid).push(frame);
1480
1901
  }
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
1902
  resetProtocolV2Frames(uuid) {
1489
- this.protocolV2FrameQueues.delete(uuid);
1490
- this.protocolV2FramePromises.delete(uuid);
1903
+ this.rejectProtocolV2Frames(uuid, new Error(`Protocol V2 frame state reset for ${uuid}`));
1491
1904
  }
1492
1905
  rejectProtocolV2Frames(uuid, error) {
1493
1906
  this.protocolV2FrameQueues.delete(uuid);
@@ -1515,20 +1928,70 @@ class ReactNativeBleTransport {
1515
1928
  }
1516
1929
  });
1517
1930
  }
1518
- writeProtocolV2Frame(transport, frame) {
1931
+ writeProtocolV2Packet(uuid, transport, base64, context, assertCurrentGeneration) {
1932
+ return __awaiter(this, void 0, void 0, function* () {
1933
+ const shouldUseWriteWithResponse = shouldWriteProtocolV2WithResponse({
1934
+ platform: reactNative.Platform.OS,
1935
+ highThroughput: context.highThroughput,
1936
+ requestedWithResponse: context.writeWithResponse,
1937
+ characteristic: transport.writeCharacteristic,
1938
+ });
1939
+ let attempt = 0;
1940
+ for (;;) {
1941
+ assertCurrentGeneration();
1942
+ if (context.signal.aborted) {
1943
+ throw new Error(`Protocol V2 BLE write aborted for ${context.messageName}`);
1944
+ }
1945
+ try {
1946
+ yield this.writeBlePacket(uuid, base64, payload => shouldUseWriteWithResponse
1947
+ ? transport.writeCharacteristic.writeWithResponse(payload)
1948
+ : transport.writeCharacteristic.writeWithoutResponse(payload), () => {
1949
+ try {
1950
+ assertCurrentGeneration();
1951
+ return !context.signal.aborted;
1952
+ }
1953
+ catch (_a) {
1954
+ return false;
1955
+ }
1956
+ });
1957
+ assertCurrentGeneration();
1958
+ return;
1959
+ }
1960
+ catch (error) {
1961
+ if (getFirmwareUploadWriteRetryType(error) !== 'congested' ||
1962
+ attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
1963
+ throw error;
1964
+ }
1965
+ const delayMs = resolveFirmwareUploadRetryDelay(attempt);
1966
+ attempt += 1;
1967
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 congested write retry:', {
1968
+ name: context.messageName,
1969
+ attempt,
1970
+ delayMs,
1971
+ });
1972
+ yield delay(delayMs);
1973
+ }
1974
+ }
1975
+ });
1976
+ }
1977
+ writeProtocolV2Frame(uuid, transport$1, frame, context, assertCurrentGeneration) {
1519
1978
  return __awaiter(this, void 0, void 0, function* () {
1520
1979
  const tuning = getProtocolV2BleTuning();
1521
1980
  const packetCapacity = resolveProtocolV2PacketCapacity({
1522
1981
  platform: reactNative.Platform.OS,
1523
1982
  iosPacketLength: tuning.iosPacketLength,
1524
1983
  androidPacketLength: tuning.androidPacketLength,
1525
- mtu: reactNative.Platform.OS === 'android' ? transport.mtuSize : undefined,
1984
+ mtu: transport$1.mtuSize,
1985
+ });
1986
+ yield transport.writeProtocolV2BleFrame({
1987
+ frame,
1988
+ packetCapacity,
1989
+ assertActive: assertCurrentGeneration,
1990
+ signal: context.signal,
1991
+ abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
1992
+ wait: delay,
1993
+ writePacket: packet => this.writeProtocolV2Packet(uuid, transport$1, buffer.Buffer.from(packet).toString('base64'), context, assertCurrentGeneration),
1526
1994
  });
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
1995
  });
1533
1996
  }
1534
1997
  callProtocolV2(uuid, name, data, options) {
@@ -1537,15 +2000,40 @@ class ReactNativeBleTransport {
1537
2000
  if (!this._messages || !this._messagesV2) {
1538
2001
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
1539
2002
  }
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) {
2003
+ const callOptions = options;
2004
+ const highThroughputWrite = transport.isProtocolV2HighThroughputCall(name);
2005
+ if (highThroughputWrite) {
2006
+ yield this.ensureProtocolV2HighThroughputMtu(uuid);
1543
2007
  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,
2008
+ const currentTransport = this.getCachedTransport(uuid);
2009
+ const writeWithResponse = shouldWriteProtocolV2WithResponse({
2010
+ platform: reactNative.Platform.OS,
2011
+ highThroughput: true,
2012
+ requestedWithResponse: options === null || options === void 0 ? void 0 : options.writeWithResponse,
2013
+ characteristic: currentTransport.writeCharacteristic,
2014
+ });
2015
+ const packetCapacity = resolveProtocolV2PacketCapacity({
2016
+ platform: reactNative.Platform.OS,
2017
+ iosPacketLength: tuning.iosPacketLength,
2018
+ androidPacketLength: tuning.androidPacketLength,
2019
+ mtu: currentTransport.mtuSize,
1548
2020
  });
2021
+ const writeMode = writeWithResponse ? 'withResponse' : 'withoutResponse';
2022
+ const logSignature = `${name}:${writeMode}:${String(currentTransport.mtuSize)}:${packetCapacity}`;
2023
+ const loggedSignatures = (_a = this.protocolV2HighVolumeLogSignatures.get(uuid)) !== null && _a !== void 0 ? _a : new Set();
2024
+ if (!loggedSignatures.has(logSignature)) {
2025
+ loggedSignatures.add(logSignature);
2026
+ this.protocolV2HighVolumeLogSignatures.set(uuid, loggedSignatures);
2027
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume write configured', {
2028
+ name,
2029
+ writeMode,
2030
+ reportedMtu: currentTransport.mtuSize,
2031
+ packetCapacity,
2032
+ });
2033
+ }
2034
+ }
2035
+ if (highThroughputWrite) {
2036
+ yield this.enableAndroidHighConnectionPriority(uuid);
1549
2037
  }
1550
2038
  try {
1551
2039
  return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, callOptions);
@@ -1554,6 +2042,85 @@ class ReactNativeBleTransport {
1554
2042
  Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] Protocol V2 call error:', e);
1555
2043
  throw e;
1556
2044
  }
2045
+ finally {
2046
+ if (highThroughputWrite) {
2047
+ this.scheduleAndroidBalancedConnectionPriority(uuid);
2048
+ }
2049
+ }
2050
+ });
2051
+ }
2052
+ ensureProtocolV2HighThroughputMtu(uuid) {
2053
+ return __awaiter(this, void 0, void 0, function* () {
2054
+ const transport = this.getCachedTransport(uuid);
2055
+ if (!shouldRefreshNegotiatedMtu(transport.mtuSize))
2056
+ return;
2057
+ const refreshedDevice = yield requestNegotiatedMtu(transport.device, 'highThroughput', 1);
2058
+ transport.device = refreshedDevice;
2059
+ transport.mtuSize =
2060
+ typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport.mtuSize;
2061
+ if (shouldRefreshNegotiatedMtu(transport.mtuSize)) {
2062
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `Protocol V2 high-throughput BLE MTU unavailable: ${String(transport.mtuSize)}`);
2063
+ }
2064
+ });
2065
+ }
2066
+ clearAndroidPriorityResetTimer(uuid) {
2067
+ const timerId = this.androidPriorityResetTimers.get(uuid);
2068
+ if (timerId !== undefined) {
2069
+ clearTimeout(timerId);
2070
+ this.androidPriorityResetTimers.delete(uuid);
2071
+ }
2072
+ }
2073
+ enableAndroidHighConnectionPriority(uuid) {
2074
+ return __awaiter(this, void 0, void 0, function* () {
2075
+ if (reactNative.Platform.OS !== 'android')
2076
+ return;
2077
+ this.clearAndroidPriorityResetTimer(uuid);
2078
+ if (this.androidHighPriorityDevices.has(uuid))
2079
+ return;
2080
+ const transport = transportCache[uuid];
2081
+ if (!transport)
2082
+ return;
2083
+ try {
2084
+ transport.device = yield transport.device.requestConnectionPriority(reactNativeBlePlx.ConnectionPriority.High);
2085
+ this.androidHighPriorityDevices.add(uuid);
2086
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE connection priority changed', {
2087
+ priority: 'high',
2088
+ });
2089
+ }
2090
+ catch (error) {
2091
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE high priority request failed', {
2092
+ error: error instanceof Error ? error.message : String(error),
2093
+ });
2094
+ }
2095
+ });
2096
+ }
2097
+ scheduleAndroidBalancedConnectionPriority(uuid) {
2098
+ if (reactNative.Platform.OS !== 'android' || !this.androidHighPriorityDevices.has(uuid))
2099
+ return;
2100
+ this.clearAndroidPriorityResetTimer(uuid);
2101
+ const timerId = setTimeout(() => {
2102
+ this.androidPriorityResetTimers.delete(uuid);
2103
+ this.restoreAndroidConnectionPriority(uuid, transportCache[uuid]).catch(error => Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE priority restore failed', error));
2104
+ }, ANDROID_HIGH_PRIORITY_IDLE_MS);
2105
+ this.androidPriorityResetTimers.set(uuid, timerId);
2106
+ }
2107
+ restoreAndroidConnectionPriority(uuid, transport) {
2108
+ return __awaiter(this, void 0, void 0, function* () {
2109
+ this.clearAndroidPriorityResetTimer(uuid);
2110
+ if (reactNative.Platform.OS !== 'android' || !this.androidHighPriorityDevices.delete(uuid) || !transport) {
2111
+ return;
2112
+ }
2113
+ try {
2114
+ transport.device = yield transport.device.requestConnectionPriority(reactNativeBlePlx.ConnectionPriority.Balanced);
2115
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE connection priority changed', {
2116
+ priority: 'balanced',
2117
+ });
2118
+ }
2119
+ catch (error) {
2120
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE balanced priority request failed', {
2121
+ error: error instanceof Error ? error.message : String(error),
2122
+ });
2123
+ }
1557
2124
  });
1558
2125
  }
1559
2126
  createProtocolV2Adapter(uuid) {
@@ -1574,10 +2141,10 @@ class ReactNativeBleTransport {
1574
2141
  (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1575
2142
  this.resetProtocolV2Frames(uuid);
1576
2143
  },
1577
- writeFrame: (frame) => __awaiter(this, void 0, void 0, function* () {
2144
+ writeFrame: (frame, context) => __awaiter(this, void 0, void 0, function* () {
1578
2145
  assertCurrentGeneration();
1579
2146
  const currentTransport = this.getCachedTransport(uuid);
1580
- yield this.writeProtocolV2Frame(currentTransport, frame);
2147
+ yield this.writeProtocolV2Frame(uuid, currentTransport, frame, context, assertCurrentGeneration);
1581
2148
  }),
1582
2149
  readFrame: () => __awaiter(this, void 0, void 0, function* () {
1583
2150
  assertCurrentGeneration();
@@ -1589,6 +2156,8 @@ class ReactNativeBleTransport {
1589
2156
  }),
1590
2157
  reset: (reason) => {
1591
2158
  var _a;
2159
+ if (this.monitorTokens.get(uuid) !== generation)
2160
+ return;
1592
2161
  (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1593
2162
  this.rejectProtocolV2Frames(uuid, new Error(reason));
1594
2163
  },
@@ -1598,11 +2167,19 @@ class ReactNativeBleTransport {
1598
2167
  };
1599
2168
  }
1600
2169
  getProtocolType(path) {
1601
- return this.deviceProtocol.get(path);
2170
+ return this.getActiveProtocol(path);
1602
2171
  }
1603
2172
  }
1604
2173
 
2174
+ exports.BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD;
2175
+ exports.BLE_CONNECT_TIMEOUT_MS = BLE_CONNECT_TIMEOUT_MS;
2176
+ exports.BLE_GATT_SETUP_TIMEOUT_MS = BLE_GATT_SETUP_TIMEOUT_MS;
2177
+ exports.BLE_NATIVE_TEARDOWN_TIMEOUT_MS = BLE_NATIVE_TEARDOWN_TIMEOUT_MS;
2178
+ exports.BLE_WRITE_PACKET_TIMEOUT_MS = BLE_WRITE_PACKET_TIMEOUT_MS;
2179
+ exports.BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD;
2180
+ exports.PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = PROTOCOL_REPROBE_FALLBACK_ATTEMPTS;
1605
2181
  exports.configureProtocolV2BleTuning = configureProtocolV2BleTuning;
1606
2182
  exports["default"] = ReactNativeBleTransport;
2183
+ exports.getFirmwareUploadWriteRetryType = getFirmwareUploadWriteRetryType;
1607
2184
  exports.getProtocolV2BleTuning = getProtocolV2BleTuning;
1608
2185
  exports.resetProtocolV2BleTuning = resetProtocolV2BleTuning;