@onekeyfe/hd-transport-react-native 1.2.0-alpha.69 → 1.2.0-alpha.70
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/BleTransport.d.ts +1 -1
- package/dist/BleTransport.d.ts.map +1 -1
- package/dist/bleStrategy.d.ts +0 -6
- package/dist/bleStrategy.d.ts.map +1 -1
- package/dist/constants.d.ts +1 -2
- package/dist/constants.d.ts.map +1 -1
- package/dist/index.d.ts +80 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +341 -210
- package/jest.config.js +5 -0
- package/package.json +5 -5
- package/src/BleTransport.ts +1 -1
- package/src/__tests__/bleStrategy.test.ts +7 -77
- package/src/__tests__/connectTimeout.test.ts +281 -0
- package/src/__tests__/protocolReprobe.test.ts +132 -0
- package/src/__tests__/protocolV1SchemaFixture.ts +39 -0
- package/src/__tests__/protocolV2Link.test.ts +28 -121
- package/src/__tests__/staleCallTimeout.test.ts +210 -0
- package/src/__tests__/writePacketTimeout.test.ts +305 -0
- package/src/bleStrategy.ts +10 -23
- package/src/constants.ts +1 -2
- package/src/index.ts +486 -229
package/dist/index.js
CHANGED
|
@@ -93,8 +93,7 @@ const onDeviceBondState = (bleMacAddress) => new Promise((resolve, reject) => {
|
|
|
93
93
|
|
|
94
94
|
const IOS_PACKET_LENGTH = 128;
|
|
95
95
|
const ANDROID_PACKET_LENGTH = 192;
|
|
96
|
-
const
|
|
97
|
-
const ANDROID_PROTOCOL_V2_PACKET_LENGTH = 514;
|
|
96
|
+
const ANDROID_DEFAULT_MTU = 23;
|
|
98
97
|
const ClassicServiceUUID = '00000001-0000-1000-8000-00805f9b34fb';
|
|
99
98
|
const OneKeyServices = {
|
|
100
99
|
classic: {
|
|
@@ -135,20 +134,15 @@ const isSameBleUuid = (left, right) => {
|
|
|
135
134
|
function hasWritableCapability(characteristic) {
|
|
136
135
|
return !!(characteristic.isWritableWithResponse || characteristic.isWritableWithoutResponse);
|
|
137
136
|
}
|
|
138
|
-
function resolveProtocolV2PacketCapacity({ platform, iosPacketLength =
|
|
139
|
-
if (
|
|
140
|
-
|
|
137
|
+
function resolveProtocolV2PacketCapacity({ platform, iosPacketLength = IOS_PACKET_LENGTH, androidPacketLength = ANDROID_PACKET_LENGTH, mtu, }) {
|
|
138
|
+
if (platform === 'ios') {
|
|
139
|
+
return iosPacketLength;
|
|
141
140
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (!characteristic.isWritableWithResponse)
|
|
148
|
-
return false;
|
|
149
|
-
if (!characteristic.isWritableWithoutResponse)
|
|
150
|
-
return true;
|
|
151
|
-
return requestedWithResponse === true || (platform === 'ios' && !highVolume);
|
|
141
|
+
if (platform === 'android') {
|
|
142
|
+
const payloadLength = Math.max((mtu !== null && mtu !== void 0 ? mtu : ANDROID_DEFAULT_MTU) - 3, 1);
|
|
143
|
+
return Math.min(androidPacketLength, payloadLength);
|
|
144
|
+
}
|
|
145
|
+
return androidPacketLength;
|
|
152
146
|
}
|
|
153
147
|
|
|
154
148
|
const timer = process.env.NODE_ENV === 'development'
|
|
@@ -208,6 +202,7 @@ const isHeaderChunk = (chunk) => {
|
|
|
208
202
|
class BleTransport {
|
|
209
203
|
constructor(device, writeCharacteristic, notifyCharacteristic) {
|
|
210
204
|
this.name = 'ReactNativeBleTransport';
|
|
205
|
+
this.mtuSize = 23;
|
|
211
206
|
this.id = device.id;
|
|
212
207
|
this.device = device;
|
|
213
208
|
this.writeCharacteristic = writeCharacteristic;
|
|
@@ -231,6 +226,7 @@ const FIRMWARE_UPLOAD_WRITE_BURST_SIZE = reactNative.Platform.OS === 'ios' ? 4 :
|
|
|
231
226
|
const FIRMWARE_UPLOAD_WRITE_PAUSE_MS = reactNative.Platform.OS === 'ios' ? 8 : 10;
|
|
232
227
|
const FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS = reactNative.Platform.OS === 'ios' ? 24 : 30;
|
|
233
228
|
const FIRMWARE_UPLOAD_WRITE_MAX_RETRIES = 8;
|
|
229
|
+
const IOS_PROTOCOL_V2_CONTROL_WRITE_DELAY_MS = 5;
|
|
234
230
|
const ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH = 192;
|
|
235
231
|
const FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY = reactNative.Platform.OS === 'ios' ? IOS_PACKET_LENGTH : ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH;
|
|
236
232
|
const ANDROID_GATT_CONGESTED_STATUS = 143;
|
|
@@ -279,12 +275,18 @@ const getFirmwareUploadWriteRetryType = (error) => {
|
|
|
279
275
|
const resolveFirmwareUploadRetryDelay = (attempt, baseDelayMs = 200, maxDelayMs = 1200) => Math.min(baseDelayMs * Math.pow(2, attempt), maxDelayMs);
|
|
280
276
|
const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
|
|
281
277
|
const PROTOCOL_V2_PROBE_TIMEOUT_MS = 10000;
|
|
278
|
+
const BLE_WRITE_PACKET_TIMEOUT_MS = 10000;
|
|
279
|
+
const WEDGED_WRITE_MESSAGE = 'BLE write timeout after';
|
|
280
|
+
const isWedgedWriteError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleWriteCharacteristicError &&
|
|
281
|
+
typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' &&
|
|
282
|
+
error.message.startsWith(WEDGED_WRITE_MESSAGE);
|
|
283
|
+
const BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
|
|
282
284
|
const DEVICE_SCAN_TIMEOUT_MS = 3000;
|
|
283
285
|
const IOS_NOTIFY_READY_DELAY_MS = 150;
|
|
284
286
|
const ANDROID_NOTIFY_READY_DELAY_MS = 300;
|
|
285
287
|
const DEFAULT_PROTOCOL_V2_BLE_TUNING = {
|
|
286
|
-
iosPacketLength:
|
|
287
|
-
androidPacketLength:
|
|
288
|
+
iosPacketLength: IOS_PACKET_LENGTH,
|
|
289
|
+
androidPacketLength: ANDROID_PACKET_LENGTH,
|
|
288
290
|
};
|
|
289
291
|
let protocolV2BleTuning = Object.assign({}, DEFAULT_PROTOCOL_V2_BLE_TUNING);
|
|
290
292
|
const normalizePositiveInteger = (value, fallback) => {
|
|
@@ -313,17 +315,25 @@ function inferProtocolHintFromDeviceName(name) {
|
|
|
313
315
|
function getDeviceDisplayName(device) {
|
|
314
316
|
return (device === null || device === void 0 ? void 0 : device.name) || (device === null || device === void 0 ? void 0 : device.localName) || null;
|
|
315
317
|
}
|
|
316
|
-
const
|
|
317
|
-
const
|
|
318
|
-
const BLE_MTU_REFRESH_THRESHOLD = 247;
|
|
319
|
-
const BLE_MTU_REFRESH_RETRY_DELAY_MS = 200;
|
|
320
|
-
const ANDROID_HIGH_PRIORITY_IDLE_MS = 1000;
|
|
321
|
-
const getRequestedBleMtu = () => reactNative.Platform.OS === 'android' ? ANDROID_REQUEST_MTU : IOS_REQUEST_MTU;
|
|
318
|
+
const ANDROID_REQUEST_MTU = 256;
|
|
319
|
+
const BLE_NATIVE_CONNECT_TIMEOUT_MS = 3000;
|
|
322
320
|
const connectOptions = {
|
|
323
|
-
requestMTU:
|
|
324
|
-
timeout:
|
|
321
|
+
requestMTU: ANDROID_REQUEST_MTU,
|
|
322
|
+
timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
|
|
325
323
|
refreshGatt: 'OnConnected',
|
|
326
324
|
};
|
|
325
|
+
const fallbackConnectOptions = {
|
|
326
|
+
timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
|
|
327
|
+
};
|
|
328
|
+
const BLE_CONNECT_TIMEOUT_MS = BLE_NATIVE_CONNECT_TIMEOUT_MS * 2 + 2000;
|
|
329
|
+
const BLE_GATT_SETUP_TIMEOUT_MS = 10000;
|
|
330
|
+
const PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = 3;
|
|
331
|
+
const BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
|
|
332
|
+
const CONNECT_TIMEOUT_MESSAGE = 'BLE connect timeout after';
|
|
333
|
+
const isConnectTimeoutError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleConnectedError &&
|
|
334
|
+
typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' &&
|
|
335
|
+
error.message.startsWith(CONNECT_TIMEOUT_MESSAGE);
|
|
336
|
+
const isNativeOperationTimeoutError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === reactNativeBlePlx.BleErrorCode.OperationTimedOut;
|
|
327
337
|
const tryToGetConfiguration = (device) => {
|
|
328
338
|
if (!device || !device.serviceUUIDs)
|
|
329
339
|
return null;
|
|
@@ -335,25 +345,23 @@ const tryToGetConfiguration = (device) => {
|
|
|
335
345
|
return null;
|
|
336
346
|
return infos;
|
|
337
347
|
};
|
|
338
|
-
const
|
|
339
|
-
if (reactNative.Platform.OS !== '
|
|
348
|
+
const requestAndroidMtu = (device) => __awaiter(void 0, void 0, void 0, function* () {
|
|
349
|
+
if (reactNative.Platform.OS !== 'android')
|
|
340
350
|
return device;
|
|
341
351
|
try {
|
|
342
|
-
const mtuDevice = yield device.requestMTU(
|
|
352
|
+
const mtuDevice = yield device.requestMTU(ANDROID_REQUEST_MTU);
|
|
353
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] MTU configured', {
|
|
354
|
+
deviceId: device.id,
|
|
355
|
+
requested: ANDROID_REQUEST_MTU,
|
|
356
|
+
actual: mtuDevice.mtu,
|
|
357
|
+
});
|
|
343
358
|
return mtuDevice;
|
|
344
359
|
}
|
|
345
360
|
catch (error) {
|
|
346
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] MTU
|
|
347
|
-
platform: reactNative.Platform.OS,
|
|
348
|
-
stage,
|
|
349
|
-
attempt,
|
|
350
|
-
actual: device.mtu,
|
|
351
|
-
error: error instanceof Error ? error.message : String(error),
|
|
352
|
-
});
|
|
361
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android MTU request failed:', error);
|
|
353
362
|
return device;
|
|
354
363
|
}
|
|
355
364
|
});
|
|
356
|
-
const resolveNegotiatedMtu = (device) => requestNegotiatedMtu(device, 'connected', 0);
|
|
357
365
|
function remapError(error) {
|
|
358
366
|
var _a;
|
|
359
367
|
if (error instanceof reactNativeBlePlx.BleError) {
|
|
@@ -383,7 +391,12 @@ class ReactNativeBleTransport {
|
|
|
383
391
|
this.runPromiseDeviceId = null;
|
|
384
392
|
this.firmwareUploadWriteRecoveryIds = new Set();
|
|
385
393
|
this.deviceProtocol = new Map();
|
|
394
|
+
this.probingProtocols = new Map();
|
|
395
|
+
this.writeTimeoutCounts = new Map();
|
|
396
|
+
this.connectionSetupTimeoutCounts = new Map();
|
|
386
397
|
this.deviceProtocolHints = new Map();
|
|
398
|
+
this.sessionProtocols = new Map();
|
|
399
|
+
this.protocolReprobeFailures = new Map();
|
|
387
400
|
this.protocolV2Assemblers = new Map();
|
|
388
401
|
this.protocolV2FrameQueues = new Map();
|
|
389
402
|
this.protocolV2FramePromises = new Map();
|
|
@@ -410,9 +423,6 @@ class ReactNativeBleTransport {
|
|
|
410
423
|
});
|
|
411
424
|
this.monitorTokens = new Map();
|
|
412
425
|
this.disconnectEventTokens = new Map();
|
|
413
|
-
this.protocolV2HighVolumeLogSignatures = new Map();
|
|
414
|
-
this.androidHighPriorityDevices = new Set();
|
|
415
|
-
this.androidPriorityResetTimers = new Map();
|
|
416
426
|
this.nextMonitorToken = 1;
|
|
417
427
|
this.scanTimeout = (_a = options.scanTimeout) !== null && _a !== void 0 ? _a : DEVICE_SCAN_TIMEOUT_MS;
|
|
418
428
|
}
|
|
@@ -578,19 +588,19 @@ class ReactNativeBleTransport {
|
|
|
578
588
|
const isConnected = yield device.isConnected().catch(() => false);
|
|
579
589
|
if (!isConnected) {
|
|
580
590
|
try {
|
|
581
|
-
device = yield device.connect(connectOptions);
|
|
591
|
+
device = yield this.connectWithTimeout(uuid, () => device.connect(connectOptions));
|
|
582
592
|
}
|
|
583
593
|
catch (e) {
|
|
584
594
|
if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
|
|
585
595
|
e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
|
|
586
|
-
device = yield device.connect();
|
|
596
|
+
device = yield this.connectWithTimeout(uuid, () => device.connect());
|
|
587
597
|
}
|
|
588
598
|
else if (e.errorCode !== reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
|
|
589
599
|
throw e;
|
|
590
600
|
}
|
|
591
601
|
}
|
|
592
602
|
}
|
|
593
|
-
const { writeCharacteristic, notifyCharacteristic } = yield this.
|
|
603
|
+
const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, device);
|
|
594
604
|
transport.device = device;
|
|
595
605
|
transport.writeCharacteristic = writeCharacteristic;
|
|
596
606
|
transport.notifyCharacteristic = notifyCharacteristic;
|
|
@@ -727,9 +737,11 @@ class ReactNativeBleTransport {
|
|
|
727
737
|
}
|
|
728
738
|
installTransportForAcquire(uuid, device, characteristics) {
|
|
729
739
|
return __awaiter(this, void 0, void 0, function* () {
|
|
730
|
-
const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.
|
|
740
|
+
const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristicsWithTimeout(uuid, device));
|
|
731
741
|
const transport$1 = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
|
|
732
|
-
|
|
742
|
+
if (reactNative.Platform.OS === 'android') {
|
|
743
|
+
transport$1.mtuSize = typeof device.mtu === 'number' ? device.mtu : transport$1.mtuSize;
|
|
744
|
+
}
|
|
733
745
|
const monitorToken = this.nextMonitorToken;
|
|
734
746
|
this.nextMonitorToken += 1;
|
|
735
747
|
const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
|
|
@@ -738,7 +750,6 @@ class ReactNativeBleTransport {
|
|
|
738
750
|
this.monitorTokens.set(uuid, monitorToken);
|
|
739
751
|
transport$1.notifySubscription = this._monitorCharacteristic(transport$1.notifyCharacteristic, uuid, monitorToken, notifyTransactionId);
|
|
740
752
|
transportCache[uuid] = transport$1;
|
|
741
|
-
this.protocolV2HighVolumeLogSignatures.set(uuid, new Set());
|
|
742
753
|
this.protocolV2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES));
|
|
743
754
|
if (reactNative.Platform.OS === 'ios') {
|
|
744
755
|
yield new Promise(resolve => {
|
|
@@ -748,31 +759,6 @@ class ReactNativeBleTransport {
|
|
|
748
759
|
else if (reactNative.Platform.OS === 'android') {
|
|
749
760
|
yield delay(ANDROID_NOTIFY_READY_DELAY_MS);
|
|
750
761
|
}
|
|
751
|
-
const initialMtu = transport$1.mtuSize;
|
|
752
|
-
let refreshAttempts = 0;
|
|
753
|
-
if ((reactNative.Platform.OS === 'ios' || reactNative.Platform.OS === 'android') &&
|
|
754
|
-
(typeof transport$1.mtuSize !== 'number' || transport$1.mtuSize < BLE_MTU_REFRESH_THRESHOLD)) {
|
|
755
|
-
refreshAttempts += 1;
|
|
756
|
-
let refreshedDevice = yield requestNegotiatedMtu(transport$1.device, 'servicesAndNotifyReady', 1);
|
|
757
|
-
transport$1.device = refreshedDevice;
|
|
758
|
-
transport$1.mtuSize =
|
|
759
|
-
typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport$1.mtuSize;
|
|
760
|
-
if (typeof transport$1.mtuSize !== 'number' || transport$1.mtuSize < BLE_MTU_REFRESH_THRESHOLD) {
|
|
761
|
-
yield delay(BLE_MTU_REFRESH_RETRY_DELAY_MS);
|
|
762
|
-
refreshAttempts += 1;
|
|
763
|
-
refreshedDevice = yield requestNegotiatedMtu(transport$1.device, 'servicesAndNotifyReady', 2);
|
|
764
|
-
transport$1.device = refreshedDevice;
|
|
765
|
-
transport$1.mtuSize =
|
|
766
|
-
typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport$1.mtuSize;
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE MTU ready', {
|
|
770
|
-
platform: reactNative.Platform.OS,
|
|
771
|
-
requested: getRequestedBleMtu(),
|
|
772
|
-
initial: initialMtu,
|
|
773
|
-
actual: transport$1.mtuSize,
|
|
774
|
-
refreshAttempts,
|
|
775
|
-
});
|
|
776
762
|
return transport$1;
|
|
777
763
|
});
|
|
778
764
|
}
|
|
@@ -834,14 +820,17 @@ class ReactNativeBleTransport {
|
|
|
834
820
|
if (!device) {
|
|
835
821
|
Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device: ', uuid);
|
|
836
822
|
try {
|
|
837
|
-
device = yield blePlxManager.connectToDevice(uuid, connectOptions);
|
|
823
|
+
device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, connectOptions));
|
|
838
824
|
}
|
|
839
825
|
catch (e) {
|
|
840
826
|
Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device has error: ', e);
|
|
827
|
+
if (isConnectTimeoutError(e)) {
|
|
828
|
+
throw e;
|
|
829
|
+
}
|
|
841
830
|
if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
|
|
842
831
|
e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
|
|
843
832
|
Log === null || Log === void 0 ? void 0 : Log.debug('first try to reconnect without params');
|
|
844
|
-
device = yield blePlxManager.connectToDevice(uuid);
|
|
833
|
+
device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, fallbackConnectOptions));
|
|
845
834
|
}
|
|
846
835
|
else if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
|
|
847
836
|
Log === null || Log === void 0 ? void 0 : Log.debug('device already connected');
|
|
@@ -857,23 +846,27 @@ class ReactNativeBleTransport {
|
|
|
857
846
|
}
|
|
858
847
|
if (!(yield device.isConnected())) {
|
|
859
848
|
Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device: ', uuid);
|
|
849
|
+
const disconnectedDevice = device;
|
|
860
850
|
try {
|
|
861
|
-
device = yield
|
|
851
|
+
device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(connectOptions));
|
|
862
852
|
}
|
|
863
853
|
catch (e) {
|
|
864
854
|
Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device has error: ', e);
|
|
855
|
+
if (isConnectTimeoutError(e)) {
|
|
856
|
+
throw e;
|
|
857
|
+
}
|
|
865
858
|
if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
|
|
866
859
|
e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
|
|
867
860
|
Log === null || Log === void 0 ? void 0 : Log.debug('second try to reconnect without params');
|
|
868
861
|
try {
|
|
869
|
-
device = yield
|
|
862
|
+
device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
|
|
870
863
|
}
|
|
871
864
|
catch (e) {
|
|
872
865
|
Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect error: ', e);
|
|
873
866
|
if (e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
|
|
874
867
|
Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect');
|
|
875
|
-
yield
|
|
876
|
-
device = yield
|
|
868
|
+
yield disconnectedDevice.cancelConnection();
|
|
869
|
+
device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
|
|
877
870
|
}
|
|
878
871
|
}
|
|
879
872
|
}
|
|
@@ -882,9 +875,9 @@ class ReactNativeBleTransport {
|
|
|
882
875
|
}
|
|
883
876
|
}
|
|
884
877
|
}
|
|
885
|
-
device = yield
|
|
878
|
+
device = yield requestAndroidMtu(device);
|
|
886
879
|
const acquiredDevice = device;
|
|
887
|
-
const { writeCharacteristic, notifyCharacteristic } = yield this.
|
|
880
|
+
const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, acquiredDevice);
|
|
888
881
|
const protocolHint = expectedProtocol
|
|
889
882
|
? undefined
|
|
890
883
|
: (_b = (_a = input.protocolHint) !== null && _a !== void 0 ? _a : this.deviceProtocolHints.get(uuid)) !== null && _b !== void 0 ? _b : inferProtocolHintFromDeviceName(getDeviceDisplayName(acquiredDevice));
|
|
@@ -904,7 +897,7 @@ class ReactNativeBleTransport {
|
|
|
904
897
|
if (!currentTransport) {
|
|
905
898
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
|
|
906
899
|
}
|
|
907
|
-
this.attachDisconnectSubscription(currentTransport,
|
|
900
|
+
this.attachDisconnectSubscription(currentTransport, acquiredDevice, uuid);
|
|
908
901
|
return { uuid, protocolType };
|
|
909
902
|
}
|
|
910
903
|
catch (error) {
|
|
@@ -929,7 +922,7 @@ class ReactNativeBleTransport {
|
|
|
929
922
|
Log === null || Log === void 0 ? void 0 : Log.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
|
|
930
923
|
return;
|
|
931
924
|
}
|
|
932
|
-
if (this.
|
|
925
|
+
if (this.getActiveProtocol(uuid) === 'V2') {
|
|
933
926
|
let errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
|
|
934
927
|
if ((_a = error.reason) === null || _a === void 0 ? void 0 : _a.includes('The connection has timed out unexpectedly')) {
|
|
935
928
|
errorCode = hdShared.HardwareErrorCode.BleTimeoutError;
|
|
@@ -980,7 +973,7 @@ class ReactNativeBleTransport {
|
|
|
980
973
|
}
|
|
981
974
|
try {
|
|
982
975
|
const data = buffer.Buffer.from(c.value, 'base64');
|
|
983
|
-
const protocol = this.
|
|
976
|
+
const protocol = this.getActiveProtocol(uuid);
|
|
984
977
|
if (!protocol) {
|
|
985
978
|
Log === null || Log === void 0 ? void 0 : Log.debug('monitor data ignored before protocol detection: ', uuid);
|
|
986
979
|
return;
|
|
@@ -1008,7 +1001,7 @@ class ReactNativeBleTransport {
|
|
|
1008
1001
|
catch (error) {
|
|
1009
1002
|
Log === null || Log === void 0 ? void 0 : Log.debug('monitor data error: ', error);
|
|
1010
1003
|
const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
|
|
1011
|
-
if (this.
|
|
1004
|
+
if (this.getActiveProtocol(uuid) === 'V2') {
|
|
1012
1005
|
this.rejectProtocolV2Frames(uuid, notifyError);
|
|
1013
1006
|
}
|
|
1014
1007
|
else if (this.runPromiseDeviceId === uuid) {
|
|
@@ -1043,7 +1036,6 @@ class ReactNativeBleTransport {
|
|
|
1043
1036
|
this.resetProtocolV2Frames(uuid);
|
|
1044
1037
|
return Promise.resolve(true);
|
|
1045
1038
|
}
|
|
1046
|
-
yield this.restoreAndroidConnectionPriority(uuid, transport);
|
|
1047
1039
|
if (transport) {
|
|
1048
1040
|
if (this.monitorTokens.get(uuid) === transport.monitorToken) {
|
|
1049
1041
|
this.monitorTokens.delete(uuid);
|
|
@@ -1064,8 +1056,8 @@ class ReactNativeBleTransport {
|
|
|
1064
1056
|
}
|
|
1065
1057
|
delete transportCache[uuid];
|
|
1066
1058
|
}
|
|
1067
|
-
this.protocolV2HighVolumeLogSignatures.delete(uuid);
|
|
1068
1059
|
this.deviceProtocol.delete(uuid);
|
|
1060
|
+
this.probingProtocols.delete(uuid);
|
|
1069
1061
|
(_f = this.protocolV2Assemblers.get(uuid)) === null || _f === void 0 ? void 0 : _f.reset();
|
|
1070
1062
|
this.protocolV2Assemblers.delete(uuid);
|
|
1071
1063
|
this.resetProtocolV2Frames(uuid);
|
|
@@ -1114,8 +1106,19 @@ class ReactNativeBleTransport {
|
|
|
1114
1106
|
const transport = this.getCachedTransport(uuid);
|
|
1115
1107
|
const runPromise = hdShared.createDeferred();
|
|
1116
1108
|
runPromise.promise.catch(() => undefined);
|
|
1109
|
+
const supersededRunPromise = this.runPromise;
|
|
1110
|
+
if (supersededRunPromise) {
|
|
1111
|
+
supersededRunPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise));
|
|
1112
|
+
}
|
|
1117
1113
|
this.runPromise = runPromise;
|
|
1118
1114
|
this.runPromiseDeviceId = uuid;
|
|
1115
|
+
const releaseOwnershipIfCurrent = () => {
|
|
1116
|
+
if (this.runPromise === runPromise) {
|
|
1117
|
+
this.runPromise = null;
|
|
1118
|
+
this.runPromiseDeviceId = null;
|
|
1119
|
+
}
|
|
1120
|
+
};
|
|
1121
|
+
const isCurrentOwner = () => this.runPromise === runPromise;
|
|
1119
1122
|
const messages = this._messages;
|
|
1120
1123
|
const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
|
|
1121
1124
|
let timeout;
|
|
@@ -1136,6 +1139,9 @@ class ReactNativeBleTransport {
|
|
|
1136
1139
|
}
|
|
1137
1140
|
catch (e) {
|
|
1138
1141
|
onError(e);
|
|
1142
|
+
if (isWedgedWriteError(e)) {
|
|
1143
|
+
throw e;
|
|
1144
|
+
}
|
|
1139
1145
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
|
|
1140
1146
|
}
|
|
1141
1147
|
}
|
|
@@ -1163,6 +1169,9 @@ class ReactNativeBleTransport {
|
|
|
1163
1169
|
}
|
|
1164
1170
|
catch (e) {
|
|
1165
1171
|
onError(e);
|
|
1172
|
+
if (isWedgedWriteError(e)) {
|
|
1173
|
+
throw e;
|
|
1174
|
+
}
|
|
1166
1175
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
|
|
1167
1176
|
}
|
|
1168
1177
|
}
|
|
@@ -1173,8 +1182,8 @@ class ReactNativeBleTransport {
|
|
|
1173
1182
|
});
|
|
1174
1183
|
}
|
|
1175
1184
|
if (name === 'EmmcFileWrite') {
|
|
1176
|
-
yield writeChunkedData(buffers, data => transport.writeWithRetry(
|
|
1177
|
-
|
|
1185
|
+
yield writeChunkedData(buffers, data => this.writeBlePacket(uuid, data, payload => transport.writeWithRetry(payload), isCurrentOwner), e => {
|
|
1186
|
+
releaseOwnershipIfCurrent();
|
|
1178
1187
|
Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
|
|
1179
1188
|
});
|
|
1180
1189
|
}
|
|
@@ -1190,7 +1199,7 @@ class ReactNativeBleTransport {
|
|
|
1190
1199
|
let attempt = 0;
|
|
1191
1200
|
while (true) {
|
|
1192
1201
|
try {
|
|
1193
|
-
yield transport.writeWithRetry(
|
|
1202
|
+
yield this.writeBlePacket(uuid, data, payload => transport.writeWithRetry(payload), isCurrentOwner);
|
|
1194
1203
|
return;
|
|
1195
1204
|
}
|
|
1196
1205
|
catch (error) {
|
|
@@ -1209,7 +1218,7 @@ class ReactNativeBleTransport {
|
|
|
1209
1218
|
}
|
|
1210
1219
|
}
|
|
1211
1220
|
}), e => {
|
|
1212
|
-
|
|
1221
|
+
releaseOwnershipIfCurrent();
|
|
1213
1222
|
Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
|
|
1214
1223
|
});
|
|
1215
1224
|
}
|
|
@@ -1218,16 +1227,16 @@ class ReactNativeBleTransport {
|
|
|
1218
1227
|
const outData = o.toString('base64');
|
|
1219
1228
|
try {
|
|
1220
1229
|
const shouldUseWriteWithResponse = reactNative.Platform.OS === 'ios' && transport.writeCharacteristic.isWritableWithResponse;
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
else {
|
|
1225
|
-
yield transport.writeCharacteristic.writeWithoutResponse(outData);
|
|
1226
|
-
}
|
|
1230
|
+
yield this.writeBlePacket(uuid, outData, payload => shouldUseWriteWithResponse
|
|
1231
|
+
? transport.writeCharacteristic.writeWithResponse(payload)
|
|
1232
|
+
: transport.writeCharacteristic.writeWithoutResponse(payload), isCurrentOwner);
|
|
1227
1233
|
}
|
|
1228
1234
|
catch (e) {
|
|
1229
1235
|
Log === null || Log === void 0 ? void 0 : Log.debug('writeCharacteristic write error: ', e);
|
|
1230
|
-
|
|
1236
|
+
releaseOwnershipIfCurrent();
|
|
1237
|
+
if (isWedgedWriteError(e)) {
|
|
1238
|
+
throw e;
|
|
1239
|
+
}
|
|
1231
1240
|
if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected) {
|
|
1232
1241
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded);
|
|
1233
1242
|
}
|
|
@@ -1267,7 +1276,9 @@ class ReactNativeBleTransport {
|
|
|
1267
1276
|
Log === null || Log === void 0 ? void 0 : Log.error('call error: ', e);
|
|
1268
1277
|
}
|
|
1269
1278
|
const isProbeTimeout = name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS;
|
|
1279
|
+
const isStaleCall = this.runPromise !== runPromise;
|
|
1270
1280
|
if (!isProbeTimeout &&
|
|
1281
|
+
!isStaleCall &&
|
|
1271
1282
|
(e === null || e === void 0 ? void 0 : e.errorCode) === hdShared.HardwareErrorCode.BleTimeoutError) {
|
|
1272
1283
|
yield this.disconnect(uuid);
|
|
1273
1284
|
}
|
|
@@ -1338,7 +1349,10 @@ class ReactNativeBleTransport {
|
|
|
1338
1349
|
delete transportCache[session];
|
|
1339
1350
|
}
|
|
1340
1351
|
this.deviceProtocol.delete(session);
|
|
1352
|
+
this.probingProtocols.delete(session);
|
|
1341
1353
|
this.deviceProtocolHints.delete(session);
|
|
1354
|
+
this.sessionProtocols.delete(session);
|
|
1355
|
+
this.protocolReprobeFailures.delete(session);
|
|
1342
1356
|
this.protocolV2Assemblers.delete(session);
|
|
1343
1357
|
this.resetProtocolV2Frames(session);
|
|
1344
1358
|
try {
|
|
@@ -1359,6 +1373,91 @@ class ReactNativeBleTransport {
|
|
|
1359
1373
|
this.runPromise = null;
|
|
1360
1374
|
this.runPromiseDeviceId = null;
|
|
1361
1375
|
}
|
|
1376
|
+
connectWithTimeout(uuid, connect) {
|
|
1377
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1378
|
+
let timer;
|
|
1379
|
+
let timedOut = false;
|
|
1380
|
+
const pending = connect();
|
|
1381
|
+
pending.catch(() => undefined);
|
|
1382
|
+
try {
|
|
1383
|
+
const result = yield Promise.race([
|
|
1384
|
+
pending,
|
|
1385
|
+
new Promise((_, reject) => {
|
|
1386
|
+
timer = setTimeout(() => {
|
|
1387
|
+
timedOut = true;
|
|
1388
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `BLE connect timeout after ${BLE_CONNECT_TIMEOUT_MS}ms for ${uuid}`));
|
|
1389
|
+
}, BLE_CONNECT_TIMEOUT_MS);
|
|
1390
|
+
}),
|
|
1391
|
+
]);
|
|
1392
|
+
return result;
|
|
1393
|
+
}
|
|
1394
|
+
catch (error) {
|
|
1395
|
+
if (timedOut || isNativeOperationTimeoutError(error)) {
|
|
1396
|
+
this.abandonStalledConnection(uuid, timedOut ? 'connect-backstop' : 'connect-native');
|
|
1397
|
+
}
|
|
1398
|
+
throw error;
|
|
1399
|
+
}
|
|
1400
|
+
finally {
|
|
1401
|
+
if (timer)
|
|
1402
|
+
clearTimeout(timer);
|
|
1403
|
+
}
|
|
1404
|
+
});
|
|
1405
|
+
}
|
|
1406
|
+
resolveCharacteristicsWithTimeout(uuid, device) {
|
|
1407
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1408
|
+
let timer;
|
|
1409
|
+
let timedOut = false;
|
|
1410
|
+
const pending = this.resolveCharacteristics(device);
|
|
1411
|
+
pending.catch(() => undefined);
|
|
1412
|
+
try {
|
|
1413
|
+
const result = yield Promise.race([
|
|
1414
|
+
pending,
|
|
1415
|
+
new Promise((_, reject) => {
|
|
1416
|
+
timer = setTimeout(() => {
|
|
1417
|
+
timedOut = true;
|
|
1418
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `BLE GATT setup timeout after ${BLE_GATT_SETUP_TIMEOUT_MS}ms for ${uuid}`));
|
|
1419
|
+
}, BLE_GATT_SETUP_TIMEOUT_MS);
|
|
1420
|
+
}),
|
|
1421
|
+
]);
|
|
1422
|
+
this.connectionSetupTimeoutCounts.delete(uuid);
|
|
1423
|
+
return result;
|
|
1424
|
+
}
|
|
1425
|
+
catch (error) {
|
|
1426
|
+
if (timedOut || isNativeOperationTimeoutError(error)) {
|
|
1427
|
+
this.abandonStalledConnection(uuid, timedOut ? 'gatt-backstop' : 'gatt-native');
|
|
1428
|
+
}
|
|
1429
|
+
throw error;
|
|
1430
|
+
}
|
|
1431
|
+
finally {
|
|
1432
|
+
if (timer)
|
|
1433
|
+
clearTimeout(timer);
|
|
1434
|
+
}
|
|
1435
|
+
});
|
|
1436
|
+
}
|
|
1437
|
+
abandonStalledConnection(uuid, stage) {
|
|
1438
|
+
var _a, _b;
|
|
1439
|
+
const timeouts = ((_a = this.connectionSetupTimeoutCounts.get(uuid)) !== null && _a !== void 0 ? _a : 0) + 1;
|
|
1440
|
+
this.connectionSetupTimeoutCounts.set(uuid, timeouts);
|
|
1441
|
+
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE setup timed out:', uuid, {
|
|
1442
|
+
stage,
|
|
1443
|
+
setupTimeoutsSinceSuccess: timeouts,
|
|
1444
|
+
});
|
|
1445
|
+
(_b = this.blePlxManager) === null || _b === void 0 ? void 0 : _b.cancelDeviceConnection(uuid).catch(() => {
|
|
1446
|
+
});
|
|
1447
|
+
const stalled = transportCache[uuid];
|
|
1448
|
+
if (stalled) {
|
|
1449
|
+
delete transportCache[uuid];
|
|
1450
|
+
}
|
|
1451
|
+
this.deviceProtocol.delete(uuid);
|
|
1452
|
+
this.probingProtocols.delete(uuid);
|
|
1453
|
+
this.protocolV2Assemblers.delete(uuid);
|
|
1454
|
+
this.resetProtocolV2Frames(uuid);
|
|
1455
|
+
if (timeouts >= BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD) {
|
|
1456
|
+
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE setup wedged repeatedly, resetting BLE manager');
|
|
1457
|
+
this.resetPlxManager();
|
|
1458
|
+
this.connectionSetupTimeoutCounts.delete(uuid);
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1362
1461
|
getCachedTransport(uuid) {
|
|
1363
1462
|
const transport = transportCache[uuid];
|
|
1364
1463
|
if (!transport) {
|
|
@@ -1366,6 +1465,82 @@ class ReactNativeBleTransport {
|
|
|
1366
1465
|
}
|
|
1367
1466
|
return transport;
|
|
1368
1467
|
}
|
|
1468
|
+
writeBlePacket(uuid, data, write, isCurrentOwner) {
|
|
1469
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1470
|
+
let timer;
|
|
1471
|
+
let timedOut = false;
|
|
1472
|
+
try {
|
|
1473
|
+
yield Promise.race([
|
|
1474
|
+
write(data),
|
|
1475
|
+
new Promise((_, reject) => {
|
|
1476
|
+
timer = setTimeout(() => {
|
|
1477
|
+
timedOut = true;
|
|
1478
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError, `BLE write timeout after ${BLE_WRITE_PACKET_TIMEOUT_MS}ms`));
|
|
1479
|
+
}, BLE_WRITE_PACKET_TIMEOUT_MS);
|
|
1480
|
+
}),
|
|
1481
|
+
]);
|
|
1482
|
+
this.writeTimeoutCounts.delete(uuid);
|
|
1483
|
+
}
|
|
1484
|
+
catch (error) {
|
|
1485
|
+
if (timedOut) {
|
|
1486
|
+
if (isCurrentOwner && !isCurrentOwner()) {
|
|
1487
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] stale BLE write timed out, link kept:', uuid);
|
|
1488
|
+
}
|
|
1489
|
+
else {
|
|
1490
|
+
this.tearDownWedgedLink(uuid);
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
throw error;
|
|
1494
|
+
}
|
|
1495
|
+
finally {
|
|
1496
|
+
if (timer)
|
|
1497
|
+
clearTimeout(timer);
|
|
1498
|
+
}
|
|
1499
|
+
});
|
|
1500
|
+
}
|
|
1501
|
+
tearDownWedgedLink(uuid) {
|
|
1502
|
+
var _a;
|
|
1503
|
+
const timeouts = ((_a = this.writeTimeoutCounts.get(uuid)) !== null && _a !== void 0 ? _a : 0) + 1;
|
|
1504
|
+
this.writeTimeoutCounts.set(uuid, timeouts);
|
|
1505
|
+
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE write timed out, tearing down link:', uuid, {
|
|
1506
|
+
consecutiveWriteTimeouts: timeouts,
|
|
1507
|
+
});
|
|
1508
|
+
const wedged = transportCache[uuid];
|
|
1509
|
+
this.disconnect(uuid).catch(error => {
|
|
1510
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] wedged link teardown failed (ignored):', error);
|
|
1511
|
+
});
|
|
1512
|
+
if (wedged && transportCache[uuid] === wedged) {
|
|
1513
|
+
delete transportCache[uuid];
|
|
1514
|
+
}
|
|
1515
|
+
this.deviceProtocol.delete(uuid);
|
|
1516
|
+
this.probingProtocols.delete(uuid);
|
|
1517
|
+
this.protocolV2Assemblers.delete(uuid);
|
|
1518
|
+
this.resetProtocolV2Frames(uuid);
|
|
1519
|
+
if (timeouts >= BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD) {
|
|
1520
|
+
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE writes wedged repeatedly, resetting BLE manager');
|
|
1521
|
+
this.resetPlxManager();
|
|
1522
|
+
this.writeTimeoutCounts.delete(uuid);
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
resetPlxManager() {
|
|
1526
|
+
const manager = this.blePlxManager;
|
|
1527
|
+
this.blePlxManager = undefined;
|
|
1528
|
+
Object.keys(transportCache).forEach(key => {
|
|
1529
|
+
delete transportCache[key];
|
|
1530
|
+
});
|
|
1531
|
+
this.deviceProtocol.clear();
|
|
1532
|
+
this.probingProtocols.clear();
|
|
1533
|
+
this.sessionProtocols.clear();
|
|
1534
|
+
this.protocolReprobeFailures.clear();
|
|
1535
|
+
this.monitorTokens.clear();
|
|
1536
|
+
this.protocolV2Assemblers.clear();
|
|
1537
|
+
try {
|
|
1538
|
+
manager === null || manager === void 0 ? void 0 : manager.destroy();
|
|
1539
|
+
}
|
|
1540
|
+
catch (error) {
|
|
1541
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE manager destroy failed (ignored):', error);
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1369
1544
|
createProtocolMismatchError(expected) {
|
|
1370
1545
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
|
|
1371
1546
|
}
|
|
@@ -1373,11 +1548,19 @@ class ReactNativeBleTransport {
|
|
|
1373
1548
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1 GetFeatures or Protocol V2 Ping');
|
|
1374
1549
|
}
|
|
1375
1550
|
clearProbeProtocol(uuid, protocol) {
|
|
1551
|
+
if (this.probingProtocols.get(uuid) === protocol) {
|
|
1552
|
+
this.probingProtocols.delete(uuid);
|
|
1553
|
+
}
|
|
1376
1554
|
if (this.deviceProtocol.get(uuid) === protocol) {
|
|
1377
1555
|
this.deviceProtocol.delete(uuid);
|
|
1378
1556
|
}
|
|
1379
1557
|
}
|
|
1558
|
+
getActiveProtocol(uuid) {
|
|
1559
|
+
var _a;
|
|
1560
|
+
return (_a = this.deviceProtocol.get(uuid)) !== null && _a !== void 0 ? _a : this.probingProtocols.get(uuid);
|
|
1561
|
+
}
|
|
1380
1562
|
detectProtocol(uuid, expectedProtocol, protocolHint, rebuildTransport) {
|
|
1563
|
+
var _a;
|
|
1381
1564
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1382
1565
|
if (reactNative.Platform.OS === 'ios' && expectedProtocol) {
|
|
1383
1566
|
this.deviceProtocol.set(uuid, expectedProtocol);
|
|
@@ -1391,6 +1574,7 @@ class ReactNativeBleTransport {
|
|
|
1391
1574
|
if (expectedProtocol === 'V1') {
|
|
1392
1575
|
if (yield this.probeProtocolV1(uuid)) {
|
|
1393
1576
|
this.deviceProtocol.set(uuid, 'V1');
|
|
1577
|
+
this.sessionProtocols.set(uuid, 'V1');
|
|
1394
1578
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
|
|
1395
1579
|
deviceId: uuid,
|
|
1396
1580
|
protocol: 'V1',
|
|
@@ -1403,6 +1587,7 @@ class ReactNativeBleTransport {
|
|
|
1403
1587
|
if (expectedProtocol === 'V2') {
|
|
1404
1588
|
if (yield this.probeProtocolV2(uuid)) {
|
|
1405
1589
|
this.deviceProtocol.set(uuid, 'V2');
|
|
1590
|
+
this.sessionProtocols.set(uuid, 'V2');
|
|
1406
1591
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
|
|
1407
1592
|
deviceId: uuid,
|
|
1408
1593
|
protocol: 'V2',
|
|
@@ -1412,7 +1597,13 @@ class ReactNativeBleTransport {
|
|
|
1412
1597
|
}
|
|
1413
1598
|
throw this.createProtocolMismatchError(expectedProtocol);
|
|
1414
1599
|
}
|
|
1415
|
-
const
|
|
1600
|
+
const sessionProtocol = this.sessionProtocols.get(uuid);
|
|
1601
|
+
const reprobeFailures = (_a = this.protocolReprobeFailures.get(uuid)) !== null && _a !== void 0 ? _a : 0;
|
|
1602
|
+
const fullProbeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
|
|
1603
|
+
const trustSessionProtocol = sessionProtocol !== undefined &&
|
|
1604
|
+
!protocolHint &&
|
|
1605
|
+
reprobeFailures < PROTOCOL_REPROBE_FALLBACK_ATTEMPTS;
|
|
1606
|
+
const probeOrder = trustSessionProtocol ? [sessionProtocol] : fullProbeOrder;
|
|
1416
1607
|
for (let i = 0; i < probeOrder.length; i += 1) {
|
|
1417
1608
|
const protocol = probeOrder[i];
|
|
1418
1609
|
if (i > 0) {
|
|
@@ -1427,6 +1618,8 @@ class ReactNativeBleTransport {
|
|
|
1427
1618
|
const detected = protocol === 'V1' ? yield this.probeProtocolV1(uuid) : yield this.probeProtocolV2(uuid);
|
|
1428
1619
|
if (detected) {
|
|
1429
1620
|
this.deviceProtocol.set(uuid, protocol);
|
|
1621
|
+
this.sessionProtocols.set(uuid, protocol);
|
|
1622
|
+
this.protocolReprobeFailures.delete(uuid);
|
|
1430
1623
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
|
|
1431
1624
|
deviceId: uuid,
|
|
1432
1625
|
protocol,
|
|
@@ -1435,7 +1628,14 @@ class ReactNativeBleTransport {
|
|
|
1435
1628
|
return protocol;
|
|
1436
1629
|
}
|
|
1437
1630
|
}
|
|
1631
|
+
if (trustSessionProtocol) {
|
|
1632
|
+
this.protocolReprobeFailures.set(uuid, reprobeFailures + 1);
|
|
1633
|
+
}
|
|
1634
|
+
else {
|
|
1635
|
+
this.protocolReprobeFailures.delete(uuid);
|
|
1636
|
+
}
|
|
1438
1637
|
this.deviceProtocol.delete(uuid);
|
|
1638
|
+
this.probingProtocols.delete(uuid);
|
|
1439
1639
|
throw this.createProtocolDetectionError();
|
|
1440
1640
|
});
|
|
1441
1641
|
}
|
|
@@ -1487,13 +1687,17 @@ class ReactNativeBleTransport {
|
|
|
1487
1687
|
return false;
|
|
1488
1688
|
}
|
|
1489
1689
|
try {
|
|
1490
|
-
this.
|
|
1690
|
+
this.probingProtocols.set(uuid, 'V1');
|
|
1491
1691
|
yield this.callProtocolV1(uuid, 'GetFeatures', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
|
|
1692
|
+
this.probingProtocols.delete(uuid);
|
|
1492
1693
|
return true;
|
|
1493
1694
|
}
|
|
1494
1695
|
catch (error) {
|
|
1495
1696
|
this.clearProbeProtocol(uuid, 'V1');
|
|
1496
1697
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe failed:', error);
|
|
1698
|
+
if (isWedgedWriteError(error)) {
|
|
1699
|
+
throw error;
|
|
1700
|
+
}
|
|
1497
1701
|
return false;
|
|
1498
1702
|
}
|
|
1499
1703
|
});
|
|
@@ -1504,7 +1708,7 @@ class ReactNativeBleTransport {
|
|
|
1504
1708
|
if (!this._messages || !this._messagesV2) {
|
|
1505
1709
|
return false;
|
|
1506
1710
|
}
|
|
1507
|
-
this.
|
|
1711
|
+
this.probingProtocols.set(uuid, 'V2');
|
|
1508
1712
|
(_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1509
1713
|
const detected = yield transport.probeProtocolV2({
|
|
1510
1714
|
call: (name, data, options) => this.callProtocolV2(uuid, name, data, options),
|
|
@@ -1520,6 +1724,9 @@ class ReactNativeBleTransport {
|
|
|
1520
1724
|
if (!detected) {
|
|
1521
1725
|
this.clearProbeProtocol(uuid, 'V2');
|
|
1522
1726
|
}
|
|
1727
|
+
else {
|
|
1728
|
+
this.probingProtocols.delete(uuid);
|
|
1729
|
+
}
|
|
1523
1730
|
return detected;
|
|
1524
1731
|
});
|
|
1525
1732
|
}
|
|
@@ -1591,14 +1798,10 @@ class ReactNativeBleTransport {
|
|
|
1591
1798
|
}
|
|
1592
1799
|
});
|
|
1593
1800
|
}
|
|
1594
|
-
writeProtocolV2Packet(transport, base64, context, assertCurrentGeneration) {
|
|
1801
|
+
writeProtocolV2Packet(uuid, transport, base64, context, assertCurrentGeneration) {
|
|
1595
1802
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1596
|
-
const shouldUseWriteWithResponse =
|
|
1597
|
-
|
|
1598
|
-
highVolume: context.highVolume,
|
|
1599
|
-
requestedWithResponse: context.writeWithResponse,
|
|
1600
|
-
characteristic: transport.writeCharacteristic,
|
|
1601
|
-
});
|
|
1803
|
+
const shouldUseWriteWithResponse = transport.writeCharacteristic.isWritableWithResponse &&
|
|
1804
|
+
(context.writeWithResponse === true || (reactNative.Platform.OS === 'ios' && !context.highVolume));
|
|
1602
1805
|
let attempt = 0;
|
|
1603
1806
|
for (;;) {
|
|
1604
1807
|
assertCurrentGeneration();
|
|
@@ -1606,12 +1809,17 @@ class ReactNativeBleTransport {
|
|
|
1606
1809
|
throw new Error(`Protocol V2 BLE write aborted for ${context.messageName}`);
|
|
1607
1810
|
}
|
|
1608
1811
|
try {
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1812
|
+
yield this.writeBlePacket(uuid, base64, payload => shouldUseWriteWithResponse
|
|
1813
|
+
? transport.writeCharacteristic.writeWithResponse(payload)
|
|
1814
|
+
: transport.writeCharacteristic.writeWithoutResponse(payload), () => {
|
|
1815
|
+
try {
|
|
1816
|
+
assertCurrentGeneration();
|
|
1817
|
+
return !context.signal.aborted;
|
|
1818
|
+
}
|
|
1819
|
+
catch (_a) {
|
|
1820
|
+
return false;
|
|
1821
|
+
}
|
|
1822
|
+
});
|
|
1615
1823
|
assertCurrentGeneration();
|
|
1616
1824
|
return;
|
|
1617
1825
|
}
|
|
@@ -1632,28 +1840,34 @@ class ReactNativeBleTransport {
|
|
|
1632
1840
|
}
|
|
1633
1841
|
});
|
|
1634
1842
|
}
|
|
1635
|
-
writeProtocolV2Frame(transport$1, frame, context, assertCurrentGeneration) {
|
|
1843
|
+
writeProtocolV2Frame(uuid, transport$1, frame, context, assertCurrentGeneration) {
|
|
1636
1844
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1637
1845
|
const tuning = getProtocolV2BleTuning();
|
|
1638
1846
|
const packetCapacity = resolveProtocolV2PacketCapacity({
|
|
1639
1847
|
platform: reactNative.Platform.OS,
|
|
1640
1848
|
iosPacketLength: tuning.iosPacketLength,
|
|
1641
1849
|
androidPacketLength: tuning.androidPacketLength,
|
|
1642
|
-
mtu: transport$1.mtuSize,
|
|
1850
|
+
mtu: reactNative.Platform.OS === 'android' ? transport$1.mtuSize : undefined,
|
|
1643
1851
|
});
|
|
1852
|
+
const initialDelayMs = reactNative.Platform.OS === 'ios' && !context.highVolume && frame.length <= packetCapacity
|
|
1853
|
+
? IOS_PROTOCOL_V2_CONTROL_WRITE_DELAY_MS
|
|
1854
|
+
: 0;
|
|
1644
1855
|
yield transport.writeProtocolV2BleFrame({
|
|
1645
1856
|
frame,
|
|
1646
1857
|
packetCapacity,
|
|
1647
1858
|
assertActive: assertCurrentGeneration,
|
|
1648
1859
|
signal: context.signal,
|
|
1649
1860
|
abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
|
|
1861
|
+
initialDelayMs,
|
|
1862
|
+
burstSize: FIRMWARE_UPLOAD_WRITE_BURST_SIZE,
|
|
1863
|
+
burstPauseMs: FIRMWARE_UPLOAD_WRITE_PAUSE_MS,
|
|
1864
|
+
flushDelayMs: FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS,
|
|
1650
1865
|
wait: delay,
|
|
1651
|
-
writePacket: packet => this.writeProtocolV2Packet(transport$1, buffer.Buffer.from(packet).toString('base64'), context, assertCurrentGeneration),
|
|
1866
|
+
writePacket: packet => this.writeProtocolV2Packet(uuid, transport$1, buffer.Buffer.from(packet).toString('base64'), context, assertCurrentGeneration),
|
|
1652
1867
|
});
|
|
1653
1868
|
});
|
|
1654
1869
|
}
|
|
1655
1870
|
callProtocolV2(uuid, name, data, options) {
|
|
1656
|
-
var _a;
|
|
1657
1871
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1658
1872
|
if (!this._messages || !this._messagesV2) {
|
|
1659
1873
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
@@ -1662,35 +1876,11 @@ class ReactNativeBleTransport {
|
|
|
1662
1876
|
const highVolumeWrite = transport.LogBlockCommand.has(name);
|
|
1663
1877
|
if (highVolumeWrite) {
|
|
1664
1878
|
const tuning = getProtocolV2BleTuning();
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
requestedWithResponse: options === null || options === void 0 ? void 0 : options.writeWithResponse,
|
|
1670
|
-
characteristic: currentTransport.writeCharacteristic,
|
|
1879
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume write configured', {
|
|
1880
|
+
name,
|
|
1881
|
+
writeMode: (options === null || options === void 0 ? void 0 : options.writeWithResponse) ? 'withResponse' : 'withoutResponse',
|
|
1882
|
+
packetCapacity: reactNative.Platform.OS === 'ios' ? tuning.iosPacketLength : tuning.androidPacketLength,
|
|
1671
1883
|
});
|
|
1672
|
-
const packetCapacity = resolveProtocolV2PacketCapacity({
|
|
1673
|
-
platform: reactNative.Platform.OS,
|
|
1674
|
-
iosPacketLength: tuning.iosPacketLength,
|
|
1675
|
-
androidPacketLength: tuning.androidPacketLength,
|
|
1676
|
-
mtu: currentTransport.mtuSize,
|
|
1677
|
-
});
|
|
1678
|
-
const writeMode = writeWithResponse ? 'withResponse' : 'withoutResponse';
|
|
1679
|
-
const logSignature = `${name}:${writeMode}:${String(currentTransport.mtuSize)}:${packetCapacity}`;
|
|
1680
|
-
const loggedSignatures = (_a = this.protocolV2HighVolumeLogSignatures.get(uuid)) !== null && _a !== void 0 ? _a : new Set();
|
|
1681
|
-
if (!loggedSignatures.has(logSignature)) {
|
|
1682
|
-
loggedSignatures.add(logSignature);
|
|
1683
|
-
this.protocolV2HighVolumeLogSignatures.set(uuid, loggedSignatures);
|
|
1684
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume write configured', {
|
|
1685
|
-
name,
|
|
1686
|
-
writeMode,
|
|
1687
|
-
reportedMtu: currentTransport.mtuSize,
|
|
1688
|
-
packetCapacity,
|
|
1689
|
-
});
|
|
1690
|
-
}
|
|
1691
|
-
}
|
|
1692
|
-
if (highVolumeWrite) {
|
|
1693
|
-
yield this.enableAndroidHighConnectionPriority(uuid);
|
|
1694
1884
|
}
|
|
1695
1885
|
try {
|
|
1696
1886
|
return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, callOptions);
|
|
@@ -1699,71 +1889,6 @@ class ReactNativeBleTransport {
|
|
|
1699
1889
|
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] Protocol V2 call error:', e);
|
|
1700
1890
|
throw e;
|
|
1701
1891
|
}
|
|
1702
|
-
finally {
|
|
1703
|
-
if (highVolumeWrite) {
|
|
1704
|
-
this.scheduleAndroidBalancedConnectionPriority(uuid);
|
|
1705
|
-
}
|
|
1706
|
-
}
|
|
1707
|
-
});
|
|
1708
|
-
}
|
|
1709
|
-
clearAndroidPriorityResetTimer(uuid) {
|
|
1710
|
-
const timerId = this.androidPriorityResetTimers.get(uuid);
|
|
1711
|
-
if (timerId !== undefined) {
|
|
1712
|
-
clearTimeout(timerId);
|
|
1713
|
-
this.androidPriorityResetTimers.delete(uuid);
|
|
1714
|
-
}
|
|
1715
|
-
}
|
|
1716
|
-
enableAndroidHighConnectionPriority(uuid) {
|
|
1717
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1718
|
-
if (reactNative.Platform.OS !== 'android')
|
|
1719
|
-
return;
|
|
1720
|
-
this.clearAndroidPriorityResetTimer(uuid);
|
|
1721
|
-
if (this.androidHighPriorityDevices.has(uuid))
|
|
1722
|
-
return;
|
|
1723
|
-
const transport = transportCache[uuid];
|
|
1724
|
-
if (!transport)
|
|
1725
|
-
return;
|
|
1726
|
-
try {
|
|
1727
|
-
transport.device = yield transport.device.requestConnectionPriority(reactNativeBlePlx.ConnectionPriority.High);
|
|
1728
|
-
this.androidHighPriorityDevices.add(uuid);
|
|
1729
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE connection priority changed', {
|
|
1730
|
-
priority: 'high',
|
|
1731
|
-
});
|
|
1732
|
-
}
|
|
1733
|
-
catch (error) {
|
|
1734
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE high priority request failed', {
|
|
1735
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1736
|
-
});
|
|
1737
|
-
}
|
|
1738
|
-
});
|
|
1739
|
-
}
|
|
1740
|
-
scheduleAndroidBalancedConnectionPriority(uuid) {
|
|
1741
|
-
if (reactNative.Platform.OS !== 'android' || !this.androidHighPriorityDevices.has(uuid))
|
|
1742
|
-
return;
|
|
1743
|
-
this.clearAndroidPriorityResetTimer(uuid);
|
|
1744
|
-
const timerId = setTimeout(() => {
|
|
1745
|
-
this.androidPriorityResetTimers.delete(uuid);
|
|
1746
|
-
this.restoreAndroidConnectionPriority(uuid, transportCache[uuid]).catch(error => Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE priority restore failed', error));
|
|
1747
|
-
}, ANDROID_HIGH_PRIORITY_IDLE_MS);
|
|
1748
|
-
this.androidPriorityResetTimers.set(uuid, timerId);
|
|
1749
|
-
}
|
|
1750
|
-
restoreAndroidConnectionPriority(uuid, transport) {
|
|
1751
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1752
|
-
this.clearAndroidPriorityResetTimer(uuid);
|
|
1753
|
-
if (reactNative.Platform.OS !== 'android' || !this.androidHighPriorityDevices.delete(uuid) || !transport) {
|
|
1754
|
-
return;
|
|
1755
|
-
}
|
|
1756
|
-
try {
|
|
1757
|
-
transport.device = yield transport.device.requestConnectionPriority(reactNativeBlePlx.ConnectionPriority.Balanced);
|
|
1758
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE connection priority changed', {
|
|
1759
|
-
priority: 'balanced',
|
|
1760
|
-
});
|
|
1761
|
-
}
|
|
1762
|
-
catch (error) {
|
|
1763
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE balanced priority request failed', {
|
|
1764
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1765
|
-
});
|
|
1766
|
-
}
|
|
1767
1892
|
});
|
|
1768
1893
|
}
|
|
1769
1894
|
createProtocolV2Adapter(uuid) {
|
|
@@ -1787,7 +1912,7 @@ class ReactNativeBleTransport {
|
|
|
1787
1912
|
writeFrame: (frame, context) => __awaiter(this, void 0, void 0, function* () {
|
|
1788
1913
|
assertCurrentGeneration();
|
|
1789
1914
|
const currentTransport = this.getCachedTransport(uuid);
|
|
1790
|
-
yield this.writeProtocolV2Frame(currentTransport, frame, context, assertCurrentGeneration);
|
|
1915
|
+
yield this.writeProtocolV2Frame(uuid, currentTransport, frame, context, assertCurrentGeneration);
|
|
1791
1916
|
}),
|
|
1792
1917
|
readFrame: () => __awaiter(this, void 0, void 0, function* () {
|
|
1793
1918
|
assertCurrentGeneration();
|
|
@@ -1808,10 +1933,16 @@ class ReactNativeBleTransport {
|
|
|
1808
1933
|
};
|
|
1809
1934
|
}
|
|
1810
1935
|
getProtocolType(path) {
|
|
1811
|
-
return this.
|
|
1936
|
+
return this.getActiveProtocol(path);
|
|
1812
1937
|
}
|
|
1813
1938
|
}
|
|
1814
1939
|
|
|
1940
|
+
exports.BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD;
|
|
1941
|
+
exports.BLE_CONNECT_TIMEOUT_MS = BLE_CONNECT_TIMEOUT_MS;
|
|
1942
|
+
exports.BLE_GATT_SETUP_TIMEOUT_MS = BLE_GATT_SETUP_TIMEOUT_MS;
|
|
1943
|
+
exports.BLE_WRITE_PACKET_TIMEOUT_MS = BLE_WRITE_PACKET_TIMEOUT_MS;
|
|
1944
|
+
exports.BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD;
|
|
1945
|
+
exports.PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = PROTOCOL_REPROBE_FALLBACK_ATTEMPTS;
|
|
1815
1946
|
exports.configureProtocolV2BleTuning = configureProtocolV2BleTuning;
|
|
1816
1947
|
exports["default"] = ReactNativeBleTransport;
|
|
1817
1948
|
exports.getFirmwareUploadWriteRetryType = getFirmwareUploadWriteRetryType;
|