@onekeyfe/hd-transport-react-native 1.2.0-alpha.67 → 1.2.0-alpha.69

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 = 514;
97
98
  const ClassicServiceUUID = '00000001-0000-1000-8000-00805f9b34fb';
98
99
  const OneKeyServices = {
99
100
  classic: {
@@ -134,15 +135,20 @@ const isSameBleUuid = (left, right) => {
134
135
  function hasWritableCapability(characteristic) {
135
136
  return !!(characteristic.isWritableWithResponse || characteristic.isWritableWithoutResponse);
136
137
  }
137
- function resolveProtocolV2PacketCapacity({ platform, iosPacketLength = IOS_PACKET_LENGTH, androidPacketLength = ANDROID_PACKET_LENGTH, mtu, }) {
138
- if (platform === 'ios') {
139
- return iosPacketLength;
138
+ function resolveProtocolV2PacketCapacity({ platform, iosPacketLength = IOS_PROTOCOL_V2_PACKET_LENGTH, androidPacketLength = ANDROID_PROTOCOL_V2_PACKET_LENGTH, mtu, }) {
139
+ if (typeof mtu !== 'number' || !Number.isFinite(mtu) || mtu <= 3) {
140
+ throw new Error(`Protocol V2 BLE requires a negotiated MTU, received: ${String(mtu)}`);
140
141
  }
141
- if (platform === 'android') {
142
- const payloadLength = Math.max((mtu !== null && mtu !== void 0 ? mtu : ANDROID_DEFAULT_MTU) - 3, 1);
143
- return Math.min(androidPacketLength, payloadLength);
144
- }
145
- return androidPacketLength;
142
+ const payloadLength = Math.floor(mtu) - 3;
143
+ const configuredPacketLength = platform === 'ios' ? iosPacketLength : androidPacketLength;
144
+ return Math.min(configuredPacketLength, payloadLength);
145
+ }
146
+ function shouldWriteProtocolV2WithResponse({ platform, highVolume, requestedWithResponse, characteristic, }) {
147
+ if (!characteristic.isWritableWithResponse)
148
+ return false;
149
+ if (!characteristic.isWritableWithoutResponse)
150
+ return true;
151
+ return requestedWithResponse === true || (platform === 'ios' && !highVolume);
146
152
  }
147
153
 
148
154
  const timer = process.env.NODE_ENV === 'development'
@@ -202,7 +208,6 @@ const isHeaderChunk = (chunk) => {
202
208
  class BleTransport {
203
209
  constructor(device, writeCharacteristic, notifyCharacteristic) {
204
210
  this.name = 'ReactNativeBleTransport';
205
- this.mtuSize = 23;
206
211
  this.id = device.id;
207
212
  this.device = device;
208
213
  this.writeCharacteristic = writeCharacteristic;
@@ -210,6 +215,10 @@ class BleTransport {
210
215
  }
211
216
  writeWithRetry(data) {
212
217
  return __awaiter(this, void 0, void 0, function* () {
218
+ if (reactNative.Platform.OS === 'ios' && this.writeCharacteristic.isWritableWithResponse) {
219
+ yield this.writeCharacteristic.writeWithResponse(data);
220
+ return;
221
+ }
213
222
  yield this.writeCharacteristic.writeWithoutResponse(data);
214
223
  });
215
224
  }
@@ -225,6 +234,32 @@ const FIRMWARE_UPLOAD_WRITE_MAX_RETRIES = 8;
225
234
  const ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH = 192;
226
235
  const FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY = reactNative.Platform.OS === 'ios' ? IOS_PACKET_LENGTH : ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH;
227
236
  const ANDROID_GATT_CONGESTED_STATUS = 143;
237
+ const isAsciiWhitespace = (code) => code === 0x09 ||
238
+ code === 0x0a ||
239
+ code === 0x0b ||
240
+ code === 0x0c ||
241
+ code === 0x0d ||
242
+ code === 0x20;
243
+ const hasGattCongestedStatus = (text) => {
244
+ let searchFrom = 0;
245
+ while (searchFrom < text.length) {
246
+ const statusIndex = text.indexOf('status', searchFrom);
247
+ if (statusIndex < 0)
248
+ return false;
249
+ let cursor = statusIndex + 'status'.length;
250
+ while (cursor < text.length && isAsciiWhitespace(text.charCodeAt(cursor)))
251
+ cursor += 1;
252
+ if (text[cursor] === ':' || text[cursor] === '=') {
253
+ cursor += 1;
254
+ while (cursor < text.length && isAsciiWhitespace(text.charCodeAt(cursor)))
255
+ cursor += 1;
256
+ }
257
+ if (text.startsWith(String(ANDROID_GATT_CONGESTED_STATUS), cursor))
258
+ return true;
259
+ searchFrom = statusIndex + 'status'.length;
260
+ }
261
+ return false;
262
+ };
228
263
  const delay = (ms) => new Promise(resolve => {
229
264
  setTimeout(resolve, ms);
230
265
  });
@@ -239,23 +274,17 @@ const getFirmwareUploadWriteRetryType = (error) => {
239
274
  const text = [bleWriteError.reason, bleWriteError.message, bleWriteError.name]
240
275
  .filter(value => typeof value === 'string')
241
276
  .join(' ');
242
- return /GATT_CONGESTED|status\s*[:=]?\s*143/.test(text) ? 'congested' : null;
277
+ return text.includes('GATT_CONGESTED') || hasGattCongestedStatus(text) ? 'congested' : null;
243
278
  };
244
279
  const resolveFirmwareUploadRetryDelay = (attempt, baseDelayMs = 200, maxDelayMs = 1200) => Math.min(baseDelayMs * Math.pow(2, attempt), maxDelayMs);
245
280
  const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
246
281
  const PROTOCOL_V2_PROBE_TIMEOUT_MS = 10000;
247
- const BLE_WRITE_PACKET_TIMEOUT_MS = 10000;
248
- const WEDGED_WRITE_MESSAGE = 'BLE write timeout after';
249
- const isWedgedWriteError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleWriteCharacteristicError &&
250
- typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' &&
251
- error.message.startsWith(WEDGED_WRITE_MESSAGE);
252
- const BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
253
282
  const DEVICE_SCAN_TIMEOUT_MS = 3000;
254
283
  const IOS_NOTIFY_READY_DELAY_MS = 150;
255
284
  const ANDROID_NOTIFY_READY_DELAY_MS = 300;
256
285
  const DEFAULT_PROTOCOL_V2_BLE_TUNING = {
257
- iosPacketLength: IOS_PACKET_LENGTH,
258
- androidPacketLength: ANDROID_PACKET_LENGTH,
286
+ iosPacketLength: IOS_PROTOCOL_V2_PACKET_LENGTH,
287
+ androidPacketLength: ANDROID_PROTOCOL_V2_PACKET_LENGTH,
259
288
  };
260
289
  let protocolV2BleTuning = Object.assign({}, DEFAULT_PROTOCOL_V2_BLE_TUNING);
261
290
  const normalizePositiveInteger = (value, fallback) => {
@@ -284,25 +313,17 @@ function inferProtocolHintFromDeviceName(name) {
284
313
  function getDeviceDisplayName(device) {
285
314
  return (device === null || device === void 0 ? void 0 : device.name) || (device === null || device === void 0 ? void 0 : device.localName) || null;
286
315
  }
287
- const ANDROID_REQUEST_MTU = 256;
288
- const BLE_NATIVE_CONNECT_TIMEOUT_MS = 3000;
316
+ const IOS_REQUEST_MTU = 247;
317
+ const ANDROID_REQUEST_MTU = 517;
318
+ const BLE_MTU_REFRESH_THRESHOLD = 247;
319
+ const BLE_MTU_REFRESH_RETRY_DELAY_MS = 200;
320
+ const ANDROID_HIGH_PRIORITY_IDLE_MS = 1000;
321
+ const getRequestedBleMtu = () => reactNative.Platform.OS === 'android' ? ANDROID_REQUEST_MTU : IOS_REQUEST_MTU;
289
322
  const connectOptions = {
290
- requestMTU: ANDROID_REQUEST_MTU,
291
- timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
323
+ requestMTU: getRequestedBleMtu(),
324
+ timeout: 3000,
292
325
  refreshGatt: 'OnConnected',
293
326
  };
294
- const fallbackConnectOptions = {
295
- timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
296
- };
297
- const BLE_CONNECT_TIMEOUT_MS = BLE_NATIVE_CONNECT_TIMEOUT_MS * 2 + 2000;
298
- const BLE_GATT_SETUP_TIMEOUT_MS = 10000;
299
- const PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = 3;
300
- const BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
301
- const CONNECT_TIMEOUT_MESSAGE = 'BLE connect timeout after';
302
- const isConnectTimeoutError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleConnectedError &&
303
- typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' &&
304
- error.message.startsWith(CONNECT_TIMEOUT_MESSAGE);
305
- const isNativeOperationTimeoutError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === reactNativeBlePlx.BleErrorCode.OperationTimedOut;
306
327
  const tryToGetConfiguration = (device) => {
307
328
  if (!device || !device.serviceUUIDs)
308
329
  return null;
@@ -314,23 +335,25 @@ const tryToGetConfiguration = (device) => {
314
335
  return null;
315
336
  return infos;
316
337
  };
317
- const requestAndroidMtu = (device) => __awaiter(void 0, void 0, void 0, function* () {
318
- if (reactNative.Platform.OS !== 'android')
338
+ const requestNegotiatedMtu = (device, stage, attempt) => __awaiter(void 0, void 0, void 0, function* () {
339
+ if (reactNative.Platform.OS !== 'ios' && reactNative.Platform.OS !== 'android')
319
340
  return device;
320
341
  try {
321
- const mtuDevice = yield device.requestMTU(ANDROID_REQUEST_MTU);
322
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] MTU configured', {
323
- deviceId: device.id,
324
- requested: ANDROID_REQUEST_MTU,
325
- actual: mtuDevice.mtu,
326
- });
342
+ const mtuDevice = yield device.requestMTU(getRequestedBleMtu());
327
343
  return mtuDevice;
328
344
  }
329
345
  catch (error) {
330
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android MTU request failed:', error);
346
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] MTU refresh failed, continuing with current value', {
347
+ platform: reactNative.Platform.OS,
348
+ stage,
349
+ attempt,
350
+ actual: device.mtu,
351
+ error: error instanceof Error ? error.message : String(error),
352
+ });
331
353
  return device;
332
354
  }
333
355
  });
356
+ const resolveNegotiatedMtu = (device) => requestNegotiatedMtu(device, 'connected', 0);
334
357
  function remapError(error) {
335
358
  var _a;
336
359
  if (error instanceof reactNativeBlePlx.BleError) {
@@ -360,12 +383,7 @@ class ReactNativeBleTransport {
360
383
  this.runPromiseDeviceId = null;
361
384
  this.firmwareUploadWriteRecoveryIds = new Set();
362
385
  this.deviceProtocol = new Map();
363
- this.probingProtocols = new Map();
364
- this.writeTimeoutCounts = new Map();
365
- this.connectionSetupTimeoutCounts = new Map();
366
386
  this.deviceProtocolHints = new Map();
367
- this.sessionProtocols = new Map();
368
- this.protocolReprobeFailures = new Map();
369
387
  this.protocolV2Assemblers = new Map();
370
388
  this.protocolV2FrameQueues = new Map();
371
389
  this.protocolV2FramePromises = new Map();
@@ -392,6 +410,9 @@ class ReactNativeBleTransport {
392
410
  });
393
411
  this.monitorTokens = new Map();
394
412
  this.disconnectEventTokens = new Map();
413
+ this.protocolV2HighVolumeLogSignatures = new Map();
414
+ this.androidHighPriorityDevices = new Set();
415
+ this.androidPriorityResetTimers = new Map();
395
416
  this.nextMonitorToken = 1;
396
417
  this.scanTimeout = (_a = options.scanTimeout) !== null && _a !== void 0 ? _a : DEVICE_SCAN_TIMEOUT_MS;
397
418
  }
@@ -557,19 +578,19 @@ class ReactNativeBleTransport {
557
578
  const isConnected = yield device.isConnected().catch(() => false);
558
579
  if (!isConnected) {
559
580
  try {
560
- device = yield this.connectWithTimeout(uuid, () => device.connect(connectOptions));
581
+ device = yield device.connect(connectOptions);
561
582
  }
562
583
  catch (e) {
563
584
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
564
585
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
565
- device = yield this.connectWithTimeout(uuid, () => device.connect());
586
+ device = yield device.connect();
566
587
  }
567
588
  else if (e.errorCode !== reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
568
589
  throw e;
569
590
  }
570
591
  }
571
592
  }
572
- const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, device);
593
+ const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristics(device);
573
594
  transport.device = device;
574
595
  transport.writeCharacteristic = writeCharacteristic;
575
596
  transport.notifyCharacteristic = notifyCharacteristic;
@@ -638,12 +659,17 @@ class ReactNativeBleTransport {
638
659
  return;
639
660
  }
640
661
  const displayName = getDeviceDisplayName(device);
641
- const isOneKey = hdShared.isOnekeyBluetoothDevice({
642
- id: device === null || device === void 0 ? void 0 : device.id,
643
- name: device === null || device === void 0 ? void 0 : device.name,
644
- localName: device === null || device === void 0 ? void 0 : device.localName,
645
- serviceUuids: device === null || device === void 0 ? void 0 : device.serviceUUIDs,
646
- });
662
+ const isUnnamedIOSPeripheral = reactNative.Platform.OS === 'ios' && !(displayName === null || displayName === void 0 ? void 0 : displayName.trim());
663
+ const isFindMyPeripheral = hdShared.isPro2FindMyAdvertisementName(device === null || device === void 0 ? void 0 : device.name) ||
664
+ hdShared.isPro2FindMyAdvertisementName(device === null || device === void 0 ? void 0 : device.localName);
665
+ const isOneKey = !isUnnamedIOSPeripheral &&
666
+ !isFindMyPeripheral &&
667
+ hdShared.isOnekeyBluetoothDevice({
668
+ id: device === null || device === void 0 ? void 0 : device.id,
669
+ name: device === null || device === void 0 ? void 0 : device.name,
670
+ localName: device === null || device === void 0 ? void 0 : device.localName,
671
+ serviceUuids: device === null || device === void 0 ? void 0 : device.serviceUUIDs,
672
+ });
647
673
  if (isOneKey) {
648
674
  addDevice(device);
649
675
  }
@@ -661,12 +687,15 @@ class ReactNativeBleTransport {
661
687
  const localName = 'localName' in device && typeof device.localName === 'string'
662
688
  ? device.localName
663
689
  : null;
664
- if (hdShared.isOnekeyBluetoothDevice({
665
- id: device.id,
666
- name: device.name,
667
- localName,
668
- serviceUuids: device.serviceUUIDs,
669
- })) {
690
+ const isFindMyPeripheral = hdShared.isPro2FindMyAdvertisementName(device.name) ||
691
+ hdShared.isPro2FindMyAdvertisementName(localName);
692
+ if (!isFindMyPeripheral &&
693
+ hdShared.isOnekeyBluetoothDevice({
694
+ id: device.id,
695
+ name: device.name,
696
+ localName,
697
+ serviceUuids: device.serviceUUIDs,
698
+ })) {
670
699
  Log === null || Log === void 0 ? void 0 : Log.debug('search connected peripheral: ', device.id);
671
700
  addDevice(device);
672
701
  }
@@ -698,11 +727,9 @@ class ReactNativeBleTransport {
698
727
  }
699
728
  installTransportForAcquire(uuid, device, characteristics) {
700
729
  return __awaiter(this, void 0, void 0, function* () {
701
- const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristicsWithTimeout(uuid, device));
730
+ const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristics(device));
702
731
  const transport$1 = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
703
- if (reactNative.Platform.OS === 'android') {
704
- transport$1.mtuSize = typeof device.mtu === 'number' ? device.mtu : transport$1.mtuSize;
705
- }
732
+ transport$1.mtuSize = typeof device.mtu === 'number' ? device.mtu : undefined;
706
733
  const monitorToken = this.nextMonitorToken;
707
734
  this.nextMonitorToken += 1;
708
735
  const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
@@ -711,6 +738,7 @@ class ReactNativeBleTransport {
711
738
  this.monitorTokens.set(uuid, monitorToken);
712
739
  transport$1.notifySubscription = this._monitorCharacteristic(transport$1.notifyCharacteristic, uuid, monitorToken, notifyTransactionId);
713
740
  transportCache[uuid] = transport$1;
741
+ this.protocolV2HighVolumeLogSignatures.set(uuid, new Set());
714
742
  this.protocolV2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES));
715
743
  if (reactNative.Platform.OS === 'ios') {
716
744
  yield new Promise(resolve => {
@@ -720,6 +748,31 @@ class ReactNativeBleTransport {
720
748
  else if (reactNative.Platform.OS === 'android') {
721
749
  yield delay(ANDROID_NOTIFY_READY_DELAY_MS);
722
750
  }
751
+ const initialMtu = transport$1.mtuSize;
752
+ let refreshAttempts = 0;
753
+ if ((reactNative.Platform.OS === 'ios' || reactNative.Platform.OS === 'android') &&
754
+ (typeof transport$1.mtuSize !== 'number' || transport$1.mtuSize < BLE_MTU_REFRESH_THRESHOLD)) {
755
+ refreshAttempts += 1;
756
+ let refreshedDevice = yield requestNegotiatedMtu(transport$1.device, 'servicesAndNotifyReady', 1);
757
+ transport$1.device = refreshedDevice;
758
+ transport$1.mtuSize =
759
+ typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport$1.mtuSize;
760
+ if (typeof transport$1.mtuSize !== 'number' || transport$1.mtuSize < BLE_MTU_REFRESH_THRESHOLD) {
761
+ yield delay(BLE_MTU_REFRESH_RETRY_DELAY_MS);
762
+ refreshAttempts += 1;
763
+ refreshedDevice = yield requestNegotiatedMtu(transport$1.device, 'servicesAndNotifyReady', 2);
764
+ transport$1.device = refreshedDevice;
765
+ transport$1.mtuSize =
766
+ typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport$1.mtuSize;
767
+ }
768
+ }
769
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE MTU ready', {
770
+ platform: reactNative.Platform.OS,
771
+ requested: getRequestedBleMtu(),
772
+ initial: initialMtu,
773
+ actual: transport$1.mtuSize,
774
+ refreshAttempts,
775
+ });
723
776
  return transport$1;
724
777
  });
725
778
  }
@@ -781,17 +834,14 @@ class ReactNativeBleTransport {
781
834
  if (!device) {
782
835
  Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device: ', uuid);
783
836
  try {
784
- device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, connectOptions));
837
+ device = yield blePlxManager.connectToDevice(uuid, connectOptions);
785
838
  }
786
839
  catch (e) {
787
840
  Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device has error: ', e);
788
- if (isConnectTimeoutError(e)) {
789
- throw e;
790
- }
791
841
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
792
842
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
793
843
  Log === null || Log === void 0 ? void 0 : Log.debug('first try to reconnect without params');
794
- device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, fallbackConnectOptions));
844
+ device = yield blePlxManager.connectToDevice(uuid);
795
845
  }
796
846
  else if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
797
847
  Log === null || Log === void 0 ? void 0 : Log.debug('device already connected');
@@ -807,27 +857,23 @@ class ReactNativeBleTransport {
807
857
  }
808
858
  if (!(yield device.isConnected())) {
809
859
  Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device: ', uuid);
810
- const disconnectedDevice = device;
811
860
  try {
812
- device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(connectOptions));
861
+ device = yield device.connect(connectOptions);
813
862
  }
814
863
  catch (e) {
815
864
  Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device has error: ', e);
816
- if (isConnectTimeoutError(e)) {
817
- throw e;
818
- }
819
865
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
820
866
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
821
867
  Log === null || Log === void 0 ? void 0 : Log.debug('second try to reconnect without params');
822
868
  try {
823
- device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
869
+ device = yield device.connect();
824
870
  }
825
871
  catch (e) {
826
872
  Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect error: ', e);
827
873
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
828
874
  Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect');
829
- yield disconnectedDevice.cancelConnection();
830
- device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
875
+ yield device.cancelConnection();
876
+ device = yield device.connect();
831
877
  }
832
878
  }
833
879
  }
@@ -836,9 +882,9 @@ class ReactNativeBleTransport {
836
882
  }
837
883
  }
838
884
  }
839
- device = yield requestAndroidMtu(device);
885
+ device = yield resolveNegotiatedMtu(device);
840
886
  const acquiredDevice = device;
841
- const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, acquiredDevice);
887
+ const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristics(acquiredDevice);
842
888
  const protocolHint = expectedProtocol
843
889
  ? undefined
844
890
  : (_b = (_a = input.protocolHint) !== null && _a !== void 0 ? _a : this.deviceProtocolHints.get(uuid)) !== null && _b !== void 0 ? _b : inferProtocolHintFromDeviceName(getDeviceDisplayName(acquiredDevice));
@@ -858,7 +904,7 @@ class ReactNativeBleTransport {
858
904
  if (!currentTransport) {
859
905
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
860
906
  }
861
- this.attachDisconnectSubscription(currentTransport, acquiredDevice, uuid);
907
+ this.attachDisconnectSubscription(currentTransport, currentTransport.device, uuid);
862
908
  return { uuid, protocolType };
863
909
  }
864
910
  catch (error) {
@@ -883,7 +929,7 @@ class ReactNativeBleTransport {
883
929
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
884
930
  return;
885
931
  }
886
- if (this.getActiveProtocol(uuid) === 'V2') {
932
+ if (this.deviceProtocol.get(uuid) === 'V2') {
887
933
  let errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
888
934
  if ((_a = error.reason) === null || _a === void 0 ? void 0 : _a.includes('The connection has timed out unexpectedly')) {
889
935
  errorCode = hdShared.HardwareErrorCode.BleTimeoutError;
@@ -934,7 +980,7 @@ class ReactNativeBleTransport {
934
980
  }
935
981
  try {
936
982
  const data = buffer.Buffer.from(c.value, 'base64');
937
- const protocol = this.getActiveProtocol(uuid);
983
+ const protocol = this.deviceProtocol.get(uuid);
938
984
  if (!protocol) {
939
985
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor data ignored before protocol detection: ', uuid);
940
986
  return;
@@ -962,7 +1008,7 @@ class ReactNativeBleTransport {
962
1008
  catch (error) {
963
1009
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor data error: ', error);
964
1010
  const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
965
- if (this.getActiveProtocol(uuid) === 'V2') {
1011
+ if (this.deviceProtocol.get(uuid) === 'V2') {
966
1012
  this.rejectProtocolV2Frames(uuid, notifyError);
967
1013
  }
968
1014
  else if (this.runPromiseDeviceId === uuid) {
@@ -997,6 +1043,7 @@ class ReactNativeBleTransport {
997
1043
  this.resetProtocolV2Frames(uuid);
998
1044
  return Promise.resolve(true);
999
1045
  }
1046
+ yield this.restoreAndroidConnectionPriority(uuid, transport);
1000
1047
  if (transport) {
1001
1048
  if (this.monitorTokens.get(uuid) === transport.monitorToken) {
1002
1049
  this.monitorTokens.delete(uuid);
@@ -1017,8 +1064,8 @@ class ReactNativeBleTransport {
1017
1064
  }
1018
1065
  delete transportCache[uuid];
1019
1066
  }
1067
+ this.protocolV2HighVolumeLogSignatures.delete(uuid);
1020
1068
  this.deviceProtocol.delete(uuid);
1021
- this.probingProtocols.delete(uuid);
1022
1069
  (_f = this.protocolV2Assemblers.get(uuid)) === null || _f === void 0 ? void 0 : _f.reset();
1023
1070
  this.protocolV2Assemblers.delete(uuid);
1024
1071
  this.resetProtocolV2Frames(uuid);
@@ -1067,19 +1114,8 @@ class ReactNativeBleTransport {
1067
1114
  const transport = this.getCachedTransport(uuid);
1068
1115
  const runPromise = hdShared.createDeferred();
1069
1116
  runPromise.promise.catch(() => undefined);
1070
- const supersededRunPromise = this.runPromise;
1071
- if (supersededRunPromise) {
1072
- supersededRunPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise));
1073
- }
1074
1117
  this.runPromise = runPromise;
1075
1118
  this.runPromiseDeviceId = uuid;
1076
- const releaseOwnershipIfCurrent = () => {
1077
- if (this.runPromise === runPromise) {
1078
- this.runPromise = null;
1079
- this.runPromiseDeviceId = null;
1080
- }
1081
- };
1082
- const isCurrentOwner = () => this.runPromise === runPromise;
1083
1119
  const messages = this._messages;
1084
1120
  const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
1085
1121
  let timeout;
@@ -1100,9 +1136,6 @@ class ReactNativeBleTransport {
1100
1136
  }
1101
1137
  catch (e) {
1102
1138
  onError(e);
1103
- if (isWedgedWriteError(e)) {
1104
- throw e;
1105
- }
1106
1139
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1107
1140
  }
1108
1141
  }
@@ -1130,9 +1163,6 @@ class ReactNativeBleTransport {
1130
1163
  }
1131
1164
  catch (e) {
1132
1165
  onError(e);
1133
- if (isWedgedWriteError(e)) {
1134
- throw e;
1135
- }
1136
1166
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1137
1167
  }
1138
1168
  }
@@ -1143,8 +1173,8 @@ class ReactNativeBleTransport {
1143
1173
  });
1144
1174
  }
1145
1175
  if (name === 'EmmcFileWrite') {
1146
- yield writeChunkedData(buffers, data => this.writeBlePacket(uuid, data, payload => transport.writeWithRetry(payload), isCurrentOwner), e => {
1147
- releaseOwnershipIfCurrent();
1176
+ yield writeChunkedData(buffers, data => transport.writeWithRetry(data), e => {
1177
+ this.runPromise = null;
1148
1178
  Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
1149
1179
  });
1150
1180
  }
@@ -1160,7 +1190,7 @@ class ReactNativeBleTransport {
1160
1190
  let attempt = 0;
1161
1191
  while (true) {
1162
1192
  try {
1163
- yield this.writeBlePacket(uuid, data, payload => transport.writeCharacteristic.writeWithoutResponse(payload), isCurrentOwner);
1193
+ yield transport.writeWithRetry(data);
1164
1194
  return;
1165
1195
  }
1166
1196
  catch (error) {
@@ -1179,7 +1209,7 @@ class ReactNativeBleTransport {
1179
1209
  }
1180
1210
  }
1181
1211
  }), e => {
1182
- releaseOwnershipIfCurrent();
1212
+ this.runPromise = null;
1183
1213
  Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
1184
1214
  });
1185
1215
  }
@@ -1187,14 +1217,17 @@ class ReactNativeBleTransport {
1187
1217
  for (const o of buffers) {
1188
1218
  const outData = o.toString('base64');
1189
1219
  try {
1190
- yield this.writeBlePacket(uuid, outData, payload => transport.writeCharacteristic.writeWithoutResponse(payload), isCurrentOwner);
1220
+ const shouldUseWriteWithResponse = reactNative.Platform.OS === 'ios' && transport.writeCharacteristic.isWritableWithResponse;
1221
+ if (shouldUseWriteWithResponse) {
1222
+ yield transport.writeCharacteristic.writeWithResponse(outData);
1223
+ }
1224
+ else {
1225
+ yield transport.writeCharacteristic.writeWithoutResponse(outData);
1226
+ }
1191
1227
  }
1192
1228
  catch (e) {
1193
1229
  Log === null || Log === void 0 ? void 0 : Log.debug('writeCharacteristic write error: ', e);
1194
- releaseOwnershipIfCurrent();
1195
- if (isWedgedWriteError(e)) {
1196
- throw e;
1197
- }
1230
+ this.runPromise = null;
1198
1231
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected) {
1199
1232
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded);
1200
1233
  }
@@ -1234,9 +1267,7 @@ class ReactNativeBleTransport {
1234
1267
  Log === null || Log === void 0 ? void 0 : Log.error('call error: ', e);
1235
1268
  }
1236
1269
  const isProbeTimeout = name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS;
1237
- const isStaleCall = this.runPromise !== runPromise;
1238
1270
  if (!isProbeTimeout &&
1239
- !isStaleCall &&
1240
1271
  (e === null || e === void 0 ? void 0 : e.errorCode) === hdShared.HardwareErrorCode.BleTimeoutError) {
1241
1272
  yield this.disconnect(uuid);
1242
1273
  }
@@ -1307,10 +1338,7 @@ class ReactNativeBleTransport {
1307
1338
  delete transportCache[session];
1308
1339
  }
1309
1340
  this.deviceProtocol.delete(session);
1310
- this.probingProtocols.delete(session);
1311
1341
  this.deviceProtocolHints.delete(session);
1312
- this.sessionProtocols.delete(session);
1313
- this.protocolReprobeFailures.delete(session);
1314
1342
  this.protocolV2Assemblers.delete(session);
1315
1343
  this.resetProtocolV2Frames(session);
1316
1344
  try {
@@ -1331,91 +1359,6 @@ class ReactNativeBleTransport {
1331
1359
  this.runPromise = null;
1332
1360
  this.runPromiseDeviceId = null;
1333
1361
  }
1334
- connectWithTimeout(uuid, connect) {
1335
- return __awaiter(this, void 0, void 0, function* () {
1336
- let timer;
1337
- let timedOut = false;
1338
- const pending = connect();
1339
- pending.catch(() => undefined);
1340
- try {
1341
- const result = yield Promise.race([
1342
- pending,
1343
- new Promise((_, reject) => {
1344
- timer = setTimeout(() => {
1345
- timedOut = true;
1346
- reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `BLE connect timeout after ${BLE_CONNECT_TIMEOUT_MS}ms for ${uuid}`));
1347
- }, BLE_CONNECT_TIMEOUT_MS);
1348
- }),
1349
- ]);
1350
- return result;
1351
- }
1352
- catch (error) {
1353
- if (timedOut || isNativeOperationTimeoutError(error)) {
1354
- this.abandonStalledConnection(uuid, timedOut ? 'connect-backstop' : 'connect-native');
1355
- }
1356
- throw error;
1357
- }
1358
- finally {
1359
- if (timer)
1360
- clearTimeout(timer);
1361
- }
1362
- });
1363
- }
1364
- resolveCharacteristicsWithTimeout(uuid, device) {
1365
- return __awaiter(this, void 0, void 0, function* () {
1366
- let timer;
1367
- let timedOut = false;
1368
- const pending = this.resolveCharacteristics(device);
1369
- pending.catch(() => undefined);
1370
- try {
1371
- const result = yield Promise.race([
1372
- pending,
1373
- new Promise((_, reject) => {
1374
- timer = setTimeout(() => {
1375
- timedOut = true;
1376
- reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `BLE GATT setup timeout after ${BLE_GATT_SETUP_TIMEOUT_MS}ms for ${uuid}`));
1377
- }, BLE_GATT_SETUP_TIMEOUT_MS);
1378
- }),
1379
- ]);
1380
- this.connectionSetupTimeoutCounts.delete(uuid);
1381
- return result;
1382
- }
1383
- catch (error) {
1384
- if (timedOut || isNativeOperationTimeoutError(error)) {
1385
- this.abandonStalledConnection(uuid, timedOut ? 'gatt-backstop' : 'gatt-native');
1386
- }
1387
- throw error;
1388
- }
1389
- finally {
1390
- if (timer)
1391
- clearTimeout(timer);
1392
- }
1393
- });
1394
- }
1395
- abandonStalledConnection(uuid, stage) {
1396
- var _a, _b;
1397
- const timeouts = ((_a = this.connectionSetupTimeoutCounts.get(uuid)) !== null && _a !== void 0 ? _a : 0) + 1;
1398
- this.connectionSetupTimeoutCounts.set(uuid, timeouts);
1399
- Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE setup timed out:', uuid, {
1400
- stage,
1401
- setupTimeoutsSinceSuccess: timeouts,
1402
- });
1403
- (_b = this.blePlxManager) === null || _b === void 0 ? void 0 : _b.cancelDeviceConnection(uuid).catch(() => {
1404
- });
1405
- const stalled = transportCache[uuid];
1406
- if (stalled) {
1407
- delete transportCache[uuid];
1408
- }
1409
- this.deviceProtocol.delete(uuid);
1410
- this.probingProtocols.delete(uuid);
1411
- this.protocolV2Assemblers.delete(uuid);
1412
- this.resetProtocolV2Frames(uuid);
1413
- if (timeouts >= BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD) {
1414
- Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE setup wedged repeatedly, resetting BLE manager');
1415
- this.resetPlxManager();
1416
- this.connectionSetupTimeoutCounts.delete(uuid);
1417
- }
1418
- }
1419
1362
  getCachedTransport(uuid) {
1420
1363
  const transport = transportCache[uuid];
1421
1364
  if (!transport) {
@@ -1423,82 +1366,6 @@ class ReactNativeBleTransport {
1423
1366
  }
1424
1367
  return transport;
1425
1368
  }
1426
- writeBlePacket(uuid, data, write, isCurrentOwner) {
1427
- return __awaiter(this, void 0, void 0, function* () {
1428
- let timer;
1429
- let timedOut = false;
1430
- try {
1431
- yield Promise.race([
1432
- write(data),
1433
- new Promise((_, reject) => {
1434
- timer = setTimeout(() => {
1435
- timedOut = true;
1436
- reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError, `BLE write timeout after ${BLE_WRITE_PACKET_TIMEOUT_MS}ms`));
1437
- }, BLE_WRITE_PACKET_TIMEOUT_MS);
1438
- }),
1439
- ]);
1440
- this.writeTimeoutCounts.delete(uuid);
1441
- }
1442
- catch (error) {
1443
- if (timedOut) {
1444
- if (isCurrentOwner && !isCurrentOwner()) {
1445
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] stale BLE write timed out, link kept:', uuid);
1446
- }
1447
- else {
1448
- this.tearDownWedgedLink(uuid);
1449
- }
1450
- }
1451
- throw error;
1452
- }
1453
- finally {
1454
- if (timer)
1455
- clearTimeout(timer);
1456
- }
1457
- });
1458
- }
1459
- tearDownWedgedLink(uuid) {
1460
- var _a;
1461
- const timeouts = ((_a = this.writeTimeoutCounts.get(uuid)) !== null && _a !== void 0 ? _a : 0) + 1;
1462
- this.writeTimeoutCounts.set(uuid, timeouts);
1463
- Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE write timed out, tearing down link:', uuid, {
1464
- consecutiveWriteTimeouts: timeouts,
1465
- });
1466
- const wedged = transportCache[uuid];
1467
- this.disconnect(uuid).catch(error => {
1468
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] wedged link teardown failed (ignored):', error);
1469
- });
1470
- if (wedged && transportCache[uuid] === wedged) {
1471
- delete transportCache[uuid];
1472
- }
1473
- this.deviceProtocol.delete(uuid);
1474
- this.probingProtocols.delete(uuid);
1475
- this.protocolV2Assemblers.delete(uuid);
1476
- this.resetProtocolV2Frames(uuid);
1477
- if (timeouts >= BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD) {
1478
- Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE writes wedged repeatedly, resetting BLE manager');
1479
- this.resetPlxManager();
1480
- this.writeTimeoutCounts.delete(uuid);
1481
- }
1482
- }
1483
- resetPlxManager() {
1484
- const manager = this.blePlxManager;
1485
- this.blePlxManager = undefined;
1486
- Object.keys(transportCache).forEach(key => {
1487
- delete transportCache[key];
1488
- });
1489
- this.deviceProtocol.clear();
1490
- this.probingProtocols.clear();
1491
- this.sessionProtocols.clear();
1492
- this.protocolReprobeFailures.clear();
1493
- this.monitorTokens.clear();
1494
- this.protocolV2Assemblers.clear();
1495
- try {
1496
- manager === null || manager === void 0 ? void 0 : manager.destroy();
1497
- }
1498
- catch (error) {
1499
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE manager destroy failed (ignored):', error);
1500
- }
1501
- }
1502
1369
  createProtocolMismatchError(expected) {
1503
1370
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
1504
1371
  }
@@ -1506,24 +1373,24 @@ class ReactNativeBleTransport {
1506
1373
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1 GetFeatures or Protocol V2 Ping');
1507
1374
  }
1508
1375
  clearProbeProtocol(uuid, protocol) {
1509
- if (this.probingProtocols.get(uuid) === protocol) {
1510
- this.probingProtocols.delete(uuid);
1511
- }
1512
1376
  if (this.deviceProtocol.get(uuid) === protocol) {
1513
1377
  this.deviceProtocol.delete(uuid);
1514
1378
  }
1515
1379
  }
1516
- getActiveProtocol(uuid) {
1517
- var _a;
1518
- return (_a = this.deviceProtocol.get(uuid)) !== null && _a !== void 0 ? _a : this.probingProtocols.get(uuid);
1519
- }
1520
1380
  detectProtocol(uuid, expectedProtocol, protocolHint, rebuildTransport) {
1521
- var _a;
1522
1381
  return __awaiter(this, void 0, void 0, function* () {
1382
+ if (reactNative.Platform.OS === 'ios' && expectedProtocol) {
1383
+ this.deviceProtocol.set(uuid, expectedProtocol);
1384
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol selected', {
1385
+ deviceId: uuid,
1386
+ protocol: expectedProtocol,
1387
+ source: 'expected',
1388
+ });
1389
+ return expectedProtocol;
1390
+ }
1523
1391
  if (expectedProtocol === 'V1') {
1524
1392
  if (yield this.probeProtocolV1(uuid)) {
1525
1393
  this.deviceProtocol.set(uuid, 'V1');
1526
- this.sessionProtocols.set(uuid, 'V1');
1527
1394
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1528
1395
  deviceId: uuid,
1529
1396
  protocol: 'V1',
@@ -1536,7 +1403,6 @@ class ReactNativeBleTransport {
1536
1403
  if (expectedProtocol === 'V2') {
1537
1404
  if (yield this.probeProtocolV2(uuid)) {
1538
1405
  this.deviceProtocol.set(uuid, 'V2');
1539
- this.sessionProtocols.set(uuid, 'V2');
1540
1406
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1541
1407
  deviceId: uuid,
1542
1408
  protocol: 'V2',
@@ -1546,13 +1412,7 @@ class ReactNativeBleTransport {
1546
1412
  }
1547
1413
  throw this.createProtocolMismatchError(expectedProtocol);
1548
1414
  }
1549
- const sessionProtocol = this.sessionProtocols.get(uuid);
1550
- const reprobeFailures = (_a = this.protocolReprobeFailures.get(uuid)) !== null && _a !== void 0 ? _a : 0;
1551
- const fullProbeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
1552
- const trustSessionProtocol = sessionProtocol !== undefined &&
1553
- !protocolHint &&
1554
- reprobeFailures < PROTOCOL_REPROBE_FALLBACK_ATTEMPTS;
1555
- const probeOrder = trustSessionProtocol ? [sessionProtocol] : fullProbeOrder;
1415
+ const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
1556
1416
  for (let i = 0; i < probeOrder.length; i += 1) {
1557
1417
  const protocol = probeOrder[i];
1558
1418
  if (i > 0) {
@@ -1567,8 +1427,6 @@ class ReactNativeBleTransport {
1567
1427
  const detected = protocol === 'V1' ? yield this.probeProtocolV1(uuid) : yield this.probeProtocolV2(uuid);
1568
1428
  if (detected) {
1569
1429
  this.deviceProtocol.set(uuid, protocol);
1570
- this.sessionProtocols.set(uuid, protocol);
1571
- this.protocolReprobeFailures.delete(uuid);
1572
1430
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1573
1431
  deviceId: uuid,
1574
1432
  protocol,
@@ -1577,14 +1435,7 @@ class ReactNativeBleTransport {
1577
1435
  return protocol;
1578
1436
  }
1579
1437
  }
1580
- if (trustSessionProtocol) {
1581
- this.protocolReprobeFailures.set(uuid, reprobeFailures + 1);
1582
- }
1583
- else {
1584
- this.protocolReprobeFailures.delete(uuid);
1585
- }
1586
1438
  this.deviceProtocol.delete(uuid);
1587
- this.probingProtocols.delete(uuid);
1588
1439
  throw this.createProtocolDetectionError();
1589
1440
  });
1590
1441
  }
@@ -1636,17 +1487,13 @@ class ReactNativeBleTransport {
1636
1487
  return false;
1637
1488
  }
1638
1489
  try {
1639
- this.probingProtocols.set(uuid, 'V1');
1490
+ this.deviceProtocol.set(uuid, 'V1');
1640
1491
  yield this.callProtocolV1(uuid, 'GetFeatures', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
1641
- this.probingProtocols.delete(uuid);
1642
1492
  return true;
1643
1493
  }
1644
1494
  catch (error) {
1645
1495
  this.clearProbeProtocol(uuid, 'V1');
1646
1496
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe failed:', error);
1647
- if (isWedgedWriteError(error)) {
1648
- throw error;
1649
- }
1650
1497
  return false;
1651
1498
  }
1652
1499
  });
@@ -1657,7 +1504,7 @@ class ReactNativeBleTransport {
1657
1504
  if (!this._messages || !this._messagesV2) {
1658
1505
  return false;
1659
1506
  }
1660
- this.probingProtocols.set(uuid, 'V2');
1507
+ this.deviceProtocol.set(uuid, 'V2');
1661
1508
  (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1662
1509
  const detected = yield transport.probeProtocolV2({
1663
1510
  call: (name, data, options) => this.callProtocolV2(uuid, name, data, options),
@@ -1673,9 +1520,6 @@ class ReactNativeBleTransport {
1673
1520
  if (!detected) {
1674
1521
  this.clearProbeProtocol(uuid, 'V2');
1675
1522
  }
1676
- else {
1677
- this.probingProtocols.delete(uuid);
1678
- }
1679
1523
  return detected;
1680
1524
  });
1681
1525
  }
@@ -1747,8 +1591,14 @@ class ReactNativeBleTransport {
1747
1591
  }
1748
1592
  });
1749
1593
  }
1750
- writeProtocolV2Packet(uuid, transport, base64, context, assertCurrentGeneration) {
1594
+ writeProtocolV2Packet(transport, base64, context, assertCurrentGeneration) {
1751
1595
  return __awaiter(this, void 0, void 0, function* () {
1596
+ const shouldUseWriteWithResponse = shouldWriteProtocolV2WithResponse({
1597
+ platform: reactNative.Platform.OS,
1598
+ highVolume: context.highVolume,
1599
+ requestedWithResponse: context.writeWithResponse,
1600
+ characteristic: transport.writeCharacteristic,
1601
+ });
1752
1602
  let attempt = 0;
1753
1603
  for (;;) {
1754
1604
  assertCurrentGeneration();
@@ -1756,15 +1606,12 @@ class ReactNativeBleTransport {
1756
1606
  throw new Error(`Protocol V2 BLE write aborted for ${context.messageName}`);
1757
1607
  }
1758
1608
  try {
1759
- yield this.writeBlePacket(uuid, base64, payload => transport.writeCharacteristic.writeWithoutResponse(payload), () => {
1760
- try {
1761
- assertCurrentGeneration();
1762
- return !context.signal.aborted;
1763
- }
1764
- catch (_a) {
1765
- return false;
1766
- }
1767
- });
1609
+ if (shouldUseWriteWithResponse) {
1610
+ yield transport.writeCharacteristic.writeWithResponse(base64);
1611
+ }
1612
+ else {
1613
+ yield transport.writeCharacteristic.writeWithoutResponse(base64);
1614
+ }
1768
1615
  assertCurrentGeneration();
1769
1616
  return;
1770
1617
  }
@@ -1785,14 +1632,14 @@ class ReactNativeBleTransport {
1785
1632
  }
1786
1633
  });
1787
1634
  }
1788
- writeProtocolV2Frame(uuid, transport$1, frame, context, assertCurrentGeneration) {
1635
+ writeProtocolV2Frame(transport$1, frame, context, assertCurrentGeneration) {
1789
1636
  return __awaiter(this, void 0, void 0, function* () {
1790
1637
  const tuning = getProtocolV2BleTuning();
1791
1638
  const packetCapacity = resolveProtocolV2PacketCapacity({
1792
1639
  platform: reactNative.Platform.OS,
1793
1640
  iosPacketLength: tuning.iosPacketLength,
1794
1641
  androidPacketLength: tuning.androidPacketLength,
1795
- mtu: reactNative.Platform.OS === 'android' ? transport$1.mtuSize : undefined,
1642
+ mtu: transport$1.mtuSize,
1796
1643
  });
1797
1644
  yield transport.writeProtocolV2BleFrame({
1798
1645
  frame,
@@ -1800,15 +1647,13 @@ class ReactNativeBleTransport {
1800
1647
  assertActive: assertCurrentGeneration,
1801
1648
  signal: context.signal,
1802
1649
  abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
1803
- burstSize: FIRMWARE_UPLOAD_WRITE_BURST_SIZE,
1804
- burstPauseMs: FIRMWARE_UPLOAD_WRITE_PAUSE_MS,
1805
- flushDelayMs: FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS,
1806
1650
  wait: delay,
1807
- writePacket: packet => this.writeProtocolV2Packet(uuid, transport$1, buffer.Buffer.from(packet).toString('base64'), context, assertCurrentGeneration),
1651
+ writePacket: packet => this.writeProtocolV2Packet(transport$1, buffer.Buffer.from(packet).toString('base64'), context, assertCurrentGeneration),
1808
1652
  });
1809
1653
  });
1810
1654
  }
1811
1655
  callProtocolV2(uuid, name, data, options) {
1656
+ var _a;
1812
1657
  return __awaiter(this, void 0, void 0, function* () {
1813
1658
  if (!this._messages || !this._messagesV2) {
1814
1659
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
@@ -1817,11 +1662,35 @@ class ReactNativeBleTransport {
1817
1662
  const highVolumeWrite = transport.LogBlockCommand.has(name);
1818
1663
  if (highVolumeWrite) {
1819
1664
  const tuning = getProtocolV2BleTuning();
1820
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume write configured', {
1821
- name,
1822
- writeMode: 'withoutResponse',
1823
- packetCapacity: reactNative.Platform.OS === 'ios' ? tuning.iosPacketLength : tuning.androidPacketLength,
1665
+ const currentTransport = this.getCachedTransport(uuid);
1666
+ const writeWithResponse = shouldWriteProtocolV2WithResponse({
1667
+ platform: reactNative.Platform.OS,
1668
+ highVolume: true,
1669
+ requestedWithResponse: options === null || options === void 0 ? void 0 : options.writeWithResponse,
1670
+ characteristic: currentTransport.writeCharacteristic,
1671
+ });
1672
+ const packetCapacity = resolveProtocolV2PacketCapacity({
1673
+ platform: reactNative.Platform.OS,
1674
+ iosPacketLength: tuning.iosPacketLength,
1675
+ androidPacketLength: tuning.androidPacketLength,
1676
+ mtu: currentTransport.mtuSize,
1824
1677
  });
1678
+ const writeMode = writeWithResponse ? 'withResponse' : 'withoutResponse';
1679
+ const logSignature = `${name}:${writeMode}:${String(currentTransport.mtuSize)}:${packetCapacity}`;
1680
+ const loggedSignatures = (_a = this.protocolV2HighVolumeLogSignatures.get(uuid)) !== null && _a !== void 0 ? _a : new Set();
1681
+ if (!loggedSignatures.has(logSignature)) {
1682
+ loggedSignatures.add(logSignature);
1683
+ this.protocolV2HighVolumeLogSignatures.set(uuid, loggedSignatures);
1684
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume write configured', {
1685
+ name,
1686
+ writeMode,
1687
+ reportedMtu: currentTransport.mtuSize,
1688
+ packetCapacity,
1689
+ });
1690
+ }
1691
+ }
1692
+ if (highVolumeWrite) {
1693
+ yield this.enableAndroidHighConnectionPriority(uuid);
1825
1694
  }
1826
1695
  try {
1827
1696
  return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, callOptions);
@@ -1830,6 +1699,71 @@ class ReactNativeBleTransport {
1830
1699
  Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] Protocol V2 call error:', e);
1831
1700
  throw e;
1832
1701
  }
1702
+ finally {
1703
+ if (highVolumeWrite) {
1704
+ this.scheduleAndroidBalancedConnectionPriority(uuid);
1705
+ }
1706
+ }
1707
+ });
1708
+ }
1709
+ clearAndroidPriorityResetTimer(uuid) {
1710
+ const timerId = this.androidPriorityResetTimers.get(uuid);
1711
+ if (timerId !== undefined) {
1712
+ clearTimeout(timerId);
1713
+ this.androidPriorityResetTimers.delete(uuid);
1714
+ }
1715
+ }
1716
+ enableAndroidHighConnectionPriority(uuid) {
1717
+ return __awaiter(this, void 0, void 0, function* () {
1718
+ if (reactNative.Platform.OS !== 'android')
1719
+ return;
1720
+ this.clearAndroidPriorityResetTimer(uuid);
1721
+ if (this.androidHighPriorityDevices.has(uuid))
1722
+ return;
1723
+ const transport = transportCache[uuid];
1724
+ if (!transport)
1725
+ return;
1726
+ try {
1727
+ transport.device = yield transport.device.requestConnectionPriority(reactNativeBlePlx.ConnectionPriority.High);
1728
+ this.androidHighPriorityDevices.add(uuid);
1729
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE connection priority changed', {
1730
+ priority: 'high',
1731
+ });
1732
+ }
1733
+ catch (error) {
1734
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE high priority request failed', {
1735
+ error: error instanceof Error ? error.message : String(error),
1736
+ });
1737
+ }
1738
+ });
1739
+ }
1740
+ scheduleAndroidBalancedConnectionPriority(uuid) {
1741
+ if (reactNative.Platform.OS !== 'android' || !this.androidHighPriorityDevices.has(uuid))
1742
+ return;
1743
+ this.clearAndroidPriorityResetTimer(uuid);
1744
+ const timerId = setTimeout(() => {
1745
+ this.androidPriorityResetTimers.delete(uuid);
1746
+ this.restoreAndroidConnectionPriority(uuid, transportCache[uuid]).catch(error => Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE priority restore failed', error));
1747
+ }, ANDROID_HIGH_PRIORITY_IDLE_MS);
1748
+ this.androidPriorityResetTimers.set(uuid, timerId);
1749
+ }
1750
+ restoreAndroidConnectionPriority(uuid, transport) {
1751
+ return __awaiter(this, void 0, void 0, function* () {
1752
+ this.clearAndroidPriorityResetTimer(uuid);
1753
+ if (reactNative.Platform.OS !== 'android' || !this.androidHighPriorityDevices.delete(uuid) || !transport) {
1754
+ return;
1755
+ }
1756
+ try {
1757
+ transport.device = yield transport.device.requestConnectionPriority(reactNativeBlePlx.ConnectionPriority.Balanced);
1758
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE connection priority changed', {
1759
+ priority: 'balanced',
1760
+ });
1761
+ }
1762
+ catch (error) {
1763
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE balanced priority request failed', {
1764
+ error: error instanceof Error ? error.message : String(error),
1765
+ });
1766
+ }
1833
1767
  });
1834
1768
  }
1835
1769
  createProtocolV2Adapter(uuid) {
@@ -1853,7 +1787,7 @@ class ReactNativeBleTransport {
1853
1787
  writeFrame: (frame, context) => __awaiter(this, void 0, void 0, function* () {
1854
1788
  assertCurrentGeneration();
1855
1789
  const currentTransport = this.getCachedTransport(uuid);
1856
- yield this.writeProtocolV2Frame(uuid, currentTransport, frame, context, assertCurrentGeneration);
1790
+ yield this.writeProtocolV2Frame(currentTransport, frame, context, assertCurrentGeneration);
1857
1791
  }),
1858
1792
  readFrame: () => __awaiter(this, void 0, void 0, function* () {
1859
1793
  assertCurrentGeneration();
@@ -1874,16 +1808,10 @@ class ReactNativeBleTransport {
1874
1808
  };
1875
1809
  }
1876
1810
  getProtocolType(path) {
1877
- return this.getActiveProtocol(path);
1811
+ return this.deviceProtocol.get(path);
1878
1812
  }
1879
1813
  }
1880
1814
 
1881
- exports.BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD;
1882
- exports.BLE_CONNECT_TIMEOUT_MS = BLE_CONNECT_TIMEOUT_MS;
1883
- exports.BLE_GATT_SETUP_TIMEOUT_MS = BLE_GATT_SETUP_TIMEOUT_MS;
1884
- exports.BLE_WRITE_PACKET_TIMEOUT_MS = BLE_WRITE_PACKET_TIMEOUT_MS;
1885
- exports.BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD;
1886
- exports.PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = PROTOCOL_REPROBE_FALLBACK_ATTEMPTS;
1887
1815
  exports.configureProtocolV2BleTuning = configureProtocolV2BleTuning;
1888
1816
  exports["default"] = ReactNativeBleTransport;
1889
1817
  exports.getFirmwareUploadWriteRetryType = getFirmwareUploadWriteRetryType;