@onekeyfe/hd-transport-react-native 1.2.0-alpha.7 → 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
@@ -117,42 +117,23 @@ const getInfosForServiceUuid = (serviceUuid, deviceType) => {
117
117
  return null;
118
118
  }
119
119
  const normalizedServiceUuid = normalizeBleUuid(serviceUuid);
120
- const service = (_a = services[serviceUuid]) !== null && _a !== void 0 ? _a : Object.values(services).find(item => normalizeBleUuid(item.serviceUuid) === normalizedServiceUuid);
120
+ const service = (_a = services[serviceUuid]) !== null && _a !== void 0 ? _a : Object.values(services).find(item => normalizeBleUuid(item.serviceUuid) === normalizedServiceUuid ||
121
+ hdShared.matchesKnownBleUuid(serviceUuid, hdShared.createKnownBleUuidAliases(item.serviceUuid)));
121
122
  if (!service) {
122
123
  return null;
123
124
  }
124
125
  return service;
125
126
  };
126
127
  const normalizeBleUuid = (uuid) => (uuid !== null && uuid !== void 0 ? uuid : '').replace(/-/g, '').toLowerCase();
127
- const getBleUuidKey = (uuid) => {
128
- const normalized = normalizeBleUuid(uuid);
129
- return normalized.length >= 8 ? normalized.substring(4, 8) : normalized;
130
- };
131
128
  const isSameBleUuid = (left, right) => {
132
- const normalizedLeft = normalizeBleUuid(left);
133
- const normalizedRight = normalizeBleUuid(right);
134
- return (normalizedLeft === normalizedRight ||
135
- (getBleUuidKey(left) !== '' && getBleUuidKey(left) === getBleUuidKey(right)));
129
+ if (!left || !right)
130
+ return false;
131
+ return hdShared.matchesKnownBleUuid(left, hdShared.createKnownBleUuidAliases(right));
136
132
  };
137
133
 
138
134
  function hasWritableCapability(characteristic) {
139
135
  return !!(characteristic.isWritableWithResponse || characteristic.isWritableWithoutResponse);
140
136
  }
141
- function resolveBleWriteMode(characteristic, preferredMode = 'withoutResponse') {
142
- if (preferredMode === 'withoutResponse' && characteristic.isWritableWithoutResponse) {
143
- return 'withoutResponse';
144
- }
145
- if (preferredMode === 'withResponse' && characteristic.isWritableWithResponse) {
146
- return 'withResponse';
147
- }
148
- if (characteristic.isWritableWithoutResponse) {
149
- return 'withoutResponse';
150
- }
151
- if (characteristic.isWritableWithResponse) {
152
- return 'withResponse';
153
- }
154
- return preferredMode;
155
- }
156
137
  function resolveProtocolV2PacketCapacity({ platform, iosPacketLength = IOS_PACKET_LENGTH, androidPacketLength = ANDROID_PACKET_LENGTH, mtu, }) {
157
138
  if (platform === 'ios') {
158
139
  return iosPacketLength;
@@ -189,7 +170,6 @@ const timer = process.env.NODE_ENV === 'development'
189
170
  const subscribeBleOn = (bleManager, ms = 1000) => new Promise((resolve, reject) => {
190
171
  let done = false;
191
172
  const subscription = bleManager.onStateChange(state => {
192
- console.log('ble state -> ', state);
193
173
  if (state === 'PoweredOn') {
194
174
  if (done)
195
175
  return;
@@ -219,7 +199,6 @@ const isHeaderChunk = (chunk) => {
219
199
  return false;
220
200
  };
221
201
 
222
- const Log$1 = bleLogger;
223
202
  class BleTransport {
224
203
  constructor(device, writeCharacteristic, notifyCharacteristic) {
225
204
  this.name = 'ReactNativeBleTransport';
@@ -229,37 +208,16 @@ class BleTransport {
229
208
  this.writeCharacteristic = writeCharacteristic;
230
209
  this.notifyCharacteristic = notifyCharacteristic;
231
210
  }
232
- writeWithRetry(data, retryCount = BleTransport.MAX_RETRIES) {
211
+ writeWithRetry(data) {
233
212
  return __awaiter(this, void 0, void 0, function* () {
234
- try {
235
- yield this.writeCharacteristic.writeWithoutResponse(data);
236
- }
237
- catch (error) {
238
- Log$1 === null || Log$1 === void 0 ? void 0 : Log$1.debug(`Write retry attempt ${BleTransport.MAX_RETRIES - retryCount + 1}, error: ${error}`);
239
- if (retryCount > 0) {
240
- yield hdShared.wait(BleTransport.RETRY_DELAY);
241
- if (error.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected ||
242
- error.errorCode === reactNativeBlePlx.BleErrorCode.CharacteristicNotFound) {
243
- try {
244
- yield this.device.connect();
245
- yield this.device.discoverAllServicesAndCharacteristics();
246
- }
247
- catch (e) {
248
- Log$1 === null || Log$1 === void 0 ? void 0 : Log$1.debug(`Connect or discoverAllServicesAndCharacteristics error: ${e}`);
249
- }
250
- }
251
- else {
252
- Log$1 === null || Log$1 === void 0 ? void 0 : Log$1.debug(`writeCharacteristic error: ${error}`);
253
- }
254
- return this.writeWithRetry(data, retryCount - 1);
255
- }
256
- throw error;
213
+ if (reactNative.Platform.OS === 'ios' && this.writeCharacteristic.isWritableWithResponse) {
214
+ yield this.writeCharacteristic.writeWithResponse(data);
215
+ return;
257
216
  }
217
+ yield this.writeCharacteristic.writeWithoutResponse(data);
258
218
  });
259
219
  }
260
220
  }
261
- BleTransport.MAX_RETRIES = 5;
262
- BleTransport.RETRY_DELAY = 2000;
263
221
 
264
222
  const { check, ProtocolV1, parseConfigure } = transport__default["default"];
265
223
  const Log = bleLogger;
@@ -268,10 +226,36 @@ const FIRMWARE_UPLOAD_WRITE_BURST_SIZE = reactNative.Platform.OS === 'ios' ? 4 :
268
226
  const FIRMWARE_UPLOAD_WRITE_PAUSE_MS = reactNative.Platform.OS === 'ios' ? 8 : 10;
269
227
  const FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS = reactNative.Platform.OS === 'ios' ? 24 : 30;
270
228
  const FIRMWARE_UPLOAD_WRITE_MAX_RETRIES = 8;
271
- const FIRMWARE_UPLOAD_RECONNECT_RETRY_DELAY_MS = 2000;
229
+ const IOS_PROTOCOL_V2_CONTROL_WRITE_DELAY_MS = 5;
272
230
  const ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH = 192;
273
231
  const FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY = reactNative.Platform.OS === 'ios' ? IOS_PACKET_LENGTH : ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH;
274
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
+ };
275
259
  const delay = (ms) => new Promise(resolve => {
276
260
  setTimeout(resolve, ms);
277
261
  });
@@ -279,10 +263,6 @@ const getFirmwareUploadWriteRetryType = (error) => {
279
263
  if (!error || typeof error !== 'object')
280
264
  return null;
281
265
  const bleWriteError = error;
282
- if (bleWriteError.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected ||
283
- bleWriteError.errorCode === reactNativeBlePlx.BleErrorCode.CharacteristicNotFound) {
284
- return 'reconnectable';
285
- }
286
266
  if (bleWriteError.androidErrorCode === ANDROID_GATT_CONGESTED_STATUS ||
287
267
  bleWriteError.status === ANDROID_GATT_CONGESTED_STATUS) {
288
268
  return 'congested';
@@ -290,25 +270,23 @@ const getFirmwareUploadWriteRetryType = (error) => {
290
270
  const text = [bleWriteError.reason, bleWriteError.message, bleWriteError.name]
291
271
  .filter(value => typeof value === 'string')
292
272
  .join(' ');
293
- return /GATT_CONGESTED|status\s*[:=]?\s*143/.test(text) ? 'congested' : null;
273
+ return text.includes('GATT_CONGESTED') || hasGattCongestedStatus(text) ? 'congested' : null;
294
274
  };
295
275
  const resolveFirmwareUploadRetryDelay = (attempt, baseDelayMs = 200, maxDelayMs = 1200) => Math.min(baseDelayMs * Math.pow(2, attempt), maxDelayMs);
296
- const BLE_RESPONSE_TIMEOUT_MS = 30000;
297
276
  const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
298
277
  const PROTOCOL_V2_PROBE_TIMEOUT_MS = 10000;
299
- const DEVICE_SCAN_TIMEOUT_MS = 8000;
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;
284
+ const DEVICE_SCAN_TIMEOUT_MS = 3000;
300
285
  const IOS_NOTIFY_READY_DELAY_MS = 150;
301
286
  const ANDROID_NOTIFY_READY_DELAY_MS = 300;
302
- const HIGH_VOLUME_WRITE_BURST_SIZE = reactNative.Platform.OS === 'ios' ? 4 : 6;
303
- const HIGH_VOLUME_WRITE_PAUSE_MS = reactNative.Platform.OS === 'ios' ? 6 : 2;
304
- const HIGH_VOLUME_WRITE_FLUSH_DELAY_MS = reactNative.Platform.OS === 'ios' ? 20 : 8;
305
287
  const DEFAULT_PROTOCOL_V2_BLE_TUNING = {
306
288
  iosPacketLength: IOS_PACKET_LENGTH,
307
289
  androidPacketLength: ANDROID_PACKET_LENGTH,
308
- highVolumeWriteBurstSize: HIGH_VOLUME_WRITE_BURST_SIZE,
309
- highVolumeWritePauseMs: HIGH_VOLUME_WRITE_PAUSE_MS,
310
- highVolumeWriteFlushDelayMs: HIGH_VOLUME_WRITE_FLUSH_DELAY_MS,
311
- highVolumeWriteWithResponse: false,
312
290
  };
313
291
  let protocolV2BleTuning = Object.assign({}, DEFAULT_PROTOCOL_V2_BLE_TUNING);
314
292
  const normalizePositiveInteger = (value, fallback) => {
@@ -318,20 +296,15 @@ const normalizePositiveInteger = (value, fallback) => {
318
296
  return Math.floor(normalized);
319
297
  };
320
298
  function configureProtocolV2BleTuning(tuning = {}) {
321
- var _a;
322
299
  protocolV2BleTuning = {
323
300
  iosPacketLength: normalizePositiveInteger(tuning.iosPacketLength, protocolV2BleTuning.iosPacketLength),
324
301
  androidPacketLength: normalizePositiveInteger(tuning.androidPacketLength, protocolV2BleTuning.androidPacketLength),
325
- highVolumeWriteBurstSize: normalizePositiveInteger(tuning.highVolumeWriteBurstSize, protocolV2BleTuning.highVolumeWriteBurstSize),
326
- highVolumeWritePauseMs: normalizePositiveInteger(tuning.highVolumeWritePauseMs, protocolV2BleTuning.highVolumeWritePauseMs),
327
- highVolumeWriteFlushDelayMs: normalizePositiveInteger(tuning.highVolumeWriteFlushDelayMs, protocolV2BleTuning.highVolumeWriteFlushDelayMs),
328
- highVolumeWriteWithResponse: (_a = tuning.highVolumeWriteWithResponse) !== null && _a !== void 0 ? _a : protocolV2BleTuning.highVolumeWriteWithResponse,
329
302
  };
330
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 BLE tuning configured:', protocolV2BleTuning);
303
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE tuning configured', protocolV2BleTuning);
331
304
  }
332
305
  function resetProtocolV2BleTuning() {
333
306
  protocolV2BleTuning = Object.assign({}, DEFAULT_PROTOCOL_V2_BLE_TUNING);
334
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 BLE tuning reset:', protocolV2BleTuning);
307
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE tuning reset', protocolV2BleTuning);
335
308
  }
336
309
  function getProtocolV2BleTuning() {
337
310
  return Object.assign({}, protocolV2BleTuning);
@@ -342,19 +315,25 @@ function inferProtocolHintFromDeviceName(name) {
342
315
  function getDeviceDisplayName(device) {
343
316
  return (device === null || device === void 0 ? void 0 : device.name) || (device === null || device === void 0 ? void 0 : device.localName) || null;
344
317
  }
345
- function isGenericBleService(uuid) {
346
- return ['1800', '1801', '180a'].includes(getBleUuidKey(uuid));
347
- }
348
- function hasKnownOneKeyService(device) {
349
- var _a;
350
- return ((_a = device === null || device === void 0 ? void 0 : device.serviceUUIDs) !== null && _a !== void 0 ? _a : []).some(serviceUuid => getInfosForServiceUuid(serviceUuid, 'classic'));
351
- }
352
318
  const ANDROID_REQUEST_MTU = 256;
319
+ const BLE_NATIVE_CONNECT_TIMEOUT_MS = 3000;
353
320
  const connectOptions = {
354
321
  requestMTU: ANDROID_REQUEST_MTU,
355
- timeout: 3000,
322
+ timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
356
323
  refreshGatt: 'OnConnected',
357
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;
358
337
  const tryToGetConfiguration = (device) => {
359
338
  if (!device || !device.serviceUUIDs)
360
339
  return null;
@@ -371,9 +350,10 @@ const requestAndroidMtu = (device) => __awaiter(void 0, void 0, void 0, function
371
350
  return device;
372
351
  try {
373
352
  const mtuDevice = yield device.requestMTU(ANDROID_REQUEST_MTU);
374
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android MTU requested:', {
353
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] MTU configured', {
354
+ deviceId: device.id,
375
355
  requested: ANDROID_REQUEST_MTU,
376
- mtu: mtuDevice.mtu,
356
+ actual: mtuDevice.mtu,
377
357
  });
378
358
  return mtuDevice;
379
359
  }
@@ -408,15 +388,41 @@ class ReactNativeBleTransport {
408
388
  this.stopped = false;
409
389
  this.scanTimeout = DEVICE_SCAN_TIMEOUT_MS;
410
390
  this.runPromise = null;
391
+ this.runPromiseDeviceId = null;
411
392
  this.firmwareUploadWriteRecoveryIds = new Set();
412
393
  this.deviceProtocol = new Map();
394
+ this.probingProtocols = new Map();
395
+ this.writeTimeoutCounts = new Map();
396
+ this.connectionSetupTimeoutCounts = new Map();
413
397
  this.deviceProtocolHints = new Map();
398
+ this.sessionProtocols = new Map();
399
+ this.protocolReprobeFailures = new Map();
414
400
  this.protocolV2Assemblers = new Map();
415
401
  this.protocolV2FrameQueues = new Map();
416
402
  this.protocolV2FramePromises = new Map();
417
- this.activeProtocolV2Call = null;
418
- this.nextProtocolV2CallToken = 1;
403
+ this.protocolV2Links = new transport.ProtocolV2LinkManager({
404
+ getSchemas: () => {
405
+ if (!this._messages || !this._messagesV2) {
406
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
407
+ }
408
+ return {
409
+ protocolV1: this._messages,
410
+ protocolV2: this._messagesV2,
411
+ };
412
+ },
413
+ classifyError: () => 'link-fatal',
414
+ onLinkInvalidated: (uuid, reason) => __awaiter(this, void 0, void 0, function* () {
415
+ var _b;
416
+ (_b = this.protocolV2Assemblers.get(uuid)) === null || _b === void 0 ? void 0 : _b.reset();
417
+ this.rejectProtocolV2Frames(uuid, new Error(reason));
418
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 link invalidated:', uuid, reason);
419
+ if (reason.startsWith('Protocol V2 link-fatal error:')) {
420
+ yield this.releaseNative(uuid, true);
421
+ }
422
+ }),
423
+ });
419
424
  this.monitorTokens = new Map();
425
+ this.disconnectEventTokens = new Map();
420
426
  this.nextMonitorToken = 1;
421
427
  this.scanTimeout = (_a = options.scanTimeout) !== null && _a !== void 0 ? _a : DEVICE_SCAN_TIMEOUT_MS;
422
428
  }
@@ -430,8 +436,18 @@ class ReactNativeBleTransport {
430
436
  this._messages = messages;
431
437
  }
432
438
  configureProtocolV2(signedData) {
439
+ const configuration = typeof signedData === 'string' ? signedData : JSON.stringify(signedData);
440
+ if (this.protocolV2SchemaConfiguration === configuration) {
441
+ return;
442
+ }
443
+ const isReconfiguration = this.protocolV2SchemaConfiguration !== undefined;
433
444
  this._messagesV2 = parseConfigure(signedData);
434
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 schema configured');
445
+ this.protocolV2SchemaConfiguration = configuration;
446
+ if (isReconfiguration) {
447
+ this.protocolV2Links
448
+ .invalidateAllLinks('Protocol V2 schema reconfigured')
449
+ .catch(error => Log === null || Log === void 0 ? void 0 : Log.debug('Protocol V2 schema link cleanup failed:', error));
450
+ }
435
451
  }
436
452
  listen() {
437
453
  }
@@ -442,7 +458,6 @@ class ReactNativeBleTransport {
442
458
  return Promise.resolve(this.blePlxManager);
443
459
  }
444
460
  resolveCharacteristics(device) {
445
- var _a, _b, _c, _d;
446
461
  return __awaiter(this, void 0, void 0, function* () {
447
462
  yield device.discoverAllServicesAndCharacteristics();
448
463
  let infos = tryToGetConfiguration(device);
@@ -459,19 +474,11 @@ class ReactNativeBleTransport {
459
474
  }
460
475
  }
461
476
  }
462
- let fallbackServiceUuid;
463
477
  if (!infos) {
464
478
  const services = yield device.services();
465
479
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Known OneKey service UUID not found, discovered services:', services === null || services === void 0 ? void 0 : services.map(service => service.uuid));
466
- const knownService = services.find(service => getInfosForServiceUuid(service.uuid, 'classic'));
467
- const fallbackService = (_a = knownService !== null && knownService !== void 0 ? knownService : services.find(service => !isGenericBleService(service.uuid))) !== null && _a !== void 0 ? _a : services[0];
468
- if (fallbackService) {
469
- fallbackServiceUuid = fallbackService.uuid;
470
- characteristics = yield device.characteristicsForService(fallbackService.uuid);
471
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Using fallback BLE service:', fallbackService.uuid);
472
- }
473
480
  }
474
- if (!infos && !fallbackServiceUuid) {
481
+ if (!infos) {
475
482
  try {
476
483
  Log === null || Log === void 0 ? void 0 : Log.debug('cancel connection when service not found');
477
484
  yield device.cancelConnection();
@@ -481,9 +488,7 @@ class ReactNativeBleTransport {
481
488
  }
482
489
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
483
490
  }
484
- const serviceUuid = (_b = infos === null || infos === void 0 ? void 0 : infos.serviceUuid) !== null && _b !== void 0 ? _b : fallbackServiceUuid;
485
- const writeUuid = (_c = infos === null || infos === void 0 ? void 0 : infos.writeUuid) !== null && _c !== void 0 ? _c : '00000002-0000-1000-8000-00805f9b34fb';
486
- const notifyUuid = (_d = infos === null || infos === void 0 ? void 0 : infos.notifyUuid) !== null && _d !== void 0 ? _d : '00000003-0000-1000-8000-00805f9b34fb';
491
+ const { serviceUuid, writeUuid, notifyUuid } = infos;
487
492
  if (!serviceUuid) {
488
493
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
489
494
  }
@@ -524,8 +529,8 @@ class ReactNativeBleTransport {
524
529
  attachDisconnectSubscription(transport, device, uuid) {
525
530
  var _a;
526
531
  (_a = transport.disconnectSubscription) === null || _a === void 0 ? void 0 : _a.remove();
532
+ const { monitorToken } = transport;
527
533
  transport.disconnectSubscription = device.onDisconnected(() => {
528
- var _a;
529
534
  if (this.firmwareUploadWriteRecoveryIds.has(uuid)) {
530
535
  Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored during FirmwareUpload write recovery: ', uuid);
531
536
  return;
@@ -534,17 +539,16 @@ class ReactNativeBleTransport {
534
539
  Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored for stale transport: ', device === null || device === void 0 ? void 0 : device.id);
535
540
  return;
536
541
  }
542
+ if (this.monitorTokens.get(uuid) !== monitorToken) {
543
+ Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored for stale generation: ', device === null || device === void 0 ? void 0 : device.id);
544
+ return;
545
+ }
537
546
  try {
538
547
  Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect: ', device === null || device === void 0 ? void 0 : device.id);
539
- (_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit('device-disconnect', {
540
- name: device === null || device === void 0 ? void 0 : device.name,
541
- id: device === null || device === void 0 ? void 0 : device.id,
542
- connectId: device === null || device === void 0 ? void 0 : device.id,
543
- });
544
- if (this.runPromise) {
548
+ this.emitDeviceDisconnect(uuid, device === null || device === void 0 ? void 0 : device.name, monitorToken);
549
+ if (this.runPromise && this.runPromiseDeviceId === uuid) {
545
550
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError);
546
551
  this.runPromise.reject(error);
547
- this.rejectAllProtocolV2Frames(error);
548
552
  }
549
553
  }
550
554
  catch (e) {
@@ -555,6 +559,22 @@ class ReactNativeBleTransport {
555
559
  }
556
560
  });
557
561
  }
562
+ emitDeviceDisconnect(uuid, name, token) {
563
+ var _a;
564
+ if (token === undefined || this.disconnectEventTokens.get(uuid) === token) {
565
+ return;
566
+ }
567
+ if (this.monitorTokens.get(uuid) !== token) {
568
+ Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect event ignored for stale generation: ', uuid);
569
+ return;
570
+ }
571
+ this.disconnectEventTokens.set(uuid, token);
572
+ (_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit(transport.TRANSPORT_EVENT.DEVICE_DISCONNECT, {
573
+ name,
574
+ id: uuid,
575
+ connectId: uuid,
576
+ });
577
+ }
558
578
  reconnectFirmwareUploadTransport(uuid, transport) {
559
579
  var _a, _b;
560
580
  return __awaiter(this, void 0, void 0, function* () {
@@ -568,19 +588,19 @@ class ReactNativeBleTransport {
568
588
  const isConnected = yield device.isConnected().catch(() => false);
569
589
  if (!isConnected) {
570
590
  try {
571
- device = yield device.connect(connectOptions);
591
+ device = yield this.connectWithTimeout(uuid, () => device.connect(connectOptions));
572
592
  }
573
593
  catch (e) {
574
594
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
575
595
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
576
- device = yield device.connect();
596
+ device = yield this.connectWithTimeout(uuid, () => device.connect());
577
597
  }
578
598
  else if (e.errorCode !== reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
579
599
  throw e;
580
600
  }
581
601
  }
582
602
  }
583
- const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristics(device);
603
+ const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, device);
584
604
  transport.device = device;
585
605
  transport.writeCharacteristic = writeCharacteristic;
586
606
  transport.notifyCharacteristic = notifyCharacteristic;
@@ -624,13 +644,12 @@ class ReactNativeBleTransport {
624
644
  return;
625
645
  }
626
646
  }
627
- blePlxManager.startDeviceScan(null, {
647
+ blePlxManager.startDeviceScan(getBluetoothServiceUuids(), {
628
648
  allowDuplicates: true,
629
649
  scanMode: reactNativeBlePlx.ScanMode.LowLatency,
630
650
  }, (error, device) => {
631
- var _a, _b, _c;
651
+ var _a;
632
652
  if (error) {
633
- Log === null || Log === void 0 ? void 0 : Log.debug('ble scan manager: ', blePlxManager);
634
653
  Log === null || Log === void 0 ? void 0 : Log.debug('ble scan error: ', error);
635
654
  if ([reactNativeBlePlx.BleErrorCode.BluetoothPoweredOff, reactNativeBlePlx.BleErrorCode.BluetoothInUnknownState].includes(error.errorCode)) {
636
655
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePermissionError));
@@ -650,25 +669,19 @@ class ReactNativeBleTransport {
650
669
  return;
651
670
  }
652
671
  const displayName = getDeviceDisplayName(device);
653
- const isOneKey = hdShared.isOnekeyDevice((_b = device === null || device === void 0 ? void 0 : device.name) !== null && _b !== void 0 ? _b : null, device === null || device === void 0 ? void 0 : device.id) ||
654
- hdShared.isOnekeyDevice((_c = device === null || device === void 0 ? void 0 : device.localName) !== null && _c !== void 0 ? _c : null, device === null || device === void 0 ? void 0 : device.id) ||
655
- hasKnownOneKeyService(device);
656
- const shouldTraceCandidate = !!displayName && /onekey|bixinkey|pro\s*2|pro\b|touch|^k\d|^t\d/i.test(displayName);
657
- if (shouldTraceCandidate) {
658
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] scan candidate', {
672
+ const isUnnamedIOSPeripheral = reactNative.Platform.OS === 'ios' && !(displayName === null || displayName === void 0 ? void 0 : displayName.trim());
673
+ const isFindMyPeripheral = hdShared.isPro2FindMyAdvertisementName(device === null || device === void 0 ? void 0 : device.name) ||
674
+ hdShared.isPro2FindMyAdvertisementName(device === null || device === void 0 ? void 0 : device.localName);
675
+ const isOneKey = !isUnnamedIOSPeripheral &&
676
+ !isFindMyPeripheral &&
677
+ hdShared.isOnekeyBluetoothDevice({
678
+ id: device === null || device === void 0 ? void 0 : device.id,
659
679
  name: device === null || device === void 0 ? void 0 : device.name,
660
680
  localName: device === null || device === void 0 ? void 0 : device.localName,
661
- id: device === null || device === void 0 ? void 0 : device.id,
662
- serviceUUIDs: device === null || device === void 0 ? void 0 : device.serviceUUIDs,
663
- accepted: isOneKey,
681
+ serviceUuids: device === null || device === void 0 ? void 0 : device.serviceUUIDs,
664
682
  });
665
- }
666
683
  if (isOneKey) {
667
- Log === null || Log === void 0 ? void 0 : Log.debug('search device start ======================');
668
- const { name, localName, id, serviceUUIDs } = device !== null && device !== void 0 ? device : {};
669
- Log === null || Log === void 0 ? void 0 : Log.debug(`device name: ${name !== null && name !== void 0 ? name : ''}\nlocalName: ${localName !== null && localName !== void 0 ? localName : ''}\nid: ${id !== null && id !== void 0 ? id : ''}\nserviceUUIDs: ${(serviceUUIDs !== null && serviceUUIDs !== void 0 ? serviceUUIDs : []).join(',')}`);
670
684
  addDevice(device);
671
- Log === null || Log === void 0 ? void 0 : Log.debug('search device end ======================\n');
672
685
  }
673
686
  else if (displayName && /\bpro\s*2\b/i.test(displayName)) {
674
687
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Pro2-like BLE device was not accepted:', {
@@ -679,10 +692,23 @@ class ReactNativeBleTransport {
679
692
  });
680
693
  }
681
694
  });
682
- getConnectedDeviceIds(getBluetoothServiceUuids()).then(devices => {
695
+ getConnectedDeviceIds(reactNative.Platform.OS === 'ios' ? getBluetoothServiceUuids() : []).then(devices => {
683
696
  for (const device of devices) {
684
- Log === null || Log === void 0 ? void 0 : Log.debug('search connected peripheral: ', device.id);
685
- addDevice(device);
697
+ const localName = 'localName' in device && typeof device.localName === 'string'
698
+ ? device.localName
699
+ : null;
700
+ const isFindMyPeripheral = hdShared.isPro2FindMyAdvertisementName(device.name) ||
701
+ hdShared.isPro2FindMyAdvertisementName(localName);
702
+ if (!isFindMyPeripheral &&
703
+ hdShared.isOnekeyBluetoothDevice({
704
+ id: device.id,
705
+ name: device.name,
706
+ localName,
707
+ serviceUuids: device.serviceUUIDs,
708
+ })) {
709
+ Log === null || Log === void 0 ? void 0 : Log.debug('search connected peripheral: ', device.id);
710
+ addDevice(device);
711
+ }
686
712
  }
687
713
  });
688
714
  const addDevice = (device) => {
@@ -694,6 +720,12 @@ class ReactNativeBleTransport {
694
720
  this.deviceProtocolHints.set(device.id, protocolHint);
695
721
  }
696
722
  deviceList.push(Object.assign(Object.assign({}, device), { name: displayName, commType: 'ble' }));
723
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] OneKey BLE device discovered', {
724
+ deviceId: device.id,
725
+ name: displayName,
726
+ serviceUUIDs: device.serviceUUIDs,
727
+ protocolHint,
728
+ });
697
729
  }
698
730
  };
699
731
  timer.timeout(() => {
@@ -703,6 +735,33 @@ class ReactNativeBleTransport {
703
735
  }));
704
736
  });
705
737
  }
738
+ installTransportForAcquire(uuid, device, characteristics) {
739
+ return __awaiter(this, void 0, void 0, function* () {
740
+ const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristicsWithTimeout(uuid, device));
741
+ const transport$1 = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
742
+ if (reactNative.Platform.OS === 'android') {
743
+ transport$1.mtuSize = typeof device.mtu === 'number' ? device.mtu : transport$1.mtuSize;
744
+ }
745
+ const monitorToken = this.nextMonitorToken;
746
+ this.nextMonitorToken += 1;
747
+ const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
748
+ transport$1.monitorToken = monitorToken;
749
+ transport$1.notifyTransactionId = notifyTransactionId;
750
+ this.monitorTokens.set(uuid, monitorToken);
751
+ transport$1.notifySubscription = this._monitorCharacteristic(transport$1.notifyCharacteristic, uuid, monitorToken, notifyTransactionId);
752
+ transportCache[uuid] = transport$1;
753
+ this.protocolV2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES));
754
+ if (reactNative.Platform.OS === 'ios') {
755
+ yield new Promise(resolve => {
756
+ setTimeout(resolve, IOS_NOTIFY_READY_DELAY_MS);
757
+ });
758
+ }
759
+ else if (reactNative.Platform.OS === 'android') {
760
+ yield delay(ANDROID_NOTIFY_READY_DELAY_MS);
761
+ }
762
+ return transport$1;
763
+ });
764
+ }
706
765
  acquire(input) {
707
766
  var _a, _b;
708
767
  return __awaiter(this, void 0, void 0, function* () {
@@ -727,9 +786,8 @@ class ReactNativeBleTransport {
727
786
  if (forceCleanRunPromise && this.runPromise) {
728
787
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
729
788
  this.runPromise.reject(error);
730
- this.rejectAllProtocolV2Frames(error);
731
789
  this.runPromise = null;
732
- this.activeProtocolV2Call = null;
790
+ this.runPromiseDeviceId = null;
733
791
  Log === null || Log === void 0 ? void 0 : Log.debug('Force clean Bluetooth run promise, forceCleanRunPromise: ', forceCleanRunPromise);
734
792
  }
735
793
  const blePlxManager = yield this.getPlxManager();
@@ -762,14 +820,17 @@ class ReactNativeBleTransport {
762
820
  if (!device) {
763
821
  Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device: ', uuid);
764
822
  try {
765
- device = yield blePlxManager.connectToDevice(uuid, connectOptions);
823
+ device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, connectOptions));
766
824
  }
767
825
  catch (e) {
768
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
+ }
769
830
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
770
831
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
771
832
  Log === null || Log === void 0 ? void 0 : Log.debug('first try to reconnect without params');
772
- device = yield blePlxManager.connectToDevice(uuid);
833
+ device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, fallbackConnectOptions));
773
834
  }
774
835
  else if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
775
836
  Log === null || Log === void 0 ? void 0 : Log.debug('device already connected');
@@ -785,23 +846,27 @@ class ReactNativeBleTransport {
785
846
  }
786
847
  if (!(yield device.isConnected())) {
787
848
  Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device: ', uuid);
849
+ const disconnectedDevice = device;
788
850
  try {
789
- device = yield device.connect(connectOptions);
851
+ device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(connectOptions));
790
852
  }
791
853
  catch (e) {
792
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
+ }
793
858
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
794
859
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
795
860
  Log === null || Log === void 0 ? void 0 : Log.debug('second try to reconnect without params');
796
861
  try {
797
- device = yield device.connect();
862
+ device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
798
863
  }
799
864
  catch (e) {
800
865
  Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect error: ', e);
801
866
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
802
867
  Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect');
803
- yield device.cancelConnection();
804
- device = yield device.connect();
868
+ yield disconnectedDevice.cancelConnection();
869
+ device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
805
870
  }
806
871
  }
807
872
  }
@@ -811,50 +876,41 @@ class ReactNativeBleTransport {
811
876
  }
812
877
  }
813
878
  device = yield requestAndroidMtu(device);
814
- const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristics(device);
879
+ const acquiredDevice = device;
880
+ const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, acquiredDevice);
815
881
  const protocolHint = expectedProtocol
816
882
  ? undefined
817
- : (_a = this.deviceProtocolHints.get(uuid)) !== null && _a !== void 0 ? _a : inferProtocolHintFromDeviceName(getDeviceDisplayName(device));
883
+ : (_b = (_a = input.protocolHint) !== null && _a !== void 0 ? _a : this.deviceProtocolHints.get(uuid)) !== null && _b !== void 0 ? _b : inferProtocolHintFromDeviceName(getDeviceDisplayName(acquiredDevice));
818
884
  yield this.release(uuid, true);
819
885
  if (protocolHint) {
820
886
  this.deviceProtocolHints.set(uuid, protocolHint);
821
887
  }
822
- const transport$1 = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
823
- if (reactNative.Platform.OS === 'android') {
824
- transport$1.mtuSize = typeof device.mtu === 'number' ? device.mtu : transport$1.mtuSize;
825
- }
826
- const monitorToken = this.nextMonitorToken;
827
- this.nextMonitorToken += 1;
828
- const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
829
- transport$1.monitorToken = monitorToken;
830
- transport$1.notifyTransactionId = notifyTransactionId;
831
- this.monitorTokens.set(uuid, monitorToken);
832
- transport$1.notifySubscription = this._monitorCharacteristic(transport$1.notifyCharacteristic, uuid, monitorToken, notifyTransactionId);
833
- transportCache[uuid] = transport$1;
834
- this.protocolV2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler());
835
- if (reactNative.Platform.OS === 'ios') {
836
- yield new Promise(resolve => {
837
- setTimeout(resolve, IOS_NOTIFY_READY_DELAY_MS);
838
- });
888
+ yield this.installTransportForAcquire(uuid, acquiredDevice, {
889
+ writeCharacteristic,
890
+ notifyCharacteristic,
891
+ });
892
+ try {
893
+ const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint, () => __awaiter(this, void 0, void 0, function* () {
894
+ yield this.installTransportForAcquire(uuid, acquiredDevice);
895
+ }));
896
+ const currentTransport = transportCache[uuid];
897
+ if (!currentTransport) {
898
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
899
+ }
900
+ this.attachDisconnectSubscription(currentTransport, acquiredDevice, uuid);
901
+ return { uuid, protocolType };
839
902
  }
840
- else if (reactNative.Platform.OS === 'android') {
841
- yield delay(ANDROID_NOTIFY_READY_DELAY_MS);
903
+ catch (error) {
904
+ yield this.release(uuid, true);
905
+ throw error;
842
906
  }
843
- const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint);
844
- (_b = this.emitter) === null || _b === void 0 ? void 0 : _b.emit('device-connect', {
845
- name: device.name,
846
- id: device.id,
847
- connectId: device.id,
848
- });
849
- this.attachDisconnectSubscription(transport$1, device, uuid);
850
- return { uuid, protocolType };
851
907
  });
852
908
  }
853
909
  _monitorCharacteristic(characteristic, uuid, monitorToken, notifyTransactionId) {
854
910
  let bufferLength = 0;
855
911
  let buffer$1 = [];
856
912
  const subscription = characteristic.monitor((error, c) => {
857
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
913
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
858
914
  const isCurrentMonitor = this.monitorTokens.get(uuid) === monitorToken;
859
915
  if (error) {
860
916
  Log === null || Log === void 0 ? void 0 : Log.debug(`error monitor ${characteristic.uuid}, deviceId: ${characteristic.deviceID}: ${error}`);
@@ -866,28 +922,44 @@ class ReactNativeBleTransport {
866
922
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
867
923
  return;
868
924
  }
869
- if (this.runPromise) {
870
- let ERROR = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
925
+ if (this.getActiveProtocol(uuid) === 'V2') {
926
+ let errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
871
927
  if ((_a = error.reason) === null || _a === void 0 ? void 0 : _a.includes('The connection has timed out unexpectedly')) {
872
- ERROR = hdShared.HardwareErrorCode.BleTimeoutError;
928
+ errorCode = hdShared.HardwareErrorCode.BleTimeoutError;
873
929
  }
874
- if ((_b = error.reason) === null || _b === void 0 ? void 0 : _b.includes('Encryption is insufficient')) {
875
- ERROR = hdShared.HardwareErrorCode.BleDeviceBondError;
930
+ else if ((_b = error.reason) === null || _b === void 0 ? void 0 : _b.includes('Encryption is insufficient')) {
931
+ errorCode = hdShared.HardwareErrorCode.BleDeviceBondError;
876
932
  }
877
- if (((_c = error.reason) === null || _c === void 0 ? void 0 : _c.includes('Cannot write client characteristic config descriptor')) ||
933
+ else if (((_c = error.reason) === null || _c === void 0 ? void 0 : _c.includes('Cannot write client characteristic config descriptor')) ||
878
934
  ((_d = error.reason) === null || _d === void 0 ? void 0 : _d.includes('Cannot find client characteristic config descriptor')) ||
879
935
  ((_e = error.reason) === null || _e === void 0 ? void 0 : _e.includes('The handle is invalid')) ||
880
936
  ((_f = error.reason) === null || _f === void 0 ? void 0 : _f.includes('Writing is not permitted')) ||
881
937
  ((_g = error.reason) === null || _g === void 0 ? void 0 : _g.includes('notify change failed for device'))) {
938
+ errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure;
939
+ }
940
+ this.rejectProtocolV2Frames(uuid, hdShared.ERRORS.TypedError(errorCode));
941
+ return;
942
+ }
943
+ if (this.runPromise && this.runPromiseDeviceId === uuid) {
944
+ let ERROR = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
945
+ if ((_h = error.reason) === null || _h === void 0 ? void 0 : _h.includes('The connection has timed out unexpectedly')) {
946
+ ERROR = hdShared.HardwareErrorCode.BleTimeoutError;
947
+ }
948
+ if ((_j = error.reason) === null || _j === void 0 ? void 0 : _j.includes('Encryption is insufficient')) {
949
+ ERROR = hdShared.HardwareErrorCode.BleDeviceBondError;
950
+ }
951
+ if (((_k = error.reason) === null || _k === void 0 ? void 0 : _k.includes('Cannot write client characteristic config descriptor')) ||
952
+ ((_l = error.reason) === null || _l === void 0 ? void 0 : _l.includes('Cannot find client characteristic config descriptor')) ||
953
+ ((_m = error.reason) === null || _m === void 0 ? void 0 : _m.includes('The handle is invalid')) ||
954
+ ((_o = error.reason) === null || _o === void 0 ? void 0 : _o.includes('Writing is not permitted')) ||
955
+ ((_p = error.reason) === null || _p === void 0 ? void 0 : _p.includes('notify change failed for device'))) {
882
956
  const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure);
883
957
  this.runPromise.reject(notifyError);
884
- this.rejectAllProtocolV2Frames(notifyError);
885
958
  Log === null || Log === void 0 ? void 0 : Log.debug(`${hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure} ${error.message} ${error.reason}`);
886
959
  return;
887
960
  }
888
961
  const notifyError = hdShared.ERRORS.TypedError(ERROR);
889
962
  this.runPromise.reject(notifyError);
890
- this.rejectAllProtocolV2Frames(notifyError);
891
963
  Log === null || Log === void 0 ? void 0 : Log.debug(': monitor notify error, and has unreleased Promise', Error);
892
964
  }
893
965
  return;
@@ -901,13 +973,13 @@ class ReactNativeBleTransport {
901
973
  }
902
974
  try {
903
975
  const data = buffer.Buffer.from(c.value, 'base64');
904
- const protocol = this.deviceProtocol.get(uuid);
976
+ const protocol = this.getActiveProtocol(uuid);
905
977
  if (!protocol) {
906
978
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor data ignored before protocol detection: ', uuid);
907
979
  return;
908
980
  }
909
981
  if (protocol === 'V2') {
910
- this.handleProtocolV2Notification(uuid, new Uint8Array(data));
982
+ this.handleProtocolV2Notification(uuid, monitorToken, new Uint8Array(data));
911
983
  return;
912
984
  }
913
985
  if (isHeaderChunk(data)) {
@@ -921,28 +993,40 @@ class ReactNativeBleTransport {
921
993
  const value = buffer.Buffer.from(buffer$1);
922
994
  bufferLength = 0;
923
995
  buffer$1 = [];
924
- (_h = this.runPromise) === null || _h === void 0 ? void 0 : _h.resolve(value.toString('hex'));
996
+ if (this.runPromiseDeviceId === uuid) {
997
+ (_q = this.runPromise) === null || _q === void 0 ? void 0 : _q.resolve(value.toString('hex'));
998
+ }
925
999
  }
926
1000
  }
927
1001
  catch (error) {
928
1002
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor data error: ', error);
929
1003
  const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
930
- (_j = this.runPromise) === null || _j === void 0 ? void 0 : _j.reject(notifyError);
931
- this.rejectAllProtocolV2Frames(notifyError);
1004
+ if (this.getActiveProtocol(uuid) === 'V2') {
1005
+ this.rejectProtocolV2Frames(uuid, notifyError);
1006
+ }
1007
+ else if (this.runPromiseDeviceId === uuid) {
1008
+ (_r = this.runPromise) === null || _r === void 0 ? void 0 : _r.reject(notifyError);
1009
+ }
932
1010
  }
933
1011
  }, notifyTransactionId);
934
1012
  return subscription;
935
1013
  }
936
1014
  release(uuid, onclose = false) {
937
- var _a, _b, _c, _d, _e, _f, _g, _h;
1015
+ return __awaiter(this, void 0, void 0, function* () {
1016
+ yield this.protocolV2Links.invalidateLink(uuid, 'React Native BLE transport released');
1017
+ return this.releaseNative(uuid, onclose);
1018
+ });
1019
+ }
1020
+ releaseNative(uuid, onclose = false) {
1021
+ var _a, _b, _c, _d, _e, _f, _g;
938
1022
  return __awaiter(this, void 0, void 0, function* () {
939
1023
  const transport = transportCache[uuid];
940
- if (this.runPromise) {
1024
+ if (this.runPromise && this.runPromiseDeviceId === uuid) {
941
1025
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
942
1026
  this.runPromise.reject(error);
943
1027
  this.runPromise = null;
944
- this.rejectAllProtocolV2Frames(error);
945
- this.activeProtocolV2Call = null;
1028
+ this.runPromiseDeviceId = null;
1029
+ this.rejectProtocolV2Frames(uuid, error);
946
1030
  }
947
1031
  else {
948
1032
  this.resetProtocolV2Frames(uuid);
@@ -950,9 +1034,6 @@ class ReactNativeBleTransport {
950
1034
  if (reactNative.Platform.OS === 'android' && !onclose && transport) {
951
1035
  (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
952
1036
  this.resetProtocolV2Frames(uuid);
953
- if (((_b = this.activeProtocolV2Call) === null || _b === void 0 ? void 0 : _b.uuid) === uuid) {
954
- this.activeProtocolV2Call = null;
955
- }
956
1037
  return Promise.resolve(true);
957
1038
  }
958
1039
  if (transport) {
@@ -960,14 +1041,14 @@ class ReactNativeBleTransport {
960
1041
  this.monitorTokens.delete(uuid);
961
1042
  }
962
1043
  Log === null || Log === void 0 ? void 0 : Log.debug('release: removing disconnect subscription for device: ', uuid);
963
- (_c = transport.disconnectSubscription) === null || _c === void 0 ? void 0 : _c.remove();
1044
+ (_b = transport.disconnectSubscription) === null || _b === void 0 ? void 0 : _b.remove();
964
1045
  transport.disconnectSubscription = undefined;
965
- Log === null || Log === void 0 ? void 0 : Log.debug('release: removing notify subscription, characteristic: ', (_d = transport.notifyCharacteristic) === null || _d === void 0 ? void 0 : _d.uuid);
966
- (_e = transport.notifySubscription) === null || _e === void 0 ? void 0 : _e.remove();
1046
+ Log === null || Log === void 0 ? void 0 : Log.debug('release: removing notify subscription, characteristic: ', (_c = transport.notifyCharacteristic) === null || _c === void 0 ? void 0 : _c.uuid);
1047
+ (_d = transport.notifySubscription) === null || _d === void 0 ? void 0 : _d.remove();
967
1048
  transport.notifySubscription = undefined;
968
1049
  if (transport.notifyTransactionId) {
969
1050
  try {
970
- yield ((_f = this.blePlxManager) === null || _f === void 0 ? void 0 : _f.cancelTransaction(transport.notifyTransactionId));
1051
+ yield ((_e = this.blePlxManager) === null || _e === void 0 ? void 0 : _e.cancelTransaction(transport.notifyTransactionId));
971
1052
  }
972
1053
  catch (e) {
973
1054
  Log === null || Log === void 0 ? void 0 : Log.debug('release: cancel notify transaction error (ignored): ', (e === null || e === void 0 ? void 0 : e.message) || e);
@@ -976,12 +1057,12 @@ class ReactNativeBleTransport {
976
1057
  delete transportCache[uuid];
977
1058
  }
978
1059
  this.deviceProtocol.delete(uuid);
979
- this.deviceProtocolHints.delete(uuid);
980
- (_g = this.protocolV2Assemblers.get(uuid)) === null || _g === void 0 ? void 0 : _g.reset();
1060
+ this.probingProtocols.delete(uuid);
1061
+ (_f = this.protocolV2Assemblers.get(uuid)) === null || _f === void 0 ? void 0 : _f.reset();
981
1062
  this.protocolV2Assemblers.delete(uuid);
982
1063
  this.resetProtocolV2Frames(uuid);
983
1064
  try {
984
- yield ((_h = this.blePlxManager) === null || _h === void 0 ? void 0 : _h.cancelTransaction(uuid));
1065
+ yield ((_g = this.blePlxManager) === null || _g === void 0 ? void 0 : _g.cancelTransaction(uuid));
985
1066
  }
986
1067
  catch (e) {
987
1068
  Log === null || Log === void 0 ? void 0 : Log.debug('release: cancel transaction error (ignored): ', (e === null || e === void 0 ? void 0 : e.message) || e);
@@ -1002,30 +1083,18 @@ class ReactNativeBleTransport {
1002
1083
  if (this._messages == null) {
1003
1084
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
1004
1085
  }
1005
- const forceRun = name === 'Initialize' || name === 'Cancel';
1006
- Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native call this.runPromise', this.runPromise);
1007
- if (this.runPromise && !forceRun) {
1008
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportCallInProgress);
1009
- }
1010
1086
  const protocol = this.getProtocolType(uuid);
1011
1087
  if (!protocol) {
1012
1088
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${uuid}`);
1013
1089
  }
1014
- if (name === 'ResourceUpdate' || name === 'ResourceAck') {
1015
- Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native', 'call-', ' name: ', name, ' data: ', {
1016
- file_name: data === null || data === void 0 ? void 0 : data.file_name,
1017
- hash: data === null || data === void 0 ? void 0 : data.hash,
1018
- });
1019
- }
1020
- else if (transport.LogBlockCommand.has(name)) {
1021
- Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native', 'call-', ' name: ', name, ' protocol: ', protocol);
1022
- }
1023
- else {
1024
- Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native', 'call-', ' name: ', name, ' data: ', data, ' protocol: ', protocol);
1025
- }
1090
+ Log === null || Log === void 0 ? void 0 : Log.debug('transport call', transport.createTransportCallLog(name, protocol, data));
1026
1091
  if (protocol === 'V2') {
1027
1092
  return this.callProtocolV2(uuid, name, data, options);
1028
1093
  }
1094
+ const forceRun = name === 'Initialize' || name === 'Cancel';
1095
+ if (this.runPromise && !forceRun) {
1096
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportCallInProgress);
1097
+ }
1029
1098
  return this.callProtocolV1(uuid, name, data, options);
1030
1099
  });
1031
1100
  }
@@ -1037,7 +1106,19 @@ class ReactNativeBleTransport {
1037
1106
  const transport = this.getCachedTransport(uuid);
1038
1107
  const runPromise = hdShared.createDeferred();
1039
1108
  runPromise.promise.catch(() => undefined);
1109
+ const supersededRunPromise = this.runPromise;
1110
+ if (supersededRunPromise) {
1111
+ supersededRunPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise));
1112
+ }
1040
1113
  this.runPromise = runPromise;
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;
1041
1122
  const messages = this._messages;
1042
1123
  const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
1043
1124
  let timeout;
@@ -1058,6 +1139,9 @@ class ReactNativeBleTransport {
1058
1139
  }
1059
1140
  catch (e) {
1060
1141
  onError(e);
1142
+ if (isWedgedWriteError(e)) {
1143
+ throw e;
1144
+ }
1061
1145
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1062
1146
  }
1063
1147
  }
@@ -1085,6 +1169,9 @@ class ReactNativeBleTransport {
1085
1169
  }
1086
1170
  catch (e) {
1087
1171
  onError(e);
1172
+ if (isWedgedWriteError(e)) {
1173
+ throw e;
1174
+ }
1088
1175
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1089
1176
  }
1090
1177
  }
@@ -1095,13 +1182,13 @@ class ReactNativeBleTransport {
1095
1182
  });
1096
1183
  }
1097
1184
  if (name === 'EmmcFileWrite') {
1098
- yield writeChunkedData(buffers, data => transport.writeWithRetry(data), e => {
1099
- this.runPromise = null;
1185
+ yield writeChunkedData(buffers, data => this.writeBlePacket(uuid, data, payload => transport.writeWithRetry(payload), isCurrentOwner), e => {
1186
+ releaseOwnershipIfCurrent();
1100
1187
  Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
1101
1188
  });
1102
1189
  }
1103
1190
  else if (name === 'FirmwareUpload') {
1104
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] FirmwareUpload write uses throttled BLE packets:', {
1191
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Firmware upload transport configured', {
1105
1192
  packetCapacity: FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY,
1106
1193
  burstSize: FIRMWARE_UPLOAD_WRITE_BURST_SIZE,
1107
1194
  pauseMs: FIRMWARE_UPLOAD_WRITE_PAUSE_MS,
@@ -1112,7 +1199,7 @@ class ReactNativeBleTransport {
1112
1199
  let attempt = 0;
1113
1200
  while (true) {
1114
1201
  try {
1115
- yield transport.writeCharacteristic.writeWithoutResponse(data);
1202
+ yield this.writeBlePacket(uuid, data, payload => transport.writeWithRetry(payload), isCurrentOwner);
1116
1203
  return;
1117
1204
  }
1118
1205
  catch (error) {
@@ -1120,36 +1207,18 @@ class ReactNativeBleTransport {
1120
1207
  if (!retryType || attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
1121
1208
  throw error;
1122
1209
  }
1123
- const shouldReconnect = retryType === 'reconnectable';
1124
- const delayMs = shouldReconnect
1125
- ? FIRMWARE_UPLOAD_RECONNECT_RETRY_DELAY_MS
1126
- : resolveFirmwareUploadRetryDelay(attempt);
1210
+ const delayMs = resolveFirmwareUploadRetryDelay(attempt);
1127
1211
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] FirmwareUpload write retry:', {
1128
1212
  attempt: attempt + 1,
1129
1213
  delayMs,
1130
- reconnect: shouldReconnect,
1131
1214
  error,
1132
1215
  });
1133
- if (shouldReconnect) {
1134
- this.firmwareUploadWriteRecoveryIds.add(uuid);
1135
- }
1136
1216
  yield delay(delayMs);
1137
1217
  attempt += 1;
1138
- if (shouldReconnect) {
1139
- try {
1140
- yield this.reconnectFirmwareUploadTransport(uuid, transport);
1141
- }
1142
- catch (e) {
1143
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] FirmwareUpload reconnect error:', e);
1144
- if (attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
1145
- throw e;
1146
- }
1147
- }
1148
- }
1149
1218
  }
1150
1219
  }
1151
1220
  }), e => {
1152
- this.runPromise = null;
1221
+ releaseOwnershipIfCurrent();
1153
1222
  Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
1154
1223
  });
1155
1224
  }
@@ -1157,11 +1226,17 @@ class ReactNativeBleTransport {
1157
1226
  for (const o of buffers) {
1158
1227
  const outData = o.toString('base64');
1159
1228
  try {
1160
- yield transport.writeCharacteristic.writeWithoutResponse(outData);
1229
+ const shouldUseWriteWithResponse = reactNative.Platform.OS === 'ios' && transport.writeCharacteristic.isWritableWithResponse;
1230
+ yield this.writeBlePacket(uuid, outData, payload => shouldUseWriteWithResponse
1231
+ ? transport.writeCharacteristic.writeWithResponse(payload)
1232
+ : transport.writeCharacteristic.writeWithoutResponse(payload), isCurrentOwner);
1161
1233
  }
1162
1234
  catch (e) {
1163
1235
  Log === null || Log === void 0 ? void 0 : Log.debug('writeCharacteristic write error: ', e);
1164
- this.runPromise = null;
1236
+ releaseOwnershipIfCurrent();
1237
+ if (isWedgedWriteError(e)) {
1238
+ throw e;
1239
+ }
1165
1240
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected) {
1166
1241
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded);
1167
1242
  }
@@ -1190,17 +1265,23 @@ class ReactNativeBleTransport {
1190
1265
  if (typeof response !== 'string') {
1191
1266
  throw new Error('Returning data is not string.');
1192
1267
  }
1193
- Log === null || Log === void 0 ? void 0 : Log.debug('receive data: ', response);
1194
1268
  const jsonData = ProtocolV1.decodeMessage(messages, response);
1195
1269
  return check.call(jsonData);
1196
1270
  }
1197
1271
  catch (e) {
1198
- if (name === 'Initialize' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS) {
1199
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 Initialize probe call failed:', e);
1272
+ if (name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS) {
1273
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe call failed:', e);
1200
1274
  }
1201
1275
  else {
1202
1276
  Log === null || Log === void 0 ? void 0 : Log.error('call error: ', e);
1203
1277
  }
1278
+ const isProbeTimeout = name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS;
1279
+ const isStaleCall = this.runPromise !== runPromise;
1280
+ if (!isProbeTimeout &&
1281
+ !isStaleCall &&
1282
+ (e === null || e === void 0 ? void 0 : e.errorCode) === hdShared.HardwareErrorCode.BleTimeoutError) {
1283
+ yield this.disconnect(uuid);
1284
+ }
1204
1285
  throw e;
1205
1286
  }
1206
1287
  finally {
@@ -1208,6 +1289,7 @@ class ReactNativeBleTransport {
1208
1289
  clearTimeout(timeout);
1209
1290
  if (this.runPromise === runPromise) {
1210
1291
  this.runPromise = null;
1292
+ this.runPromiseDeviceId = null;
1211
1293
  }
1212
1294
  }
1213
1295
  });
@@ -1216,10 +1298,11 @@ class ReactNativeBleTransport {
1216
1298
  this.stopped = true;
1217
1299
  }
1218
1300
  disconnect(session) {
1219
- var _a, _b, _c, _d, _e, _f;
1301
+ var _a, _b, _c, _d, _e;
1220
1302
  return __awaiter(this, void 0, void 0, function* () {
1221
- Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native transport resetSession: ', session);
1303
+ yield this.protocolV2Links.invalidateLink(session, 'React Native BLE transport disconnected');
1222
1304
  const transport = transportCache[session];
1305
+ const monitorToken = (_a = transport === null || transport === void 0 ? void 0 : transport.monitorToken) !== null && _a !== void 0 ? _a : this.monitorTokens.get(session);
1223
1306
  if (transport === null || transport === void 0 ? void 0 : transport.disconnectSubscription) {
1224
1307
  try {
1225
1308
  Log === null || Log === void 0 ? void 0 : Log.debug('disconnect: removing disconnect subscription');
@@ -1232,7 +1315,7 @@ class ReactNativeBleTransport {
1232
1315
  }
1233
1316
  if (transport === null || transport === void 0 ? void 0 : transport.notifySubscription) {
1234
1317
  try {
1235
- Log === null || Log === void 0 ? void 0 : Log.debug('disconnect: removing notify subscription, characteristic: ', (_a = transport.notifyCharacteristic) === null || _a === void 0 ? void 0 : _a.uuid);
1318
+ Log === null || Log === void 0 ? void 0 : Log.debug('disconnect: removing notify subscription, characteristic: ', (_b = transport.notifyCharacteristic) === null || _b === void 0 ? void 0 : _b.uuid);
1236
1319
  transport.notifySubscription.remove();
1237
1320
  transport.notifySubscription = undefined;
1238
1321
  }
@@ -1242,7 +1325,7 @@ class ReactNativeBleTransport {
1242
1325
  }
1243
1326
  if (session) {
1244
1327
  try {
1245
- yield ((_b = this.blePlxManager) === null || _b === void 0 ? void 0 : _b.cancelTransaction(session));
1328
+ yield ((_c = this.blePlxManager) === null || _c === void 0 ? void 0 : _c.cancelTransaction(session));
1246
1329
  }
1247
1330
  catch (e) {
1248
1331
  Log === null || Log === void 0 ? void 0 : Log.debug('resetSession: cancel transaction error (ignored): ', (e === null || e === void 0 ? void 0 : e.message) || e);
@@ -1257,7 +1340,7 @@ class ReactNativeBleTransport {
1257
1340
  }
1258
1341
  }
1259
1342
  try {
1260
- yield ((_c = this.blePlxManager) === null || _c === void 0 ? void 0 : _c.cancelDeviceConnection(session));
1343
+ yield ((_d = this.blePlxManager) === null || _d === void 0 ? void 0 : _d.cancelDeviceConnection(session));
1261
1344
  }
1262
1345
  catch (e) {
1263
1346
  Log === null || Log === void 0 ? void 0 : Log.debug('resetSession: manager.cancelDeviceConnection error (ignored): ', (e === null || e === void 0 ? void 0 : e.message) || e);
@@ -1266,22 +1349,21 @@ class ReactNativeBleTransport {
1266
1349
  delete transportCache[session];
1267
1350
  }
1268
1351
  this.deviceProtocol.delete(session);
1352
+ this.probingProtocols.delete(session);
1269
1353
  this.deviceProtocolHints.delete(session);
1354
+ this.sessionProtocols.delete(session);
1355
+ this.protocolReprobeFailures.delete(session);
1270
1356
  this.protocolV2Assemblers.delete(session);
1271
1357
  this.resetProtocolV2Frames(session);
1272
- if (((_d = this.activeProtocolV2Call) === null || _d === void 0 ? void 0 : _d.uuid) === session) {
1273
- this.activeProtocolV2Call = null;
1274
- }
1275
1358
  try {
1276
- (_e = this.emitter) === null || _e === void 0 ? void 0 : _e.emit('device-disconnect', {
1277
- name: (_f = transport === null || transport === void 0 ? void 0 : transport.device) === null || _f === void 0 ? void 0 : _f.name,
1278
- id: session,
1279
- connectId: session,
1280
- });
1359
+ this.emitDeviceDisconnect(session, (_e = transport === null || transport === void 0 ? void 0 : transport.device) === null || _e === void 0 ? void 0 : _e.name, monitorToken);
1281
1360
  }
1282
1361
  catch (e) {
1283
1362
  Log === null || Log === void 0 ? void 0 : Log.error('resetSession: emit disconnect event error: ', e);
1284
1363
  }
1364
+ if (monitorToken !== undefined && this.monitorTokens.get(session) === monitorToken) {
1365
+ this.monitorTokens.delete(session);
1366
+ }
1285
1367
  yield new Promise(resolve => setTimeout(() => resolve(), 100));
1286
1368
  });
1287
1369
  }
@@ -1289,6 +1371,92 @@ class ReactNativeBleTransport {
1289
1371
  Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native transport cancel');
1290
1372
  if (this.runPromise) ;
1291
1373
  this.runPromise = null;
1374
+ this.runPromiseDeviceId = null;
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
+ }
1292
1460
  }
1293
1461
  getCachedTransport(uuid) {
1294
1462
  const transport = transportCache[uuid];
@@ -1297,58 +1465,187 @@ class ReactNativeBleTransport {
1297
1465
  }
1298
1466
  return transport;
1299
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
+ }
1300
1544
  createProtocolMismatchError(expected) {
1301
1545
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
1302
1546
  }
1303
1547
  createProtocolDetectionError() {
1304
- return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1 Initialize or Protocol V2 Ping');
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');
1305
1549
  }
1306
1550
  clearProbeProtocol(uuid, protocol) {
1551
+ if (this.probingProtocols.get(uuid) === protocol) {
1552
+ this.probingProtocols.delete(uuid);
1553
+ }
1307
1554
  if (this.deviceProtocol.get(uuid) === protocol) {
1308
1555
  this.deviceProtocol.delete(uuid);
1309
1556
  }
1310
1557
  }
1311
- detectProtocol(uuid, expectedProtocol, protocolHint) {
1558
+ getActiveProtocol(uuid) {
1559
+ var _a;
1560
+ return (_a = this.deviceProtocol.get(uuid)) !== null && _a !== void 0 ? _a : this.probingProtocols.get(uuid);
1561
+ }
1562
+ detectProtocol(uuid, expectedProtocol, protocolHint, rebuildTransport) {
1563
+ var _a;
1312
1564
  return __awaiter(this, void 0, void 0, function* () {
1565
+ if (reactNative.Platform.OS === 'ios' && expectedProtocol) {
1566
+ this.deviceProtocol.set(uuid, expectedProtocol);
1567
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol selected', {
1568
+ deviceId: uuid,
1569
+ protocol: expectedProtocol,
1570
+ source: 'expected',
1571
+ });
1572
+ return expectedProtocol;
1573
+ }
1313
1574
  if (expectedProtocol === 'V1') {
1314
1575
  if (yield this.probeProtocolV1(uuid)) {
1315
1576
  this.deviceProtocol.set(uuid, 'V1');
1316
- Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> V1 (expected)`);
1577
+ this.sessionProtocols.set(uuid, 'V1');
1578
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1579
+ deviceId: uuid,
1580
+ protocol: 'V1',
1581
+ source: 'expected',
1582
+ });
1317
1583
  return 'V1';
1318
1584
  }
1319
1585
  throw this.createProtocolMismatchError(expectedProtocol);
1320
1586
  }
1321
1587
  if (expectedProtocol === 'V2') {
1322
- this.deviceProtocol.set(uuid, 'V2');
1323
- Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> V2 (expected)`);
1324
- return 'V2';
1588
+ if (yield this.probeProtocolV2(uuid)) {
1589
+ this.deviceProtocol.set(uuid, 'V2');
1590
+ this.sessionProtocols.set(uuid, 'V2');
1591
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1592
+ deviceId: uuid,
1593
+ protocol: 'V2',
1594
+ source: 'expected',
1595
+ });
1596
+ return 'V2';
1597
+ }
1598
+ throw this.createProtocolMismatchError(expectedProtocol);
1325
1599
  }
1326
- 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;
1327
1607
  for (let i = 0; i < probeOrder.length; i += 1) {
1328
1608
  const protocol = probeOrder[i];
1329
1609
  if (i > 0) {
1330
1610
  yield this.resetProbeStateAfterProtocolProbe(uuid, probeOrder[i - 1]);
1611
+ if (!transportCache[uuid]) {
1612
+ if (!rebuildTransport) {
1613
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
1614
+ }
1615
+ yield rebuildTransport();
1616
+ }
1331
1617
  }
1332
1618
  const detected = protocol === 'V1' ? yield this.probeProtocolV1(uuid) : yield this.probeProtocolV2(uuid);
1333
1619
  if (detected) {
1334
1620
  this.deviceProtocol.set(uuid, protocol);
1335
- Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> ${protocol}`);
1621
+ this.sessionProtocols.set(uuid, protocol);
1622
+ this.protocolReprobeFailures.delete(uuid);
1623
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1624
+ deviceId: uuid,
1625
+ protocol,
1626
+ source: 'probe',
1627
+ });
1336
1628
  return protocol;
1337
1629
  }
1338
1630
  }
1631
+ if (trustSessionProtocol) {
1632
+ this.protocolReprobeFailures.set(uuid, reprobeFailures + 1);
1633
+ }
1634
+ else {
1635
+ this.protocolReprobeFailures.delete(uuid);
1636
+ }
1339
1637
  this.deviceProtocol.delete(uuid);
1638
+ this.probingProtocols.delete(uuid);
1340
1639
  throw this.createProtocolDetectionError();
1341
1640
  });
1342
1641
  }
1343
1642
  resetProbeStateAfterProtocolProbe(uuid, protocol) {
1344
- var _a, _b, _c, _d;
1643
+ var _a, _b, _c;
1345
1644
  return __awaiter(this, void 0, void 0, function* () {
1346
1645
  const transport = transportCache[uuid];
1646
+ yield this.protocolV2Links.invalidateLink(uuid, `Reset notify state after Protocol ${protocol} probe`);
1347
1647
  (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1348
1648
  this.resetProtocolV2Frames(uuid);
1349
- if (((_b = this.activeProtocolV2Call) === null || _b === void 0 ? void 0 : _b.uuid) === uuid) {
1350
- this.activeProtocolV2Call = null;
1351
- }
1352
1649
  if (this.runPromise) {
1353
1650
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
1354
1651
  this.runPromise.reject(error);
@@ -1360,11 +1657,11 @@ class ReactNativeBleTransport {
1360
1657
  if (this.monitorTokens.get(uuid) === transport.monitorToken) {
1361
1658
  this.monitorTokens.delete(uuid);
1362
1659
  }
1363
- (_c = transport.notifySubscription) === null || _c === void 0 ? void 0 : _c.remove();
1660
+ (_b = transport.notifySubscription) === null || _b === void 0 ? void 0 : _b.remove();
1364
1661
  transport.notifySubscription = undefined;
1365
1662
  if (previousNotifyTransactionId) {
1366
1663
  try {
1367
- yield ((_d = this.blePlxManager) === null || _d === void 0 ? void 0 : _d.cancelTransaction(previousNotifyTransactionId));
1664
+ yield ((_c = this.blePlxManager) === null || _c === void 0 ? void 0 : _c.cancelTransaction(previousNotifyTransactionId));
1368
1665
  }
1369
1666
  catch (error) {
1370
1667
  Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] cancel notify after Protocol ${protocol} probe failed:`, (error === null || error === void 0 ? void 0 : error.message) || error);
@@ -1390,13 +1687,17 @@ class ReactNativeBleTransport {
1390
1687
  return false;
1391
1688
  }
1392
1689
  try {
1393
- this.deviceProtocol.set(uuid, 'V1');
1394
- yield this.callProtocolV1(uuid, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
1690
+ this.probingProtocols.set(uuid, 'V1');
1691
+ yield this.callProtocolV1(uuid, 'GetFeatures', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
1692
+ this.probingProtocols.delete(uuid);
1395
1693
  return true;
1396
1694
  }
1397
1695
  catch (error) {
1398
1696
  this.clearProbeProtocol(uuid, 'V1');
1399
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 Initialize probe failed:', error);
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
+ }
1400
1701
  return false;
1401
1702
  }
1402
1703
  });
@@ -1407,7 +1708,7 @@ class ReactNativeBleTransport {
1407
1708
  if (!this._messages || !this._messagesV2) {
1408
1709
  return false;
1409
1710
  }
1410
- this.deviceProtocol.set(uuid, 'V2');
1711
+ this.probingProtocols.set(uuid, 'V2');
1411
1712
  (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1412
1713
  const detected = yield transport.probeProtocolV2({
1413
1714
  call: (name, data, options) => this.callProtocolV2(uuid, name, data, options),
@@ -1423,17 +1724,16 @@ class ReactNativeBleTransport {
1423
1724
  if (!detected) {
1424
1725
  this.clearProbeProtocol(uuid, 'V2');
1425
1726
  }
1727
+ else {
1728
+ this.probingProtocols.delete(uuid);
1729
+ }
1426
1730
  return detected;
1427
1731
  });
1428
1732
  }
1429
- handleProtocolV2Notification(uuid, data) {
1430
- var _a, _b, _c;
1733
+ handleProtocolV2Notification(uuid, monitorToken, data) {
1431
1734
  try {
1432
- if (!this.runPromise || ((_a = this.activeProtocolV2Call) === null || _a === void 0 ? void 0 : _a.uuid) !== uuid) {
1433
- (_b = this.protocolV2Assemblers.get(uuid)) === null || _b === void 0 ? void 0 : _b.reset();
1434
- this.resetProtocolV2Frames(uuid);
1735
+ if (this.monitorTokens.get(uuid) !== monitorToken)
1435
1736
  return;
1436
- }
1437
1737
  if (data.length === 0)
1438
1738
  return;
1439
1739
  const assembler = this.protocolV2Assemblers.get(uuid);
@@ -1446,8 +1746,10 @@ class ReactNativeBleTransport {
1446
1746
  catch (error) {
1447
1747
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 notification error:', error);
1448
1748
  const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1449
- (_c = this.runPromise) === null || _c === void 0 ? void 0 : _c.reject(notifyError);
1450
- this.rejectAllProtocolV2Frames(notifyError);
1749
+ this.rejectProtocolV2Frames(uuid, notifyError);
1750
+ this.protocolV2Links
1751
+ .invalidateLink(uuid, `Protocol V2 notification error: ${error}`)
1752
+ .catch(invalidateError => Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 notify cleanup failed:', invalidateError));
1451
1753
  }
1452
1754
  }
1453
1755
  getProtocolV2FrameQueue(uuid) {
@@ -1467,20 +1769,16 @@ class ReactNativeBleTransport {
1467
1769
  }
1468
1770
  this.getProtocolV2FrameQueue(uuid).push(frame);
1469
1771
  }
1470
- rejectAllProtocolV2Frames(error) {
1471
- this.protocolV2FrameQueues.clear();
1472
- for (const framePromise of this.protocolV2FramePromises.values()) {
1473
- framePromise.reject(error);
1474
- }
1475
- this.protocolV2FramePromises.clear();
1476
- }
1477
1772
  resetProtocolV2Frames(uuid) {
1478
- this.protocolV2FrameQueues.delete(uuid);
1479
- this.protocolV2FramePromises.delete(uuid);
1773
+ this.rejectProtocolV2Frames(uuid, new Error(`Protocol V2 frame state reset for ${uuid}`));
1480
1774
  }
1481
- isActiveProtocolV2Call(uuid, token) {
1482
- var _a;
1483
- return ((_a = this.activeProtocolV2Call) === null || _a === void 0 ? void 0 : _a.uuid) === uuid && this.activeProtocolV2Call.token === token;
1775
+ rejectProtocolV2Frames(uuid, error) {
1776
+ this.protocolV2FrameQueues.delete(uuid);
1777
+ const framePromise = this.protocolV2FramePromises.get(uuid);
1778
+ if (framePromise) {
1779
+ this.protocolV2FramePromises.delete(uuid);
1780
+ framePromise.reject(error);
1781
+ }
1484
1782
  }
1485
1783
  readProtocolV2Frame(uuid) {
1486
1784
  return __awaiter(this, void 0, void 0, function* () {
@@ -1500,133 +1798,153 @@ class ReactNativeBleTransport {
1500
1798
  }
1501
1799
  });
1502
1800
  }
1503
- writeProtocolV2Frame(transport, frame, options) {
1801
+ writeProtocolV2Packet(uuid, transport, base64, context, assertCurrentGeneration) {
1802
+ return __awaiter(this, void 0, void 0, function* () {
1803
+ const shouldUseWriteWithResponse = transport.writeCharacteristic.isWritableWithResponse &&
1804
+ (context.writeWithResponse === true || (reactNative.Platform.OS === 'ios' && !context.highVolume));
1805
+ let attempt = 0;
1806
+ for (;;) {
1807
+ assertCurrentGeneration();
1808
+ if (context.signal.aborted) {
1809
+ throw new Error(`Protocol V2 BLE write aborted for ${context.messageName}`);
1810
+ }
1811
+ try {
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
+ });
1823
+ assertCurrentGeneration();
1824
+ return;
1825
+ }
1826
+ catch (error) {
1827
+ if (getFirmwareUploadWriteRetryType(error) !== 'congested' ||
1828
+ attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
1829
+ throw error;
1830
+ }
1831
+ const delayMs = resolveFirmwareUploadRetryDelay(attempt);
1832
+ attempt += 1;
1833
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 congested write retry:', {
1834
+ name: context.messageName,
1835
+ attempt,
1836
+ delayMs,
1837
+ });
1838
+ yield delay(delayMs);
1839
+ }
1840
+ }
1841
+ });
1842
+ }
1843
+ writeProtocolV2Frame(uuid, transport$1, frame, context, assertCurrentGeneration) {
1504
1844
  return __awaiter(this, void 0, void 0, function* () {
1505
1845
  const tuning = getProtocolV2BleTuning();
1506
1846
  const packetCapacity = resolveProtocolV2PacketCapacity({
1507
1847
  platform: reactNative.Platform.OS,
1508
1848
  iosPacketLength: tuning.iosPacketLength,
1509
1849
  androidPacketLength: tuning.androidPacketLength,
1510
- mtu: reactNative.Platform.OS === 'android' ? transport.mtuSize : undefined,
1850
+ mtu: reactNative.Platform.OS === 'android' ? transport$1.mtuSize : undefined,
1851
+ });
1852
+ const initialDelayMs = reactNative.Platform.OS === 'ios' && !context.highVolume && frame.length <= packetCapacity
1853
+ ? IOS_PROTOCOL_V2_CONTROL_WRITE_DELAY_MS
1854
+ : 0;
1855
+ yield transport.writeProtocolV2BleFrame({
1856
+ frame,
1857
+ packetCapacity,
1858
+ assertActive: assertCurrentGeneration,
1859
+ signal: context.signal,
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,
1865
+ wait: delay,
1866
+ writePacket: packet => this.writeProtocolV2Packet(uuid, transport$1, buffer.Buffer.from(packet).toString('base64'), context, assertCurrentGeneration),
1511
1867
  });
1512
- const writeWithResponse = !!(options === null || options === void 0 ? void 0 : options.writeWithResponse) || (!!(options === null || options === void 0 ? void 0 : options.highVolume) && tuning.highVolumeWriteWithResponse);
1513
- const writeMode = resolveBleWriteMode(transport.writeCharacteristic, writeWithResponse ? 'withResponse' : 'withoutResponse');
1514
- const shouldThrottle = !!(options === null || options === void 0 ? void 0 : options.highVolume) && writeMode === 'withoutResponse';
1515
- let packetsWritten = 0;
1516
- for (let offset = 0; offset < frame.length; offset += packetCapacity) {
1517
- const chunk = frame.slice(offset, offset + packetCapacity);
1518
- const base64 = buffer.Buffer.from(chunk).toString('base64');
1519
- if (writeMode === 'withResponse') {
1520
- yield transport.writeCharacteristic.writeWithResponse(base64);
1521
- }
1522
- else {
1523
- yield transport.writeCharacteristic.writeWithoutResponse(base64);
1524
- }
1525
- packetsWritten += 1;
1526
- if (shouldThrottle &&
1527
- packetsWritten % tuning.highVolumeWriteBurstSize === 0 &&
1528
- offset + packetCapacity < frame.length) {
1529
- yield delay(tuning.highVolumeWritePauseMs);
1530
- }
1531
- }
1532
- if (shouldThrottle) {
1533
- yield delay(tuning.highVolumeWriteFlushDelayMs);
1534
- }
1535
1868
  });
1536
1869
  }
1537
1870
  callProtocolV2(uuid, name, data, options) {
1538
- var _a, _b, _c, _d;
1539
1871
  return __awaiter(this, void 0, void 0, function* () {
1540
1872
  if (!this._messages || !this._messagesV2) {
1541
1873
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
1542
1874
  }
1543
- const forceRun = name === 'Initialize' || name === 'Cancel' || name === 'Ping';
1544
- if (this.runPromise) {
1545
- if (!forceRun) {
1546
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportCallInProgress);
1547
- }
1548
- const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
1549
- this.runPromise.reject(error);
1550
- this.rejectAllProtocolV2Frames(error);
1551
- this.runPromise = null;
1552
- this.activeProtocolV2Call = null;
1553
- }
1554
- const transport$1 = this.getCachedTransport(uuid);
1555
- const runPromise = hdShared.createDeferred();
1556
- runPromise.promise.catch(() => undefined);
1557
- this.runPromise = runPromise;
1558
- const callToken = this.nextProtocolV2CallToken++;
1559
- this.activeProtocolV2Call = { uuid, token: callToken };
1560
- (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1561
- this.resetProtocolV2Frames(uuid);
1562
- let completed = false;
1563
- const callOptions = Object.assign(Object.assign({}, options), { timeoutMs: (_b = options === null || options === void 0 ? void 0 : options.timeoutMs) !== null && _b !== void 0 ? _b : BLE_RESPONSE_TIMEOUT_MS });
1875
+ const callOptions = options;
1564
1876
  const highVolumeWrite = transport.LogBlockCommand.has(name);
1565
1877
  if (highVolumeWrite) {
1566
1878
  const tuning = getProtocolV2BleTuning();
1567
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume write uses throttled writeWithoutResponse:', name, {
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',
1568
1882
  packetCapacity: reactNative.Platform.OS === 'ios' ? tuning.iosPacketLength : tuning.androidPacketLength,
1569
- burstSize: tuning.highVolumeWriteBurstSize,
1570
- pauseMs: tuning.highVolumeWritePauseMs,
1571
- flushDelayMs: tuning.highVolumeWriteFlushDelayMs,
1572
- writeWithResponse: tuning.highVolumeWriteWithResponse,
1573
1883
  });
1574
1884
  }
1575
1885
  try {
1576
- const session = new transport.ProtocolV2Session({
1577
- schemas: {
1578
- protocolV1: this._messages,
1579
- protocolV2: this._messagesV2,
1580
- },
1581
- router: transport.PROTOCOL_V2_CHANNEL_BLE_UART,
1582
- writeFrame: (frame) => __awaiter(this, void 0, void 0, function* () {
1583
- yield this.writeProtocolV2Frame(transport$1, frame, {
1584
- highVolume: highVolumeWrite,
1585
- });
1586
- }),
1587
- readFrame: () => __awaiter(this, void 0, void 0, function* () {
1588
- const rxFrame = yield this.readProtocolV2Frame(uuid);
1589
- if (!(rxFrame instanceof Uint8Array)) {
1590
- throw new Error('Protocol V2 response is not Uint8Array');
1591
- }
1592
- return rxFrame;
1593
- }),
1594
- logger: Log,
1595
- logPrefix: 'ProtocolV2 RN-BLE',
1596
- createTimeoutError: (_messageName, timeout) => hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, `BLE response timeout after ${timeout}ms for ${name}`),
1597
- });
1598
- const result = yield session.call(name, data, callOptions);
1599
- completed = true;
1600
- return result;
1886
+ return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, callOptions);
1601
1887
  }
1602
1888
  catch (e) {
1603
- if (this.isActiveProtocolV2Call(uuid, callToken)) {
1604
- (_c = this.protocolV2Assemblers.get(uuid)) === null || _c === void 0 ? void 0 : _c.reset();
1605
- this.resetProtocolV2Frames(uuid);
1606
- }
1607
1889
  Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] Protocol V2 call error:', e);
1608
1890
  throw e;
1609
1891
  }
1610
- finally {
1611
- if (this.isActiveProtocolV2Call(uuid, callToken)) {
1612
- if (!completed) {
1613
- (_d = this.protocolV2Assemblers.get(uuid)) === null || _d === void 0 ? void 0 : _d.reset();
1614
- }
1615
- this.resetProtocolV2Frames(uuid);
1616
- this.activeProtocolV2Call = null;
1617
- }
1618
- if (this.runPromise === runPromise) {
1619
- this.runPromise = null;
1620
- }
1621
- }
1622
1892
  });
1623
1893
  }
1894
+ createProtocolV2Adapter(uuid) {
1895
+ var _a;
1896
+ const generation = (_a = this.monitorTokens.get(uuid)) !== null && _a !== void 0 ? _a : 0;
1897
+ const assertCurrentGeneration = () => {
1898
+ if (this.monitorTokens.get(uuid) !== generation) {
1899
+ throw new Error(`Protocol V2 monitor generation changed for ${uuid}`);
1900
+ }
1901
+ };
1902
+ return {
1903
+ router: transport.PROTOCOL_V2_CHANNEL_BLE_UART,
1904
+ maxFrameBytes: transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES,
1905
+ generation,
1906
+ prepareCall: () => {
1907
+ var _a;
1908
+ assertCurrentGeneration();
1909
+ (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1910
+ this.resetProtocolV2Frames(uuid);
1911
+ },
1912
+ writeFrame: (frame, context) => __awaiter(this, void 0, void 0, function* () {
1913
+ assertCurrentGeneration();
1914
+ const currentTransport = this.getCachedTransport(uuid);
1915
+ yield this.writeProtocolV2Frame(uuid, currentTransport, frame, context, assertCurrentGeneration);
1916
+ }),
1917
+ readFrame: () => __awaiter(this, void 0, void 0, function* () {
1918
+ assertCurrentGeneration();
1919
+ const rxFrame = yield this.readProtocolV2Frame(uuid);
1920
+ if (!(rxFrame instanceof Uint8Array)) {
1921
+ throw new Error('Protocol V2 response is not Uint8Array');
1922
+ }
1923
+ return rxFrame;
1924
+ }),
1925
+ reset: (reason) => {
1926
+ var _a;
1927
+ (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1928
+ this.rejectProtocolV2Frames(uuid, new Error(reason));
1929
+ },
1930
+ logger: Log,
1931
+ logPrefix: 'ProtocolV2 RN-BLE',
1932
+ createTimeoutError: (messageName, timeout) => hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, `BLE response timeout after ${timeout}ms for ${messageName}`),
1933
+ };
1934
+ }
1624
1935
  getProtocolType(path) {
1625
- return this.deviceProtocol.get(path);
1936
+ return this.getActiveProtocol(path);
1626
1937
  }
1627
1938
  }
1628
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;
1629
1946
  exports.configureProtocolV2BleTuning = configureProtocolV2BleTuning;
1630
1947
  exports["default"] = ReactNativeBleTransport;
1948
+ exports.getFirmwareUploadWriteRetryType = getFirmwareUploadWriteRetryType;
1631
1949
  exports.getProtocolV2BleTuning = getProtocolV2BleTuning;
1632
1950
  exports.resetProtocolV2BleTuning = resetProtocolV2BleTuning;