@onekeyfe/hd-transport-react-native 1.2.2-alpha.115 → 1.2.2-alpha.116
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 +48 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +212 -14
- package/package.json +5 -5
- package/src/__tests__/connectTimeout.test.ts +104 -0
- package/src/__tests__/protocolV2Link.test.ts +450 -24
- package/src/index.ts +341 -22
package/dist/index.d.ts
CHANGED
|
@@ -74,6 +74,28 @@ declare function getProtocolV2BleTuning(): {
|
|
|
74
74
|
iosPacketLength: number;
|
|
75
75
|
androidPacketLength: number;
|
|
76
76
|
};
|
|
77
|
+
/**
|
|
78
|
+
* Bound for the Android MTU exchange. A healthy exchange with a Pro 2 completes in ~50ms.
|
|
79
|
+
* When the phone has no cached GATT table (first connect after bonding, or a connect after
|
|
80
|
+
* an aborted discovery) the stack runs its own discovery first and only executes the MTU
|
|
81
|
+
* request after it; single passes on a Pro 2 at MTU 23 measured 1.7-4.8s and a refresh
|
|
82
|
+
* plus restart 7.7s, so the bound has to sit well above those. An exchange the stack holds
|
|
83
|
+
* as already in progress never completes, so a longer bound costs nothing on that link.
|
|
84
|
+
*/
|
|
85
|
+
declare const ANDROID_MTU_EXCHANGE_TIMEOUT_MS = 12000;
|
|
86
|
+
/**
|
|
87
|
+
* Android keeps an LE link up for its GATT link idle timer (4s) after the last client
|
|
88
|
+
* closes, and per-link ATT state such as a pending MTU exchange lives as long as the link.
|
|
89
|
+
* A reconnect inside that window attaches to the same link. The only connectivity signal
|
|
90
|
+
* reachable from here, BluetoothManager.getConnectedDevices(GATT), is built from the
|
|
91
|
+
* per-app GATT client registry rather than the LE link: it empties the moment this client
|
|
92
|
+
* closes on phones with no resident GATT server, and stays populated for the life of the
|
|
93
|
+
* link on phones that have one. So an unusable link is held for at least the quiet period
|
|
94
|
+
* (past the idle timer), and additionally until that registry no longer lists the device,
|
|
95
|
+
* within the overall bound.
|
|
96
|
+
*/
|
|
97
|
+
declare const ANDROID_LINK_DROP_QUIET_MS = 5000;
|
|
98
|
+
declare const ANDROID_LINK_DROP_TIMEOUT_MS = 8000;
|
|
77
99
|
/**
|
|
78
100
|
* JS backstop for connect. The native adapter applies its own 3s budget, but it
|
|
79
101
|
* schedules that timeout on its serial queue, so a busy queue (e.g. right after a
|
|
@@ -137,6 +159,16 @@ declare class ReactNativeBleTransport {
|
|
|
137
159
|
private protocolReprobeFailures;
|
|
138
160
|
/** Endpoints whose last detection got no answer on any protocol. */
|
|
139
161
|
private silentDetections;
|
|
162
|
+
/**
|
|
163
|
+
* Android endpoints whose cached GATT table is suspect and must be rediscovered on the
|
|
164
|
+
* next connect. The cache is no longer refreshed on every connect, so a missing OneKey
|
|
165
|
+
* service or characteristic marks the endpoint instead.
|
|
166
|
+
*/
|
|
167
|
+
private androidGattCacheRefreshes;
|
|
168
|
+
/** Instance copies of the Android timing bounds so tests can shorten real-time waits. */
|
|
169
|
+
androidMtuExchangeTimeoutMs: number;
|
|
170
|
+
androidLinkDropQuietMs: number;
|
|
171
|
+
androidLinkDropTimeoutMs: number;
|
|
140
172
|
/**
|
|
141
173
|
* Endpoints already sent a Protocol V1 wake. The wake starts a fresh wallet session
|
|
142
174
|
* on the device, and a failed detection tears the link down and reconnects on every
|
|
@@ -203,6 +235,21 @@ declare class ReactNativeBleTransport {
|
|
|
203
235
|
private runBestEffortNativeOperation;
|
|
204
236
|
private runLifecycleOperation;
|
|
205
237
|
cancel(): Promise<void>;
|
|
238
|
+
/**
|
|
239
|
+
* Negotiate the ATT MTU as the first request on an Android GATT client, with nothing queued
|
|
240
|
+
* ahead of it, and never close the client while it is outstanding: an exchange abandoned
|
|
241
|
+
* mid-flight stays marked in progress for the whole LE link. A link that already negotiated
|
|
242
|
+
* an MTU answers at once with the recorded value.
|
|
243
|
+
*/
|
|
244
|
+
private negotiateAndroidMtu;
|
|
245
|
+
/**
|
|
246
|
+
* Abandon an Android LE link whose per-link GATT state is unusable. Closing the GATT
|
|
247
|
+
* client alone keeps the link up for the stack's idle timer, and Core's retry arrives
|
|
248
|
+
* inside that window, so this holds for at least ANDROID_LINK_DROP_QUIET_MS and until the
|
|
249
|
+
* GATT registry no longer lists the device, capped at ANDROID_LINK_DROP_TIMEOUT_MS.
|
|
250
|
+
*/
|
|
251
|
+
private dropAndroidLink;
|
|
252
|
+
private resolveCharacteristicsForAcquire;
|
|
206
253
|
/** Run a native connect under the JS backstop budget. */
|
|
207
254
|
private connectWithTimeout;
|
|
208
255
|
/** Resolve the complete GATT shape under a budget so acquire() always settles. */
|
|
@@ -277,4 +324,4 @@ declare class ReactNativeBleTransport {
|
|
|
277
324
|
getProtocolType(path: string): ProtocolType | undefined;
|
|
278
325
|
}
|
|
279
326
|
|
|
280
|
-
export { BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD, BLE_CONNECT_TIMEOUT_MS, BLE_GATT_SETUP_TIMEOUT_MS, BLE_NATIVE_TEARDOWN_TIMEOUT_MS, BLE_SETUP_WEDGED_MESSAGE, BLE_WRITE_PACKET_TIMEOUT_MS, BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD, IOneKeyDevice, PROTOCOL_REPROBE_FALLBACK_ATTEMPTS, ProtocolV2BleTuning, configureProtocolV2BleTuning, ReactNativeBleTransport as default, getFirmwareUploadWriteRetryType, getProtocolV2BleTuning, resetProtocolV2BleTuning };
|
|
327
|
+
export { ANDROID_LINK_DROP_QUIET_MS, ANDROID_LINK_DROP_TIMEOUT_MS, ANDROID_MTU_EXCHANGE_TIMEOUT_MS, BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD, BLE_CONNECT_TIMEOUT_MS, BLE_GATT_SETUP_TIMEOUT_MS, BLE_NATIVE_TEARDOWN_TIMEOUT_MS, BLE_SETUP_WEDGED_MESSAGE, BLE_WRITE_PACKET_TIMEOUT_MS, BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD, IOneKeyDevice, PROTOCOL_REPROBE_FALLBACK_ATTEMPTS, ProtocolV2BleTuning, configureProtocolV2BleTuning, ReactNativeBleTransport as default, getFirmwareUploadWriteRetryType, getProtocolV2BleTuning, resetProtocolV2BleTuning };
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAGL,UAAU,IAAI,aAAa,EAG5B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,SAAS,EAAE,EAChB,KAAK,oBAAoB,EAIzB,KAAK,YAAY,EAKjB,KAAK,oBAAoB,EAI1B,MAAM,wBAAwB,CAAC;AAgChC,OAAO,YAAY,MAAM,gBAAgB,CAAC;AAI1C,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;AAEjE,KAAK,8BAA8B,GAAG,eAAe,GAAG;IAMtD,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAkBF,KAAK,4BAA4B,GAAG,WAAW,CAAC;AAChD,KAAK,0BAA0B,GAAG;IAChC,mBAAmB,EAAE,cAAc,CAAC;IACpC,oBAAoB,EAAE,cAAc,CAAC;CACtC,CAAC;AAkCF,eAAO,MAAM,+BAA+B,UACnC,OAAO,KACb,4BAA4B,GAAG,IAsBjC,CAAC;AAcF,eAAO,MAAM,2BAA2B,QAAS,CAAC;AAClD,eAAO,MAAM,8BAA8B,OAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -384,7 +384,8 @@ const shouldRethrowProtocolProbeError = (error) => {
|
|
|
384
384
|
code === hdShared.HardwareErrorCode.BleDeviceDisconnected ||
|
|
385
385
|
code === hdShared.HardwareErrorCode.BleCharacteristicNotifyError ||
|
|
386
386
|
code === hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure ||
|
|
387
|
-
code === hdShared.HardwareErrorCode.BleWriteCharacteristicError
|
|
387
|
+
code === hdShared.HardwareErrorCode.BleWriteCharacteristicError ||
|
|
388
|
+
isProtocolV2DefaultMtuError(error));
|
|
388
389
|
};
|
|
389
390
|
const BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
|
|
390
391
|
const DEVICE_SCAN_TIMEOUT_MS = 3000;
|
|
@@ -432,6 +433,40 @@ const connectOptions = {
|
|
|
432
433
|
const fallbackConnectOptions = {
|
|
433
434
|
timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
|
|
434
435
|
};
|
|
436
|
+
const androidConnectOptions = {
|
|
437
|
+
timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
|
|
438
|
+
};
|
|
439
|
+
const androidRefreshGattConnectOptions = {
|
|
440
|
+
timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
|
|
441
|
+
refreshGatt: 'OnConnected',
|
|
442
|
+
};
|
|
443
|
+
const ANDROID_MTU_EXCHANGE_TIMEOUT_MS = 12000;
|
|
444
|
+
const ANDROID_LINK_DROP_QUIET_MS = 5000;
|
|
445
|
+
const ANDROID_LINK_DROP_TIMEOUT_MS = 8000;
|
|
446
|
+
const ANDROID_LINK_DROP_POLL_MS = 250;
|
|
447
|
+
const isKnownDefaultMtu = (mtu) => typeof mtu === 'number' && Number.isFinite(mtu) && mtu <= 23;
|
|
448
|
+
const DEFAULT_MTU_LINK_MESSAGE = 'BLE link stayed at the default MTU';
|
|
449
|
+
const createDefaultMtuLinkError = (mtu) => hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `${DEFAULT_MTU_LINK_MESSAGE} ${String(mtu)}, reconnecting on a fresh link`);
|
|
450
|
+
const PROTOCOL_V2_DEFAULT_MTU_MESSAGE = 'Protocol V2 needs a negotiated BLE MTU';
|
|
451
|
+
const isProtocolV2DefaultMtuError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleConnectedError &&
|
|
452
|
+
typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' &&
|
|
453
|
+
error.message.startsWith(PROTOCOL_V2_DEFAULT_MTU_MESSAGE);
|
|
454
|
+
const createProtocolV2DefaultMtuError = (mtu) => hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `${PROTOCOL_V2_DEFAULT_MTU_MESSAGE}, current MTU ${String(mtu)}`);
|
|
455
|
+
const isMissingGattShapeError = (error) => {
|
|
456
|
+
const code = error === null || error === void 0 ? void 0 : error.errorCode;
|
|
457
|
+
const message = error === null || error === void 0 ? void 0 : error.message;
|
|
458
|
+
return (code === hdShared.HardwareErrorCode.BleServiceNotFound ||
|
|
459
|
+
code === hdShared.HardwareErrorCode.BleCharacteristicNotFound ||
|
|
460
|
+
(typeof message === 'string' &&
|
|
461
|
+
(message.includes('BLECharacteristicNotFound') ||
|
|
462
|
+
message.includes('BLECharacteristicNotWritable') ||
|
|
463
|
+
message.includes('BLECharacteristicNotNotifiable'))));
|
|
464
|
+
};
|
|
465
|
+
const isStaleGattTableNotifyReason = (reason) => !!reason &&
|
|
466
|
+
(reason.includes('Cannot write client characteristic config descriptor') ||
|
|
467
|
+
reason.includes('Cannot find client characteristic config descriptor') ||
|
|
468
|
+
reason.includes('The handle is invalid') ||
|
|
469
|
+
reason.includes('Writing is not permitted'));
|
|
435
470
|
const BLE_CONNECT_TIMEOUT_MS = BLE_NATIVE_CONNECT_TIMEOUT_MS * 2 + 2000;
|
|
436
471
|
const BLE_GATT_SETUP_TIMEOUT_MS = 10000;
|
|
437
472
|
const PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = 3;
|
|
@@ -556,6 +591,10 @@ class ReactNativeBleTransport {
|
|
|
556
591
|
this.confirmedProtocolV2 = new Set();
|
|
557
592
|
this.protocolReprobeFailures = new Map();
|
|
558
593
|
this.silentDetections = new Set();
|
|
594
|
+
this.androidGattCacheRefreshes = new Set();
|
|
595
|
+
this.androidMtuExchangeTimeoutMs = ANDROID_MTU_EXCHANGE_TIMEOUT_MS;
|
|
596
|
+
this.androidLinkDropQuietMs = ANDROID_LINK_DROP_QUIET_MS;
|
|
597
|
+
this.androidLinkDropTimeoutMs = ANDROID_LINK_DROP_TIMEOUT_MS;
|
|
559
598
|
this.protocolWakeAttempts = new Set();
|
|
560
599
|
this.staleBondErrors = new Map();
|
|
561
600
|
this.acquiringProtocolV2 = new Set();
|
|
@@ -790,9 +829,10 @@ class ReactNativeBleTransport {
|
|
|
790
829
|
transport.notifySubscription = undefined;
|
|
791
830
|
let { device } = transport;
|
|
792
831
|
const isConnected = yield device.isConnected().catch(() => false);
|
|
832
|
+
const reconnectOptions = reactNative.Platform.OS === 'android' ? androidRefreshGattConnectOptions : connectOptions;
|
|
793
833
|
if (!isConnected) {
|
|
794
834
|
try {
|
|
795
|
-
device = yield this.connectWithTimeout(uuid, () => device.connect(
|
|
835
|
+
device = yield this.connectWithTimeout(uuid, () => device.connect(reconnectOptions));
|
|
796
836
|
}
|
|
797
837
|
catch (e) {
|
|
798
838
|
if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
|
|
@@ -805,7 +845,16 @@ class ReactNativeBleTransport {
|
|
|
805
845
|
}
|
|
806
846
|
}
|
|
807
847
|
const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, device);
|
|
848
|
+
if (reactNative.Platform.OS === 'android') {
|
|
849
|
+
const manager = yield this.getPlxManager();
|
|
850
|
+
device = yield this.negotiateAndroidMtu(uuid, manager, device);
|
|
851
|
+
if (isKnownDefaultMtu(device.mtu)) {
|
|
852
|
+
yield this.dropAndroidLink(uuid, manager, device, 'firmware reconnect default mtu');
|
|
853
|
+
throw createDefaultMtuLinkError(device.mtu);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
808
856
|
transport.device = device;
|
|
857
|
+
transport.mtuSize = typeof device.mtu === 'number' ? device.mtu : transport.mtuSize;
|
|
809
858
|
transport.writeCharacteristic = writeCharacteristic;
|
|
810
859
|
transport.notifyCharacteristic = notifyCharacteristic;
|
|
811
860
|
const monitorToken = this.nextMonitorToken;
|
|
@@ -952,7 +1001,7 @@ class ReactNativeBleTransport {
|
|
|
952
1001
|
}
|
|
953
1002
|
installTransportForAcquire(uuid, device, characteristics) {
|
|
954
1003
|
return __awaiter(this, void 0, void 0, function* () {
|
|
955
|
-
const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.
|
|
1004
|
+
const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristicsForAcquire(uuid, device));
|
|
956
1005
|
if (this.stopped)
|
|
957
1006
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
958
1007
|
const transport$1 = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
|
|
@@ -1030,7 +1079,11 @@ class ReactNativeBleTransport {
|
|
|
1030
1079
|
const isCachedDeviceConnected = yield cachedTransport.device
|
|
1031
1080
|
.isConnected()
|
|
1032
1081
|
.catch(() => false);
|
|
1082
|
+
const isCachedAndroidLinkUsable = reactNative.Platform.OS !== 'android' ||
|
|
1083
|
+
(!isKnownDefaultMtu(cachedTransport.mtuSize) &&
|
|
1084
|
+
!this.androidGattCacheRefreshes.has(uuid));
|
|
1033
1085
|
if (isCachedDeviceConnected &&
|
|
1086
|
+
isCachedAndroidLinkUsable &&
|
|
1034
1087
|
cachedProtocol &&
|
|
1035
1088
|
(!expectedProtocol || cachedProtocol === expectedProtocol)) {
|
|
1036
1089
|
if (this.stopped)
|
|
@@ -1043,7 +1096,15 @@ class ReactNativeBleTransport {
|
|
|
1043
1096
|
}
|
|
1044
1097
|
}
|
|
1045
1098
|
let device = null;
|
|
1046
|
-
|
|
1099
|
+
const isAndroid = reactNative.Platform.OS === 'android';
|
|
1100
|
+
const refreshAndroidGattCache = isAndroid && (!!skipProtocolProbe || this.androidGattCacheRefreshes.has(uuid));
|
|
1101
|
+
let nativeConnectOptions = connectOptions;
|
|
1102
|
+
if (isAndroid) {
|
|
1103
|
+
nativeConnectOptions = refreshAndroidGattCache
|
|
1104
|
+
? androidRefreshGattConnectOptions
|
|
1105
|
+
: androidConnectOptions;
|
|
1106
|
+
}
|
|
1107
|
+
let androidRefreshConnectRan = false;
|
|
1047
1108
|
if (forceCleanRunPromise && this.runPromise) {
|
|
1048
1109
|
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
|
|
1049
1110
|
this.runPromise.reject(error);
|
|
@@ -1093,7 +1154,8 @@ class ReactNativeBleTransport {
|
|
|
1093
1154
|
if (!device) {
|
|
1094
1155
|
Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device: ', uuid);
|
|
1095
1156
|
try {
|
|
1096
|
-
device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid,
|
|
1157
|
+
device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, nativeConnectOptions));
|
|
1158
|
+
androidRefreshConnectRan = refreshAndroidGattCache;
|
|
1097
1159
|
}
|
|
1098
1160
|
catch (e) {
|
|
1099
1161
|
Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device has error: ', e);
|
|
@@ -1103,7 +1165,6 @@ class ReactNativeBleTransport {
|
|
|
1103
1165
|
if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
|
|
1104
1166
|
e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
|
|
1105
1167
|
Log === null || Log === void 0 ? void 0 : Log.debug('first try to reconnect without params');
|
|
1106
|
-
mtuHandshakeRefused = true;
|
|
1107
1168
|
yield this.runBestEffortNativeOperation('connect cancelled: cancel manager connection', () => blePlxManager.cancelDeviceConnection(uuid));
|
|
1108
1169
|
device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, fallbackConnectOptions));
|
|
1109
1170
|
}
|
|
@@ -1119,11 +1180,17 @@ class ReactNativeBleTransport {
|
|
|
1119
1180
|
if (!device) {
|
|
1120
1181
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, 'unable to connect to device');
|
|
1121
1182
|
}
|
|
1183
|
+
if (isAndroid && refreshAndroidGattCache && (yield device.isConnected().catch(() => false))) {
|
|
1184
|
+
yield this.dropAndroidLink(uuid, blePlxManager, device, 'gatt cache refresh');
|
|
1185
|
+
if (this.stopped)
|
|
1186
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1187
|
+
}
|
|
1122
1188
|
if (!(yield device.isConnected())) {
|
|
1123
1189
|
Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device: ', uuid);
|
|
1124
1190
|
const disconnectedDevice = device;
|
|
1125
1191
|
try {
|
|
1126
|
-
device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(
|
|
1192
|
+
device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(nativeConnectOptions));
|
|
1193
|
+
androidRefreshConnectRan = refreshAndroidGattCache;
|
|
1127
1194
|
}
|
|
1128
1195
|
catch (e) {
|
|
1129
1196
|
Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device has error: ', e);
|
|
@@ -1133,7 +1200,6 @@ class ReactNativeBleTransport {
|
|
|
1133
1200
|
if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
|
|
1134
1201
|
e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
|
|
1135
1202
|
Log === null || Log === void 0 ? void 0 : Log.debug('second try to reconnect without params');
|
|
1136
|
-
mtuHandshakeRefused = true;
|
|
1137
1203
|
yield this.runBestEffortNativeOperation('connect cancelled: cancel device connection', () => disconnectedDevice.cancelConnection());
|
|
1138
1204
|
try {
|
|
1139
1205
|
device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
|
|
@@ -1169,11 +1235,23 @@ class ReactNativeBleTransport {
|
|
|
1169
1235
|
}
|
|
1170
1236
|
if (this.stopped)
|
|
1171
1237
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1238
|
+
let characteristics;
|
|
1239
|
+
if (isAndroid) {
|
|
1240
|
+
if (refreshAndroidGattCache) {
|
|
1241
|
+
characteristics = yield this.resolveCharacteristicsForAcquire(uuid, device);
|
|
1242
|
+
if (androidRefreshConnectRan)
|
|
1243
|
+
this.androidGattCacheRefreshes.delete(uuid);
|
|
1244
|
+
if (this.stopped)
|
|
1245
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1246
|
+
}
|
|
1247
|
+
device = yield this.negotiateAndroidMtu(uuid, blePlxManager, device);
|
|
1248
|
+
if (isKnownDefaultMtu(device.mtu)) {
|
|
1249
|
+
const resetManager = this.abandonStalledConnection(uuid, 'mtu-default');
|
|
1250
|
+
yield this.dropAndroidLink(uuid, blePlxManager, device, 'default mtu');
|
|
1251
|
+
if (resetManager)
|
|
1252
|
+
throw this.createWedgedBleSetupError();
|
|
1253
|
+
throw createDefaultMtuLinkError(device.mtu);
|
|
1254
|
+
}
|
|
1177
1255
|
}
|
|
1178
1256
|
else {
|
|
1179
1257
|
const negotiatedMtu = yield resolveNegotiatedMtu(device, transactionId => blePlxManager.cancelTransaction(transactionId));
|
|
@@ -1188,7 +1266,7 @@ class ReactNativeBleTransport {
|
|
|
1188
1266
|
if (this.stopped)
|
|
1189
1267
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1190
1268
|
const acquiredDevice = device;
|
|
1191
|
-
const { writeCharacteristic, notifyCharacteristic } = yield this.
|
|
1269
|
+
const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristicsForAcquire(uuid, acquiredDevice));
|
|
1192
1270
|
if (this.stopped)
|
|
1193
1271
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1194
1272
|
const protocolHint = expectedProtocol
|
|
@@ -1284,6 +1362,9 @@ class ReactNativeBleTransport {
|
|
|
1284
1362
|
((_f = error.reason) === null || _f === void 0 ? void 0 : _f.includes('notify change failed for device'))) {
|
|
1285
1363
|
errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure;
|
|
1286
1364
|
}
|
|
1365
|
+
if (reactNative.Platform.OS === 'android' && isStaleGattTableNotifyReason(error.reason)) {
|
|
1366
|
+
this.androidGattCacheRefreshes.add(uuid);
|
|
1367
|
+
}
|
|
1287
1368
|
this.rejectProtocolV2Frames(uuid, hdShared.ERRORS.TypedError(errorCode));
|
|
1288
1369
|
return;
|
|
1289
1370
|
}
|
|
@@ -1298,6 +1379,9 @@ class ReactNativeBleTransport {
|
|
|
1298
1379
|
((_l = error.reason) === null || _l === void 0 ? void 0 : _l.includes('Writing is not permitted')) ||
|
|
1299
1380
|
((_m = error.reason) === null || _m === void 0 ? void 0 : _m.includes('notify change failed for device'))) {
|
|
1300
1381
|
const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure);
|
|
1382
|
+
if (reactNative.Platform.OS === 'android' && isStaleGattTableNotifyReason(error.reason)) {
|
|
1383
|
+
this.androidGattCacheRefreshes.add(uuid);
|
|
1384
|
+
}
|
|
1301
1385
|
this.runPromise.reject(notifyError);
|
|
1302
1386
|
Log === null || Log === void 0 ? void 0 : Log.debug(`${hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure} ${error.message} ${error.reason}`);
|
|
1303
1387
|
return;
|
|
@@ -1832,6 +1916,111 @@ class ReactNativeBleTransport {
|
|
|
1832
1916
|
}
|
|
1833
1917
|
});
|
|
1834
1918
|
}
|
|
1919
|
+
negotiateAndroidMtu(uuid, manager, device) {
|
|
1920
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1921
|
+
if (!shouldRefreshNegotiatedMtu(device.mtu))
|
|
1922
|
+
return device;
|
|
1923
|
+
const startedAt = Date.now();
|
|
1924
|
+
const transactionId = `${device.id}:mtu:connected:0:${startedAt}`;
|
|
1925
|
+
let timer;
|
|
1926
|
+
let timedOut = false;
|
|
1927
|
+
const request = device.requestMTU(ANDROID_REQUEST_MTU, transactionId);
|
|
1928
|
+
request.catch(() => undefined);
|
|
1929
|
+
try {
|
|
1930
|
+
const negotiated = yield Promise.race([
|
|
1931
|
+
request,
|
|
1932
|
+
new Promise((_, reject) => {
|
|
1933
|
+
timer = setTimeout(() => {
|
|
1934
|
+
timedOut = true;
|
|
1935
|
+
reject(new Error(`BLE MTU exchange timeout after ${this.androidMtuExchangeTimeoutMs}ms`));
|
|
1936
|
+
}, this.androidMtuExchangeTimeoutMs);
|
|
1937
|
+
}),
|
|
1938
|
+
]);
|
|
1939
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE MTU exchange completed', {
|
|
1940
|
+
connectIdSuffix: uuid.slice(-8),
|
|
1941
|
+
elapsedMs: Date.now() - startedAt,
|
|
1942
|
+
actual: negotiated.mtu,
|
|
1943
|
+
});
|
|
1944
|
+
return negotiated;
|
|
1945
|
+
}
|
|
1946
|
+
catch (error) {
|
|
1947
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE MTU exchange failed', {
|
|
1948
|
+
connectIdSuffix: uuid.slice(-8),
|
|
1949
|
+
elapsedMs: Date.now() - startedAt,
|
|
1950
|
+
timedOut,
|
|
1951
|
+
actual: device.mtu,
|
|
1952
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1953
|
+
});
|
|
1954
|
+
if (this.stopped)
|
|
1955
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected);
|
|
1956
|
+
if (!timedOut)
|
|
1957
|
+
return device;
|
|
1958
|
+
yield Promise.resolve(manager.cancelTransaction(transactionId)).catch(() => undefined);
|
|
1959
|
+
const resetManager = this.abandonStalledConnection(uuid, 'mtu-backstop');
|
|
1960
|
+
yield this.dropAndroidLink(uuid, manager, device, 'mtu exchange timeout');
|
|
1961
|
+
if (resetManager)
|
|
1962
|
+
throw this.createWedgedBleSetupError();
|
|
1963
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, 'BLE MTU exchange did not complete, reconnecting on a fresh link');
|
|
1964
|
+
}
|
|
1965
|
+
finally {
|
|
1966
|
+
if (timer)
|
|
1967
|
+
clearTimeout(timer);
|
|
1968
|
+
}
|
|
1969
|
+
});
|
|
1970
|
+
}
|
|
1971
|
+
dropAndroidLink(uuid, manager, device, reason) {
|
|
1972
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1973
|
+
yield this.runNativeTeardown(uuid, manager, () => __awaiter(this, void 0, void 0, function* () {
|
|
1974
|
+
yield Promise.all([
|
|
1975
|
+
this.runBestEffortNativeOperation(`${reason}: cancel manager connection`, () => manager.cancelDeviceConnection(uuid)),
|
|
1976
|
+
this.runBestEffortNativeOperation(`${reason}: cancel device connection`, () => device.cancelConnection()),
|
|
1977
|
+
]);
|
|
1978
|
+
}));
|
|
1979
|
+
const startedAt = Date.now();
|
|
1980
|
+
const target = uuid.toUpperCase();
|
|
1981
|
+
let registryClear = false;
|
|
1982
|
+
let quietPeriodElapsed = false;
|
|
1983
|
+
while (!this.stopped) {
|
|
1984
|
+
const elapsed = Date.now() - startedAt;
|
|
1985
|
+
quietPeriodElapsed = elapsed >= this.androidLinkDropQuietMs;
|
|
1986
|
+
if (!registryClear) {
|
|
1987
|
+
const connected = yield getConnectedDeviceIds([]).catch(() => undefined);
|
|
1988
|
+
registryClear =
|
|
1989
|
+
!!connected &&
|
|
1990
|
+
!connected.some(peripheral => { var _a; return String((_a = peripheral === null || peripheral === void 0 ? void 0 : peripheral.id) !== null && _a !== void 0 ? _a : '').toUpperCase() === target; });
|
|
1991
|
+
}
|
|
1992
|
+
if ((registryClear && quietPeriodElapsed) || elapsed >= this.androidLinkDropTimeoutMs) {
|
|
1993
|
+
break;
|
|
1994
|
+
}
|
|
1995
|
+
yield delay(ANDROID_LINK_DROP_POLL_MS);
|
|
1996
|
+
}
|
|
1997
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE link drop', {
|
|
1998
|
+
connectIdSuffix: uuid.slice(-8),
|
|
1999
|
+
reason,
|
|
2000
|
+
registryClear,
|
|
2001
|
+
quietPeriodElapsed,
|
|
2002
|
+
stopped: this.stopped,
|
|
2003
|
+
elapsedMs: Date.now() - startedAt,
|
|
2004
|
+
});
|
|
2005
|
+
});
|
|
2006
|
+
}
|
|
2007
|
+
resolveCharacteristicsForAcquire(uuid, device) {
|
|
2008
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2009
|
+
try {
|
|
2010
|
+
return yield this.resolveCharacteristicsWithTimeout(uuid, device);
|
|
2011
|
+
}
|
|
2012
|
+
catch (error) {
|
|
2013
|
+
if (reactNative.Platform.OS === 'android' && isMissingGattShapeError(error)) {
|
|
2014
|
+
this.androidGattCacheRefreshes.add(uuid);
|
|
2015
|
+
const manager = this.blePlxManager;
|
|
2016
|
+
if (manager) {
|
|
2017
|
+
yield this.dropAndroidLink(uuid, manager, device, 'stale gatt table');
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
throw error;
|
|
2021
|
+
}
|
|
2022
|
+
});
|
|
2023
|
+
}
|
|
1835
2024
|
connectWithTimeout(uuid, connect) {
|
|
1836
2025
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1837
2026
|
if (this.stopped)
|
|
@@ -2459,6 +2648,12 @@ class ReactNativeBleTransport {
|
|
|
2459
2648
|
if (!this._messages || !this._messagesV2) {
|
|
2460
2649
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
2461
2650
|
}
|
|
2651
|
+
if (reactNative.Platform.OS === 'android') {
|
|
2652
|
+
const activeTransport = transportCache[uuid];
|
|
2653
|
+
if (activeTransport && isKnownDefaultMtu(activeTransport.mtuSize)) {
|
|
2654
|
+
throw createProtocolV2DefaultMtuError(activeTransport.mtuSize);
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2462
2657
|
const isProtocolProbe = this.probingProtocols.get(uuid) === 'V2';
|
|
2463
2658
|
const callOptions = options;
|
|
2464
2659
|
const highThroughputWrite = transport.isProtocolV2HighThroughputCall(name);
|
|
@@ -2639,6 +2834,9 @@ class ReactNativeBleTransport {
|
|
|
2639
2834
|
}
|
|
2640
2835
|
}
|
|
2641
2836
|
|
|
2837
|
+
exports.ANDROID_LINK_DROP_QUIET_MS = ANDROID_LINK_DROP_QUIET_MS;
|
|
2838
|
+
exports.ANDROID_LINK_DROP_TIMEOUT_MS = ANDROID_LINK_DROP_TIMEOUT_MS;
|
|
2839
|
+
exports.ANDROID_MTU_EXCHANGE_TIMEOUT_MS = ANDROID_MTU_EXCHANGE_TIMEOUT_MS;
|
|
2642
2840
|
exports.BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD;
|
|
2643
2841
|
exports.BLE_CONNECT_TIMEOUT_MS = BLE_CONNECT_TIMEOUT_MS;
|
|
2644
2842
|
exports.BLE_GATT_SETUP_TIMEOUT_MS = BLE_GATT_SETUP_TIMEOUT_MS;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-transport-react-native",
|
|
3
|
-
"version": "1.2.2-alpha.
|
|
3
|
+
"version": "1.2.2-alpha.116",
|
|
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.2-alpha.
|
|
24
|
-
"@onekeyfe/hd-shared": "1.2.2-alpha.
|
|
25
|
-
"@onekeyfe/hd-transport": "1.2.2-alpha.
|
|
23
|
+
"@onekeyfe/hd-core": "1.2.2-alpha.116",
|
|
24
|
+
"@onekeyfe/hd-shared": "1.2.2-alpha.116",
|
|
25
|
+
"@onekeyfe/hd-transport": "1.2.2-alpha.116",
|
|
26
26
|
"@onekeyfe/react-native-ble-utils": "0.1.6",
|
|
27
27
|
"react-native-ble-plx": "3.5.1"
|
|
28
28
|
},
|
|
29
|
-
"gitHead": "
|
|
29
|
+
"gitHead": "222deb57d08349d95cfcaa8fac4b705e3aeaff8a"
|
|
30
30
|
}
|
|
@@ -6,6 +6,9 @@ import { ERRORS, HardwareErrorCode, createDeferred } from '@onekeyfe/hd-shared';
|
|
|
6
6
|
|
|
7
7
|
import { onDeviceBondState } from '../BleManager';
|
|
8
8
|
import ReactNativeBleTransport, {
|
|
9
|
+
ANDROID_LINK_DROP_QUIET_MS,
|
|
10
|
+
ANDROID_LINK_DROP_TIMEOUT_MS,
|
|
11
|
+
ANDROID_MTU_EXCHANGE_TIMEOUT_MS,
|
|
9
12
|
BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD,
|
|
10
13
|
BLE_CONNECT_TIMEOUT_MS,
|
|
11
14
|
BLE_GATT_SETUP_TIMEOUT_MS,
|
|
@@ -641,3 +644,104 @@ describe('BLE connect timeout', () => {
|
|
|
641
644
|
expect((transport as any).connectionSetupTimeoutCounts.has(UUID)).toBe(false);
|
|
642
645
|
});
|
|
643
646
|
});
|
|
647
|
+
|
|
648
|
+
describe('Android MTU and link-drop bounds', () => {
|
|
649
|
+
beforeAll(() => {
|
|
650
|
+
jest.useFakeTimers({ doNotFake: ['setImmediate', 'performance'] });
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
afterAll(() => {
|
|
654
|
+
jest.useRealTimers();
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
afterEach(() => {
|
|
658
|
+
jest.clearAllTimers();
|
|
659
|
+
jest.restoreAllMocks();
|
|
660
|
+
Object.assign(Platform, { OS: 'ios' });
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
test('the bounds sit above a cold-cache discovery and the 4s GATT link idle timer', () => {
|
|
664
|
+
// Single discovery passes on a Pro 2 at MTU 23 measured up to 4.8s, a refresh plus
|
|
665
|
+
// restart 7.7s; a stuck exchange never completes, so the bound only has to clear those.
|
|
666
|
+
expect(ANDROID_MTU_EXCHANGE_TIMEOUT_MS).toBeGreaterThanOrEqual(10_000);
|
|
667
|
+
expect(ANDROID_MTU_EXCHANGE_TIMEOUT_MS).toBeLessThanOrEqual(20_000);
|
|
668
|
+
// Android keeps the LE link for 4s after the last GATT client closes.
|
|
669
|
+
expect(ANDROID_LINK_DROP_QUIET_MS).toBeGreaterThan(4000);
|
|
670
|
+
expect(ANDROID_LINK_DROP_QUIET_MS).toBeLessThanOrEqual(ANDROID_LINK_DROP_TIMEOUT_MS);
|
|
671
|
+
expect(ANDROID_LINK_DROP_TIMEOUT_MS).toBeLessThanOrEqual(10_000);
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
test('holds an unusable link past the idle timer even when the GATT registry is already empty', async () => {
|
|
675
|
+
Object.assign(Platform, { OS: 'android' });
|
|
676
|
+
const { transport, device, bleManager } = createHarness(() => Promise.resolve(device));
|
|
677
|
+
device.isConnected.mockResolvedValue(true);
|
|
678
|
+
Object.assign(device, { mtu: 23, requestMTU: jest.fn(() => new Promise(() => {})) });
|
|
679
|
+
let settled: Error | undefined;
|
|
680
|
+
const acquire = transport.acquire({ uuid: UUID, expectedProtocol: 'V1' }).catch(error => {
|
|
681
|
+
settled = error;
|
|
682
|
+
});
|
|
683
|
+
await flush();
|
|
684
|
+
await flush();
|
|
685
|
+
expect(device.requestMTU).toHaveBeenCalledTimes(1);
|
|
686
|
+
|
|
687
|
+
jest.advanceTimersByTime(ANDROID_MTU_EXCHANGE_TIMEOUT_MS);
|
|
688
|
+
await flush();
|
|
689
|
+
await flush();
|
|
690
|
+
expect(bleManager.cancelDeviceConnection).toHaveBeenCalledWith(UUID);
|
|
691
|
+
|
|
692
|
+
// BluetoothManager.getConnectedDevices(GATT) reports this app's GATT client, which is
|
|
693
|
+
// already closed; the LE link itself stays up for the idle timer, so the wait must not
|
|
694
|
+
// release on that signal alone.
|
|
695
|
+
expect(
|
|
696
|
+
(BleUtils as unknown as { getConnectedPeripherals: jest.Mock }).getConnectedPeripherals
|
|
697
|
+
).toHaveBeenCalled();
|
|
698
|
+
jest.advanceTimersByTime(4000);
|
|
699
|
+
await flush();
|
|
700
|
+
await flush();
|
|
701
|
+
expect(settled).toBeUndefined();
|
|
702
|
+
|
|
703
|
+
await advanceUntil(() => !!settled, ANDROID_LINK_DROP_TIMEOUT_MS);
|
|
704
|
+
await acquire;
|
|
705
|
+
expect(settled).toMatchObject({ errorCode: HardwareErrorCode.BleConnectedError });
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
test('a slow but successful MTU exchange completes inside the bound', async () => {
|
|
709
|
+
Object.assign(Platform, { OS: 'android' });
|
|
710
|
+
const { transport, device } = createHarness(() => Promise.resolve(device));
|
|
711
|
+
device.isConnected.mockResolvedValue(true);
|
|
712
|
+
const negotiated = { ...device, mtu: 247 };
|
|
713
|
+
Object.assign(device, {
|
|
714
|
+
mtu: 23,
|
|
715
|
+
// A cold cache makes the stack discover first; the exchange then completes late.
|
|
716
|
+
requestMTU: jest.fn(
|
|
717
|
+
() =>
|
|
718
|
+
new Promise(resolve => {
|
|
719
|
+
setTimeout(() => resolve(negotiated), 8000);
|
|
720
|
+
})
|
|
721
|
+
),
|
|
722
|
+
});
|
|
723
|
+
const [, notifyCharacteristic] = await device.characteristicsForService();
|
|
724
|
+
Object.assign(notifyCharacteristic, { monitor: jest.fn(() => ({ remove: jest.fn() })) });
|
|
725
|
+
let result: unknown;
|
|
726
|
+
let failure: Error | undefined;
|
|
727
|
+
const acquire = transport.acquire({ uuid: UUID, expectedProtocol: 'V1' }).then(
|
|
728
|
+
value => {
|
|
729
|
+
result = value;
|
|
730
|
+
},
|
|
731
|
+
error => {
|
|
732
|
+
failure = error;
|
|
733
|
+
}
|
|
734
|
+
);
|
|
735
|
+
await flush();
|
|
736
|
+
await flush();
|
|
737
|
+
expect(device.discoverAllServicesAndCharacteristics).not.toHaveBeenCalled();
|
|
738
|
+
|
|
739
|
+
jest.advanceTimersByTime(8000);
|
|
740
|
+
await advanceUntil(() => result !== undefined || failure !== undefined, 5000);
|
|
741
|
+
await acquire;
|
|
742
|
+
expect(failure).toBeUndefined();
|
|
743
|
+
expect(result).toEqual({ uuid: UUID, protocolType: 'V1' });
|
|
744
|
+
expect(device.discoverAllServicesAndCharacteristics).toHaveBeenCalledTimes(1);
|
|
745
|
+
await transport.release(UUID, true);
|
|
746
|
+
});
|
|
747
|
+
});
|