@onekeyfe/hd-transport-react-native 1.2.0-alpha.5 → 1.2.0-alpha.50

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,7 @@ 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;
225
+ const IOS_PROTOCOL_V2_CONTROL_WRITE_DELAY_MS = 5;
272
226
  const ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH = 192;
273
227
  const FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY = reactNative.Platform.OS === 'ios' ? IOS_PACKET_LENGTH : ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH;
274
228
  const ANDROID_GATT_CONGESTED_STATUS = 143;
@@ -279,10 +233,6 @@ const getFirmwareUploadWriteRetryType = (error) => {
279
233
  if (!error || typeof error !== 'object')
280
234
  return null;
281
235
  const bleWriteError = error;
282
- if (bleWriteError.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected ||
283
- bleWriteError.errorCode === reactNativeBlePlx.BleErrorCode.CharacteristicNotFound) {
284
- return 'reconnectable';
285
- }
286
236
  if (bleWriteError.androidErrorCode === ANDROID_GATT_CONGESTED_STATUS ||
287
237
  bleWriteError.status === ANDROID_GATT_CONGESTED_STATUS) {
288
238
  return 'congested';
@@ -293,22 +243,14 @@ const getFirmwareUploadWriteRetryType = (error) => {
293
243
  return /GATT_CONGESTED|status\s*[:=]?\s*143/.test(text) ? 'congested' : null;
294
244
  };
295
245
  const resolveFirmwareUploadRetryDelay = (attempt, baseDelayMs = 200, maxDelayMs = 1200) => Math.min(baseDelayMs * Math.pow(2, attempt), maxDelayMs);
296
- const BLE_RESPONSE_TIMEOUT_MS = 30000;
297
246
  const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
298
247
  const PROTOCOL_V2_PROBE_TIMEOUT_MS = 10000;
299
- const DEVICE_SCAN_TIMEOUT_MS = 8000;
248
+ const DEVICE_SCAN_TIMEOUT_MS = 3000;
300
249
  const IOS_NOTIFY_READY_DELAY_MS = 150;
301
250
  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
251
  const DEFAULT_PROTOCOL_V2_BLE_TUNING = {
306
252
  iosPacketLength: IOS_PACKET_LENGTH,
307
253
  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
254
  };
313
255
  let protocolV2BleTuning = Object.assign({}, DEFAULT_PROTOCOL_V2_BLE_TUNING);
314
256
  const normalizePositiveInteger = (value, fallback) => {
@@ -318,20 +260,15 @@ const normalizePositiveInteger = (value, fallback) => {
318
260
  return Math.floor(normalized);
319
261
  };
320
262
  function configureProtocolV2BleTuning(tuning = {}) {
321
- var _a;
322
263
  protocolV2BleTuning = {
323
264
  iosPacketLength: normalizePositiveInteger(tuning.iosPacketLength, protocolV2BleTuning.iosPacketLength),
324
265
  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
266
  };
330
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 BLE tuning configured:', protocolV2BleTuning);
267
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE tuning configured', protocolV2BleTuning);
331
268
  }
332
269
  function resetProtocolV2BleTuning() {
333
270
  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);
271
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE tuning reset', protocolV2BleTuning);
335
272
  }
336
273
  function getProtocolV2BleTuning() {
337
274
  return Object.assign({}, protocolV2BleTuning);
@@ -342,13 +279,6 @@ function inferProtocolHintFromDeviceName(name) {
342
279
  function getDeviceDisplayName(device) {
343
280
  return (device === null || device === void 0 ? void 0 : device.name) || (device === null || device === void 0 ? void 0 : device.localName) || null;
344
281
  }
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
282
  const ANDROID_REQUEST_MTU = 256;
353
283
  const connectOptions = {
354
284
  requestMTU: ANDROID_REQUEST_MTU,
@@ -371,9 +301,10 @@ const requestAndroidMtu = (device) => __awaiter(void 0, void 0, void 0, function
371
301
  return device;
372
302
  try {
373
303
  const mtuDevice = yield device.requestMTU(ANDROID_REQUEST_MTU);
374
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android MTU requested:', {
304
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] MTU configured', {
305
+ deviceId: device.id,
375
306
  requested: ANDROID_REQUEST_MTU,
376
- mtu: mtuDevice.mtu,
307
+ actual: mtuDevice.mtu,
377
308
  });
378
309
  return mtuDevice;
379
310
  }
@@ -408,15 +339,36 @@ class ReactNativeBleTransport {
408
339
  this.stopped = false;
409
340
  this.scanTimeout = DEVICE_SCAN_TIMEOUT_MS;
410
341
  this.runPromise = null;
342
+ this.runPromiseDeviceId = null;
411
343
  this.firmwareUploadWriteRecoveryIds = new Set();
412
344
  this.deviceProtocol = new Map();
413
345
  this.deviceProtocolHints = new Map();
414
346
  this.protocolV2Assemblers = new Map();
415
347
  this.protocolV2FrameQueues = new Map();
416
348
  this.protocolV2FramePromises = new Map();
417
- this.activeProtocolV2Call = null;
418
- this.nextProtocolV2CallToken = 1;
349
+ this.protocolV2Links = new transport.ProtocolV2LinkManager({
350
+ getSchemas: () => {
351
+ if (!this._messages || !this._messagesV2) {
352
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
353
+ }
354
+ return {
355
+ protocolV1: this._messages,
356
+ protocolV2: this._messagesV2,
357
+ };
358
+ },
359
+ classifyError: () => 'link-fatal',
360
+ onLinkInvalidated: (uuid, reason) => __awaiter(this, void 0, void 0, function* () {
361
+ var _b;
362
+ (_b = this.protocolV2Assemblers.get(uuid)) === null || _b === void 0 ? void 0 : _b.reset();
363
+ this.rejectProtocolV2Frames(uuid, new Error(reason));
364
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 link invalidated:', uuid, reason);
365
+ if (reason.startsWith('Protocol V2 link-fatal error:')) {
366
+ yield this.releaseNative(uuid, true);
367
+ }
368
+ }),
369
+ });
419
370
  this.monitorTokens = new Map();
371
+ this.disconnectEventTokens = new Map();
420
372
  this.nextMonitorToken = 1;
421
373
  this.scanTimeout = (_a = options.scanTimeout) !== null && _a !== void 0 ? _a : DEVICE_SCAN_TIMEOUT_MS;
422
374
  }
@@ -430,8 +382,18 @@ class ReactNativeBleTransport {
430
382
  this._messages = messages;
431
383
  }
432
384
  configureProtocolV2(signedData) {
385
+ const configuration = typeof signedData === 'string' ? signedData : JSON.stringify(signedData);
386
+ if (this.protocolV2SchemaConfiguration === configuration) {
387
+ return;
388
+ }
389
+ const isReconfiguration = this.protocolV2SchemaConfiguration !== undefined;
433
390
  this._messagesV2 = parseConfigure(signedData);
434
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 schema configured');
391
+ this.protocolV2SchemaConfiguration = configuration;
392
+ if (isReconfiguration) {
393
+ this.protocolV2Links
394
+ .invalidateAllLinks('Protocol V2 schema reconfigured')
395
+ .catch(error => Log === null || Log === void 0 ? void 0 : Log.debug('Protocol V2 schema link cleanup failed:', error));
396
+ }
435
397
  }
436
398
  listen() {
437
399
  }
@@ -442,7 +404,6 @@ class ReactNativeBleTransport {
442
404
  return Promise.resolve(this.blePlxManager);
443
405
  }
444
406
  resolveCharacteristics(device) {
445
- var _a, _b, _c, _d;
446
407
  return __awaiter(this, void 0, void 0, function* () {
447
408
  yield device.discoverAllServicesAndCharacteristics();
448
409
  let infos = tryToGetConfiguration(device);
@@ -459,19 +420,11 @@ class ReactNativeBleTransport {
459
420
  }
460
421
  }
461
422
  }
462
- let fallbackServiceUuid;
463
423
  if (!infos) {
464
424
  const services = yield device.services();
465
425
  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
426
  }
474
- if (!infos && !fallbackServiceUuid) {
427
+ if (!infos) {
475
428
  try {
476
429
  Log === null || Log === void 0 ? void 0 : Log.debug('cancel connection when service not found');
477
430
  yield device.cancelConnection();
@@ -481,9 +434,7 @@ class ReactNativeBleTransport {
481
434
  }
482
435
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
483
436
  }
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';
437
+ const { serviceUuid, writeUuid, notifyUuid } = infos;
487
438
  if (!serviceUuid) {
488
439
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
489
440
  }
@@ -524,8 +475,8 @@ class ReactNativeBleTransport {
524
475
  attachDisconnectSubscription(transport, device, uuid) {
525
476
  var _a;
526
477
  (_a = transport.disconnectSubscription) === null || _a === void 0 ? void 0 : _a.remove();
478
+ const { monitorToken } = transport;
527
479
  transport.disconnectSubscription = device.onDisconnected(() => {
528
- var _a;
529
480
  if (this.firmwareUploadWriteRecoveryIds.has(uuid)) {
530
481
  Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored during FirmwareUpload write recovery: ', uuid);
531
482
  return;
@@ -534,17 +485,16 @@ class ReactNativeBleTransport {
534
485
  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
486
  return;
536
487
  }
488
+ if (this.monitorTokens.get(uuid) !== monitorToken) {
489
+ Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored for stale generation: ', device === null || device === void 0 ? void 0 : device.id);
490
+ return;
491
+ }
537
492
  try {
538
493
  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) {
494
+ this.emitDeviceDisconnect(uuid, device === null || device === void 0 ? void 0 : device.name, monitorToken);
495
+ if (this.runPromise && this.runPromiseDeviceId === uuid) {
545
496
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError);
546
497
  this.runPromise.reject(error);
547
- this.rejectAllProtocolV2Frames(error);
548
498
  }
549
499
  }
550
500
  catch (e) {
@@ -555,6 +505,22 @@ class ReactNativeBleTransport {
555
505
  }
556
506
  });
557
507
  }
508
+ emitDeviceDisconnect(uuid, name, token) {
509
+ var _a;
510
+ if (token === undefined || this.disconnectEventTokens.get(uuid) === token) {
511
+ return;
512
+ }
513
+ if (this.monitorTokens.get(uuid) !== token) {
514
+ Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect event ignored for stale generation: ', uuid);
515
+ return;
516
+ }
517
+ this.disconnectEventTokens.set(uuid, token);
518
+ (_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit(transport.TRANSPORT_EVENT.DEVICE_DISCONNECT, {
519
+ name,
520
+ id: uuid,
521
+ connectId: uuid,
522
+ });
523
+ }
558
524
  reconnectFirmwareUploadTransport(uuid, transport) {
559
525
  var _a, _b;
560
526
  return __awaiter(this, void 0, void 0, function* () {
@@ -624,13 +590,12 @@ class ReactNativeBleTransport {
624
590
  return;
625
591
  }
626
592
  }
627
- blePlxManager.startDeviceScan(null, {
593
+ blePlxManager.startDeviceScan(getBluetoothServiceUuids(), {
628
594
  allowDuplicates: true,
629
595
  scanMode: reactNativeBlePlx.ScanMode.LowLatency,
630
596
  }, (error, device) => {
631
- var _a, _b, _c;
597
+ var _a;
632
598
  if (error) {
633
- Log === null || Log === void 0 ? void 0 : Log.debug('ble scan manager: ', blePlxManager);
634
599
  Log === null || Log === void 0 ? void 0 : Log.debug('ble scan error: ', error);
635
600
  if ([reactNativeBlePlx.BleErrorCode.BluetoothPoweredOff, reactNativeBlePlx.BleErrorCode.BluetoothInUnknownState].includes(error.errorCode)) {
636
601
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePermissionError));
@@ -650,25 +615,19 @@ class ReactNativeBleTransport {
650
615
  return;
651
616
  }
652
617
  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', {
618
+ const isUnnamedIOSPeripheral = reactNative.Platform.OS === 'ios' && !(displayName === null || displayName === void 0 ? void 0 : displayName.trim());
619
+ const isFindMyPeripheral = hdShared.isPro2FindMyAdvertisementName(device === null || device === void 0 ? void 0 : device.name) ||
620
+ hdShared.isPro2FindMyAdvertisementName(device === null || device === void 0 ? void 0 : device.localName);
621
+ const isOneKey = !isUnnamedIOSPeripheral &&
622
+ !isFindMyPeripheral &&
623
+ hdShared.isOnekeyBluetoothDevice({
624
+ id: device === null || device === void 0 ? void 0 : device.id,
659
625
  name: device === null || device === void 0 ? void 0 : device.name,
660
626
  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,
627
+ serviceUuids: device === null || device === void 0 ? void 0 : device.serviceUUIDs,
664
628
  });
665
- }
666
629
  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
630
  addDevice(device);
671
- Log === null || Log === void 0 ? void 0 : Log.debug('search device end ======================\n');
672
631
  }
673
632
  else if (displayName && /\bpro\s*2\b/i.test(displayName)) {
674
633
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Pro2-like BLE device was not accepted:', {
@@ -679,10 +638,23 @@ class ReactNativeBleTransport {
679
638
  });
680
639
  }
681
640
  });
682
- getConnectedDeviceIds(getBluetoothServiceUuids()).then(devices => {
641
+ getConnectedDeviceIds(reactNative.Platform.OS === 'ios' ? getBluetoothServiceUuids() : []).then(devices => {
683
642
  for (const device of devices) {
684
- Log === null || Log === void 0 ? void 0 : Log.debug('search connected peripheral: ', device.id);
685
- addDevice(device);
643
+ const localName = 'localName' in device && typeof device.localName === 'string'
644
+ ? device.localName
645
+ : null;
646
+ const isFindMyPeripheral = hdShared.isPro2FindMyAdvertisementName(device.name) ||
647
+ hdShared.isPro2FindMyAdvertisementName(localName);
648
+ if (!isFindMyPeripheral &&
649
+ hdShared.isOnekeyBluetoothDevice({
650
+ id: device.id,
651
+ name: device.name,
652
+ localName,
653
+ serviceUuids: device.serviceUUIDs,
654
+ })) {
655
+ Log === null || Log === void 0 ? void 0 : Log.debug('search connected peripheral: ', device.id);
656
+ addDevice(device);
657
+ }
686
658
  }
687
659
  });
688
660
  const addDevice = (device) => {
@@ -694,6 +666,12 @@ class ReactNativeBleTransport {
694
666
  this.deviceProtocolHints.set(device.id, protocolHint);
695
667
  }
696
668
  deviceList.push(Object.assign(Object.assign({}, device), { name: displayName, commType: 'ble' }));
669
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] OneKey BLE device discovered', {
670
+ deviceId: device.id,
671
+ name: displayName,
672
+ serviceUUIDs: device.serviceUUIDs,
673
+ protocolHint,
674
+ });
697
675
  }
698
676
  };
699
677
  timer.timeout(() => {
@@ -703,6 +681,33 @@ class ReactNativeBleTransport {
703
681
  }));
704
682
  });
705
683
  }
684
+ installTransportForAcquire(uuid, device, characteristics) {
685
+ return __awaiter(this, void 0, void 0, function* () {
686
+ const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristics(device));
687
+ const transport$1 = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
688
+ if (reactNative.Platform.OS === 'android') {
689
+ transport$1.mtuSize = typeof device.mtu === 'number' ? device.mtu : transport$1.mtuSize;
690
+ }
691
+ const monitorToken = this.nextMonitorToken;
692
+ this.nextMonitorToken += 1;
693
+ const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
694
+ transport$1.monitorToken = monitorToken;
695
+ transport$1.notifyTransactionId = notifyTransactionId;
696
+ this.monitorTokens.set(uuid, monitorToken);
697
+ transport$1.notifySubscription = this._monitorCharacteristic(transport$1.notifyCharacteristic, uuid, monitorToken, notifyTransactionId);
698
+ transportCache[uuid] = transport$1;
699
+ this.protocolV2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES));
700
+ if (reactNative.Platform.OS === 'ios') {
701
+ yield new Promise(resolve => {
702
+ setTimeout(resolve, IOS_NOTIFY_READY_DELAY_MS);
703
+ });
704
+ }
705
+ else if (reactNative.Platform.OS === 'android') {
706
+ yield delay(ANDROID_NOTIFY_READY_DELAY_MS);
707
+ }
708
+ return transport$1;
709
+ });
710
+ }
706
711
  acquire(input) {
707
712
  var _a, _b;
708
713
  return __awaiter(this, void 0, void 0, function* () {
@@ -727,9 +732,8 @@ class ReactNativeBleTransport {
727
732
  if (forceCleanRunPromise && this.runPromise) {
728
733
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
729
734
  this.runPromise.reject(error);
730
- this.rejectAllProtocolV2Frames(error);
731
735
  this.runPromise = null;
732
- this.activeProtocolV2Call = null;
736
+ this.runPromiseDeviceId = null;
733
737
  Log === null || Log === void 0 ? void 0 : Log.debug('Force clean Bluetooth run promise, forceCleanRunPromise: ', forceCleanRunPromise);
734
738
  }
735
739
  const blePlxManager = yield this.getPlxManager();
@@ -811,50 +815,41 @@ class ReactNativeBleTransport {
811
815
  }
812
816
  }
813
817
  device = yield requestAndroidMtu(device);
814
- const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristics(device);
818
+ const acquiredDevice = device;
819
+ const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristics(acquiredDevice);
815
820
  const protocolHint = expectedProtocol
816
821
  ? undefined
817
- : (_a = this.deviceProtocolHints.get(uuid)) !== null && _a !== void 0 ? _a : inferProtocolHintFromDeviceName(getDeviceDisplayName(device));
822
+ : (_b = (_a = input.protocolHint) !== null && _a !== void 0 ? _a : this.deviceProtocolHints.get(uuid)) !== null && _b !== void 0 ? _b : inferProtocolHintFromDeviceName(getDeviceDisplayName(acquiredDevice));
818
823
  yield this.release(uuid, true);
819
824
  if (protocolHint) {
820
825
  this.deviceProtocolHints.set(uuid, protocolHint);
821
826
  }
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
- });
827
+ yield this.installTransportForAcquire(uuid, acquiredDevice, {
828
+ writeCharacteristic,
829
+ notifyCharacteristic,
830
+ });
831
+ try {
832
+ const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint, () => __awaiter(this, void 0, void 0, function* () {
833
+ yield this.installTransportForAcquire(uuid, acquiredDevice);
834
+ }));
835
+ const currentTransport = transportCache[uuid];
836
+ if (!currentTransport) {
837
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
838
+ }
839
+ this.attachDisconnectSubscription(currentTransport, acquiredDevice, uuid);
840
+ return { uuid, protocolType };
839
841
  }
840
- else if (reactNative.Platform.OS === 'android') {
841
- yield delay(ANDROID_NOTIFY_READY_DELAY_MS);
842
+ catch (error) {
843
+ yield this.release(uuid, true);
844
+ throw error;
842
845
  }
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
846
  });
852
847
  }
853
848
  _monitorCharacteristic(characteristic, uuid, monitorToken, notifyTransactionId) {
854
849
  let bufferLength = 0;
855
850
  let buffer$1 = [];
856
851
  const subscription = characteristic.monitor((error, c) => {
857
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
852
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
858
853
  const isCurrentMonitor = this.monitorTokens.get(uuid) === monitorToken;
859
854
  if (error) {
860
855
  Log === null || Log === void 0 ? void 0 : Log.debug(`error monitor ${characteristic.uuid}, deviceId: ${characteristic.deviceID}: ${error}`);
@@ -866,28 +861,44 @@ class ReactNativeBleTransport {
866
861
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
867
862
  return;
868
863
  }
869
- if (this.runPromise) {
870
- let ERROR = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
864
+ if (this.deviceProtocol.get(uuid) === 'V2') {
865
+ let errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
871
866
  if ((_a = error.reason) === null || _a === void 0 ? void 0 : _a.includes('The connection has timed out unexpectedly')) {
872
- ERROR = hdShared.HardwareErrorCode.BleTimeoutError;
867
+ errorCode = hdShared.HardwareErrorCode.BleTimeoutError;
873
868
  }
874
- if ((_b = error.reason) === null || _b === void 0 ? void 0 : _b.includes('Encryption is insufficient')) {
875
- ERROR = hdShared.HardwareErrorCode.BleDeviceBondError;
869
+ else if ((_b = error.reason) === null || _b === void 0 ? void 0 : _b.includes('Encryption is insufficient')) {
870
+ errorCode = hdShared.HardwareErrorCode.BleDeviceBondError;
876
871
  }
877
- if (((_c = error.reason) === null || _c === void 0 ? void 0 : _c.includes('Cannot write client characteristic config descriptor')) ||
872
+ else if (((_c = error.reason) === null || _c === void 0 ? void 0 : _c.includes('Cannot write client characteristic config descriptor')) ||
878
873
  ((_d = error.reason) === null || _d === void 0 ? void 0 : _d.includes('Cannot find client characteristic config descriptor')) ||
879
874
  ((_e = error.reason) === null || _e === void 0 ? void 0 : _e.includes('The handle is invalid')) ||
880
875
  ((_f = error.reason) === null || _f === void 0 ? void 0 : _f.includes('Writing is not permitted')) ||
881
876
  ((_g = error.reason) === null || _g === void 0 ? void 0 : _g.includes('notify change failed for device'))) {
877
+ errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure;
878
+ }
879
+ this.rejectProtocolV2Frames(uuid, hdShared.ERRORS.TypedError(errorCode));
880
+ return;
881
+ }
882
+ if (this.runPromise && this.runPromiseDeviceId === uuid) {
883
+ let ERROR = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
884
+ if ((_h = error.reason) === null || _h === void 0 ? void 0 : _h.includes('The connection has timed out unexpectedly')) {
885
+ ERROR = hdShared.HardwareErrorCode.BleTimeoutError;
886
+ }
887
+ if ((_j = error.reason) === null || _j === void 0 ? void 0 : _j.includes('Encryption is insufficient')) {
888
+ ERROR = hdShared.HardwareErrorCode.BleDeviceBondError;
889
+ }
890
+ if (((_k = error.reason) === null || _k === void 0 ? void 0 : _k.includes('Cannot write client characteristic config descriptor')) ||
891
+ ((_l = error.reason) === null || _l === void 0 ? void 0 : _l.includes('Cannot find client characteristic config descriptor')) ||
892
+ ((_m = error.reason) === null || _m === void 0 ? void 0 : _m.includes('The handle is invalid')) ||
893
+ ((_o = error.reason) === null || _o === void 0 ? void 0 : _o.includes('Writing is not permitted')) ||
894
+ ((_p = error.reason) === null || _p === void 0 ? void 0 : _p.includes('notify change failed for device'))) {
882
895
  const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure);
883
896
  this.runPromise.reject(notifyError);
884
- this.rejectAllProtocolV2Frames(notifyError);
885
897
  Log === null || Log === void 0 ? void 0 : Log.debug(`${hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure} ${error.message} ${error.reason}`);
886
898
  return;
887
899
  }
888
900
  const notifyError = hdShared.ERRORS.TypedError(ERROR);
889
901
  this.runPromise.reject(notifyError);
890
- this.rejectAllProtocolV2Frames(notifyError);
891
902
  Log === null || Log === void 0 ? void 0 : Log.debug(': monitor notify error, and has unreleased Promise', Error);
892
903
  }
893
904
  return;
@@ -907,7 +918,7 @@ class ReactNativeBleTransport {
907
918
  return;
908
919
  }
909
920
  if (protocol === 'V2') {
910
- this.handleProtocolV2Notification(uuid, new Uint8Array(data));
921
+ this.handleProtocolV2Notification(uuid, monitorToken, new Uint8Array(data));
911
922
  return;
912
923
  }
913
924
  if (isHeaderChunk(data)) {
@@ -921,28 +932,40 @@ class ReactNativeBleTransport {
921
932
  const value = buffer.Buffer.from(buffer$1);
922
933
  bufferLength = 0;
923
934
  buffer$1 = [];
924
- (_h = this.runPromise) === null || _h === void 0 ? void 0 : _h.resolve(value.toString('hex'));
935
+ if (this.runPromiseDeviceId === uuid) {
936
+ (_q = this.runPromise) === null || _q === void 0 ? void 0 : _q.resolve(value.toString('hex'));
937
+ }
925
938
  }
926
939
  }
927
940
  catch (error) {
928
941
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor data error: ', error);
929
942
  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);
943
+ if (this.deviceProtocol.get(uuid) === 'V2') {
944
+ this.rejectProtocolV2Frames(uuid, notifyError);
945
+ }
946
+ else if (this.runPromiseDeviceId === uuid) {
947
+ (_r = this.runPromise) === null || _r === void 0 ? void 0 : _r.reject(notifyError);
948
+ }
932
949
  }
933
950
  }, notifyTransactionId);
934
951
  return subscription;
935
952
  }
936
953
  release(uuid, onclose = false) {
937
- var _a, _b, _c, _d, _e, _f, _g, _h;
954
+ return __awaiter(this, void 0, void 0, function* () {
955
+ yield this.protocolV2Links.invalidateLink(uuid, 'React Native BLE transport released');
956
+ return this.releaseNative(uuid, onclose);
957
+ });
958
+ }
959
+ releaseNative(uuid, onclose = false) {
960
+ var _a, _b, _c, _d, _e, _f, _g;
938
961
  return __awaiter(this, void 0, void 0, function* () {
939
962
  const transport = transportCache[uuid];
940
- if (this.runPromise) {
963
+ if (this.runPromise && this.runPromiseDeviceId === uuid) {
941
964
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
942
965
  this.runPromise.reject(error);
943
966
  this.runPromise = null;
944
- this.rejectAllProtocolV2Frames(error);
945
- this.activeProtocolV2Call = null;
967
+ this.runPromiseDeviceId = null;
968
+ this.rejectProtocolV2Frames(uuid, error);
946
969
  }
947
970
  else {
948
971
  this.resetProtocolV2Frames(uuid);
@@ -950,9 +973,6 @@ class ReactNativeBleTransport {
950
973
  if (reactNative.Platform.OS === 'android' && !onclose && transport) {
951
974
  (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
952
975
  this.resetProtocolV2Frames(uuid);
953
- if (((_b = this.activeProtocolV2Call) === null || _b === void 0 ? void 0 : _b.uuid) === uuid) {
954
- this.activeProtocolV2Call = null;
955
- }
956
976
  return Promise.resolve(true);
957
977
  }
958
978
  if (transport) {
@@ -960,14 +980,14 @@ class ReactNativeBleTransport {
960
980
  this.monitorTokens.delete(uuid);
961
981
  }
962
982
  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();
983
+ (_b = transport.disconnectSubscription) === null || _b === void 0 ? void 0 : _b.remove();
964
984
  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();
985
+ 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);
986
+ (_d = transport.notifySubscription) === null || _d === void 0 ? void 0 : _d.remove();
967
987
  transport.notifySubscription = undefined;
968
988
  if (transport.notifyTransactionId) {
969
989
  try {
970
- yield ((_f = this.blePlxManager) === null || _f === void 0 ? void 0 : _f.cancelTransaction(transport.notifyTransactionId));
990
+ yield ((_e = this.blePlxManager) === null || _e === void 0 ? void 0 : _e.cancelTransaction(transport.notifyTransactionId));
971
991
  }
972
992
  catch (e) {
973
993
  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 +996,11 @@ class ReactNativeBleTransport {
976
996
  delete transportCache[uuid];
977
997
  }
978
998
  this.deviceProtocol.delete(uuid);
979
- this.deviceProtocolHints.delete(uuid);
980
- (_g = this.protocolV2Assemblers.get(uuid)) === null || _g === void 0 ? void 0 : _g.reset();
999
+ (_f = this.protocolV2Assemblers.get(uuid)) === null || _f === void 0 ? void 0 : _f.reset();
981
1000
  this.protocolV2Assemblers.delete(uuid);
982
1001
  this.resetProtocolV2Frames(uuid);
983
1002
  try {
984
- yield ((_h = this.blePlxManager) === null || _h === void 0 ? void 0 : _h.cancelTransaction(uuid));
1003
+ yield ((_g = this.blePlxManager) === null || _g === void 0 ? void 0 : _g.cancelTransaction(uuid));
985
1004
  }
986
1005
  catch (e) {
987
1006
  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 +1021,18 @@ class ReactNativeBleTransport {
1002
1021
  if (this._messages == null) {
1003
1022
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
1004
1023
  }
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
1024
  const protocol = this.getProtocolType(uuid);
1011
1025
  if (!protocol) {
1012
1026
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${uuid}`);
1013
1027
  }
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
- }
1028
+ Log === null || Log === void 0 ? void 0 : Log.debug('transport call', transport.createTransportCallLog(name, protocol, data));
1026
1029
  if (protocol === 'V2') {
1027
1030
  return this.callProtocolV2(uuid, name, data, options);
1028
1031
  }
1032
+ const forceRun = name === 'Initialize' || name === 'Cancel';
1033
+ if (this.runPromise && !forceRun) {
1034
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportCallInProgress);
1035
+ }
1029
1036
  return this.callProtocolV1(uuid, name, data, options);
1030
1037
  });
1031
1038
  }
@@ -1038,6 +1045,7 @@ class ReactNativeBleTransport {
1038
1045
  const runPromise = hdShared.createDeferred();
1039
1046
  runPromise.promise.catch(() => undefined);
1040
1047
  this.runPromise = runPromise;
1048
+ this.runPromiseDeviceId = uuid;
1041
1049
  const messages = this._messages;
1042
1050
  const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
1043
1051
  let timeout;
@@ -1101,7 +1109,7 @@ class ReactNativeBleTransport {
1101
1109
  });
1102
1110
  }
1103
1111
  else if (name === 'FirmwareUpload') {
1104
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] FirmwareUpload write uses throttled BLE packets:', {
1112
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Firmware upload transport configured', {
1105
1113
  packetCapacity: FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY,
1106
1114
  burstSize: FIRMWARE_UPLOAD_WRITE_BURST_SIZE,
1107
1115
  pauseMs: FIRMWARE_UPLOAD_WRITE_PAUSE_MS,
@@ -1120,32 +1128,14 @@ class ReactNativeBleTransport {
1120
1128
  if (!retryType || attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
1121
1129
  throw error;
1122
1130
  }
1123
- const shouldReconnect = retryType === 'reconnectable';
1124
- const delayMs = shouldReconnect
1125
- ? FIRMWARE_UPLOAD_RECONNECT_RETRY_DELAY_MS
1126
- : resolveFirmwareUploadRetryDelay(attempt);
1131
+ const delayMs = resolveFirmwareUploadRetryDelay(attempt);
1127
1132
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] FirmwareUpload write retry:', {
1128
1133
  attempt: attempt + 1,
1129
1134
  delayMs,
1130
- reconnect: shouldReconnect,
1131
1135
  error,
1132
1136
  });
1133
- if (shouldReconnect) {
1134
- this.firmwareUploadWriteRecoveryIds.add(uuid);
1135
- }
1136
1137
  yield delay(delayMs);
1137
1138
  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
1139
  }
1150
1140
  }
1151
1141
  }), e => {
@@ -1157,7 +1147,13 @@ class ReactNativeBleTransport {
1157
1147
  for (const o of buffers) {
1158
1148
  const outData = o.toString('base64');
1159
1149
  try {
1160
- yield transport.writeCharacteristic.writeWithoutResponse(outData);
1150
+ const shouldUseWriteWithResponse = reactNative.Platform.OS === 'ios' && transport.writeCharacteristic.isWritableWithResponse;
1151
+ if (shouldUseWriteWithResponse) {
1152
+ yield transport.writeCharacteristic.writeWithResponse(outData);
1153
+ }
1154
+ else {
1155
+ yield transport.writeCharacteristic.writeWithoutResponse(outData);
1156
+ }
1161
1157
  }
1162
1158
  catch (e) {
1163
1159
  Log === null || Log === void 0 ? void 0 : Log.debug('writeCharacteristic write error: ', e);
@@ -1190,17 +1186,21 @@ class ReactNativeBleTransport {
1190
1186
  if (typeof response !== 'string') {
1191
1187
  throw new Error('Returning data is not string.');
1192
1188
  }
1193
- Log === null || Log === void 0 ? void 0 : Log.debug('receive data: ', response);
1194
1189
  const jsonData = ProtocolV1.decodeMessage(messages, response);
1195
1190
  return check.call(jsonData);
1196
1191
  }
1197
1192
  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);
1193
+ if (name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS) {
1194
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe call failed:', e);
1200
1195
  }
1201
1196
  else {
1202
1197
  Log === null || Log === void 0 ? void 0 : Log.error('call error: ', e);
1203
1198
  }
1199
+ const isProbeTimeout = name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS;
1200
+ if (!isProbeTimeout &&
1201
+ (e === null || e === void 0 ? void 0 : e.errorCode) === hdShared.HardwareErrorCode.BleTimeoutError) {
1202
+ yield this.disconnect(uuid);
1203
+ }
1204
1204
  throw e;
1205
1205
  }
1206
1206
  finally {
@@ -1208,6 +1208,7 @@ class ReactNativeBleTransport {
1208
1208
  clearTimeout(timeout);
1209
1209
  if (this.runPromise === runPromise) {
1210
1210
  this.runPromise = null;
1211
+ this.runPromiseDeviceId = null;
1211
1212
  }
1212
1213
  }
1213
1214
  });
@@ -1216,10 +1217,11 @@ class ReactNativeBleTransport {
1216
1217
  this.stopped = true;
1217
1218
  }
1218
1219
  disconnect(session) {
1219
- var _a, _b, _c, _d, _e, _f;
1220
+ var _a, _b, _c, _d, _e;
1220
1221
  return __awaiter(this, void 0, void 0, function* () {
1221
- Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native transport resetSession: ', session);
1222
+ yield this.protocolV2Links.invalidateLink(session, 'React Native BLE transport disconnected');
1222
1223
  const transport = transportCache[session];
1224
+ const monitorToken = (_a = transport === null || transport === void 0 ? void 0 : transport.monitorToken) !== null && _a !== void 0 ? _a : this.monitorTokens.get(session);
1223
1225
  if (transport === null || transport === void 0 ? void 0 : transport.disconnectSubscription) {
1224
1226
  try {
1225
1227
  Log === null || Log === void 0 ? void 0 : Log.debug('disconnect: removing disconnect subscription');
@@ -1232,7 +1234,7 @@ class ReactNativeBleTransport {
1232
1234
  }
1233
1235
  if (transport === null || transport === void 0 ? void 0 : transport.notifySubscription) {
1234
1236
  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);
1237
+ 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
1238
  transport.notifySubscription.remove();
1237
1239
  transport.notifySubscription = undefined;
1238
1240
  }
@@ -1242,7 +1244,7 @@ class ReactNativeBleTransport {
1242
1244
  }
1243
1245
  if (session) {
1244
1246
  try {
1245
- yield ((_b = this.blePlxManager) === null || _b === void 0 ? void 0 : _b.cancelTransaction(session));
1247
+ yield ((_c = this.blePlxManager) === null || _c === void 0 ? void 0 : _c.cancelTransaction(session));
1246
1248
  }
1247
1249
  catch (e) {
1248
1250
  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 +1259,7 @@ class ReactNativeBleTransport {
1257
1259
  }
1258
1260
  }
1259
1261
  try {
1260
- yield ((_c = this.blePlxManager) === null || _c === void 0 ? void 0 : _c.cancelDeviceConnection(session));
1262
+ yield ((_d = this.blePlxManager) === null || _d === void 0 ? void 0 : _d.cancelDeviceConnection(session));
1261
1263
  }
1262
1264
  catch (e) {
1263
1265
  Log === null || Log === void 0 ? void 0 : Log.debug('resetSession: manager.cancelDeviceConnection error (ignored): ', (e === null || e === void 0 ? void 0 : e.message) || e);
@@ -1269,19 +1271,15 @@ class ReactNativeBleTransport {
1269
1271
  this.deviceProtocolHints.delete(session);
1270
1272
  this.protocolV2Assemblers.delete(session);
1271
1273
  this.resetProtocolV2Frames(session);
1272
- if (((_d = this.activeProtocolV2Call) === null || _d === void 0 ? void 0 : _d.uuid) === session) {
1273
- this.activeProtocolV2Call = null;
1274
- }
1275
1274
  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
- });
1275
+ this.emitDeviceDisconnect(session, (_e = transport === null || transport === void 0 ? void 0 : transport.device) === null || _e === void 0 ? void 0 : _e.name, monitorToken);
1281
1276
  }
1282
1277
  catch (e) {
1283
1278
  Log === null || Log === void 0 ? void 0 : Log.error('resetSession: emit disconnect event error: ', e);
1284
1279
  }
1280
+ if (monitorToken !== undefined && this.monitorTokens.get(session) === monitorToken) {
1281
+ this.monitorTokens.delete(session);
1282
+ }
1285
1283
  yield new Promise(resolve => setTimeout(() => resolve(), 100));
1286
1284
  });
1287
1285
  }
@@ -1289,6 +1287,7 @@ class ReactNativeBleTransport {
1289
1287
  Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native transport cancel');
1290
1288
  if (this.runPromise) ;
1291
1289
  this.runPromise = null;
1290
+ this.runPromiseDeviceId = null;
1292
1291
  }
1293
1292
  getCachedTransport(uuid) {
1294
1293
  const transport = transportCache[uuid];
@@ -1301,38 +1300,77 @@ class ReactNativeBleTransport {
1301
1300
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
1302
1301
  }
1303
1302
  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');
1303
+ 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
1304
  }
1306
1305
  clearProbeProtocol(uuid, protocol) {
1307
1306
  if (this.deviceProtocol.get(uuid) === protocol) {
1308
1307
  this.deviceProtocol.delete(uuid);
1309
1308
  }
1310
1309
  }
1311
- detectProtocol(uuid, expectedProtocol, protocolHint) {
1310
+ detectProtocol(uuid, expectedProtocol, protocolHint, rebuildTransport) {
1311
+ var _a;
1312
1312
  return __awaiter(this, void 0, void 0, function* () {
1313
+ if (reactNative.Platform.OS === 'ios') {
1314
+ const protocol = (_a = expectedProtocol !== null && expectedProtocol !== void 0 ? expectedProtocol : protocolHint) !== null && _a !== void 0 ? _a : 'V1';
1315
+ let source = 'ios-legacy-default';
1316
+ if (expectedProtocol) {
1317
+ source = 'expected';
1318
+ }
1319
+ else if (protocolHint) {
1320
+ source = 'hint';
1321
+ }
1322
+ this.deviceProtocol.set(uuid, protocol);
1323
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol selected', {
1324
+ deviceId: uuid,
1325
+ protocol,
1326
+ source,
1327
+ });
1328
+ return protocol;
1329
+ }
1313
1330
  if (expectedProtocol === 'V1') {
1314
1331
  if (yield this.probeProtocolV1(uuid)) {
1315
1332
  this.deviceProtocol.set(uuid, 'V1');
1316
- Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> V1 (expected)`);
1333
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1334
+ deviceId: uuid,
1335
+ protocol: 'V1',
1336
+ source: 'expected',
1337
+ });
1317
1338
  return 'V1';
1318
1339
  }
1319
1340
  throw this.createProtocolMismatchError(expectedProtocol);
1320
1341
  }
1321
1342
  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';
1343
+ if (yield this.probeProtocolV2(uuid)) {
1344
+ this.deviceProtocol.set(uuid, 'V2');
1345
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1346
+ deviceId: uuid,
1347
+ protocol: 'V2',
1348
+ source: 'expected',
1349
+ });
1350
+ return 'V2';
1351
+ }
1352
+ throw this.createProtocolMismatchError(expectedProtocol);
1325
1353
  }
1326
1354
  const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
1327
1355
  for (let i = 0; i < probeOrder.length; i += 1) {
1328
1356
  const protocol = probeOrder[i];
1329
1357
  if (i > 0) {
1330
1358
  yield this.resetProbeStateAfterProtocolProbe(uuid, probeOrder[i - 1]);
1359
+ if (!transportCache[uuid]) {
1360
+ if (!rebuildTransport) {
1361
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
1362
+ }
1363
+ yield rebuildTransport();
1364
+ }
1331
1365
  }
1332
1366
  const detected = protocol === 'V1' ? yield this.probeProtocolV1(uuid) : yield this.probeProtocolV2(uuid);
1333
1367
  if (detected) {
1334
1368
  this.deviceProtocol.set(uuid, protocol);
1335
- Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> ${protocol}`);
1369
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1370
+ deviceId: uuid,
1371
+ protocol,
1372
+ source: 'probe',
1373
+ });
1336
1374
  return protocol;
1337
1375
  }
1338
1376
  }
@@ -1341,14 +1379,12 @@ class ReactNativeBleTransport {
1341
1379
  });
1342
1380
  }
1343
1381
  resetProbeStateAfterProtocolProbe(uuid, protocol) {
1344
- var _a, _b, _c, _d;
1382
+ var _a, _b, _c;
1345
1383
  return __awaiter(this, void 0, void 0, function* () {
1346
1384
  const transport = transportCache[uuid];
1385
+ yield this.protocolV2Links.invalidateLink(uuid, `Reset notify state after Protocol ${protocol} probe`);
1347
1386
  (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1348
1387
  this.resetProtocolV2Frames(uuid);
1349
- if (((_b = this.activeProtocolV2Call) === null || _b === void 0 ? void 0 : _b.uuid) === uuid) {
1350
- this.activeProtocolV2Call = null;
1351
- }
1352
1388
  if (this.runPromise) {
1353
1389
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
1354
1390
  this.runPromise.reject(error);
@@ -1360,11 +1396,11 @@ class ReactNativeBleTransport {
1360
1396
  if (this.monitorTokens.get(uuid) === transport.monitorToken) {
1361
1397
  this.monitorTokens.delete(uuid);
1362
1398
  }
1363
- (_c = transport.notifySubscription) === null || _c === void 0 ? void 0 : _c.remove();
1399
+ (_b = transport.notifySubscription) === null || _b === void 0 ? void 0 : _b.remove();
1364
1400
  transport.notifySubscription = undefined;
1365
1401
  if (previousNotifyTransactionId) {
1366
1402
  try {
1367
- yield ((_d = this.blePlxManager) === null || _d === void 0 ? void 0 : _d.cancelTransaction(previousNotifyTransactionId));
1403
+ yield ((_c = this.blePlxManager) === null || _c === void 0 ? void 0 : _c.cancelTransaction(previousNotifyTransactionId));
1368
1404
  }
1369
1405
  catch (error) {
1370
1406
  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);
@@ -1391,12 +1427,12 @@ class ReactNativeBleTransport {
1391
1427
  }
1392
1428
  try {
1393
1429
  this.deviceProtocol.set(uuid, 'V1');
1394
- yield this.callProtocolV1(uuid, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
1430
+ yield this.callProtocolV1(uuid, 'GetFeatures', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
1395
1431
  return true;
1396
1432
  }
1397
1433
  catch (error) {
1398
1434
  this.clearProbeProtocol(uuid, 'V1');
1399
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 Initialize probe failed:', error);
1435
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe failed:', error);
1400
1436
  return false;
1401
1437
  }
1402
1438
  });
@@ -1426,14 +1462,10 @@ class ReactNativeBleTransport {
1426
1462
  return detected;
1427
1463
  });
1428
1464
  }
1429
- handleProtocolV2Notification(uuid, data) {
1430
- var _a, _b, _c;
1465
+ handleProtocolV2Notification(uuid, monitorToken, data) {
1431
1466
  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);
1467
+ if (this.monitorTokens.get(uuid) !== monitorToken)
1435
1468
  return;
1436
- }
1437
1469
  if (data.length === 0)
1438
1470
  return;
1439
1471
  const assembler = this.protocolV2Assemblers.get(uuid);
@@ -1446,8 +1478,10 @@ class ReactNativeBleTransport {
1446
1478
  catch (error) {
1447
1479
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 notification error:', error);
1448
1480
  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);
1481
+ this.rejectProtocolV2Frames(uuid, notifyError);
1482
+ this.protocolV2Links
1483
+ .invalidateLink(uuid, `Protocol V2 notification error: ${error}`)
1484
+ .catch(invalidateError => Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 notify cleanup failed:', invalidateError));
1451
1485
  }
1452
1486
  }
1453
1487
  getProtocolV2FrameQueue(uuid) {
@@ -1467,20 +1501,16 @@ class ReactNativeBleTransport {
1467
1501
  }
1468
1502
  this.getProtocolV2FrameQueue(uuid).push(frame);
1469
1503
  }
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
1504
  resetProtocolV2Frames(uuid) {
1478
- this.protocolV2FrameQueues.delete(uuid);
1479
- this.protocolV2FramePromises.delete(uuid);
1505
+ this.rejectProtocolV2Frames(uuid, new Error(`Protocol V2 frame state reset for ${uuid}`));
1480
1506
  }
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;
1507
+ rejectProtocolV2Frames(uuid, error) {
1508
+ this.protocolV2FrameQueues.delete(uuid);
1509
+ const framePromise = this.protocolV2FramePromises.get(uuid);
1510
+ if (framePromise) {
1511
+ this.protocolV2FramePromises.delete(uuid);
1512
+ framePromise.reject(error);
1513
+ }
1484
1514
  }
1485
1515
  readProtocolV2Frame(uuid) {
1486
1516
  return __awaiter(this, void 0, void 0, function* () {
@@ -1500,127 +1530,136 @@ class ReactNativeBleTransport {
1500
1530
  }
1501
1531
  });
1502
1532
  }
1503
- writeProtocolV2Frame(transport, frame, options) {
1533
+ writeProtocolV2Packet(transport, base64, context, assertCurrentGeneration) {
1534
+ return __awaiter(this, void 0, void 0, function* () {
1535
+ const shouldUseWriteWithResponse = reactNative.Platform.OS === 'ios' &&
1536
+ !context.highVolume &&
1537
+ transport.writeCharacteristic.isWritableWithResponse;
1538
+ let attempt = 0;
1539
+ for (;;) {
1540
+ assertCurrentGeneration();
1541
+ if (context.signal.aborted) {
1542
+ throw new Error(`Protocol V2 BLE write aborted for ${context.messageName}`);
1543
+ }
1544
+ try {
1545
+ if (shouldUseWriteWithResponse) {
1546
+ yield transport.writeCharacteristic.writeWithResponse(base64);
1547
+ }
1548
+ else {
1549
+ yield transport.writeCharacteristic.writeWithoutResponse(base64);
1550
+ }
1551
+ assertCurrentGeneration();
1552
+ return;
1553
+ }
1554
+ catch (error) {
1555
+ if (getFirmwareUploadWriteRetryType(error) !== 'congested' ||
1556
+ attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
1557
+ throw error;
1558
+ }
1559
+ const delayMs = resolveFirmwareUploadRetryDelay(attempt);
1560
+ attempt += 1;
1561
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 congested write retry:', {
1562
+ name: context.messageName,
1563
+ attempt,
1564
+ delayMs,
1565
+ });
1566
+ yield delay(delayMs);
1567
+ }
1568
+ }
1569
+ });
1570
+ }
1571
+ writeProtocolV2Frame(transport$1, frame, context, assertCurrentGeneration) {
1504
1572
  return __awaiter(this, void 0, void 0, function* () {
1505
1573
  const tuning = getProtocolV2BleTuning();
1506
1574
  const packetCapacity = resolveProtocolV2PacketCapacity({
1507
1575
  platform: reactNative.Platform.OS,
1508
1576
  iosPacketLength: tuning.iosPacketLength,
1509
1577
  androidPacketLength: tuning.androidPacketLength,
1510
- mtu: reactNative.Platform.OS === 'android' ? transport.mtuSize : undefined,
1578
+ mtu: reactNative.Platform.OS === 'android' ? transport$1.mtuSize : undefined,
1579
+ });
1580
+ const initialDelayMs = reactNative.Platform.OS === 'ios' && !context.highVolume && frame.length <= packetCapacity
1581
+ ? IOS_PROTOCOL_V2_CONTROL_WRITE_DELAY_MS
1582
+ : 0;
1583
+ yield transport.writeProtocolV2BleFrame({
1584
+ frame,
1585
+ packetCapacity,
1586
+ assertActive: assertCurrentGeneration,
1587
+ signal: context.signal,
1588
+ abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
1589
+ initialDelayMs,
1590
+ burstSize: FIRMWARE_UPLOAD_WRITE_BURST_SIZE,
1591
+ burstPauseMs: FIRMWARE_UPLOAD_WRITE_PAUSE_MS,
1592
+ flushDelayMs: FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS,
1593
+ wait: delay,
1594
+ writePacket: packet => this.writeProtocolV2Packet(transport$1, buffer.Buffer.from(packet).toString('base64'), context, assertCurrentGeneration),
1511
1595
  });
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
1596
  });
1536
1597
  }
1537
1598
  callProtocolV2(uuid, name, data, options) {
1538
- var _a, _b, _c, _d;
1539
1599
  return __awaiter(this, void 0, void 0, function* () {
1540
1600
  if (!this._messages || !this._messagesV2) {
1541
1601
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
1542
1602
  }
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 });
1603
+ const callOptions = options;
1564
1604
  const highVolumeWrite = transport.LogBlockCommand.has(name);
1565
1605
  if (highVolumeWrite) {
1566
1606
  const tuning = getProtocolV2BleTuning();
1567
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume write uses throttled writeWithoutResponse:', name, {
1607
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume write configured', {
1608
+ name,
1609
+ writeMode: 'withoutResponse',
1568
1610
  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
1611
  });
1574
1612
  }
1575
1613
  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;
1614
+ return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, callOptions);
1601
1615
  }
1602
1616
  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
1617
  Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] Protocol V2 call error:', e);
1608
1618
  throw e;
1609
1619
  }
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
1620
  });
1623
1621
  }
1622
+ createProtocolV2Adapter(uuid) {
1623
+ var _a;
1624
+ const generation = (_a = this.monitorTokens.get(uuid)) !== null && _a !== void 0 ? _a : 0;
1625
+ const assertCurrentGeneration = () => {
1626
+ if (this.monitorTokens.get(uuid) !== generation) {
1627
+ throw new Error(`Protocol V2 monitor generation changed for ${uuid}`);
1628
+ }
1629
+ };
1630
+ return {
1631
+ router: transport.PROTOCOL_V2_CHANNEL_BLE_UART,
1632
+ maxFrameBytes: transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES,
1633
+ generation,
1634
+ prepareCall: () => {
1635
+ var _a;
1636
+ assertCurrentGeneration();
1637
+ (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1638
+ this.resetProtocolV2Frames(uuid);
1639
+ },
1640
+ writeFrame: (frame, context) => __awaiter(this, void 0, void 0, function* () {
1641
+ assertCurrentGeneration();
1642
+ const currentTransport = this.getCachedTransport(uuid);
1643
+ yield this.writeProtocolV2Frame(currentTransport, frame, context, assertCurrentGeneration);
1644
+ }),
1645
+ readFrame: () => __awaiter(this, void 0, void 0, function* () {
1646
+ assertCurrentGeneration();
1647
+ const rxFrame = yield this.readProtocolV2Frame(uuid);
1648
+ if (!(rxFrame instanceof Uint8Array)) {
1649
+ throw new Error('Protocol V2 response is not Uint8Array');
1650
+ }
1651
+ return rxFrame;
1652
+ }),
1653
+ reset: (reason) => {
1654
+ var _a;
1655
+ (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1656
+ this.rejectProtocolV2Frames(uuid, new Error(reason));
1657
+ },
1658
+ logger: Log,
1659
+ logPrefix: 'ProtocolV2 RN-BLE',
1660
+ createTimeoutError: (messageName, timeout) => hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, `BLE response timeout after ${timeout}ms for ${messageName}`),
1661
+ };
1662
+ }
1624
1663
  getProtocolType(path) {
1625
1664
  return this.deviceProtocol.get(path);
1626
1665
  }
@@ -1628,5 +1667,6 @@ class ReactNativeBleTransport {
1628
1667
 
1629
1668
  exports.configureProtocolV2BleTuning = configureProtocolV2BleTuning;
1630
1669
  exports["default"] = ReactNativeBleTransport;
1670
+ exports.getFirmwareUploadWriteRetryType = getFirmwareUploadWriteRetryType;
1631
1671
  exports.getProtocolV2BleTuning = getProtocolV2BleTuning;
1632
1672
  exports.resetProtocolV2BleTuning = resetProtocolV2BleTuning;