@onekeyfe/hd-transport-react-native 1.2.0-alpha.65 → 1.2.0-alpha.66

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
@@ -210,6 +210,10 @@ class BleTransport {
210
210
  }
211
211
  writeWithRetry(data) {
212
212
  return __awaiter(this, void 0, void 0, function* () {
213
+ if (reactNative.Platform.OS === 'ios' && this.writeCharacteristic.isWritableWithResponse) {
214
+ yield this.writeCharacteristic.writeWithResponse(data);
215
+ return;
216
+ }
213
217
  yield this.writeCharacteristic.writeWithoutResponse(data);
214
218
  });
215
219
  }
@@ -222,9 +226,36 @@ const FIRMWARE_UPLOAD_WRITE_BURST_SIZE = reactNative.Platform.OS === 'ios' ? 4 :
222
226
  const FIRMWARE_UPLOAD_WRITE_PAUSE_MS = reactNative.Platform.OS === 'ios' ? 8 : 10;
223
227
  const FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS = reactNative.Platform.OS === 'ios' ? 24 : 30;
224
228
  const FIRMWARE_UPLOAD_WRITE_MAX_RETRIES = 8;
229
+ const IOS_PROTOCOL_V2_CONTROL_WRITE_DELAY_MS = 5;
225
230
  const ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH = 192;
226
231
  const FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY = reactNative.Platform.OS === 'ios' ? IOS_PACKET_LENGTH : ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH;
227
232
  const ANDROID_GATT_CONGESTED_STATUS = 143;
233
+ const isAsciiWhitespace = (code) => code === 0x09 ||
234
+ code === 0x0a ||
235
+ code === 0x0b ||
236
+ code === 0x0c ||
237
+ code === 0x0d ||
238
+ code === 0x20;
239
+ const hasGattCongestedStatus = (text) => {
240
+ let searchFrom = 0;
241
+ while (searchFrom < text.length) {
242
+ const statusIndex = text.indexOf('status', searchFrom);
243
+ if (statusIndex < 0)
244
+ return false;
245
+ let cursor = statusIndex + 'status'.length;
246
+ while (cursor < text.length && isAsciiWhitespace(text.charCodeAt(cursor)))
247
+ cursor += 1;
248
+ if (text[cursor] === ':' || text[cursor] === '=') {
249
+ cursor += 1;
250
+ while (cursor < text.length && isAsciiWhitespace(text.charCodeAt(cursor)))
251
+ cursor += 1;
252
+ }
253
+ if (text.startsWith(String(ANDROID_GATT_CONGESTED_STATUS), cursor))
254
+ return true;
255
+ searchFrom = statusIndex + 'status'.length;
256
+ }
257
+ return false;
258
+ };
228
259
  const delay = (ms) => new Promise(resolve => {
229
260
  setTimeout(resolve, ms);
230
261
  });
@@ -239,17 +270,11 @@ const getFirmwareUploadWriteRetryType = (error) => {
239
270
  const text = [bleWriteError.reason, bleWriteError.message, bleWriteError.name]
240
271
  .filter(value => typeof value === 'string')
241
272
  .join(' ');
242
- return /GATT_CONGESTED|status\s*[:=]?\s*143/.test(text) ? 'congested' : null;
273
+ return text.includes('GATT_CONGESTED') || hasGattCongestedStatus(text) ? 'congested' : null;
243
274
  };
244
275
  const resolveFirmwareUploadRetryDelay = (attempt, baseDelayMs = 200, maxDelayMs = 1200) => Math.min(baseDelayMs * Math.pow(2, attempt), maxDelayMs);
245
276
  const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
246
277
  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
278
  const DEVICE_SCAN_TIMEOUT_MS = 3000;
254
279
  const IOS_NOTIFY_READY_DELAY_MS = 150;
255
280
  const ANDROID_NOTIFY_READY_DELAY_MS = 300;
@@ -285,24 +310,11 @@ function getDeviceDisplayName(device) {
285
310
  return (device === null || device === void 0 ? void 0 : device.name) || (device === null || device === void 0 ? void 0 : device.localName) || null;
286
311
  }
287
312
  const ANDROID_REQUEST_MTU = 256;
288
- const BLE_NATIVE_CONNECT_TIMEOUT_MS = 3000;
289
313
  const connectOptions = {
290
314
  requestMTU: ANDROID_REQUEST_MTU,
291
- timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
315
+ timeout: 3000,
292
316
  refreshGatt: 'OnConnected',
293
317
  };
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
318
  const tryToGetConfiguration = (device) => {
307
319
  if (!device || !device.serviceUUIDs)
308
320
  return null;
@@ -360,12 +372,7 @@ class ReactNativeBleTransport {
360
372
  this.runPromiseDeviceId = null;
361
373
  this.firmwareUploadWriteRecoveryIds = new Set();
362
374
  this.deviceProtocol = new Map();
363
- this.probingProtocols = new Map();
364
- this.writeTimeoutCounts = new Map();
365
- this.connectionSetupTimeoutCounts = new Map();
366
375
  this.deviceProtocolHints = new Map();
367
- this.sessionProtocols = new Map();
368
- this.protocolReprobeFailures = new Map();
369
376
  this.protocolV2Assemblers = new Map();
370
377
  this.protocolV2FrameQueues = new Map();
371
378
  this.protocolV2FramePromises = new Map();
@@ -557,19 +564,19 @@ class ReactNativeBleTransport {
557
564
  const isConnected = yield device.isConnected().catch(() => false);
558
565
  if (!isConnected) {
559
566
  try {
560
- device = yield this.connectWithTimeout(uuid, () => device.connect(connectOptions));
567
+ device = yield device.connect(connectOptions);
561
568
  }
562
569
  catch (e) {
563
570
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
564
571
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
565
- device = yield this.connectWithTimeout(uuid, () => device.connect());
572
+ device = yield device.connect();
566
573
  }
567
574
  else if (e.errorCode !== reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
568
575
  throw e;
569
576
  }
570
577
  }
571
578
  }
572
- const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, device);
579
+ const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristics(device);
573
580
  transport.device = device;
574
581
  transport.writeCharacteristic = writeCharacteristic;
575
582
  transport.notifyCharacteristic = notifyCharacteristic;
@@ -638,12 +645,17 @@ class ReactNativeBleTransport {
638
645
  return;
639
646
  }
640
647
  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
- });
648
+ const isUnnamedIOSPeripheral = reactNative.Platform.OS === 'ios' && !(displayName === null || displayName === void 0 ? void 0 : displayName.trim());
649
+ const isFindMyPeripheral = hdShared.isPro2FindMyAdvertisementName(device === null || device === void 0 ? void 0 : device.name) ||
650
+ hdShared.isPro2FindMyAdvertisementName(device === null || device === void 0 ? void 0 : device.localName);
651
+ const isOneKey = !isUnnamedIOSPeripheral &&
652
+ !isFindMyPeripheral &&
653
+ hdShared.isOnekeyBluetoothDevice({
654
+ id: device === null || device === void 0 ? void 0 : device.id,
655
+ name: device === null || device === void 0 ? void 0 : device.name,
656
+ localName: device === null || device === void 0 ? void 0 : device.localName,
657
+ serviceUuids: device === null || device === void 0 ? void 0 : device.serviceUUIDs,
658
+ });
647
659
  if (isOneKey) {
648
660
  addDevice(device);
649
661
  }
@@ -661,12 +673,15 @@ class ReactNativeBleTransport {
661
673
  const localName = 'localName' in device && typeof device.localName === 'string'
662
674
  ? device.localName
663
675
  : null;
664
- if (hdShared.isOnekeyBluetoothDevice({
665
- id: device.id,
666
- name: device.name,
667
- localName,
668
- serviceUuids: device.serviceUUIDs,
669
- })) {
676
+ const isFindMyPeripheral = hdShared.isPro2FindMyAdvertisementName(device.name) ||
677
+ hdShared.isPro2FindMyAdvertisementName(localName);
678
+ if (!isFindMyPeripheral &&
679
+ hdShared.isOnekeyBluetoothDevice({
680
+ id: device.id,
681
+ name: device.name,
682
+ localName,
683
+ serviceUuids: device.serviceUUIDs,
684
+ })) {
670
685
  Log === null || Log === void 0 ? void 0 : Log.debug('search connected peripheral: ', device.id);
671
686
  addDevice(device);
672
687
  }
@@ -698,7 +713,7 @@ class ReactNativeBleTransport {
698
713
  }
699
714
  installTransportForAcquire(uuid, device, characteristics) {
700
715
  return __awaiter(this, void 0, void 0, function* () {
701
- const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristicsWithTimeout(uuid, device));
716
+ const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristics(device));
702
717
  const transport$1 = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
703
718
  if (reactNative.Platform.OS === 'android') {
704
719
  transport$1.mtuSize = typeof device.mtu === 'number' ? device.mtu : transport$1.mtuSize;
@@ -781,17 +796,14 @@ class ReactNativeBleTransport {
781
796
  if (!device) {
782
797
  Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device: ', uuid);
783
798
  try {
784
- device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, connectOptions));
799
+ device = yield blePlxManager.connectToDevice(uuid, connectOptions);
785
800
  }
786
801
  catch (e) {
787
802
  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
803
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
792
804
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
793
805
  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));
806
+ device = yield blePlxManager.connectToDevice(uuid);
795
807
  }
796
808
  else if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
797
809
  Log === null || Log === void 0 ? void 0 : Log.debug('device already connected');
@@ -807,27 +819,23 @@ class ReactNativeBleTransport {
807
819
  }
808
820
  if (!(yield device.isConnected())) {
809
821
  Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device: ', uuid);
810
- const disconnectedDevice = device;
811
822
  try {
812
- device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(connectOptions));
823
+ device = yield device.connect(connectOptions);
813
824
  }
814
825
  catch (e) {
815
826
  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
827
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
820
828
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
821
829
  Log === null || Log === void 0 ? void 0 : Log.debug('second try to reconnect without params');
822
830
  try {
823
- device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
831
+ device = yield device.connect();
824
832
  }
825
833
  catch (e) {
826
834
  Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect error: ', e);
827
835
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
828
836
  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));
837
+ yield device.cancelConnection();
838
+ device = yield device.connect();
831
839
  }
832
840
  }
833
841
  }
@@ -838,7 +846,7 @@ class ReactNativeBleTransport {
838
846
  }
839
847
  device = yield requestAndroidMtu(device);
840
848
  const acquiredDevice = device;
841
- const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, acquiredDevice);
849
+ const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristics(acquiredDevice);
842
850
  const protocolHint = expectedProtocol
843
851
  ? undefined
844
852
  : (_b = (_a = input.protocolHint) !== null && _a !== void 0 ? _a : this.deviceProtocolHints.get(uuid)) !== null && _b !== void 0 ? _b : inferProtocolHintFromDeviceName(getDeviceDisplayName(acquiredDevice));
@@ -883,7 +891,7 @@ class ReactNativeBleTransport {
883
891
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
884
892
  return;
885
893
  }
886
- if (this.getActiveProtocol(uuid) === 'V2') {
894
+ if (this.deviceProtocol.get(uuid) === 'V2') {
887
895
  let errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
888
896
  if ((_a = error.reason) === null || _a === void 0 ? void 0 : _a.includes('The connection has timed out unexpectedly')) {
889
897
  errorCode = hdShared.HardwareErrorCode.BleTimeoutError;
@@ -934,7 +942,7 @@ class ReactNativeBleTransport {
934
942
  }
935
943
  try {
936
944
  const data = buffer.Buffer.from(c.value, 'base64');
937
- const protocol = this.getActiveProtocol(uuid);
945
+ const protocol = this.deviceProtocol.get(uuid);
938
946
  if (!protocol) {
939
947
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor data ignored before protocol detection: ', uuid);
940
948
  return;
@@ -962,7 +970,7 @@ class ReactNativeBleTransport {
962
970
  catch (error) {
963
971
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor data error: ', error);
964
972
  const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
965
- if (this.getActiveProtocol(uuid) === 'V2') {
973
+ if (this.deviceProtocol.get(uuid) === 'V2') {
966
974
  this.rejectProtocolV2Frames(uuid, notifyError);
967
975
  }
968
976
  else if (this.runPromiseDeviceId === uuid) {
@@ -1018,7 +1026,6 @@ class ReactNativeBleTransport {
1018
1026
  delete transportCache[uuid];
1019
1027
  }
1020
1028
  this.deviceProtocol.delete(uuid);
1021
- this.probingProtocols.delete(uuid);
1022
1029
  (_f = this.protocolV2Assemblers.get(uuid)) === null || _f === void 0 ? void 0 : _f.reset();
1023
1030
  this.protocolV2Assemblers.delete(uuid);
1024
1031
  this.resetProtocolV2Frames(uuid);
@@ -1067,19 +1074,8 @@ class ReactNativeBleTransport {
1067
1074
  const transport = this.getCachedTransport(uuid);
1068
1075
  const runPromise = hdShared.createDeferred();
1069
1076
  runPromise.promise.catch(() => undefined);
1070
- const supersededRunPromise = this.runPromise;
1071
- if (supersededRunPromise) {
1072
- supersededRunPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise));
1073
- }
1074
1077
  this.runPromise = runPromise;
1075
1078
  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
1079
  const messages = this._messages;
1084
1080
  const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
1085
1081
  let timeout;
@@ -1100,9 +1096,6 @@ class ReactNativeBleTransport {
1100
1096
  }
1101
1097
  catch (e) {
1102
1098
  onError(e);
1103
- if (isWedgedWriteError(e)) {
1104
- throw e;
1105
- }
1106
1099
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1107
1100
  }
1108
1101
  }
@@ -1130,9 +1123,6 @@ class ReactNativeBleTransport {
1130
1123
  }
1131
1124
  catch (e) {
1132
1125
  onError(e);
1133
- if (isWedgedWriteError(e)) {
1134
- throw e;
1135
- }
1136
1126
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1137
1127
  }
1138
1128
  }
@@ -1143,8 +1133,8 @@ class ReactNativeBleTransport {
1143
1133
  });
1144
1134
  }
1145
1135
  if (name === 'EmmcFileWrite') {
1146
- yield writeChunkedData(buffers, data => this.writeBlePacket(uuid, data, payload => transport.writeWithRetry(payload), isCurrentOwner), e => {
1147
- releaseOwnershipIfCurrent();
1136
+ yield writeChunkedData(buffers, data => transport.writeWithRetry(data), e => {
1137
+ this.runPromise = null;
1148
1138
  Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
1149
1139
  });
1150
1140
  }
@@ -1160,7 +1150,7 @@ class ReactNativeBleTransport {
1160
1150
  let attempt = 0;
1161
1151
  while (true) {
1162
1152
  try {
1163
- yield this.writeBlePacket(uuid, data, payload => transport.writeCharacteristic.writeWithoutResponse(payload), isCurrentOwner);
1153
+ yield transport.writeWithRetry(data);
1164
1154
  return;
1165
1155
  }
1166
1156
  catch (error) {
@@ -1179,7 +1169,7 @@ class ReactNativeBleTransport {
1179
1169
  }
1180
1170
  }
1181
1171
  }), e => {
1182
- releaseOwnershipIfCurrent();
1172
+ this.runPromise = null;
1183
1173
  Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
1184
1174
  });
1185
1175
  }
@@ -1187,14 +1177,17 @@ class ReactNativeBleTransport {
1187
1177
  for (const o of buffers) {
1188
1178
  const outData = o.toString('base64');
1189
1179
  try {
1190
- yield this.writeBlePacket(uuid, outData, payload => transport.writeCharacteristic.writeWithoutResponse(payload), isCurrentOwner);
1180
+ const shouldUseWriteWithResponse = reactNative.Platform.OS === 'ios' && transport.writeCharacteristic.isWritableWithResponse;
1181
+ if (shouldUseWriteWithResponse) {
1182
+ yield transport.writeCharacteristic.writeWithResponse(outData);
1183
+ }
1184
+ else {
1185
+ yield transport.writeCharacteristic.writeWithoutResponse(outData);
1186
+ }
1191
1187
  }
1192
1188
  catch (e) {
1193
1189
  Log === null || Log === void 0 ? void 0 : Log.debug('writeCharacteristic write error: ', e);
1194
- releaseOwnershipIfCurrent();
1195
- if (isWedgedWriteError(e)) {
1196
- throw e;
1197
- }
1190
+ this.runPromise = null;
1198
1191
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected) {
1199
1192
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded);
1200
1193
  }
@@ -1234,9 +1227,7 @@ class ReactNativeBleTransport {
1234
1227
  Log === null || Log === void 0 ? void 0 : Log.error('call error: ', e);
1235
1228
  }
1236
1229
  const isProbeTimeout = name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS;
1237
- const isStaleCall = this.runPromise !== runPromise;
1238
1230
  if (!isProbeTimeout &&
1239
- !isStaleCall &&
1240
1231
  (e === null || e === void 0 ? void 0 : e.errorCode) === hdShared.HardwareErrorCode.BleTimeoutError) {
1241
1232
  yield this.disconnect(uuid);
1242
1233
  }
@@ -1307,10 +1298,7 @@ class ReactNativeBleTransport {
1307
1298
  delete transportCache[session];
1308
1299
  }
1309
1300
  this.deviceProtocol.delete(session);
1310
- this.probingProtocols.delete(session);
1311
1301
  this.deviceProtocolHints.delete(session);
1312
- this.sessionProtocols.delete(session);
1313
- this.protocolReprobeFailures.delete(session);
1314
1302
  this.protocolV2Assemblers.delete(session);
1315
1303
  this.resetProtocolV2Frames(session);
1316
1304
  try {
@@ -1331,91 +1319,6 @@ class ReactNativeBleTransport {
1331
1319
  this.runPromise = null;
1332
1320
  this.runPromiseDeviceId = null;
1333
1321
  }
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
1322
  getCachedTransport(uuid) {
1420
1323
  const transport = transportCache[uuid];
1421
1324
  if (!transport) {
@@ -1423,82 +1326,6 @@ class ReactNativeBleTransport {
1423
1326
  }
1424
1327
  return transport;
1425
1328
  }
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
1329
  createProtocolMismatchError(expected) {
1503
1330
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
1504
1331
  }
@@ -1506,24 +1333,24 @@ class ReactNativeBleTransport {
1506
1333
  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
1334
  }
1508
1335
  clearProbeProtocol(uuid, protocol) {
1509
- if (this.probingProtocols.get(uuid) === protocol) {
1510
- this.probingProtocols.delete(uuid);
1511
- }
1512
1336
  if (this.deviceProtocol.get(uuid) === protocol) {
1513
1337
  this.deviceProtocol.delete(uuid);
1514
1338
  }
1515
1339
  }
1516
- getActiveProtocol(uuid) {
1517
- var _a;
1518
- return (_a = this.deviceProtocol.get(uuid)) !== null && _a !== void 0 ? _a : this.probingProtocols.get(uuid);
1519
- }
1520
1340
  detectProtocol(uuid, expectedProtocol, protocolHint, rebuildTransport) {
1521
- var _a;
1522
1341
  return __awaiter(this, void 0, void 0, function* () {
1342
+ if (reactNative.Platform.OS === 'ios' && expectedProtocol) {
1343
+ this.deviceProtocol.set(uuid, expectedProtocol);
1344
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol selected', {
1345
+ deviceId: uuid,
1346
+ protocol: expectedProtocol,
1347
+ source: 'expected',
1348
+ });
1349
+ return expectedProtocol;
1350
+ }
1523
1351
  if (expectedProtocol === 'V1') {
1524
1352
  if (yield this.probeProtocolV1(uuid)) {
1525
1353
  this.deviceProtocol.set(uuid, 'V1');
1526
- this.sessionProtocols.set(uuid, 'V1');
1527
1354
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1528
1355
  deviceId: uuid,
1529
1356
  protocol: 'V1',
@@ -1536,7 +1363,6 @@ class ReactNativeBleTransport {
1536
1363
  if (expectedProtocol === 'V2') {
1537
1364
  if (yield this.probeProtocolV2(uuid)) {
1538
1365
  this.deviceProtocol.set(uuid, 'V2');
1539
- this.sessionProtocols.set(uuid, 'V2');
1540
1366
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1541
1367
  deviceId: uuid,
1542
1368
  protocol: 'V2',
@@ -1546,13 +1372,7 @@ class ReactNativeBleTransport {
1546
1372
  }
1547
1373
  throw this.createProtocolMismatchError(expectedProtocol);
1548
1374
  }
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;
1375
+ const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
1556
1376
  for (let i = 0; i < probeOrder.length; i += 1) {
1557
1377
  const protocol = probeOrder[i];
1558
1378
  if (i > 0) {
@@ -1567,8 +1387,6 @@ class ReactNativeBleTransport {
1567
1387
  const detected = protocol === 'V1' ? yield this.probeProtocolV1(uuid) : yield this.probeProtocolV2(uuid);
1568
1388
  if (detected) {
1569
1389
  this.deviceProtocol.set(uuid, protocol);
1570
- this.sessionProtocols.set(uuid, protocol);
1571
- this.protocolReprobeFailures.delete(uuid);
1572
1390
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1573
1391
  deviceId: uuid,
1574
1392
  protocol,
@@ -1577,14 +1395,7 @@ class ReactNativeBleTransport {
1577
1395
  return protocol;
1578
1396
  }
1579
1397
  }
1580
- if (trustSessionProtocol) {
1581
- this.protocolReprobeFailures.set(uuid, reprobeFailures + 1);
1582
- }
1583
- else {
1584
- this.protocolReprobeFailures.delete(uuid);
1585
- }
1586
1398
  this.deviceProtocol.delete(uuid);
1587
- this.probingProtocols.delete(uuid);
1588
1399
  throw this.createProtocolDetectionError();
1589
1400
  });
1590
1401
  }
@@ -1636,17 +1447,13 @@ class ReactNativeBleTransport {
1636
1447
  return false;
1637
1448
  }
1638
1449
  try {
1639
- this.probingProtocols.set(uuid, 'V1');
1450
+ this.deviceProtocol.set(uuid, 'V1');
1640
1451
  yield this.callProtocolV1(uuid, 'GetFeatures', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
1641
- this.probingProtocols.delete(uuid);
1642
1452
  return true;
1643
1453
  }
1644
1454
  catch (error) {
1645
1455
  this.clearProbeProtocol(uuid, 'V1');
1646
1456
  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
1457
  return false;
1651
1458
  }
1652
1459
  });
@@ -1657,7 +1464,7 @@ class ReactNativeBleTransport {
1657
1464
  if (!this._messages || !this._messagesV2) {
1658
1465
  return false;
1659
1466
  }
1660
- this.probingProtocols.set(uuid, 'V2');
1467
+ this.deviceProtocol.set(uuid, 'V2');
1661
1468
  (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1662
1469
  const detected = yield transport.probeProtocolV2({
1663
1470
  call: (name, data, options) => this.callProtocolV2(uuid, name, data, options),
@@ -1673,9 +1480,6 @@ class ReactNativeBleTransport {
1673
1480
  if (!detected) {
1674
1481
  this.clearProbeProtocol(uuid, 'V2');
1675
1482
  }
1676
- else {
1677
- this.probingProtocols.delete(uuid);
1678
- }
1679
1483
  return detected;
1680
1484
  });
1681
1485
  }
@@ -1747,8 +1551,10 @@ class ReactNativeBleTransport {
1747
1551
  }
1748
1552
  });
1749
1553
  }
1750
- writeProtocolV2Packet(uuid, transport, base64, context, assertCurrentGeneration) {
1554
+ writeProtocolV2Packet(transport, base64, context, assertCurrentGeneration) {
1751
1555
  return __awaiter(this, void 0, void 0, function* () {
1556
+ const shouldUseWriteWithResponse = transport.writeCharacteristic.isWritableWithResponse &&
1557
+ (context.writeWithResponse === true || (reactNative.Platform.OS === 'ios' && !context.highVolume));
1752
1558
  let attempt = 0;
1753
1559
  for (;;) {
1754
1560
  assertCurrentGeneration();
@@ -1756,15 +1562,12 @@ class ReactNativeBleTransport {
1756
1562
  throw new Error(`Protocol V2 BLE write aborted for ${context.messageName}`);
1757
1563
  }
1758
1564
  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
- });
1565
+ if (shouldUseWriteWithResponse) {
1566
+ yield transport.writeCharacteristic.writeWithResponse(base64);
1567
+ }
1568
+ else {
1569
+ yield transport.writeCharacteristic.writeWithoutResponse(base64);
1570
+ }
1768
1571
  assertCurrentGeneration();
1769
1572
  return;
1770
1573
  }
@@ -1785,7 +1588,7 @@ class ReactNativeBleTransport {
1785
1588
  }
1786
1589
  });
1787
1590
  }
1788
- writeProtocolV2Frame(uuid, transport$1, frame, context, assertCurrentGeneration) {
1591
+ writeProtocolV2Frame(transport$1, frame, context, assertCurrentGeneration) {
1789
1592
  return __awaiter(this, void 0, void 0, function* () {
1790
1593
  const tuning = getProtocolV2BleTuning();
1791
1594
  const packetCapacity = resolveProtocolV2PacketCapacity({
@@ -1794,17 +1597,21 @@ class ReactNativeBleTransport {
1794
1597
  androidPacketLength: tuning.androidPacketLength,
1795
1598
  mtu: reactNative.Platform.OS === 'android' ? transport$1.mtuSize : undefined,
1796
1599
  });
1600
+ const initialDelayMs = reactNative.Platform.OS === 'ios' && !context.highVolume && frame.length <= packetCapacity
1601
+ ? IOS_PROTOCOL_V2_CONTROL_WRITE_DELAY_MS
1602
+ : 0;
1797
1603
  yield transport.writeProtocolV2BleFrame({
1798
1604
  frame,
1799
1605
  packetCapacity,
1800
1606
  assertActive: assertCurrentGeneration,
1801
1607
  signal: context.signal,
1802
1608
  abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
1609
+ initialDelayMs,
1803
1610
  burstSize: FIRMWARE_UPLOAD_WRITE_BURST_SIZE,
1804
1611
  burstPauseMs: FIRMWARE_UPLOAD_WRITE_PAUSE_MS,
1805
1612
  flushDelayMs: FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS,
1806
1613
  wait: delay,
1807
- writePacket: packet => this.writeProtocolV2Packet(uuid, transport$1, buffer.Buffer.from(packet).toString('base64'), context, assertCurrentGeneration),
1614
+ writePacket: packet => this.writeProtocolV2Packet(transport$1, buffer.Buffer.from(packet).toString('base64'), context, assertCurrentGeneration),
1808
1615
  });
1809
1616
  });
1810
1617
  }
@@ -1819,7 +1626,7 @@ class ReactNativeBleTransport {
1819
1626
  const tuning = getProtocolV2BleTuning();
1820
1627
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume write configured', {
1821
1628
  name,
1822
- writeMode: 'withoutResponse',
1629
+ writeMode: (options === null || options === void 0 ? void 0 : options.writeWithResponse) ? 'withResponse' : 'withoutResponse',
1823
1630
  packetCapacity: reactNative.Platform.OS === 'ios' ? tuning.iosPacketLength : tuning.androidPacketLength,
1824
1631
  });
1825
1632
  }
@@ -1853,7 +1660,7 @@ class ReactNativeBleTransport {
1853
1660
  writeFrame: (frame, context) => __awaiter(this, void 0, void 0, function* () {
1854
1661
  assertCurrentGeneration();
1855
1662
  const currentTransport = this.getCachedTransport(uuid);
1856
- yield this.writeProtocolV2Frame(uuid, currentTransport, frame, context, assertCurrentGeneration);
1663
+ yield this.writeProtocolV2Frame(currentTransport, frame, context, assertCurrentGeneration);
1857
1664
  }),
1858
1665
  readFrame: () => __awaiter(this, void 0, void 0, function* () {
1859
1666
  assertCurrentGeneration();
@@ -1874,16 +1681,10 @@ class ReactNativeBleTransport {
1874
1681
  };
1875
1682
  }
1876
1683
  getProtocolType(path) {
1877
- return this.getActiveProtocol(path);
1684
+ return this.deviceProtocol.get(path);
1878
1685
  }
1879
1686
  }
1880
1687
 
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
1688
  exports.configureProtocolV2BleTuning = configureProtocolV2BleTuning;
1888
1689
  exports["default"] = ReactNativeBleTransport;
1889
1690
  exports.getFirmwareUploadWriteRetryType = getFirmwareUploadWriteRetryType;