@onekeyfe/hd-transport-react-native 1.2.0-alpha.6 → 1.2.0-alpha.63

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,12 @@ 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;
257
- }
213
+ yield this.writeCharacteristic.writeWithoutResponse(data);
258
214
  });
259
215
  }
260
216
  }
261
- BleTransport.MAX_RETRIES = 5;
262
- BleTransport.RETRY_DELAY = 2000;
263
217
 
264
218
  const { check, ProtocolV1, parseConfigure } = transport__default["default"];
265
219
  const Log = bleLogger;
@@ -268,7 +222,6 @@ const FIRMWARE_UPLOAD_WRITE_BURST_SIZE = reactNative.Platform.OS === 'ios' ? 4 :
268
222
  const FIRMWARE_UPLOAD_WRITE_PAUSE_MS = reactNative.Platform.OS === 'ios' ? 8 : 10;
269
223
  const FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS = reactNative.Platform.OS === 'ios' ? 24 : 30;
270
224
  const FIRMWARE_UPLOAD_WRITE_MAX_RETRIES = 8;
271
- const FIRMWARE_UPLOAD_RECONNECT_RETRY_DELAY_MS = 2000;
272
225
  const ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH = 192;
273
226
  const FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY = reactNative.Platform.OS === 'ios' ? IOS_PACKET_LENGTH : ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH;
274
227
  const ANDROID_GATT_CONGESTED_STATUS = 143;
@@ -279,10 +232,6 @@ const getFirmwareUploadWriteRetryType = (error) => {
279
232
  if (!error || typeof error !== 'object')
280
233
  return null;
281
234
  const bleWriteError = error;
282
- if (bleWriteError.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected ||
283
- bleWriteError.errorCode === reactNativeBlePlx.BleErrorCode.CharacteristicNotFound) {
284
- return 'reconnectable';
285
- }
286
235
  if (bleWriteError.androidErrorCode === ANDROID_GATT_CONGESTED_STATUS ||
287
236
  bleWriteError.status === ANDROID_GATT_CONGESTED_STATUS) {
288
237
  return 'congested';
@@ -293,22 +242,20 @@ const getFirmwareUploadWriteRetryType = (error) => {
293
242
  return /GATT_CONGESTED|status\s*[:=]?\s*143/.test(text) ? 'congested' : null;
294
243
  };
295
244
  const resolveFirmwareUploadRetryDelay = (attempt, baseDelayMs = 200, maxDelayMs = 1200) => Math.min(baseDelayMs * Math.pow(2, attempt), maxDelayMs);
296
- const BLE_RESPONSE_TIMEOUT_MS = 30000;
297
245
  const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
298
246
  const PROTOCOL_V2_PROBE_TIMEOUT_MS = 10000;
299
- const DEVICE_SCAN_TIMEOUT_MS = 8000;
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
+ const DEVICE_SCAN_TIMEOUT_MS = 3000;
300
254
  const IOS_NOTIFY_READY_DELAY_MS = 150;
301
255
  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
256
  const DEFAULT_PROTOCOL_V2_BLE_TUNING = {
306
257
  iosPacketLength: IOS_PACKET_LENGTH,
307
258
  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
259
  };
313
260
  let protocolV2BleTuning = Object.assign({}, DEFAULT_PROTOCOL_V2_BLE_TUNING);
314
261
  const normalizePositiveInteger = (value, fallback) => {
@@ -318,20 +265,15 @@ const normalizePositiveInteger = (value, fallback) => {
318
265
  return Math.floor(normalized);
319
266
  };
320
267
  function configureProtocolV2BleTuning(tuning = {}) {
321
- var _a;
322
268
  protocolV2BleTuning = {
323
269
  iosPacketLength: normalizePositiveInteger(tuning.iosPacketLength, protocolV2BleTuning.iosPacketLength),
324
270
  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
271
  };
330
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 BLE tuning configured:', protocolV2BleTuning);
272
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE tuning configured', protocolV2BleTuning);
331
273
  }
332
274
  function resetProtocolV2BleTuning() {
333
275
  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);
276
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE tuning reset', protocolV2BleTuning);
335
277
  }
336
278
  function getProtocolV2BleTuning() {
337
279
  return Object.assign({}, protocolV2BleTuning);
@@ -342,19 +284,22 @@ function inferProtocolHintFromDeviceName(name) {
342
284
  function getDeviceDisplayName(device) {
343
285
  return (device === null || device === void 0 ? void 0 : device.name) || (device === null || device === void 0 ? void 0 : device.localName) || null;
344
286
  }
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
287
  const ANDROID_REQUEST_MTU = 256;
288
+ const BLE_NATIVE_CONNECT_TIMEOUT_MS = 3000;
353
289
  const connectOptions = {
354
290
  requestMTU: ANDROID_REQUEST_MTU,
355
- timeout: 3000,
291
+ timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
356
292
  refreshGatt: 'OnConnected',
357
293
  };
294
+ const fallbackConnectOptions = {
295
+ timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
296
+ };
297
+ const BLE_CONNECT_TIMEOUT_MS = BLE_NATIVE_CONNECT_TIMEOUT_MS * 2 + 2000;
298
+ const BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
299
+ const CONNECT_TIMEOUT_MESSAGE = 'BLE connect timeout after';
300
+ const isConnectTimeoutError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleConnectedError &&
301
+ typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' &&
302
+ error.message.startsWith(CONNECT_TIMEOUT_MESSAGE);
358
303
  const tryToGetConfiguration = (device) => {
359
304
  if (!device || !device.serviceUUIDs)
360
305
  return null;
@@ -371,9 +316,10 @@ const requestAndroidMtu = (device) => __awaiter(void 0, void 0, void 0, function
371
316
  return device;
372
317
  try {
373
318
  const mtuDevice = yield device.requestMTU(ANDROID_REQUEST_MTU);
374
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android MTU requested:', {
319
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] MTU configured', {
320
+ deviceId: device.id,
375
321
  requested: ANDROID_REQUEST_MTU,
376
- mtu: mtuDevice.mtu,
322
+ actual: mtuDevice.mtu,
377
323
  });
378
324
  return mtuDevice;
379
325
  }
@@ -408,15 +354,39 @@ class ReactNativeBleTransport {
408
354
  this.stopped = false;
409
355
  this.scanTimeout = DEVICE_SCAN_TIMEOUT_MS;
410
356
  this.runPromise = null;
357
+ this.runPromiseDeviceId = null;
411
358
  this.firmwareUploadWriteRecoveryIds = new Set();
412
359
  this.deviceProtocol = new Map();
360
+ this.probingProtocols = new Map();
361
+ this.writeTimeoutCounts = new Map();
362
+ this.connectTimeoutCounts = new Map();
413
363
  this.deviceProtocolHints = new Map();
414
364
  this.protocolV2Assemblers = new Map();
415
365
  this.protocolV2FrameQueues = new Map();
416
366
  this.protocolV2FramePromises = new Map();
417
- this.activeProtocolV2Call = null;
418
- this.nextProtocolV2CallToken = 1;
367
+ this.protocolV2Links = new transport.ProtocolV2LinkManager({
368
+ getSchemas: () => {
369
+ if (!this._messages || !this._messagesV2) {
370
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
371
+ }
372
+ return {
373
+ protocolV1: this._messages,
374
+ protocolV2: this._messagesV2,
375
+ };
376
+ },
377
+ classifyError: () => 'link-fatal',
378
+ onLinkInvalidated: (uuid, reason) => __awaiter(this, void 0, void 0, function* () {
379
+ var _b;
380
+ (_b = this.protocolV2Assemblers.get(uuid)) === null || _b === void 0 ? void 0 : _b.reset();
381
+ this.rejectProtocolV2Frames(uuid, new Error(reason));
382
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 link invalidated:', uuid, reason);
383
+ if (reason.startsWith('Protocol V2 link-fatal error:')) {
384
+ yield this.releaseNative(uuid, true);
385
+ }
386
+ }),
387
+ });
419
388
  this.monitorTokens = new Map();
389
+ this.disconnectEventTokens = new Map();
420
390
  this.nextMonitorToken = 1;
421
391
  this.scanTimeout = (_a = options.scanTimeout) !== null && _a !== void 0 ? _a : DEVICE_SCAN_TIMEOUT_MS;
422
392
  }
@@ -430,8 +400,18 @@ class ReactNativeBleTransport {
430
400
  this._messages = messages;
431
401
  }
432
402
  configureProtocolV2(signedData) {
403
+ const configuration = typeof signedData === 'string' ? signedData : JSON.stringify(signedData);
404
+ if (this.protocolV2SchemaConfiguration === configuration) {
405
+ return;
406
+ }
407
+ const isReconfiguration = this.protocolV2SchemaConfiguration !== undefined;
433
408
  this._messagesV2 = parseConfigure(signedData);
434
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 schema configured');
409
+ this.protocolV2SchemaConfiguration = configuration;
410
+ if (isReconfiguration) {
411
+ this.protocolV2Links
412
+ .invalidateAllLinks('Protocol V2 schema reconfigured')
413
+ .catch(error => Log === null || Log === void 0 ? void 0 : Log.debug('Protocol V2 schema link cleanup failed:', error));
414
+ }
435
415
  }
436
416
  listen() {
437
417
  }
@@ -442,7 +422,6 @@ class ReactNativeBleTransport {
442
422
  return Promise.resolve(this.blePlxManager);
443
423
  }
444
424
  resolveCharacteristics(device) {
445
- var _a, _b, _c, _d;
446
425
  return __awaiter(this, void 0, void 0, function* () {
447
426
  yield device.discoverAllServicesAndCharacteristics();
448
427
  let infos = tryToGetConfiguration(device);
@@ -459,19 +438,11 @@ class ReactNativeBleTransport {
459
438
  }
460
439
  }
461
440
  }
462
- let fallbackServiceUuid;
463
441
  if (!infos) {
464
442
  const services = yield device.services();
465
443
  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
444
  }
474
- if (!infos && !fallbackServiceUuid) {
445
+ if (!infos) {
475
446
  try {
476
447
  Log === null || Log === void 0 ? void 0 : Log.debug('cancel connection when service not found');
477
448
  yield device.cancelConnection();
@@ -481,9 +452,7 @@ class ReactNativeBleTransport {
481
452
  }
482
453
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
483
454
  }
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';
455
+ const { serviceUuid, writeUuid, notifyUuid } = infos;
487
456
  if (!serviceUuid) {
488
457
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
489
458
  }
@@ -524,8 +493,8 @@ class ReactNativeBleTransport {
524
493
  attachDisconnectSubscription(transport, device, uuid) {
525
494
  var _a;
526
495
  (_a = transport.disconnectSubscription) === null || _a === void 0 ? void 0 : _a.remove();
496
+ const { monitorToken } = transport;
527
497
  transport.disconnectSubscription = device.onDisconnected(() => {
528
- var _a;
529
498
  if (this.firmwareUploadWriteRecoveryIds.has(uuid)) {
530
499
  Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored during FirmwareUpload write recovery: ', uuid);
531
500
  return;
@@ -534,17 +503,16 @@ class ReactNativeBleTransport {
534
503
  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
504
  return;
536
505
  }
506
+ if (this.monitorTokens.get(uuid) !== monitorToken) {
507
+ Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored for stale generation: ', device === null || device === void 0 ? void 0 : device.id);
508
+ return;
509
+ }
537
510
  try {
538
511
  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) {
512
+ this.emitDeviceDisconnect(uuid, device === null || device === void 0 ? void 0 : device.name, monitorToken);
513
+ if (this.runPromise && this.runPromiseDeviceId === uuid) {
545
514
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError);
546
515
  this.runPromise.reject(error);
547
- this.rejectAllProtocolV2Frames(error);
548
516
  }
549
517
  }
550
518
  catch (e) {
@@ -555,6 +523,22 @@ class ReactNativeBleTransport {
555
523
  }
556
524
  });
557
525
  }
526
+ emitDeviceDisconnect(uuid, name, token) {
527
+ var _a;
528
+ if (token === undefined || this.disconnectEventTokens.get(uuid) === token) {
529
+ return;
530
+ }
531
+ if (this.monitorTokens.get(uuid) !== token) {
532
+ Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect event ignored for stale generation: ', uuid);
533
+ return;
534
+ }
535
+ this.disconnectEventTokens.set(uuid, token);
536
+ (_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit(transport.TRANSPORT_EVENT.DEVICE_DISCONNECT, {
537
+ name,
538
+ id: uuid,
539
+ connectId: uuid,
540
+ });
541
+ }
558
542
  reconnectFirmwareUploadTransport(uuid, transport) {
559
543
  var _a, _b;
560
544
  return __awaiter(this, void 0, void 0, function* () {
@@ -568,12 +552,12 @@ class ReactNativeBleTransport {
568
552
  const isConnected = yield device.isConnected().catch(() => false);
569
553
  if (!isConnected) {
570
554
  try {
571
- device = yield device.connect(connectOptions);
555
+ device = yield this.connectWithTimeout(uuid, () => device.connect(connectOptions));
572
556
  }
573
557
  catch (e) {
574
558
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
575
559
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
576
- device = yield device.connect();
560
+ device = yield this.connectWithTimeout(uuid, () => device.connect());
577
561
  }
578
562
  else if (e.errorCode !== reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
579
563
  throw e;
@@ -624,13 +608,12 @@ class ReactNativeBleTransport {
624
608
  return;
625
609
  }
626
610
  }
627
- blePlxManager.startDeviceScan(null, {
611
+ blePlxManager.startDeviceScan(getBluetoothServiceUuids(), {
628
612
  allowDuplicates: true,
629
613
  scanMode: reactNativeBlePlx.ScanMode.LowLatency,
630
614
  }, (error, device) => {
631
- var _a, _b, _c;
615
+ var _a;
632
616
  if (error) {
633
- Log === null || Log === void 0 ? void 0 : Log.debug('ble scan manager: ', blePlxManager);
634
617
  Log === null || Log === void 0 ? void 0 : Log.debug('ble scan error: ', error);
635
618
  if ([reactNativeBlePlx.BleErrorCode.BluetoothPoweredOff, reactNativeBlePlx.BleErrorCode.BluetoothInUnknownState].includes(error.errorCode)) {
636
619
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePermissionError));
@@ -650,25 +633,14 @@ class ReactNativeBleTransport {
650
633
  return;
651
634
  }
652
635
  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', {
659
- name: device === null || device === void 0 ? void 0 : device.name,
660
- 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,
664
- });
665
- }
636
+ const isOneKey = hdShared.isOnekeyBluetoothDevice({
637
+ id: device === null || device === void 0 ? void 0 : device.id,
638
+ name: device === null || device === void 0 ? void 0 : device.name,
639
+ localName: device === null || device === void 0 ? void 0 : device.localName,
640
+ serviceUuids: device === null || device === void 0 ? void 0 : device.serviceUUIDs,
641
+ });
666
642
  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
643
  addDevice(device);
671
- Log === null || Log === void 0 ? void 0 : Log.debug('search device end ======================\n');
672
644
  }
673
645
  else if (displayName && /\bpro\s*2\b/i.test(displayName)) {
674
646
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Pro2-like BLE device was not accepted:', {
@@ -679,10 +651,20 @@ class ReactNativeBleTransport {
679
651
  });
680
652
  }
681
653
  });
682
- getConnectedDeviceIds(getBluetoothServiceUuids()).then(devices => {
654
+ getConnectedDeviceIds(reactNative.Platform.OS === 'ios' ? getBluetoothServiceUuids() : []).then(devices => {
683
655
  for (const device of devices) {
684
- Log === null || Log === void 0 ? void 0 : Log.debug('search connected peripheral: ', device.id);
685
- addDevice(device);
656
+ const localName = 'localName' in device && typeof device.localName === 'string'
657
+ ? device.localName
658
+ : null;
659
+ if (hdShared.isOnekeyBluetoothDevice({
660
+ id: device.id,
661
+ name: device.name,
662
+ localName,
663
+ serviceUuids: device.serviceUUIDs,
664
+ })) {
665
+ Log === null || Log === void 0 ? void 0 : Log.debug('search connected peripheral: ', device.id);
666
+ addDevice(device);
667
+ }
686
668
  }
687
669
  });
688
670
  const addDevice = (device) => {
@@ -694,6 +676,12 @@ class ReactNativeBleTransport {
694
676
  this.deviceProtocolHints.set(device.id, protocolHint);
695
677
  }
696
678
  deviceList.push(Object.assign(Object.assign({}, device), { name: displayName, commType: 'ble' }));
679
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] OneKey BLE device discovered', {
680
+ deviceId: device.id,
681
+ name: displayName,
682
+ serviceUUIDs: device.serviceUUIDs,
683
+ protocolHint,
684
+ });
697
685
  }
698
686
  };
699
687
  timer.timeout(() => {
@@ -703,6 +691,33 @@ class ReactNativeBleTransport {
703
691
  }));
704
692
  });
705
693
  }
694
+ installTransportForAcquire(uuid, device, characteristics) {
695
+ return __awaiter(this, void 0, void 0, function* () {
696
+ const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristics(device));
697
+ const transport$1 = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
698
+ if (reactNative.Platform.OS === 'android') {
699
+ transport$1.mtuSize = typeof device.mtu === 'number' ? device.mtu : transport$1.mtuSize;
700
+ }
701
+ const monitorToken = this.nextMonitorToken;
702
+ this.nextMonitorToken += 1;
703
+ const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
704
+ transport$1.monitorToken = monitorToken;
705
+ transport$1.notifyTransactionId = notifyTransactionId;
706
+ this.monitorTokens.set(uuid, monitorToken);
707
+ transport$1.notifySubscription = this._monitorCharacteristic(transport$1.notifyCharacteristic, uuid, monitorToken, notifyTransactionId);
708
+ transportCache[uuid] = transport$1;
709
+ this.protocolV2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES));
710
+ if (reactNative.Platform.OS === 'ios') {
711
+ yield new Promise(resolve => {
712
+ setTimeout(resolve, IOS_NOTIFY_READY_DELAY_MS);
713
+ });
714
+ }
715
+ else if (reactNative.Platform.OS === 'android') {
716
+ yield delay(ANDROID_NOTIFY_READY_DELAY_MS);
717
+ }
718
+ return transport$1;
719
+ });
720
+ }
706
721
  acquire(input) {
707
722
  var _a, _b;
708
723
  return __awaiter(this, void 0, void 0, function* () {
@@ -727,9 +742,8 @@ class ReactNativeBleTransport {
727
742
  if (forceCleanRunPromise && this.runPromise) {
728
743
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
729
744
  this.runPromise.reject(error);
730
- this.rejectAllProtocolV2Frames(error);
731
745
  this.runPromise = null;
732
- this.activeProtocolV2Call = null;
746
+ this.runPromiseDeviceId = null;
733
747
  Log === null || Log === void 0 ? void 0 : Log.debug('Force clean Bluetooth run promise, forceCleanRunPromise: ', forceCleanRunPromise);
734
748
  }
735
749
  const blePlxManager = yield this.getPlxManager();
@@ -762,14 +776,17 @@ class ReactNativeBleTransport {
762
776
  if (!device) {
763
777
  Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device: ', uuid);
764
778
  try {
765
- device = yield blePlxManager.connectToDevice(uuid, connectOptions);
779
+ device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, connectOptions));
766
780
  }
767
781
  catch (e) {
768
782
  Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device has error: ', e);
783
+ if (isConnectTimeoutError(e)) {
784
+ throw e;
785
+ }
769
786
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
770
787
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
771
788
  Log === null || Log === void 0 ? void 0 : Log.debug('first try to reconnect without params');
772
- device = yield blePlxManager.connectToDevice(uuid);
789
+ device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, fallbackConnectOptions));
773
790
  }
774
791
  else if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
775
792
  Log === null || Log === void 0 ? void 0 : Log.debug('device already connected');
@@ -785,23 +802,27 @@ class ReactNativeBleTransport {
785
802
  }
786
803
  if (!(yield device.isConnected())) {
787
804
  Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device: ', uuid);
805
+ const disconnectedDevice = device;
788
806
  try {
789
- device = yield device.connect(connectOptions);
807
+ device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(connectOptions));
790
808
  }
791
809
  catch (e) {
792
810
  Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device has error: ', e);
811
+ if (isConnectTimeoutError(e)) {
812
+ throw e;
813
+ }
793
814
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
794
815
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
795
816
  Log === null || Log === void 0 ? void 0 : Log.debug('second try to reconnect without params');
796
817
  try {
797
- device = yield device.connect();
818
+ device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
798
819
  }
799
820
  catch (e) {
800
821
  Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect error: ', e);
801
822
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
802
823
  Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect');
803
- yield device.cancelConnection();
804
- device = yield device.connect();
824
+ yield disconnectedDevice.cancelConnection();
825
+ device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
805
826
  }
806
827
  }
807
828
  }
@@ -811,50 +832,41 @@ class ReactNativeBleTransport {
811
832
  }
812
833
  }
813
834
  device = yield requestAndroidMtu(device);
814
- const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristics(device);
835
+ const acquiredDevice = device;
836
+ const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristics(acquiredDevice);
815
837
  const protocolHint = expectedProtocol
816
838
  ? undefined
817
- : (_a = this.deviceProtocolHints.get(uuid)) !== null && _a !== void 0 ? _a : inferProtocolHintFromDeviceName(getDeviceDisplayName(device));
839
+ : (_b = (_a = input.protocolHint) !== null && _a !== void 0 ? _a : this.deviceProtocolHints.get(uuid)) !== null && _b !== void 0 ? _b : inferProtocolHintFromDeviceName(getDeviceDisplayName(acquiredDevice));
818
840
  yield this.release(uuid, true);
819
841
  if (protocolHint) {
820
842
  this.deviceProtocolHints.set(uuid, protocolHint);
821
843
  }
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
- });
844
+ yield this.installTransportForAcquire(uuid, acquiredDevice, {
845
+ writeCharacteristic,
846
+ notifyCharacteristic,
847
+ });
848
+ try {
849
+ const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint, () => __awaiter(this, void 0, void 0, function* () {
850
+ yield this.installTransportForAcquire(uuid, acquiredDevice);
851
+ }));
852
+ const currentTransport = transportCache[uuid];
853
+ if (!currentTransport) {
854
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
855
+ }
856
+ this.attachDisconnectSubscription(currentTransport, acquiredDevice, uuid);
857
+ return { uuid, protocolType };
839
858
  }
840
- else if (reactNative.Platform.OS === 'android') {
841
- yield delay(ANDROID_NOTIFY_READY_DELAY_MS);
859
+ catch (error) {
860
+ yield this.release(uuid, true);
861
+ throw error;
842
862
  }
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
863
  });
852
864
  }
853
865
  _monitorCharacteristic(characteristic, uuid, monitorToken, notifyTransactionId) {
854
866
  let bufferLength = 0;
855
867
  let buffer$1 = [];
856
868
  const subscription = characteristic.monitor((error, c) => {
857
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
869
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
858
870
  const isCurrentMonitor = this.monitorTokens.get(uuid) === monitorToken;
859
871
  if (error) {
860
872
  Log === null || Log === void 0 ? void 0 : Log.debug(`error monitor ${characteristic.uuid}, deviceId: ${characteristic.deviceID}: ${error}`);
@@ -866,28 +878,44 @@ class ReactNativeBleTransport {
866
878
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
867
879
  return;
868
880
  }
869
- if (this.runPromise) {
870
- let ERROR = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
881
+ if (this.getActiveProtocol(uuid) === 'V2') {
882
+ let errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
871
883
  if ((_a = error.reason) === null || _a === void 0 ? void 0 : _a.includes('The connection has timed out unexpectedly')) {
872
- ERROR = hdShared.HardwareErrorCode.BleTimeoutError;
884
+ errorCode = hdShared.HardwareErrorCode.BleTimeoutError;
873
885
  }
874
- if ((_b = error.reason) === null || _b === void 0 ? void 0 : _b.includes('Encryption is insufficient')) {
875
- ERROR = hdShared.HardwareErrorCode.BleDeviceBondError;
886
+ else if ((_b = error.reason) === null || _b === void 0 ? void 0 : _b.includes('Encryption is insufficient')) {
887
+ errorCode = hdShared.HardwareErrorCode.BleDeviceBondError;
876
888
  }
877
- if (((_c = error.reason) === null || _c === void 0 ? void 0 : _c.includes('Cannot write client characteristic config descriptor')) ||
889
+ else if (((_c = error.reason) === null || _c === void 0 ? void 0 : _c.includes('Cannot write client characteristic config descriptor')) ||
878
890
  ((_d = error.reason) === null || _d === void 0 ? void 0 : _d.includes('Cannot find client characteristic config descriptor')) ||
879
891
  ((_e = error.reason) === null || _e === void 0 ? void 0 : _e.includes('The handle is invalid')) ||
880
892
  ((_f = error.reason) === null || _f === void 0 ? void 0 : _f.includes('Writing is not permitted')) ||
881
893
  ((_g = error.reason) === null || _g === void 0 ? void 0 : _g.includes('notify change failed for device'))) {
894
+ errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure;
895
+ }
896
+ this.rejectProtocolV2Frames(uuid, hdShared.ERRORS.TypedError(errorCode));
897
+ return;
898
+ }
899
+ if (this.runPromise && this.runPromiseDeviceId === uuid) {
900
+ let ERROR = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
901
+ if ((_h = error.reason) === null || _h === void 0 ? void 0 : _h.includes('The connection has timed out unexpectedly')) {
902
+ ERROR = hdShared.HardwareErrorCode.BleTimeoutError;
903
+ }
904
+ if ((_j = error.reason) === null || _j === void 0 ? void 0 : _j.includes('Encryption is insufficient')) {
905
+ ERROR = hdShared.HardwareErrorCode.BleDeviceBondError;
906
+ }
907
+ if (((_k = error.reason) === null || _k === void 0 ? void 0 : _k.includes('Cannot write client characteristic config descriptor')) ||
908
+ ((_l = error.reason) === null || _l === void 0 ? void 0 : _l.includes('Cannot find client characteristic config descriptor')) ||
909
+ ((_m = error.reason) === null || _m === void 0 ? void 0 : _m.includes('The handle is invalid')) ||
910
+ ((_o = error.reason) === null || _o === void 0 ? void 0 : _o.includes('Writing is not permitted')) ||
911
+ ((_p = error.reason) === null || _p === void 0 ? void 0 : _p.includes('notify change failed for device'))) {
882
912
  const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure);
883
913
  this.runPromise.reject(notifyError);
884
- this.rejectAllProtocolV2Frames(notifyError);
885
914
  Log === null || Log === void 0 ? void 0 : Log.debug(`${hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure} ${error.message} ${error.reason}`);
886
915
  return;
887
916
  }
888
917
  const notifyError = hdShared.ERRORS.TypedError(ERROR);
889
918
  this.runPromise.reject(notifyError);
890
- this.rejectAllProtocolV2Frames(notifyError);
891
919
  Log === null || Log === void 0 ? void 0 : Log.debug(': monitor notify error, and has unreleased Promise', Error);
892
920
  }
893
921
  return;
@@ -901,13 +929,13 @@ class ReactNativeBleTransport {
901
929
  }
902
930
  try {
903
931
  const data = buffer.Buffer.from(c.value, 'base64');
904
- const protocol = this.deviceProtocol.get(uuid);
932
+ const protocol = this.getActiveProtocol(uuid);
905
933
  if (!protocol) {
906
934
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor data ignored before protocol detection: ', uuid);
907
935
  return;
908
936
  }
909
937
  if (protocol === 'V2') {
910
- this.handleProtocolV2Notification(uuid, new Uint8Array(data));
938
+ this.handleProtocolV2Notification(uuid, monitorToken, new Uint8Array(data));
911
939
  return;
912
940
  }
913
941
  if (isHeaderChunk(data)) {
@@ -921,28 +949,40 @@ class ReactNativeBleTransport {
921
949
  const value = buffer.Buffer.from(buffer$1);
922
950
  bufferLength = 0;
923
951
  buffer$1 = [];
924
- (_h = this.runPromise) === null || _h === void 0 ? void 0 : _h.resolve(value.toString('hex'));
952
+ if (this.runPromiseDeviceId === uuid) {
953
+ (_q = this.runPromise) === null || _q === void 0 ? void 0 : _q.resolve(value.toString('hex'));
954
+ }
925
955
  }
926
956
  }
927
957
  catch (error) {
928
958
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor data error: ', error);
929
959
  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);
960
+ if (this.getActiveProtocol(uuid) === 'V2') {
961
+ this.rejectProtocolV2Frames(uuid, notifyError);
962
+ }
963
+ else if (this.runPromiseDeviceId === uuid) {
964
+ (_r = this.runPromise) === null || _r === void 0 ? void 0 : _r.reject(notifyError);
965
+ }
932
966
  }
933
967
  }, notifyTransactionId);
934
968
  return subscription;
935
969
  }
936
970
  release(uuid, onclose = false) {
937
- var _a, _b, _c, _d, _e, _f, _g, _h;
971
+ return __awaiter(this, void 0, void 0, function* () {
972
+ yield this.protocolV2Links.invalidateLink(uuid, 'React Native BLE transport released');
973
+ return this.releaseNative(uuid, onclose);
974
+ });
975
+ }
976
+ releaseNative(uuid, onclose = false) {
977
+ var _a, _b, _c, _d, _e, _f, _g;
938
978
  return __awaiter(this, void 0, void 0, function* () {
939
979
  const transport = transportCache[uuid];
940
- if (this.runPromise) {
980
+ if (this.runPromise && this.runPromiseDeviceId === uuid) {
941
981
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
942
982
  this.runPromise.reject(error);
943
983
  this.runPromise = null;
944
- this.rejectAllProtocolV2Frames(error);
945
- this.activeProtocolV2Call = null;
984
+ this.runPromiseDeviceId = null;
985
+ this.rejectProtocolV2Frames(uuid, error);
946
986
  }
947
987
  else {
948
988
  this.resetProtocolV2Frames(uuid);
@@ -950,9 +990,6 @@ class ReactNativeBleTransport {
950
990
  if (reactNative.Platform.OS === 'android' && !onclose && transport) {
951
991
  (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
952
992
  this.resetProtocolV2Frames(uuid);
953
- if (((_b = this.activeProtocolV2Call) === null || _b === void 0 ? void 0 : _b.uuid) === uuid) {
954
- this.activeProtocolV2Call = null;
955
- }
956
993
  return Promise.resolve(true);
957
994
  }
958
995
  if (transport) {
@@ -960,14 +997,14 @@ class ReactNativeBleTransport {
960
997
  this.monitorTokens.delete(uuid);
961
998
  }
962
999
  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();
1000
+ (_b = transport.disconnectSubscription) === null || _b === void 0 ? void 0 : _b.remove();
964
1001
  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();
1002
+ 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);
1003
+ (_d = transport.notifySubscription) === null || _d === void 0 ? void 0 : _d.remove();
967
1004
  transport.notifySubscription = undefined;
968
1005
  if (transport.notifyTransactionId) {
969
1006
  try {
970
- yield ((_f = this.blePlxManager) === null || _f === void 0 ? void 0 : _f.cancelTransaction(transport.notifyTransactionId));
1007
+ yield ((_e = this.blePlxManager) === null || _e === void 0 ? void 0 : _e.cancelTransaction(transport.notifyTransactionId));
971
1008
  }
972
1009
  catch (e) {
973
1010
  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 +1013,12 @@ class ReactNativeBleTransport {
976
1013
  delete transportCache[uuid];
977
1014
  }
978
1015
  this.deviceProtocol.delete(uuid);
979
- this.deviceProtocolHints.delete(uuid);
980
- (_g = this.protocolV2Assemblers.get(uuid)) === null || _g === void 0 ? void 0 : _g.reset();
1016
+ this.probingProtocols.delete(uuid);
1017
+ (_f = this.protocolV2Assemblers.get(uuid)) === null || _f === void 0 ? void 0 : _f.reset();
981
1018
  this.protocolV2Assemblers.delete(uuid);
982
1019
  this.resetProtocolV2Frames(uuid);
983
1020
  try {
984
- yield ((_h = this.blePlxManager) === null || _h === void 0 ? void 0 : _h.cancelTransaction(uuid));
1021
+ yield ((_g = this.blePlxManager) === null || _g === void 0 ? void 0 : _g.cancelTransaction(uuid));
985
1022
  }
986
1023
  catch (e) {
987
1024
  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 +1039,18 @@ class ReactNativeBleTransport {
1002
1039
  if (this._messages == null) {
1003
1040
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
1004
1041
  }
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
1042
  const protocol = this.getProtocolType(uuid);
1011
1043
  if (!protocol) {
1012
1044
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${uuid}`);
1013
1045
  }
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
- }
1046
+ Log === null || Log === void 0 ? void 0 : Log.debug('transport call', transport.createTransportCallLog(name, protocol, data));
1026
1047
  if (protocol === 'V2') {
1027
1048
  return this.callProtocolV2(uuid, name, data, options);
1028
1049
  }
1050
+ const forceRun = name === 'Initialize' || name === 'Cancel';
1051
+ if (this.runPromise && !forceRun) {
1052
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportCallInProgress);
1053
+ }
1029
1054
  return this.callProtocolV1(uuid, name, data, options);
1030
1055
  });
1031
1056
  }
@@ -1037,7 +1062,19 @@ class ReactNativeBleTransport {
1037
1062
  const transport = this.getCachedTransport(uuid);
1038
1063
  const runPromise = hdShared.createDeferred();
1039
1064
  runPromise.promise.catch(() => undefined);
1065
+ const supersededRunPromise = this.runPromise;
1066
+ if (supersededRunPromise) {
1067
+ supersededRunPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise));
1068
+ }
1040
1069
  this.runPromise = runPromise;
1070
+ this.runPromiseDeviceId = uuid;
1071
+ const releaseOwnershipIfCurrent = () => {
1072
+ if (this.runPromise === runPromise) {
1073
+ this.runPromise = null;
1074
+ this.runPromiseDeviceId = null;
1075
+ }
1076
+ };
1077
+ const isCurrentOwner = () => this.runPromise === runPromise;
1041
1078
  const messages = this._messages;
1042
1079
  const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
1043
1080
  let timeout;
@@ -1058,6 +1095,9 @@ class ReactNativeBleTransport {
1058
1095
  }
1059
1096
  catch (e) {
1060
1097
  onError(e);
1098
+ if (isWedgedWriteError(e)) {
1099
+ throw e;
1100
+ }
1061
1101
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1062
1102
  }
1063
1103
  }
@@ -1085,6 +1125,9 @@ class ReactNativeBleTransport {
1085
1125
  }
1086
1126
  catch (e) {
1087
1127
  onError(e);
1128
+ if (isWedgedWriteError(e)) {
1129
+ throw e;
1130
+ }
1088
1131
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1089
1132
  }
1090
1133
  }
@@ -1095,13 +1138,13 @@ class ReactNativeBleTransport {
1095
1138
  });
1096
1139
  }
1097
1140
  if (name === 'EmmcFileWrite') {
1098
- yield writeChunkedData(buffers, data => transport.writeWithRetry(data), e => {
1099
- this.runPromise = null;
1141
+ yield writeChunkedData(buffers, data => this.writeBlePacket(uuid, data, payload => transport.writeWithRetry(payload), isCurrentOwner), e => {
1142
+ releaseOwnershipIfCurrent();
1100
1143
  Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
1101
1144
  });
1102
1145
  }
1103
1146
  else if (name === 'FirmwareUpload') {
1104
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] FirmwareUpload write uses throttled BLE packets:', {
1147
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Firmware upload transport configured', {
1105
1148
  packetCapacity: FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY,
1106
1149
  burstSize: FIRMWARE_UPLOAD_WRITE_BURST_SIZE,
1107
1150
  pauseMs: FIRMWARE_UPLOAD_WRITE_PAUSE_MS,
@@ -1112,7 +1155,7 @@ class ReactNativeBleTransport {
1112
1155
  let attempt = 0;
1113
1156
  while (true) {
1114
1157
  try {
1115
- yield transport.writeCharacteristic.writeWithoutResponse(data);
1158
+ yield this.writeBlePacket(uuid, data, payload => transport.writeCharacteristic.writeWithoutResponse(payload), isCurrentOwner);
1116
1159
  return;
1117
1160
  }
1118
1161
  catch (error) {
@@ -1120,36 +1163,18 @@ class ReactNativeBleTransport {
1120
1163
  if (!retryType || attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
1121
1164
  throw error;
1122
1165
  }
1123
- const shouldReconnect = retryType === 'reconnectable';
1124
- const delayMs = shouldReconnect
1125
- ? FIRMWARE_UPLOAD_RECONNECT_RETRY_DELAY_MS
1126
- : resolveFirmwareUploadRetryDelay(attempt);
1166
+ const delayMs = resolveFirmwareUploadRetryDelay(attempt);
1127
1167
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] FirmwareUpload write retry:', {
1128
1168
  attempt: attempt + 1,
1129
1169
  delayMs,
1130
- reconnect: shouldReconnect,
1131
1170
  error,
1132
1171
  });
1133
- if (shouldReconnect) {
1134
- this.firmwareUploadWriteRecoveryIds.add(uuid);
1135
- }
1136
1172
  yield delay(delayMs);
1137
1173
  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
1174
  }
1150
1175
  }
1151
1176
  }), e => {
1152
- this.runPromise = null;
1177
+ releaseOwnershipIfCurrent();
1153
1178
  Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
1154
1179
  });
1155
1180
  }
@@ -1157,11 +1182,14 @@ class ReactNativeBleTransport {
1157
1182
  for (const o of buffers) {
1158
1183
  const outData = o.toString('base64');
1159
1184
  try {
1160
- yield transport.writeCharacteristic.writeWithoutResponse(outData);
1185
+ yield this.writeBlePacket(uuid, outData, payload => transport.writeCharacteristic.writeWithoutResponse(payload), isCurrentOwner);
1161
1186
  }
1162
1187
  catch (e) {
1163
1188
  Log === null || Log === void 0 ? void 0 : Log.debug('writeCharacteristic write error: ', e);
1164
- this.runPromise = null;
1189
+ releaseOwnershipIfCurrent();
1190
+ if (isWedgedWriteError(e)) {
1191
+ throw e;
1192
+ }
1165
1193
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected) {
1166
1194
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded);
1167
1195
  }
@@ -1190,17 +1218,23 @@ class ReactNativeBleTransport {
1190
1218
  if (typeof response !== 'string') {
1191
1219
  throw new Error('Returning data is not string.');
1192
1220
  }
1193
- Log === null || Log === void 0 ? void 0 : Log.debug('receive data: ', response);
1194
1221
  const jsonData = ProtocolV1.decodeMessage(messages, response);
1195
1222
  return check.call(jsonData);
1196
1223
  }
1197
1224
  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);
1225
+ if (name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS) {
1226
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe call failed:', e);
1200
1227
  }
1201
1228
  else {
1202
1229
  Log === null || Log === void 0 ? void 0 : Log.error('call error: ', e);
1203
1230
  }
1231
+ const isProbeTimeout = name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS;
1232
+ const isStaleCall = this.runPromise !== runPromise;
1233
+ if (!isProbeTimeout &&
1234
+ !isStaleCall &&
1235
+ (e === null || e === void 0 ? void 0 : e.errorCode) === hdShared.HardwareErrorCode.BleTimeoutError) {
1236
+ yield this.disconnect(uuid);
1237
+ }
1204
1238
  throw e;
1205
1239
  }
1206
1240
  finally {
@@ -1208,6 +1242,7 @@ class ReactNativeBleTransport {
1208
1242
  clearTimeout(timeout);
1209
1243
  if (this.runPromise === runPromise) {
1210
1244
  this.runPromise = null;
1245
+ this.runPromiseDeviceId = null;
1211
1246
  }
1212
1247
  }
1213
1248
  });
@@ -1216,10 +1251,11 @@ class ReactNativeBleTransport {
1216
1251
  this.stopped = true;
1217
1252
  }
1218
1253
  disconnect(session) {
1219
- var _a, _b, _c, _d, _e, _f;
1254
+ var _a, _b, _c, _d, _e;
1220
1255
  return __awaiter(this, void 0, void 0, function* () {
1221
- Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native transport resetSession: ', session);
1256
+ yield this.protocolV2Links.invalidateLink(session, 'React Native BLE transport disconnected');
1222
1257
  const transport = transportCache[session];
1258
+ const monitorToken = (_a = transport === null || transport === void 0 ? void 0 : transport.monitorToken) !== null && _a !== void 0 ? _a : this.monitorTokens.get(session);
1223
1259
  if (transport === null || transport === void 0 ? void 0 : transport.disconnectSubscription) {
1224
1260
  try {
1225
1261
  Log === null || Log === void 0 ? void 0 : Log.debug('disconnect: removing disconnect subscription');
@@ -1232,7 +1268,7 @@ class ReactNativeBleTransport {
1232
1268
  }
1233
1269
  if (transport === null || transport === void 0 ? void 0 : transport.notifySubscription) {
1234
1270
  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);
1271
+ 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
1272
  transport.notifySubscription.remove();
1237
1273
  transport.notifySubscription = undefined;
1238
1274
  }
@@ -1242,7 +1278,7 @@ class ReactNativeBleTransport {
1242
1278
  }
1243
1279
  if (session) {
1244
1280
  try {
1245
- yield ((_b = this.blePlxManager) === null || _b === void 0 ? void 0 : _b.cancelTransaction(session));
1281
+ yield ((_c = this.blePlxManager) === null || _c === void 0 ? void 0 : _c.cancelTransaction(session));
1246
1282
  }
1247
1283
  catch (e) {
1248
1284
  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 +1293,7 @@ class ReactNativeBleTransport {
1257
1293
  }
1258
1294
  }
1259
1295
  try {
1260
- yield ((_c = this.blePlxManager) === null || _c === void 0 ? void 0 : _c.cancelDeviceConnection(session));
1296
+ yield ((_d = this.blePlxManager) === null || _d === void 0 ? void 0 : _d.cancelDeviceConnection(session));
1261
1297
  }
1262
1298
  catch (e) {
1263
1299
  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 +1302,19 @@ class ReactNativeBleTransport {
1266
1302
  delete transportCache[session];
1267
1303
  }
1268
1304
  this.deviceProtocol.delete(session);
1305
+ this.probingProtocols.delete(session);
1269
1306
  this.deviceProtocolHints.delete(session);
1270
1307
  this.protocolV2Assemblers.delete(session);
1271
1308
  this.resetProtocolV2Frames(session);
1272
- if (((_d = this.activeProtocolV2Call) === null || _d === void 0 ? void 0 : _d.uuid) === session) {
1273
- this.activeProtocolV2Call = null;
1274
- }
1275
1309
  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
- });
1310
+ this.emitDeviceDisconnect(session, (_e = transport === null || transport === void 0 ? void 0 : transport.device) === null || _e === void 0 ? void 0 : _e.name, monitorToken);
1281
1311
  }
1282
1312
  catch (e) {
1283
1313
  Log === null || Log === void 0 ? void 0 : Log.error('resetSession: emit disconnect event error: ', e);
1284
1314
  }
1315
+ if (monitorToken !== undefined && this.monitorTokens.get(session) === monitorToken) {
1316
+ this.monitorTokens.delete(session);
1317
+ }
1285
1318
  yield new Promise(resolve => setTimeout(() => resolve(), 100));
1286
1319
  });
1287
1320
  }
@@ -1289,6 +1322,61 @@ class ReactNativeBleTransport {
1289
1322
  Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native transport cancel');
1290
1323
  if (this.runPromise) ;
1291
1324
  this.runPromise = null;
1325
+ this.runPromiseDeviceId = null;
1326
+ }
1327
+ connectWithTimeout(uuid, connect) {
1328
+ return __awaiter(this, void 0, void 0, function* () {
1329
+ let timer;
1330
+ let timedOut = false;
1331
+ const pending = connect();
1332
+ pending.catch(() => undefined);
1333
+ try {
1334
+ const result = yield Promise.race([
1335
+ pending,
1336
+ new Promise((_, reject) => {
1337
+ timer = setTimeout(() => {
1338
+ timedOut = true;
1339
+ reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `BLE connect timeout after ${BLE_CONNECT_TIMEOUT_MS}ms for ${uuid}`));
1340
+ }, BLE_CONNECT_TIMEOUT_MS);
1341
+ }),
1342
+ ]);
1343
+ this.connectTimeoutCounts.delete(uuid);
1344
+ return result;
1345
+ }
1346
+ catch (error) {
1347
+ if (timedOut) {
1348
+ this.abandonStalledConnect(uuid);
1349
+ }
1350
+ throw error;
1351
+ }
1352
+ finally {
1353
+ if (timer)
1354
+ clearTimeout(timer);
1355
+ }
1356
+ });
1357
+ }
1358
+ abandonStalledConnect(uuid) {
1359
+ var _a, _b;
1360
+ const timeouts = ((_a = this.connectTimeoutCounts.get(uuid)) !== null && _a !== void 0 ? _a : 0) + 1;
1361
+ this.connectTimeoutCounts.set(uuid, timeouts);
1362
+ Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE connect timed out:', uuid, {
1363
+ consecutiveConnectTimeouts: timeouts,
1364
+ });
1365
+ (_b = this.blePlxManager) === null || _b === void 0 ? void 0 : _b.cancelDeviceConnection(uuid).catch(() => {
1366
+ });
1367
+ const stalled = transportCache[uuid];
1368
+ if (stalled) {
1369
+ delete transportCache[uuid];
1370
+ }
1371
+ this.deviceProtocol.delete(uuid);
1372
+ this.probingProtocols.delete(uuid);
1373
+ this.protocolV2Assemblers.delete(uuid);
1374
+ this.resetProtocolV2Frames(uuid);
1375
+ if (timeouts >= BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD) {
1376
+ Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE connects wedged repeatedly, resetting BLE manager');
1377
+ this.resetPlxManager();
1378
+ this.connectTimeoutCounts.delete(uuid);
1379
+ }
1292
1380
  }
1293
1381
  getCachedTransport(uuid) {
1294
1382
  const transport = transportCache[uuid];
@@ -1297,58 +1385,159 @@ class ReactNativeBleTransport {
1297
1385
  }
1298
1386
  return transport;
1299
1387
  }
1388
+ writeBlePacket(uuid, data, write, isCurrentOwner) {
1389
+ return __awaiter(this, void 0, void 0, function* () {
1390
+ let timer;
1391
+ let timedOut = false;
1392
+ try {
1393
+ yield Promise.race([
1394
+ write(data),
1395
+ new Promise((_, reject) => {
1396
+ timer = setTimeout(() => {
1397
+ timedOut = true;
1398
+ reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError, `BLE write timeout after ${BLE_WRITE_PACKET_TIMEOUT_MS}ms`));
1399
+ }, BLE_WRITE_PACKET_TIMEOUT_MS);
1400
+ }),
1401
+ ]);
1402
+ this.writeTimeoutCounts.delete(uuid);
1403
+ }
1404
+ catch (error) {
1405
+ if (timedOut) {
1406
+ if (isCurrentOwner && !isCurrentOwner()) {
1407
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] stale BLE write timed out, link kept:', uuid);
1408
+ }
1409
+ else {
1410
+ this.tearDownWedgedLink(uuid);
1411
+ }
1412
+ }
1413
+ throw error;
1414
+ }
1415
+ finally {
1416
+ if (timer)
1417
+ clearTimeout(timer);
1418
+ }
1419
+ });
1420
+ }
1421
+ tearDownWedgedLink(uuid) {
1422
+ var _a;
1423
+ const timeouts = ((_a = this.writeTimeoutCounts.get(uuid)) !== null && _a !== void 0 ? _a : 0) + 1;
1424
+ this.writeTimeoutCounts.set(uuid, timeouts);
1425
+ Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE write timed out, tearing down link:', uuid, {
1426
+ consecutiveWriteTimeouts: timeouts,
1427
+ });
1428
+ const wedged = transportCache[uuid];
1429
+ this.disconnect(uuid).catch(error => {
1430
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] wedged link teardown failed (ignored):', error);
1431
+ });
1432
+ if (wedged && transportCache[uuid] === wedged) {
1433
+ delete transportCache[uuid];
1434
+ }
1435
+ this.deviceProtocol.delete(uuid);
1436
+ this.probingProtocols.delete(uuid);
1437
+ this.protocolV2Assemblers.delete(uuid);
1438
+ this.resetProtocolV2Frames(uuid);
1439
+ if (timeouts >= BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD) {
1440
+ Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE writes wedged repeatedly, resetting BLE manager');
1441
+ this.resetPlxManager();
1442
+ this.writeTimeoutCounts.delete(uuid);
1443
+ }
1444
+ }
1445
+ resetPlxManager() {
1446
+ const manager = this.blePlxManager;
1447
+ this.blePlxManager = undefined;
1448
+ Object.keys(transportCache).forEach(key => {
1449
+ delete transportCache[key];
1450
+ });
1451
+ this.deviceProtocol.clear();
1452
+ this.probingProtocols.clear();
1453
+ this.monitorTokens.clear();
1454
+ this.protocolV2Assemblers.clear();
1455
+ try {
1456
+ manager === null || manager === void 0 ? void 0 : manager.destroy();
1457
+ }
1458
+ catch (error) {
1459
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE manager destroy failed (ignored):', error);
1460
+ }
1461
+ }
1300
1462
  createProtocolMismatchError(expected) {
1301
1463
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
1302
1464
  }
1303
1465
  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');
1466
+ 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
1467
  }
1306
1468
  clearProbeProtocol(uuid, protocol) {
1469
+ if (this.probingProtocols.get(uuid) === protocol) {
1470
+ this.probingProtocols.delete(uuid);
1471
+ }
1307
1472
  if (this.deviceProtocol.get(uuid) === protocol) {
1308
1473
  this.deviceProtocol.delete(uuid);
1309
1474
  }
1310
1475
  }
1311
- detectProtocol(uuid, expectedProtocol, protocolHint) {
1476
+ getActiveProtocol(uuid) {
1477
+ var _a;
1478
+ return (_a = this.deviceProtocol.get(uuid)) !== null && _a !== void 0 ? _a : this.probingProtocols.get(uuid);
1479
+ }
1480
+ detectProtocol(uuid, expectedProtocol, protocolHint, rebuildTransport) {
1312
1481
  return __awaiter(this, void 0, void 0, function* () {
1313
1482
  if (expectedProtocol === 'V1') {
1314
1483
  if (yield this.probeProtocolV1(uuid)) {
1315
1484
  this.deviceProtocol.set(uuid, 'V1');
1316
- Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> V1 (expected)`);
1485
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1486
+ deviceId: uuid,
1487
+ protocol: 'V1',
1488
+ source: 'expected',
1489
+ });
1317
1490
  return 'V1';
1318
1491
  }
1319
1492
  throw this.createProtocolMismatchError(expectedProtocol);
1320
1493
  }
1321
1494
  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';
1495
+ if (yield this.probeProtocolV2(uuid)) {
1496
+ this.deviceProtocol.set(uuid, 'V2');
1497
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1498
+ deviceId: uuid,
1499
+ protocol: 'V2',
1500
+ source: 'expected',
1501
+ });
1502
+ return 'V2';
1503
+ }
1504
+ throw this.createProtocolMismatchError(expectedProtocol);
1325
1505
  }
1326
1506
  const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
1327
1507
  for (let i = 0; i < probeOrder.length; i += 1) {
1328
1508
  const protocol = probeOrder[i];
1329
1509
  if (i > 0) {
1330
1510
  yield this.resetProbeStateAfterProtocolProbe(uuid, probeOrder[i - 1]);
1511
+ if (!transportCache[uuid]) {
1512
+ if (!rebuildTransport) {
1513
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
1514
+ }
1515
+ yield rebuildTransport();
1516
+ }
1331
1517
  }
1332
1518
  const detected = protocol === 'V1' ? yield this.probeProtocolV1(uuid) : yield this.probeProtocolV2(uuid);
1333
1519
  if (detected) {
1334
1520
  this.deviceProtocol.set(uuid, protocol);
1335
- Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> ${protocol}`);
1521
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1522
+ deviceId: uuid,
1523
+ protocol,
1524
+ source: 'probe',
1525
+ });
1336
1526
  return protocol;
1337
1527
  }
1338
1528
  }
1339
1529
  this.deviceProtocol.delete(uuid);
1530
+ this.probingProtocols.delete(uuid);
1340
1531
  throw this.createProtocolDetectionError();
1341
1532
  });
1342
1533
  }
1343
1534
  resetProbeStateAfterProtocolProbe(uuid, protocol) {
1344
- var _a, _b, _c, _d;
1535
+ var _a, _b, _c;
1345
1536
  return __awaiter(this, void 0, void 0, function* () {
1346
1537
  const transport = transportCache[uuid];
1538
+ yield this.protocolV2Links.invalidateLink(uuid, `Reset notify state after Protocol ${protocol} probe`);
1347
1539
  (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1348
1540
  this.resetProtocolV2Frames(uuid);
1349
- if (((_b = this.activeProtocolV2Call) === null || _b === void 0 ? void 0 : _b.uuid) === uuid) {
1350
- this.activeProtocolV2Call = null;
1351
- }
1352
1541
  if (this.runPromise) {
1353
1542
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
1354
1543
  this.runPromise.reject(error);
@@ -1360,11 +1549,11 @@ class ReactNativeBleTransport {
1360
1549
  if (this.monitorTokens.get(uuid) === transport.monitorToken) {
1361
1550
  this.monitorTokens.delete(uuid);
1362
1551
  }
1363
- (_c = transport.notifySubscription) === null || _c === void 0 ? void 0 : _c.remove();
1552
+ (_b = transport.notifySubscription) === null || _b === void 0 ? void 0 : _b.remove();
1364
1553
  transport.notifySubscription = undefined;
1365
1554
  if (previousNotifyTransactionId) {
1366
1555
  try {
1367
- yield ((_d = this.blePlxManager) === null || _d === void 0 ? void 0 : _d.cancelTransaction(previousNotifyTransactionId));
1556
+ yield ((_c = this.blePlxManager) === null || _c === void 0 ? void 0 : _c.cancelTransaction(previousNotifyTransactionId));
1368
1557
  }
1369
1558
  catch (error) {
1370
1559
  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 +1579,17 @@ class ReactNativeBleTransport {
1390
1579
  return false;
1391
1580
  }
1392
1581
  try {
1393
- this.deviceProtocol.set(uuid, 'V1');
1394
- yield this.callProtocolV1(uuid, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
1582
+ this.probingProtocols.set(uuid, 'V1');
1583
+ yield this.callProtocolV1(uuid, 'GetFeatures', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
1584
+ this.probingProtocols.delete(uuid);
1395
1585
  return true;
1396
1586
  }
1397
1587
  catch (error) {
1398
1588
  this.clearProbeProtocol(uuid, 'V1');
1399
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 Initialize probe failed:', error);
1589
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe failed:', error);
1590
+ if (isWedgedWriteError(error)) {
1591
+ throw error;
1592
+ }
1400
1593
  return false;
1401
1594
  }
1402
1595
  });
@@ -1407,7 +1600,7 @@ class ReactNativeBleTransport {
1407
1600
  if (!this._messages || !this._messagesV2) {
1408
1601
  return false;
1409
1602
  }
1410
- this.deviceProtocol.set(uuid, 'V2');
1603
+ this.probingProtocols.set(uuid, 'V2');
1411
1604
  (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1412
1605
  const detected = yield transport.probeProtocolV2({
1413
1606
  call: (name, data, options) => this.callProtocolV2(uuid, name, data, options),
@@ -1423,17 +1616,16 @@ class ReactNativeBleTransport {
1423
1616
  if (!detected) {
1424
1617
  this.clearProbeProtocol(uuid, 'V2');
1425
1618
  }
1619
+ else {
1620
+ this.probingProtocols.delete(uuid);
1621
+ }
1426
1622
  return detected;
1427
1623
  });
1428
1624
  }
1429
- handleProtocolV2Notification(uuid, data) {
1430
- var _a, _b, _c;
1625
+ handleProtocolV2Notification(uuid, monitorToken, data) {
1431
1626
  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);
1627
+ if (this.monitorTokens.get(uuid) !== monitorToken)
1435
1628
  return;
1436
- }
1437
1629
  if (data.length === 0)
1438
1630
  return;
1439
1631
  const assembler = this.protocolV2Assemblers.get(uuid);
@@ -1446,8 +1638,10 @@ class ReactNativeBleTransport {
1446
1638
  catch (error) {
1447
1639
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 notification error:', error);
1448
1640
  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);
1641
+ this.rejectProtocolV2Frames(uuid, notifyError);
1642
+ this.protocolV2Links
1643
+ .invalidateLink(uuid, `Protocol V2 notification error: ${error}`)
1644
+ .catch(invalidateError => Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 notify cleanup failed:', invalidateError));
1451
1645
  }
1452
1646
  }
1453
1647
  getProtocolV2FrameQueue(uuid) {
@@ -1467,20 +1661,16 @@ class ReactNativeBleTransport {
1467
1661
  }
1468
1662
  this.getProtocolV2FrameQueue(uuid).push(frame);
1469
1663
  }
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
1664
  resetProtocolV2Frames(uuid) {
1478
- this.protocolV2FrameQueues.delete(uuid);
1479
- this.protocolV2FramePromises.delete(uuid);
1665
+ this.rejectProtocolV2Frames(uuid, new Error(`Protocol V2 frame state reset for ${uuid}`));
1480
1666
  }
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;
1667
+ rejectProtocolV2Frames(uuid, error) {
1668
+ this.protocolV2FrameQueues.delete(uuid);
1669
+ const framePromise = this.protocolV2FramePromises.get(uuid);
1670
+ if (framePromise) {
1671
+ this.protocolV2FramePromises.delete(uuid);
1672
+ framePromise.reject(error);
1673
+ }
1484
1674
  }
1485
1675
  readProtocolV2Frame(uuid) {
1486
1676
  return __awaiter(this, void 0, void 0, function* () {
@@ -1500,133 +1690,143 @@ class ReactNativeBleTransport {
1500
1690
  }
1501
1691
  });
1502
1692
  }
1503
- writeProtocolV2Frame(transport, frame, options) {
1693
+ writeProtocolV2Packet(uuid, transport, base64, context, assertCurrentGeneration) {
1694
+ return __awaiter(this, void 0, void 0, function* () {
1695
+ let attempt = 0;
1696
+ for (;;) {
1697
+ assertCurrentGeneration();
1698
+ if (context.signal.aborted) {
1699
+ throw new Error(`Protocol V2 BLE write aborted for ${context.messageName}`);
1700
+ }
1701
+ try {
1702
+ yield this.writeBlePacket(uuid, base64, payload => transport.writeCharacteristic.writeWithoutResponse(payload), () => {
1703
+ try {
1704
+ assertCurrentGeneration();
1705
+ return !context.signal.aborted;
1706
+ }
1707
+ catch (_a) {
1708
+ return false;
1709
+ }
1710
+ });
1711
+ assertCurrentGeneration();
1712
+ return;
1713
+ }
1714
+ catch (error) {
1715
+ if (getFirmwareUploadWriteRetryType(error) !== 'congested' ||
1716
+ attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
1717
+ throw error;
1718
+ }
1719
+ const delayMs = resolveFirmwareUploadRetryDelay(attempt);
1720
+ attempt += 1;
1721
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 congested write retry:', {
1722
+ name: context.messageName,
1723
+ attempt,
1724
+ delayMs,
1725
+ });
1726
+ yield delay(delayMs);
1727
+ }
1728
+ }
1729
+ });
1730
+ }
1731
+ writeProtocolV2Frame(uuid, transport$1, frame, context, assertCurrentGeneration) {
1504
1732
  return __awaiter(this, void 0, void 0, function* () {
1505
1733
  const tuning = getProtocolV2BleTuning();
1506
1734
  const packetCapacity = resolveProtocolV2PacketCapacity({
1507
1735
  platform: reactNative.Platform.OS,
1508
1736
  iosPacketLength: tuning.iosPacketLength,
1509
1737
  androidPacketLength: tuning.androidPacketLength,
1510
- mtu: reactNative.Platform.OS === 'android' ? transport.mtuSize : undefined,
1738
+ mtu: reactNative.Platform.OS === 'android' ? transport$1.mtuSize : undefined,
1739
+ });
1740
+ yield transport.writeProtocolV2BleFrame({
1741
+ frame,
1742
+ packetCapacity,
1743
+ assertActive: assertCurrentGeneration,
1744
+ signal: context.signal,
1745
+ abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
1746
+ burstSize: FIRMWARE_UPLOAD_WRITE_BURST_SIZE,
1747
+ burstPauseMs: FIRMWARE_UPLOAD_WRITE_PAUSE_MS,
1748
+ flushDelayMs: FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS,
1749
+ wait: delay,
1750
+ writePacket: packet => this.writeProtocolV2Packet(uuid, transport$1, buffer.Buffer.from(packet).toString('base64'), context, assertCurrentGeneration),
1511
1751
  });
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
1752
  });
1536
1753
  }
1537
1754
  callProtocolV2(uuid, name, data, options) {
1538
- var _a, _b, _c, _d;
1539
1755
  return __awaiter(this, void 0, void 0, function* () {
1540
1756
  if (!this._messages || !this._messagesV2) {
1541
1757
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
1542
1758
  }
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 });
1759
+ const callOptions = options;
1564
1760
  const highVolumeWrite = transport.LogBlockCommand.has(name);
1565
1761
  if (highVolumeWrite) {
1566
1762
  const tuning = getProtocolV2BleTuning();
1567
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume write uses throttled writeWithoutResponse:', name, {
1763
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume write configured', {
1764
+ name,
1765
+ writeMode: 'withoutResponse',
1568
1766
  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
1767
  });
1574
1768
  }
1575
1769
  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;
1770
+ return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, callOptions);
1601
1771
  }
1602
1772
  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
1773
  Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] Protocol V2 call error:', e);
1608
1774
  throw e;
1609
1775
  }
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
1776
  });
1623
1777
  }
1778
+ createProtocolV2Adapter(uuid) {
1779
+ var _a;
1780
+ const generation = (_a = this.monitorTokens.get(uuid)) !== null && _a !== void 0 ? _a : 0;
1781
+ const assertCurrentGeneration = () => {
1782
+ if (this.monitorTokens.get(uuid) !== generation) {
1783
+ throw new Error(`Protocol V2 monitor generation changed for ${uuid}`);
1784
+ }
1785
+ };
1786
+ return {
1787
+ router: transport.PROTOCOL_V2_CHANNEL_BLE_UART,
1788
+ maxFrameBytes: transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES,
1789
+ generation,
1790
+ prepareCall: () => {
1791
+ var _a;
1792
+ assertCurrentGeneration();
1793
+ (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1794
+ this.resetProtocolV2Frames(uuid);
1795
+ },
1796
+ writeFrame: (frame, context) => __awaiter(this, void 0, void 0, function* () {
1797
+ assertCurrentGeneration();
1798
+ const currentTransport = this.getCachedTransport(uuid);
1799
+ yield this.writeProtocolV2Frame(uuid, currentTransport, frame, context, assertCurrentGeneration);
1800
+ }),
1801
+ readFrame: () => __awaiter(this, void 0, void 0, function* () {
1802
+ assertCurrentGeneration();
1803
+ const rxFrame = yield this.readProtocolV2Frame(uuid);
1804
+ if (!(rxFrame instanceof Uint8Array)) {
1805
+ throw new Error('Protocol V2 response is not Uint8Array');
1806
+ }
1807
+ return rxFrame;
1808
+ }),
1809
+ reset: (reason) => {
1810
+ var _a;
1811
+ (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1812
+ this.rejectProtocolV2Frames(uuid, new Error(reason));
1813
+ },
1814
+ logger: Log,
1815
+ logPrefix: 'ProtocolV2 RN-BLE',
1816
+ createTimeoutError: (messageName, timeout) => hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, `BLE response timeout after ${timeout}ms for ${messageName}`),
1817
+ };
1818
+ }
1624
1819
  getProtocolType(path) {
1625
- return this.deviceProtocol.get(path);
1820
+ return this.getActiveProtocol(path);
1626
1821
  }
1627
1822
  }
1628
1823
 
1824
+ exports.BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD;
1825
+ exports.BLE_CONNECT_TIMEOUT_MS = BLE_CONNECT_TIMEOUT_MS;
1826
+ exports.BLE_WRITE_PACKET_TIMEOUT_MS = BLE_WRITE_PACKET_TIMEOUT_MS;
1827
+ exports.BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD;
1629
1828
  exports.configureProtocolV2BleTuning = configureProtocolV2BleTuning;
1630
1829
  exports["default"] = ReactNativeBleTransport;
1830
+ exports.getFirmwareUploadWriteRetryType = getFirmwareUploadWriteRetryType;
1631
1831
  exports.getProtocolV2BleTuning = getProtocolV2BleTuning;
1632
1832
  exports.resetProtocolV2BleTuning = resetProtocolV2BleTuning;