@onekeyfe/hd-transport-react-native 1.1.27-alpha.4 → 1.1.27-alpha.41
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/BleManager.d.ts.map +1 -1
- package/dist/BleTransport.d.ts +2 -0
- package/dist/BleTransport.d.ts.map +1 -1
- package/dist/bleStrategy.d.ts +15 -0
- package/dist/bleStrategy.d.ts.map +1 -0
- package/dist/constants.d.ts +4 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/index.d.ts +56 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +828 -124
- package/dist/logger.d.ts +14 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/BleManager.ts +14 -14
- package/src/BleTransport.ts +9 -5
- package/src/__tests__/bleStrategy.test.ts +47 -0
- package/src/bleStrategy.ts +62 -0
- package/src/constants.ts +25 -1
- package/src/index.ts +899 -69
- package/src/logger.ts +19 -0
- package/src/types.ts +3 -0
- package/src/utils/validateNotify.ts +4 -4
package/dist/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
3
5
|
var reactNative = require('react-native');
|
|
4
6
|
var buffer = require('buffer');
|
|
5
7
|
var reactNativeBlePlx = require('react-native-ble-plx');
|
|
6
8
|
var ByteBuffer = require('bytebuffer');
|
|
7
9
|
var transport = require('@onekeyfe/hd-transport');
|
|
8
10
|
var hdShared = require('@onekeyfe/hd-shared');
|
|
9
|
-
var hdCore = require('@onekeyfe/hd-core');
|
|
10
11
|
var BleUtils = require('@onekeyfe/react-native-ble-utils');
|
|
11
12
|
|
|
12
13
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
@@ -45,44 +46,126 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
45
46
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
46
47
|
};
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
let activeLogger;
|
|
50
|
+
const setBleLogger = (logger) => {
|
|
51
|
+
activeLogger = logger;
|
|
52
|
+
};
|
|
53
|
+
const bleLogger = {
|
|
54
|
+
debug: (...args) => { var _a; return (_a = activeLogger === null || activeLogger === void 0 ? void 0 : activeLogger.debug) === null || _a === void 0 ? void 0 : _a.call(activeLogger, ...args); },
|
|
55
|
+
error: (...args) => { var _a; return (_a = activeLogger === null || activeLogger === void 0 ? void 0 : activeLogger.error) === null || _a === void 0 ? void 0 : _a.call(activeLogger, ...args); },
|
|
56
|
+
warn: (...args) => { var _a; return (_a = activeLogger === null || activeLogger === void 0 ? void 0 : activeLogger.warn) === null || _a === void 0 ? void 0 : _a.call(activeLogger, ...args); },
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const Logger = bleLogger;
|
|
49
60
|
const getConnectedDeviceIds = (serviceUuids) => BleUtils__default["default"].getConnectedPeripherals(serviceUuids);
|
|
50
61
|
const pairDevice = (macAddress) => BleUtils__default["default"].pairDevice(macAddress);
|
|
51
62
|
const onDeviceBondState = (bleMacAddress) => new Promise((resolve, reject) => {
|
|
52
63
|
let timeout;
|
|
53
|
-
|
|
64
|
+
let cleanupListener;
|
|
65
|
+
const cleanup = () => {
|
|
54
66
|
if (timeout) {
|
|
55
67
|
clearTimeout(timeout);
|
|
56
68
|
}
|
|
57
69
|
if (cleanupListener)
|
|
58
70
|
cleanupListener();
|
|
59
71
|
};
|
|
60
|
-
|
|
72
|
+
timeout = setTimeout(() => {
|
|
73
|
+
cleanup();
|
|
74
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'device is not bonded'));
|
|
75
|
+
}, 60 * 1000);
|
|
76
|
+
cleanupListener = BleUtils__default["default"].onDeviceBondState(peripheral => {
|
|
61
77
|
var _a;
|
|
62
78
|
if (((_a = peripheral.id) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== bleMacAddress.toLowerCase()) {
|
|
63
79
|
return;
|
|
64
80
|
}
|
|
65
81
|
const { bondState } = peripheral;
|
|
66
|
-
if (bondState.preState === 'BOND_NONE' && bondState.state === 'BOND_BONDING') {
|
|
67
|
-
timeout = setTimeout(() => {
|
|
68
|
-
cleanup(cleanupListener);
|
|
69
|
-
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'device is not bonded'));
|
|
70
|
-
}, 60 * 1000);
|
|
71
|
-
}
|
|
72
82
|
const hasBonded = bondState.preState === 'BOND_BONDING' && bondState.state === 'BOND_BONDED';
|
|
73
83
|
const hasCanceled = bondState.preState === 'BOND_BONDING' && bondState.state === 'BOND_NONE';
|
|
74
84
|
Logger.debug('onDeviceBondState bondState:', bondState);
|
|
75
85
|
if (hasBonded) {
|
|
76
|
-
cleanup(
|
|
86
|
+
cleanup();
|
|
77
87
|
resolve(peripheral);
|
|
78
88
|
}
|
|
79
89
|
else if (hasCanceled) {
|
|
80
|
-
cleanup(
|
|
90
|
+
cleanup();
|
|
81
91
|
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceBondedCanceled, 'bonding canceled'));
|
|
82
92
|
}
|
|
83
93
|
});
|
|
84
94
|
});
|
|
85
95
|
|
|
96
|
+
const IOS_PACKET_LENGTH = 128;
|
|
97
|
+
const ANDROID_PACKET_LENGTH = 192;
|
|
98
|
+
const ANDROID_DEFAULT_MTU = 23;
|
|
99
|
+
const ClassicServiceUUID = '00000001-0000-1000-8000-00805f9b34fb';
|
|
100
|
+
const OneKeyServices = {
|
|
101
|
+
classic: {
|
|
102
|
+
[ClassicServiceUUID]: {
|
|
103
|
+
serviceUuid: ClassicServiceUUID,
|
|
104
|
+
writeUuid: '00000002-0000-1000-8000-00805f9b34fb',
|
|
105
|
+
notifyUuid: '00000003-0000-1000-8000-00805f9b34fb',
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
const bluetoothServices = [];
|
|
110
|
+
for (const deviceType of Object.keys(OneKeyServices)) {
|
|
111
|
+
const services = OneKeyServices[deviceType];
|
|
112
|
+
bluetoothServices.push(...Object.keys(services));
|
|
113
|
+
}
|
|
114
|
+
const getBluetoothServiceUuids = () => bluetoothServices;
|
|
115
|
+
const getInfosForServiceUuid = (serviceUuid, deviceType) => {
|
|
116
|
+
var _a;
|
|
117
|
+
const services = OneKeyServices[deviceType];
|
|
118
|
+
if (!services) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
const normalizedServiceUuid = normalizeBleUuid(serviceUuid);
|
|
122
|
+
const service = (_a = services[serviceUuid]) !== null && _a !== void 0 ? _a : Object.values(services).find(item => normalizeBleUuid(item.serviceUuid) === normalizedServiceUuid);
|
|
123
|
+
if (!service) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
return service;
|
|
127
|
+
};
|
|
128
|
+
const normalizeBleUuid = (uuid) => (uuid !== null && uuid !== void 0 ? uuid : '').replace(/-/g, '').toLowerCase();
|
|
129
|
+
const getBleUuidKey = (uuid) => {
|
|
130
|
+
const normalized = normalizeBleUuid(uuid);
|
|
131
|
+
return normalized.length >= 8 ? normalized.substring(4, 8) : normalized;
|
|
132
|
+
};
|
|
133
|
+
const isSameBleUuid = (left, right) => {
|
|
134
|
+
const normalizedLeft = normalizeBleUuid(left);
|
|
135
|
+
const normalizedRight = normalizeBleUuid(right);
|
|
136
|
+
return (normalizedLeft === normalizedRight ||
|
|
137
|
+
(getBleUuidKey(left) !== '' && getBleUuidKey(left) === getBleUuidKey(right)));
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
function hasWritableCapability(characteristic) {
|
|
141
|
+
return !!(characteristic.isWritableWithResponse || characteristic.isWritableWithoutResponse);
|
|
142
|
+
}
|
|
143
|
+
function resolveBleWriteMode(characteristic, preferredMode = 'withoutResponse') {
|
|
144
|
+
if (preferredMode === 'withoutResponse' && characteristic.isWritableWithoutResponse) {
|
|
145
|
+
return 'withoutResponse';
|
|
146
|
+
}
|
|
147
|
+
if (preferredMode === 'withResponse' && characteristic.isWritableWithResponse) {
|
|
148
|
+
return 'withResponse';
|
|
149
|
+
}
|
|
150
|
+
if (characteristic.isWritableWithoutResponse) {
|
|
151
|
+
return 'withoutResponse';
|
|
152
|
+
}
|
|
153
|
+
if (characteristic.isWritableWithResponse) {
|
|
154
|
+
return 'withResponse';
|
|
155
|
+
}
|
|
156
|
+
return preferredMode;
|
|
157
|
+
}
|
|
158
|
+
function resolveProtocolV2PacketCapacity({ platform, iosPacketLength = IOS_PACKET_LENGTH, androidPacketLength = ANDROID_PACKET_LENGTH, mtu, }) {
|
|
159
|
+
if (platform === 'ios') {
|
|
160
|
+
return iosPacketLength;
|
|
161
|
+
}
|
|
162
|
+
if (platform === 'android') {
|
|
163
|
+
const payloadLength = Math.max((mtu !== null && mtu !== void 0 ? mtu : ANDROID_DEFAULT_MTU) - 3, 1);
|
|
164
|
+
return Math.min(androidPacketLength, payloadLength);
|
|
165
|
+
}
|
|
166
|
+
return androidPacketLength;
|
|
167
|
+
}
|
|
168
|
+
|
|
86
169
|
const timer = process.env.NODE_ENV === 'development'
|
|
87
170
|
? {
|
|
88
171
|
timeout: (fn, ms) => {
|
|
@@ -126,58 +209,27 @@ const subscribeBleOn = (bleManager, ms = 1000) => new Promise((resolve, reject)
|
|
|
126
209
|
}, ms);
|
|
127
210
|
});
|
|
128
211
|
|
|
129
|
-
const IOS_PACKET_LENGTH = 128;
|
|
130
|
-
const ANDROID_PACKET_LENGTH = 192;
|
|
131
|
-
const ClassicServiceUUID = '00000001-0000-1000-8000-00805f9b34fb';
|
|
132
|
-
const OneKeyServices = {
|
|
133
|
-
classic: {
|
|
134
|
-
[ClassicServiceUUID]: {
|
|
135
|
-
serviceUuid: ClassicServiceUUID,
|
|
136
|
-
writeUuid: '00000002-0000-1000-8000-00805f9b34fb',
|
|
137
|
-
notifyUuid: '00000003-0000-1000-8000-00805f9b34fb',
|
|
138
|
-
},
|
|
139
|
-
},
|
|
140
|
-
};
|
|
141
|
-
const bluetoothServices = [];
|
|
142
|
-
for (const deviceType of Object.keys(OneKeyServices)) {
|
|
143
|
-
const services = OneKeyServices[deviceType];
|
|
144
|
-
bluetoothServices.push(...Object.keys(services));
|
|
145
|
-
}
|
|
146
|
-
const getBluetoothServiceUuids = () => bluetoothServices;
|
|
147
|
-
const getInfosForServiceUuid = (serviceUuid, deviceType) => {
|
|
148
|
-
const services = OneKeyServices[deviceType];
|
|
149
|
-
if (!services) {
|
|
150
|
-
return null;
|
|
151
|
-
}
|
|
152
|
-
const service = services[serviceUuid];
|
|
153
|
-
if (!service) {
|
|
154
|
-
return null;
|
|
155
|
-
}
|
|
156
|
-
return service;
|
|
157
|
-
};
|
|
158
|
-
|
|
159
212
|
const isHeaderChunk = (chunk) => {
|
|
160
213
|
if (chunk.length < 9)
|
|
161
214
|
return false;
|
|
162
215
|
const [MagicQuestionMark, sharp1, sharp2] = chunk;
|
|
163
|
-
if (String.fromCharCode(MagicQuestionMark) === String.fromCharCode(transport.
|
|
164
|
-
String.fromCharCode(sharp1) === String.fromCharCode(transport.
|
|
165
|
-
String.fromCharCode(sharp2) === String.fromCharCode(transport.
|
|
216
|
+
if (String.fromCharCode(MagicQuestionMark) === String.fromCharCode(transport.PROTOCOL_V1_REPORT_ID) &&
|
|
217
|
+
String.fromCharCode(sharp1) === String.fromCharCode(transport.PROTOCOL_V1_HEADER_BYTE) &&
|
|
218
|
+
String.fromCharCode(sharp2) === String.fromCharCode(transport.PROTOCOL_V1_HEADER_BYTE)) {
|
|
166
219
|
return true;
|
|
167
220
|
}
|
|
168
221
|
return false;
|
|
169
222
|
};
|
|
170
223
|
|
|
171
|
-
const Log$1 =
|
|
224
|
+
const Log$1 = bleLogger;
|
|
172
225
|
class BleTransport {
|
|
173
226
|
constructor(device, writeCharacteristic, notifyCharacteristic) {
|
|
174
227
|
this.name = 'ReactNativeBleTransport';
|
|
175
|
-
this.mtuSize =
|
|
228
|
+
this.mtuSize = 23;
|
|
176
229
|
this.id = device.id;
|
|
177
230
|
this.device = device;
|
|
178
231
|
this.writeCharacteristic = writeCharacteristic;
|
|
179
232
|
this.notifyCharacteristic = notifyCharacteristic;
|
|
180
|
-
console.log(`BleTransport(${String(this.id)}) new instance`);
|
|
181
233
|
}
|
|
182
234
|
writeWithRetry(data, retryCount = BleTransport.MAX_RETRIES) {
|
|
183
235
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -187,7 +239,7 @@ class BleTransport {
|
|
|
187
239
|
catch (error) {
|
|
188
240
|
Log$1 === null || Log$1 === void 0 ? void 0 : Log$1.debug(`Write retry attempt ${BleTransport.MAX_RETRIES - retryCount + 1}, error: ${error}`);
|
|
189
241
|
if (retryCount > 0) {
|
|
190
|
-
yield
|
|
242
|
+
yield hdShared.wait(BleTransport.RETRY_DELAY);
|
|
191
243
|
if (error.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected ||
|
|
192
244
|
error.errorCode === reactNativeBlePlx.BleErrorCode.CharacteristicNotFound) {
|
|
193
245
|
try {
|
|
@@ -211,23 +263,101 @@ class BleTransport {
|
|
|
211
263
|
BleTransport.MAX_RETRIES = 5;
|
|
212
264
|
BleTransport.RETRY_DELAY = 2000;
|
|
213
265
|
|
|
214
|
-
const { check,
|
|
215
|
-
const Log =
|
|
266
|
+
const { check, ProtocolV1, parseConfigure } = transport__default["default"];
|
|
267
|
+
const Log = bleLogger;
|
|
216
268
|
const transportCache = {};
|
|
217
|
-
|
|
218
|
-
|
|
269
|
+
const BLE_RESPONSE_TIMEOUT_MS = 30000;
|
|
270
|
+
const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
|
|
271
|
+
const PROTOCOL_V2_PROBE_TIMEOUT_MS = 10000;
|
|
272
|
+
const DEVICE_SCAN_TIMEOUT_MS = 8000;
|
|
273
|
+
const IOS_NOTIFY_READY_DELAY_MS = 150;
|
|
274
|
+
const ANDROID_NOTIFY_READY_DELAY_MS = 300;
|
|
275
|
+
const HIGH_VOLUME_WRITE_BURST_SIZE = reactNative.Platform.OS === 'ios' ? 4 : 6;
|
|
276
|
+
const HIGH_VOLUME_WRITE_PAUSE_MS = reactNative.Platform.OS === 'ios' ? 6 : 2;
|
|
277
|
+
const HIGH_VOLUME_WRITE_FLUSH_DELAY_MS = reactNative.Platform.OS === 'ios' ? 20 : 8;
|
|
278
|
+
const delay = (ms) => new Promise(resolve => {
|
|
279
|
+
setTimeout(resolve, ms);
|
|
280
|
+
});
|
|
281
|
+
const DEFAULT_PROTOCOL_V2_BLE_TUNING = {
|
|
282
|
+
iosPacketLength: IOS_PACKET_LENGTH,
|
|
283
|
+
androidPacketLength: ANDROID_PACKET_LENGTH,
|
|
284
|
+
highVolumeWriteBurstSize: HIGH_VOLUME_WRITE_BURST_SIZE,
|
|
285
|
+
highVolumeWritePauseMs: HIGH_VOLUME_WRITE_PAUSE_MS,
|
|
286
|
+
highVolumeWriteFlushDelayMs: HIGH_VOLUME_WRITE_FLUSH_DELAY_MS,
|
|
287
|
+
highVolumeWriteWithResponse: false,
|
|
288
|
+
};
|
|
289
|
+
let protocolV2BleTuning = Object.assign({}, DEFAULT_PROTOCOL_V2_BLE_TUNING);
|
|
290
|
+
const normalizePositiveInteger = (value, fallback) => {
|
|
291
|
+
const normalized = Number(value);
|
|
292
|
+
if (!Number.isFinite(normalized) || normalized <= 0)
|
|
293
|
+
return fallback;
|
|
294
|
+
return Math.floor(normalized);
|
|
295
|
+
};
|
|
296
|
+
function configureProtocolV2BleTuning(tuning = {}) {
|
|
297
|
+
var _a;
|
|
298
|
+
protocolV2BleTuning = {
|
|
299
|
+
iosPacketLength: normalizePositiveInteger(tuning.iosPacketLength, protocolV2BleTuning.iosPacketLength),
|
|
300
|
+
androidPacketLength: normalizePositiveInteger(tuning.androidPacketLength, protocolV2BleTuning.androidPacketLength),
|
|
301
|
+
highVolumeWriteBurstSize: normalizePositiveInteger(tuning.highVolumeWriteBurstSize, protocolV2BleTuning.highVolumeWriteBurstSize),
|
|
302
|
+
highVolumeWritePauseMs: normalizePositiveInteger(tuning.highVolumeWritePauseMs, protocolV2BleTuning.highVolumeWritePauseMs),
|
|
303
|
+
highVolumeWriteFlushDelayMs: normalizePositiveInteger(tuning.highVolumeWriteFlushDelayMs, protocolV2BleTuning.highVolumeWriteFlushDelayMs),
|
|
304
|
+
highVolumeWriteWithResponse: (_a = tuning.highVolumeWriteWithResponse) !== null && _a !== void 0 ? _a : protocolV2BleTuning.highVolumeWriteWithResponse,
|
|
305
|
+
};
|
|
306
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 BLE tuning configured:', protocolV2BleTuning);
|
|
307
|
+
}
|
|
308
|
+
function resetProtocolV2BleTuning() {
|
|
309
|
+
protocolV2BleTuning = Object.assign({}, DEFAULT_PROTOCOL_V2_BLE_TUNING);
|
|
310
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 BLE tuning reset:', protocolV2BleTuning);
|
|
311
|
+
}
|
|
312
|
+
function getProtocolV2BleTuning() {
|
|
313
|
+
return Object.assign({}, protocolV2BleTuning);
|
|
314
|
+
}
|
|
315
|
+
function inferProtocolHintFromDeviceName(name) {
|
|
316
|
+
return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
|
|
317
|
+
}
|
|
318
|
+
function getDeviceDisplayName(device) {
|
|
319
|
+
return (device === null || device === void 0 ? void 0 : device.name) || (device === null || device === void 0 ? void 0 : device.localName) || null;
|
|
320
|
+
}
|
|
321
|
+
function isGenericBleService(uuid) {
|
|
322
|
+
return ['1800', '1801', '180a'].includes(getBleUuidKey(uuid));
|
|
323
|
+
}
|
|
324
|
+
function hasKnownOneKeyService(device) {
|
|
325
|
+
var _a;
|
|
326
|
+
return ((_a = device === null || device === void 0 ? void 0 : device.serviceUUIDs) !== null && _a !== void 0 ? _a : []).some(serviceUuid => getInfosForServiceUuid(serviceUuid, 'classic'));
|
|
327
|
+
}
|
|
328
|
+
const ANDROID_REQUEST_MTU = 256;
|
|
329
|
+
const connectOptions = {
|
|
330
|
+
requestMTU: ANDROID_REQUEST_MTU,
|
|
219
331
|
timeout: 3000,
|
|
220
332
|
refreshGatt: 'OnConnected',
|
|
221
333
|
};
|
|
222
334
|
const tryToGetConfiguration = (device) => {
|
|
223
335
|
if (!device || !device.serviceUUIDs)
|
|
224
336
|
return null;
|
|
225
|
-
const
|
|
337
|
+
const serviceUUID = device.serviceUUIDs.find(uuid => getInfosForServiceUuid(uuid, 'classic'));
|
|
338
|
+
if (!serviceUUID)
|
|
339
|
+
return null;
|
|
226
340
|
const infos = getInfosForServiceUuid(serviceUUID, 'classic');
|
|
227
341
|
if (!infos)
|
|
228
342
|
return null;
|
|
229
343
|
return infos;
|
|
230
344
|
};
|
|
345
|
+
const requestAndroidMtu = (device) => __awaiter(void 0, void 0, void 0, function* () {
|
|
346
|
+
if (reactNative.Platform.OS !== 'android')
|
|
347
|
+
return device;
|
|
348
|
+
try {
|
|
349
|
+
const mtuDevice = yield device.requestMTU(ANDROID_REQUEST_MTU);
|
|
350
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android MTU requested:', {
|
|
351
|
+
requested: ANDROID_REQUEST_MTU,
|
|
352
|
+
mtu: mtuDevice.mtu,
|
|
353
|
+
});
|
|
354
|
+
return mtuDevice;
|
|
355
|
+
}
|
|
356
|
+
catch (error) {
|
|
357
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android MTU request failed:', error);
|
|
358
|
+
return device;
|
|
359
|
+
}
|
|
360
|
+
});
|
|
231
361
|
function remapError(error) {
|
|
232
362
|
var _a;
|
|
233
363
|
if (error instanceof reactNativeBlePlx.BleError) {
|
|
@@ -252,11 +382,21 @@ class ReactNativeBleTransport {
|
|
|
252
382
|
this.name = 'ReactNativeBleTransport';
|
|
253
383
|
this.configured = false;
|
|
254
384
|
this.stopped = false;
|
|
255
|
-
this.scanTimeout =
|
|
385
|
+
this.scanTimeout = DEVICE_SCAN_TIMEOUT_MS;
|
|
256
386
|
this.runPromise = null;
|
|
257
|
-
this.
|
|
387
|
+
this.deviceProtocol = new Map();
|
|
388
|
+
this.deviceProtocolHints = new Map();
|
|
389
|
+
this.protocolV2Assemblers = new Map();
|
|
390
|
+
this.protocolV2FrameQueues = new Map();
|
|
391
|
+
this.protocolV2FramePromises = new Map();
|
|
392
|
+
this.activeProtocolV2Call = null;
|
|
393
|
+
this.nextProtocolV2CallToken = 1;
|
|
394
|
+
this.monitorTokens = new Map();
|
|
395
|
+
this.nextMonitorToken = 1;
|
|
396
|
+
this.scanTimeout = (_a = options.scanTimeout) !== null && _a !== void 0 ? _a : DEVICE_SCAN_TIMEOUT_MS;
|
|
258
397
|
}
|
|
259
|
-
init(
|
|
398
|
+
init(logger, emitter) {
|
|
399
|
+
setBleLogger(logger);
|
|
260
400
|
this.emitter = emitter;
|
|
261
401
|
}
|
|
262
402
|
configure(signedData) {
|
|
@@ -264,6 +404,10 @@ class ReactNativeBleTransport {
|
|
|
264
404
|
this.configured = true;
|
|
265
405
|
this._messages = messages;
|
|
266
406
|
}
|
|
407
|
+
configureProtocolV2(signedData) {
|
|
408
|
+
this._messagesV2 = parseConfigure(signedData);
|
|
409
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 schema configured');
|
|
410
|
+
}
|
|
267
411
|
listen() {
|
|
268
412
|
}
|
|
269
413
|
getPlxManager() {
|
|
@@ -299,9 +443,10 @@ class ReactNativeBleTransport {
|
|
|
299
443
|
}
|
|
300
444
|
}
|
|
301
445
|
blePlxManager.startDeviceScan(null, {
|
|
446
|
+
allowDuplicates: true,
|
|
302
447
|
scanMode: reactNativeBlePlx.ScanMode.LowLatency,
|
|
303
448
|
}, (error, device) => {
|
|
304
|
-
var _a, _b;
|
|
449
|
+
var _a, _b, _c;
|
|
305
450
|
if (error) {
|
|
306
451
|
Log === null || Log === void 0 ? void 0 : Log.debug('ble scan manager: ', blePlxManager);
|
|
307
452
|
Log === null || Log === void 0 ? void 0 : Log.debug('ble scan error: ', error);
|
|
@@ -322,13 +467,35 @@ class ReactNativeBleTransport {
|
|
|
322
467
|
}
|
|
323
468
|
return;
|
|
324
469
|
}
|
|
325
|
-
|
|
470
|
+
const displayName = getDeviceDisplayName(device);
|
|
471
|
+
const isOneKey = hdShared.isOnekeyDevice((_b = device === null || device === void 0 ? void 0 : device.name) !== null && _b !== void 0 ? _b : null, device === null || device === void 0 ? void 0 : device.id) ||
|
|
472
|
+
hdShared.isOnekeyDevice((_c = device === null || device === void 0 ? void 0 : device.localName) !== null && _c !== void 0 ? _c : null, device === null || device === void 0 ? void 0 : device.id) ||
|
|
473
|
+
hasKnownOneKeyService(device);
|
|
474
|
+
const shouldTraceCandidate = !!displayName && /onekey|bixinkey|pro\s*2|pro\b|touch|^k\d|^t\d/i.test(displayName);
|
|
475
|
+
if (shouldTraceCandidate) {
|
|
476
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] scan candidate', {
|
|
477
|
+
name: device === null || device === void 0 ? void 0 : device.name,
|
|
478
|
+
localName: device === null || device === void 0 ? void 0 : device.localName,
|
|
479
|
+
id: device === null || device === void 0 ? void 0 : device.id,
|
|
480
|
+
serviceUUIDs: device === null || device === void 0 ? void 0 : device.serviceUUIDs,
|
|
481
|
+
accepted: isOneKey,
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
if (isOneKey) {
|
|
326
485
|
Log === null || Log === void 0 ? void 0 : Log.debug('search device start ======================');
|
|
327
|
-
const { name, localName, id } = device !== null && device !== void 0 ? device : {};
|
|
328
|
-
Log === null || Log === void 0 ? void 0 : Log.debug(`device name: ${name !== null && name !== void 0 ? name : ''}\nlocalName: ${localName !== null && localName !== void 0 ? localName : ''}\nid: ${id !== null && id !== void 0 ? id : ''}`);
|
|
486
|
+
const { name, localName, id, serviceUUIDs } = device !== null && device !== void 0 ? device : {};
|
|
487
|
+
Log === null || Log === void 0 ? void 0 : Log.debug(`device name: ${name !== null && name !== void 0 ? name : ''}\nlocalName: ${localName !== null && localName !== void 0 ? localName : ''}\nid: ${id !== null && id !== void 0 ? id : ''}\nserviceUUIDs: ${(serviceUUIDs !== null && serviceUUIDs !== void 0 ? serviceUUIDs : []).join(',')}`);
|
|
329
488
|
addDevice(device);
|
|
330
489
|
Log === null || Log === void 0 ? void 0 : Log.debug('search device end ======================\n');
|
|
331
490
|
}
|
|
491
|
+
else if (displayName && /\bpro\s*2\b/i.test(displayName)) {
|
|
492
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Pro2-like BLE device was not accepted:', {
|
|
493
|
+
name: device === null || device === void 0 ? void 0 : device.name,
|
|
494
|
+
localName: device === null || device === void 0 ? void 0 : device.localName,
|
|
495
|
+
id: device === null || device === void 0 ? void 0 : device.id,
|
|
496
|
+
serviceUUIDs: device === null || device === void 0 ? void 0 : device.serviceUUIDs,
|
|
497
|
+
});
|
|
498
|
+
}
|
|
332
499
|
});
|
|
333
500
|
getConnectedDeviceIds(getBluetoothServiceUuids()).then(devices => {
|
|
334
501
|
for (const device of devices) {
|
|
@@ -337,8 +504,14 @@ class ReactNativeBleTransport {
|
|
|
337
504
|
}
|
|
338
505
|
});
|
|
339
506
|
const addDevice = (device) => {
|
|
507
|
+
var _a;
|
|
340
508
|
if (deviceList.every(d => d.id !== device.id)) {
|
|
341
|
-
|
|
509
|
+
const displayName = (_a = getDeviceDisplayName(device)) !== null && _a !== void 0 ? _a : 'Unknown BLE Device';
|
|
510
|
+
const protocolHint = inferProtocolHintFromDeviceName(displayName);
|
|
511
|
+
if (protocolHint) {
|
|
512
|
+
this.deviceProtocolHints.set(device.id, protocolHint);
|
|
513
|
+
}
|
|
514
|
+
deviceList.push(Object.assign(Object.assign({}, device), { name: displayName, commType: 'ble' }));
|
|
342
515
|
}
|
|
343
516
|
};
|
|
344
517
|
timer.timeout(() => {
|
|
@@ -349,19 +522,32 @@ class ReactNativeBleTransport {
|
|
|
349
522
|
});
|
|
350
523
|
}
|
|
351
524
|
acquire(input) {
|
|
352
|
-
var _a;
|
|
525
|
+
var _a, _b, _c, _d, _e, _f;
|
|
353
526
|
return __awaiter(this, void 0, void 0, function* () {
|
|
354
|
-
const { uuid, forceCleanRunPromise } = input;
|
|
527
|
+
const { uuid, forceCleanRunPromise, expectedProtocol } = input;
|
|
355
528
|
if (!uuid) {
|
|
356
529
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleRequiredUUID);
|
|
357
530
|
}
|
|
358
|
-
|
|
359
|
-
if (
|
|
360
|
-
|
|
361
|
-
yield
|
|
531
|
+
const cachedTransport = transportCache[uuid];
|
|
532
|
+
if (cachedTransport) {
|
|
533
|
+
const cachedProtocol = this.deviceProtocol.get(uuid);
|
|
534
|
+
const isCachedDeviceConnected = yield cachedTransport.device.isConnected().catch(() => false);
|
|
535
|
+
if (isCachedDeviceConnected &&
|
|
536
|
+
cachedProtocol &&
|
|
537
|
+
(!expectedProtocol || cachedProtocol === expectedProtocol)) {
|
|
538
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] reuse cached BLE transport:', uuid, cachedProtocol);
|
|
539
|
+
return { uuid, protocolType: cachedProtocol };
|
|
540
|
+
}
|
|
541
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('transport not reusable, will release: ', uuid);
|
|
542
|
+
yield this.release(uuid, true);
|
|
362
543
|
}
|
|
544
|
+
let device = null;
|
|
363
545
|
if (forceCleanRunPromise && this.runPromise) {
|
|
364
|
-
|
|
546
|
+
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
|
|
547
|
+
this.runPromise.reject(error);
|
|
548
|
+
this.rejectAllProtocolV2Frames(error);
|
|
549
|
+
this.runPromise = null;
|
|
550
|
+
this.activeProtocolV2Call = null;
|
|
365
551
|
Log === null || Log === void 0 ? void 0 : Log.debug('Force clean Bluetooth run promise, forceCleanRunPromise: ', forceCleanRunPromise);
|
|
366
552
|
}
|
|
367
553
|
const blePlxManager = yield this.getPlxManager();
|
|
@@ -377,6 +563,9 @@ class ReactNativeBleTransport {
|
|
|
377
563
|
if (bondState.bonding) {
|
|
378
564
|
yield onDeviceBondState(uuid);
|
|
379
565
|
}
|
|
566
|
+
else if (!bondState.bonded) {
|
|
567
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'device is not bonded');
|
|
568
|
+
}
|
|
380
569
|
}
|
|
381
570
|
if (!device) {
|
|
382
571
|
const devices = yield blePlxManager.devices([uuid]);
|
|
@@ -397,7 +586,6 @@ class ReactNativeBleTransport {
|
|
|
397
586
|
Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device has error: ', e);
|
|
398
587
|
if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
|
|
399
588
|
e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
|
|
400
|
-
connectOptions = {};
|
|
401
589
|
Log === null || Log === void 0 ? void 0 : Log.debug('first try to reconnect without params');
|
|
402
590
|
device = yield blePlxManager.connectToDevice(uuid);
|
|
403
591
|
}
|
|
@@ -416,23 +604,22 @@ class ReactNativeBleTransport {
|
|
|
416
604
|
if (!(yield device.isConnected())) {
|
|
417
605
|
Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device: ', uuid);
|
|
418
606
|
try {
|
|
419
|
-
yield device.connect(connectOptions);
|
|
607
|
+
device = yield device.connect(connectOptions);
|
|
420
608
|
}
|
|
421
609
|
catch (e) {
|
|
422
610
|
Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device has error: ', e);
|
|
423
611
|
if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
|
|
424
612
|
e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
|
|
425
|
-
connectOptions = {};
|
|
426
613
|
Log === null || Log === void 0 ? void 0 : Log.debug('second try to reconnect without params');
|
|
427
614
|
try {
|
|
428
|
-
yield device.connect();
|
|
615
|
+
device = yield device.connect();
|
|
429
616
|
}
|
|
430
617
|
catch (e) {
|
|
431
618
|
Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect error: ', e);
|
|
432
619
|
if (e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
|
|
433
620
|
Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect');
|
|
434
621
|
yield device.cancelConnection();
|
|
435
|
-
yield device.connect();
|
|
622
|
+
device = yield device.connect();
|
|
436
623
|
}
|
|
437
624
|
}
|
|
438
625
|
}
|
|
@@ -441,9 +628,11 @@ class ReactNativeBleTransport {
|
|
|
441
628
|
}
|
|
442
629
|
}
|
|
443
630
|
}
|
|
631
|
+
device = yield requestAndroidMtu(device);
|
|
444
632
|
yield device.discoverAllServicesAndCharacteristics();
|
|
445
633
|
let infos = tryToGetConfiguration(device);
|
|
446
634
|
let characteristics;
|
|
635
|
+
let fallbackServiceUuid;
|
|
447
636
|
if (!infos) {
|
|
448
637
|
for (const serviceUuid of getBluetoothServiceUuids()) {
|
|
449
638
|
try {
|
|
@@ -457,16 +646,34 @@ class ReactNativeBleTransport {
|
|
|
457
646
|
}
|
|
458
647
|
}
|
|
459
648
|
if (!infos) {
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
649
|
+
const services = yield device.services();
|
|
650
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Known OneKey service UUID not found, discovered services:', services === null || services === void 0 ? void 0 : services.map(service => service.uuid));
|
|
651
|
+
const knownService = services.find(service => getInfosForServiceUuid(service.uuid, 'classic'));
|
|
652
|
+
const fallbackService = (_a = knownService !== null && knownService !== void 0 ? knownService : services.find(service => !isGenericBleService(service.uuid))) !== null && _a !== void 0 ? _a : services[0];
|
|
653
|
+
if (fallbackService) {
|
|
654
|
+
fallbackServiceUuid = fallbackService.uuid;
|
|
655
|
+
characteristics = yield device.characteristicsForService(fallbackService.uuid);
|
|
656
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Using fallback BLE service:', fallbackService.uuid);
|
|
463
657
|
}
|
|
464
|
-
|
|
465
|
-
|
|
658
|
+
}
|
|
659
|
+
if (!infos) {
|
|
660
|
+
if (!fallbackServiceUuid) {
|
|
661
|
+
try {
|
|
662
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('cancel connection when service not found');
|
|
663
|
+
yield device.cancelConnection();
|
|
664
|
+
}
|
|
665
|
+
catch (e) {
|
|
666
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('cancel connection error when service not found: ', e.message || e.reason);
|
|
667
|
+
}
|
|
668
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
|
|
466
669
|
}
|
|
670
|
+
}
|
|
671
|
+
const serviceUuid = (_b = infos === null || infos === void 0 ? void 0 : infos.serviceUuid) !== null && _b !== void 0 ? _b : fallbackServiceUuid;
|
|
672
|
+
const writeUuid = (_c = infos === null || infos === void 0 ? void 0 : infos.writeUuid) !== null && _c !== void 0 ? _c : '00000002-0000-1000-8000-00805f9b34fb';
|
|
673
|
+
const notifyUuid = (_d = infos === null || infos === void 0 ? void 0 : infos.notifyUuid) !== null && _d !== void 0 ? _d : '00000003-0000-1000-8000-00805f9b34fb';
|
|
674
|
+
if (!serviceUuid) {
|
|
467
675
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
|
|
468
676
|
}
|
|
469
|
-
const { serviceUuid, writeUuid, notifyUuid } = infos;
|
|
470
677
|
if (!characteristics) {
|
|
471
678
|
characteristics = yield device.characteristicsForService(serviceUuid);
|
|
472
679
|
}
|
|
@@ -476,10 +683,10 @@ class ReactNativeBleTransport {
|
|
|
476
683
|
let writeCharacteristic;
|
|
477
684
|
let notifyCharacteristic;
|
|
478
685
|
for (const c of characteristics) {
|
|
479
|
-
if (c.uuid
|
|
686
|
+
if (isSameBleUuid(c.uuid, writeUuid)) {
|
|
480
687
|
writeCharacteristic = c;
|
|
481
688
|
}
|
|
482
|
-
else if (c.uuid
|
|
689
|
+
else if (isSameBleUuid(c.uuid, notifyUuid)) {
|
|
483
690
|
notifyCharacteristic = c;
|
|
484
691
|
}
|
|
485
692
|
}
|
|
@@ -489,23 +696,52 @@ class ReactNativeBleTransport {
|
|
|
489
696
|
if (!notifyCharacteristic) {
|
|
490
697
|
throw hdShared.ERRORS.TypedError('BLECharacteristicNotFound: notify characteristic not found');
|
|
491
698
|
}
|
|
492
|
-
if (!writeCharacteristic
|
|
699
|
+
if (!hasWritableCapability(writeCharacteristic)) {
|
|
493
700
|
throw hdShared.ERRORS.TypedError('BLECharacteristicNotWritable: write characteristic not writable');
|
|
494
701
|
}
|
|
495
702
|
if (!notifyCharacteristic.isNotifiable) {
|
|
496
703
|
throw hdShared.ERRORS.TypedError('BLECharacteristicNotNotifiable: notify characteristic not notifiable');
|
|
497
704
|
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
(
|
|
705
|
+
const protocolHint = expectedProtocol
|
|
706
|
+
? undefined
|
|
707
|
+
: (_e = this.deviceProtocolHints.get(uuid)) !== null && _e !== void 0 ? _e : inferProtocolHintFromDeviceName(getDeviceDisplayName(device));
|
|
708
|
+
yield this.release(uuid, true);
|
|
709
|
+
if (protocolHint) {
|
|
710
|
+
this.deviceProtocolHints.set(uuid, protocolHint);
|
|
711
|
+
}
|
|
712
|
+
const transport$1 = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
|
|
713
|
+
if (reactNative.Platform.OS === 'android') {
|
|
714
|
+
transport$1.mtuSize = typeof device.mtu === 'number' ? device.mtu : transport$1.mtuSize;
|
|
715
|
+
}
|
|
716
|
+
const monitorToken = this.nextMonitorToken;
|
|
717
|
+
this.nextMonitorToken += 1;
|
|
718
|
+
const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
|
|
719
|
+
transport$1.monitorToken = monitorToken;
|
|
720
|
+
transport$1.notifyTransactionId = notifyTransactionId;
|
|
721
|
+
this.monitorTokens.set(uuid, monitorToken);
|
|
722
|
+
transport$1.notifySubscription = this._monitorCharacteristic(transport$1.notifyCharacteristic, uuid, monitorToken, notifyTransactionId);
|
|
723
|
+
transportCache[uuid] = transport$1;
|
|
724
|
+
this.protocolV2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler());
|
|
725
|
+
if (reactNative.Platform.OS === 'ios') {
|
|
726
|
+
yield new Promise(resolve => {
|
|
727
|
+
setTimeout(resolve, IOS_NOTIFY_READY_DELAY_MS);
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
else if (reactNative.Platform.OS === 'android') {
|
|
731
|
+
yield delay(ANDROID_NOTIFY_READY_DELAY_MS);
|
|
732
|
+
}
|
|
733
|
+
const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint);
|
|
734
|
+
(_f = this.emitter) === null || _f === void 0 ? void 0 : _f.emit('device-connect', {
|
|
503
735
|
name: device.name,
|
|
504
736
|
id: device.id,
|
|
505
737
|
connectId: device.id,
|
|
506
738
|
});
|
|
507
|
-
transport.disconnectSubscription = device.onDisconnected(() => {
|
|
739
|
+
transport$1.disconnectSubscription = device.onDisconnected(() => {
|
|
508
740
|
var _a;
|
|
741
|
+
if (transportCache[uuid] !== transport$1) {
|
|
742
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored for stale transport: ', device === null || device === void 0 ? void 0 : device.id);
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
509
745
|
try {
|
|
510
746
|
Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect: ', device === null || device === void 0 ? void 0 : device.id);
|
|
511
747
|
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit('device-disconnect', {
|
|
@@ -514,26 +750,33 @@ class ReactNativeBleTransport {
|
|
|
514
750
|
connectId: device === null || device === void 0 ? void 0 : device.id,
|
|
515
751
|
});
|
|
516
752
|
if (this.runPromise) {
|
|
517
|
-
|
|
753
|
+
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError);
|
|
754
|
+
this.runPromise.reject(error);
|
|
755
|
+
this.rejectAllProtocolV2Frames(error);
|
|
518
756
|
}
|
|
519
757
|
}
|
|
520
758
|
catch (e) {
|
|
521
759
|
Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect error: ', e);
|
|
522
760
|
}
|
|
523
761
|
finally {
|
|
524
|
-
this.release(uuid);
|
|
762
|
+
this.release(uuid, true);
|
|
525
763
|
}
|
|
526
764
|
});
|
|
527
|
-
return { uuid };
|
|
765
|
+
return { uuid, protocolType };
|
|
528
766
|
});
|
|
529
767
|
}
|
|
530
|
-
_monitorCharacteristic(characteristic, uuid) {
|
|
768
|
+
_monitorCharacteristic(characteristic, uuid, monitorToken, notifyTransactionId) {
|
|
531
769
|
let bufferLength = 0;
|
|
532
770
|
let buffer$1 = [];
|
|
533
771
|
const subscription = characteristic.monitor((error, c) => {
|
|
534
772
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
773
|
+
const isCurrentMonitor = this.monitorTokens.get(uuid) === monitorToken;
|
|
535
774
|
if (error) {
|
|
536
775
|
Log === null || Log === void 0 ? void 0 : Log.debug(`error monitor ${characteristic.uuid}, deviceId: ${characteristic.deviceID}: ${error}`);
|
|
776
|
+
if (!isCurrentMonitor) {
|
|
777
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
537
780
|
if (this.runPromise) {
|
|
538
781
|
let ERROR = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
|
|
539
782
|
if ((_a = error.reason) === null || _a === void 0 ? void 0 : _a.includes('The connection has timed out unexpectedly')) {
|
|
@@ -547,20 +790,37 @@ class ReactNativeBleTransport {
|
|
|
547
790
|
((_e = error.reason) === null || _e === void 0 ? void 0 : _e.includes('The handle is invalid')) ||
|
|
548
791
|
((_f = error.reason) === null || _f === void 0 ? void 0 : _f.includes('Writing is not permitted')) ||
|
|
549
792
|
((_g = error.reason) === null || _g === void 0 ? void 0 : _g.includes('notify change failed for device'))) {
|
|
550
|
-
|
|
793
|
+
const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure);
|
|
794
|
+
this.runPromise.reject(notifyError);
|
|
795
|
+
this.rejectAllProtocolV2Frames(notifyError);
|
|
551
796
|
Log === null || Log === void 0 ? void 0 : Log.debug(`${hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure} ${error.message} ${error.reason}`);
|
|
552
797
|
return;
|
|
553
798
|
}
|
|
554
|
-
|
|
799
|
+
const notifyError = hdShared.ERRORS.TypedError(ERROR);
|
|
800
|
+
this.runPromise.reject(notifyError);
|
|
801
|
+
this.rejectAllProtocolV2Frames(notifyError);
|
|
555
802
|
Log === null || Log === void 0 ? void 0 : Log.debug(': monitor notify error, and has unreleased Promise', Error);
|
|
556
803
|
}
|
|
557
804
|
return;
|
|
558
805
|
}
|
|
806
|
+
if (!isCurrentMonitor) {
|
|
807
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('monitor data ignored for stale transport: ', uuid, notifyTransactionId);
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
559
810
|
if (!c) {
|
|
560
811
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleMonitorError);
|
|
561
812
|
}
|
|
562
813
|
try {
|
|
563
814
|
const data = buffer.Buffer.from(c.value, 'base64');
|
|
815
|
+
const protocol = this.deviceProtocol.get(uuid);
|
|
816
|
+
if (!protocol) {
|
|
817
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('monitor data ignored before protocol detection: ', uuid);
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
if (protocol === 'V2') {
|
|
821
|
+
this.handleProtocolV2Notification(uuid, new Uint8Array(data));
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
564
824
|
if (isHeaderChunk(data)) {
|
|
565
825
|
bufferLength = data.readInt32BE(5);
|
|
566
826
|
buffer$1 = [...data.subarray(3)];
|
|
@@ -568,7 +828,7 @@ class ReactNativeBleTransport {
|
|
|
568
828
|
else {
|
|
569
829
|
buffer$1 = buffer$1.concat([...data]);
|
|
570
830
|
}
|
|
571
|
-
if (buffer$1.length - transport.
|
|
831
|
+
if (buffer$1.length - transport.PROTOCOL_V1_MESSAGE_HEADER_SIZE >= bufferLength) {
|
|
572
832
|
const value = buffer.Buffer.from(buffer$1);
|
|
573
833
|
bufferLength = 0;
|
|
574
834
|
buffer$1 = [];
|
|
@@ -577,24 +837,65 @@ class ReactNativeBleTransport {
|
|
|
577
837
|
}
|
|
578
838
|
catch (error) {
|
|
579
839
|
Log === null || Log === void 0 ? void 0 : Log.debug('monitor data error: ', error);
|
|
580
|
-
|
|
840
|
+
const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
|
|
841
|
+
(_j = this.runPromise) === null || _j === void 0 ? void 0 : _j.reject(notifyError);
|
|
842
|
+
this.rejectAllProtocolV2Frames(notifyError);
|
|
581
843
|
}
|
|
582
|
-
},
|
|
844
|
+
}, notifyTransactionId);
|
|
583
845
|
return subscription;
|
|
584
846
|
}
|
|
585
|
-
release(uuid) {
|
|
586
|
-
var _a, _b, _c;
|
|
847
|
+
release(uuid, onclose = false) {
|
|
848
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
587
849
|
return __awaiter(this, void 0, void 0, function* () {
|
|
588
850
|
const transport = transportCache[uuid];
|
|
851
|
+
if (this.runPromise) {
|
|
852
|
+
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
|
|
853
|
+
this.runPromise.reject(error);
|
|
854
|
+
this.runPromise = null;
|
|
855
|
+
this.rejectAllProtocolV2Frames(error);
|
|
856
|
+
this.activeProtocolV2Call = null;
|
|
857
|
+
}
|
|
858
|
+
else {
|
|
859
|
+
this.resetProtocolV2Frames(uuid);
|
|
860
|
+
}
|
|
861
|
+
if (reactNative.Platform.OS === 'android' && !onclose && transport) {
|
|
862
|
+
(_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
863
|
+
this.resetProtocolV2Frames(uuid);
|
|
864
|
+
if (((_b = this.activeProtocolV2Call) === null || _b === void 0 ? void 0 : _b.uuid) === uuid) {
|
|
865
|
+
this.activeProtocolV2Call = null;
|
|
866
|
+
}
|
|
867
|
+
return Promise.resolve(true);
|
|
868
|
+
}
|
|
589
869
|
if (transport) {
|
|
870
|
+
if (this.monitorTokens.get(uuid) === transport.monitorToken) {
|
|
871
|
+
this.monitorTokens.delete(uuid);
|
|
872
|
+
}
|
|
590
873
|
Log === null || Log === void 0 ? void 0 : Log.debug('release: removing disconnect subscription for device: ', uuid);
|
|
591
|
-
(
|
|
874
|
+
(_c = transport.disconnectSubscription) === null || _c === void 0 ? void 0 : _c.remove();
|
|
592
875
|
transport.disconnectSubscription = undefined;
|
|
593
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('release: removing notify subscription, characteristic: ', (
|
|
594
|
-
(
|
|
876
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('release: removing notify subscription, characteristic: ', (_d = transport.notifyCharacteristic) === null || _d === void 0 ? void 0 : _d.uuid);
|
|
877
|
+
(_e = transport.notifySubscription) === null || _e === void 0 ? void 0 : _e.remove();
|
|
595
878
|
transport.notifySubscription = undefined;
|
|
879
|
+
if (transport.notifyTransactionId) {
|
|
880
|
+
try {
|
|
881
|
+
yield ((_f = this.blePlxManager) === null || _f === void 0 ? void 0 : _f.cancelTransaction(transport.notifyTransactionId));
|
|
882
|
+
}
|
|
883
|
+
catch (e) {
|
|
884
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('release: cancel notify transaction error (ignored): ', (e === null || e === void 0 ? void 0 : e.message) || e);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
596
887
|
delete transportCache[uuid];
|
|
597
|
-
|
|
888
|
+
}
|
|
889
|
+
this.deviceProtocol.delete(uuid);
|
|
890
|
+
this.deviceProtocolHints.delete(uuid);
|
|
891
|
+
(_g = this.protocolV2Assemblers.get(uuid)) === null || _g === void 0 ? void 0 : _g.reset();
|
|
892
|
+
this.protocolV2Assemblers.delete(uuid);
|
|
893
|
+
this.resetProtocolV2Frames(uuid);
|
|
894
|
+
try {
|
|
895
|
+
yield ((_h = this.blePlxManager) === null || _h === void 0 ? void 0 : _h.cancelTransaction(uuid));
|
|
896
|
+
}
|
|
897
|
+
catch (e) {
|
|
898
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('release: cancel transaction error (ignored): ', (e === null || e === void 0 ? void 0 : e.message) || e);
|
|
598
899
|
}
|
|
599
900
|
return Promise.resolve(true);
|
|
600
901
|
});
|
|
@@ -604,7 +905,7 @@ class ReactNativeBleTransport {
|
|
|
604
905
|
yield this.call(session, name, data);
|
|
605
906
|
});
|
|
606
907
|
}
|
|
607
|
-
call(uuid, name, data) {
|
|
908
|
+
call(uuid, name, data, options) {
|
|
608
909
|
return __awaiter(this, void 0, void 0, function* () {
|
|
609
910
|
if (this.stopped) {
|
|
610
911
|
return Promise.reject(hdShared.ERRORS.TypedError('Transport stopped.'));
|
|
@@ -617,12 +918,10 @@ class ReactNativeBleTransport {
|
|
|
617
918
|
if (this.runPromise && !forceRun) {
|
|
618
919
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportCallInProgress);
|
|
619
920
|
}
|
|
620
|
-
const
|
|
621
|
-
if (!
|
|
622
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.
|
|
921
|
+
const protocol = this.getProtocolType(uuid);
|
|
922
|
+
if (!protocol) {
|
|
923
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${uuid}`);
|
|
623
924
|
}
|
|
624
|
-
this.runPromise = hdShared.createDeferred();
|
|
625
|
-
const messages = this._messages;
|
|
626
925
|
if (name === 'ResourceUpdate' || name === 'ResourceAck') {
|
|
627
926
|
Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native', 'call-', ' name: ', name, ' data: ', {
|
|
628
927
|
file_name: data === null || data === void 0 ? void 0 : data.file_name,
|
|
@@ -630,12 +929,29 @@ class ReactNativeBleTransport {
|
|
|
630
929
|
});
|
|
631
930
|
}
|
|
632
931
|
else if (transport.LogBlockCommand.has(name)) {
|
|
633
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native', 'call-', ' name: ', name);
|
|
932
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native', 'call-', ' name: ', name, ' protocol: ', protocol);
|
|
634
933
|
}
|
|
635
934
|
else {
|
|
636
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native', 'call-', ' name: ', name, ' data: ', data);
|
|
935
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native', 'call-', ' name: ', name, ' data: ', data, ' protocol: ', protocol);
|
|
936
|
+
}
|
|
937
|
+
if (protocol === 'V2') {
|
|
938
|
+
return this.callProtocolV2(uuid, name, data, options);
|
|
939
|
+
}
|
|
940
|
+
return this.callProtocolV1(uuid, name, data, options);
|
|
941
|
+
});
|
|
942
|
+
}
|
|
943
|
+
callProtocolV1(uuid, name, data, options) {
|
|
944
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
945
|
+
if (!this._messages) {
|
|
946
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
637
947
|
}
|
|
638
|
-
const
|
|
948
|
+
const transport = this.getCachedTransport(uuid);
|
|
949
|
+
const runPromise = hdShared.createDeferred();
|
|
950
|
+
runPromise.promise.catch(() => undefined);
|
|
951
|
+
this.runPromise = runPromise;
|
|
952
|
+
const messages = this._messages;
|
|
953
|
+
const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
|
|
954
|
+
let timeout;
|
|
639
955
|
function writeChunkedData(buffers, writeFunction, onError) {
|
|
640
956
|
return __awaiter(this, void 0, void 0, function* () {
|
|
641
957
|
const packetCapacity = reactNative.Platform.OS === 'ios' ? IOS_PACKET_LENGTH : ANDROID_PACKET_LENGTH;
|
|
@@ -660,14 +976,14 @@ class ReactNativeBleTransport {
|
|
|
660
976
|
});
|
|
661
977
|
}
|
|
662
978
|
if (name === 'EmmcFileWrite') {
|
|
663
|
-
yield writeChunkedData(buffers, data => transport
|
|
979
|
+
yield writeChunkedData(buffers, data => transport.writeWithRetry(data), e => {
|
|
664
980
|
this.runPromise = null;
|
|
665
981
|
Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
|
|
666
982
|
});
|
|
667
983
|
}
|
|
668
984
|
else if (name === 'FirmwareUpload') {
|
|
669
985
|
yield writeChunkedData(buffers, (data) => __awaiter(this, void 0, void 0, function* () {
|
|
670
|
-
yield transport
|
|
986
|
+
yield transport.writeCharacteristic.writeWithoutResponse(data);
|
|
671
987
|
}), e => {
|
|
672
988
|
this.runPromise = null;
|
|
673
989
|
Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
|
|
@@ -677,7 +993,7 @@ class ReactNativeBleTransport {
|
|
|
677
993
|
for (const o of buffers) {
|
|
678
994
|
const outData = o.toString('base64');
|
|
679
995
|
try {
|
|
680
|
-
yield transport
|
|
996
|
+
yield transport.writeCharacteristic.writeWithoutResponse(outData);
|
|
681
997
|
}
|
|
682
998
|
catch (e) {
|
|
683
999
|
Log === null || Log === void 0 ? void 0 : Log.debug('writeCharacteristic write error: ', e);
|
|
@@ -695,20 +1011,40 @@ class ReactNativeBleTransport {
|
|
|
695
1011
|
}
|
|
696
1012
|
}
|
|
697
1013
|
try {
|
|
698
|
-
const response = yield
|
|
1014
|
+
const response = yield Promise.race([
|
|
1015
|
+
runPromise.promise,
|
|
1016
|
+
new Promise((_, reject) => {
|
|
1017
|
+
if (options === null || options === void 0 ? void 0 : options.timeoutMs) {
|
|
1018
|
+
timeout = setTimeout(() => {
|
|
1019
|
+
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, `BLE response timeout after ${options.timeoutMs}ms for ${name}`);
|
|
1020
|
+
runPromise.reject(error);
|
|
1021
|
+
reject(error);
|
|
1022
|
+
}, options.timeoutMs);
|
|
1023
|
+
}
|
|
1024
|
+
}),
|
|
1025
|
+
]);
|
|
699
1026
|
if (typeof response !== 'string') {
|
|
700
1027
|
throw new Error('Returning data is not string.');
|
|
701
1028
|
}
|
|
702
1029
|
Log === null || Log === void 0 ? void 0 : Log.debug('receive data: ', response);
|
|
703
|
-
const jsonData =
|
|
1030
|
+
const jsonData = ProtocolV1.decodeMessage(messages, response);
|
|
704
1031
|
return check.call(jsonData);
|
|
705
1032
|
}
|
|
706
1033
|
catch (e) {
|
|
707
|
-
|
|
1034
|
+
if (name === 'Initialize' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS) {
|
|
1035
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 Initialize probe call failed:', e);
|
|
1036
|
+
}
|
|
1037
|
+
else {
|
|
1038
|
+
Log === null || Log === void 0 ? void 0 : Log.error('call error: ', e);
|
|
1039
|
+
}
|
|
708
1040
|
throw e;
|
|
709
1041
|
}
|
|
710
1042
|
finally {
|
|
711
|
-
|
|
1043
|
+
if (timeout)
|
|
1044
|
+
clearTimeout(timeout);
|
|
1045
|
+
if (this.runPromise === runPromise) {
|
|
1046
|
+
this.runPromise = null;
|
|
1047
|
+
}
|
|
712
1048
|
}
|
|
713
1049
|
});
|
|
714
1050
|
}
|
|
@@ -716,7 +1052,7 @@ class ReactNativeBleTransport {
|
|
|
716
1052
|
this.stopped = true;
|
|
717
1053
|
}
|
|
718
1054
|
disconnect(session) {
|
|
719
|
-
var _a, _b, _c, _d, _e;
|
|
1055
|
+
var _a, _b, _c, _d, _e, _f;
|
|
720
1056
|
return __awaiter(this, void 0, void 0, function* () {
|
|
721
1057
|
Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native transport resetSession: ', session);
|
|
722
1058
|
const transport = transportCache[session];
|
|
@@ -765,9 +1101,16 @@ class ReactNativeBleTransport {
|
|
|
765
1101
|
if (transportCache[session]) {
|
|
766
1102
|
delete transportCache[session];
|
|
767
1103
|
}
|
|
1104
|
+
this.deviceProtocol.delete(session);
|
|
1105
|
+
this.deviceProtocolHints.delete(session);
|
|
1106
|
+
this.protocolV2Assemblers.delete(session);
|
|
1107
|
+
this.resetProtocolV2Frames(session);
|
|
1108
|
+
if (((_d = this.activeProtocolV2Call) === null || _d === void 0 ? void 0 : _d.uuid) === session) {
|
|
1109
|
+
this.activeProtocolV2Call = null;
|
|
1110
|
+
}
|
|
768
1111
|
try {
|
|
769
|
-
(
|
|
770
|
-
name: (
|
|
1112
|
+
(_e = this.emitter) === null || _e === void 0 ? void 0 : _e.emit('device-disconnect', {
|
|
1113
|
+
name: (_f = transport === null || transport === void 0 ? void 0 : transport.device) === null || _f === void 0 ? void 0 : _f.name,
|
|
771
1114
|
id: session,
|
|
772
1115
|
connectId: session,
|
|
773
1116
|
});
|
|
@@ -783,6 +1126,367 @@ class ReactNativeBleTransport {
|
|
|
783
1126
|
if (this.runPromise) ;
|
|
784
1127
|
this.runPromise = null;
|
|
785
1128
|
}
|
|
1129
|
+
getCachedTransport(uuid) {
|
|
1130
|
+
const transport = transportCache[uuid];
|
|
1131
|
+
if (!transport) {
|
|
1132
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
|
|
1133
|
+
}
|
|
1134
|
+
return transport;
|
|
1135
|
+
}
|
|
1136
|
+
createProtocolMismatchError(expected) {
|
|
1137
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
|
|
1138
|
+
}
|
|
1139
|
+
createProtocolDetectionError() {
|
|
1140
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1 Initialize or Protocol V2 Ping');
|
|
1141
|
+
}
|
|
1142
|
+
clearProbeProtocol(uuid, protocol) {
|
|
1143
|
+
if (this.deviceProtocol.get(uuid) === protocol) {
|
|
1144
|
+
this.deviceProtocol.delete(uuid);
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
detectProtocol(uuid, expectedProtocol, protocolHint) {
|
|
1148
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1149
|
+
if (expectedProtocol === 'V1') {
|
|
1150
|
+
if (yield this.probeProtocolV1(uuid)) {
|
|
1151
|
+
this.deviceProtocol.set(uuid, 'V1');
|
|
1152
|
+
Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> V1 (expected)`);
|
|
1153
|
+
return 'V1';
|
|
1154
|
+
}
|
|
1155
|
+
throw this.createProtocolMismatchError(expectedProtocol);
|
|
1156
|
+
}
|
|
1157
|
+
if (expectedProtocol === 'V2') {
|
|
1158
|
+
this.deviceProtocol.set(uuid, 'V2');
|
|
1159
|
+
Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> V2 (expected)`);
|
|
1160
|
+
return 'V2';
|
|
1161
|
+
}
|
|
1162
|
+
if (protocolHint === 'V2') {
|
|
1163
|
+
this.deviceProtocol.set(uuid, 'V2');
|
|
1164
|
+
Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> V2 (hint)`);
|
|
1165
|
+
return 'V2';
|
|
1166
|
+
}
|
|
1167
|
+
if (this.deviceProtocol.get(uuid) === 'V2') {
|
|
1168
|
+
this.deviceProtocol.set(uuid, 'V2');
|
|
1169
|
+
Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> V2 (cached)`);
|
|
1170
|
+
return 'V2';
|
|
1171
|
+
}
|
|
1172
|
+
const protocolV1Detected = yield this.probeProtocolV1(uuid);
|
|
1173
|
+
if (protocolV1Detected) {
|
|
1174
|
+
this.deviceProtocol.set(uuid, 'V1');
|
|
1175
|
+
Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> V1`);
|
|
1176
|
+
return 'V1';
|
|
1177
|
+
}
|
|
1178
|
+
yield this.resetProbeStateAfterProtocolProbe(uuid, 'V1');
|
|
1179
|
+
if (yield this.probeProtocolV2(uuid)) {
|
|
1180
|
+
this.deviceProtocol.set(uuid, 'V2');
|
|
1181
|
+
Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] detectProtocol: uuid=${uuid} -> V2`);
|
|
1182
|
+
return 'V2';
|
|
1183
|
+
}
|
|
1184
|
+
this.deviceProtocol.delete(uuid);
|
|
1185
|
+
throw this.createProtocolDetectionError();
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
resetProbeStateAfterProtocolProbe(uuid, protocol) {
|
|
1189
|
+
var _a, _b, _c, _d;
|
|
1190
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1191
|
+
const transport = transportCache[uuid];
|
|
1192
|
+
(_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1193
|
+
this.resetProtocolV2Frames(uuid);
|
|
1194
|
+
if (((_b = this.activeProtocolV2Call) === null || _b === void 0 ? void 0 : _b.uuid) === uuid) {
|
|
1195
|
+
this.activeProtocolV2Call = null;
|
|
1196
|
+
}
|
|
1197
|
+
if (this.runPromise) {
|
|
1198
|
+
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
|
|
1199
|
+
this.runPromise.reject(error);
|
|
1200
|
+
this.runPromise = null;
|
|
1201
|
+
}
|
|
1202
|
+
if (!transport)
|
|
1203
|
+
return;
|
|
1204
|
+
const previousNotifyTransactionId = transport.notifyTransactionId;
|
|
1205
|
+
if (this.monitorTokens.get(uuid) === transport.monitorToken) {
|
|
1206
|
+
this.monitorTokens.delete(uuid);
|
|
1207
|
+
}
|
|
1208
|
+
(_c = transport.notifySubscription) === null || _c === void 0 ? void 0 : _c.remove();
|
|
1209
|
+
transport.notifySubscription = undefined;
|
|
1210
|
+
if (previousNotifyTransactionId) {
|
|
1211
|
+
try {
|
|
1212
|
+
yield ((_d = this.blePlxManager) === null || _d === void 0 ? void 0 : _d.cancelTransaction(previousNotifyTransactionId));
|
|
1213
|
+
}
|
|
1214
|
+
catch (error) {
|
|
1215
|
+
Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] cancel notify after Protocol ${protocol} probe failed:`, (error === null || error === void 0 ? void 0 : error.message) || error);
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
const monitorToken = this.nextMonitorToken;
|
|
1219
|
+
this.nextMonitorToken += 1;
|
|
1220
|
+
const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
|
|
1221
|
+
transport.monitorToken = monitorToken;
|
|
1222
|
+
transport.notifyTransactionId = notifyTransactionId;
|
|
1223
|
+
this.monitorTokens.set(uuid, monitorToken);
|
|
1224
|
+
transport.notifySubscription = this._monitorCharacteristic(transport.notifyCharacteristic, uuid, monitorToken, notifyTransactionId);
|
|
1225
|
+
if (reactNative.Platform.OS === 'ios') {
|
|
1226
|
+
yield new Promise(resolve => {
|
|
1227
|
+
setTimeout(resolve, IOS_NOTIFY_READY_DELAY_MS);
|
|
1228
|
+
});
|
|
1229
|
+
}
|
|
1230
|
+
});
|
|
1231
|
+
}
|
|
1232
|
+
probeProtocolV1(uuid) {
|
|
1233
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1234
|
+
if (!this._messages) {
|
|
1235
|
+
return false;
|
|
1236
|
+
}
|
|
1237
|
+
try {
|
|
1238
|
+
this.deviceProtocol.set(uuid, 'V1');
|
|
1239
|
+
yield this.callProtocolV1(uuid, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
|
|
1240
|
+
return true;
|
|
1241
|
+
}
|
|
1242
|
+
catch (error) {
|
|
1243
|
+
this.clearProbeProtocol(uuid, 'V1');
|
|
1244
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 Initialize probe failed:', error);
|
|
1245
|
+
return false;
|
|
1246
|
+
}
|
|
1247
|
+
});
|
|
1248
|
+
}
|
|
1249
|
+
probeProtocolV2(uuid) {
|
|
1250
|
+
var _a;
|
|
1251
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1252
|
+
if (!this._messages || !this._messagesV2) {
|
|
1253
|
+
return false;
|
|
1254
|
+
}
|
|
1255
|
+
this.deviceProtocol.set(uuid, 'V2');
|
|
1256
|
+
(_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1257
|
+
const detected = yield transport.probeProtocolV2({
|
|
1258
|
+
call: (name, data, options) => this.callProtocolV2(uuid, name, data, options),
|
|
1259
|
+
timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT_MS,
|
|
1260
|
+
logger: Log,
|
|
1261
|
+
logPrefix: 'ProtocolV2 RN-BLE',
|
|
1262
|
+
onProbeFailed: () => {
|
|
1263
|
+
var _a;
|
|
1264
|
+
(_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1265
|
+
this.resetProtocolV2Frames(uuid);
|
|
1266
|
+
},
|
|
1267
|
+
});
|
|
1268
|
+
if (!detected) {
|
|
1269
|
+
this.clearProbeProtocol(uuid, 'V2');
|
|
1270
|
+
}
|
|
1271
|
+
return detected;
|
|
1272
|
+
});
|
|
1273
|
+
}
|
|
1274
|
+
handleProtocolV2Notification(uuid, data) {
|
|
1275
|
+
var _a, _b, _c;
|
|
1276
|
+
try {
|
|
1277
|
+
if (!this.runPromise || ((_a = this.activeProtocolV2Call) === null || _a === void 0 ? void 0 : _a.uuid) !== uuid) {
|
|
1278
|
+
(_b = this.protocolV2Assemblers.get(uuid)) === null || _b === void 0 ? void 0 : _b.reset();
|
|
1279
|
+
this.resetProtocolV2Frames(uuid);
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
if (data.length === 0)
|
|
1283
|
+
return;
|
|
1284
|
+
const assembler = this.protocolV2Assemblers.get(uuid);
|
|
1285
|
+
if (!assembler)
|
|
1286
|
+
return;
|
|
1287
|
+
let frameData = assembler.push(data);
|
|
1288
|
+
while (frameData) {
|
|
1289
|
+
this.resolveProtocolV2Frame(uuid, frameData);
|
|
1290
|
+
frameData = assembler.push(new Uint8Array(0));
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
catch (error) {
|
|
1294
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 notification error:', error);
|
|
1295
|
+
const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
|
|
1296
|
+
(_c = this.runPromise) === null || _c === void 0 ? void 0 : _c.reject(notifyError);
|
|
1297
|
+
this.rejectAllProtocolV2Frames(notifyError);
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
getProtocolV2FrameQueue(uuid) {
|
|
1301
|
+
let queue = this.protocolV2FrameQueues.get(uuid);
|
|
1302
|
+
if (!queue) {
|
|
1303
|
+
queue = [];
|
|
1304
|
+
this.protocolV2FrameQueues.set(uuid, queue);
|
|
1305
|
+
}
|
|
1306
|
+
return queue;
|
|
1307
|
+
}
|
|
1308
|
+
resolveProtocolV2Frame(uuid, frame) {
|
|
1309
|
+
const framePromise = this.protocolV2FramePromises.get(uuid);
|
|
1310
|
+
if (framePromise) {
|
|
1311
|
+
framePromise.resolve(frame);
|
|
1312
|
+
this.protocolV2FramePromises.delete(uuid);
|
|
1313
|
+
return;
|
|
1314
|
+
}
|
|
1315
|
+
this.getProtocolV2FrameQueue(uuid).push(frame);
|
|
1316
|
+
}
|
|
1317
|
+
rejectAllProtocolV2Frames(error) {
|
|
1318
|
+
this.protocolV2FrameQueues.clear();
|
|
1319
|
+
for (const framePromise of this.protocolV2FramePromises.values()) {
|
|
1320
|
+
framePromise.reject(error);
|
|
1321
|
+
}
|
|
1322
|
+
this.protocolV2FramePromises.clear();
|
|
1323
|
+
}
|
|
1324
|
+
resetProtocolV2Frames(uuid) {
|
|
1325
|
+
this.protocolV2FrameQueues.delete(uuid);
|
|
1326
|
+
this.protocolV2FramePromises.delete(uuid);
|
|
1327
|
+
}
|
|
1328
|
+
isActiveProtocolV2Call(uuid, token) {
|
|
1329
|
+
var _a;
|
|
1330
|
+
return ((_a = this.activeProtocolV2Call) === null || _a === void 0 ? void 0 : _a.uuid) === uuid && this.activeProtocolV2Call.token === token;
|
|
1331
|
+
}
|
|
1332
|
+
readProtocolV2Frame(uuid) {
|
|
1333
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1334
|
+
const queuedFrame = this.getProtocolV2FrameQueue(uuid).shift();
|
|
1335
|
+
if (queuedFrame) {
|
|
1336
|
+
return queuedFrame;
|
|
1337
|
+
}
|
|
1338
|
+
const framePromise = hdShared.createDeferred();
|
|
1339
|
+
this.protocolV2FramePromises.set(uuid, framePromise);
|
|
1340
|
+
try {
|
|
1341
|
+
return yield framePromise.promise;
|
|
1342
|
+
}
|
|
1343
|
+
finally {
|
|
1344
|
+
if (this.protocolV2FramePromises.get(uuid) === framePromise) {
|
|
1345
|
+
this.protocolV2FramePromises.delete(uuid);
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
});
|
|
1349
|
+
}
|
|
1350
|
+
writeProtocolV2Frame(transport, frame, options) {
|
|
1351
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1352
|
+
const tuning = getProtocolV2BleTuning();
|
|
1353
|
+
const packetCapacity = resolveProtocolV2PacketCapacity({
|
|
1354
|
+
platform: reactNative.Platform.OS,
|
|
1355
|
+
iosPacketLength: tuning.iosPacketLength,
|
|
1356
|
+
androidPacketLength: tuning.androidPacketLength,
|
|
1357
|
+
mtu: reactNative.Platform.OS === 'android' ? transport.mtuSize : undefined,
|
|
1358
|
+
});
|
|
1359
|
+
const writeWithResponse = !!(options === null || options === void 0 ? void 0 : options.writeWithResponse) || (!!(options === null || options === void 0 ? void 0 : options.highVolume) && tuning.highVolumeWriteWithResponse);
|
|
1360
|
+
const writeMode = resolveBleWriteMode(transport.writeCharacteristic, writeWithResponse ? 'withResponse' : 'withoutResponse');
|
|
1361
|
+
const shouldThrottle = !!(options === null || options === void 0 ? void 0 : options.highVolume) && writeMode === 'withoutResponse';
|
|
1362
|
+
let packetsWritten = 0;
|
|
1363
|
+
try {
|
|
1364
|
+
for (let offset = 0; offset < frame.length; offset += packetCapacity) {
|
|
1365
|
+
const chunk = frame.slice(offset, offset + packetCapacity);
|
|
1366
|
+
const base64 = buffer.Buffer.from(chunk).toString('base64');
|
|
1367
|
+
if (writeMode === 'withResponse') {
|
|
1368
|
+
yield transport.writeCharacteristic.writeWithResponse(base64);
|
|
1369
|
+
}
|
|
1370
|
+
else {
|
|
1371
|
+
yield transport.writeCharacteristic.writeWithoutResponse(base64);
|
|
1372
|
+
}
|
|
1373
|
+
packetsWritten += 1;
|
|
1374
|
+
if (shouldThrottle &&
|
|
1375
|
+
packetsWritten % tuning.highVolumeWriteBurstSize === 0 &&
|
|
1376
|
+
offset + packetCapacity < frame.length) {
|
|
1377
|
+
yield delay(tuning.highVolumeWritePauseMs);
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
if (shouldThrottle) {
|
|
1381
|
+
yield delay(tuning.highVolumeWriteFlushDelayMs);
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
catch (error) {
|
|
1385
|
+
if ((options === null || options === void 0 ? void 0 : options.highVolume) && !writeWithResponse && packetsWritten === 0) {
|
|
1386
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume writeWithoutResponse failed before data was sent, fallback to writeWithResponse:', error);
|
|
1387
|
+
yield this.writeProtocolV2Frame(transport, frame, {
|
|
1388
|
+
highVolume: true,
|
|
1389
|
+
writeWithResponse: true,
|
|
1390
|
+
});
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
throw error;
|
|
1394
|
+
}
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
callProtocolV2(uuid, name, data, options) {
|
|
1398
|
+
var _a, _b, _c, _d;
|
|
1399
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1400
|
+
if (!this._messages || !this._messagesV2) {
|
|
1401
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
1402
|
+
}
|
|
1403
|
+
const forceRun = name === 'Initialize' || name === 'Cancel' || name === 'GetProtoVersion';
|
|
1404
|
+
if (this.runPromise) {
|
|
1405
|
+
if (!forceRun) {
|
|
1406
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportCallInProgress);
|
|
1407
|
+
}
|
|
1408
|
+
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
|
|
1409
|
+
this.runPromise.reject(error);
|
|
1410
|
+
this.rejectAllProtocolV2Frames(error);
|
|
1411
|
+
this.runPromise = null;
|
|
1412
|
+
this.activeProtocolV2Call = null;
|
|
1413
|
+
}
|
|
1414
|
+
const transport$1 = this.getCachedTransport(uuid);
|
|
1415
|
+
const runPromise = hdShared.createDeferred();
|
|
1416
|
+
runPromise.promise.catch(() => undefined);
|
|
1417
|
+
this.runPromise = runPromise;
|
|
1418
|
+
const callToken = this.nextProtocolV2CallToken++;
|
|
1419
|
+
this.activeProtocolV2Call = { uuid, token: callToken };
|
|
1420
|
+
(_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1421
|
+
this.resetProtocolV2Frames(uuid);
|
|
1422
|
+
let completed = false;
|
|
1423
|
+
const callOptions = Object.assign(Object.assign({}, options), { timeoutMs: (_b = options === null || options === void 0 ? void 0 : options.timeoutMs) !== null && _b !== void 0 ? _b : BLE_RESPONSE_TIMEOUT_MS });
|
|
1424
|
+
const highVolumeWrite = transport.LogBlockCommand.has(name);
|
|
1425
|
+
if (highVolumeWrite) {
|
|
1426
|
+
const tuning = getProtocolV2BleTuning();
|
|
1427
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume write uses throttled writeWithoutResponse:', name, {
|
|
1428
|
+
packetCapacity: reactNative.Platform.OS === 'ios' ? tuning.iosPacketLength : tuning.androidPacketLength,
|
|
1429
|
+
burstSize: tuning.highVolumeWriteBurstSize,
|
|
1430
|
+
pauseMs: tuning.highVolumeWritePauseMs,
|
|
1431
|
+
flushDelayMs: tuning.highVolumeWriteFlushDelayMs,
|
|
1432
|
+
writeWithResponse: tuning.highVolumeWriteWithResponse,
|
|
1433
|
+
});
|
|
1434
|
+
}
|
|
1435
|
+
try {
|
|
1436
|
+
const session = new transport.ProtocolV2Session({
|
|
1437
|
+
schemas: {
|
|
1438
|
+
protocolV1: this._messages,
|
|
1439
|
+
protocolV2: this._messagesV2,
|
|
1440
|
+
},
|
|
1441
|
+
router: transport.PROTOCOL_V2_CHANNEL_BLE_UART,
|
|
1442
|
+
writeFrame: (frame) => __awaiter(this, void 0, void 0, function* () {
|
|
1443
|
+
yield this.writeProtocolV2Frame(transport$1, frame, {
|
|
1444
|
+
highVolume: highVolumeWrite,
|
|
1445
|
+
});
|
|
1446
|
+
}),
|
|
1447
|
+
readFrame: () => __awaiter(this, void 0, void 0, function* () {
|
|
1448
|
+
const rxFrame = yield this.readProtocolV2Frame(uuid);
|
|
1449
|
+
if (!(rxFrame instanceof Uint8Array)) {
|
|
1450
|
+
throw new Error('Protocol V2 response is not Uint8Array');
|
|
1451
|
+
}
|
|
1452
|
+
return rxFrame;
|
|
1453
|
+
}),
|
|
1454
|
+
logger: Log,
|
|
1455
|
+
logPrefix: 'ProtocolV2 RN-BLE',
|
|
1456
|
+
createTimeoutError: (_messageName, timeout) => hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, `BLE response timeout after ${timeout}ms for ${name}`),
|
|
1457
|
+
});
|
|
1458
|
+
const result = yield session.call(name, data, callOptions);
|
|
1459
|
+
completed = true;
|
|
1460
|
+
return result;
|
|
1461
|
+
}
|
|
1462
|
+
catch (e) {
|
|
1463
|
+
if (this.isActiveProtocolV2Call(uuid, callToken)) {
|
|
1464
|
+
(_c = this.protocolV2Assemblers.get(uuid)) === null || _c === void 0 ? void 0 : _c.reset();
|
|
1465
|
+
this.resetProtocolV2Frames(uuid);
|
|
1466
|
+
}
|
|
1467
|
+
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] Protocol V2 call error:', e);
|
|
1468
|
+
throw e;
|
|
1469
|
+
}
|
|
1470
|
+
finally {
|
|
1471
|
+
if (this.isActiveProtocolV2Call(uuid, callToken)) {
|
|
1472
|
+
if (!completed) {
|
|
1473
|
+
(_d = this.protocolV2Assemblers.get(uuid)) === null || _d === void 0 ? void 0 : _d.reset();
|
|
1474
|
+
}
|
|
1475
|
+
this.resetProtocolV2Frames(uuid);
|
|
1476
|
+
this.activeProtocolV2Call = null;
|
|
1477
|
+
}
|
|
1478
|
+
if (this.runPromise === runPromise) {
|
|
1479
|
+
this.runPromise = null;
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
});
|
|
1483
|
+
}
|
|
1484
|
+
getProtocolType(path) {
|
|
1485
|
+
return this.deviceProtocol.get(path);
|
|
1486
|
+
}
|
|
786
1487
|
}
|
|
787
1488
|
|
|
788
|
-
|
|
1489
|
+
exports.configureProtocolV2BleTuning = configureProtocolV2BleTuning;
|
|
1490
|
+
exports["default"] = ReactNativeBleTransport;
|
|
1491
|
+
exports.getProtocolV2BleTuning = getProtocolV2BleTuning;
|
|
1492
|
+
exports.resetProtocolV2BleTuning = resetProtocolV2BleTuning;
|