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

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,8 +93,7 @@ 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 IOS_PROTOCOL_V2_PACKET_LENGTH = 244;
97
- const ANDROID_PROTOCOL_V2_PACKET_LENGTH = 514;
96
+ const ANDROID_DEFAULT_MTU = 23;
98
97
  const ClassicServiceUUID = '00000001-0000-1000-8000-00805f9b34fb';
99
98
  const OneKeyServices = {
100
99
  classic: {
@@ -135,20 +134,15 @@ const isSameBleUuid = (left, right) => {
135
134
  function hasWritableCapability(characteristic) {
136
135
  return !!(characteristic.isWritableWithResponse || characteristic.isWritableWithoutResponse);
137
136
  }
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)}`);
137
+ function resolveProtocolV2PacketCapacity({ platform, iosPacketLength = IOS_PACKET_LENGTH, androidPacketLength = ANDROID_PACKET_LENGTH, mtu, }) {
138
+ if (platform === 'ios') {
139
+ return iosPacketLength;
141
140
  }
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);
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;
152
146
  }
153
147
 
154
148
  const timer = process.env.NODE_ENV === 'development'
@@ -208,6 +202,7 @@ const isHeaderChunk = (chunk) => {
208
202
  class BleTransport {
209
203
  constructor(device, writeCharacteristic, notifyCharacteristic) {
210
204
  this.name = 'ReactNativeBleTransport';
205
+ this.mtuSize = 23;
211
206
  this.id = device.id;
212
207
  this.device = device;
213
208
  this.writeCharacteristic = writeCharacteristic;
@@ -231,6 +226,7 @@ const FIRMWARE_UPLOAD_WRITE_BURST_SIZE = reactNative.Platform.OS === 'ios' ? 4 :
231
226
  const FIRMWARE_UPLOAD_WRITE_PAUSE_MS = reactNative.Platform.OS === 'ios' ? 8 : 10;
232
227
  const FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS = reactNative.Platform.OS === 'ios' ? 24 : 30;
233
228
  const FIRMWARE_UPLOAD_WRITE_MAX_RETRIES = 8;
229
+ const IOS_PROTOCOL_V2_CONTROL_WRITE_DELAY_MS = 5;
234
230
  const ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH = 192;
235
231
  const FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY = reactNative.Platform.OS === 'ios' ? IOS_PACKET_LENGTH : ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH;
236
232
  const ANDROID_GATT_CONGESTED_STATUS = 143;
@@ -279,12 +275,18 @@ const getFirmwareUploadWriteRetryType = (error) => {
279
275
  const resolveFirmwareUploadRetryDelay = (attempt, baseDelayMs = 200, maxDelayMs = 1200) => Math.min(baseDelayMs * Math.pow(2, attempt), maxDelayMs);
280
276
  const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
281
277
  const PROTOCOL_V2_PROBE_TIMEOUT_MS = 10000;
278
+ const BLE_WRITE_PACKET_TIMEOUT_MS = 10000;
279
+ const WEDGED_WRITE_MESSAGE = 'BLE write timeout after';
280
+ const isWedgedWriteError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleWriteCharacteristicError &&
281
+ typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' &&
282
+ error.message.startsWith(WEDGED_WRITE_MESSAGE);
283
+ const BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
282
284
  const DEVICE_SCAN_TIMEOUT_MS = 3000;
283
285
  const IOS_NOTIFY_READY_DELAY_MS = 150;
284
286
  const ANDROID_NOTIFY_READY_DELAY_MS = 300;
285
287
  const DEFAULT_PROTOCOL_V2_BLE_TUNING = {
286
- iosPacketLength: IOS_PROTOCOL_V2_PACKET_LENGTH,
287
- androidPacketLength: ANDROID_PROTOCOL_V2_PACKET_LENGTH,
288
+ iosPacketLength: IOS_PACKET_LENGTH,
289
+ androidPacketLength: ANDROID_PACKET_LENGTH,
288
290
  };
289
291
  let protocolV2BleTuning = Object.assign({}, DEFAULT_PROTOCOL_V2_BLE_TUNING);
290
292
  const normalizePositiveInteger = (value, fallback) => {
@@ -313,17 +315,25 @@ function inferProtocolHintFromDeviceName(name) {
313
315
  function getDeviceDisplayName(device) {
314
316
  return (device === null || device === void 0 ? void 0 : device.name) || (device === null || device === void 0 ? void 0 : device.localName) || null;
315
317
  }
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;
318
+ const ANDROID_REQUEST_MTU = 256;
319
+ const BLE_NATIVE_CONNECT_TIMEOUT_MS = 3000;
322
320
  const connectOptions = {
323
- requestMTU: getRequestedBleMtu(),
324
- timeout: 3000,
321
+ requestMTU: ANDROID_REQUEST_MTU,
322
+ timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
325
323
  refreshGatt: 'OnConnected',
326
324
  };
325
+ const fallbackConnectOptions = {
326
+ timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
327
+ };
328
+ const BLE_CONNECT_TIMEOUT_MS = BLE_NATIVE_CONNECT_TIMEOUT_MS * 2 + 2000;
329
+ const BLE_GATT_SETUP_TIMEOUT_MS = 10000;
330
+ const PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = 3;
331
+ const BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
332
+ const CONNECT_TIMEOUT_MESSAGE = 'BLE connect timeout after';
333
+ const isConnectTimeoutError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleConnectedError &&
334
+ typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' &&
335
+ error.message.startsWith(CONNECT_TIMEOUT_MESSAGE);
336
+ const isNativeOperationTimeoutError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === reactNativeBlePlx.BleErrorCode.OperationTimedOut;
327
337
  const tryToGetConfiguration = (device) => {
328
338
  if (!device || !device.serviceUUIDs)
329
339
  return null;
@@ -335,25 +345,23 @@ const tryToGetConfiguration = (device) => {
335
345
  return null;
336
346
  return infos;
337
347
  };
338
- const requestNegotiatedMtu = (device, stage, attempt) => __awaiter(void 0, void 0, void 0, function* () {
339
- if (reactNative.Platform.OS !== 'ios' && reactNative.Platform.OS !== 'android')
348
+ const requestAndroidMtu = (device) => __awaiter(void 0, void 0, void 0, function* () {
349
+ if (reactNative.Platform.OS !== 'android')
340
350
  return device;
341
351
  try {
342
- const mtuDevice = yield device.requestMTU(getRequestedBleMtu());
352
+ const mtuDevice = yield device.requestMTU(ANDROID_REQUEST_MTU);
353
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] MTU configured', {
354
+ deviceId: device.id,
355
+ requested: ANDROID_REQUEST_MTU,
356
+ actual: mtuDevice.mtu,
357
+ });
343
358
  return mtuDevice;
344
359
  }
345
360
  catch (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
- });
361
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android MTU request failed:', error);
353
362
  return device;
354
363
  }
355
364
  });
356
- const resolveNegotiatedMtu = (device) => requestNegotiatedMtu(device, 'connected', 0);
357
365
  function remapError(error) {
358
366
  var _a;
359
367
  if (error instanceof reactNativeBlePlx.BleError) {
@@ -383,7 +391,12 @@ class ReactNativeBleTransport {
383
391
  this.runPromiseDeviceId = null;
384
392
  this.firmwareUploadWriteRecoveryIds = new Set();
385
393
  this.deviceProtocol = new Map();
394
+ this.probingProtocols = new Map();
395
+ this.writeTimeoutCounts = new Map();
396
+ this.connectionSetupTimeoutCounts = new Map();
386
397
  this.deviceProtocolHints = new Map();
398
+ this.sessionProtocols = new Map();
399
+ this.protocolReprobeFailures = new Map();
387
400
  this.protocolV2Assemblers = new Map();
388
401
  this.protocolV2FrameQueues = new Map();
389
402
  this.protocolV2FramePromises = new Map();
@@ -410,9 +423,6 @@ class ReactNativeBleTransport {
410
423
  });
411
424
  this.monitorTokens = new Map();
412
425
  this.disconnectEventTokens = new Map();
413
- this.protocolV2HighVolumeLogSignatures = new Map();
414
- this.androidHighPriorityDevices = new Set();
415
- this.androidPriorityResetTimers = new Map();
416
426
  this.nextMonitorToken = 1;
417
427
  this.scanTimeout = (_a = options.scanTimeout) !== null && _a !== void 0 ? _a : DEVICE_SCAN_TIMEOUT_MS;
418
428
  }
@@ -578,19 +588,19 @@ class ReactNativeBleTransport {
578
588
  const isConnected = yield device.isConnected().catch(() => false);
579
589
  if (!isConnected) {
580
590
  try {
581
- device = yield device.connect(connectOptions);
591
+ device = yield this.connectWithTimeout(uuid, () => device.connect(connectOptions));
582
592
  }
583
593
  catch (e) {
584
594
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
585
595
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
586
- device = yield device.connect();
596
+ device = yield this.connectWithTimeout(uuid, () => device.connect());
587
597
  }
588
598
  else if (e.errorCode !== reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
589
599
  throw e;
590
600
  }
591
601
  }
592
602
  }
593
- const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristics(device);
603
+ const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, device);
594
604
  transport.device = device;
595
605
  transport.writeCharacteristic = writeCharacteristic;
596
606
  transport.notifyCharacteristic = notifyCharacteristic;
@@ -727,9 +737,11 @@ class ReactNativeBleTransport {
727
737
  }
728
738
  installTransportForAcquire(uuid, device, characteristics) {
729
739
  return __awaiter(this, void 0, void 0, function* () {
730
- const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristics(device));
740
+ const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristicsWithTimeout(uuid, device));
731
741
  const transport$1 = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
732
- transport$1.mtuSize = typeof device.mtu === 'number' ? device.mtu : undefined;
742
+ if (reactNative.Platform.OS === 'android') {
743
+ transport$1.mtuSize = typeof device.mtu === 'number' ? device.mtu : transport$1.mtuSize;
744
+ }
733
745
  const monitorToken = this.nextMonitorToken;
734
746
  this.nextMonitorToken += 1;
735
747
  const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
@@ -738,7 +750,6 @@ class ReactNativeBleTransport {
738
750
  this.monitorTokens.set(uuid, monitorToken);
739
751
  transport$1.notifySubscription = this._monitorCharacteristic(transport$1.notifyCharacteristic, uuid, monitorToken, notifyTransactionId);
740
752
  transportCache[uuid] = transport$1;
741
- this.protocolV2HighVolumeLogSignatures.set(uuid, new Set());
742
753
  this.protocolV2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES));
743
754
  if (reactNative.Platform.OS === 'ios') {
744
755
  yield new Promise(resolve => {
@@ -748,31 +759,6 @@ class ReactNativeBleTransport {
748
759
  else if (reactNative.Platform.OS === 'android') {
749
760
  yield delay(ANDROID_NOTIFY_READY_DELAY_MS);
750
761
  }
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
- });
776
762
  return transport$1;
777
763
  });
778
764
  }
@@ -834,14 +820,17 @@ class ReactNativeBleTransport {
834
820
  if (!device) {
835
821
  Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device: ', uuid);
836
822
  try {
837
- device = yield blePlxManager.connectToDevice(uuid, connectOptions);
823
+ device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, connectOptions));
838
824
  }
839
825
  catch (e) {
840
826
  Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device has error: ', e);
827
+ if (isConnectTimeoutError(e)) {
828
+ throw e;
829
+ }
841
830
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
842
831
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
843
832
  Log === null || Log === void 0 ? void 0 : Log.debug('first try to reconnect without params');
844
- device = yield blePlxManager.connectToDevice(uuid);
833
+ device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, fallbackConnectOptions));
845
834
  }
846
835
  else if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
847
836
  Log === null || Log === void 0 ? void 0 : Log.debug('device already connected');
@@ -857,23 +846,27 @@ class ReactNativeBleTransport {
857
846
  }
858
847
  if (!(yield device.isConnected())) {
859
848
  Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device: ', uuid);
849
+ const disconnectedDevice = device;
860
850
  try {
861
- device = yield device.connect(connectOptions);
851
+ device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(connectOptions));
862
852
  }
863
853
  catch (e) {
864
854
  Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device has error: ', e);
855
+ if (isConnectTimeoutError(e)) {
856
+ throw e;
857
+ }
865
858
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
866
859
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
867
860
  Log === null || Log === void 0 ? void 0 : Log.debug('second try to reconnect without params');
868
861
  try {
869
- device = yield device.connect();
862
+ device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
870
863
  }
871
864
  catch (e) {
872
865
  Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect error: ', e);
873
866
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
874
867
  Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect');
875
- yield device.cancelConnection();
876
- device = yield device.connect();
868
+ yield disconnectedDevice.cancelConnection();
869
+ device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
877
870
  }
878
871
  }
879
872
  }
@@ -882,9 +875,9 @@ class ReactNativeBleTransport {
882
875
  }
883
876
  }
884
877
  }
885
- device = yield resolveNegotiatedMtu(device);
878
+ device = yield requestAndroidMtu(device);
886
879
  const acquiredDevice = device;
887
- const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristics(acquiredDevice);
880
+ const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, acquiredDevice);
888
881
  const protocolHint = expectedProtocol
889
882
  ? undefined
890
883
  : (_b = (_a = input.protocolHint) !== null && _a !== void 0 ? _a : this.deviceProtocolHints.get(uuid)) !== null && _b !== void 0 ? _b : inferProtocolHintFromDeviceName(getDeviceDisplayName(acquiredDevice));
@@ -904,7 +897,7 @@ class ReactNativeBleTransport {
904
897
  if (!currentTransport) {
905
898
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
906
899
  }
907
- this.attachDisconnectSubscription(currentTransport, currentTransport.device, uuid);
900
+ this.attachDisconnectSubscription(currentTransport, acquiredDevice, uuid);
908
901
  return { uuid, protocolType };
909
902
  }
910
903
  catch (error) {
@@ -929,7 +922,7 @@ class ReactNativeBleTransport {
929
922
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
930
923
  return;
931
924
  }
932
- if (this.deviceProtocol.get(uuid) === 'V2') {
925
+ if (this.getActiveProtocol(uuid) === 'V2') {
933
926
  let errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
934
927
  if ((_a = error.reason) === null || _a === void 0 ? void 0 : _a.includes('The connection has timed out unexpectedly')) {
935
928
  errorCode = hdShared.HardwareErrorCode.BleTimeoutError;
@@ -980,7 +973,7 @@ class ReactNativeBleTransport {
980
973
  }
981
974
  try {
982
975
  const data = buffer.Buffer.from(c.value, 'base64');
983
- const protocol = this.deviceProtocol.get(uuid);
976
+ const protocol = this.getActiveProtocol(uuid);
984
977
  if (!protocol) {
985
978
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor data ignored before protocol detection: ', uuid);
986
979
  return;
@@ -1008,7 +1001,7 @@ class ReactNativeBleTransport {
1008
1001
  catch (error) {
1009
1002
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor data error: ', error);
1010
1003
  const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1011
- if (this.deviceProtocol.get(uuid) === 'V2') {
1004
+ if (this.getActiveProtocol(uuid) === 'V2') {
1012
1005
  this.rejectProtocolV2Frames(uuid, notifyError);
1013
1006
  }
1014
1007
  else if (this.runPromiseDeviceId === uuid) {
@@ -1043,7 +1036,6 @@ class ReactNativeBleTransport {
1043
1036
  this.resetProtocolV2Frames(uuid);
1044
1037
  return Promise.resolve(true);
1045
1038
  }
1046
- yield this.restoreAndroidConnectionPriority(uuid, transport);
1047
1039
  if (transport) {
1048
1040
  if (this.monitorTokens.get(uuid) === transport.monitorToken) {
1049
1041
  this.monitorTokens.delete(uuid);
@@ -1064,8 +1056,8 @@ class ReactNativeBleTransport {
1064
1056
  }
1065
1057
  delete transportCache[uuid];
1066
1058
  }
1067
- this.protocolV2HighVolumeLogSignatures.delete(uuid);
1068
1059
  this.deviceProtocol.delete(uuid);
1060
+ this.probingProtocols.delete(uuid);
1069
1061
  (_f = this.protocolV2Assemblers.get(uuid)) === null || _f === void 0 ? void 0 : _f.reset();
1070
1062
  this.protocolV2Assemblers.delete(uuid);
1071
1063
  this.resetProtocolV2Frames(uuid);
@@ -1114,8 +1106,19 @@ class ReactNativeBleTransport {
1114
1106
  const transport = this.getCachedTransport(uuid);
1115
1107
  const runPromise = hdShared.createDeferred();
1116
1108
  runPromise.promise.catch(() => undefined);
1109
+ const supersededRunPromise = this.runPromise;
1110
+ if (supersededRunPromise) {
1111
+ supersededRunPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise));
1112
+ }
1117
1113
  this.runPromise = runPromise;
1118
1114
  this.runPromiseDeviceId = uuid;
1115
+ const releaseOwnershipIfCurrent = () => {
1116
+ if (this.runPromise === runPromise) {
1117
+ this.runPromise = null;
1118
+ this.runPromiseDeviceId = null;
1119
+ }
1120
+ };
1121
+ const isCurrentOwner = () => this.runPromise === runPromise;
1119
1122
  const messages = this._messages;
1120
1123
  const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
1121
1124
  let timeout;
@@ -1136,6 +1139,9 @@ class ReactNativeBleTransport {
1136
1139
  }
1137
1140
  catch (e) {
1138
1141
  onError(e);
1142
+ if (isWedgedWriteError(e)) {
1143
+ throw e;
1144
+ }
1139
1145
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1140
1146
  }
1141
1147
  }
@@ -1163,6 +1169,9 @@ class ReactNativeBleTransport {
1163
1169
  }
1164
1170
  catch (e) {
1165
1171
  onError(e);
1172
+ if (isWedgedWriteError(e)) {
1173
+ throw e;
1174
+ }
1166
1175
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1167
1176
  }
1168
1177
  }
@@ -1173,8 +1182,8 @@ class ReactNativeBleTransport {
1173
1182
  });
1174
1183
  }
1175
1184
  if (name === 'EmmcFileWrite') {
1176
- yield writeChunkedData(buffers, data => transport.writeWithRetry(data), e => {
1177
- this.runPromise = null;
1185
+ yield writeChunkedData(buffers, data => this.writeBlePacket(uuid, data, payload => transport.writeWithRetry(payload), isCurrentOwner), e => {
1186
+ releaseOwnershipIfCurrent();
1178
1187
  Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
1179
1188
  });
1180
1189
  }
@@ -1190,7 +1199,7 @@ class ReactNativeBleTransport {
1190
1199
  let attempt = 0;
1191
1200
  while (true) {
1192
1201
  try {
1193
- yield transport.writeWithRetry(data);
1202
+ yield this.writeBlePacket(uuid, data, payload => transport.writeWithRetry(payload), isCurrentOwner);
1194
1203
  return;
1195
1204
  }
1196
1205
  catch (error) {
@@ -1209,7 +1218,7 @@ class ReactNativeBleTransport {
1209
1218
  }
1210
1219
  }
1211
1220
  }), e => {
1212
- this.runPromise = null;
1221
+ releaseOwnershipIfCurrent();
1213
1222
  Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
1214
1223
  });
1215
1224
  }
@@ -1218,16 +1227,16 @@ class ReactNativeBleTransport {
1218
1227
  const outData = o.toString('base64');
1219
1228
  try {
1220
1229
  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
- }
1230
+ yield this.writeBlePacket(uuid, outData, payload => shouldUseWriteWithResponse
1231
+ ? transport.writeCharacteristic.writeWithResponse(payload)
1232
+ : transport.writeCharacteristic.writeWithoutResponse(payload), isCurrentOwner);
1227
1233
  }
1228
1234
  catch (e) {
1229
1235
  Log === null || Log === void 0 ? void 0 : Log.debug('writeCharacteristic write error: ', e);
1230
- this.runPromise = null;
1236
+ releaseOwnershipIfCurrent();
1237
+ if (isWedgedWriteError(e)) {
1238
+ throw e;
1239
+ }
1231
1240
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected) {
1232
1241
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded);
1233
1242
  }
@@ -1267,7 +1276,9 @@ class ReactNativeBleTransport {
1267
1276
  Log === null || Log === void 0 ? void 0 : Log.error('call error: ', e);
1268
1277
  }
1269
1278
  const isProbeTimeout = name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS;
1279
+ const isStaleCall = this.runPromise !== runPromise;
1270
1280
  if (!isProbeTimeout &&
1281
+ !isStaleCall &&
1271
1282
  (e === null || e === void 0 ? void 0 : e.errorCode) === hdShared.HardwareErrorCode.BleTimeoutError) {
1272
1283
  yield this.disconnect(uuid);
1273
1284
  }
@@ -1338,7 +1349,10 @@ class ReactNativeBleTransport {
1338
1349
  delete transportCache[session];
1339
1350
  }
1340
1351
  this.deviceProtocol.delete(session);
1352
+ this.probingProtocols.delete(session);
1341
1353
  this.deviceProtocolHints.delete(session);
1354
+ this.sessionProtocols.delete(session);
1355
+ this.protocolReprobeFailures.delete(session);
1342
1356
  this.protocolV2Assemblers.delete(session);
1343
1357
  this.resetProtocolV2Frames(session);
1344
1358
  try {
@@ -1359,6 +1373,91 @@ class ReactNativeBleTransport {
1359
1373
  this.runPromise = null;
1360
1374
  this.runPromiseDeviceId = null;
1361
1375
  }
1376
+ connectWithTimeout(uuid, connect) {
1377
+ return __awaiter(this, void 0, void 0, function* () {
1378
+ let timer;
1379
+ let timedOut = false;
1380
+ const pending = connect();
1381
+ pending.catch(() => undefined);
1382
+ try {
1383
+ const result = yield Promise.race([
1384
+ pending,
1385
+ new Promise((_, reject) => {
1386
+ timer = setTimeout(() => {
1387
+ timedOut = true;
1388
+ reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `BLE connect timeout after ${BLE_CONNECT_TIMEOUT_MS}ms for ${uuid}`));
1389
+ }, BLE_CONNECT_TIMEOUT_MS);
1390
+ }),
1391
+ ]);
1392
+ return result;
1393
+ }
1394
+ catch (error) {
1395
+ if (timedOut || isNativeOperationTimeoutError(error)) {
1396
+ this.abandonStalledConnection(uuid, timedOut ? 'connect-backstop' : 'connect-native');
1397
+ }
1398
+ throw error;
1399
+ }
1400
+ finally {
1401
+ if (timer)
1402
+ clearTimeout(timer);
1403
+ }
1404
+ });
1405
+ }
1406
+ resolveCharacteristicsWithTimeout(uuid, device) {
1407
+ return __awaiter(this, void 0, void 0, function* () {
1408
+ let timer;
1409
+ let timedOut = false;
1410
+ const pending = this.resolveCharacteristics(device);
1411
+ pending.catch(() => undefined);
1412
+ try {
1413
+ const result = yield Promise.race([
1414
+ pending,
1415
+ new Promise((_, reject) => {
1416
+ timer = setTimeout(() => {
1417
+ timedOut = true;
1418
+ reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `BLE GATT setup timeout after ${BLE_GATT_SETUP_TIMEOUT_MS}ms for ${uuid}`));
1419
+ }, BLE_GATT_SETUP_TIMEOUT_MS);
1420
+ }),
1421
+ ]);
1422
+ this.connectionSetupTimeoutCounts.delete(uuid);
1423
+ return result;
1424
+ }
1425
+ catch (error) {
1426
+ if (timedOut || isNativeOperationTimeoutError(error)) {
1427
+ this.abandonStalledConnection(uuid, timedOut ? 'gatt-backstop' : 'gatt-native');
1428
+ }
1429
+ throw error;
1430
+ }
1431
+ finally {
1432
+ if (timer)
1433
+ clearTimeout(timer);
1434
+ }
1435
+ });
1436
+ }
1437
+ abandonStalledConnection(uuid, stage) {
1438
+ var _a, _b;
1439
+ const timeouts = ((_a = this.connectionSetupTimeoutCounts.get(uuid)) !== null && _a !== void 0 ? _a : 0) + 1;
1440
+ this.connectionSetupTimeoutCounts.set(uuid, timeouts);
1441
+ Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE setup timed out:', uuid, {
1442
+ stage,
1443
+ setupTimeoutsSinceSuccess: timeouts,
1444
+ });
1445
+ (_b = this.blePlxManager) === null || _b === void 0 ? void 0 : _b.cancelDeviceConnection(uuid).catch(() => {
1446
+ });
1447
+ const stalled = transportCache[uuid];
1448
+ if (stalled) {
1449
+ delete transportCache[uuid];
1450
+ }
1451
+ this.deviceProtocol.delete(uuid);
1452
+ this.probingProtocols.delete(uuid);
1453
+ this.protocolV2Assemblers.delete(uuid);
1454
+ this.resetProtocolV2Frames(uuid);
1455
+ if (timeouts >= BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD) {
1456
+ Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE setup wedged repeatedly, resetting BLE manager');
1457
+ this.resetPlxManager();
1458
+ this.connectionSetupTimeoutCounts.delete(uuid);
1459
+ }
1460
+ }
1362
1461
  getCachedTransport(uuid) {
1363
1462
  const transport = transportCache[uuid];
1364
1463
  if (!transport) {
@@ -1366,6 +1465,82 @@ class ReactNativeBleTransport {
1366
1465
  }
1367
1466
  return transport;
1368
1467
  }
1468
+ writeBlePacket(uuid, data, write, isCurrentOwner) {
1469
+ return __awaiter(this, void 0, void 0, function* () {
1470
+ let timer;
1471
+ let timedOut = false;
1472
+ try {
1473
+ yield Promise.race([
1474
+ write(data),
1475
+ new Promise((_, reject) => {
1476
+ timer = setTimeout(() => {
1477
+ timedOut = true;
1478
+ reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError, `BLE write timeout after ${BLE_WRITE_PACKET_TIMEOUT_MS}ms`));
1479
+ }, BLE_WRITE_PACKET_TIMEOUT_MS);
1480
+ }),
1481
+ ]);
1482
+ this.writeTimeoutCounts.delete(uuid);
1483
+ }
1484
+ catch (error) {
1485
+ if (timedOut) {
1486
+ if (isCurrentOwner && !isCurrentOwner()) {
1487
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] stale BLE write timed out, link kept:', uuid);
1488
+ }
1489
+ else {
1490
+ this.tearDownWedgedLink(uuid);
1491
+ }
1492
+ }
1493
+ throw error;
1494
+ }
1495
+ finally {
1496
+ if (timer)
1497
+ clearTimeout(timer);
1498
+ }
1499
+ });
1500
+ }
1501
+ tearDownWedgedLink(uuid) {
1502
+ var _a;
1503
+ const timeouts = ((_a = this.writeTimeoutCounts.get(uuid)) !== null && _a !== void 0 ? _a : 0) + 1;
1504
+ this.writeTimeoutCounts.set(uuid, timeouts);
1505
+ Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE write timed out, tearing down link:', uuid, {
1506
+ consecutiveWriteTimeouts: timeouts,
1507
+ });
1508
+ const wedged = transportCache[uuid];
1509
+ this.disconnect(uuid).catch(error => {
1510
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] wedged link teardown failed (ignored):', error);
1511
+ });
1512
+ if (wedged && transportCache[uuid] === wedged) {
1513
+ delete transportCache[uuid];
1514
+ }
1515
+ this.deviceProtocol.delete(uuid);
1516
+ this.probingProtocols.delete(uuid);
1517
+ this.protocolV2Assemblers.delete(uuid);
1518
+ this.resetProtocolV2Frames(uuid);
1519
+ if (timeouts >= BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD) {
1520
+ Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE writes wedged repeatedly, resetting BLE manager');
1521
+ this.resetPlxManager();
1522
+ this.writeTimeoutCounts.delete(uuid);
1523
+ }
1524
+ }
1525
+ resetPlxManager() {
1526
+ const manager = this.blePlxManager;
1527
+ this.blePlxManager = undefined;
1528
+ Object.keys(transportCache).forEach(key => {
1529
+ delete transportCache[key];
1530
+ });
1531
+ this.deviceProtocol.clear();
1532
+ this.probingProtocols.clear();
1533
+ this.sessionProtocols.clear();
1534
+ this.protocolReprobeFailures.clear();
1535
+ this.monitorTokens.clear();
1536
+ this.protocolV2Assemblers.clear();
1537
+ try {
1538
+ manager === null || manager === void 0 ? void 0 : manager.destroy();
1539
+ }
1540
+ catch (error) {
1541
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE manager destroy failed (ignored):', error);
1542
+ }
1543
+ }
1369
1544
  createProtocolMismatchError(expected) {
1370
1545
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
1371
1546
  }
@@ -1373,11 +1548,19 @@ class ReactNativeBleTransport {
1373
1548
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1 GetFeatures or Protocol V2 Ping');
1374
1549
  }
1375
1550
  clearProbeProtocol(uuid, protocol) {
1551
+ if (this.probingProtocols.get(uuid) === protocol) {
1552
+ this.probingProtocols.delete(uuid);
1553
+ }
1376
1554
  if (this.deviceProtocol.get(uuid) === protocol) {
1377
1555
  this.deviceProtocol.delete(uuid);
1378
1556
  }
1379
1557
  }
1558
+ getActiveProtocol(uuid) {
1559
+ var _a;
1560
+ return (_a = this.deviceProtocol.get(uuid)) !== null && _a !== void 0 ? _a : this.probingProtocols.get(uuid);
1561
+ }
1380
1562
  detectProtocol(uuid, expectedProtocol, protocolHint, rebuildTransport) {
1563
+ var _a;
1381
1564
  return __awaiter(this, void 0, void 0, function* () {
1382
1565
  if (reactNative.Platform.OS === 'ios' && expectedProtocol) {
1383
1566
  this.deviceProtocol.set(uuid, expectedProtocol);
@@ -1391,6 +1574,7 @@ class ReactNativeBleTransport {
1391
1574
  if (expectedProtocol === 'V1') {
1392
1575
  if (yield this.probeProtocolV1(uuid)) {
1393
1576
  this.deviceProtocol.set(uuid, 'V1');
1577
+ this.sessionProtocols.set(uuid, 'V1');
1394
1578
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1395
1579
  deviceId: uuid,
1396
1580
  protocol: 'V1',
@@ -1403,6 +1587,7 @@ class ReactNativeBleTransport {
1403
1587
  if (expectedProtocol === 'V2') {
1404
1588
  if (yield this.probeProtocolV2(uuid)) {
1405
1589
  this.deviceProtocol.set(uuid, 'V2');
1590
+ this.sessionProtocols.set(uuid, 'V2');
1406
1591
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1407
1592
  deviceId: uuid,
1408
1593
  protocol: 'V2',
@@ -1412,7 +1597,13 @@ class ReactNativeBleTransport {
1412
1597
  }
1413
1598
  throw this.createProtocolMismatchError(expectedProtocol);
1414
1599
  }
1415
- const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
1600
+ const sessionProtocol = this.sessionProtocols.get(uuid);
1601
+ const reprobeFailures = (_a = this.protocolReprobeFailures.get(uuid)) !== null && _a !== void 0 ? _a : 0;
1602
+ const fullProbeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
1603
+ const trustSessionProtocol = sessionProtocol !== undefined &&
1604
+ !protocolHint &&
1605
+ reprobeFailures < PROTOCOL_REPROBE_FALLBACK_ATTEMPTS;
1606
+ const probeOrder = trustSessionProtocol ? [sessionProtocol] : fullProbeOrder;
1416
1607
  for (let i = 0; i < probeOrder.length; i += 1) {
1417
1608
  const protocol = probeOrder[i];
1418
1609
  if (i > 0) {
@@ -1427,6 +1618,8 @@ class ReactNativeBleTransport {
1427
1618
  const detected = protocol === 'V1' ? yield this.probeProtocolV1(uuid) : yield this.probeProtocolV2(uuid);
1428
1619
  if (detected) {
1429
1620
  this.deviceProtocol.set(uuid, protocol);
1621
+ this.sessionProtocols.set(uuid, protocol);
1622
+ this.protocolReprobeFailures.delete(uuid);
1430
1623
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1431
1624
  deviceId: uuid,
1432
1625
  protocol,
@@ -1435,7 +1628,14 @@ class ReactNativeBleTransport {
1435
1628
  return protocol;
1436
1629
  }
1437
1630
  }
1631
+ if (trustSessionProtocol) {
1632
+ this.protocolReprobeFailures.set(uuid, reprobeFailures + 1);
1633
+ }
1634
+ else {
1635
+ this.protocolReprobeFailures.delete(uuid);
1636
+ }
1438
1637
  this.deviceProtocol.delete(uuid);
1638
+ this.probingProtocols.delete(uuid);
1439
1639
  throw this.createProtocolDetectionError();
1440
1640
  });
1441
1641
  }
@@ -1487,13 +1687,17 @@ class ReactNativeBleTransport {
1487
1687
  return false;
1488
1688
  }
1489
1689
  try {
1490
- this.deviceProtocol.set(uuid, 'V1');
1690
+ this.probingProtocols.set(uuid, 'V1');
1491
1691
  yield this.callProtocolV1(uuid, 'GetFeatures', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
1692
+ this.probingProtocols.delete(uuid);
1492
1693
  return true;
1493
1694
  }
1494
1695
  catch (error) {
1495
1696
  this.clearProbeProtocol(uuid, 'V1');
1496
1697
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe failed:', error);
1698
+ if (isWedgedWriteError(error)) {
1699
+ throw error;
1700
+ }
1497
1701
  return false;
1498
1702
  }
1499
1703
  });
@@ -1504,7 +1708,7 @@ class ReactNativeBleTransport {
1504
1708
  if (!this._messages || !this._messagesV2) {
1505
1709
  return false;
1506
1710
  }
1507
- this.deviceProtocol.set(uuid, 'V2');
1711
+ this.probingProtocols.set(uuid, 'V2');
1508
1712
  (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1509
1713
  const detected = yield transport.probeProtocolV2({
1510
1714
  call: (name, data, options) => this.callProtocolV2(uuid, name, data, options),
@@ -1520,6 +1724,9 @@ class ReactNativeBleTransport {
1520
1724
  if (!detected) {
1521
1725
  this.clearProbeProtocol(uuid, 'V2');
1522
1726
  }
1727
+ else {
1728
+ this.probingProtocols.delete(uuid);
1729
+ }
1523
1730
  return detected;
1524
1731
  });
1525
1732
  }
@@ -1591,14 +1798,10 @@ class ReactNativeBleTransport {
1591
1798
  }
1592
1799
  });
1593
1800
  }
1594
- writeProtocolV2Packet(transport, base64, context, assertCurrentGeneration) {
1801
+ writeProtocolV2Packet(uuid, transport, base64, context, assertCurrentGeneration) {
1595
1802
  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
- });
1803
+ const shouldUseWriteWithResponse = transport.writeCharacteristic.isWritableWithResponse &&
1804
+ (context.writeWithResponse === true || (reactNative.Platform.OS === 'ios' && !context.highVolume));
1602
1805
  let attempt = 0;
1603
1806
  for (;;) {
1604
1807
  assertCurrentGeneration();
@@ -1606,12 +1809,17 @@ class ReactNativeBleTransport {
1606
1809
  throw new Error(`Protocol V2 BLE write aborted for ${context.messageName}`);
1607
1810
  }
1608
1811
  try {
1609
- if (shouldUseWriteWithResponse) {
1610
- yield transport.writeCharacteristic.writeWithResponse(base64);
1611
- }
1612
- else {
1613
- yield transport.writeCharacteristic.writeWithoutResponse(base64);
1614
- }
1812
+ yield this.writeBlePacket(uuid, base64, payload => shouldUseWriteWithResponse
1813
+ ? transport.writeCharacteristic.writeWithResponse(payload)
1814
+ : transport.writeCharacteristic.writeWithoutResponse(payload), () => {
1815
+ try {
1816
+ assertCurrentGeneration();
1817
+ return !context.signal.aborted;
1818
+ }
1819
+ catch (_a) {
1820
+ return false;
1821
+ }
1822
+ });
1615
1823
  assertCurrentGeneration();
1616
1824
  return;
1617
1825
  }
@@ -1632,28 +1840,34 @@ class ReactNativeBleTransport {
1632
1840
  }
1633
1841
  });
1634
1842
  }
1635
- writeProtocolV2Frame(transport$1, frame, context, assertCurrentGeneration) {
1843
+ writeProtocolV2Frame(uuid, transport$1, frame, context, assertCurrentGeneration) {
1636
1844
  return __awaiter(this, void 0, void 0, function* () {
1637
1845
  const tuning = getProtocolV2BleTuning();
1638
1846
  const packetCapacity = resolveProtocolV2PacketCapacity({
1639
1847
  platform: reactNative.Platform.OS,
1640
1848
  iosPacketLength: tuning.iosPacketLength,
1641
1849
  androidPacketLength: tuning.androidPacketLength,
1642
- mtu: transport$1.mtuSize,
1850
+ mtu: reactNative.Platform.OS === 'android' ? transport$1.mtuSize : undefined,
1643
1851
  });
1852
+ const initialDelayMs = reactNative.Platform.OS === 'ios' && !context.highVolume && frame.length <= packetCapacity
1853
+ ? IOS_PROTOCOL_V2_CONTROL_WRITE_DELAY_MS
1854
+ : 0;
1644
1855
  yield transport.writeProtocolV2BleFrame({
1645
1856
  frame,
1646
1857
  packetCapacity,
1647
1858
  assertActive: assertCurrentGeneration,
1648
1859
  signal: context.signal,
1649
1860
  abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
1861
+ initialDelayMs,
1862
+ burstSize: FIRMWARE_UPLOAD_WRITE_BURST_SIZE,
1863
+ burstPauseMs: FIRMWARE_UPLOAD_WRITE_PAUSE_MS,
1864
+ flushDelayMs: FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS,
1650
1865
  wait: delay,
1651
- writePacket: packet => this.writeProtocolV2Packet(transport$1, buffer.Buffer.from(packet).toString('base64'), context, assertCurrentGeneration),
1866
+ writePacket: packet => this.writeProtocolV2Packet(uuid, transport$1, buffer.Buffer.from(packet).toString('base64'), context, assertCurrentGeneration),
1652
1867
  });
1653
1868
  });
1654
1869
  }
1655
1870
  callProtocolV2(uuid, name, data, options) {
1656
- var _a;
1657
1871
  return __awaiter(this, void 0, void 0, function* () {
1658
1872
  if (!this._messages || !this._messagesV2) {
1659
1873
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
@@ -1662,35 +1876,11 @@ class ReactNativeBleTransport {
1662
1876
  const highVolumeWrite = transport.LogBlockCommand.has(name);
1663
1877
  if (highVolumeWrite) {
1664
1878
  const tuning = getProtocolV2BleTuning();
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,
1879
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume write configured', {
1880
+ name,
1881
+ writeMode: (options === null || options === void 0 ? void 0 : options.writeWithResponse) ? 'withResponse' : 'withoutResponse',
1882
+ packetCapacity: reactNative.Platform.OS === 'ios' ? tuning.iosPacketLength : tuning.androidPacketLength,
1671
1883
  });
1672
- const packetCapacity = resolveProtocolV2PacketCapacity({
1673
- platform: reactNative.Platform.OS,
1674
- iosPacketLength: tuning.iosPacketLength,
1675
- androidPacketLength: tuning.androidPacketLength,
1676
- mtu: currentTransport.mtuSize,
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);
1694
1884
  }
1695
1885
  try {
1696
1886
  return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, callOptions);
@@ -1699,71 +1889,6 @@ class ReactNativeBleTransport {
1699
1889
  Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] Protocol V2 call error:', e);
1700
1890
  throw e;
1701
1891
  }
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
- }
1767
1892
  });
1768
1893
  }
1769
1894
  createProtocolV2Adapter(uuid) {
@@ -1787,7 +1912,7 @@ class ReactNativeBleTransport {
1787
1912
  writeFrame: (frame, context) => __awaiter(this, void 0, void 0, function* () {
1788
1913
  assertCurrentGeneration();
1789
1914
  const currentTransport = this.getCachedTransport(uuid);
1790
- yield this.writeProtocolV2Frame(currentTransport, frame, context, assertCurrentGeneration);
1915
+ yield this.writeProtocolV2Frame(uuid, currentTransport, frame, context, assertCurrentGeneration);
1791
1916
  }),
1792
1917
  readFrame: () => __awaiter(this, void 0, void 0, function* () {
1793
1918
  assertCurrentGeneration();
@@ -1808,10 +1933,16 @@ class ReactNativeBleTransport {
1808
1933
  };
1809
1934
  }
1810
1935
  getProtocolType(path) {
1811
- return this.deviceProtocol.get(path);
1936
+ return this.getActiveProtocol(path);
1812
1937
  }
1813
1938
  }
1814
1939
 
1940
+ exports.BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD;
1941
+ exports.BLE_CONNECT_TIMEOUT_MS = BLE_CONNECT_TIMEOUT_MS;
1942
+ exports.BLE_GATT_SETUP_TIMEOUT_MS = BLE_GATT_SETUP_TIMEOUT_MS;
1943
+ exports.BLE_WRITE_PACKET_TIMEOUT_MS = BLE_WRITE_PACKET_TIMEOUT_MS;
1944
+ exports.BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD;
1945
+ exports.PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = PROTOCOL_REPROBE_FALLBACK_ATTEMPTS;
1815
1946
  exports.configureProtocolV2BleTuning = configureProtocolV2BleTuning;
1816
1947
  exports["default"] = ReactNativeBleTransport;
1817
1948
  exports.getFirmwareUploadWriteRetryType = getFirmwareUploadWriteRetryType;