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

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