@onekeyfe/hd-transport-react-native 1.2.0-alpha.64 → 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,22 +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 PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = 3;
299
- const BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
300
- const CONNECT_TIMEOUT_MESSAGE = 'BLE connect timeout after';
301
- const isConnectTimeoutError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleConnectedError &&
302
- typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' &&
303
- error.message.startsWith(CONNECT_TIMEOUT_MESSAGE);
304
318
  const tryToGetConfiguration = (device) => {
305
319
  if (!device || !device.serviceUUIDs)
306
320
  return null;
@@ -358,12 +372,7 @@ class ReactNativeBleTransport {
358
372
  this.runPromiseDeviceId = null;
359
373
  this.firmwareUploadWriteRecoveryIds = new Set();
360
374
  this.deviceProtocol = new Map();
361
- this.probingProtocols = new Map();
362
- this.writeTimeoutCounts = new Map();
363
- this.connectTimeoutCounts = new Map();
364
375
  this.deviceProtocolHints = new Map();
365
- this.sessionProtocols = new Map();
366
- this.protocolReprobeFailures = new Map();
367
376
  this.protocolV2Assemblers = new Map();
368
377
  this.protocolV2FrameQueues = new Map();
369
378
  this.protocolV2FramePromises = new Map();
@@ -555,12 +564,12 @@ class ReactNativeBleTransport {
555
564
  const isConnected = yield device.isConnected().catch(() => false);
556
565
  if (!isConnected) {
557
566
  try {
558
- device = yield this.connectWithTimeout(uuid, () => device.connect(connectOptions));
567
+ device = yield device.connect(connectOptions);
559
568
  }
560
569
  catch (e) {
561
570
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
562
571
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
563
- device = yield this.connectWithTimeout(uuid, () => device.connect());
572
+ device = yield device.connect();
564
573
  }
565
574
  else if (e.errorCode !== reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
566
575
  throw e;
@@ -636,12 +645,17 @@ class ReactNativeBleTransport {
636
645
  return;
637
646
  }
638
647
  const displayName = getDeviceDisplayName(device);
639
- const isOneKey = hdShared.isOnekeyBluetoothDevice({
640
- id: device === null || device === void 0 ? void 0 : device.id,
641
- name: device === null || device === void 0 ? void 0 : device.name,
642
- localName: device === null || device === void 0 ? void 0 : device.localName,
643
- serviceUuids: device === null || device === void 0 ? void 0 : device.serviceUUIDs,
644
- });
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
+ });
645
659
  if (isOneKey) {
646
660
  addDevice(device);
647
661
  }
@@ -659,12 +673,15 @@ class ReactNativeBleTransport {
659
673
  const localName = 'localName' in device && typeof device.localName === 'string'
660
674
  ? device.localName
661
675
  : null;
662
- if (hdShared.isOnekeyBluetoothDevice({
663
- id: device.id,
664
- name: device.name,
665
- localName,
666
- serviceUuids: device.serviceUUIDs,
667
- })) {
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
+ })) {
668
685
  Log === null || Log === void 0 ? void 0 : Log.debug('search connected peripheral: ', device.id);
669
686
  addDevice(device);
670
687
  }
@@ -779,17 +796,14 @@ class ReactNativeBleTransport {
779
796
  if (!device) {
780
797
  Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device: ', uuid);
781
798
  try {
782
- device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, connectOptions));
799
+ device = yield blePlxManager.connectToDevice(uuid, connectOptions);
783
800
  }
784
801
  catch (e) {
785
802
  Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device has error: ', e);
786
- if (isConnectTimeoutError(e)) {
787
- throw e;
788
- }
789
803
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
790
804
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
791
805
  Log === null || Log === void 0 ? void 0 : Log.debug('first try to reconnect without params');
792
- device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, fallbackConnectOptions));
806
+ device = yield blePlxManager.connectToDevice(uuid);
793
807
  }
794
808
  else if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
795
809
  Log === null || Log === void 0 ? void 0 : Log.debug('device already connected');
@@ -805,27 +819,23 @@ class ReactNativeBleTransport {
805
819
  }
806
820
  if (!(yield device.isConnected())) {
807
821
  Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device: ', uuid);
808
- const disconnectedDevice = device;
809
822
  try {
810
- device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(connectOptions));
823
+ device = yield device.connect(connectOptions);
811
824
  }
812
825
  catch (e) {
813
826
  Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device has error: ', e);
814
- if (isConnectTimeoutError(e)) {
815
- throw e;
816
- }
817
827
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
818
828
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
819
829
  Log === null || Log === void 0 ? void 0 : Log.debug('second try to reconnect without params');
820
830
  try {
821
- device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
831
+ device = yield device.connect();
822
832
  }
823
833
  catch (e) {
824
834
  Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect error: ', e);
825
835
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
826
836
  Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect');
827
- yield disconnectedDevice.cancelConnection();
828
- device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
837
+ yield device.cancelConnection();
838
+ device = yield device.connect();
829
839
  }
830
840
  }
831
841
  }
@@ -881,7 +891,7 @@ class ReactNativeBleTransport {
881
891
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
882
892
  return;
883
893
  }
884
- if (this.getActiveProtocol(uuid) === 'V2') {
894
+ if (this.deviceProtocol.get(uuid) === 'V2') {
885
895
  let errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
886
896
  if ((_a = error.reason) === null || _a === void 0 ? void 0 : _a.includes('The connection has timed out unexpectedly')) {
887
897
  errorCode = hdShared.HardwareErrorCode.BleTimeoutError;
@@ -932,7 +942,7 @@ class ReactNativeBleTransport {
932
942
  }
933
943
  try {
934
944
  const data = buffer.Buffer.from(c.value, 'base64');
935
- const protocol = this.getActiveProtocol(uuid);
945
+ const protocol = this.deviceProtocol.get(uuid);
936
946
  if (!protocol) {
937
947
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor data ignored before protocol detection: ', uuid);
938
948
  return;
@@ -960,7 +970,7 @@ class ReactNativeBleTransport {
960
970
  catch (error) {
961
971
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor data error: ', error);
962
972
  const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
963
- if (this.getActiveProtocol(uuid) === 'V2') {
973
+ if (this.deviceProtocol.get(uuid) === 'V2') {
964
974
  this.rejectProtocolV2Frames(uuid, notifyError);
965
975
  }
966
976
  else if (this.runPromiseDeviceId === uuid) {
@@ -1016,7 +1026,6 @@ class ReactNativeBleTransport {
1016
1026
  delete transportCache[uuid];
1017
1027
  }
1018
1028
  this.deviceProtocol.delete(uuid);
1019
- this.probingProtocols.delete(uuid);
1020
1029
  (_f = this.protocolV2Assemblers.get(uuid)) === null || _f === void 0 ? void 0 : _f.reset();
1021
1030
  this.protocolV2Assemblers.delete(uuid);
1022
1031
  this.resetProtocolV2Frames(uuid);
@@ -1065,19 +1074,8 @@ class ReactNativeBleTransport {
1065
1074
  const transport = this.getCachedTransport(uuid);
1066
1075
  const runPromise = hdShared.createDeferred();
1067
1076
  runPromise.promise.catch(() => undefined);
1068
- const supersededRunPromise = this.runPromise;
1069
- if (supersededRunPromise) {
1070
- supersededRunPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise));
1071
- }
1072
1077
  this.runPromise = runPromise;
1073
1078
  this.runPromiseDeviceId = uuid;
1074
- const releaseOwnershipIfCurrent = () => {
1075
- if (this.runPromise === runPromise) {
1076
- this.runPromise = null;
1077
- this.runPromiseDeviceId = null;
1078
- }
1079
- };
1080
- const isCurrentOwner = () => this.runPromise === runPromise;
1081
1079
  const messages = this._messages;
1082
1080
  const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
1083
1081
  let timeout;
@@ -1098,9 +1096,6 @@ class ReactNativeBleTransport {
1098
1096
  }
1099
1097
  catch (e) {
1100
1098
  onError(e);
1101
- if (isWedgedWriteError(e)) {
1102
- throw e;
1103
- }
1104
1099
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1105
1100
  }
1106
1101
  }
@@ -1128,9 +1123,6 @@ class ReactNativeBleTransport {
1128
1123
  }
1129
1124
  catch (e) {
1130
1125
  onError(e);
1131
- if (isWedgedWriteError(e)) {
1132
- throw e;
1133
- }
1134
1126
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1135
1127
  }
1136
1128
  }
@@ -1141,8 +1133,8 @@ class ReactNativeBleTransport {
1141
1133
  });
1142
1134
  }
1143
1135
  if (name === 'EmmcFileWrite') {
1144
- yield writeChunkedData(buffers, data => this.writeBlePacket(uuid, data, payload => transport.writeWithRetry(payload), isCurrentOwner), e => {
1145
- releaseOwnershipIfCurrent();
1136
+ yield writeChunkedData(buffers, data => transport.writeWithRetry(data), e => {
1137
+ this.runPromise = null;
1146
1138
  Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
1147
1139
  });
1148
1140
  }
@@ -1158,7 +1150,7 @@ class ReactNativeBleTransport {
1158
1150
  let attempt = 0;
1159
1151
  while (true) {
1160
1152
  try {
1161
- yield this.writeBlePacket(uuid, data, payload => transport.writeCharacteristic.writeWithoutResponse(payload), isCurrentOwner);
1153
+ yield transport.writeWithRetry(data);
1162
1154
  return;
1163
1155
  }
1164
1156
  catch (error) {
@@ -1177,7 +1169,7 @@ class ReactNativeBleTransport {
1177
1169
  }
1178
1170
  }
1179
1171
  }), e => {
1180
- releaseOwnershipIfCurrent();
1172
+ this.runPromise = null;
1181
1173
  Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
1182
1174
  });
1183
1175
  }
@@ -1185,14 +1177,17 @@ class ReactNativeBleTransport {
1185
1177
  for (const o of buffers) {
1186
1178
  const outData = o.toString('base64');
1187
1179
  try {
1188
- 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
+ }
1189
1187
  }
1190
1188
  catch (e) {
1191
1189
  Log === null || Log === void 0 ? void 0 : Log.debug('writeCharacteristic write error: ', e);
1192
- releaseOwnershipIfCurrent();
1193
- if (isWedgedWriteError(e)) {
1194
- throw e;
1195
- }
1190
+ this.runPromise = null;
1196
1191
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected) {
1197
1192
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded);
1198
1193
  }
@@ -1232,9 +1227,7 @@ class ReactNativeBleTransport {
1232
1227
  Log === null || Log === void 0 ? void 0 : Log.error('call error: ', e);
1233
1228
  }
1234
1229
  const isProbeTimeout = name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS;
1235
- const isStaleCall = this.runPromise !== runPromise;
1236
1230
  if (!isProbeTimeout &&
1237
- !isStaleCall &&
1238
1231
  (e === null || e === void 0 ? void 0 : e.errorCode) === hdShared.HardwareErrorCode.BleTimeoutError) {
1239
1232
  yield this.disconnect(uuid);
1240
1233
  }
@@ -1305,10 +1298,7 @@ class ReactNativeBleTransport {
1305
1298
  delete transportCache[session];
1306
1299
  }
1307
1300
  this.deviceProtocol.delete(session);
1308
- this.probingProtocols.delete(session);
1309
1301
  this.deviceProtocolHints.delete(session);
1310
- this.sessionProtocols.delete(session);
1311
- this.protocolReprobeFailures.delete(session);
1312
1302
  this.protocolV2Assemblers.delete(session);
1313
1303
  this.resetProtocolV2Frames(session);
1314
1304
  try {
@@ -1329,60 +1319,6 @@ class ReactNativeBleTransport {
1329
1319
  this.runPromise = null;
1330
1320
  this.runPromiseDeviceId = null;
1331
1321
  }
1332
- connectWithTimeout(uuid, connect) {
1333
- return __awaiter(this, void 0, void 0, function* () {
1334
- let timer;
1335
- let timedOut = false;
1336
- const pending = connect();
1337
- pending.catch(() => undefined);
1338
- try {
1339
- const result = yield Promise.race([
1340
- pending,
1341
- new Promise((_, reject) => {
1342
- timer = setTimeout(() => {
1343
- timedOut = true;
1344
- reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `BLE connect timeout after ${BLE_CONNECT_TIMEOUT_MS}ms for ${uuid}`));
1345
- }, BLE_CONNECT_TIMEOUT_MS);
1346
- }),
1347
- ]);
1348
- this.connectTimeoutCounts.delete(uuid);
1349
- return result;
1350
- }
1351
- catch (error) {
1352
- if (timedOut) {
1353
- this.abandonStalledConnect(uuid);
1354
- }
1355
- throw error;
1356
- }
1357
- finally {
1358
- if (timer)
1359
- clearTimeout(timer);
1360
- }
1361
- });
1362
- }
1363
- abandonStalledConnect(uuid) {
1364
- var _a, _b;
1365
- const timeouts = ((_a = this.connectTimeoutCounts.get(uuid)) !== null && _a !== void 0 ? _a : 0) + 1;
1366
- this.connectTimeoutCounts.set(uuid, timeouts);
1367
- Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE connect timed out:', uuid, {
1368
- consecutiveConnectTimeouts: timeouts,
1369
- });
1370
- (_b = this.blePlxManager) === null || _b === void 0 ? void 0 : _b.cancelDeviceConnection(uuid).catch(() => {
1371
- });
1372
- const stalled = transportCache[uuid];
1373
- if (stalled) {
1374
- delete transportCache[uuid];
1375
- }
1376
- this.deviceProtocol.delete(uuid);
1377
- this.probingProtocols.delete(uuid);
1378
- this.protocolV2Assemblers.delete(uuid);
1379
- this.resetProtocolV2Frames(uuid);
1380
- if (timeouts >= BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD) {
1381
- Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE connects wedged repeatedly, resetting BLE manager');
1382
- this.resetPlxManager();
1383
- this.connectTimeoutCounts.delete(uuid);
1384
- }
1385
- }
1386
1322
  getCachedTransport(uuid) {
1387
1323
  const transport = transportCache[uuid];
1388
1324
  if (!transport) {
@@ -1390,82 +1326,6 @@ class ReactNativeBleTransport {
1390
1326
  }
1391
1327
  return transport;
1392
1328
  }
1393
- writeBlePacket(uuid, data, write, isCurrentOwner) {
1394
- return __awaiter(this, void 0, void 0, function* () {
1395
- let timer;
1396
- let timedOut = false;
1397
- try {
1398
- yield Promise.race([
1399
- write(data),
1400
- new Promise((_, reject) => {
1401
- timer = setTimeout(() => {
1402
- timedOut = true;
1403
- reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError, `BLE write timeout after ${BLE_WRITE_PACKET_TIMEOUT_MS}ms`));
1404
- }, BLE_WRITE_PACKET_TIMEOUT_MS);
1405
- }),
1406
- ]);
1407
- this.writeTimeoutCounts.delete(uuid);
1408
- }
1409
- catch (error) {
1410
- if (timedOut) {
1411
- if (isCurrentOwner && !isCurrentOwner()) {
1412
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] stale BLE write timed out, link kept:', uuid);
1413
- }
1414
- else {
1415
- this.tearDownWedgedLink(uuid);
1416
- }
1417
- }
1418
- throw error;
1419
- }
1420
- finally {
1421
- if (timer)
1422
- clearTimeout(timer);
1423
- }
1424
- });
1425
- }
1426
- tearDownWedgedLink(uuid) {
1427
- var _a;
1428
- const timeouts = ((_a = this.writeTimeoutCounts.get(uuid)) !== null && _a !== void 0 ? _a : 0) + 1;
1429
- this.writeTimeoutCounts.set(uuid, timeouts);
1430
- Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE write timed out, tearing down link:', uuid, {
1431
- consecutiveWriteTimeouts: timeouts,
1432
- });
1433
- const wedged = transportCache[uuid];
1434
- this.disconnect(uuid).catch(error => {
1435
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] wedged link teardown failed (ignored):', error);
1436
- });
1437
- if (wedged && transportCache[uuid] === wedged) {
1438
- delete transportCache[uuid];
1439
- }
1440
- this.deviceProtocol.delete(uuid);
1441
- this.probingProtocols.delete(uuid);
1442
- this.protocolV2Assemblers.delete(uuid);
1443
- this.resetProtocolV2Frames(uuid);
1444
- if (timeouts >= BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD) {
1445
- Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE writes wedged repeatedly, resetting BLE manager');
1446
- this.resetPlxManager();
1447
- this.writeTimeoutCounts.delete(uuid);
1448
- }
1449
- }
1450
- resetPlxManager() {
1451
- const manager = this.blePlxManager;
1452
- this.blePlxManager = undefined;
1453
- Object.keys(transportCache).forEach(key => {
1454
- delete transportCache[key];
1455
- });
1456
- this.deviceProtocol.clear();
1457
- this.probingProtocols.clear();
1458
- this.sessionProtocols.clear();
1459
- this.protocolReprobeFailures.clear();
1460
- this.monitorTokens.clear();
1461
- this.protocolV2Assemblers.clear();
1462
- try {
1463
- manager === null || manager === void 0 ? void 0 : manager.destroy();
1464
- }
1465
- catch (error) {
1466
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE manager destroy failed (ignored):', error);
1467
- }
1468
- }
1469
1329
  createProtocolMismatchError(expected) {
1470
1330
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
1471
1331
  }
@@ -1473,24 +1333,24 @@ class ReactNativeBleTransport {
1473
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');
1474
1334
  }
1475
1335
  clearProbeProtocol(uuid, protocol) {
1476
- if (this.probingProtocols.get(uuid) === protocol) {
1477
- this.probingProtocols.delete(uuid);
1478
- }
1479
1336
  if (this.deviceProtocol.get(uuid) === protocol) {
1480
1337
  this.deviceProtocol.delete(uuid);
1481
1338
  }
1482
1339
  }
1483
- getActiveProtocol(uuid) {
1484
- var _a;
1485
- return (_a = this.deviceProtocol.get(uuid)) !== null && _a !== void 0 ? _a : this.probingProtocols.get(uuid);
1486
- }
1487
1340
  detectProtocol(uuid, expectedProtocol, protocolHint, rebuildTransport) {
1488
- var _a;
1489
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
+ }
1490
1351
  if (expectedProtocol === 'V1') {
1491
1352
  if (yield this.probeProtocolV1(uuid)) {
1492
1353
  this.deviceProtocol.set(uuid, 'V1');
1493
- this.sessionProtocols.set(uuid, 'V1');
1494
1354
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1495
1355
  deviceId: uuid,
1496
1356
  protocol: 'V1',
@@ -1503,7 +1363,6 @@ class ReactNativeBleTransport {
1503
1363
  if (expectedProtocol === 'V2') {
1504
1364
  if (yield this.probeProtocolV2(uuid)) {
1505
1365
  this.deviceProtocol.set(uuid, 'V2');
1506
- this.sessionProtocols.set(uuid, 'V2');
1507
1366
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1508
1367
  deviceId: uuid,
1509
1368
  protocol: 'V2',
@@ -1513,13 +1372,7 @@ class ReactNativeBleTransport {
1513
1372
  }
1514
1373
  throw this.createProtocolMismatchError(expectedProtocol);
1515
1374
  }
1516
- const sessionProtocol = this.sessionProtocols.get(uuid);
1517
- const reprobeFailures = (_a = this.protocolReprobeFailures.get(uuid)) !== null && _a !== void 0 ? _a : 0;
1518
- const fullProbeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
1519
- const trustSessionProtocol = sessionProtocol !== undefined &&
1520
- !protocolHint &&
1521
- reprobeFailures < PROTOCOL_REPROBE_FALLBACK_ATTEMPTS;
1522
- const probeOrder = trustSessionProtocol ? [sessionProtocol] : fullProbeOrder;
1375
+ const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
1523
1376
  for (let i = 0; i < probeOrder.length; i += 1) {
1524
1377
  const protocol = probeOrder[i];
1525
1378
  if (i > 0) {
@@ -1534,8 +1387,6 @@ class ReactNativeBleTransport {
1534
1387
  const detected = protocol === 'V1' ? yield this.probeProtocolV1(uuid) : yield this.probeProtocolV2(uuid);
1535
1388
  if (detected) {
1536
1389
  this.deviceProtocol.set(uuid, protocol);
1537
- this.sessionProtocols.set(uuid, protocol);
1538
- this.protocolReprobeFailures.delete(uuid);
1539
1390
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1540
1391
  deviceId: uuid,
1541
1392
  protocol,
@@ -1544,14 +1395,7 @@ class ReactNativeBleTransport {
1544
1395
  return protocol;
1545
1396
  }
1546
1397
  }
1547
- if (trustSessionProtocol) {
1548
- this.protocolReprobeFailures.set(uuid, reprobeFailures + 1);
1549
- }
1550
- else {
1551
- this.protocolReprobeFailures.delete(uuid);
1552
- }
1553
1398
  this.deviceProtocol.delete(uuid);
1554
- this.probingProtocols.delete(uuid);
1555
1399
  throw this.createProtocolDetectionError();
1556
1400
  });
1557
1401
  }
@@ -1603,17 +1447,13 @@ class ReactNativeBleTransport {
1603
1447
  return false;
1604
1448
  }
1605
1449
  try {
1606
- this.probingProtocols.set(uuid, 'V1');
1450
+ this.deviceProtocol.set(uuid, 'V1');
1607
1451
  yield this.callProtocolV1(uuid, 'GetFeatures', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
1608
- this.probingProtocols.delete(uuid);
1609
1452
  return true;
1610
1453
  }
1611
1454
  catch (error) {
1612
1455
  this.clearProbeProtocol(uuid, 'V1');
1613
1456
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe failed:', error);
1614
- if (isWedgedWriteError(error)) {
1615
- throw error;
1616
- }
1617
1457
  return false;
1618
1458
  }
1619
1459
  });
@@ -1624,7 +1464,7 @@ class ReactNativeBleTransport {
1624
1464
  if (!this._messages || !this._messagesV2) {
1625
1465
  return false;
1626
1466
  }
1627
- this.probingProtocols.set(uuid, 'V2');
1467
+ this.deviceProtocol.set(uuid, 'V2');
1628
1468
  (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1629
1469
  const detected = yield transport.probeProtocolV2({
1630
1470
  call: (name, data, options) => this.callProtocolV2(uuid, name, data, options),
@@ -1640,9 +1480,6 @@ class ReactNativeBleTransport {
1640
1480
  if (!detected) {
1641
1481
  this.clearProbeProtocol(uuid, 'V2');
1642
1482
  }
1643
- else {
1644
- this.probingProtocols.delete(uuid);
1645
- }
1646
1483
  return detected;
1647
1484
  });
1648
1485
  }
@@ -1714,8 +1551,10 @@ class ReactNativeBleTransport {
1714
1551
  }
1715
1552
  });
1716
1553
  }
1717
- writeProtocolV2Packet(uuid, transport, base64, context, assertCurrentGeneration) {
1554
+ writeProtocolV2Packet(transport, base64, context, assertCurrentGeneration) {
1718
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));
1719
1558
  let attempt = 0;
1720
1559
  for (;;) {
1721
1560
  assertCurrentGeneration();
@@ -1723,15 +1562,12 @@ class ReactNativeBleTransport {
1723
1562
  throw new Error(`Protocol V2 BLE write aborted for ${context.messageName}`);
1724
1563
  }
1725
1564
  try {
1726
- yield this.writeBlePacket(uuid, base64, payload => transport.writeCharacteristic.writeWithoutResponse(payload), () => {
1727
- try {
1728
- assertCurrentGeneration();
1729
- return !context.signal.aborted;
1730
- }
1731
- catch (_a) {
1732
- return false;
1733
- }
1734
- });
1565
+ if (shouldUseWriteWithResponse) {
1566
+ yield transport.writeCharacteristic.writeWithResponse(base64);
1567
+ }
1568
+ else {
1569
+ yield transport.writeCharacteristic.writeWithoutResponse(base64);
1570
+ }
1735
1571
  assertCurrentGeneration();
1736
1572
  return;
1737
1573
  }
@@ -1752,7 +1588,7 @@ class ReactNativeBleTransport {
1752
1588
  }
1753
1589
  });
1754
1590
  }
1755
- writeProtocolV2Frame(uuid, transport$1, frame, context, assertCurrentGeneration) {
1591
+ writeProtocolV2Frame(transport$1, frame, context, assertCurrentGeneration) {
1756
1592
  return __awaiter(this, void 0, void 0, function* () {
1757
1593
  const tuning = getProtocolV2BleTuning();
1758
1594
  const packetCapacity = resolveProtocolV2PacketCapacity({
@@ -1761,17 +1597,21 @@ class ReactNativeBleTransport {
1761
1597
  androidPacketLength: tuning.androidPacketLength,
1762
1598
  mtu: reactNative.Platform.OS === 'android' ? transport$1.mtuSize : undefined,
1763
1599
  });
1600
+ const initialDelayMs = reactNative.Platform.OS === 'ios' && !context.highVolume && frame.length <= packetCapacity
1601
+ ? IOS_PROTOCOL_V2_CONTROL_WRITE_DELAY_MS
1602
+ : 0;
1764
1603
  yield transport.writeProtocolV2BleFrame({
1765
1604
  frame,
1766
1605
  packetCapacity,
1767
1606
  assertActive: assertCurrentGeneration,
1768
1607
  signal: context.signal,
1769
1608
  abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
1609
+ initialDelayMs,
1770
1610
  burstSize: FIRMWARE_UPLOAD_WRITE_BURST_SIZE,
1771
1611
  burstPauseMs: FIRMWARE_UPLOAD_WRITE_PAUSE_MS,
1772
1612
  flushDelayMs: FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS,
1773
1613
  wait: delay,
1774
- 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),
1775
1615
  });
1776
1616
  });
1777
1617
  }
@@ -1786,7 +1626,7 @@ class ReactNativeBleTransport {
1786
1626
  const tuning = getProtocolV2BleTuning();
1787
1627
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume write configured', {
1788
1628
  name,
1789
- writeMode: 'withoutResponse',
1629
+ writeMode: (options === null || options === void 0 ? void 0 : options.writeWithResponse) ? 'withResponse' : 'withoutResponse',
1790
1630
  packetCapacity: reactNative.Platform.OS === 'ios' ? tuning.iosPacketLength : tuning.androidPacketLength,
1791
1631
  });
1792
1632
  }
@@ -1820,7 +1660,7 @@ class ReactNativeBleTransport {
1820
1660
  writeFrame: (frame, context) => __awaiter(this, void 0, void 0, function* () {
1821
1661
  assertCurrentGeneration();
1822
1662
  const currentTransport = this.getCachedTransport(uuid);
1823
- yield this.writeProtocolV2Frame(uuid, currentTransport, frame, context, assertCurrentGeneration);
1663
+ yield this.writeProtocolV2Frame(currentTransport, frame, context, assertCurrentGeneration);
1824
1664
  }),
1825
1665
  readFrame: () => __awaiter(this, void 0, void 0, function* () {
1826
1666
  assertCurrentGeneration();
@@ -1841,15 +1681,10 @@ class ReactNativeBleTransport {
1841
1681
  };
1842
1682
  }
1843
1683
  getProtocolType(path) {
1844
- return this.getActiveProtocol(path);
1684
+ return this.deviceProtocol.get(path);
1845
1685
  }
1846
1686
  }
1847
1687
 
1848
- exports.BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD;
1849
- exports.BLE_CONNECT_TIMEOUT_MS = BLE_CONNECT_TIMEOUT_MS;
1850
- exports.BLE_WRITE_PACKET_TIMEOUT_MS = BLE_WRITE_PACKET_TIMEOUT_MS;
1851
- exports.BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD;
1852
- exports.PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = PROTOCOL_REPROBE_FALLBACK_ATTEMPTS;
1853
1688
  exports.configureProtocolV2BleTuning = configureProtocolV2BleTuning;
1854
1689
  exports["default"] = ReactNativeBleTransport;
1855
1690
  exports.getFirmwareUploadWriteRetryType = getFirmwareUploadWriteRetryType;