@onekeyfe/hd-transport-react-native 1.2.0-alpha.71 → 1.2.0-alpha.72
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/bleStrategy.d.ts +3 -2
- package/dist/bleStrategy.d.ts.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -16
- package/package.json +5 -5
- package/src/__tests__/bleStrategy.test.ts +25 -7
- package/src/__tests__/protocolV2Link.test.ts +64 -5
- package/src/bleStrategy.ts +10 -8
- package/src/constants.ts +1 -1
- package/src/index.ts +29 -11
package/dist/bleStrategy.d.ts
CHANGED
|
@@ -10,9 +10,10 @@ export declare function resolveProtocolV2PacketCapacity({ platform, iosPacketLen
|
|
|
10
10
|
androidPacketLength?: number;
|
|
11
11
|
mtu?: number | null;
|
|
12
12
|
}): number;
|
|
13
|
-
export declare function
|
|
13
|
+
export declare function shouldRefreshNegotiatedMtu(mtu?: number | null): boolean;
|
|
14
|
+
export declare function shouldWriteProtocolV2WithResponse({ platform, highThroughput, requestedWithResponse, characteristic, }: {
|
|
14
15
|
platform: BlePlatform;
|
|
15
|
-
|
|
16
|
+
highThroughput: boolean;
|
|
16
17
|
requestedWithResponse?: boolean;
|
|
17
18
|
characteristic: BleWriteCapability;
|
|
18
19
|
}): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bleStrategy.d.ts","sourceRoot":"","sources":["../src/bleStrategy.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,SAAS,GAAG,MAAM,CAAC;AAErD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,sBAAsB,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACxC,yBAAyB,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CAC5C,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,cAAc,EAAE,kBAAkB,WAEvE;AAED,wBAAgB,+BAA+B,CAAC,EAC9C,QAAQ,EACR,eAA+C,EAC/C,mBAAuD,EACvD,GAAG,GACJ,EAAE;IACD,QAAQ,EAAE,WAAW,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB,
|
|
1
|
+
{"version":3,"file":"bleStrategy.d.ts","sourceRoot":"","sources":["../src/bleStrategy.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,SAAS,GAAG,MAAM,CAAC;AAErD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,sBAAsB,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACxC,yBAAyB,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CAC5C,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,cAAc,EAAE,kBAAkB,WAEvE;AAED,wBAAgB,+BAA+B,CAAC,EAC9C,QAAQ,EACR,eAA+C,EAC/C,mBAAuD,EACvD,GAAG,GACJ,EAAE;IACD,QAAQ,EAAE,WAAW,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB,UAMA;AAED,wBAAgB,0BAA0B,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,WAE7D;AAED,wBAAgB,iCAAiC,CAAC,EAChD,QAAQ,EACR,cAAc,EACd,qBAAqB,EACrB,cAAc,GACf,EAAE;IACD,QAAQ,EAAE,WAAW,CAAC;IACtB,cAAc,EAAE,OAAO,CAAC;IACxB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,cAAc,EAAE,kBAAkB,CAAC;CACpC,WAIA"}
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare const IOS_PACKET_LENGTH = 128;
|
|
2
2
|
export declare const ANDROID_PACKET_LENGTH = 192;
|
|
3
3
|
export declare const IOS_PROTOCOL_V2_PACKET_LENGTH = 244;
|
|
4
|
-
export declare const ANDROID_PROTOCOL_V2_PACKET_LENGTH =
|
|
4
|
+
export declare const ANDROID_PROTOCOL_V2_PACKET_LENGTH = 244;
|
|
5
5
|
export declare const getBluetoothServiceUuids: () => string[];
|
|
6
6
|
export declare const getInfosForServiceUuid: (serviceUuid: string, deviceType: 'classic') => {
|
|
7
7
|
serviceUuid: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -197,6 +197,7 @@ declare class ReactNativeBleTransport {
|
|
|
197
197
|
private writeProtocolV2Packet;
|
|
198
198
|
private writeProtocolV2Frame;
|
|
199
199
|
private callProtocolV2;
|
|
200
|
+
private ensureProtocolV2HighThroughputMtu;
|
|
200
201
|
private clearAndroidPriorityResetTimer;
|
|
201
202
|
private enableAndroidHighConnectionPriority;
|
|
202
203
|
private scheduleAndroidBalancedConnectionPriority;
|
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,EAIL,UAAU,IAAI,aAAa,EAG5B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -94,7 +94,7 @@ const onDeviceBondState = (bleMacAddress) => new Promise((resolve, reject) => {
|
|
|
94
94
|
const IOS_PACKET_LENGTH = 128;
|
|
95
95
|
const ANDROID_PACKET_LENGTH = 192;
|
|
96
96
|
const IOS_PROTOCOL_V2_PACKET_LENGTH = 244;
|
|
97
|
-
const ANDROID_PROTOCOL_V2_PACKET_LENGTH =
|
|
97
|
+
const ANDROID_PROTOCOL_V2_PACKET_LENGTH = 244;
|
|
98
98
|
const ClassicServiceUUID = '00000001-0000-1000-8000-00805f9b34fb';
|
|
99
99
|
const OneKeyServices = {
|
|
100
100
|
classic: {
|
|
@@ -136,19 +136,20 @@ function hasWritableCapability(characteristic) {
|
|
|
136
136
|
return !!(characteristic.isWritableWithResponse || characteristic.isWritableWithoutResponse);
|
|
137
137
|
}
|
|
138
138
|
function resolveProtocolV2PacketCapacity({ platform, iosPacketLength = IOS_PROTOCOL_V2_PACKET_LENGTH, androidPacketLength = ANDROID_PROTOCOL_V2_PACKET_LENGTH, mtu, }) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
const payloadLength = Math.floor(mtu) - 3;
|
|
139
|
+
const negotiatedMtu = typeof mtu === 'number' && Number.isFinite(mtu) && mtu > 3 ? Math.floor(mtu) : 23;
|
|
140
|
+
const payloadLength = negotiatedMtu - 3;
|
|
143
141
|
const configuredPacketLength = platform === 'ios' ? iosPacketLength : androidPacketLength;
|
|
144
142
|
return Math.min(configuredPacketLength, payloadLength);
|
|
145
143
|
}
|
|
146
|
-
function
|
|
144
|
+
function shouldRefreshNegotiatedMtu(mtu) {
|
|
145
|
+
return typeof mtu !== 'number' || !Number.isFinite(mtu) || mtu <= 23;
|
|
146
|
+
}
|
|
147
|
+
function shouldWriteProtocolV2WithResponse({ platform, highThroughput, requestedWithResponse, characteristic, }) {
|
|
147
148
|
if (!characteristic.isWritableWithResponse)
|
|
148
149
|
return false;
|
|
149
150
|
if (!characteristic.isWritableWithoutResponse)
|
|
150
151
|
return true;
|
|
151
|
-
return requestedWithResponse === true || (platform === 'ios' && !
|
|
152
|
+
return requestedWithResponse === true || (platform === 'ios' && !highThroughput);
|
|
152
153
|
}
|
|
153
154
|
|
|
154
155
|
const timer = process.env.NODE_ENV === 'development'
|
|
@@ -321,7 +322,6 @@ function getDeviceDisplayName(device) {
|
|
|
321
322
|
}
|
|
322
323
|
const IOS_REQUEST_MTU = 247;
|
|
323
324
|
const ANDROID_REQUEST_MTU = 517;
|
|
324
|
-
const BLE_MTU_REFRESH_THRESHOLD = 247;
|
|
325
325
|
const BLE_MTU_REFRESH_RETRY_DELAY_MS = 200;
|
|
326
326
|
const ANDROID_HIGH_PRIORITY_IDLE_MS = 1000;
|
|
327
327
|
const getRequestedBleMtu = () => reactNative.Platform.OS === 'android' ? ANDROID_REQUEST_MTU : IOS_REQUEST_MTU;
|
|
@@ -775,13 +775,13 @@ class ReactNativeBleTransport {
|
|
|
775
775
|
const initialMtu = transport$1.mtuSize;
|
|
776
776
|
let refreshAttempts = 0;
|
|
777
777
|
if ((reactNative.Platform.OS === 'ios' || reactNative.Platform.OS === 'android') &&
|
|
778
|
-
(
|
|
778
|
+
shouldRefreshNegotiatedMtu(transport$1.mtuSize)) {
|
|
779
779
|
refreshAttempts += 1;
|
|
780
780
|
let refreshedDevice = yield requestNegotiatedMtu(transport$1.device, 'servicesAndNotifyReady', 1);
|
|
781
781
|
transport$1.device = refreshedDevice;
|
|
782
782
|
transport$1.mtuSize =
|
|
783
783
|
typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport$1.mtuSize;
|
|
784
|
-
if (
|
|
784
|
+
if (shouldRefreshNegotiatedMtu(transport$1.mtuSize)) {
|
|
785
785
|
yield delay(BLE_MTU_REFRESH_RETRY_DELAY_MS);
|
|
786
786
|
refreshAttempts += 1;
|
|
787
787
|
refreshedDevice = yield requestNegotiatedMtu(transport$1.device, 'servicesAndNotifyReady', 2);
|
|
@@ -1842,7 +1842,7 @@ class ReactNativeBleTransport {
|
|
|
1842
1842
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1843
1843
|
const shouldUseWriteWithResponse = shouldWriteProtocolV2WithResponse({
|
|
1844
1844
|
platform: reactNative.Platform.OS,
|
|
1845
|
-
|
|
1845
|
+
highThroughput: context.highThroughput,
|
|
1846
1846
|
requestedWithResponse: context.writeWithResponse,
|
|
1847
1847
|
characteristic: transport.writeCharacteristic,
|
|
1848
1848
|
});
|
|
@@ -1911,13 +1911,14 @@ class ReactNativeBleTransport {
|
|
|
1911
1911
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
1912
1912
|
}
|
|
1913
1913
|
const callOptions = options;
|
|
1914
|
-
const
|
|
1915
|
-
if (
|
|
1914
|
+
const highThroughputWrite = transport.isProtocolV2HighThroughputCall(name);
|
|
1915
|
+
if (highThroughputWrite) {
|
|
1916
|
+
yield this.ensureProtocolV2HighThroughputMtu(uuid);
|
|
1916
1917
|
const tuning = getProtocolV2BleTuning();
|
|
1917
1918
|
const currentTransport = this.getCachedTransport(uuid);
|
|
1918
1919
|
const writeWithResponse = shouldWriteProtocolV2WithResponse({
|
|
1919
1920
|
platform: reactNative.Platform.OS,
|
|
1920
|
-
|
|
1921
|
+
highThroughput: true,
|
|
1921
1922
|
requestedWithResponse: options === null || options === void 0 ? void 0 : options.writeWithResponse,
|
|
1922
1923
|
characteristic: currentTransport.writeCharacteristic,
|
|
1923
1924
|
});
|
|
@@ -1941,7 +1942,7 @@ class ReactNativeBleTransport {
|
|
|
1941
1942
|
});
|
|
1942
1943
|
}
|
|
1943
1944
|
}
|
|
1944
|
-
if (
|
|
1945
|
+
if (highThroughputWrite) {
|
|
1945
1946
|
yield this.enableAndroidHighConnectionPriority(uuid);
|
|
1946
1947
|
}
|
|
1947
1948
|
try {
|
|
@@ -1952,12 +1953,26 @@ class ReactNativeBleTransport {
|
|
|
1952
1953
|
throw e;
|
|
1953
1954
|
}
|
|
1954
1955
|
finally {
|
|
1955
|
-
if (
|
|
1956
|
+
if (highThroughputWrite) {
|
|
1956
1957
|
this.scheduleAndroidBalancedConnectionPriority(uuid);
|
|
1957
1958
|
}
|
|
1958
1959
|
}
|
|
1959
1960
|
});
|
|
1960
1961
|
}
|
|
1962
|
+
ensureProtocolV2HighThroughputMtu(uuid) {
|
|
1963
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1964
|
+
const transport = this.getCachedTransport(uuid);
|
|
1965
|
+
if (!shouldRefreshNegotiatedMtu(transport.mtuSize))
|
|
1966
|
+
return;
|
|
1967
|
+
const refreshedDevice = yield requestNegotiatedMtu(transport.device, 'highThroughput', 1);
|
|
1968
|
+
transport.device = refreshedDevice;
|
|
1969
|
+
transport.mtuSize =
|
|
1970
|
+
typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport.mtuSize;
|
|
1971
|
+
if (shouldRefreshNegotiatedMtu(transport.mtuSize)) {
|
|
1972
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `Protocol V2 high-throughput BLE MTU unavailable: ${String(transport.mtuSize)}`);
|
|
1973
|
+
}
|
|
1974
|
+
});
|
|
1975
|
+
}
|
|
1961
1976
|
clearAndroidPriorityResetTimer(uuid) {
|
|
1962
1977
|
const timerId = this.androidPriorityResetTimers.get(uuid);
|
|
1963
1978
|
if (timerId !== undefined) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-transport-react-native",
|
|
3
|
-
"version": "1.2.0-alpha.
|
|
3
|
+
"version": "1.2.0-alpha.72",
|
|
4
4
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -20,11 +20,11 @@
|
|
|
20
20
|
"lint:fix": "eslint . --fix"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@onekeyfe/hd-core": "1.2.0-alpha.
|
|
24
|
-
"@onekeyfe/hd-shared": "1.2.0-alpha.
|
|
25
|
-
"@onekeyfe/hd-transport": "1.2.0-alpha.
|
|
23
|
+
"@onekeyfe/hd-core": "1.2.0-alpha.72",
|
|
24
|
+
"@onekeyfe/hd-shared": "1.2.0-alpha.72",
|
|
25
|
+
"@onekeyfe/hd-transport": "1.2.0-alpha.72",
|
|
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": "76311ccd3753eca8e492b5324f93542bd38525ba"
|
|
30
30
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
hasWritableCapability,
|
|
3
3
|
resolveProtocolV2PacketCapacity,
|
|
4
|
+
shouldRefreshNegotiatedMtu,
|
|
4
5
|
shouldWriteProtocolV2WithResponse,
|
|
5
6
|
} from '../bleStrategy';
|
|
6
7
|
|
|
@@ -14,13 +15,20 @@ describe('React Native BLE strategy', () => {
|
|
|
14
15
|
expect(hasWritableCapability(characteristic)).toBe(true);
|
|
15
16
|
});
|
|
16
17
|
|
|
17
|
-
test('
|
|
18
|
-
expect(
|
|
18
|
+
test('uses the conservative ATT payload when MTU is unavailable', () => {
|
|
19
|
+
expect(
|
|
19
20
|
resolveProtocolV2PacketCapacity({
|
|
20
21
|
platform: 'android',
|
|
21
22
|
mtu: null,
|
|
22
23
|
})
|
|
23
|
-
).
|
|
24
|
+
).toBe(20);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('only refreshes an unavailable or default MTU snapshot', () => {
|
|
28
|
+
expect(shouldRefreshNegotiatedMtu(undefined)).toBe(true);
|
|
29
|
+
expect(shouldRefreshNegotiatedMtu(23)).toBe(true);
|
|
30
|
+
expect(shouldRefreshNegotiatedMtu(185)).toBe(false);
|
|
31
|
+
expect(shouldRefreshNegotiatedMtu(247)).toBe(false);
|
|
24
32
|
});
|
|
25
33
|
|
|
26
34
|
test('caps Android packet length by negotiated MTU payload', () => {
|
|
@@ -33,10 +41,20 @@ describe('React Native BLE strategy', () => {
|
|
|
33
41
|
).toBe(97);
|
|
34
42
|
});
|
|
35
43
|
|
|
36
|
-
test('
|
|
44
|
+
test('keeps the default Android packet length within the validated ceiling', () => {
|
|
45
|
+
expect(
|
|
46
|
+
resolveProtocolV2PacketCapacity({
|
|
47
|
+
platform: 'android',
|
|
48
|
+
mtu: 517,
|
|
49
|
+
})
|
|
50
|
+
).toBe(244);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('allows an explicit Android packet length override for validated experiments', () => {
|
|
37
54
|
expect(
|
|
38
55
|
resolveProtocolV2PacketCapacity({
|
|
39
56
|
platform: 'android',
|
|
57
|
+
androidPacketLength: 514,
|
|
40
58
|
mtu: 517,
|
|
41
59
|
})
|
|
42
60
|
).toBe(514);
|
|
@@ -81,7 +99,7 @@ describe('React Native BLE strategy', () => {
|
|
|
81
99
|
expect(
|
|
82
100
|
shouldWriteProtocolV2WithResponse({
|
|
83
101
|
platform: 'ios',
|
|
84
|
-
|
|
102
|
+
highThroughput: true,
|
|
85
103
|
requestedWithResponse: false,
|
|
86
104
|
characteristic,
|
|
87
105
|
})
|
|
@@ -89,7 +107,7 @@ describe('React Native BLE strategy', () => {
|
|
|
89
107
|
expect(
|
|
90
108
|
shouldWriteProtocolV2WithResponse({
|
|
91
109
|
platform: 'ios',
|
|
92
|
-
|
|
110
|
+
highThroughput: true,
|
|
93
111
|
requestedWithResponse: true,
|
|
94
112
|
characteristic,
|
|
95
113
|
})
|
|
@@ -100,7 +118,7 @@ describe('React Native BLE strategy', () => {
|
|
|
100
118
|
expect(
|
|
101
119
|
shouldWriteProtocolV2WithResponse({
|
|
102
120
|
platform: 'ios',
|
|
103
|
-
|
|
121
|
+
highThroughput: true,
|
|
104
122
|
requestedWithResponse: false,
|
|
105
123
|
characteristic: {
|
|
106
124
|
isWritableWithResponse: true,
|
|
@@ -491,6 +491,63 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
491
491
|
await transport.release(uuid, true);
|
|
492
492
|
});
|
|
493
493
|
|
|
494
|
+
test('accepts a stable low MTU without the delayed refresh loop', async () => {
|
|
495
|
+
const { transport, uuid, device } = createHarness();
|
|
496
|
+
device.mtu = 185;
|
|
497
|
+
|
|
498
|
+
await expect(transport.acquire({ uuid })).resolves.toEqual({
|
|
499
|
+
uuid,
|
|
500
|
+
protocolType: 'V2',
|
|
501
|
+
});
|
|
502
|
+
expect(device.requestMTU).toHaveBeenCalledTimes(1);
|
|
503
|
+
expect((transport as any).getCachedTransport(uuid).mtuSize).toBe(185);
|
|
504
|
+
await transport.release(uuid, true);
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
test('continues Protocol V2 probing with a conservative packet size when MTU is unavailable', async () => {
|
|
508
|
+
const { transport, uuid, device, writeCharacteristic } = createHarness();
|
|
509
|
+
device.mtu = undefined;
|
|
510
|
+
|
|
511
|
+
await expect(transport.acquire({ uuid })).resolves.toEqual({
|
|
512
|
+
uuid,
|
|
513
|
+
protocolType: 'V2',
|
|
514
|
+
});
|
|
515
|
+
expect(device.requestMTU).toHaveBeenCalledTimes(3);
|
|
516
|
+
expect((transport as any).getCachedTransport(uuid).mtuSize).toBeUndefined();
|
|
517
|
+
expect(writeCharacteristic.writeWithResponse).toHaveBeenCalled();
|
|
518
|
+
await transport.release(uuid, true);
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
test('refreshes an unavailable MTU before a Protocol V2 high-volume write', async () => {
|
|
522
|
+
const { transport, uuid, device, writeCharacteristic } = createHarness();
|
|
523
|
+
device.mtu = undefined;
|
|
524
|
+
|
|
525
|
+
await transport.acquire({ uuid, expectedProtocol: 'V2' });
|
|
526
|
+
device.requestMTU.mockImplementationOnce(() => {
|
|
527
|
+
device.mtu = 247;
|
|
528
|
+
return Promise.resolve(device);
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
await expect(transport.call(uuid, 'FileWrite', {})).resolves.toBeDefined();
|
|
532
|
+
expect(device.requestMTU).toHaveBeenCalledTimes(4);
|
|
533
|
+
expect(writeCharacteristic.writeWithoutResponse).toHaveBeenCalledTimes(1);
|
|
534
|
+
await transport.release(uuid, true);
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
test('rejects a Protocol V2 high-volume write when MTU remains unavailable', async () => {
|
|
538
|
+
const { transport, uuid, device, writeCharacteristic } = createHarness();
|
|
539
|
+
device.mtu = undefined;
|
|
540
|
+
|
|
541
|
+
await transport.acquire({ uuid, expectedProtocol: 'V2' });
|
|
542
|
+
|
|
543
|
+
await expect(transport.call(uuid, 'FileWrite', {})).rejects.toMatchObject({
|
|
544
|
+
errorCode: HardwareErrorCode.BleConnectedError,
|
|
545
|
+
});
|
|
546
|
+
expect(device.requestMTU).toHaveBeenCalledTimes(4);
|
|
547
|
+
expect(writeCharacteristic.writeWithoutResponse).not.toHaveBeenCalled();
|
|
548
|
+
await transport.release(uuid, true);
|
|
549
|
+
});
|
|
550
|
+
|
|
494
551
|
test('reconnects before falling back to Protocol V1 after a fatal V2 probe failure', async () => {
|
|
495
552
|
setPlatformOS('android');
|
|
496
553
|
const { transport, uuid, device, notifySubscriptionRemovers, disconnectSubscriptionRemovers } =
|
|
@@ -692,7 +749,7 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
692
749
|
const context = {
|
|
693
750
|
messageName: 'ProtocolInfoRequest',
|
|
694
751
|
timeoutMs: 1000,
|
|
695
|
-
|
|
752
|
+
highThroughput: false,
|
|
696
753
|
generation: 1,
|
|
697
754
|
signal: new AbortController().signal,
|
|
698
755
|
};
|
|
@@ -726,7 +783,7 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
726
783
|
const context = {
|
|
727
784
|
messageName: 'ProtocolInfoRequest',
|
|
728
785
|
timeoutMs: 1000,
|
|
729
|
-
|
|
786
|
+
highThroughput: false,
|
|
730
787
|
generation: 1,
|
|
731
788
|
signal: new AbortController().signal,
|
|
732
789
|
};
|
|
@@ -743,6 +800,7 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
743
800
|
);
|
|
744
801
|
|
|
745
802
|
await call;
|
|
803
|
+
// The only scheduled timer is the per-packet BLE write watchdog: no pacing delay.
|
|
746
804
|
expect(setTimeoutSpy.mock.calls.map(([, timeout]) => timeout)).toEqual([
|
|
747
805
|
BLE_WRITE_PACKET_TIMEOUT_MS,
|
|
748
806
|
]);
|
|
@@ -801,7 +859,7 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
801
859
|
const context = {
|
|
802
860
|
messageName: 'Ping',
|
|
803
861
|
timeoutMs: 1000,
|
|
804
|
-
|
|
862
|
+
highThroughput: false,
|
|
805
863
|
generation: 1,
|
|
806
864
|
signal: new AbortController().signal,
|
|
807
865
|
};
|
|
@@ -834,7 +892,7 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
834
892
|
const context = {
|
|
835
893
|
messageName: 'FileWrite',
|
|
836
894
|
timeoutMs: 1000,
|
|
837
|
-
|
|
895
|
+
highThroughput: true,
|
|
838
896
|
generation: 1,
|
|
839
897
|
signal: new AbortController().signal,
|
|
840
898
|
};
|
|
@@ -862,7 +920,7 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
862
920
|
const context = {
|
|
863
921
|
messageName: 'FileWrite',
|
|
864
922
|
timeoutMs: 1000,
|
|
865
|
-
|
|
923
|
+
highThroughput: true,
|
|
866
924
|
generation: 1,
|
|
867
925
|
signal: new AbortController().signal,
|
|
868
926
|
};
|
|
@@ -878,6 +936,7 @@ describe('ReactNativeBleTransport Protocol V2 link lifecycle', () => {
|
|
|
878
936
|
);
|
|
879
937
|
|
|
880
938
|
expect(writeWithoutResponse).toHaveBeenCalledTimes(3);
|
|
939
|
+
// One BLE write watchdog per packet and nothing else: no burst or flush pauses.
|
|
881
940
|
expect(setTimeoutSpy.mock.calls.map(([, timeout]) => timeout)).toEqual([
|
|
882
941
|
BLE_WRITE_PACKET_TIMEOUT_MS,
|
|
883
942
|
BLE_WRITE_PACKET_TIMEOUT_MS,
|
package/src/bleStrategy.ts
CHANGED
|
@@ -22,27 +22,29 @@ export function resolveProtocolV2PacketCapacity({
|
|
|
22
22
|
androidPacketLength?: number;
|
|
23
23
|
mtu?: number | null;
|
|
24
24
|
}) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const payloadLength = Math.floor(mtu) - 3;
|
|
25
|
+
const negotiatedMtu =
|
|
26
|
+
typeof mtu === 'number' && Number.isFinite(mtu) && mtu > 3 ? Math.floor(mtu) : 23;
|
|
27
|
+
const payloadLength = negotiatedMtu - 3;
|
|
30
28
|
const configuredPacketLength = platform === 'ios' ? iosPacketLength : androidPacketLength;
|
|
31
29
|
return Math.min(configuredPacketLength, payloadLength);
|
|
32
30
|
}
|
|
33
31
|
|
|
32
|
+
export function shouldRefreshNegotiatedMtu(mtu?: number | null) {
|
|
33
|
+
return typeof mtu !== 'number' || !Number.isFinite(mtu) || mtu <= 23;
|
|
34
|
+
}
|
|
35
|
+
|
|
34
36
|
export function shouldWriteProtocolV2WithResponse({
|
|
35
37
|
platform,
|
|
36
|
-
|
|
38
|
+
highThroughput,
|
|
37
39
|
requestedWithResponse,
|
|
38
40
|
characteristic,
|
|
39
41
|
}: {
|
|
40
42
|
platform: BlePlatform;
|
|
41
|
-
|
|
43
|
+
highThroughput: boolean;
|
|
42
44
|
requestedWithResponse?: boolean;
|
|
43
45
|
characteristic: BleWriteCapability;
|
|
44
46
|
}) {
|
|
45
47
|
if (!characteristic.isWritableWithResponse) return false;
|
|
46
48
|
if (!characteristic.isWritableWithoutResponse) return true;
|
|
47
|
-
return requestedWithResponse === true || (platform === 'ios' && !
|
|
49
|
+
return requestedWithResponse === true || (platform === 'ios' && !highThroughput);
|
|
48
50
|
}
|
package/src/constants.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { createKnownBleUuidAliases, matchesKnownBleUuid } from '@onekeyfe/hd-sha
|
|
|
3
3
|
export const IOS_PACKET_LENGTH = 128;
|
|
4
4
|
export const ANDROID_PACKET_LENGTH = 192;
|
|
5
5
|
export const IOS_PROTOCOL_V2_PACKET_LENGTH = 244;
|
|
6
|
-
export const ANDROID_PROTOCOL_V2_PACKET_LENGTH =
|
|
6
|
+
export const ANDROID_PROTOCOL_V2_PACKET_LENGTH = 244;
|
|
7
7
|
|
|
8
8
|
type BluetoothServices = Record<
|
|
9
9
|
string,
|
package/src/index.ts
CHANGED
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
} from 'react-native-ble-plx';
|
|
11
11
|
import ByteBuffer from 'bytebuffer';
|
|
12
12
|
import transport, {
|
|
13
|
-
LogBlockCommand,
|
|
14
13
|
type OneKeyDeviceInfoBase,
|
|
15
14
|
PROTOCOL_V1_MESSAGE_HEADER_SIZE,
|
|
16
15
|
PROTOCOL_V2_BLE_FRAME_MAX_BYTES,
|
|
@@ -21,6 +20,7 @@ import transport, {
|
|
|
21
20
|
ProtocolV2LinkManager,
|
|
22
21
|
TRANSPORT_EVENT,
|
|
23
22
|
type TransportCallOptions,
|
|
23
|
+
isProtocolV2HighThroughputCall,
|
|
24
24
|
probeProtocolV2 as probeProtocolV2Helper,
|
|
25
25
|
writeProtocolV2BleFrame,
|
|
26
26
|
} from '@onekeyfe/hd-transport';
|
|
@@ -36,6 +36,7 @@ import { getConnectedDeviceIds, onDeviceBondState, pairDevice } from './BleManag
|
|
|
36
36
|
import {
|
|
37
37
|
hasWritableCapability,
|
|
38
38
|
resolveProtocolV2PacketCapacity,
|
|
39
|
+
shouldRefreshNegotiatedMtu,
|
|
39
40
|
shouldWriteProtocolV2WithResponse,
|
|
40
41
|
} from './bleStrategy';
|
|
41
42
|
import { subscribeBleOn } from './subscribeBleOn';
|
|
@@ -213,7 +214,6 @@ function getDeviceDisplayName(device?: Device | null) {
|
|
|
213
214
|
|
|
214
215
|
const IOS_REQUEST_MTU = 247;
|
|
215
216
|
const ANDROID_REQUEST_MTU = 517;
|
|
216
|
-
const BLE_MTU_REFRESH_THRESHOLD = 247;
|
|
217
217
|
const BLE_MTU_REFRESH_RETRY_DELAY_MS = 200;
|
|
218
218
|
const ANDROID_HIGH_PRIORITY_IDLE_MS = 1000;
|
|
219
219
|
|
|
@@ -279,7 +279,7 @@ const tryToGetConfiguration = (device: Device) => {
|
|
|
279
279
|
|
|
280
280
|
const requestNegotiatedMtu = async (
|
|
281
281
|
device: Device,
|
|
282
|
-
stage: 'connected' | 'servicesAndNotifyReady',
|
|
282
|
+
stage: 'connected' | 'servicesAndNotifyReady' | 'highThroughput',
|
|
283
283
|
attempt: number
|
|
284
284
|
) => {
|
|
285
285
|
if (Platform.OS !== 'ios' && Platform.OS !== 'android') return device;
|
|
@@ -835,7 +835,7 @@ export default class ReactNativeBleTransport {
|
|
|
835
835
|
let refreshAttempts = 0;
|
|
836
836
|
if (
|
|
837
837
|
(Platform.OS === 'ios' || Platform.OS === 'android') &&
|
|
838
|
-
(
|
|
838
|
+
shouldRefreshNegotiatedMtu(transport.mtuSize)
|
|
839
839
|
) {
|
|
840
840
|
refreshAttempts += 1;
|
|
841
841
|
let refreshedDevice = await requestNegotiatedMtu(
|
|
@@ -847,7 +847,7 @@ export default class ReactNativeBleTransport {
|
|
|
847
847
|
transport.mtuSize =
|
|
848
848
|
typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport.mtuSize;
|
|
849
849
|
|
|
850
|
-
if (
|
|
850
|
+
if (shouldRefreshNegotiatedMtu(transport.mtuSize)) {
|
|
851
851
|
await delay(BLE_MTU_REFRESH_RETRY_DELAY_MS);
|
|
852
852
|
refreshAttempts += 1;
|
|
853
853
|
refreshedDevice = await requestNegotiatedMtu(transport.device, 'servicesAndNotifyReady', 2);
|
|
@@ -2173,7 +2173,7 @@ export default class ReactNativeBleTransport {
|
|
|
2173
2173
|
) {
|
|
2174
2174
|
const shouldUseWriteWithResponse = shouldWriteProtocolV2WithResponse({
|
|
2175
2175
|
platform: Platform.OS,
|
|
2176
|
-
|
|
2176
|
+
highThroughput: context.highThroughput,
|
|
2177
2177
|
requestedWithResponse: context.writeWithResponse,
|
|
2178
2178
|
characteristic: transport.writeCharacteristic,
|
|
2179
2179
|
});
|
|
@@ -2266,14 +2266,15 @@ export default class ReactNativeBleTransport {
|
|
|
2266
2266
|
}
|
|
2267
2267
|
|
|
2268
2268
|
const callOptions = options;
|
|
2269
|
-
const
|
|
2269
|
+
const highThroughputWrite = isProtocolV2HighThroughputCall(name);
|
|
2270
2270
|
|
|
2271
|
-
if (
|
|
2271
|
+
if (highThroughputWrite) {
|
|
2272
|
+
await this.ensureProtocolV2HighThroughputMtu(uuid);
|
|
2272
2273
|
const tuning = getProtocolV2BleTuning();
|
|
2273
2274
|
const currentTransport = this.getCachedTransport(uuid);
|
|
2274
2275
|
const writeWithResponse = shouldWriteProtocolV2WithResponse({
|
|
2275
2276
|
platform: Platform.OS,
|
|
2276
|
-
|
|
2277
|
+
highThroughput: true,
|
|
2277
2278
|
requestedWithResponse: options?.writeWithResponse,
|
|
2278
2279
|
characteristic: currentTransport.writeCharacteristic,
|
|
2279
2280
|
});
|
|
@@ -2301,7 +2302,7 @@ export default class ReactNativeBleTransport {
|
|
|
2301
2302
|
}
|
|
2302
2303
|
}
|
|
2303
2304
|
|
|
2304
|
-
if (
|
|
2305
|
+
if (highThroughputWrite) {
|
|
2305
2306
|
await this.enableAndroidHighConnectionPriority(uuid);
|
|
2306
2307
|
}
|
|
2307
2308
|
|
|
@@ -2317,12 +2318,29 @@ export default class ReactNativeBleTransport {
|
|
|
2317
2318
|
Log?.error('[ReactNativeBleTransport] Protocol V2 call error:', e);
|
|
2318
2319
|
throw e;
|
|
2319
2320
|
} finally {
|
|
2320
|
-
if (
|
|
2321
|
+
if (highThroughputWrite) {
|
|
2321
2322
|
this.scheduleAndroidBalancedConnectionPriority(uuid);
|
|
2322
2323
|
}
|
|
2323
2324
|
}
|
|
2324
2325
|
}
|
|
2325
2326
|
|
|
2327
|
+
private async ensureProtocolV2HighThroughputMtu(uuid: string) {
|
|
2328
|
+
const transport = this.getCachedTransport(uuid);
|
|
2329
|
+
if (!shouldRefreshNegotiatedMtu(transport.mtuSize)) return;
|
|
2330
|
+
|
|
2331
|
+
const refreshedDevice = await requestNegotiatedMtu(transport.device, 'highThroughput', 1);
|
|
2332
|
+
transport.device = refreshedDevice;
|
|
2333
|
+
transport.mtuSize =
|
|
2334
|
+
typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport.mtuSize;
|
|
2335
|
+
|
|
2336
|
+
if (shouldRefreshNegotiatedMtu(transport.mtuSize)) {
|
|
2337
|
+
throw ERRORS.TypedError(
|
|
2338
|
+
HardwareErrorCode.BleConnectedError,
|
|
2339
|
+
`Protocol V2 high-throughput BLE MTU unavailable: ${String(transport.mtuSize)}`
|
|
2340
|
+
);
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
|
|
2326
2344
|
private clearAndroidPriorityResetTimer(uuid: string) {
|
|
2327
2345
|
const timerId = this.androidPriorityResetTimers.get(uuid);
|
|
2328
2346
|
if (timerId !== undefined) {
|