@onekeyfe/hd-transport-react-native 1.2.0-alpha.53 → 1.2.0-alpha.54

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.d.ts CHANGED
@@ -35,6 +35,17 @@ type ResolvedBleCharacteristics = {
35
35
  notifyCharacteristic: Characteristic;
36
36
  };
37
37
  declare const getFirmwareUploadWriteRetryType: (error: unknown) => FirmwareUploadWriteRetryType | null;
38
+ /**
39
+ * Per-packet write budget. iOS only resolves writeWithoutResponse once CoreBluetooth
40
+ * reports the peripheral ready again; a peripheral wedged by its own firmware reboot
41
+ * stops reporting ready while staying connected, so the write promise never settles.
42
+ * Response timeouts cannot cover that — they are armed after the writes complete —
43
+ * and an unbounded write leaves the whole transport unusable until the process dies.
44
+ * A healthy packet completes in milliseconds, so this only fires on a dead link.
45
+ */
46
+ declare const BLE_WRITE_PACKET_TIMEOUT_MS = 10000;
47
+ /** Consecutive wedged writes on one device before the BLE manager itself is recreated. */
48
+ declare const BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
38
49
  type ProtocolV2BleTuning = {
39
50
  iosPacketLength?: number;
40
51
  androidPacketLength?: number;
@@ -61,6 +72,15 @@ declare class ReactNativeBleTransport {
61
72
  firmwareUploadWriteRecoveryIds: Set<string>;
62
73
  /** Per-device protocol type detected by active wire-level probe after connect. */
63
74
  private deviceProtocol;
75
+ /**
76
+ * Protocol a probe is currently trying, before the device has confirmed it. Calls
77
+ * must route with it, but acquire() must not treat it as a detected protocol: a
78
+ * probe that never answers would otherwise leave the reuse fast path handing out a
79
+ * transport that was never validated.
80
+ */
81
+ private probingProtocols;
82
+ /** Consecutive write timeouts per device; reset by any write that completes. */
83
+ private writeTimeoutCounts;
64
84
  private deviceProtocolHints;
65
85
  private protocolV2Assemblers;
66
86
  private protocolV2FrameQueues;
@@ -100,9 +120,26 @@ declare class ReactNativeBleTransport {
100
120
  disconnect(session: string): Promise<void>;
101
121
  cancel(): void;
102
122
  private getCachedTransport;
123
+ /**
124
+ * Write one packet under a bounded budget. A write that never settles means the
125
+ * peripheral is wedged even though the GATT link still reports connected, so the
126
+ * link is torn down: releasing JS state alone would leave the poisoned peripheral
127
+ * cached and every later call would hang on it again.
128
+ */
129
+ private writeBlePacket;
130
+ /**
131
+ * Drop a link whose writes stopped completing. The JS state is purged synchronously
132
+ * so the next acquire() cannot reuse the dead transport, while the native teardown is
133
+ * intentionally NOT awaited: it talks to the very layer that just stopped settling
134
+ * promises, so awaiting it could hang exactly like the write it is recovering from.
135
+ */
136
+ private tearDownWedgedLink;
137
+ private resetPlxManager;
103
138
  private createProtocolMismatchError;
104
139
  private createProtocolDetectionError;
105
140
  private clearProbeProtocol;
141
+ /** Protocol to route a call with: confirmed if known, otherwise the one being probed. */
142
+ private getActiveProtocol;
106
143
  private detectProtocol;
107
144
  private resetProbeStateAfterProtocolProbe;
108
145
  private probeProtocolV1;
@@ -120,4 +157,4 @@ declare class ReactNativeBleTransport {
120
157
  getProtocolType(path: string): ProtocolType | undefined;
121
158
  }
122
159
 
123
- export { IOneKeyDevice, ProtocolV2BleTuning, configureProtocolV2BleTuning, ReactNativeBleTransport as default, getFirmwareUploadWriteRetryType, getProtocolV2BleTuning, resetProtocolV2BleTuning };
160
+ export { BLE_WRITE_PACKET_TIMEOUT_MS, BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD, IOneKeyDevice, ProtocolV2BleTuning, configureProtocolV2BleTuning, ReactNativeBleTransport as default, getFirmwareUploadWriteRetryType, getProtocolV2BleTuning, resetProtocolV2BleTuning };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAIL,UAAU,IAAI,aAAa,EAE5B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,SAAS,EAAE,EAEhB,KAAK,oBAAoB,EAIzB,KAAK,YAAY,EAKjB,KAAK,oBAAoB,EAG1B,MAAM,wBAAwB,CAAC;AAmBhC,OAAO,YAAY,MAAM,gBAAgB,CAAC;AAK1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACjF,OAAO,KAAK,YAAY,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAgBjE,KAAK,4BAA4B,GAAG,WAAW,CAAC;AAChD,KAAK,0BAA0B,GAAG;IAChC,mBAAmB,EAAE,cAAc,CAAC;IACpC,oBAAoB,EAAE,cAAc,CAAC;CACtC,CAAC;AAOF,eAAO,MAAM,+BAA+B,UACnC,OAAO,KACb,4BAA4B,GAAG,IAsBjC,CAAC;AAcF,eAAO,MAAM,2BAA2B,QAAS,CAAC;AAOlD,eAAO,MAAM,yCAAyC,IAAI,CAAC;AAI3D,MAAM,MAAM,mBAAmB,GAAG;IAChC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAiBF,wBAAgB,4BAA4B,CAAC,MAAM,GAAE,mBAAwB,QAY5E;AAED,wBAAgB,wBAAwB,SAGvC;AAED,wBAAgB,sBAAsB;;;EAErC;AAkBD,MAAM,MAAM,aAAa,GAAG,oBAAoB,GAAG,MAAM,CAAC;AA4D1D,MAAM,CAAC,OAAO,OAAO,uBAAuB;IAC1C,aAAa,EAAE,aAAa,GAAG,SAAS,CAAC;IAEzC,SAAS,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAEnE,WAAW,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAErE,OAAO,CAAC,6BAA6B,CAAqB;IAE1D,IAAI,SAA6B;IAEjC,UAAU,UAAS;IAEnB,OAAO,UAAS;IAEhB,WAAW,SAA0B;IAErC,UAAU,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,IAAI,CAAQ;IAExC,OAAO,CAAC,kBAAkB,CAAuB;IAEjD,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB,8BAA8B,cAAqB;IAGnD,OAAO,CAAC,cAAc,CAAwC;IAQ9D,OAAO,CAAC,gBAAgB,CAAwC;IAGhE,OAAO,CAAC,kBAAkB,CAAkC;IAE5D,OAAO,CAAC,mBAAmB,CAAwC;IAEnE,OAAO,CAAC,oBAAoB,CAAoD;IAEhF,OAAO,CAAC,qBAAqB,CAAwC;IAErE,OAAO,CAAC,uBAAuB,CAAgD;IAE/E,OAAO,CAAC,eAAe,CAmBpB;IAEH,OAAO,CAAC,aAAa,CAAkC;IAEvD,OAAO,CAAC,qBAAqB,CAAkC;IAE/D,OAAO,CAAC,gBAAgB,CAAK;gBAEjB,OAAO,EAAE,gBAAgB;IAIrC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,YAAY;IAKvC,SAAS,CAAC,UAAU,EAAE,GAAG;IAMzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAgBnC,MAAM;IAIN,aAAa,IAAI,OAAO,CAAC,aAAa,CAAC;IAMjC,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,0BAA0B,CAAC;IAmFjF,4BAA4B,CAAC,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAgClF,OAAO,CAAC,oBAAoB;IAgBtB,gCAAgC,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY;IAuDtE,SAAS;YAmID,0BAA0B;IAwClC,OAAO,CAAC,KAAK,EAAE,eAAe;;;;IAsKpC,sBAAsB,CACpB,cAAc,EAAE,cAAc,EAC9B,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,mBAAmB,EAAE,MAAM,GAC1B,YAAY;IAsIT,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,UAAQ;YAK7B,aAAa;IA+DrB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAIjE,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YA+BlB,cAAc;IAoP5B,IAAI;IAIE,UAAU,CAAC,OAAO,EAAE,MAAM;IA+EhC,MAAM;IASN,OAAO,CAAC,kBAAkB;YAcZ,cAAc;IA8C5B,OAAO,CAAC,kBAAkB;IA6B1B,OAAO,CAAC,eAAe;IAkBvB,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;IAOpC,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,iBAAiB;YAIX,cAAc;YAmEd,iCAAiC;YAoDjC,eAAe;YAwBf,eAAe;IA0B7B,OAAO,CAAC,4BAA4B;IA2BpC,OAAO,CAAC,uBAAuB;IAS/B,OAAO,CAAC,sBAAsB;IAU9B,OAAO,CAAC,qBAAqB;IAI7B,OAAO,CAAC,sBAAsB;YAShB,mBAAmB;YAiBnB,qBAAqB;YAkDrB,oBAAoB;YAmCpB,cAAc;IAoC5B,OAAO,CAAC,uBAAuB;IAkD/B,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;CAGxD"}
package/dist/index.js CHANGED
@@ -222,7 +222,6 @@ const FIRMWARE_UPLOAD_WRITE_BURST_SIZE = reactNative.Platform.OS === 'ios' ? 4 :
222
222
  const FIRMWARE_UPLOAD_WRITE_PAUSE_MS = reactNative.Platform.OS === 'ios' ? 8 : 10;
223
223
  const FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS = reactNative.Platform.OS === 'ios' ? 24 : 30;
224
224
  const FIRMWARE_UPLOAD_WRITE_MAX_RETRIES = 8;
225
- const IOS_PROTOCOL_V2_CONTROL_WRITE_DELAY_MS = 5;
226
225
  const ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH = 192;
227
226
  const FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY = reactNative.Platform.OS === 'ios' ? IOS_PACKET_LENGTH : ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH;
228
227
  const ANDROID_GATT_CONGESTED_STATUS = 143;
@@ -245,6 +244,12 @@ const getFirmwareUploadWriteRetryType = (error) => {
245
244
  const resolveFirmwareUploadRetryDelay = (attempt, baseDelayMs = 200, maxDelayMs = 1200) => Math.min(baseDelayMs * Math.pow(2, attempt), maxDelayMs);
246
245
  const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
247
246
  const PROTOCOL_V2_PROBE_TIMEOUT_MS = 10000;
247
+ const BLE_WRITE_PACKET_TIMEOUT_MS = 10000;
248
+ const WEDGED_WRITE_MESSAGE = 'BLE write timeout after';
249
+ const isWedgedWriteError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleWriteCharacteristicError &&
250
+ typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' &&
251
+ error.message.startsWith(WEDGED_WRITE_MESSAGE);
252
+ const BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
248
253
  const DEVICE_SCAN_TIMEOUT_MS = 3000;
249
254
  const IOS_NOTIFY_READY_DELAY_MS = 150;
250
255
  const ANDROID_NOTIFY_READY_DELAY_MS = 300;
@@ -342,6 +347,8 @@ class ReactNativeBleTransport {
342
347
  this.runPromiseDeviceId = null;
343
348
  this.firmwareUploadWriteRecoveryIds = new Set();
344
349
  this.deviceProtocol = new Map();
350
+ this.probingProtocols = new Map();
351
+ this.writeTimeoutCounts = new Map();
345
352
  this.deviceProtocolHints = new Map();
346
353
  this.protocolV2Assemblers = new Map();
347
354
  this.protocolV2FrameQueues = new Map();
@@ -615,17 +622,12 @@ class ReactNativeBleTransport {
615
622
  return;
616
623
  }
617
624
  const displayName = getDeviceDisplayName(device);
618
- const isUnnamedIOSPeripheral = reactNative.Platform.OS === 'ios' && !(displayName === null || displayName === void 0 ? void 0 : displayName.trim());
619
- const isFindMyPeripheral = hdShared.isPro2FindMyAdvertisementName(device === null || device === void 0 ? void 0 : device.name) ||
620
- hdShared.isPro2FindMyAdvertisementName(device === null || device === void 0 ? void 0 : device.localName);
621
- const isOneKey = !isUnnamedIOSPeripheral &&
622
- !isFindMyPeripheral &&
623
- hdShared.isOnekeyBluetoothDevice({
624
- id: device === null || device === void 0 ? void 0 : device.id,
625
- name: device === null || device === void 0 ? void 0 : device.name,
626
- localName: device === null || device === void 0 ? void 0 : device.localName,
627
- serviceUuids: device === null || device === void 0 ? void 0 : device.serviceUUIDs,
628
- });
625
+ const isOneKey = hdShared.isOnekeyBluetoothDevice({
626
+ id: device === null || device === void 0 ? void 0 : device.id,
627
+ name: device === null || device === void 0 ? void 0 : device.name,
628
+ localName: device === null || device === void 0 ? void 0 : device.localName,
629
+ serviceUuids: device === null || device === void 0 ? void 0 : device.serviceUUIDs,
630
+ });
629
631
  if (isOneKey) {
630
632
  addDevice(device);
631
633
  }
@@ -643,15 +645,12 @@ class ReactNativeBleTransport {
643
645
  const localName = 'localName' in device && typeof device.localName === 'string'
644
646
  ? device.localName
645
647
  : null;
646
- const isFindMyPeripheral = hdShared.isPro2FindMyAdvertisementName(device.name) ||
647
- hdShared.isPro2FindMyAdvertisementName(localName);
648
- if (!isFindMyPeripheral &&
649
- hdShared.isOnekeyBluetoothDevice({
650
- id: device.id,
651
- name: device.name,
652
- localName,
653
- serviceUuids: device.serviceUUIDs,
654
- })) {
648
+ if (hdShared.isOnekeyBluetoothDevice({
649
+ id: device.id,
650
+ name: device.name,
651
+ localName,
652
+ serviceUuids: device.serviceUUIDs,
653
+ })) {
655
654
  Log === null || Log === void 0 ? void 0 : Log.debug('search connected peripheral: ', device.id);
656
655
  addDevice(device);
657
656
  }
@@ -861,7 +860,7 @@ class ReactNativeBleTransport {
861
860
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
862
861
  return;
863
862
  }
864
- if (this.deviceProtocol.get(uuid) === 'V2') {
863
+ if (this.getActiveProtocol(uuid) === 'V2') {
865
864
  let errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
866
865
  if ((_a = error.reason) === null || _a === void 0 ? void 0 : _a.includes('The connection has timed out unexpectedly')) {
867
866
  errorCode = hdShared.HardwareErrorCode.BleTimeoutError;
@@ -912,7 +911,7 @@ class ReactNativeBleTransport {
912
911
  }
913
912
  try {
914
913
  const data = buffer.Buffer.from(c.value, 'base64');
915
- const protocol = this.deviceProtocol.get(uuid);
914
+ const protocol = this.getActiveProtocol(uuid);
916
915
  if (!protocol) {
917
916
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor data ignored before protocol detection: ', uuid);
918
917
  return;
@@ -940,7 +939,7 @@ class ReactNativeBleTransport {
940
939
  catch (error) {
941
940
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor data error: ', error);
942
941
  const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
943
- if (this.deviceProtocol.get(uuid) === 'V2') {
942
+ if (this.getActiveProtocol(uuid) === 'V2') {
944
943
  this.rejectProtocolV2Frames(uuid, notifyError);
945
944
  }
946
945
  else if (this.runPromiseDeviceId === uuid) {
@@ -996,6 +995,7 @@ class ReactNativeBleTransport {
996
995
  delete transportCache[uuid];
997
996
  }
998
997
  this.deviceProtocol.delete(uuid);
998
+ this.probingProtocols.delete(uuid);
999
999
  (_f = this.protocolV2Assemblers.get(uuid)) === null || _f === void 0 ? void 0 : _f.reset();
1000
1000
  this.protocolV2Assemblers.delete(uuid);
1001
1001
  this.resetProtocolV2Frames(uuid);
@@ -1044,8 +1044,19 @@ class ReactNativeBleTransport {
1044
1044
  const transport = this.getCachedTransport(uuid);
1045
1045
  const runPromise = hdShared.createDeferred();
1046
1046
  runPromise.promise.catch(() => undefined);
1047
+ const supersededRunPromise = this.runPromise;
1048
+ if (supersededRunPromise) {
1049
+ supersededRunPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise));
1050
+ }
1047
1051
  this.runPromise = runPromise;
1048
1052
  this.runPromiseDeviceId = uuid;
1053
+ const releaseOwnershipIfCurrent = () => {
1054
+ if (this.runPromise === runPromise) {
1055
+ this.runPromise = null;
1056
+ this.runPromiseDeviceId = null;
1057
+ }
1058
+ };
1059
+ const isCurrentOwner = () => this.runPromise === runPromise;
1049
1060
  const messages = this._messages;
1050
1061
  const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
1051
1062
  let timeout;
@@ -1066,6 +1077,9 @@ class ReactNativeBleTransport {
1066
1077
  }
1067
1078
  catch (e) {
1068
1079
  onError(e);
1080
+ if (isWedgedWriteError(e)) {
1081
+ throw e;
1082
+ }
1069
1083
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1070
1084
  }
1071
1085
  }
@@ -1093,6 +1107,9 @@ class ReactNativeBleTransport {
1093
1107
  }
1094
1108
  catch (e) {
1095
1109
  onError(e);
1110
+ if (isWedgedWriteError(e)) {
1111
+ throw e;
1112
+ }
1096
1113
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1097
1114
  }
1098
1115
  }
@@ -1103,8 +1120,8 @@ class ReactNativeBleTransport {
1103
1120
  });
1104
1121
  }
1105
1122
  if (name === 'EmmcFileWrite') {
1106
- yield writeChunkedData(buffers, data => transport.writeWithRetry(data), e => {
1107
- this.runPromise = null;
1123
+ yield writeChunkedData(buffers, data => this.writeBlePacket(uuid, data, payload => transport.writeWithRetry(payload), isCurrentOwner), e => {
1124
+ releaseOwnershipIfCurrent();
1108
1125
  Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
1109
1126
  });
1110
1127
  }
@@ -1120,7 +1137,7 @@ class ReactNativeBleTransport {
1120
1137
  let attempt = 0;
1121
1138
  while (true) {
1122
1139
  try {
1123
- yield transport.writeCharacteristic.writeWithoutResponse(data);
1140
+ yield this.writeBlePacket(uuid, data, payload => transport.writeCharacteristic.writeWithoutResponse(payload), isCurrentOwner);
1124
1141
  return;
1125
1142
  }
1126
1143
  catch (error) {
@@ -1139,7 +1156,7 @@ class ReactNativeBleTransport {
1139
1156
  }
1140
1157
  }
1141
1158
  }), e => {
1142
- this.runPromise = null;
1159
+ releaseOwnershipIfCurrent();
1143
1160
  Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
1144
1161
  });
1145
1162
  }
@@ -1147,17 +1164,14 @@ class ReactNativeBleTransport {
1147
1164
  for (const o of buffers) {
1148
1165
  const outData = o.toString('base64');
1149
1166
  try {
1150
- const shouldUseWriteWithResponse = reactNative.Platform.OS === 'ios' && transport.writeCharacteristic.isWritableWithResponse;
1151
- if (shouldUseWriteWithResponse) {
1152
- yield transport.writeCharacteristic.writeWithResponse(outData);
1153
- }
1154
- else {
1155
- yield transport.writeCharacteristic.writeWithoutResponse(outData);
1156
- }
1167
+ yield this.writeBlePacket(uuid, outData, payload => transport.writeCharacteristic.writeWithoutResponse(payload), isCurrentOwner);
1157
1168
  }
1158
1169
  catch (e) {
1159
1170
  Log === null || Log === void 0 ? void 0 : Log.debug('writeCharacteristic write error: ', e);
1160
- this.runPromise = null;
1171
+ releaseOwnershipIfCurrent();
1172
+ if (isWedgedWriteError(e)) {
1173
+ throw e;
1174
+ }
1161
1175
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected) {
1162
1176
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded);
1163
1177
  }
@@ -1197,7 +1211,9 @@ class ReactNativeBleTransport {
1197
1211
  Log === null || Log === void 0 ? void 0 : Log.error('call error: ', e);
1198
1212
  }
1199
1213
  const isProbeTimeout = name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS;
1214
+ const isStaleCall = this.runPromise !== runPromise;
1200
1215
  if (!isProbeTimeout &&
1216
+ !isStaleCall &&
1201
1217
  (e === null || e === void 0 ? void 0 : e.errorCode) === hdShared.HardwareErrorCode.BleTimeoutError) {
1202
1218
  yield this.disconnect(uuid);
1203
1219
  }
@@ -1268,6 +1284,7 @@ class ReactNativeBleTransport {
1268
1284
  delete transportCache[session];
1269
1285
  }
1270
1286
  this.deviceProtocol.delete(session);
1287
+ this.probingProtocols.delete(session);
1271
1288
  this.deviceProtocolHints.delete(session);
1272
1289
  this.protocolV2Assemblers.delete(session);
1273
1290
  this.resetProtocolV2Frames(session);
@@ -1296,6 +1313,80 @@ class ReactNativeBleTransport {
1296
1313
  }
1297
1314
  return transport;
1298
1315
  }
1316
+ writeBlePacket(uuid, data, write, isCurrentOwner) {
1317
+ return __awaiter(this, void 0, void 0, function* () {
1318
+ let timer;
1319
+ let timedOut = false;
1320
+ try {
1321
+ yield Promise.race([
1322
+ write(data),
1323
+ new Promise((_, reject) => {
1324
+ timer = setTimeout(() => {
1325
+ timedOut = true;
1326
+ reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError, `BLE write timeout after ${BLE_WRITE_PACKET_TIMEOUT_MS}ms`));
1327
+ }, BLE_WRITE_PACKET_TIMEOUT_MS);
1328
+ }),
1329
+ ]);
1330
+ this.writeTimeoutCounts.delete(uuid);
1331
+ }
1332
+ catch (error) {
1333
+ if (timedOut) {
1334
+ if (isCurrentOwner && !isCurrentOwner()) {
1335
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] stale BLE write timed out, link kept:', uuid);
1336
+ }
1337
+ else {
1338
+ this.tearDownWedgedLink(uuid);
1339
+ }
1340
+ }
1341
+ throw error;
1342
+ }
1343
+ finally {
1344
+ if (timer)
1345
+ clearTimeout(timer);
1346
+ }
1347
+ });
1348
+ }
1349
+ tearDownWedgedLink(uuid) {
1350
+ var _a;
1351
+ const timeouts = ((_a = this.writeTimeoutCounts.get(uuid)) !== null && _a !== void 0 ? _a : 0) + 1;
1352
+ this.writeTimeoutCounts.set(uuid, timeouts);
1353
+ Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE write timed out, tearing down link:', uuid, {
1354
+ consecutiveWriteTimeouts: timeouts,
1355
+ });
1356
+ const wedged = transportCache[uuid];
1357
+ this.disconnect(uuid).catch(error => {
1358
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] wedged link teardown failed (ignored):', error);
1359
+ });
1360
+ if (wedged && transportCache[uuid] === wedged) {
1361
+ delete transportCache[uuid];
1362
+ }
1363
+ this.deviceProtocol.delete(uuid);
1364
+ this.probingProtocols.delete(uuid);
1365
+ this.protocolV2Assemblers.delete(uuid);
1366
+ this.resetProtocolV2Frames(uuid);
1367
+ if (timeouts >= BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD) {
1368
+ Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE writes wedged repeatedly, resetting BLE manager');
1369
+ this.resetPlxManager();
1370
+ this.writeTimeoutCounts.delete(uuid);
1371
+ }
1372
+ }
1373
+ resetPlxManager() {
1374
+ const manager = this.blePlxManager;
1375
+ this.blePlxManager = undefined;
1376
+ Object.keys(transportCache).forEach(key => {
1377
+ delete transportCache[key];
1378
+ });
1379
+ this.deviceProtocol.clear();
1380
+ this.probingProtocols.clear();
1381
+ this.monitorTokens.clear();
1382
+ this.protocolV2Assemblers.clear();
1383
+ try {
1384
+ manager === null || manager === void 0 ? void 0 : manager.destroy();
1385
+ }
1386
+ catch (error) {
1387
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE manager destroy failed (ignored):', error);
1388
+ }
1389
+ }
1299
1390
  createProtocolMismatchError(expected) {
1300
1391
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
1301
1392
  }
@@ -1303,30 +1394,19 @@ class ReactNativeBleTransport {
1303
1394
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1 GetFeatures or Protocol V2 Ping');
1304
1395
  }
1305
1396
  clearProbeProtocol(uuid, protocol) {
1397
+ if (this.probingProtocols.get(uuid) === protocol) {
1398
+ this.probingProtocols.delete(uuid);
1399
+ }
1306
1400
  if (this.deviceProtocol.get(uuid) === protocol) {
1307
1401
  this.deviceProtocol.delete(uuid);
1308
1402
  }
1309
1403
  }
1310
- detectProtocol(uuid, expectedProtocol, protocolHint, rebuildTransport) {
1404
+ getActiveProtocol(uuid) {
1311
1405
  var _a;
1406
+ return (_a = this.deviceProtocol.get(uuid)) !== null && _a !== void 0 ? _a : this.probingProtocols.get(uuid);
1407
+ }
1408
+ detectProtocol(uuid, expectedProtocol, protocolHint, rebuildTransport) {
1312
1409
  return __awaiter(this, void 0, void 0, function* () {
1313
- if (reactNative.Platform.OS === 'ios') {
1314
- const protocol = (_a = expectedProtocol !== null && expectedProtocol !== void 0 ? expectedProtocol : protocolHint) !== null && _a !== void 0 ? _a : 'V1';
1315
- let source = 'ios-legacy-default';
1316
- if (expectedProtocol) {
1317
- source = 'expected';
1318
- }
1319
- else if (protocolHint) {
1320
- source = 'hint';
1321
- }
1322
- this.deviceProtocol.set(uuid, protocol);
1323
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol selected', {
1324
- deviceId: uuid,
1325
- protocol,
1326
- source,
1327
- });
1328
- return protocol;
1329
- }
1330
1410
  if (expectedProtocol === 'V1') {
1331
1411
  if (yield this.probeProtocolV1(uuid)) {
1332
1412
  this.deviceProtocol.set(uuid, 'V1');
@@ -1375,6 +1455,7 @@ class ReactNativeBleTransport {
1375
1455
  }
1376
1456
  }
1377
1457
  this.deviceProtocol.delete(uuid);
1458
+ this.probingProtocols.delete(uuid);
1378
1459
  throw this.createProtocolDetectionError();
1379
1460
  });
1380
1461
  }
@@ -1426,13 +1507,17 @@ class ReactNativeBleTransport {
1426
1507
  return false;
1427
1508
  }
1428
1509
  try {
1429
- this.deviceProtocol.set(uuid, 'V1');
1510
+ this.probingProtocols.set(uuid, 'V1');
1430
1511
  yield this.callProtocolV1(uuid, 'GetFeatures', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
1512
+ this.probingProtocols.delete(uuid);
1431
1513
  return true;
1432
1514
  }
1433
1515
  catch (error) {
1434
1516
  this.clearProbeProtocol(uuid, 'V1');
1435
1517
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe failed:', error);
1518
+ if (isWedgedWriteError(error)) {
1519
+ throw error;
1520
+ }
1436
1521
  return false;
1437
1522
  }
1438
1523
  });
@@ -1443,7 +1528,7 @@ class ReactNativeBleTransport {
1443
1528
  if (!this._messages || !this._messagesV2) {
1444
1529
  return false;
1445
1530
  }
1446
- this.deviceProtocol.set(uuid, 'V2');
1531
+ this.probingProtocols.set(uuid, 'V2');
1447
1532
  (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1448
1533
  const detected = yield transport.probeProtocolV2({
1449
1534
  call: (name, data, options) => this.callProtocolV2(uuid, name, data, options),
@@ -1459,6 +1544,9 @@ class ReactNativeBleTransport {
1459
1544
  if (!detected) {
1460
1545
  this.clearProbeProtocol(uuid, 'V2');
1461
1546
  }
1547
+ else {
1548
+ this.probingProtocols.delete(uuid);
1549
+ }
1462
1550
  return detected;
1463
1551
  });
1464
1552
  }
@@ -1530,11 +1618,8 @@ class ReactNativeBleTransport {
1530
1618
  }
1531
1619
  });
1532
1620
  }
1533
- writeProtocolV2Packet(transport, base64, context, assertCurrentGeneration) {
1621
+ writeProtocolV2Packet(uuid, transport, base64, context, assertCurrentGeneration) {
1534
1622
  return __awaiter(this, void 0, void 0, function* () {
1535
- const shouldUseWriteWithResponse = reactNative.Platform.OS === 'ios' &&
1536
- !context.highVolume &&
1537
- transport.writeCharacteristic.isWritableWithResponse;
1538
1623
  let attempt = 0;
1539
1624
  for (;;) {
1540
1625
  assertCurrentGeneration();
@@ -1542,12 +1627,15 @@ class ReactNativeBleTransport {
1542
1627
  throw new Error(`Protocol V2 BLE write aborted for ${context.messageName}`);
1543
1628
  }
1544
1629
  try {
1545
- if (shouldUseWriteWithResponse) {
1546
- yield transport.writeCharacteristic.writeWithResponse(base64);
1547
- }
1548
- else {
1549
- yield transport.writeCharacteristic.writeWithoutResponse(base64);
1550
- }
1630
+ yield this.writeBlePacket(uuid, base64, payload => transport.writeCharacteristic.writeWithoutResponse(payload), () => {
1631
+ try {
1632
+ assertCurrentGeneration();
1633
+ return !context.signal.aborted;
1634
+ }
1635
+ catch (_a) {
1636
+ return false;
1637
+ }
1638
+ });
1551
1639
  assertCurrentGeneration();
1552
1640
  return;
1553
1641
  }
@@ -1568,7 +1656,7 @@ class ReactNativeBleTransport {
1568
1656
  }
1569
1657
  });
1570
1658
  }
1571
- writeProtocolV2Frame(transport$1, frame, context, assertCurrentGeneration) {
1659
+ writeProtocolV2Frame(uuid, transport$1, frame, context, assertCurrentGeneration) {
1572
1660
  return __awaiter(this, void 0, void 0, function* () {
1573
1661
  const tuning = getProtocolV2BleTuning();
1574
1662
  const packetCapacity = resolveProtocolV2PacketCapacity({
@@ -1577,21 +1665,17 @@ class ReactNativeBleTransport {
1577
1665
  androidPacketLength: tuning.androidPacketLength,
1578
1666
  mtu: reactNative.Platform.OS === 'android' ? transport$1.mtuSize : undefined,
1579
1667
  });
1580
- const initialDelayMs = reactNative.Platform.OS === 'ios' && !context.highVolume && frame.length <= packetCapacity
1581
- ? IOS_PROTOCOL_V2_CONTROL_WRITE_DELAY_MS
1582
- : 0;
1583
1668
  yield transport.writeProtocolV2BleFrame({
1584
1669
  frame,
1585
1670
  packetCapacity,
1586
1671
  assertActive: assertCurrentGeneration,
1587
1672
  signal: context.signal,
1588
1673
  abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
1589
- initialDelayMs,
1590
1674
  burstSize: FIRMWARE_UPLOAD_WRITE_BURST_SIZE,
1591
1675
  burstPauseMs: FIRMWARE_UPLOAD_WRITE_PAUSE_MS,
1592
1676
  flushDelayMs: FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS,
1593
1677
  wait: delay,
1594
- writePacket: packet => this.writeProtocolV2Packet(transport$1, buffer.Buffer.from(packet).toString('base64'), context, assertCurrentGeneration),
1678
+ writePacket: packet => this.writeProtocolV2Packet(uuid, transport$1, buffer.Buffer.from(packet).toString('base64'), context, assertCurrentGeneration),
1595
1679
  });
1596
1680
  });
1597
1681
  }
@@ -1640,7 +1724,7 @@ class ReactNativeBleTransport {
1640
1724
  writeFrame: (frame, context) => __awaiter(this, void 0, void 0, function* () {
1641
1725
  assertCurrentGeneration();
1642
1726
  const currentTransport = this.getCachedTransport(uuid);
1643
- yield this.writeProtocolV2Frame(currentTransport, frame, context, assertCurrentGeneration);
1727
+ yield this.writeProtocolV2Frame(uuid, currentTransport, frame, context, assertCurrentGeneration);
1644
1728
  }),
1645
1729
  readFrame: () => __awaiter(this, void 0, void 0, function* () {
1646
1730
  assertCurrentGeneration();
@@ -1661,10 +1745,12 @@ class ReactNativeBleTransport {
1661
1745
  };
1662
1746
  }
1663
1747
  getProtocolType(path) {
1664
- return this.deviceProtocol.get(path);
1748
+ return this.getActiveProtocol(path);
1665
1749
  }
1666
1750
  }
1667
1751
 
1752
+ exports.BLE_WRITE_PACKET_TIMEOUT_MS = BLE_WRITE_PACKET_TIMEOUT_MS;
1753
+ exports.BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD;
1668
1754
  exports.configureProtocolV2BleTuning = configureProtocolV2BleTuning;
1669
1755
  exports["default"] = ReactNativeBleTransport;
1670
1756
  exports.getFirmwareUploadWriteRetryType = getFirmwareUploadWriteRetryType;
package/jest.config.js CHANGED
@@ -2,4 +2,9 @@ module.exports = {
2
2
  preset: '../../jest.config.js',
3
3
  testEnvironment: 'node',
4
4
  modulePathIgnorePatterns: ['node_modules', '<rootDir>/dist'],
5
+ moduleNameMapper: {
6
+ // The workspace symlinks resolve to prebuilt dist outputs that can lag behind src.
7
+ '^@onekeyfe/hd-transport$': '<rootDir>/../hd-transport/src/index.ts',
8
+ '^@onekeyfe/hd-shared$': '<rootDir>/../shared/src/index.ts',
9
+ },
5
10
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-react-native",
3
- "version": "1.2.0-alpha.53",
3
+ "version": "1.2.0-alpha.54",
4
4
  "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -20,11 +20,11 @@
20
20
  "lint:fix": "eslint . --fix"
21
21
  },
22
22
  "dependencies": {
23
- "@onekeyfe/hd-core": "1.2.0-alpha.53",
24
- "@onekeyfe/hd-shared": "1.2.0-alpha.53",
25
- "@onekeyfe/hd-transport": "1.2.0-alpha.53",
23
+ "@onekeyfe/hd-core": "1.2.0-alpha.54",
24
+ "@onekeyfe/hd-shared": "1.2.0-alpha.54",
25
+ "@onekeyfe/hd-transport": "1.2.0-alpha.54",
26
26
  "@onekeyfe/react-native-ble-utils": "^0.1.6",
27
27
  "react-native-ble-plx": "3.5.1"
28
28
  },
29
- "gitHead": "196b7301f81d730460045c65c90d22706434b04d"
29
+ "gitHead": "a382a4719286e0df04fe706ed13099020eb8e13f"
30
30
  }