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

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