@onekeyfe/hd-transport-react-native 1.2.0-alpha.56 → 1.2.0-alpha.62
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 +59 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +245 -84
- package/jest.config.js +5 -0
- package/package.json +5 -5
- package/src/BleTransport.ts +1 -1
- package/src/__tests__/BleTransport.test.ts +2 -2
- package/src/__tests__/connectTimeout.test.ts +192 -0
- package/src/__tests__/protocolV2Link.test.ts +41 -241
- package/src/__tests__/staleCallTimeout.test.ts +211 -0
- package/src/__tests__/writePacketTimeout.test.ts +306 -0
- package/src/index.ts +357 -83
- package/src/__tests__/enumerate.test.ts +0 -132
package/dist/index.js
CHANGED
|
@@ -210,7 +210,7 @@ class BleTransport {
|
|
|
210
210
|
}
|
|
211
211
|
writeWithRetry(data) {
|
|
212
212
|
return __awaiter(this, void 0, void 0, function* () {
|
|
213
|
-
yield this.writeCharacteristic.
|
|
213
|
+
yield this.writeCharacteristic.writeWithoutResponse(data);
|
|
214
214
|
});
|
|
215
215
|
}
|
|
216
216
|
}
|
|
@@ -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;
|
|
@@ -280,11 +285,21 @@ function getDeviceDisplayName(device) {
|
|
|
280
285
|
return (device === null || device === void 0 ? void 0 : device.name) || (device === null || device === void 0 ? void 0 : device.localName) || null;
|
|
281
286
|
}
|
|
282
287
|
const ANDROID_REQUEST_MTU = 256;
|
|
288
|
+
const BLE_NATIVE_CONNECT_TIMEOUT_MS = 3000;
|
|
283
289
|
const connectOptions = {
|
|
284
290
|
requestMTU: ANDROID_REQUEST_MTU,
|
|
285
|
-
timeout:
|
|
291
|
+
timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
|
|
286
292
|
refreshGatt: 'OnConnected',
|
|
287
293
|
};
|
|
294
|
+
const fallbackConnectOptions = {
|
|
295
|
+
timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
|
|
296
|
+
};
|
|
297
|
+
const BLE_CONNECT_TIMEOUT_MS = BLE_NATIVE_CONNECT_TIMEOUT_MS * 2 + 2000;
|
|
298
|
+
const BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
|
|
299
|
+
const CONNECT_TIMEOUT_MESSAGE = 'BLE connect timeout after';
|
|
300
|
+
const isConnectTimeoutError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleConnectedError &&
|
|
301
|
+
typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' &&
|
|
302
|
+
error.message.startsWith(CONNECT_TIMEOUT_MESSAGE);
|
|
288
303
|
const tryToGetConfiguration = (device) => {
|
|
289
304
|
if (!device || !device.serviceUUIDs)
|
|
290
305
|
return null;
|
|
@@ -342,6 +357,9 @@ class ReactNativeBleTransport {
|
|
|
342
357
|
this.runPromiseDeviceId = null;
|
|
343
358
|
this.firmwareUploadWriteRecoveryIds = new Set();
|
|
344
359
|
this.deviceProtocol = new Map();
|
|
360
|
+
this.probingProtocols = new Map();
|
|
361
|
+
this.writeTimeoutCounts = new Map();
|
|
362
|
+
this.connectTimeoutCounts = new Map();
|
|
345
363
|
this.deviceProtocolHints = new Map();
|
|
346
364
|
this.protocolV2Assemblers = new Map();
|
|
347
365
|
this.protocolV2FrameQueues = new Map();
|
|
@@ -534,12 +552,12 @@ class ReactNativeBleTransport {
|
|
|
534
552
|
const isConnected = yield device.isConnected().catch(() => false);
|
|
535
553
|
if (!isConnected) {
|
|
536
554
|
try {
|
|
537
|
-
device = yield device.connect(connectOptions);
|
|
555
|
+
device = yield this.connectWithTimeout(uuid, () => device.connect(connectOptions));
|
|
538
556
|
}
|
|
539
557
|
catch (e) {
|
|
540
558
|
if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
|
|
541
559
|
e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
|
|
542
|
-
device = yield device.connect();
|
|
560
|
+
device = yield this.connectWithTimeout(uuid, () => device.connect());
|
|
543
561
|
}
|
|
544
562
|
else if (e.errorCode !== reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
|
|
545
563
|
throw e;
|
|
@@ -615,17 +633,12 @@ class ReactNativeBleTransport {
|
|
|
615
633
|
return;
|
|
616
634
|
}
|
|
617
635
|
const displayName = getDeviceDisplayName(device);
|
|
618
|
-
const
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
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
|
-
});
|
|
636
|
+
const isOneKey = hdShared.isOnekeyBluetoothDevice({
|
|
637
|
+
id: device === null || device === void 0 ? void 0 : device.id,
|
|
638
|
+
name: device === null || device === void 0 ? void 0 : device.name,
|
|
639
|
+
localName: device === null || device === void 0 ? void 0 : device.localName,
|
|
640
|
+
serviceUuids: device === null || device === void 0 ? void 0 : device.serviceUUIDs,
|
|
641
|
+
});
|
|
629
642
|
if (isOneKey) {
|
|
630
643
|
addDevice(device);
|
|
631
644
|
}
|
|
@@ -643,15 +656,12 @@ class ReactNativeBleTransport {
|
|
|
643
656
|
const localName = 'localName' in device && typeof device.localName === 'string'
|
|
644
657
|
? device.localName
|
|
645
658
|
: null;
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
localName,
|
|
653
|
-
serviceUuids: device.serviceUUIDs,
|
|
654
|
-
})) {
|
|
659
|
+
if (hdShared.isOnekeyBluetoothDevice({
|
|
660
|
+
id: device.id,
|
|
661
|
+
name: device.name,
|
|
662
|
+
localName,
|
|
663
|
+
serviceUuids: device.serviceUUIDs,
|
|
664
|
+
})) {
|
|
655
665
|
Log === null || Log === void 0 ? void 0 : Log.debug('search connected peripheral: ', device.id);
|
|
656
666
|
addDevice(device);
|
|
657
667
|
}
|
|
@@ -766,14 +776,17 @@ class ReactNativeBleTransport {
|
|
|
766
776
|
if (!device) {
|
|
767
777
|
Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device: ', uuid);
|
|
768
778
|
try {
|
|
769
|
-
device = yield blePlxManager.connectToDevice(uuid, connectOptions);
|
|
779
|
+
device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, connectOptions));
|
|
770
780
|
}
|
|
771
781
|
catch (e) {
|
|
772
782
|
Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device has error: ', e);
|
|
783
|
+
if (isConnectTimeoutError(e)) {
|
|
784
|
+
throw e;
|
|
785
|
+
}
|
|
773
786
|
if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
|
|
774
787
|
e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
|
|
775
788
|
Log === null || Log === void 0 ? void 0 : Log.debug('first try to reconnect without params');
|
|
776
|
-
device = yield blePlxManager.connectToDevice(uuid);
|
|
789
|
+
device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, fallbackConnectOptions));
|
|
777
790
|
}
|
|
778
791
|
else if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
|
|
779
792
|
Log === null || Log === void 0 ? void 0 : Log.debug('device already connected');
|
|
@@ -789,23 +802,27 @@ class ReactNativeBleTransport {
|
|
|
789
802
|
}
|
|
790
803
|
if (!(yield device.isConnected())) {
|
|
791
804
|
Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device: ', uuid);
|
|
805
|
+
const disconnectedDevice = device;
|
|
792
806
|
try {
|
|
793
|
-
device = yield
|
|
807
|
+
device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(connectOptions));
|
|
794
808
|
}
|
|
795
809
|
catch (e) {
|
|
796
810
|
Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device has error: ', e);
|
|
811
|
+
if (isConnectTimeoutError(e)) {
|
|
812
|
+
throw e;
|
|
813
|
+
}
|
|
797
814
|
if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
|
|
798
815
|
e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
|
|
799
816
|
Log === null || Log === void 0 ? void 0 : Log.debug('second try to reconnect without params');
|
|
800
817
|
try {
|
|
801
|
-
device = yield
|
|
818
|
+
device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
|
|
802
819
|
}
|
|
803
820
|
catch (e) {
|
|
804
821
|
Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect error: ', e);
|
|
805
822
|
if (e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
|
|
806
823
|
Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect');
|
|
807
|
-
yield
|
|
808
|
-
device = yield
|
|
824
|
+
yield disconnectedDevice.cancelConnection();
|
|
825
|
+
device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
|
|
809
826
|
}
|
|
810
827
|
}
|
|
811
828
|
}
|
|
@@ -861,7 +878,7 @@ class ReactNativeBleTransport {
|
|
|
861
878
|
Log === null || Log === void 0 ? void 0 : Log.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
|
|
862
879
|
return;
|
|
863
880
|
}
|
|
864
|
-
if (this.
|
|
881
|
+
if (this.getActiveProtocol(uuid) === 'V2') {
|
|
865
882
|
let errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
|
|
866
883
|
if ((_a = error.reason) === null || _a === void 0 ? void 0 : _a.includes('The connection has timed out unexpectedly')) {
|
|
867
884
|
errorCode = hdShared.HardwareErrorCode.BleTimeoutError;
|
|
@@ -912,7 +929,7 @@ class ReactNativeBleTransport {
|
|
|
912
929
|
}
|
|
913
930
|
try {
|
|
914
931
|
const data = buffer.Buffer.from(c.value, 'base64');
|
|
915
|
-
const protocol = this.
|
|
932
|
+
const protocol = this.getActiveProtocol(uuid);
|
|
916
933
|
if (!protocol) {
|
|
917
934
|
Log === null || Log === void 0 ? void 0 : Log.debug('monitor data ignored before protocol detection: ', uuid);
|
|
918
935
|
return;
|
|
@@ -940,7 +957,7 @@ class ReactNativeBleTransport {
|
|
|
940
957
|
catch (error) {
|
|
941
958
|
Log === null || Log === void 0 ? void 0 : Log.debug('monitor data error: ', error);
|
|
942
959
|
const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
|
|
943
|
-
if (this.
|
|
960
|
+
if (this.getActiveProtocol(uuid) === 'V2') {
|
|
944
961
|
this.rejectProtocolV2Frames(uuid, notifyError);
|
|
945
962
|
}
|
|
946
963
|
else if (this.runPromiseDeviceId === uuid) {
|
|
@@ -996,6 +1013,7 @@ class ReactNativeBleTransport {
|
|
|
996
1013
|
delete transportCache[uuid];
|
|
997
1014
|
}
|
|
998
1015
|
this.deviceProtocol.delete(uuid);
|
|
1016
|
+
this.probingProtocols.delete(uuid);
|
|
999
1017
|
(_f = this.protocolV2Assemblers.get(uuid)) === null || _f === void 0 ? void 0 : _f.reset();
|
|
1000
1018
|
this.protocolV2Assemblers.delete(uuid);
|
|
1001
1019
|
this.resetProtocolV2Frames(uuid);
|
|
@@ -1044,8 +1062,19 @@ class ReactNativeBleTransport {
|
|
|
1044
1062
|
const transport = this.getCachedTransport(uuid);
|
|
1045
1063
|
const runPromise = hdShared.createDeferred();
|
|
1046
1064
|
runPromise.promise.catch(() => undefined);
|
|
1065
|
+
const supersededRunPromise = this.runPromise;
|
|
1066
|
+
if (supersededRunPromise) {
|
|
1067
|
+
supersededRunPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise));
|
|
1068
|
+
}
|
|
1047
1069
|
this.runPromise = runPromise;
|
|
1048
1070
|
this.runPromiseDeviceId = uuid;
|
|
1071
|
+
const releaseOwnershipIfCurrent = () => {
|
|
1072
|
+
if (this.runPromise === runPromise) {
|
|
1073
|
+
this.runPromise = null;
|
|
1074
|
+
this.runPromiseDeviceId = null;
|
|
1075
|
+
}
|
|
1076
|
+
};
|
|
1077
|
+
const isCurrentOwner = () => this.runPromise === runPromise;
|
|
1049
1078
|
const messages = this._messages;
|
|
1050
1079
|
const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
|
|
1051
1080
|
let timeout;
|
|
@@ -1066,6 +1095,9 @@ class ReactNativeBleTransport {
|
|
|
1066
1095
|
}
|
|
1067
1096
|
catch (e) {
|
|
1068
1097
|
onError(e);
|
|
1098
|
+
if (isWedgedWriteError(e)) {
|
|
1099
|
+
throw e;
|
|
1100
|
+
}
|
|
1069
1101
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
|
|
1070
1102
|
}
|
|
1071
1103
|
}
|
|
@@ -1093,6 +1125,9 @@ class ReactNativeBleTransport {
|
|
|
1093
1125
|
}
|
|
1094
1126
|
catch (e) {
|
|
1095
1127
|
onError(e);
|
|
1128
|
+
if (isWedgedWriteError(e)) {
|
|
1129
|
+
throw e;
|
|
1130
|
+
}
|
|
1096
1131
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
|
|
1097
1132
|
}
|
|
1098
1133
|
}
|
|
@@ -1103,8 +1138,8 @@ class ReactNativeBleTransport {
|
|
|
1103
1138
|
});
|
|
1104
1139
|
}
|
|
1105
1140
|
if (name === 'EmmcFileWrite') {
|
|
1106
|
-
yield writeChunkedData(buffers, data => transport.writeWithRetry(
|
|
1107
|
-
|
|
1141
|
+
yield writeChunkedData(buffers, data => this.writeBlePacket(uuid, data, payload => transport.writeWithRetry(payload), isCurrentOwner), e => {
|
|
1142
|
+
releaseOwnershipIfCurrent();
|
|
1108
1143
|
Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
|
|
1109
1144
|
});
|
|
1110
1145
|
}
|
|
@@ -1120,7 +1155,7 @@ class ReactNativeBleTransport {
|
|
|
1120
1155
|
let attempt = 0;
|
|
1121
1156
|
while (true) {
|
|
1122
1157
|
try {
|
|
1123
|
-
yield transport.writeCharacteristic.
|
|
1158
|
+
yield this.writeBlePacket(uuid, data, payload => transport.writeCharacteristic.writeWithoutResponse(payload), isCurrentOwner);
|
|
1124
1159
|
return;
|
|
1125
1160
|
}
|
|
1126
1161
|
catch (error) {
|
|
@@ -1139,7 +1174,7 @@ class ReactNativeBleTransport {
|
|
|
1139
1174
|
}
|
|
1140
1175
|
}
|
|
1141
1176
|
}), e => {
|
|
1142
|
-
|
|
1177
|
+
releaseOwnershipIfCurrent();
|
|
1143
1178
|
Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
|
|
1144
1179
|
});
|
|
1145
1180
|
}
|
|
@@ -1147,17 +1182,14 @@ class ReactNativeBleTransport {
|
|
|
1147
1182
|
for (const o of buffers) {
|
|
1148
1183
|
const outData = o.toString('base64');
|
|
1149
1184
|
try {
|
|
1150
|
-
|
|
1151
|
-
if (shouldUseWriteWithResponse) {
|
|
1152
|
-
yield transport.writeCharacteristic.writeWithResponse(outData);
|
|
1153
|
-
}
|
|
1154
|
-
else {
|
|
1155
|
-
yield transport.writeCharacteristic.writeWithoutResponse(outData);
|
|
1156
|
-
}
|
|
1185
|
+
yield this.writeBlePacket(uuid, outData, payload => transport.writeCharacteristic.writeWithoutResponse(payload), isCurrentOwner);
|
|
1157
1186
|
}
|
|
1158
1187
|
catch (e) {
|
|
1159
1188
|
Log === null || Log === void 0 ? void 0 : Log.debug('writeCharacteristic write error: ', e);
|
|
1160
|
-
|
|
1189
|
+
releaseOwnershipIfCurrent();
|
|
1190
|
+
if (isWedgedWriteError(e)) {
|
|
1191
|
+
throw e;
|
|
1192
|
+
}
|
|
1161
1193
|
if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected) {
|
|
1162
1194
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded);
|
|
1163
1195
|
}
|
|
@@ -1197,7 +1229,9 @@ class ReactNativeBleTransport {
|
|
|
1197
1229
|
Log === null || Log === void 0 ? void 0 : Log.error('call error: ', e);
|
|
1198
1230
|
}
|
|
1199
1231
|
const isProbeTimeout = name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS;
|
|
1232
|
+
const isStaleCall = this.runPromise !== runPromise;
|
|
1200
1233
|
if (!isProbeTimeout &&
|
|
1234
|
+
!isStaleCall &&
|
|
1201
1235
|
(e === null || e === void 0 ? void 0 : e.errorCode) === hdShared.HardwareErrorCode.BleTimeoutError) {
|
|
1202
1236
|
yield this.disconnect(uuid);
|
|
1203
1237
|
}
|
|
@@ -1268,6 +1302,7 @@ class ReactNativeBleTransport {
|
|
|
1268
1302
|
delete transportCache[session];
|
|
1269
1303
|
}
|
|
1270
1304
|
this.deviceProtocol.delete(session);
|
|
1305
|
+
this.probingProtocols.delete(session);
|
|
1271
1306
|
this.deviceProtocolHints.delete(session);
|
|
1272
1307
|
this.protocolV2Assemblers.delete(session);
|
|
1273
1308
|
this.resetProtocolV2Frames(session);
|
|
@@ -1289,6 +1324,60 @@ class ReactNativeBleTransport {
|
|
|
1289
1324
|
this.runPromise = null;
|
|
1290
1325
|
this.runPromiseDeviceId = null;
|
|
1291
1326
|
}
|
|
1327
|
+
connectWithTimeout(uuid, connect) {
|
|
1328
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1329
|
+
let timer;
|
|
1330
|
+
let timedOut = false;
|
|
1331
|
+
const pending = connect();
|
|
1332
|
+
pending.catch(() => undefined);
|
|
1333
|
+
try {
|
|
1334
|
+
const result = yield Promise.race([
|
|
1335
|
+
pending,
|
|
1336
|
+
new Promise((_, reject) => {
|
|
1337
|
+
timer = setTimeout(() => {
|
|
1338
|
+
timedOut = true;
|
|
1339
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `BLE connect timeout after ${BLE_CONNECT_TIMEOUT_MS}ms for ${uuid}`));
|
|
1340
|
+
}, BLE_CONNECT_TIMEOUT_MS);
|
|
1341
|
+
}),
|
|
1342
|
+
]);
|
|
1343
|
+
this.connectTimeoutCounts.delete(uuid);
|
|
1344
|
+
return result;
|
|
1345
|
+
}
|
|
1346
|
+
catch (error) {
|
|
1347
|
+
if (timedOut) {
|
|
1348
|
+
this.abandonStalledConnect(uuid);
|
|
1349
|
+
}
|
|
1350
|
+
throw error;
|
|
1351
|
+
}
|
|
1352
|
+
finally {
|
|
1353
|
+
if (timer)
|
|
1354
|
+
clearTimeout(timer);
|
|
1355
|
+
}
|
|
1356
|
+
});
|
|
1357
|
+
}
|
|
1358
|
+
abandonStalledConnect(uuid) {
|
|
1359
|
+
var _a, _b;
|
|
1360
|
+
const timeouts = ((_a = this.connectTimeoutCounts.get(uuid)) !== null && _a !== void 0 ? _a : 0) + 1;
|
|
1361
|
+
this.connectTimeoutCounts.set(uuid, timeouts);
|
|
1362
|
+
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE connect timed out:', uuid, {
|
|
1363
|
+
consecutiveConnectTimeouts: timeouts,
|
|
1364
|
+
});
|
|
1365
|
+
(_b = this.blePlxManager) === null || _b === void 0 ? void 0 : _b.cancelDeviceConnection(uuid).catch(() => {
|
|
1366
|
+
});
|
|
1367
|
+
const stalled = transportCache[uuid];
|
|
1368
|
+
if (stalled) {
|
|
1369
|
+
delete transportCache[uuid];
|
|
1370
|
+
}
|
|
1371
|
+
this.deviceProtocol.delete(uuid);
|
|
1372
|
+
this.probingProtocols.delete(uuid);
|
|
1373
|
+
this.protocolV2Assemblers.delete(uuid);
|
|
1374
|
+
this.resetProtocolV2Frames(uuid);
|
|
1375
|
+
if (timeouts >= BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD) {
|
|
1376
|
+
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE connects wedged repeatedly, resetting BLE manager');
|
|
1377
|
+
this.resetPlxManager();
|
|
1378
|
+
this.connectTimeoutCounts.delete(uuid);
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1292
1381
|
getCachedTransport(uuid) {
|
|
1293
1382
|
const transport = transportCache[uuid];
|
|
1294
1383
|
if (!transport) {
|
|
@@ -1296,6 +1385,80 @@ class ReactNativeBleTransport {
|
|
|
1296
1385
|
}
|
|
1297
1386
|
return transport;
|
|
1298
1387
|
}
|
|
1388
|
+
writeBlePacket(uuid, data, write, isCurrentOwner) {
|
|
1389
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1390
|
+
let timer;
|
|
1391
|
+
let timedOut = false;
|
|
1392
|
+
try {
|
|
1393
|
+
yield Promise.race([
|
|
1394
|
+
write(data),
|
|
1395
|
+
new Promise((_, reject) => {
|
|
1396
|
+
timer = setTimeout(() => {
|
|
1397
|
+
timedOut = true;
|
|
1398
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError, `BLE write timeout after ${BLE_WRITE_PACKET_TIMEOUT_MS}ms`));
|
|
1399
|
+
}, BLE_WRITE_PACKET_TIMEOUT_MS);
|
|
1400
|
+
}),
|
|
1401
|
+
]);
|
|
1402
|
+
this.writeTimeoutCounts.delete(uuid);
|
|
1403
|
+
}
|
|
1404
|
+
catch (error) {
|
|
1405
|
+
if (timedOut) {
|
|
1406
|
+
if (isCurrentOwner && !isCurrentOwner()) {
|
|
1407
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] stale BLE write timed out, link kept:', uuid);
|
|
1408
|
+
}
|
|
1409
|
+
else {
|
|
1410
|
+
this.tearDownWedgedLink(uuid);
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
throw error;
|
|
1414
|
+
}
|
|
1415
|
+
finally {
|
|
1416
|
+
if (timer)
|
|
1417
|
+
clearTimeout(timer);
|
|
1418
|
+
}
|
|
1419
|
+
});
|
|
1420
|
+
}
|
|
1421
|
+
tearDownWedgedLink(uuid) {
|
|
1422
|
+
var _a;
|
|
1423
|
+
const timeouts = ((_a = this.writeTimeoutCounts.get(uuid)) !== null && _a !== void 0 ? _a : 0) + 1;
|
|
1424
|
+
this.writeTimeoutCounts.set(uuid, timeouts);
|
|
1425
|
+
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE write timed out, tearing down link:', uuid, {
|
|
1426
|
+
consecutiveWriteTimeouts: timeouts,
|
|
1427
|
+
});
|
|
1428
|
+
const wedged = transportCache[uuid];
|
|
1429
|
+
this.disconnect(uuid).catch(error => {
|
|
1430
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] wedged link teardown failed (ignored):', error);
|
|
1431
|
+
});
|
|
1432
|
+
if (wedged && transportCache[uuid] === wedged) {
|
|
1433
|
+
delete transportCache[uuid];
|
|
1434
|
+
}
|
|
1435
|
+
this.deviceProtocol.delete(uuid);
|
|
1436
|
+
this.probingProtocols.delete(uuid);
|
|
1437
|
+
this.protocolV2Assemblers.delete(uuid);
|
|
1438
|
+
this.resetProtocolV2Frames(uuid);
|
|
1439
|
+
if (timeouts >= BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD) {
|
|
1440
|
+
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE writes wedged repeatedly, resetting BLE manager');
|
|
1441
|
+
this.resetPlxManager();
|
|
1442
|
+
this.writeTimeoutCounts.delete(uuid);
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
resetPlxManager() {
|
|
1446
|
+
const manager = this.blePlxManager;
|
|
1447
|
+
this.blePlxManager = undefined;
|
|
1448
|
+
Object.keys(transportCache).forEach(key => {
|
|
1449
|
+
delete transportCache[key];
|
|
1450
|
+
});
|
|
1451
|
+
this.deviceProtocol.clear();
|
|
1452
|
+
this.probingProtocols.clear();
|
|
1453
|
+
this.monitorTokens.clear();
|
|
1454
|
+
this.protocolV2Assemblers.clear();
|
|
1455
|
+
try {
|
|
1456
|
+
manager === null || manager === void 0 ? void 0 : manager.destroy();
|
|
1457
|
+
}
|
|
1458
|
+
catch (error) {
|
|
1459
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE manager destroy failed (ignored):', error);
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1299
1462
|
createProtocolMismatchError(expected) {
|
|
1300
1463
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
|
|
1301
1464
|
}
|
|
@@ -1303,30 +1466,19 @@ class ReactNativeBleTransport {
|
|
|
1303
1466
|
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
1467
|
}
|
|
1305
1468
|
clearProbeProtocol(uuid, protocol) {
|
|
1469
|
+
if (this.probingProtocols.get(uuid) === protocol) {
|
|
1470
|
+
this.probingProtocols.delete(uuid);
|
|
1471
|
+
}
|
|
1306
1472
|
if (this.deviceProtocol.get(uuid) === protocol) {
|
|
1307
1473
|
this.deviceProtocol.delete(uuid);
|
|
1308
1474
|
}
|
|
1309
1475
|
}
|
|
1310
|
-
|
|
1476
|
+
getActiveProtocol(uuid) {
|
|
1311
1477
|
var _a;
|
|
1478
|
+
return (_a = this.deviceProtocol.get(uuid)) !== null && _a !== void 0 ? _a : this.probingProtocols.get(uuid);
|
|
1479
|
+
}
|
|
1480
|
+
detectProtocol(uuid, expectedProtocol, protocolHint, rebuildTransport) {
|
|
1312
1481
|
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
1482
|
if (expectedProtocol === 'V1') {
|
|
1331
1483
|
if (yield this.probeProtocolV1(uuid)) {
|
|
1332
1484
|
this.deviceProtocol.set(uuid, 'V1');
|
|
@@ -1375,6 +1527,7 @@ class ReactNativeBleTransport {
|
|
|
1375
1527
|
}
|
|
1376
1528
|
}
|
|
1377
1529
|
this.deviceProtocol.delete(uuid);
|
|
1530
|
+
this.probingProtocols.delete(uuid);
|
|
1378
1531
|
throw this.createProtocolDetectionError();
|
|
1379
1532
|
});
|
|
1380
1533
|
}
|
|
@@ -1426,13 +1579,17 @@ class ReactNativeBleTransport {
|
|
|
1426
1579
|
return false;
|
|
1427
1580
|
}
|
|
1428
1581
|
try {
|
|
1429
|
-
this.
|
|
1582
|
+
this.probingProtocols.set(uuid, 'V1');
|
|
1430
1583
|
yield this.callProtocolV1(uuid, 'GetFeatures', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
|
|
1584
|
+
this.probingProtocols.delete(uuid);
|
|
1431
1585
|
return true;
|
|
1432
1586
|
}
|
|
1433
1587
|
catch (error) {
|
|
1434
1588
|
this.clearProbeProtocol(uuid, 'V1');
|
|
1435
1589
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe failed:', error);
|
|
1590
|
+
if (isWedgedWriteError(error)) {
|
|
1591
|
+
throw error;
|
|
1592
|
+
}
|
|
1436
1593
|
return false;
|
|
1437
1594
|
}
|
|
1438
1595
|
});
|
|
@@ -1443,7 +1600,7 @@ class ReactNativeBleTransport {
|
|
|
1443
1600
|
if (!this._messages || !this._messagesV2) {
|
|
1444
1601
|
return false;
|
|
1445
1602
|
}
|
|
1446
|
-
this.
|
|
1603
|
+
this.probingProtocols.set(uuid, 'V2');
|
|
1447
1604
|
(_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1448
1605
|
const detected = yield transport.probeProtocolV2({
|
|
1449
1606
|
call: (name, data, options) => this.callProtocolV2(uuid, name, data, options),
|
|
@@ -1459,6 +1616,9 @@ class ReactNativeBleTransport {
|
|
|
1459
1616
|
if (!detected) {
|
|
1460
1617
|
this.clearProbeProtocol(uuid, 'V2');
|
|
1461
1618
|
}
|
|
1619
|
+
else {
|
|
1620
|
+
this.probingProtocols.delete(uuid);
|
|
1621
|
+
}
|
|
1462
1622
|
return detected;
|
|
1463
1623
|
});
|
|
1464
1624
|
}
|
|
@@ -1530,10 +1690,8 @@ class ReactNativeBleTransport {
|
|
|
1530
1690
|
}
|
|
1531
1691
|
});
|
|
1532
1692
|
}
|
|
1533
|
-
writeProtocolV2Packet(transport, base64, context, assertCurrentGeneration) {
|
|
1693
|
+
writeProtocolV2Packet(uuid, transport, base64, context, assertCurrentGeneration) {
|
|
1534
1694
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1535
|
-
const shouldUseWriteWithResponse = transport.writeCharacteristic.isWritableWithResponse &&
|
|
1536
|
-
(context.writeWithResponse === true || (reactNative.Platform.OS === 'ios' && !context.highVolume));
|
|
1537
1695
|
let attempt = 0;
|
|
1538
1696
|
for (;;) {
|
|
1539
1697
|
assertCurrentGeneration();
|
|
@@ -1541,12 +1699,15 @@ class ReactNativeBleTransport {
|
|
|
1541
1699
|
throw new Error(`Protocol V2 BLE write aborted for ${context.messageName}`);
|
|
1542
1700
|
}
|
|
1543
1701
|
try {
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1702
|
+
yield this.writeBlePacket(uuid, base64, payload => transport.writeCharacteristic.writeWithoutResponse(payload), () => {
|
|
1703
|
+
try {
|
|
1704
|
+
assertCurrentGeneration();
|
|
1705
|
+
return !context.signal.aborted;
|
|
1706
|
+
}
|
|
1707
|
+
catch (_a) {
|
|
1708
|
+
return false;
|
|
1709
|
+
}
|
|
1710
|
+
});
|
|
1550
1711
|
assertCurrentGeneration();
|
|
1551
1712
|
return;
|
|
1552
1713
|
}
|
|
@@ -1567,7 +1728,7 @@ class ReactNativeBleTransport {
|
|
|
1567
1728
|
}
|
|
1568
1729
|
});
|
|
1569
1730
|
}
|
|
1570
|
-
writeProtocolV2Frame(transport$1, frame, context, assertCurrentGeneration) {
|
|
1731
|
+
writeProtocolV2Frame(uuid, transport$1, frame, context, assertCurrentGeneration) {
|
|
1571
1732
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1572
1733
|
const tuning = getProtocolV2BleTuning();
|
|
1573
1734
|
const packetCapacity = resolveProtocolV2PacketCapacity({
|
|
@@ -1576,21 +1737,17 @@ class ReactNativeBleTransport {
|
|
|
1576
1737
|
androidPacketLength: tuning.androidPacketLength,
|
|
1577
1738
|
mtu: reactNative.Platform.OS === 'android' ? transport$1.mtuSize : undefined,
|
|
1578
1739
|
});
|
|
1579
|
-
const initialDelayMs = reactNative.Platform.OS === 'ios' && !context.highVolume && frame.length <= packetCapacity
|
|
1580
|
-
? IOS_PROTOCOL_V2_CONTROL_WRITE_DELAY_MS
|
|
1581
|
-
: 0;
|
|
1582
1740
|
yield transport.writeProtocolV2BleFrame({
|
|
1583
1741
|
frame,
|
|
1584
1742
|
packetCapacity,
|
|
1585
1743
|
assertActive: assertCurrentGeneration,
|
|
1586
1744
|
signal: context.signal,
|
|
1587
1745
|
abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
|
|
1588
|
-
initialDelayMs,
|
|
1589
1746
|
burstSize: FIRMWARE_UPLOAD_WRITE_BURST_SIZE,
|
|
1590
1747
|
burstPauseMs: FIRMWARE_UPLOAD_WRITE_PAUSE_MS,
|
|
1591
1748
|
flushDelayMs: FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS,
|
|
1592
1749
|
wait: delay,
|
|
1593
|
-
writePacket: packet => this.writeProtocolV2Packet(transport$1, buffer.Buffer.from(packet).toString('base64'), context, assertCurrentGeneration),
|
|
1750
|
+
writePacket: packet => this.writeProtocolV2Packet(uuid, transport$1, buffer.Buffer.from(packet).toString('base64'), context, assertCurrentGeneration),
|
|
1594
1751
|
});
|
|
1595
1752
|
});
|
|
1596
1753
|
}
|
|
@@ -1605,7 +1762,7 @@ class ReactNativeBleTransport {
|
|
|
1605
1762
|
const tuning = getProtocolV2BleTuning();
|
|
1606
1763
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume write configured', {
|
|
1607
1764
|
name,
|
|
1608
|
-
writeMode:
|
|
1765
|
+
writeMode: 'withoutResponse',
|
|
1609
1766
|
packetCapacity: reactNative.Platform.OS === 'ios' ? tuning.iosPacketLength : tuning.androidPacketLength,
|
|
1610
1767
|
});
|
|
1611
1768
|
}
|
|
@@ -1639,7 +1796,7 @@ class ReactNativeBleTransport {
|
|
|
1639
1796
|
writeFrame: (frame, context) => __awaiter(this, void 0, void 0, function* () {
|
|
1640
1797
|
assertCurrentGeneration();
|
|
1641
1798
|
const currentTransport = this.getCachedTransport(uuid);
|
|
1642
|
-
yield this.writeProtocolV2Frame(currentTransport, frame, context, assertCurrentGeneration);
|
|
1799
|
+
yield this.writeProtocolV2Frame(uuid, currentTransport, frame, context, assertCurrentGeneration);
|
|
1643
1800
|
}),
|
|
1644
1801
|
readFrame: () => __awaiter(this, void 0, void 0, function* () {
|
|
1645
1802
|
assertCurrentGeneration();
|
|
@@ -1660,10 +1817,14 @@ class ReactNativeBleTransport {
|
|
|
1660
1817
|
};
|
|
1661
1818
|
}
|
|
1662
1819
|
getProtocolType(path) {
|
|
1663
|
-
return this.
|
|
1820
|
+
return this.getActiveProtocol(path);
|
|
1664
1821
|
}
|
|
1665
1822
|
}
|
|
1666
1823
|
|
|
1824
|
+
exports.BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD;
|
|
1825
|
+
exports.BLE_CONNECT_TIMEOUT_MS = BLE_CONNECT_TIMEOUT_MS;
|
|
1826
|
+
exports.BLE_WRITE_PACKET_TIMEOUT_MS = BLE_WRITE_PACKET_TIMEOUT_MS;
|
|
1827
|
+
exports.BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD;
|
|
1667
1828
|
exports.configureProtocolV2BleTuning = configureProtocolV2BleTuning;
|
|
1668
1829
|
exports["default"] = ReactNativeBleTransport;
|
|
1669
1830
|
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
|
};
|