@onekeyfe/hd-transport-react-native 1.2.0-alpha.3 → 1.2.0-alpha.30

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
@@ -95,6 +95,7 @@ const IOS_PACKET_LENGTH = 128;
95
95
  const ANDROID_PACKET_LENGTH = 192;
96
96
  const ANDROID_DEFAULT_MTU = 23;
97
97
  const ClassicServiceUUID = '00000001-0000-1000-8000-00805f9b34fb';
98
+ const Pro2ServiceUUID = 'fffd';
98
99
  const OneKeyServices = {
99
100
  classic: {
100
101
  [ClassicServiceUUID]: {
@@ -102,6 +103,11 @@ const OneKeyServices = {
102
103
  writeUuid: '00000002-0000-1000-8000-00805f9b34fb',
103
104
  notifyUuid: '00000003-0000-1000-8000-00805f9b34fb',
104
105
  },
106
+ [Pro2ServiceUUID]: {
107
+ serviceUuid: Pro2ServiceUUID,
108
+ writeUuid: '00000002-0000-1000-8000-00805f9b34fb',
109
+ notifyUuid: '00000003-0000-1000-8000-00805f9b34fb',
110
+ },
105
111
  },
106
112
  };
107
113
  const bluetoothServices = [];
@@ -117,7 +123,8 @@ const getInfosForServiceUuid = (serviceUuid, deviceType) => {
117
123
  return null;
118
124
  }
119
125
  const normalizedServiceUuid = normalizeBleUuid(serviceUuid);
120
- const service = (_a = services[serviceUuid]) !== null && _a !== void 0 ? _a : Object.values(services).find(item => normalizeBleUuid(item.serviceUuid) === normalizedServiceUuid);
126
+ const service = (_a = services[serviceUuid]) !== null && _a !== void 0 ? _a : Object.values(services).find(item => normalizeBleUuid(item.serviceUuid) === normalizedServiceUuid ||
127
+ getBleUuidKey(item.serviceUuid) === getBleUuidKey(serviceUuid));
121
128
  if (!service) {
122
129
  return null;
123
130
  }
@@ -138,21 +145,6 @@ const isSameBleUuid = (left, right) => {
138
145
  function hasWritableCapability(characteristic) {
139
146
  return !!(characteristic.isWritableWithResponse || characteristic.isWritableWithoutResponse);
140
147
  }
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
148
  function resolveProtocolV2PacketCapacity({ platform, iosPacketLength = IOS_PACKET_LENGTH, androidPacketLength = ANDROID_PACKET_LENGTH, mtu, }) {
157
149
  if (platform === 'ios') {
158
150
  return iosPacketLength;
@@ -189,7 +181,6 @@ const timer = process.env.NODE_ENV === 'development'
189
181
  const subscribeBleOn = (bleManager, ms = 1000) => new Promise((resolve, reject) => {
190
182
  let done = false;
191
183
  const subscription = bleManager.onStateChange(state => {
192
- console.log('ble state -> ', state);
193
184
  if (state === 'PoweredOn') {
194
185
  if (done)
195
186
  return;
@@ -261,6 +252,18 @@ class BleTransport {
261
252
  BleTransport.MAX_RETRIES = 5;
262
253
  BleTransport.RETRY_DELAY = 2000;
263
254
 
255
+ function createTransportCallLog(name, protocol, data) {
256
+ if (name === 'ResourceUpdate' || name === 'ResourceAck') {
257
+ return {
258
+ name,
259
+ protocol,
260
+ file_name: data.file_name,
261
+ hash: data.hash,
262
+ };
263
+ }
264
+ return { name, protocol };
265
+ }
266
+
264
267
  const { check, ProtocolV1, parseConfigure } = transport__default["default"];
265
268
  const Log = bleLogger;
266
269
  const transportCache = {};
@@ -293,22 +296,14 @@ const getFirmwareUploadWriteRetryType = (error) => {
293
296
  return /GATT_CONGESTED|status\s*[:=]?\s*143/.test(text) ? 'congested' : null;
294
297
  };
295
298
  const resolveFirmwareUploadRetryDelay = (attempt, baseDelayMs = 200, maxDelayMs = 1200) => Math.min(baseDelayMs * Math.pow(2, attempt), maxDelayMs);
296
- const BLE_RESPONSE_TIMEOUT_MS = 30000;
297
299
  const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
298
300
  const PROTOCOL_V2_PROBE_TIMEOUT_MS = 10000;
299
- const DEVICE_SCAN_TIMEOUT_MS = 8000;
301
+ const DEVICE_SCAN_TIMEOUT_MS = 3000;
300
302
  const IOS_NOTIFY_READY_DELAY_MS = 150;
301
303
  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
304
  const DEFAULT_PROTOCOL_V2_BLE_TUNING = {
306
305
  iosPacketLength: IOS_PACKET_LENGTH,
307
306
  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
307
  };
313
308
  let protocolV2BleTuning = Object.assign({}, DEFAULT_PROTOCOL_V2_BLE_TUNING);
314
309
  const normalizePositiveInteger = (value, fallback) => {
@@ -318,20 +313,15 @@ const normalizePositiveInteger = (value, fallback) => {
318
313
  return Math.floor(normalized);
319
314
  };
320
315
  function configureProtocolV2BleTuning(tuning = {}) {
321
- var _a;
322
316
  protocolV2BleTuning = {
323
317
  iosPacketLength: normalizePositiveInteger(tuning.iosPacketLength, protocolV2BleTuning.iosPacketLength),
324
318
  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
319
  };
330
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 BLE tuning configured:', protocolV2BleTuning);
320
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE tuning configured', protocolV2BleTuning);
331
321
  }
332
322
  function resetProtocolV2BleTuning() {
333
323
  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);
324
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE tuning reset', protocolV2BleTuning);
335
325
  }
336
326
  function getProtocolV2BleTuning() {
337
327
  return Object.assign({}, protocolV2BleTuning);
@@ -371,9 +361,10 @@ const requestAndroidMtu = (device) => __awaiter(void 0, void 0, void 0, function
371
361
  return device;
372
362
  try {
373
363
  const mtuDevice = yield device.requestMTU(ANDROID_REQUEST_MTU);
374
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android MTU requested:', {
364
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] MTU configured', {
365
+ deviceId: device.id,
375
366
  requested: ANDROID_REQUEST_MTU,
376
- mtu: mtuDevice.mtu,
367
+ actual: mtuDevice.mtu,
377
368
  });
378
369
  return mtuDevice;
379
370
  }
@@ -414,9 +405,29 @@ class ReactNativeBleTransport {
414
405
  this.protocolV2Assemblers = new Map();
415
406
  this.protocolV2FrameQueues = new Map();
416
407
  this.protocolV2FramePromises = new Map();
417
- this.activeProtocolV2Call = null;
418
- this.nextProtocolV2CallToken = 1;
408
+ this.protocolV2Links = new transport.ProtocolV2LinkManager({
409
+ getSchemas: () => {
410
+ if (!this._messages || !this._messagesV2) {
411
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
412
+ }
413
+ return {
414
+ protocolV1: this._messages,
415
+ protocolV2: this._messagesV2,
416
+ };
417
+ },
418
+ classifyError: () => 'link-fatal',
419
+ onLinkInvalidated: (uuid, reason) => __awaiter(this, void 0, void 0, function* () {
420
+ var _b;
421
+ (_b = this.protocolV2Assemblers.get(uuid)) === null || _b === void 0 ? void 0 : _b.reset();
422
+ this.rejectProtocolV2Frames(uuid, new Error(reason));
423
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 link invalidated:', uuid, reason);
424
+ if (reason.startsWith('Protocol V2 link-fatal error:')) {
425
+ yield this.release(uuid, true);
426
+ }
427
+ }),
428
+ });
419
429
  this.monitorTokens = new Map();
430
+ this.disconnectEventTokens = new Map();
420
431
  this.nextMonitorToken = 1;
421
432
  this.scanTimeout = (_a = options.scanTimeout) !== null && _a !== void 0 ? _a : DEVICE_SCAN_TIMEOUT_MS;
422
433
  }
@@ -430,8 +441,18 @@ class ReactNativeBleTransport {
430
441
  this._messages = messages;
431
442
  }
432
443
  configureProtocolV2(signedData) {
444
+ const configuration = typeof signedData === 'string' ? signedData : JSON.stringify(signedData);
445
+ if (this.protocolV2SchemaConfiguration === configuration) {
446
+ return;
447
+ }
448
+ const isReconfiguration = this.protocolV2SchemaConfiguration !== undefined;
433
449
  this._messagesV2 = parseConfigure(signedData);
434
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 schema configured');
450
+ this.protocolV2SchemaConfiguration = configuration;
451
+ if (isReconfiguration) {
452
+ this.protocolV2Links
453
+ .invalidateAllLinks('Protocol V2 schema reconfigured')
454
+ .catch(error => Log === null || Log === void 0 ? void 0 : Log.debug('Protocol V2 schema link cleanup failed:', error));
455
+ }
435
456
  }
436
457
  listen() {
437
458
  }
@@ -524,8 +545,8 @@ class ReactNativeBleTransport {
524
545
  attachDisconnectSubscription(transport, device, uuid) {
525
546
  var _a;
526
547
  (_a = transport.disconnectSubscription) === null || _a === void 0 ? void 0 : _a.remove();
548
+ const { monitorToken } = transport;
527
549
  transport.disconnectSubscription = device.onDisconnected(() => {
528
- var _a;
529
550
  if (this.firmwareUploadWriteRecoveryIds.has(uuid)) {
530
551
  Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored during FirmwareUpload write recovery: ', uuid);
531
552
  return;
@@ -534,13 +555,13 @@ class ReactNativeBleTransport {
534
555
  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
556
  return;
536
557
  }
558
+ if (this.monitorTokens.get(uuid) !== monitorToken) {
559
+ Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored for stale generation: ', device === null || device === void 0 ? void 0 : device.id);
560
+ return;
561
+ }
537
562
  try {
538
563
  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
- });
564
+ this.emitDeviceDisconnect(uuid, device === null || device === void 0 ? void 0 : device.name, monitorToken);
544
565
  if (this.runPromise) {
545
566
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError);
546
567
  this.runPromise.reject(error);
@@ -555,6 +576,22 @@ class ReactNativeBleTransport {
555
576
  }
556
577
  });
557
578
  }
579
+ emitDeviceDisconnect(uuid, name, token) {
580
+ var _a;
581
+ if (token === undefined || this.disconnectEventTokens.get(uuid) === token) {
582
+ return;
583
+ }
584
+ if (this.monitorTokens.get(uuid) !== token) {
585
+ Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect event ignored for stale generation: ', uuid);
586
+ return;
587
+ }
588
+ this.disconnectEventTokens.set(uuid, token);
589
+ (_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit(transport.TRANSPORT_EVENT.DEVICE_DISCONNECT, {
590
+ name,
591
+ id: uuid,
592
+ connectId: uuid,
593
+ });
594
+ }
558
595
  reconnectFirmwareUploadTransport(uuid, transport) {
559
596
  var _a, _b;
560
597
  return __awaiter(this, void 0, void 0, function* () {
@@ -624,13 +661,12 @@ class ReactNativeBleTransport {
624
661
  return;
625
662
  }
626
663
  }
627
- blePlxManager.startDeviceScan(null, {
664
+ blePlxManager.startDeviceScan(getBluetoothServiceUuids(), {
628
665
  allowDuplicates: true,
629
666
  scanMode: reactNativeBlePlx.ScanMode.LowLatency,
630
667
  }, (error, device) => {
631
668
  var _a, _b, _c;
632
669
  if (error) {
633
- Log === null || Log === void 0 ? void 0 : Log.debug('ble scan manager: ', blePlxManager);
634
670
  Log === null || Log === void 0 ? void 0 : Log.debug('ble scan error: ', error);
635
671
  if ([reactNativeBlePlx.BleErrorCode.BluetoothPoweredOff, reactNativeBlePlx.BleErrorCode.BluetoothInUnknownState].includes(error.errorCode)) {
636
672
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePermissionError));
@@ -653,22 +689,8 @@ class ReactNativeBleTransport {
653
689
  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
690
  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
691
  hasKnownOneKeyService(device);
656
- const shouldTraceCandidate = !!displayName && /onekey|bixinkey|pro\s*2|pro\b|touch|^k\d|^t\d/i.test(displayName);
657
- if (shouldTraceCandidate) {
658
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] scan candidate', {
659
- name: device === null || device === void 0 ? void 0 : device.name,
660
- localName: device === null || device === void 0 ? void 0 : device.localName,
661
- id: device === null || device === void 0 ? void 0 : device.id,
662
- serviceUUIDs: device === null || device === void 0 ? void 0 : device.serviceUUIDs,
663
- accepted: isOneKey,
664
- });
665
- }
666
692
  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
693
  addDevice(device);
671
- Log === null || Log === void 0 ? void 0 : Log.debug('search device end ======================\n');
672
694
  }
673
695
  else if (displayName && /\bpro\s*2\b/i.test(displayName)) {
674
696
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Pro2-like BLE device was not accepted:', {
@@ -679,10 +701,15 @@ class ReactNativeBleTransport {
679
701
  });
680
702
  }
681
703
  });
682
- getConnectedDeviceIds(getBluetoothServiceUuids()).then(devices => {
704
+ getConnectedDeviceIds(reactNative.Platform.OS === 'ios' ? getBluetoothServiceUuids() : []).then(devices => {
683
705
  for (const device of devices) {
684
- Log === null || Log === void 0 ? void 0 : Log.debug('search connected peripheral: ', device.id);
685
- addDevice(device);
706
+ const { serviceUUIDs } = device;
707
+ const hasCachedServiceUuid = Boolean(serviceUUIDs === null || serviceUUIDs === void 0 ? void 0 : serviceUUIDs.length);
708
+ const keepDevice = reactNative.Platform.OS === 'ios' || hasCachedServiceUuid;
709
+ if (keepDevice) {
710
+ Log === null || Log === void 0 ? void 0 : Log.debug('search connected peripheral: ', device.id);
711
+ addDevice(device);
712
+ }
686
713
  }
687
714
  });
688
715
  const addDevice = (device) => {
@@ -694,6 +721,12 @@ class ReactNativeBleTransport {
694
721
  this.deviceProtocolHints.set(device.id, protocolHint);
695
722
  }
696
723
  deviceList.push(Object.assign(Object.assign({}, device), { name: displayName, commType: 'ble' }));
724
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] OneKey BLE device discovered', {
725
+ deviceId: device.id,
726
+ name: displayName,
727
+ serviceUUIDs: device.serviceUUIDs,
728
+ protocolHint,
729
+ });
697
730
  }
698
731
  };
699
732
  timer.timeout(() => {
@@ -704,7 +737,7 @@ class ReactNativeBleTransport {
704
737
  });
705
738
  }
706
739
  acquire(input) {
707
- var _a, _b;
740
+ var _a;
708
741
  return __awaiter(this, void 0, void 0, function* () {
709
742
  const { uuid, forceCleanRunPromise, expectedProtocol } = input;
710
743
  if (!uuid) {
@@ -729,7 +762,6 @@ class ReactNativeBleTransport {
729
762
  this.runPromise.reject(error);
730
763
  this.rejectAllProtocolV2Frames(error);
731
764
  this.runPromise = null;
732
- this.activeProtocolV2Call = null;
733
765
  Log === null || Log === void 0 ? void 0 : Log.debug('Force clean Bluetooth run promise, forceCleanRunPromise: ', forceCleanRunPromise);
734
766
  }
735
767
  const blePlxManager = yield this.getPlxManager();
@@ -831,7 +863,7 @@ class ReactNativeBleTransport {
831
863
  this.monitorTokens.set(uuid, monitorToken);
832
864
  transport$1.notifySubscription = this._monitorCharacteristic(transport$1.notifyCharacteristic, uuid, monitorToken, notifyTransactionId);
833
865
  transportCache[uuid] = transport$1;
834
- this.protocolV2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler());
866
+ this.protocolV2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES));
835
867
  if (reactNative.Platform.OS === 'ios') {
836
868
  yield new Promise(resolve => {
837
869
  setTimeout(resolve, IOS_NOTIFY_READY_DELAY_MS);
@@ -841,11 +873,6 @@ class ReactNativeBleTransport {
841
873
  yield delay(ANDROID_NOTIFY_READY_DELAY_MS);
842
874
  }
843
875
  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
876
  this.attachDisconnectSubscription(transport$1, device, uuid);
850
877
  return { uuid, protocolType };
851
878
  });
@@ -854,7 +881,7 @@ class ReactNativeBleTransport {
854
881
  let bufferLength = 0;
855
882
  let buffer$1 = [];
856
883
  const subscription = characteristic.monitor((error, c) => {
857
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
884
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
858
885
  const isCurrentMonitor = this.monitorTokens.get(uuid) === monitorToken;
859
886
  if (error) {
860
887
  Log === null || Log === void 0 ? void 0 : Log.debug(`error monitor ${characteristic.uuid}, deviceId: ${characteristic.deviceID}: ${error}`);
@@ -866,19 +893,37 @@ class ReactNativeBleTransport {
866
893
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
867
894
  return;
868
895
  }
869
- if (this.runPromise) {
870
- let ERROR = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
896
+ if (this.deviceProtocol.get(uuid) === 'V2') {
897
+ let errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
871
898
  if ((_a = error.reason) === null || _a === void 0 ? void 0 : _a.includes('The connection has timed out unexpectedly')) {
872
- ERROR = hdShared.HardwareErrorCode.BleTimeoutError;
899
+ errorCode = hdShared.HardwareErrorCode.BleTimeoutError;
873
900
  }
874
- if ((_b = error.reason) === null || _b === void 0 ? void 0 : _b.includes('Encryption is insufficient')) {
875
- ERROR = hdShared.HardwareErrorCode.BleDeviceBondError;
901
+ else if ((_b = error.reason) === null || _b === void 0 ? void 0 : _b.includes('Encryption is insufficient')) {
902
+ errorCode = hdShared.HardwareErrorCode.BleDeviceBondError;
876
903
  }
877
- if (((_c = error.reason) === null || _c === void 0 ? void 0 : _c.includes('Cannot write client characteristic config descriptor')) ||
904
+ else if (((_c = error.reason) === null || _c === void 0 ? void 0 : _c.includes('Cannot write client characteristic config descriptor')) ||
878
905
  ((_d = error.reason) === null || _d === void 0 ? void 0 : _d.includes('Cannot find client characteristic config descriptor')) ||
879
906
  ((_e = error.reason) === null || _e === void 0 ? void 0 : _e.includes('The handle is invalid')) ||
880
907
  ((_f = error.reason) === null || _f === void 0 ? void 0 : _f.includes('Writing is not permitted')) ||
881
908
  ((_g = error.reason) === null || _g === void 0 ? void 0 : _g.includes('notify change failed for device'))) {
909
+ errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure;
910
+ }
911
+ this.rejectProtocolV2Frames(uuid, hdShared.ERRORS.TypedError(errorCode));
912
+ return;
913
+ }
914
+ if (this.runPromise) {
915
+ let ERROR = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
916
+ if ((_h = error.reason) === null || _h === void 0 ? void 0 : _h.includes('The connection has timed out unexpectedly')) {
917
+ ERROR = hdShared.HardwareErrorCode.BleTimeoutError;
918
+ }
919
+ if ((_j = error.reason) === null || _j === void 0 ? void 0 : _j.includes('Encryption is insufficient')) {
920
+ ERROR = hdShared.HardwareErrorCode.BleDeviceBondError;
921
+ }
922
+ if (((_k = error.reason) === null || _k === void 0 ? void 0 : _k.includes('Cannot write client characteristic config descriptor')) ||
923
+ ((_l = error.reason) === null || _l === void 0 ? void 0 : _l.includes('Cannot find client characteristic config descriptor')) ||
924
+ ((_m = error.reason) === null || _m === void 0 ? void 0 : _m.includes('The handle is invalid')) ||
925
+ ((_o = error.reason) === null || _o === void 0 ? void 0 : _o.includes('Writing is not permitted')) ||
926
+ ((_p = error.reason) === null || _p === void 0 ? void 0 : _p.includes('notify change failed for device'))) {
882
927
  const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure);
883
928
  this.runPromise.reject(notifyError);
884
929
  this.rejectAllProtocolV2Frames(notifyError);
@@ -907,7 +952,7 @@ class ReactNativeBleTransport {
907
952
  return;
908
953
  }
909
954
  if (protocol === 'V2') {
910
- this.handleProtocolV2Notification(uuid, new Uint8Array(data));
955
+ this.handleProtocolV2Notification(uuid, monitorToken, new Uint8Array(data));
911
956
  return;
912
957
  }
913
958
  if (isHeaderChunk(data)) {
@@ -921,28 +966,32 @@ class ReactNativeBleTransport {
921
966
  const value = buffer.Buffer.from(buffer$1);
922
967
  bufferLength = 0;
923
968
  buffer$1 = [];
924
- (_h = this.runPromise) === null || _h === void 0 ? void 0 : _h.resolve(value.toString('hex'));
969
+ (_q = this.runPromise) === null || _q === void 0 ? void 0 : _q.resolve(value.toString('hex'));
925
970
  }
926
971
  }
927
972
  catch (error) {
928
973
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor data error: ', error);
929
974
  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);
975
+ if (this.deviceProtocol.get(uuid) === 'V2') {
976
+ this.rejectProtocolV2Frames(uuid, notifyError);
977
+ }
978
+ else {
979
+ (_r = this.runPromise) === null || _r === void 0 ? void 0 : _r.reject(notifyError);
980
+ }
932
981
  }
933
982
  }, notifyTransactionId);
934
983
  return subscription;
935
984
  }
936
985
  release(uuid, onclose = false) {
937
- var _a, _b, _c, _d, _e, _f, _g, _h;
986
+ var _a, _b, _c, _d, _e, _f, _g;
938
987
  return __awaiter(this, void 0, void 0, function* () {
939
988
  const transport = transportCache[uuid];
989
+ yield this.protocolV2Links.invalidateLink(uuid, 'React Native BLE transport released');
940
990
  if (this.runPromise) {
941
991
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
942
992
  this.runPromise.reject(error);
943
993
  this.runPromise = null;
944
994
  this.rejectAllProtocolV2Frames(error);
945
- this.activeProtocolV2Call = null;
946
995
  }
947
996
  else {
948
997
  this.resetProtocolV2Frames(uuid);
@@ -950,9 +999,6 @@ class ReactNativeBleTransport {
950
999
  if (reactNative.Platform.OS === 'android' && !onclose && transport) {
951
1000
  (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
952
1001
  this.resetProtocolV2Frames(uuid);
953
- if (((_b = this.activeProtocolV2Call) === null || _b === void 0 ? void 0 : _b.uuid) === uuid) {
954
- this.activeProtocolV2Call = null;
955
- }
956
1002
  return Promise.resolve(true);
957
1003
  }
958
1004
  if (transport) {
@@ -960,14 +1006,14 @@ class ReactNativeBleTransport {
960
1006
  this.monitorTokens.delete(uuid);
961
1007
  }
962
1008
  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();
1009
+ (_b = transport.disconnectSubscription) === null || _b === void 0 ? void 0 : _b.remove();
964
1010
  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();
1011
+ 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);
1012
+ (_d = transport.notifySubscription) === null || _d === void 0 ? void 0 : _d.remove();
967
1013
  transport.notifySubscription = undefined;
968
1014
  if (transport.notifyTransactionId) {
969
1015
  try {
970
- yield ((_f = this.blePlxManager) === null || _f === void 0 ? void 0 : _f.cancelTransaction(transport.notifyTransactionId));
1016
+ yield ((_e = this.blePlxManager) === null || _e === void 0 ? void 0 : _e.cancelTransaction(transport.notifyTransactionId));
971
1017
  }
972
1018
  catch (e) {
973
1019
  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 +1022,11 @@ class ReactNativeBleTransport {
976
1022
  delete transportCache[uuid];
977
1023
  }
978
1024
  this.deviceProtocol.delete(uuid);
979
- this.deviceProtocolHints.delete(uuid);
980
- (_g = this.protocolV2Assemblers.get(uuid)) === null || _g === void 0 ? void 0 : _g.reset();
1025
+ (_f = this.protocolV2Assemblers.get(uuid)) === null || _f === void 0 ? void 0 : _f.reset();
981
1026
  this.protocolV2Assemblers.delete(uuid);
982
1027
  this.resetProtocolV2Frames(uuid);
983
1028
  try {
984
- yield ((_h = this.blePlxManager) === null || _h === void 0 ? void 0 : _h.cancelTransaction(uuid));
1029
+ yield ((_g = this.blePlxManager) === null || _g === void 0 ? void 0 : _g.cancelTransaction(uuid));
985
1030
  }
986
1031
  catch (e) {
987
1032
  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 +1047,18 @@ class ReactNativeBleTransport {
1002
1047
  if (this._messages == null) {
1003
1048
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
1004
1049
  }
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
1050
  const protocol = this.getProtocolType(uuid);
1011
1051
  if (!protocol) {
1012
1052
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${uuid}`);
1013
1053
  }
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
- }
1054
+ Log === null || Log === void 0 ? void 0 : Log.debug('transport call', createTransportCallLog(name, protocol, data));
1026
1055
  if (protocol === 'V2') {
1027
1056
  return this.callProtocolV2(uuid, name, data, options);
1028
1057
  }
1058
+ const forceRun = name === 'Initialize' || name === 'Cancel';
1059
+ if (this.runPromise && !forceRun) {
1060
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportCallInProgress);
1061
+ }
1029
1062
  return this.callProtocolV1(uuid, name, data, options);
1030
1063
  });
1031
1064
  }
@@ -1101,7 +1134,7 @@ class ReactNativeBleTransport {
1101
1134
  });
1102
1135
  }
1103
1136
  else if (name === 'FirmwareUpload') {
1104
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] FirmwareUpload write uses throttled BLE packets:', {
1137
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Firmware upload transport configured', {
1105
1138
  packetCapacity: FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY,
1106
1139
  burstSize: FIRMWARE_UPLOAD_WRITE_BURST_SIZE,
1107
1140
  pauseMs: FIRMWARE_UPLOAD_WRITE_PAUSE_MS,
@@ -1190,7 +1223,6 @@ class ReactNativeBleTransport {
1190
1223
  if (typeof response !== 'string') {
1191
1224
  throw new Error('Returning data is not string.');
1192
1225
  }
1193
- Log === null || Log === void 0 ? void 0 : Log.debug('receive data: ', response);
1194
1226
  const jsonData = ProtocolV1.decodeMessage(messages, response);
1195
1227
  return check.call(jsonData);
1196
1228
  }
@@ -1216,10 +1248,11 @@ class ReactNativeBleTransport {
1216
1248
  this.stopped = true;
1217
1249
  }
1218
1250
  disconnect(session) {
1219
- var _a, _b, _c, _d, _e, _f;
1251
+ var _a, _b, _c, _d, _e;
1220
1252
  return __awaiter(this, void 0, void 0, function* () {
1221
- Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native transport resetSession: ', session);
1253
+ yield this.protocolV2Links.invalidateLink(session, 'React Native BLE transport disconnected');
1222
1254
  const transport = transportCache[session];
1255
+ const monitorToken = (_a = transport === null || transport === void 0 ? void 0 : transport.monitorToken) !== null && _a !== void 0 ? _a : this.monitorTokens.get(session);
1223
1256
  if (transport === null || transport === void 0 ? void 0 : transport.disconnectSubscription) {
1224
1257
  try {
1225
1258
  Log === null || Log === void 0 ? void 0 : Log.debug('disconnect: removing disconnect subscription');
@@ -1232,7 +1265,7 @@ class ReactNativeBleTransport {
1232
1265
  }
1233
1266
  if (transport === null || transport === void 0 ? void 0 : transport.notifySubscription) {
1234
1267
  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);
1268
+ 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
1269
  transport.notifySubscription.remove();
1237
1270
  transport.notifySubscription = undefined;
1238
1271
  }
@@ -1242,7 +1275,7 @@ class ReactNativeBleTransport {
1242
1275
  }
1243
1276
  if (session) {
1244
1277
  try {
1245
- yield ((_b = this.blePlxManager) === null || _b === void 0 ? void 0 : _b.cancelTransaction(session));
1278
+ yield ((_c = this.blePlxManager) === null || _c === void 0 ? void 0 : _c.cancelTransaction(session));
1246
1279
  }
1247
1280
  catch (e) {
1248
1281
  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 +1290,7 @@ class ReactNativeBleTransport {
1257
1290
  }
1258
1291
  }
1259
1292
  try {
1260
- yield ((_c = this.blePlxManager) === null || _c === void 0 ? void 0 : _c.cancelDeviceConnection(session));
1293
+ yield ((_d = this.blePlxManager) === null || _d === void 0 ? void 0 : _d.cancelDeviceConnection(session));
1261
1294
  }
1262
1295
  catch (e) {
1263
1296
  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 +1302,15 @@ class ReactNativeBleTransport {
1269
1302
  this.deviceProtocolHints.delete(session);
1270
1303
  this.protocolV2Assemblers.delete(session);
1271
1304
  this.resetProtocolV2Frames(session);
1272
- if (((_d = this.activeProtocolV2Call) === null || _d === void 0 ? void 0 : _d.uuid) === session) {
1273
- this.activeProtocolV2Call = null;
1274
- }
1275
1305
  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
- });
1306
+ this.emitDeviceDisconnect(session, (_e = transport === null || transport === void 0 ? void 0 : transport.device) === null || _e === void 0 ? void 0 : _e.name, monitorToken);
1281
1307
  }
1282
1308
  catch (e) {
1283
1309
  Log === null || Log === void 0 ? void 0 : Log.error('resetSession: emit disconnect event error: ', e);
1284
1310
  }
1311
+ if (monitorToken !== undefined && this.monitorTokens.get(session) === monitorToken) {
1312
+ this.monitorTokens.delete(session);
1313
+ }
1285
1314
  yield new Promise(resolve => setTimeout(() => resolve(), 100));
1286
1315
  });
1287
1316
  }
@@ -1313,15 +1342,26 @@ class ReactNativeBleTransport {
1313
1342
  if (expectedProtocol === 'V1') {
1314
1343
  if (yield this.probeProtocolV1(uuid)) {
1315
1344
  this.deviceProtocol.set(uuid, 'V1');
1316
- Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> V1 (expected)`);
1345
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1346
+ deviceId: uuid,
1347
+ protocol: 'V1',
1348
+ source: 'expected',
1349
+ });
1317
1350
  return 'V1';
1318
1351
  }
1319
1352
  throw this.createProtocolMismatchError(expectedProtocol);
1320
1353
  }
1321
1354
  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';
1355
+ if (yield this.probeProtocolV2(uuid)) {
1356
+ this.deviceProtocol.set(uuid, 'V2');
1357
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1358
+ deviceId: uuid,
1359
+ protocol: 'V2',
1360
+ source: 'expected',
1361
+ });
1362
+ return 'V2';
1363
+ }
1364
+ throw this.createProtocolMismatchError(expectedProtocol);
1325
1365
  }
1326
1366
  const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
1327
1367
  for (let i = 0; i < probeOrder.length; i += 1) {
@@ -1332,7 +1372,11 @@ class ReactNativeBleTransport {
1332
1372
  const detected = protocol === 'V1' ? yield this.probeProtocolV1(uuid) : yield this.probeProtocolV2(uuid);
1333
1373
  if (detected) {
1334
1374
  this.deviceProtocol.set(uuid, protocol);
1335
- Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> ${protocol}`);
1375
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1376
+ deviceId: uuid,
1377
+ protocol,
1378
+ source: 'probe',
1379
+ });
1336
1380
  return protocol;
1337
1381
  }
1338
1382
  }
@@ -1341,14 +1385,12 @@ class ReactNativeBleTransport {
1341
1385
  });
1342
1386
  }
1343
1387
  resetProbeStateAfterProtocolProbe(uuid, protocol) {
1344
- var _a, _b, _c, _d;
1388
+ var _a, _b, _c;
1345
1389
  return __awaiter(this, void 0, void 0, function* () {
1346
1390
  const transport = transportCache[uuid];
1391
+ yield this.protocolV2Links.invalidateLink(uuid, `Reset notify state after Protocol ${protocol} probe`);
1347
1392
  (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1348
1393
  this.resetProtocolV2Frames(uuid);
1349
- if (((_b = this.activeProtocolV2Call) === null || _b === void 0 ? void 0 : _b.uuid) === uuid) {
1350
- this.activeProtocolV2Call = null;
1351
- }
1352
1394
  if (this.runPromise) {
1353
1395
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
1354
1396
  this.runPromise.reject(error);
@@ -1360,11 +1402,11 @@ class ReactNativeBleTransport {
1360
1402
  if (this.monitorTokens.get(uuid) === transport.monitorToken) {
1361
1403
  this.monitorTokens.delete(uuid);
1362
1404
  }
1363
- (_c = transport.notifySubscription) === null || _c === void 0 ? void 0 : _c.remove();
1405
+ (_b = transport.notifySubscription) === null || _b === void 0 ? void 0 : _b.remove();
1364
1406
  transport.notifySubscription = undefined;
1365
1407
  if (previousNotifyTransactionId) {
1366
1408
  try {
1367
- yield ((_d = this.blePlxManager) === null || _d === void 0 ? void 0 : _d.cancelTransaction(previousNotifyTransactionId));
1409
+ yield ((_c = this.blePlxManager) === null || _c === void 0 ? void 0 : _c.cancelTransaction(previousNotifyTransactionId));
1368
1410
  }
1369
1411
  catch (error) {
1370
1412
  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);
@@ -1426,14 +1468,10 @@ class ReactNativeBleTransport {
1426
1468
  return detected;
1427
1469
  });
1428
1470
  }
1429
- handleProtocolV2Notification(uuid, data) {
1430
- var _a, _b, _c;
1471
+ handleProtocolV2Notification(uuid, monitorToken, data) {
1431
1472
  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);
1473
+ if (this.monitorTokens.get(uuid) !== monitorToken)
1435
1474
  return;
1436
- }
1437
1475
  if (data.length === 0)
1438
1476
  return;
1439
1477
  const assembler = this.protocolV2Assemblers.get(uuid);
@@ -1446,8 +1484,10 @@ class ReactNativeBleTransport {
1446
1484
  catch (error) {
1447
1485
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 notification error:', error);
1448
1486
  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);
1487
+ this.rejectProtocolV2Frames(uuid, notifyError);
1488
+ this.protocolV2Links
1489
+ .invalidateLink(uuid, `Protocol V2 notification error: ${error}`)
1490
+ .catch(invalidateError => Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 notify cleanup failed:', invalidateError));
1451
1491
  }
1452
1492
  }
1453
1493
  getProtocolV2FrameQueue(uuid) {
@@ -1478,9 +1518,13 @@ class ReactNativeBleTransport {
1478
1518
  this.protocolV2FrameQueues.delete(uuid);
1479
1519
  this.protocolV2FramePromises.delete(uuid);
1480
1520
  }
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;
1521
+ rejectProtocolV2Frames(uuid, error) {
1522
+ this.protocolV2FrameQueues.delete(uuid);
1523
+ const framePromise = this.protocolV2FramePromises.get(uuid);
1524
+ if (framePromise) {
1525
+ this.protocolV2FramePromises.delete(uuid);
1526
+ framePromise.reject(error);
1527
+ }
1484
1528
  }
1485
1529
  readProtocolV2Frame(uuid) {
1486
1530
  return __awaiter(this, void 0, void 0, function* () {
@@ -1500,140 +1544,124 @@ class ReactNativeBleTransport {
1500
1544
  }
1501
1545
  });
1502
1546
  }
1503
- writeProtocolV2Frame(transport, frame, options) {
1547
+ writeProtocolV2Packet(transport, base64, context, assertCurrentGeneration) {
1548
+ return __awaiter(this, void 0, void 0, function* () {
1549
+ let attempt = 0;
1550
+ for (;;) {
1551
+ assertCurrentGeneration();
1552
+ if (context.signal.aborted) {
1553
+ throw new Error(`Protocol V2 BLE write aborted for ${context.messageName}`);
1554
+ }
1555
+ try {
1556
+ yield transport.writeCharacteristic.writeWithoutResponse(base64);
1557
+ assertCurrentGeneration();
1558
+ return;
1559
+ }
1560
+ catch (error) {
1561
+ if (getFirmwareUploadWriteRetryType(error) !== 'congested' ||
1562
+ attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
1563
+ throw error;
1564
+ }
1565
+ const delayMs = resolveFirmwareUploadRetryDelay(attempt);
1566
+ attempt += 1;
1567
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 congested write retry:', {
1568
+ name: context.messageName,
1569
+ attempt,
1570
+ delayMs,
1571
+ });
1572
+ yield delay(delayMs);
1573
+ }
1574
+ }
1575
+ });
1576
+ }
1577
+ writeProtocolV2Frame(transport$1, frame, context, assertCurrentGeneration) {
1504
1578
  return __awaiter(this, void 0, void 0, function* () {
1505
1579
  const tuning = getProtocolV2BleTuning();
1506
1580
  const packetCapacity = resolveProtocolV2PacketCapacity({
1507
1581
  platform: reactNative.Platform.OS,
1508
1582
  iosPacketLength: tuning.iosPacketLength,
1509
1583
  androidPacketLength: tuning.androidPacketLength,
1510
- mtu: reactNative.Platform.OS === 'android' ? transport.mtuSize : undefined,
1584
+ mtu: reactNative.Platform.OS === 'android' ? transport$1.mtuSize : undefined,
1585
+ });
1586
+ yield transport.writeProtocolV2BleFrame({
1587
+ frame,
1588
+ packetCapacity,
1589
+ assertActive: assertCurrentGeneration,
1590
+ signal: context.signal,
1591
+ abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
1592
+ burstSize: FIRMWARE_UPLOAD_WRITE_BURST_SIZE,
1593
+ burstPauseMs: FIRMWARE_UPLOAD_WRITE_PAUSE_MS,
1594
+ flushDelayMs: FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS,
1595
+ wait: delay,
1596
+ writePacket: packet => this.writeProtocolV2Packet(transport$1, buffer.Buffer.from(packet).toString('base64'), context, assertCurrentGeneration),
1511
1597
  });
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
- try {
1517
- for (let offset = 0; offset < frame.length; offset += packetCapacity) {
1518
- const chunk = frame.slice(offset, offset + packetCapacity);
1519
- const base64 = buffer.Buffer.from(chunk).toString('base64');
1520
- if (writeMode === 'withResponse') {
1521
- yield transport.writeCharacteristic.writeWithResponse(base64);
1522
- }
1523
- else {
1524
- yield transport.writeCharacteristic.writeWithoutResponse(base64);
1525
- }
1526
- packetsWritten += 1;
1527
- if (shouldThrottle &&
1528
- packetsWritten % tuning.highVolumeWriteBurstSize === 0 &&
1529
- offset + packetCapacity < frame.length) {
1530
- yield delay(tuning.highVolumeWritePauseMs);
1531
- }
1532
- }
1533
- if (shouldThrottle) {
1534
- yield delay(tuning.highVolumeWriteFlushDelayMs);
1535
- }
1536
- }
1537
- catch (error) {
1538
- if ((options === null || options === void 0 ? void 0 : options.highVolume) && !writeWithResponse && packetsWritten === 0) {
1539
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume writeWithoutResponse failed before data was sent, fallback to writeWithResponse:', error);
1540
- yield this.writeProtocolV2Frame(transport, frame, {
1541
- highVolume: true,
1542
- writeWithResponse: true,
1543
- });
1544
- return;
1545
- }
1546
- throw error;
1547
- }
1548
1598
  });
1549
1599
  }
1550
1600
  callProtocolV2(uuid, name, data, options) {
1551
- var _a, _b, _c, _d;
1552
1601
  return __awaiter(this, void 0, void 0, function* () {
1553
1602
  if (!this._messages || !this._messagesV2) {
1554
1603
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
1555
1604
  }
1556
- const forceRun = name === 'Initialize' || name === 'Cancel' || name === 'Ping';
1557
- if (this.runPromise) {
1558
- if (!forceRun) {
1559
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportCallInProgress);
1560
- }
1561
- const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
1562
- this.runPromise.reject(error);
1563
- this.rejectAllProtocolV2Frames(error);
1564
- this.runPromise = null;
1565
- this.activeProtocolV2Call = null;
1566
- }
1567
- const transport$1 = this.getCachedTransport(uuid);
1568
- const runPromise = hdShared.createDeferred();
1569
- runPromise.promise.catch(() => undefined);
1570
- this.runPromise = runPromise;
1571
- const callToken = this.nextProtocolV2CallToken++;
1572
- this.activeProtocolV2Call = { uuid, token: callToken };
1573
- (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1574
- this.resetProtocolV2Frames(uuid);
1575
- let completed = false;
1576
- 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 });
1605
+ const callOptions = options;
1577
1606
  const highVolumeWrite = transport.LogBlockCommand.has(name);
1578
1607
  if (highVolumeWrite) {
1579
1608
  const tuning = getProtocolV2BleTuning();
1580
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume write uses throttled writeWithoutResponse:', name, {
1609
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume write configured', {
1610
+ name,
1611
+ writeMode: 'withoutResponse',
1581
1612
  packetCapacity: reactNative.Platform.OS === 'ios' ? tuning.iosPacketLength : tuning.androidPacketLength,
1582
- burstSize: tuning.highVolumeWriteBurstSize,
1583
- pauseMs: tuning.highVolumeWritePauseMs,
1584
- flushDelayMs: tuning.highVolumeWriteFlushDelayMs,
1585
- writeWithResponse: tuning.highVolumeWriteWithResponse,
1586
1613
  });
1587
1614
  }
1588
1615
  try {
1589
- const session = new transport.ProtocolV2Session({
1590
- schemas: {
1591
- protocolV1: this._messages,
1592
- protocolV2: this._messagesV2,
1593
- },
1594
- router: transport.PROTOCOL_V2_CHANNEL_BLE_UART,
1595
- writeFrame: (frame) => __awaiter(this, void 0, void 0, function* () {
1596
- yield this.writeProtocolV2Frame(transport$1, frame, {
1597
- highVolume: highVolumeWrite,
1598
- });
1599
- }),
1600
- readFrame: () => __awaiter(this, void 0, void 0, function* () {
1601
- const rxFrame = yield this.readProtocolV2Frame(uuid);
1602
- if (!(rxFrame instanceof Uint8Array)) {
1603
- throw new Error('Protocol V2 response is not Uint8Array');
1604
- }
1605
- return rxFrame;
1606
- }),
1607
- logger: Log,
1608
- logPrefix: 'ProtocolV2 RN-BLE',
1609
- createTimeoutError: (_messageName, timeout) => hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, `BLE response timeout after ${timeout}ms for ${name}`),
1610
- });
1611
- const result = yield session.call(name, data, callOptions);
1612
- completed = true;
1613
- return result;
1616
+ return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, callOptions);
1614
1617
  }
1615
1618
  catch (e) {
1616
- if (this.isActiveProtocolV2Call(uuid, callToken)) {
1617
- (_c = this.protocolV2Assemblers.get(uuid)) === null || _c === void 0 ? void 0 : _c.reset();
1618
- this.resetProtocolV2Frames(uuid);
1619
- }
1620
1619
  Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] Protocol V2 call error:', e);
1621
1620
  throw e;
1622
1621
  }
1623
- finally {
1624
- if (this.isActiveProtocolV2Call(uuid, callToken)) {
1625
- if (!completed) {
1626
- (_d = this.protocolV2Assemblers.get(uuid)) === null || _d === void 0 ? void 0 : _d.reset();
1627
- }
1628
- this.resetProtocolV2Frames(uuid);
1629
- this.activeProtocolV2Call = null;
1630
- }
1631
- if (this.runPromise === runPromise) {
1632
- this.runPromise = null;
1633
- }
1634
- }
1635
1622
  });
1636
1623
  }
1624
+ createProtocolV2Adapter(uuid) {
1625
+ var _a;
1626
+ const generation = (_a = this.monitorTokens.get(uuid)) !== null && _a !== void 0 ? _a : 0;
1627
+ const assertCurrentGeneration = () => {
1628
+ if (this.monitorTokens.get(uuid) !== generation) {
1629
+ throw new Error(`Protocol V2 monitor generation changed for ${uuid}`);
1630
+ }
1631
+ };
1632
+ return {
1633
+ router: transport.PROTOCOL_V2_CHANNEL_BLE_UART,
1634
+ maxFrameBytes: transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES,
1635
+ generation,
1636
+ prepareCall: () => {
1637
+ var _a;
1638
+ assertCurrentGeneration();
1639
+ (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1640
+ this.resetProtocolV2Frames(uuid);
1641
+ },
1642
+ writeFrame: (frame, context) => __awaiter(this, void 0, void 0, function* () {
1643
+ assertCurrentGeneration();
1644
+ const currentTransport = this.getCachedTransport(uuid);
1645
+ yield this.writeProtocolV2Frame(currentTransport, frame, context, assertCurrentGeneration);
1646
+ }),
1647
+ readFrame: () => __awaiter(this, void 0, void 0, function* () {
1648
+ assertCurrentGeneration();
1649
+ const rxFrame = yield this.readProtocolV2Frame(uuid);
1650
+ if (!(rxFrame instanceof Uint8Array)) {
1651
+ throw new Error('Protocol V2 response is not Uint8Array');
1652
+ }
1653
+ return rxFrame;
1654
+ }),
1655
+ reset: (reason) => {
1656
+ var _a;
1657
+ (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1658
+ this.rejectProtocolV2Frames(uuid, new Error(reason));
1659
+ },
1660
+ logger: Log,
1661
+ logPrefix: 'ProtocolV2 RN-BLE',
1662
+ createTimeoutError: (messageName, timeout) => hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, `BLE response timeout after ${timeout}ms for ${messageName}`),
1663
+ };
1664
+ }
1637
1665
  getProtocolType(path) {
1638
1666
  return this.deviceProtocol.get(path);
1639
1667
  }