@onekeyfe/hd-transport-react-native 1.2.0-alpha.16 → 1.2.0-alpha.160
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/BleTransport.d.ts +2 -4
- package/dist/BleTransport.d.ts.map +1 -1
- package/dist/bleStaleBond.d.ts +5 -0
- package/dist/bleStaleBond.d.ts.map +1 -0
- package/dist/bleStrategy.d.ts +7 -0
- package/dist/bleStrategy.d.ts.map +1 -1
- package/dist/constants.d.ts +2 -2
- package/dist/constants.d.ts.map +1 -1
- package/dist/index.d.ts +136 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +993 -326
- package/dist/transportLog.d.ts +1 -12
- package/dist/transportLog.d.ts.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/jest.config.js +10 -0
- package/package.json +6 -5
- package/src/BleTransport.ts +10 -42
- package/src/__tests__/BleTransport.test.ts +114 -0
- package/src/__tests__/bleStaleBond.test.ts +31 -0
- package/src/__tests__/bleStrategy.test.ts +93 -5
- package/src/__tests__/connectTimeout.test.ts +303 -0
- package/src/__tests__/constants.test.ts +20 -0
- package/src/__tests__/enumerate.test.ts +150 -0
- package/src/__tests__/protocolReprobe.test.ts +156 -0
- package/src/__tests__/protocolV1SchemaFixture.ts +39 -0
- package/src/__tests__/protocolV2Link.test.ts +916 -32
- package/src/__tests__/staleCallTimeout.test.ts +210 -0
- package/src/__tests__/writePacketTimeout.test.ts +305 -0
- package/src/bleStaleBond.ts +63 -0
- package/src/bleStrategy.ts +26 -11
- package/src/constants.ts +9 -14
- package/src/index.ts +1242 -295
- package/src/transportLog.ts +1 -18
- package/src/types.ts +1 -0
package/dist/index.js
CHANGED
|
@@ -93,7 +93,8 @@ const onDeviceBondState = (bleMacAddress) => new Promise((resolve, reject) => {
|
|
|
93
93
|
|
|
94
94
|
const IOS_PACKET_LENGTH = 128;
|
|
95
95
|
const ANDROID_PACKET_LENGTH = 192;
|
|
96
|
-
const
|
|
96
|
+
const IOS_PROTOCOL_V2_PACKET_LENGTH = 244;
|
|
97
|
+
const ANDROID_PROTOCOL_V2_PACKET_LENGTH = 244;
|
|
97
98
|
const ClassicServiceUUID = '00000001-0000-1000-8000-00805f9b34fb';
|
|
98
99
|
const OneKeyServices = {
|
|
99
100
|
classic: {
|
|
@@ -117,36 +118,38 @@ const getInfosForServiceUuid = (serviceUuid, deviceType) => {
|
|
|
117
118
|
return null;
|
|
118
119
|
}
|
|
119
120
|
const normalizedServiceUuid = normalizeBleUuid(serviceUuid);
|
|
120
|
-
const service = (_a = services[serviceUuid]) !== null && _a !== void 0 ? _a : Object.values(services).find(item => normalizeBleUuid(item.serviceUuid) === normalizedServiceUuid
|
|
121
|
+
const service = (_a = services[serviceUuid]) !== null && _a !== void 0 ? _a : Object.values(services).find(item => normalizeBleUuid(item.serviceUuid) === normalizedServiceUuid ||
|
|
122
|
+
hdShared.matchesKnownBleUuid(serviceUuid, hdShared.createKnownBleUuidAliases(item.serviceUuid)));
|
|
121
123
|
if (!service) {
|
|
122
124
|
return null;
|
|
123
125
|
}
|
|
124
126
|
return service;
|
|
125
127
|
};
|
|
126
128
|
const normalizeBleUuid = (uuid) => (uuid !== null && uuid !== void 0 ? uuid : '').replace(/-/g, '').toLowerCase();
|
|
127
|
-
const getBleUuidKey = (uuid) => {
|
|
128
|
-
const normalized = normalizeBleUuid(uuid);
|
|
129
|
-
return normalized.length >= 8 ? normalized.substring(4, 8) : normalized;
|
|
130
|
-
};
|
|
131
129
|
const isSameBleUuid = (left, right) => {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
return (
|
|
135
|
-
(getBleUuidKey(left) !== '' && getBleUuidKey(left) === getBleUuidKey(right)));
|
|
130
|
+
if (!left || !right)
|
|
131
|
+
return false;
|
|
132
|
+
return hdShared.matchesKnownBleUuid(left, hdShared.createKnownBleUuidAliases(right));
|
|
136
133
|
};
|
|
137
134
|
|
|
138
135
|
function hasWritableCapability(characteristic) {
|
|
139
136
|
return !!(characteristic.isWritableWithResponse || characteristic.isWritableWithoutResponse);
|
|
140
137
|
}
|
|
141
|
-
function resolveProtocolV2PacketCapacity({ platform, iosPacketLength =
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
138
|
+
function resolveProtocolV2PacketCapacity({ platform, iosPacketLength = IOS_PROTOCOL_V2_PACKET_LENGTH, androidPacketLength = ANDROID_PROTOCOL_V2_PACKET_LENGTH, mtu, }) {
|
|
139
|
+
const negotiatedMtu = typeof mtu === 'number' && Number.isFinite(mtu) && mtu > 3 ? Math.floor(mtu) : 23;
|
|
140
|
+
const payloadLength = negotiatedMtu - 3;
|
|
141
|
+
const configuredPacketLength = platform === 'ios' ? iosPacketLength : androidPacketLength;
|
|
142
|
+
return Math.min(configuredPacketLength, payloadLength);
|
|
143
|
+
}
|
|
144
|
+
function shouldRefreshNegotiatedMtu(mtu) {
|
|
145
|
+
return typeof mtu !== 'number' || !Number.isFinite(mtu) || mtu <= 23;
|
|
146
|
+
}
|
|
147
|
+
function shouldWriteProtocolV2WithResponse({ platform, highThroughput, requestedWithResponse, characteristic, }) {
|
|
148
|
+
if (!characteristic.isWritableWithResponse)
|
|
149
|
+
return false;
|
|
150
|
+
if (!characteristic.isWritableWithoutResponse)
|
|
151
|
+
return true;
|
|
152
|
+
return requestedWithResponse === true || (platform === 'ios' && !highThroughput);
|
|
150
153
|
}
|
|
151
154
|
|
|
152
155
|
const timer = process.env.NODE_ENV === 'development'
|
|
@@ -191,6 +194,40 @@ const subscribeBleOn = (bleManager, ms = 1000) => new Promise((resolve, reject)
|
|
|
191
194
|
}, ms);
|
|
192
195
|
});
|
|
193
196
|
|
|
197
|
+
const ATT_INSUFFICIENT_AUTHENTICATION = 5;
|
|
198
|
+
const ATT_UNLIKELY_ERROR = 14;
|
|
199
|
+
const ATT_INSUFFICIENT_ENCRYPTION = 15;
|
|
200
|
+
const IOS_PEER_REMOVED_PAIRING_INFORMATION = 14;
|
|
201
|
+
const nativeErrorText = (error) => [error.reason, error.message]
|
|
202
|
+
.filter((value) => typeof value === 'string')
|
|
203
|
+
.join(' ');
|
|
204
|
+
const isNativeBleStaleBondError = (error) => {
|
|
205
|
+
if (!error || typeof error !== 'object') {
|
|
206
|
+
return typeof error === 'string' ? hdShared.isBleStaleBondErrorText(error) : false;
|
|
207
|
+
}
|
|
208
|
+
const nativeError = error;
|
|
209
|
+
if (nativeError.attErrorCode === ATT_INSUFFICIENT_AUTHENTICATION ||
|
|
210
|
+
nativeError.attErrorCode === ATT_UNLIKELY_ERROR ||
|
|
211
|
+
nativeError.attErrorCode === ATT_INSUFFICIENT_ENCRYPTION ||
|
|
212
|
+
nativeError.iosErrorCode === IOS_PEER_REMOVED_PAIRING_INFORMATION) {
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
return hdShared.isBleStaleBondErrorText(nativeErrorText(nativeError));
|
|
216
|
+
};
|
|
217
|
+
const toBleStaleBondHardwareError = (error) => {
|
|
218
|
+
if (hdShared.isBleStaleBondHardwareError(error)) {
|
|
219
|
+
return error;
|
|
220
|
+
}
|
|
221
|
+
const nativeError = (error !== null && error !== void 0 ? error : {});
|
|
222
|
+
const text = nativeErrorText(nativeError);
|
|
223
|
+
const peerRemoved = nativeError.iosErrorCode === IOS_PEER_REMOVED_PAIRING_INFORMATION ||
|
|
224
|
+
nativeError.attErrorCode === ATT_UNLIKELY_ERROR ||
|
|
225
|
+
text.includes('Peer removed pairing information');
|
|
226
|
+
return hdShared.ERRORS.TypedError(peerRemoved
|
|
227
|
+
? hdShared.HardwareErrorCode.BlePeerRemovedPairingInformation
|
|
228
|
+
: hdShared.HardwareErrorCode.BleDeviceBondError, text || undefined);
|
|
229
|
+
};
|
|
230
|
+
|
|
194
231
|
const isHeaderChunk = (chunk) => {
|
|
195
232
|
if (chunk.length < 9)
|
|
196
233
|
return false;
|
|
@@ -203,59 +240,20 @@ const isHeaderChunk = (chunk) => {
|
|
|
203
240
|
return false;
|
|
204
241
|
};
|
|
205
242
|
|
|
206
|
-
const Log$1 = bleLogger;
|
|
207
243
|
class BleTransport {
|
|
208
244
|
constructor(device, writeCharacteristic, notifyCharacteristic) {
|
|
209
245
|
this.name = 'ReactNativeBleTransport';
|
|
210
|
-
this.mtuSize = 23;
|
|
211
246
|
this.id = device.id;
|
|
212
247
|
this.device = device;
|
|
213
248
|
this.writeCharacteristic = writeCharacteristic;
|
|
214
249
|
this.notifyCharacteristic = notifyCharacteristic;
|
|
215
250
|
}
|
|
216
|
-
writeWithRetry(data
|
|
251
|
+
writeWithRetry(data) {
|
|
217
252
|
return __awaiter(this, void 0, void 0, function* () {
|
|
218
|
-
|
|
219
|
-
yield this.writeCharacteristic.writeWithoutResponse(data);
|
|
220
|
-
}
|
|
221
|
-
catch (error) {
|
|
222
|
-
Log$1 === null || Log$1 === void 0 ? void 0 : Log$1.debug(`Write retry attempt ${BleTransport.MAX_RETRIES - retryCount + 1}, error: ${error}`);
|
|
223
|
-
if (retryCount > 0) {
|
|
224
|
-
yield hdShared.wait(BleTransport.RETRY_DELAY);
|
|
225
|
-
if (error.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected ||
|
|
226
|
-
error.errorCode === reactNativeBlePlx.BleErrorCode.CharacteristicNotFound) {
|
|
227
|
-
try {
|
|
228
|
-
yield this.device.connect();
|
|
229
|
-
yield this.device.discoverAllServicesAndCharacteristics();
|
|
230
|
-
}
|
|
231
|
-
catch (e) {
|
|
232
|
-
Log$1 === null || Log$1 === void 0 ? void 0 : Log$1.debug(`Connect or discoverAllServicesAndCharacteristics error: ${e}`);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
else {
|
|
236
|
-
Log$1 === null || Log$1 === void 0 ? void 0 : Log$1.debug(`writeCharacteristic error: ${error}`);
|
|
237
|
-
}
|
|
238
|
-
return this.writeWithRetry(data, retryCount - 1);
|
|
239
|
-
}
|
|
240
|
-
throw error;
|
|
241
|
-
}
|
|
253
|
+
yield this.writeCharacteristic.writeWithoutResponse(data);
|
|
242
254
|
});
|
|
243
255
|
}
|
|
244
256
|
}
|
|
245
|
-
BleTransport.MAX_RETRIES = 5;
|
|
246
|
-
BleTransport.RETRY_DELAY = 2000;
|
|
247
|
-
|
|
248
|
-
function createTransportCallLog(name, protocol, data) {
|
|
249
|
-
if (name === 'ResourceUpdate' || name === 'ResourceAck') {
|
|
250
|
-
return {
|
|
251
|
-
name,
|
|
252
|
-
protocol,
|
|
253
|
-
file_name: data.file_name,
|
|
254
|
-
hash: data.hash,
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
return { name, protocol };
|
|
258
|
-
}
|
|
259
257
|
|
|
260
258
|
const { check, ProtocolV1, parseConfigure } = transport__default["default"];
|
|
261
259
|
const Log = bleLogger;
|
|
@@ -264,10 +262,35 @@ const FIRMWARE_UPLOAD_WRITE_BURST_SIZE = reactNative.Platform.OS === 'ios' ? 4 :
|
|
|
264
262
|
const FIRMWARE_UPLOAD_WRITE_PAUSE_MS = reactNative.Platform.OS === 'ios' ? 8 : 10;
|
|
265
263
|
const FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS = reactNative.Platform.OS === 'ios' ? 24 : 30;
|
|
266
264
|
const FIRMWARE_UPLOAD_WRITE_MAX_RETRIES = 8;
|
|
267
|
-
const FIRMWARE_UPLOAD_RECONNECT_RETRY_DELAY_MS = 2000;
|
|
268
265
|
const ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH = 192;
|
|
269
266
|
const FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY = reactNative.Platform.OS === 'ios' ? IOS_PACKET_LENGTH : ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH;
|
|
270
267
|
const ANDROID_GATT_CONGESTED_STATUS = 143;
|
|
268
|
+
const isAsciiWhitespace = (code) => code === 0x09 ||
|
|
269
|
+
code === 0x0a ||
|
|
270
|
+
code === 0x0b ||
|
|
271
|
+
code === 0x0c ||
|
|
272
|
+
code === 0x0d ||
|
|
273
|
+
code === 0x20;
|
|
274
|
+
const hasGattCongestedStatus = (text) => {
|
|
275
|
+
let searchFrom = 0;
|
|
276
|
+
while (searchFrom < text.length) {
|
|
277
|
+
const statusIndex = text.indexOf('status', searchFrom);
|
|
278
|
+
if (statusIndex < 0)
|
|
279
|
+
return false;
|
|
280
|
+
let cursor = statusIndex + 'status'.length;
|
|
281
|
+
while (cursor < text.length && isAsciiWhitespace(text.charCodeAt(cursor)))
|
|
282
|
+
cursor += 1;
|
|
283
|
+
if (text[cursor] === ':' || text[cursor] === '=') {
|
|
284
|
+
cursor += 1;
|
|
285
|
+
while (cursor < text.length && isAsciiWhitespace(text.charCodeAt(cursor)))
|
|
286
|
+
cursor += 1;
|
|
287
|
+
}
|
|
288
|
+
if (text.startsWith(String(ANDROID_GATT_CONGESTED_STATUS), cursor))
|
|
289
|
+
return true;
|
|
290
|
+
searchFrom = statusIndex + 'status'.length;
|
|
291
|
+
}
|
|
292
|
+
return false;
|
|
293
|
+
};
|
|
271
294
|
const delay = (ms) => new Promise(resolve => {
|
|
272
295
|
setTimeout(resolve, ms);
|
|
273
296
|
});
|
|
@@ -275,10 +298,6 @@ const getFirmwareUploadWriteRetryType = (error) => {
|
|
|
275
298
|
if (!error || typeof error !== 'object')
|
|
276
299
|
return null;
|
|
277
300
|
const bleWriteError = error;
|
|
278
|
-
if (bleWriteError.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected ||
|
|
279
|
-
bleWriteError.errorCode === reactNativeBlePlx.BleErrorCode.CharacteristicNotFound) {
|
|
280
|
-
return 'reconnectable';
|
|
281
|
-
}
|
|
282
301
|
if (bleWriteError.androidErrorCode === ANDROID_GATT_CONGESTED_STATUS ||
|
|
283
302
|
bleWriteError.status === ANDROID_GATT_CONGESTED_STATUS) {
|
|
284
303
|
return 'congested';
|
|
@@ -286,18 +305,24 @@ const getFirmwareUploadWriteRetryType = (error) => {
|
|
|
286
305
|
const text = [bleWriteError.reason, bleWriteError.message, bleWriteError.name]
|
|
287
306
|
.filter(value => typeof value === 'string')
|
|
288
307
|
.join(' ');
|
|
289
|
-
return
|
|
308
|
+
return text.includes('GATT_CONGESTED') || hasGattCongestedStatus(text) ? 'congested' : null;
|
|
290
309
|
};
|
|
291
310
|
const resolveFirmwareUploadRetryDelay = (attempt, baseDelayMs = 200, maxDelayMs = 1200) => Math.min(baseDelayMs * Math.pow(2, attempt), maxDelayMs);
|
|
292
|
-
const
|
|
293
|
-
const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
|
|
311
|
+
const PROTOCOL_PROBE_TIMEOUT_MS = 3000;
|
|
294
312
|
const PROTOCOL_V2_PROBE_TIMEOUT_MS = 10000;
|
|
295
|
-
const
|
|
313
|
+
const BLE_WRITE_PACKET_TIMEOUT_MS = 10000;
|
|
314
|
+
const BLE_NATIVE_TEARDOWN_TIMEOUT_MS = 3000;
|
|
315
|
+
const WEDGED_WRITE_MESSAGE = 'BLE write timeout after';
|
|
316
|
+
const isWedgedWriteError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleWriteCharacteristicError &&
|
|
317
|
+
typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' &&
|
|
318
|
+
error.message.startsWith(WEDGED_WRITE_MESSAGE);
|
|
319
|
+
const BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
|
|
320
|
+
const DEVICE_SCAN_TIMEOUT_MS = 3000;
|
|
296
321
|
const IOS_NOTIFY_READY_DELAY_MS = 150;
|
|
297
322
|
const ANDROID_NOTIFY_READY_DELAY_MS = 300;
|
|
298
323
|
const DEFAULT_PROTOCOL_V2_BLE_TUNING = {
|
|
299
|
-
iosPacketLength:
|
|
300
|
-
androidPacketLength:
|
|
324
|
+
iosPacketLength: IOS_PROTOCOL_V2_PACKET_LENGTH,
|
|
325
|
+
androidPacketLength: ANDROID_PROTOCOL_V2_PACKET_LENGTH,
|
|
301
326
|
};
|
|
302
327
|
let protocolV2BleTuning = Object.assign({}, DEFAULT_PROTOCOL_V2_BLE_TUNING);
|
|
303
328
|
const normalizePositiveInteger = (value, fallback) => {
|
|
@@ -320,25 +345,37 @@ function resetProtocolV2BleTuning() {
|
|
|
320
345
|
function getProtocolV2BleTuning() {
|
|
321
346
|
return Object.assign({}, protocolV2BleTuning);
|
|
322
347
|
}
|
|
323
|
-
function inferProtocolHintFromDeviceName(name) {
|
|
324
|
-
return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
|
|
325
|
-
}
|
|
326
348
|
function getDeviceDisplayName(device) {
|
|
327
349
|
return (device === null || device === void 0 ? void 0 : device.name) || (device === null || device === void 0 ? void 0 : device.localName) || null;
|
|
328
350
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
}
|
|
336
|
-
const ANDROID_REQUEST_MTU = 256;
|
|
351
|
+
const IOS_REQUEST_MTU = 247;
|
|
352
|
+
const ANDROID_REQUEST_MTU = 517;
|
|
353
|
+
const BLE_MTU_REFRESH_RETRY_DELAY_MS = 200;
|
|
354
|
+
const ANDROID_HIGH_PRIORITY_IDLE_MS = 1000;
|
|
355
|
+
const getRequestedBleMtu = () => reactNative.Platform.OS === 'android' ? ANDROID_REQUEST_MTU : IOS_REQUEST_MTU;
|
|
356
|
+
const BLE_NATIVE_CONNECT_TIMEOUT_MS = 3000;
|
|
337
357
|
const connectOptions = {
|
|
338
|
-
requestMTU:
|
|
339
|
-
timeout:
|
|
358
|
+
requestMTU: getRequestedBleMtu(),
|
|
359
|
+
timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
|
|
340
360
|
refreshGatt: 'OnConnected',
|
|
341
361
|
};
|
|
362
|
+
const fallbackConnectOptions = {
|
|
363
|
+
timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
|
|
364
|
+
};
|
|
365
|
+
const BLE_CONNECT_TIMEOUT_MS = BLE_NATIVE_CONNECT_TIMEOUT_MS * 2 + 2000;
|
|
366
|
+
const BLE_GATT_SETUP_TIMEOUT_MS = 10000;
|
|
367
|
+
const PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = 3;
|
|
368
|
+
const BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
|
|
369
|
+
const CONNECT_TIMEOUT_MESSAGE = 'BLE connect timeout after';
|
|
370
|
+
const BLE_SETUP_WEDGED_MESSAGE = 'BLE setup wedged repeatedly';
|
|
371
|
+
const isConnectTimeoutError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleConnectedError &&
|
|
372
|
+
typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' &&
|
|
373
|
+
error.message.startsWith(CONNECT_TIMEOUT_MESSAGE);
|
|
374
|
+
const isWedgedBleSetupError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.PollingTimeout &&
|
|
375
|
+
typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' &&
|
|
376
|
+
error.message.startsWith(BLE_SETUP_WEDGED_MESSAGE);
|
|
377
|
+
const shouldRethrowBleSetupError = (error) => isConnectTimeoutError(error) || isWedgedBleSetupError(error);
|
|
378
|
+
const isNativeOperationTimeoutError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === reactNativeBlePlx.BleErrorCode.OperationTimedOut;
|
|
342
379
|
const tryToGetConfiguration = (device) => {
|
|
343
380
|
if (!device || !device.serviceUUIDs)
|
|
344
381
|
return null;
|
|
@@ -350,29 +387,30 @@ const tryToGetConfiguration = (device) => {
|
|
|
350
387
|
return null;
|
|
351
388
|
return infos;
|
|
352
389
|
};
|
|
353
|
-
const
|
|
354
|
-
if (reactNative.Platform.OS !== 'android')
|
|
390
|
+
const requestNegotiatedMtu = (device, stage, attempt) => __awaiter(void 0, void 0, void 0, function* () {
|
|
391
|
+
if (reactNative.Platform.OS !== 'ios' && reactNative.Platform.OS !== 'android')
|
|
355
392
|
return device;
|
|
356
393
|
try {
|
|
357
|
-
const mtuDevice = yield device.requestMTU(
|
|
358
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] MTU configured', {
|
|
359
|
-
deviceId: device.id,
|
|
360
|
-
requested: ANDROID_REQUEST_MTU,
|
|
361
|
-
actual: mtuDevice.mtu,
|
|
362
|
-
});
|
|
394
|
+
const mtuDevice = yield device.requestMTU(getRequestedBleMtu());
|
|
363
395
|
return mtuDevice;
|
|
364
396
|
}
|
|
365
397
|
catch (error) {
|
|
366
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport]
|
|
398
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] MTU refresh failed, continuing with current value', {
|
|
399
|
+
platform: reactNative.Platform.OS,
|
|
400
|
+
stage,
|
|
401
|
+
attempt,
|
|
402
|
+
actual: device.mtu,
|
|
403
|
+
error: error instanceof Error ? error.message : String(error),
|
|
404
|
+
});
|
|
367
405
|
return device;
|
|
368
406
|
}
|
|
369
407
|
});
|
|
408
|
+
const resolveNegotiatedMtu = (device) => requestNegotiatedMtu(device, 'connected', 0);
|
|
370
409
|
function remapError(error) {
|
|
371
410
|
var _a;
|
|
372
411
|
if (error instanceof reactNativeBlePlx.BleError) {
|
|
373
|
-
if (error
|
|
374
|
-
error
|
|
375
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePeerRemovedPairingInformation);
|
|
412
|
+
if (isNativeBleStaleBondError(error)) {
|
|
413
|
+
throw toBleStaleBondHardwareError(error);
|
|
376
414
|
}
|
|
377
415
|
if ((error === null || error === void 0 ? void 0 : error.attErrorCode) === 22) {
|
|
378
416
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceBondError);
|
|
@@ -393,9 +431,17 @@ class ReactNativeBleTransport {
|
|
|
393
431
|
this.stopped = false;
|
|
394
432
|
this.scanTimeout = DEVICE_SCAN_TIMEOUT_MS;
|
|
395
433
|
this.runPromise = null;
|
|
434
|
+
this.runPromiseDeviceId = null;
|
|
396
435
|
this.firmwareUploadWriteRecoveryIds = new Set();
|
|
397
436
|
this.deviceProtocol = new Map();
|
|
437
|
+
this.probingProtocols = new Map();
|
|
438
|
+
this.writeTimeoutCounts = new Map();
|
|
439
|
+
this.connectionSetupTimeoutCounts = new Map();
|
|
398
440
|
this.deviceProtocolHints = new Map();
|
|
441
|
+
this.sessionProtocols = new Map();
|
|
442
|
+
this.confirmedProtocolV2 = new Set();
|
|
443
|
+
this.protocolReprobeFailures = new Map();
|
|
444
|
+
this.staleBondErrors = new Map();
|
|
399
445
|
this.protocolV2Assemblers = new Map();
|
|
400
446
|
this.protocolV2FrameQueues = new Map();
|
|
401
447
|
this.protocolV2FramePromises = new Map();
|
|
@@ -416,12 +462,17 @@ class ReactNativeBleTransport {
|
|
|
416
462
|
this.rejectProtocolV2Frames(uuid, new Error(reason));
|
|
417
463
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 link invalidated:', uuid, reason);
|
|
418
464
|
if (reason.startsWith('Protocol V2 link-fatal error:')) {
|
|
419
|
-
yield this.
|
|
465
|
+
yield this.releaseNative(uuid, true);
|
|
420
466
|
}
|
|
421
467
|
}),
|
|
422
468
|
});
|
|
423
469
|
this.monitorTokens = new Map();
|
|
470
|
+
this.disconnectEventTokens = new Map();
|
|
471
|
+
this.protocolV2HighVolumeLogSignatures = new Map();
|
|
472
|
+
this.androidHighPriorityDevices = new Set();
|
|
473
|
+
this.androidPriorityResetTimers = new Map();
|
|
424
474
|
this.nextMonitorToken = 1;
|
|
475
|
+
this.lifecycleOperations = new Map();
|
|
425
476
|
this.scanTimeout = (_a = options.scanTimeout) !== null && _a !== void 0 ? _a : DEVICE_SCAN_TIMEOUT_MS;
|
|
426
477
|
}
|
|
427
478
|
init(logger, emitter) {
|
|
@@ -434,10 +485,18 @@ class ReactNativeBleTransport {
|
|
|
434
485
|
this._messages = messages;
|
|
435
486
|
}
|
|
436
487
|
configureProtocolV2(signedData) {
|
|
488
|
+
const configuration = typeof signedData === 'string' ? signedData : JSON.stringify(signedData);
|
|
489
|
+
if (this.protocolV2SchemaConfiguration === configuration) {
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
const isReconfiguration = this.protocolV2SchemaConfiguration !== undefined;
|
|
437
493
|
this._messagesV2 = parseConfigure(signedData);
|
|
438
|
-
this.
|
|
439
|
-
|
|
440
|
-
.
|
|
494
|
+
this.protocolV2SchemaConfiguration = configuration;
|
|
495
|
+
if (isReconfiguration) {
|
|
496
|
+
this.protocolV2Links
|
|
497
|
+
.invalidateAllLinks('Protocol V2 schema reconfigured')
|
|
498
|
+
.catch(error => Log === null || Log === void 0 ? void 0 : Log.debug('Protocol V2 schema link cleanup failed:', error));
|
|
499
|
+
}
|
|
441
500
|
}
|
|
442
501
|
listen() {
|
|
443
502
|
}
|
|
@@ -448,7 +507,6 @@ class ReactNativeBleTransport {
|
|
|
448
507
|
return Promise.resolve(this.blePlxManager);
|
|
449
508
|
}
|
|
450
509
|
resolveCharacteristics(device) {
|
|
451
|
-
var _a, _b, _c, _d;
|
|
452
510
|
return __awaiter(this, void 0, void 0, function* () {
|
|
453
511
|
yield device.discoverAllServicesAndCharacteristics();
|
|
454
512
|
let infos = tryToGetConfiguration(device);
|
|
@@ -465,19 +523,11 @@ class ReactNativeBleTransport {
|
|
|
465
523
|
}
|
|
466
524
|
}
|
|
467
525
|
}
|
|
468
|
-
let fallbackServiceUuid;
|
|
469
526
|
if (!infos) {
|
|
470
527
|
const services = yield device.services();
|
|
471
528
|
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));
|
|
472
|
-
const knownService = services.find(service => getInfosForServiceUuid(service.uuid, 'classic'));
|
|
473
|
-
const fallbackService = (_a = knownService !== null && knownService !== void 0 ? knownService : services.find(service => !isGenericBleService(service.uuid))) !== null && _a !== void 0 ? _a : services[0];
|
|
474
|
-
if (fallbackService) {
|
|
475
|
-
fallbackServiceUuid = fallbackService.uuid;
|
|
476
|
-
characteristics = yield device.characteristicsForService(fallbackService.uuid);
|
|
477
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Using fallback BLE service:', fallbackService.uuid);
|
|
478
|
-
}
|
|
479
529
|
}
|
|
480
|
-
if (!infos
|
|
530
|
+
if (!infos) {
|
|
481
531
|
try {
|
|
482
532
|
Log === null || Log === void 0 ? void 0 : Log.debug('cancel connection when service not found');
|
|
483
533
|
yield device.cancelConnection();
|
|
@@ -487,9 +537,7 @@ class ReactNativeBleTransport {
|
|
|
487
537
|
}
|
|
488
538
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
|
|
489
539
|
}
|
|
490
|
-
const serviceUuid
|
|
491
|
-
const writeUuid = (_c = infos === null || infos === void 0 ? void 0 : infos.writeUuid) !== null && _c !== void 0 ? _c : '00000002-0000-1000-8000-00805f9b34fb';
|
|
492
|
-
const notifyUuid = (_d = infos === null || infos === void 0 ? void 0 : infos.notifyUuid) !== null && _d !== void 0 ? _d : '00000003-0000-1000-8000-00805f9b34fb';
|
|
540
|
+
const { serviceUuid, writeUuid, notifyUuid } = infos;
|
|
493
541
|
if (!serviceUuid) {
|
|
494
542
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
|
|
495
543
|
}
|
|
@@ -530,8 +578,8 @@ class ReactNativeBleTransport {
|
|
|
530
578
|
attachDisconnectSubscription(transport, device, uuid) {
|
|
531
579
|
var _a;
|
|
532
580
|
(_a = transport.disconnectSubscription) === null || _a === void 0 ? void 0 : _a.remove();
|
|
581
|
+
const { monitorToken } = transport;
|
|
533
582
|
transport.disconnectSubscription = device.onDisconnected(() => {
|
|
534
|
-
var _a;
|
|
535
583
|
if (this.firmwareUploadWriteRecoveryIds.has(uuid)) {
|
|
536
584
|
Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored during FirmwareUpload write recovery: ', uuid);
|
|
537
585
|
return;
|
|
@@ -540,17 +588,16 @@ class ReactNativeBleTransport {
|
|
|
540
588
|
Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored for stale transport: ', device === null || device === void 0 ? void 0 : device.id);
|
|
541
589
|
return;
|
|
542
590
|
}
|
|
591
|
+
if (this.monitorTokens.get(uuid) !== monitorToken) {
|
|
592
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored for stale generation: ', device === null || device === void 0 ? void 0 : device.id);
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
543
595
|
try {
|
|
544
596
|
Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect: ', device === null || device === void 0 ? void 0 : device.id);
|
|
545
|
-
(
|
|
546
|
-
|
|
547
|
-
id: device === null || device === void 0 ? void 0 : device.id,
|
|
548
|
-
connectId: device === null || device === void 0 ? void 0 : device.id,
|
|
549
|
-
});
|
|
550
|
-
if (this.runPromise) {
|
|
597
|
+
this.emitDeviceDisconnect(uuid, device === null || device === void 0 ? void 0 : device.name, monitorToken);
|
|
598
|
+
if (this.runPromise && this.runPromiseDeviceId === uuid) {
|
|
551
599
|
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError);
|
|
552
600
|
this.runPromise.reject(error);
|
|
553
|
-
this.rejectAllProtocolV2Frames(error);
|
|
554
601
|
}
|
|
555
602
|
}
|
|
556
603
|
catch (e) {
|
|
@@ -561,6 +608,22 @@ class ReactNativeBleTransport {
|
|
|
561
608
|
}
|
|
562
609
|
});
|
|
563
610
|
}
|
|
611
|
+
emitDeviceDisconnect(uuid, name, token) {
|
|
612
|
+
var _a;
|
|
613
|
+
if (token === undefined || this.disconnectEventTokens.get(uuid) === token) {
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
if (this.monitorTokens.get(uuid) !== token) {
|
|
617
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect event ignored for stale generation: ', uuid);
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
this.disconnectEventTokens.set(uuid, token);
|
|
621
|
+
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit(transport.TRANSPORT_EVENT.DEVICE_DISCONNECT, {
|
|
622
|
+
name,
|
|
623
|
+
id: uuid,
|
|
624
|
+
connectId: uuid,
|
|
625
|
+
});
|
|
626
|
+
}
|
|
564
627
|
reconnectFirmwareUploadTransport(uuid, transport) {
|
|
565
628
|
var _a, _b;
|
|
566
629
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -574,19 +637,19 @@ class ReactNativeBleTransport {
|
|
|
574
637
|
const isConnected = yield device.isConnected().catch(() => false);
|
|
575
638
|
if (!isConnected) {
|
|
576
639
|
try {
|
|
577
|
-
device = yield device.connect(connectOptions);
|
|
640
|
+
device = yield this.connectWithTimeout(uuid, () => device.connect(connectOptions));
|
|
578
641
|
}
|
|
579
642
|
catch (e) {
|
|
580
643
|
if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
|
|
581
644
|
e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
|
|
582
|
-
device = yield device.connect();
|
|
645
|
+
device = yield this.connectWithTimeout(uuid, () => device.connect());
|
|
583
646
|
}
|
|
584
647
|
else if (e.errorCode !== reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
|
|
585
648
|
throw e;
|
|
586
649
|
}
|
|
587
650
|
}
|
|
588
651
|
}
|
|
589
|
-
const { writeCharacteristic, notifyCharacteristic } = yield this.
|
|
652
|
+
const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, device);
|
|
590
653
|
transport.device = device;
|
|
591
654
|
transport.writeCharacteristic = writeCharacteristic;
|
|
592
655
|
transport.notifyCharacteristic = notifyCharacteristic;
|
|
@@ -634,7 +697,7 @@ class ReactNativeBleTransport {
|
|
|
634
697
|
allowDuplicates: true,
|
|
635
698
|
scanMode: reactNativeBlePlx.ScanMode.LowLatency,
|
|
636
699
|
}, (error, device) => {
|
|
637
|
-
var _a, _b
|
|
700
|
+
var _a, _b;
|
|
638
701
|
if (error) {
|
|
639
702
|
Log === null || Log === void 0 ? void 0 : Log.debug('ble scan error: ', error);
|
|
640
703
|
if ([reactNativeBlePlx.BleErrorCode.BluetoothPoweredOff, reactNativeBlePlx.BleErrorCode.BluetoothInUnknownState].includes(error.errorCode)) {
|
|
@@ -655,9 +718,14 @@ class ReactNativeBleTransport {
|
|
|
655
718
|
return;
|
|
656
719
|
}
|
|
657
720
|
const displayName = getDeviceDisplayName(device);
|
|
658
|
-
const
|
|
659
|
-
|
|
660
|
-
|
|
721
|
+
const isUnnamedIOSPeripheral = reactNative.Platform.OS === 'ios' && !(displayName === null || displayName === void 0 ? void 0 : displayName.trim());
|
|
722
|
+
const isOneKey = !isUnnamedIOSPeripheral &&
|
|
723
|
+
hdShared.isOnekeyBluetoothDevice({
|
|
724
|
+
id: device === null || device === void 0 ? void 0 : device.id,
|
|
725
|
+
name: device === null || device === void 0 ? void 0 : device.name,
|
|
726
|
+
localName: device === null || device === void 0 ? void 0 : device.localName,
|
|
727
|
+
serviceUuids: (_b = device === null || device === void 0 ? void 0 : device.serviceUUIDs) !== null && _b !== void 0 ? _b : getBluetoothServiceUuids(),
|
|
728
|
+
});
|
|
661
729
|
if (isOneKey) {
|
|
662
730
|
addDevice(device);
|
|
663
731
|
}
|
|
@@ -672,10 +740,15 @@ class ReactNativeBleTransport {
|
|
|
672
740
|
});
|
|
673
741
|
getConnectedDeviceIds(reactNative.Platform.OS === 'ios' ? getBluetoothServiceUuids() : []).then(devices => {
|
|
674
742
|
for (const device of devices) {
|
|
675
|
-
const
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
if (
|
|
743
|
+
const localName = 'localName' in device && typeof device.localName === 'string'
|
|
744
|
+
? device.localName
|
|
745
|
+
: null;
|
|
746
|
+
if (hdShared.isOnekeyBluetoothDevice({
|
|
747
|
+
id: device.id,
|
|
748
|
+
name: device.name,
|
|
749
|
+
localName,
|
|
750
|
+
serviceUuids: device.serviceUUIDs,
|
|
751
|
+
})) {
|
|
679
752
|
Log === null || Log === void 0 ? void 0 : Log.debug('search connected peripheral: ', device.id);
|
|
680
753
|
addDevice(device);
|
|
681
754
|
}
|
|
@@ -685,16 +758,11 @@ class ReactNativeBleTransport {
|
|
|
685
758
|
var _a;
|
|
686
759
|
if (deviceList.every(d => d.id !== device.id)) {
|
|
687
760
|
const displayName = (_a = getDeviceDisplayName(device)) !== null && _a !== void 0 ? _a : 'Unknown BLE Device';
|
|
688
|
-
const protocolHint = inferProtocolHintFromDeviceName(displayName);
|
|
689
|
-
if (protocolHint) {
|
|
690
|
-
this.deviceProtocolHints.set(device.id, protocolHint);
|
|
691
|
-
}
|
|
692
761
|
deviceList.push(Object.assign(Object.assign({}, device), { name: displayName, commType: 'ble' }));
|
|
693
762
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] OneKey BLE device discovered', {
|
|
694
763
|
deviceId: device.id,
|
|
695
764
|
name: displayName,
|
|
696
765
|
serviceUUIDs: device.serviceUUIDs,
|
|
697
|
-
protocolHint,
|
|
698
766
|
});
|
|
699
767
|
}
|
|
700
768
|
};
|
|
@@ -705,13 +773,70 @@ class ReactNativeBleTransport {
|
|
|
705
773
|
}));
|
|
706
774
|
});
|
|
707
775
|
}
|
|
776
|
+
installTransportForAcquire(uuid, device, characteristics) {
|
|
777
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
778
|
+
const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristicsWithTimeout(uuid, device));
|
|
779
|
+
const transport$1 = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
|
|
780
|
+
transport$1.mtuSize = typeof device.mtu === 'number' ? device.mtu : undefined;
|
|
781
|
+
const monitorToken = this.nextMonitorToken;
|
|
782
|
+
this.nextMonitorToken += 1;
|
|
783
|
+
const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
|
|
784
|
+
transport$1.monitorToken = monitorToken;
|
|
785
|
+
transport$1.notifyTransactionId = notifyTransactionId;
|
|
786
|
+
this.monitorTokens.set(uuid, monitorToken);
|
|
787
|
+
transport$1.notifySubscription = this._monitorCharacteristic(transport$1.notifyCharacteristic, uuid, monitorToken, notifyTransactionId);
|
|
788
|
+
transportCache[uuid] = transport$1;
|
|
789
|
+
this.protocolV2HighVolumeLogSignatures.set(uuid, new Set());
|
|
790
|
+
this.protocolV2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES));
|
|
791
|
+
if (reactNative.Platform.OS === 'ios') {
|
|
792
|
+
yield new Promise(resolve => {
|
|
793
|
+
setTimeout(resolve, IOS_NOTIFY_READY_DELAY_MS);
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
else if (reactNative.Platform.OS === 'android') {
|
|
797
|
+
yield delay(ANDROID_NOTIFY_READY_DELAY_MS);
|
|
798
|
+
}
|
|
799
|
+
const initialMtu = transport$1.mtuSize;
|
|
800
|
+
let refreshAttempts = 0;
|
|
801
|
+
if ((reactNative.Platform.OS === 'ios' || reactNative.Platform.OS === 'android') &&
|
|
802
|
+
shouldRefreshNegotiatedMtu(transport$1.mtuSize)) {
|
|
803
|
+
refreshAttempts += 1;
|
|
804
|
+
let refreshedDevice = yield requestNegotiatedMtu(transport$1.device, 'servicesAndNotifyReady', 1);
|
|
805
|
+
transport$1.device = refreshedDevice;
|
|
806
|
+
transport$1.mtuSize =
|
|
807
|
+
typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport$1.mtuSize;
|
|
808
|
+
if (shouldRefreshNegotiatedMtu(transport$1.mtuSize)) {
|
|
809
|
+
yield delay(BLE_MTU_REFRESH_RETRY_DELAY_MS);
|
|
810
|
+
refreshAttempts += 1;
|
|
811
|
+
refreshedDevice = yield requestNegotiatedMtu(transport$1.device, 'servicesAndNotifyReady', 2);
|
|
812
|
+
transport$1.device = refreshedDevice;
|
|
813
|
+
transport$1.mtuSize =
|
|
814
|
+
typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport$1.mtuSize;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE MTU ready', {
|
|
818
|
+
platform: reactNative.Platform.OS,
|
|
819
|
+
requested: getRequestedBleMtu(),
|
|
820
|
+
initial: initialMtu,
|
|
821
|
+
actual: transport$1.mtuSize,
|
|
822
|
+
refreshAttempts,
|
|
823
|
+
});
|
|
824
|
+
return transport$1;
|
|
825
|
+
});
|
|
826
|
+
}
|
|
708
827
|
acquire(input) {
|
|
709
|
-
var _a, _b;
|
|
710
828
|
return __awaiter(this, void 0, void 0, function* () {
|
|
711
|
-
const { uuid
|
|
829
|
+
const { uuid } = input;
|
|
712
830
|
if (!uuid) {
|
|
713
831
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleRequiredUUID);
|
|
714
832
|
}
|
|
833
|
+
return this.runLifecycleOperation(uuid, () => this.acquireUnlocked(input));
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
acquireUnlocked(input) {
|
|
837
|
+
var _a;
|
|
838
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
839
|
+
const { uuid, forceCleanRunPromise, expectedProtocol } = input;
|
|
715
840
|
const cachedTransport = transportCache[uuid];
|
|
716
841
|
if (cachedTransport) {
|
|
717
842
|
const cachedProtocol = this.deviceProtocol.get(uuid);
|
|
@@ -723,14 +848,14 @@ class ReactNativeBleTransport {
|
|
|
723
848
|
return { uuid, protocolType: cachedProtocol };
|
|
724
849
|
}
|
|
725
850
|
Log === null || Log === void 0 ? void 0 : Log.debug('transport not reusable, will release: ', uuid);
|
|
726
|
-
yield this.
|
|
851
|
+
yield this.releaseUnlocked(uuid, true);
|
|
727
852
|
}
|
|
728
853
|
let device = null;
|
|
729
854
|
if (forceCleanRunPromise && this.runPromise) {
|
|
730
855
|
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
|
|
731
856
|
this.runPromise.reject(error);
|
|
732
|
-
this.rejectAllProtocolV2Frames(error);
|
|
733
857
|
this.runPromise = null;
|
|
858
|
+
this.runPromiseDeviceId = null;
|
|
734
859
|
Log === null || Log === void 0 ? void 0 : Log.debug('Force clean Bluetooth run promise, forceCleanRunPromise: ', forceCleanRunPromise);
|
|
735
860
|
}
|
|
736
861
|
const blePlxManager = yield this.getPlxManager();
|
|
@@ -763,14 +888,17 @@ class ReactNativeBleTransport {
|
|
|
763
888
|
if (!device) {
|
|
764
889
|
Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device: ', uuid);
|
|
765
890
|
try {
|
|
766
|
-
device = yield blePlxManager.connectToDevice(uuid, connectOptions);
|
|
891
|
+
device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, connectOptions));
|
|
767
892
|
}
|
|
768
893
|
catch (e) {
|
|
769
894
|
Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device has error: ', e);
|
|
895
|
+
if (shouldRethrowBleSetupError(e)) {
|
|
896
|
+
throw e;
|
|
897
|
+
}
|
|
770
898
|
if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
|
|
771
899
|
e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
|
|
772
900
|
Log === null || Log === void 0 ? void 0 : Log.debug('first try to reconnect without params');
|
|
773
|
-
device = yield blePlxManager.connectToDevice(uuid);
|
|
901
|
+
device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, fallbackConnectOptions));
|
|
774
902
|
}
|
|
775
903
|
else if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
|
|
776
904
|
Log === null || Log === void 0 ? void 0 : Log.debug('device already connected');
|
|
@@ -786,23 +914,27 @@ class ReactNativeBleTransport {
|
|
|
786
914
|
}
|
|
787
915
|
if (!(yield device.isConnected())) {
|
|
788
916
|
Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device: ', uuid);
|
|
917
|
+
const disconnectedDevice = device;
|
|
789
918
|
try {
|
|
790
|
-
device = yield
|
|
919
|
+
device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(connectOptions));
|
|
791
920
|
}
|
|
792
921
|
catch (e) {
|
|
793
922
|
Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device has error: ', e);
|
|
923
|
+
if (shouldRethrowBleSetupError(e)) {
|
|
924
|
+
throw e;
|
|
925
|
+
}
|
|
794
926
|
if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
|
|
795
927
|
e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
|
|
796
928
|
Log === null || Log === void 0 ? void 0 : Log.debug('second try to reconnect without params');
|
|
797
929
|
try {
|
|
798
|
-
device = yield
|
|
930
|
+
device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
|
|
799
931
|
}
|
|
800
932
|
catch (e) {
|
|
801
933
|
Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect error: ', e);
|
|
802
934
|
if (e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
|
|
803
935
|
Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect');
|
|
804
|
-
yield
|
|
805
|
-
device = yield
|
|
936
|
+
yield disconnectedDevice.cancelConnection();
|
|
937
|
+
device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
|
|
806
938
|
}
|
|
807
939
|
}
|
|
808
940
|
}
|
|
@@ -811,51 +943,47 @@ class ReactNativeBleTransport {
|
|
|
811
943
|
}
|
|
812
944
|
}
|
|
813
945
|
}
|
|
814
|
-
device = yield
|
|
815
|
-
const
|
|
946
|
+
device = yield resolveNegotiatedMtu(device);
|
|
947
|
+
const acquiredDevice = device;
|
|
948
|
+
const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, acquiredDevice);
|
|
816
949
|
const protocolHint = expectedProtocol
|
|
817
950
|
? undefined
|
|
818
|
-
: (_a =
|
|
819
|
-
yield this.
|
|
951
|
+
: (_a = input.protocolHint) !== null && _a !== void 0 ? _a : this.deviceProtocolHints.get(uuid);
|
|
952
|
+
yield this.releaseUnlocked(uuid, true);
|
|
820
953
|
if (protocolHint) {
|
|
821
954
|
this.deviceProtocolHints.set(uuid, protocolHint);
|
|
822
955
|
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
yield new Promise(resolve => {
|
|
838
|
-
setTimeout(resolve, IOS_NOTIFY_READY_DELAY_MS);
|
|
839
|
-
});
|
|
956
|
+
yield this.installTransportForAcquire(uuid, acquiredDevice, {
|
|
957
|
+
writeCharacteristic,
|
|
958
|
+
notifyCharacteristic,
|
|
959
|
+
});
|
|
960
|
+
try {
|
|
961
|
+
const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint, () => __awaiter(this, void 0, void 0, function* () {
|
|
962
|
+
yield this.installTransportForAcquire(uuid, acquiredDevice);
|
|
963
|
+
}));
|
|
964
|
+
const currentTransport = transportCache[uuid];
|
|
965
|
+
if (!currentTransport) {
|
|
966
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
|
|
967
|
+
}
|
|
968
|
+
this.attachDisconnectSubscription(currentTransport, currentTransport.device, uuid);
|
|
969
|
+
return { uuid, protocolType };
|
|
840
970
|
}
|
|
841
|
-
|
|
842
|
-
|
|
971
|
+
catch (error) {
|
|
972
|
+
if (hdShared.isBleStaleBondHardwareError(error)) {
|
|
973
|
+
yield this.disconnectUnlocked(uuid);
|
|
974
|
+
}
|
|
975
|
+
else {
|
|
976
|
+
yield this.releaseUnlocked(uuid, true);
|
|
977
|
+
}
|
|
978
|
+
throw error;
|
|
843
979
|
}
|
|
844
|
-
const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint);
|
|
845
|
-
(_b = this.emitter) === null || _b === void 0 ? void 0 : _b.emit('device-connect', {
|
|
846
|
-
name: device.name,
|
|
847
|
-
id: device.id,
|
|
848
|
-
connectId: device.id,
|
|
849
|
-
});
|
|
850
|
-
this.attachDisconnectSubscription(transport$1, device, uuid);
|
|
851
|
-
return { uuid, protocolType };
|
|
852
980
|
});
|
|
853
981
|
}
|
|
854
982
|
_monitorCharacteristic(characteristic, uuid, monitorToken, notifyTransactionId) {
|
|
855
983
|
let bufferLength = 0;
|
|
856
984
|
let buffer$1 = [];
|
|
857
985
|
const subscription = characteristic.monitor((error, c) => {
|
|
858
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p
|
|
986
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
859
987
|
const isCurrentMonitor = this.monitorTokens.get(uuid) === monitorToken;
|
|
860
988
|
if (error) {
|
|
861
989
|
Log === null || Log === void 0 ? void 0 : Log.debug(`error monitor ${characteristic.uuid}, deviceId: ${characteristic.deviceID}: ${error}`);
|
|
@@ -867,46 +995,42 @@ class ReactNativeBleTransport {
|
|
|
867
995
|
Log === null || Log === void 0 ? void 0 : Log.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
|
|
868
996
|
return;
|
|
869
997
|
}
|
|
870
|
-
if (
|
|
998
|
+
if (isNativeBleStaleBondError(error)) {
|
|
999
|
+
this.rememberStaleBondError(uuid, toBleStaleBondHardwareError(error));
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
if (this.getActiveProtocol(uuid) === 'V2') {
|
|
871
1003
|
let errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
|
|
872
1004
|
if ((_a = error.reason) === null || _a === void 0 ? void 0 : _a.includes('The connection has timed out unexpectedly')) {
|
|
873
1005
|
errorCode = hdShared.HardwareErrorCode.BleTimeoutError;
|
|
874
1006
|
}
|
|
875
|
-
else if ((_b = error.reason) === null || _b === void 0 ? void 0 : _b.includes('
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
((
|
|
880
|
-
((_e = error.reason) === null || _e === void 0 ? void 0 : _e.includes('The handle is invalid')) ||
|
|
881
|
-
((_f = error.reason) === null || _f === void 0 ? void 0 : _f.includes('Writing is not permitted')) ||
|
|
882
|
-
((_g = error.reason) === null || _g === void 0 ? void 0 : _g.includes('notify change failed for device'))) {
|
|
1007
|
+
else if (((_b = error.reason) === null || _b === void 0 ? void 0 : _b.includes('Cannot write client characteristic config descriptor')) ||
|
|
1008
|
+
((_c = error.reason) === null || _c === void 0 ? void 0 : _c.includes('Cannot find client characteristic config descriptor')) ||
|
|
1009
|
+
((_d = error.reason) === null || _d === void 0 ? void 0 : _d.includes('The handle is invalid')) ||
|
|
1010
|
+
((_e = error.reason) === null || _e === void 0 ? void 0 : _e.includes('Writing is not permitted')) ||
|
|
1011
|
+
((_f = error.reason) === null || _f === void 0 ? void 0 : _f.includes('notify change failed for device'))) {
|
|
883
1012
|
errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure;
|
|
884
1013
|
}
|
|
885
1014
|
this.rejectProtocolV2Frames(uuid, hdShared.ERRORS.TypedError(errorCode));
|
|
886
1015
|
return;
|
|
887
1016
|
}
|
|
888
|
-
if (this.runPromise) {
|
|
1017
|
+
if (this.runPromise && this.runPromiseDeviceId === uuid) {
|
|
889
1018
|
let ERROR = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
|
|
890
|
-
if ((
|
|
1019
|
+
if ((_g = error.reason) === null || _g === void 0 ? void 0 : _g.includes('The connection has timed out unexpectedly')) {
|
|
891
1020
|
ERROR = hdShared.HardwareErrorCode.BleTimeoutError;
|
|
892
1021
|
}
|
|
893
|
-
if ((
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
((
|
|
898
|
-
((_m = error.reason) === null || _m === void 0 ? void 0 : _m.includes('The handle is invalid')) ||
|
|
899
|
-
((_o = error.reason) === null || _o === void 0 ? void 0 : _o.includes('Writing is not permitted')) ||
|
|
900
|
-
((_p = error.reason) === null || _p === void 0 ? void 0 : _p.includes('notify change failed for device'))) {
|
|
1022
|
+
if (((_h = error.reason) === null || _h === void 0 ? void 0 : _h.includes('Cannot write client characteristic config descriptor')) ||
|
|
1023
|
+
((_j = error.reason) === null || _j === void 0 ? void 0 : _j.includes('Cannot find client characteristic config descriptor')) ||
|
|
1024
|
+
((_k = error.reason) === null || _k === void 0 ? void 0 : _k.includes('The handle is invalid')) ||
|
|
1025
|
+
((_l = error.reason) === null || _l === void 0 ? void 0 : _l.includes('Writing is not permitted')) ||
|
|
1026
|
+
((_m = error.reason) === null || _m === void 0 ? void 0 : _m.includes('notify change failed for device'))) {
|
|
901
1027
|
const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure);
|
|
902
1028
|
this.runPromise.reject(notifyError);
|
|
903
|
-
this.rejectAllProtocolV2Frames(notifyError);
|
|
904
1029
|
Log === null || Log === void 0 ? void 0 : Log.debug(`${hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure} ${error.message} ${error.reason}`);
|
|
905
1030
|
return;
|
|
906
1031
|
}
|
|
907
1032
|
const notifyError = hdShared.ERRORS.TypedError(ERROR);
|
|
908
1033
|
this.runPromise.reject(notifyError);
|
|
909
|
-
this.rejectAllProtocolV2Frames(notifyError);
|
|
910
1034
|
Log === null || Log === void 0 ? void 0 : Log.debug(': monitor notify error, and has unreleased Promise', Error);
|
|
911
1035
|
}
|
|
912
1036
|
return;
|
|
@@ -920,7 +1044,7 @@ class ReactNativeBleTransport {
|
|
|
920
1044
|
}
|
|
921
1045
|
try {
|
|
922
1046
|
const data = buffer.Buffer.from(c.value, 'base64');
|
|
923
|
-
const protocol = this.
|
|
1047
|
+
const protocol = this.getActiveProtocol(uuid);
|
|
924
1048
|
if (!protocol) {
|
|
925
1049
|
Log === null || Log === void 0 ? void 0 : Log.debug('monitor data ignored before protocol detection: ', uuid);
|
|
926
1050
|
return;
|
|
@@ -940,32 +1064,46 @@ class ReactNativeBleTransport {
|
|
|
940
1064
|
const value = buffer.Buffer.from(buffer$1);
|
|
941
1065
|
bufferLength = 0;
|
|
942
1066
|
buffer$1 = [];
|
|
943
|
-
(
|
|
1067
|
+
if (this.runPromiseDeviceId === uuid) {
|
|
1068
|
+
(_o = this.runPromise) === null || _o === void 0 ? void 0 : _o.resolve(value.toString('hex'));
|
|
1069
|
+
}
|
|
944
1070
|
}
|
|
945
1071
|
}
|
|
946
1072
|
catch (error) {
|
|
947
1073
|
Log === null || Log === void 0 ? void 0 : Log.debug('monitor data error: ', error);
|
|
948
1074
|
const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
|
|
949
|
-
if (this.
|
|
1075
|
+
if (this.getActiveProtocol(uuid) === 'V2') {
|
|
950
1076
|
this.rejectProtocolV2Frames(uuid, notifyError);
|
|
951
1077
|
}
|
|
952
|
-
else {
|
|
953
|
-
(
|
|
1078
|
+
else if (this.runPromiseDeviceId === uuid) {
|
|
1079
|
+
(_p = this.runPromise) === null || _p === void 0 ? void 0 : _p.reject(notifyError);
|
|
954
1080
|
}
|
|
955
1081
|
}
|
|
956
1082
|
}, notifyTransactionId);
|
|
957
1083
|
return subscription;
|
|
958
1084
|
}
|
|
959
1085
|
release(uuid, onclose = false) {
|
|
960
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
961
1086
|
return __awaiter(this, void 0, void 0, function* () {
|
|
962
|
-
|
|
1087
|
+
return this.runLifecycleOperation(uuid, () => this.releaseUnlocked(uuid, onclose));
|
|
1088
|
+
});
|
|
1089
|
+
}
|
|
1090
|
+
releaseUnlocked(uuid, onclose = false) {
|
|
1091
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
963
1092
|
yield this.protocolV2Links.invalidateLink(uuid, 'React Native BLE transport released');
|
|
964
|
-
|
|
1093
|
+
return this.releaseNative(uuid, onclose);
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
releaseNative(uuid, onclose = false) {
|
|
1097
|
+
var _a, _b, _c, _d, _e;
|
|
1098
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1099
|
+
const transport = transportCache[uuid];
|
|
1100
|
+
const manager = this.blePlxManager;
|
|
1101
|
+
if (this.runPromise && this.runPromiseDeviceId === uuid) {
|
|
965
1102
|
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
|
|
966
1103
|
this.runPromise.reject(error);
|
|
967
1104
|
this.runPromise = null;
|
|
968
|
-
this.
|
|
1105
|
+
this.runPromiseDeviceId = null;
|
|
1106
|
+
this.rejectProtocolV2Frames(uuid, error);
|
|
969
1107
|
}
|
|
970
1108
|
else {
|
|
971
1109
|
this.resetProtocolV2Frames(uuid);
|
|
@@ -985,31 +1123,38 @@ class ReactNativeBleTransport {
|
|
|
985
1123
|
Log === null || Log === void 0 ? void 0 : Log.debug('release: removing notify subscription, characteristic: ', (_c = transport.notifyCharacteristic) === null || _c === void 0 ? void 0 : _c.uuid);
|
|
986
1124
|
(_d = transport.notifySubscription) === null || _d === void 0 ? void 0 : _d.remove();
|
|
987
1125
|
transport.notifySubscription = undefined;
|
|
988
|
-
if (transport
|
|
989
|
-
|
|
990
|
-
yield ((_e = this.blePlxManager) === null || _e === void 0 ? void 0 : _e.cancelTransaction(transport.notifyTransactionId));
|
|
991
|
-
}
|
|
992
|
-
catch (e) {
|
|
993
|
-
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);
|
|
994
|
-
}
|
|
1126
|
+
if (transportCache[uuid] === transport) {
|
|
1127
|
+
delete transportCache[uuid];
|
|
995
1128
|
}
|
|
996
|
-
delete transportCache[uuid];
|
|
997
1129
|
}
|
|
1130
|
+
this.protocolV2HighVolumeLogSignatures.delete(uuid);
|
|
998
1131
|
this.deviceProtocol.delete(uuid);
|
|
999
|
-
|
|
1132
|
+
this.probingProtocols.delete(uuid);
|
|
1133
|
+
this.staleBondErrors.delete(uuid);
|
|
1134
|
+
(_e = this.protocolV2Assemblers.get(uuid)) === null || _e === void 0 ? void 0 : _e.reset();
|
|
1000
1135
|
this.protocolV2Assemblers.delete(uuid);
|
|
1001
1136
|
this.resetProtocolV2Frames(uuid);
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1137
|
+
yield this.runNativeTeardown(uuid, manager, () => __awaiter(this, void 0, void 0, function* () {
|
|
1138
|
+
const operations = [
|
|
1139
|
+
this.runBestEffortNativeOperation('release: restore connection priority', () => this.restoreAndroidConnectionPriority(uuid, transport)),
|
|
1140
|
+
];
|
|
1141
|
+
if ((transport === null || transport === void 0 ? void 0 : transport.notifyTransactionId) && manager) {
|
|
1142
|
+
operations.push(this.runBestEffortNativeOperation('release: cancel notify transaction', () => manager.cancelTransaction(transport.notifyTransactionId)));
|
|
1143
|
+
}
|
|
1144
|
+
if (manager) {
|
|
1145
|
+
operations.push(this.runBestEffortNativeOperation('release: cancel transaction', () => manager.cancelTransaction(uuid)));
|
|
1146
|
+
}
|
|
1147
|
+
yield Promise.all(operations);
|
|
1148
|
+
}));
|
|
1008
1149
|
return Promise.resolve(true);
|
|
1009
1150
|
});
|
|
1010
1151
|
}
|
|
1011
1152
|
post(session, name, data) {
|
|
1012
1153
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1154
|
+
if (this.getProtocolType(session) === 'V2') {
|
|
1155
|
+
yield this.protocolV2Links.sendFlowControl(session, () => this.createProtocolV2Adapter(session), name, data);
|
|
1156
|
+
return;
|
|
1157
|
+
}
|
|
1013
1158
|
yield this.call(session, name, data);
|
|
1014
1159
|
});
|
|
1015
1160
|
}
|
|
@@ -1025,7 +1170,6 @@ class ReactNativeBleTransport {
|
|
|
1025
1170
|
if (!protocol) {
|
|
1026
1171
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${uuid}`);
|
|
1027
1172
|
}
|
|
1028
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('transport call', createTransportCallLog(name, protocol, data));
|
|
1029
1173
|
if (protocol === 'V2') {
|
|
1030
1174
|
return this.callProtocolV2(uuid, name, data, options);
|
|
1031
1175
|
}
|
|
@@ -1044,7 +1188,19 @@ class ReactNativeBleTransport {
|
|
|
1044
1188
|
const transport = this.getCachedTransport(uuid);
|
|
1045
1189
|
const runPromise = hdShared.createDeferred();
|
|
1046
1190
|
runPromise.promise.catch(() => undefined);
|
|
1191
|
+
const supersededRunPromise = this.runPromise;
|
|
1192
|
+
if (supersededRunPromise) {
|
|
1193
|
+
supersededRunPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise));
|
|
1194
|
+
}
|
|
1047
1195
|
this.runPromise = runPromise;
|
|
1196
|
+
this.runPromiseDeviceId = uuid;
|
|
1197
|
+
const releaseOwnershipIfCurrent = () => {
|
|
1198
|
+
if (this.runPromise === runPromise) {
|
|
1199
|
+
this.runPromise = null;
|
|
1200
|
+
this.runPromiseDeviceId = null;
|
|
1201
|
+
}
|
|
1202
|
+
};
|
|
1203
|
+
const isCurrentOwner = () => this.runPromise === runPromise;
|
|
1048
1204
|
const messages = this._messages;
|
|
1049
1205
|
const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
|
|
1050
1206
|
let timeout;
|
|
@@ -1065,6 +1221,12 @@ class ReactNativeBleTransport {
|
|
|
1065
1221
|
}
|
|
1066
1222
|
catch (e) {
|
|
1067
1223
|
onError(e);
|
|
1224
|
+
if (isWedgedWriteError(e)) {
|
|
1225
|
+
throw e;
|
|
1226
|
+
}
|
|
1227
|
+
if (isNativeBleStaleBondError(e) || hdShared.isBleStaleBondHardwareError(e)) {
|
|
1228
|
+
throw toBleStaleBondHardwareError(e);
|
|
1229
|
+
}
|
|
1068
1230
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
|
|
1069
1231
|
}
|
|
1070
1232
|
}
|
|
@@ -1092,6 +1254,12 @@ class ReactNativeBleTransport {
|
|
|
1092
1254
|
}
|
|
1093
1255
|
catch (e) {
|
|
1094
1256
|
onError(e);
|
|
1257
|
+
if (isWedgedWriteError(e)) {
|
|
1258
|
+
throw e;
|
|
1259
|
+
}
|
|
1260
|
+
if (isNativeBleStaleBondError(e) || hdShared.isBleStaleBondHardwareError(e)) {
|
|
1261
|
+
throw toBleStaleBondHardwareError(e);
|
|
1262
|
+
}
|
|
1095
1263
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
|
|
1096
1264
|
}
|
|
1097
1265
|
}
|
|
@@ -1102,8 +1270,8 @@ class ReactNativeBleTransport {
|
|
|
1102
1270
|
});
|
|
1103
1271
|
}
|
|
1104
1272
|
if (name === 'EmmcFileWrite') {
|
|
1105
|
-
yield writeChunkedData(buffers, data => transport.writeWithRetry(
|
|
1106
|
-
|
|
1273
|
+
yield writeChunkedData(buffers, data => this.writeBlePacket(uuid, data, payload => transport.writeWithRetry(payload), isCurrentOwner), e => {
|
|
1274
|
+
releaseOwnershipIfCurrent();
|
|
1107
1275
|
Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
|
|
1108
1276
|
});
|
|
1109
1277
|
}
|
|
@@ -1119,7 +1287,7 @@ class ReactNativeBleTransport {
|
|
|
1119
1287
|
let attempt = 0;
|
|
1120
1288
|
while (true) {
|
|
1121
1289
|
try {
|
|
1122
|
-
yield transport.
|
|
1290
|
+
yield this.writeBlePacket(uuid, data, payload => transport.writeWithRetry(payload), isCurrentOwner);
|
|
1123
1291
|
return;
|
|
1124
1292
|
}
|
|
1125
1293
|
catch (error) {
|
|
@@ -1127,36 +1295,18 @@ class ReactNativeBleTransport {
|
|
|
1127
1295
|
if (!retryType || attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
|
|
1128
1296
|
throw error;
|
|
1129
1297
|
}
|
|
1130
|
-
const
|
|
1131
|
-
const delayMs = shouldReconnect
|
|
1132
|
-
? FIRMWARE_UPLOAD_RECONNECT_RETRY_DELAY_MS
|
|
1133
|
-
: resolveFirmwareUploadRetryDelay(attempt);
|
|
1298
|
+
const delayMs = resolveFirmwareUploadRetryDelay(attempt);
|
|
1134
1299
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] FirmwareUpload write retry:', {
|
|
1135
1300
|
attempt: attempt + 1,
|
|
1136
1301
|
delayMs,
|
|
1137
|
-
reconnect: shouldReconnect,
|
|
1138
1302
|
error,
|
|
1139
1303
|
});
|
|
1140
|
-
if (shouldReconnect) {
|
|
1141
|
-
this.firmwareUploadWriteRecoveryIds.add(uuid);
|
|
1142
|
-
}
|
|
1143
1304
|
yield delay(delayMs);
|
|
1144
1305
|
attempt += 1;
|
|
1145
|
-
if (shouldReconnect) {
|
|
1146
|
-
try {
|
|
1147
|
-
yield this.reconnectFirmwareUploadTransport(uuid, transport);
|
|
1148
|
-
}
|
|
1149
|
-
catch (e) {
|
|
1150
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] FirmwareUpload reconnect error:', e);
|
|
1151
|
-
if (attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
|
|
1152
|
-
throw e;
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
1306
|
}
|
|
1157
1307
|
}
|
|
1158
1308
|
}), e => {
|
|
1159
|
-
|
|
1309
|
+
releaseOwnershipIfCurrent();
|
|
1160
1310
|
Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
|
|
1161
1311
|
});
|
|
1162
1312
|
}
|
|
@@ -1164,11 +1314,22 @@ class ReactNativeBleTransport {
|
|
|
1164
1314
|
for (const o of buffers) {
|
|
1165
1315
|
const outData = o.toString('base64');
|
|
1166
1316
|
try {
|
|
1167
|
-
|
|
1317
|
+
const shouldUseWriteWithResponse = reactNative.Platform.OS === 'ios' && transport.writeCharacteristic.isWritableWithResponse;
|
|
1318
|
+
yield this.writeBlePacket(uuid, outData, payload => shouldUseWriteWithResponse
|
|
1319
|
+
? transport.writeCharacteristic.writeWithResponse(payload)
|
|
1320
|
+
: transport.writeCharacteristic.writeWithoutResponse(payload), isCurrentOwner);
|
|
1168
1321
|
}
|
|
1169
1322
|
catch (e) {
|
|
1170
1323
|
Log === null || Log === void 0 ? void 0 : Log.debug('writeCharacteristic write error: ', e);
|
|
1171
|
-
|
|
1324
|
+
releaseOwnershipIfCurrent();
|
|
1325
|
+
if (isWedgedWriteError(e)) {
|
|
1326
|
+
throw e;
|
|
1327
|
+
}
|
|
1328
|
+
if (isNativeBleStaleBondError(e) || hdShared.isBleStaleBondHardwareError(e)) {
|
|
1329
|
+
const bondError = toBleStaleBondHardwareError(e);
|
|
1330
|
+
this.rememberStaleBondError(uuid, bondError);
|
|
1331
|
+
throw bondError;
|
|
1332
|
+
}
|
|
1172
1333
|
if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected) {
|
|
1173
1334
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded);
|
|
1174
1335
|
}
|
|
@@ -1201,12 +1362,19 @@ class ReactNativeBleTransport {
|
|
|
1201
1362
|
return check.call(jsonData);
|
|
1202
1363
|
}
|
|
1203
1364
|
catch (e) {
|
|
1204
|
-
if (name === '
|
|
1205
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1
|
|
1365
|
+
if (name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS) {
|
|
1366
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe call failed:', e);
|
|
1206
1367
|
}
|
|
1207
1368
|
else {
|
|
1208
1369
|
Log === null || Log === void 0 ? void 0 : Log.error('call error: ', e);
|
|
1209
1370
|
}
|
|
1371
|
+
const isProbeTimeout = name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS;
|
|
1372
|
+
const isStaleCall = this.runPromise !== runPromise;
|
|
1373
|
+
if (!isProbeTimeout &&
|
|
1374
|
+
!isStaleCall &&
|
|
1375
|
+
(e === null || e === void 0 ? void 0 : e.errorCode) === hdShared.HardwareErrorCode.BleTimeoutError) {
|
|
1376
|
+
yield this.disconnect(uuid);
|
|
1377
|
+
}
|
|
1210
1378
|
throw e;
|
|
1211
1379
|
}
|
|
1212
1380
|
finally {
|
|
@@ -1214,6 +1382,7 @@ class ReactNativeBleTransport {
|
|
|
1214
1382
|
clearTimeout(timeout);
|
|
1215
1383
|
if (this.runPromise === runPromise) {
|
|
1216
1384
|
this.runPromise = null;
|
|
1385
|
+
this.runPromiseDeviceId = null;
|
|
1217
1386
|
}
|
|
1218
1387
|
}
|
|
1219
1388
|
});
|
|
@@ -1222,10 +1391,17 @@ class ReactNativeBleTransport {
|
|
|
1222
1391
|
this.stopped = true;
|
|
1223
1392
|
}
|
|
1224
1393
|
disconnect(session) {
|
|
1225
|
-
|
|
1394
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1395
|
+
return this.runLifecycleOperation(session, () => this.disconnectUnlocked(session));
|
|
1396
|
+
});
|
|
1397
|
+
}
|
|
1398
|
+
disconnectUnlocked(session) {
|
|
1399
|
+
var _a, _b, _c;
|
|
1226
1400
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1227
1401
|
yield this.protocolV2Links.invalidateLink(session, 'React Native BLE transport disconnected');
|
|
1228
1402
|
const transport = transportCache[session];
|
|
1403
|
+
const manager = this.blePlxManager;
|
|
1404
|
+
const monitorToken = (_a = transport === null || transport === void 0 ? void 0 : transport.monitorToken) !== null && _a !== void 0 ? _a : this.monitorTokens.get(session);
|
|
1229
1405
|
if (transport === null || transport === void 0 ? void 0 : transport.disconnectSubscription) {
|
|
1230
1406
|
try {
|
|
1231
1407
|
Log === null || Log === void 0 ? void 0 : Log.debug('disconnect: removing disconnect subscription');
|
|
@@ -1238,7 +1414,7 @@ class ReactNativeBleTransport {
|
|
|
1238
1414
|
}
|
|
1239
1415
|
if (transport === null || transport === void 0 ? void 0 : transport.notifySubscription) {
|
|
1240
1416
|
try {
|
|
1241
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('disconnect: removing notify subscription, characteristic: ', (
|
|
1417
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('disconnect: removing notify subscription, characteristic: ', (_b = transport.notifyCharacteristic) === null || _b === void 0 ? void 0 : _b.uuid);
|
|
1242
1418
|
transport.notifySubscription.remove();
|
|
1243
1419
|
transport.notifySubscription = undefined;
|
|
1244
1420
|
}
|
|
@@ -1246,52 +1422,203 @@ class ReactNativeBleTransport {
|
|
|
1246
1422
|
Log === null || Log === void 0 ? void 0 : Log.error('disconnect: remove notify subscription error: ', e);
|
|
1247
1423
|
}
|
|
1248
1424
|
}
|
|
1249
|
-
if (session) {
|
|
1250
|
-
try {
|
|
1251
|
-
yield ((_b = this.blePlxManager) === null || _b === void 0 ? void 0 : _b.cancelTransaction(session));
|
|
1252
|
-
}
|
|
1253
|
-
catch (e) {
|
|
1254
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('resetSession: cancel transaction error (ignored): ', (e === null || e === void 0 ? void 0 : e.message) || e);
|
|
1255
|
-
}
|
|
1256
|
-
}
|
|
1257
|
-
if (transport === null || transport === void 0 ? void 0 : transport.device) {
|
|
1258
|
-
try {
|
|
1259
|
-
yield transport.device.cancelConnection();
|
|
1260
|
-
}
|
|
1261
|
-
catch (e) {
|
|
1262
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('resetSession: device.cancelConnection error (ignored): ', (e === null || e === void 0 ? void 0 : e.message) || e);
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
|
-
try {
|
|
1266
|
-
yield ((_c = this.blePlxManager) === null || _c === void 0 ? void 0 : _c.cancelDeviceConnection(session));
|
|
1267
|
-
}
|
|
1268
|
-
catch (e) {
|
|
1269
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('resetSession: manager.cancelDeviceConnection error (ignored): ', (e === null || e === void 0 ? void 0 : e.message) || e);
|
|
1270
|
-
}
|
|
1271
|
-
if (transportCache[session]) {
|
|
1425
|
+
if (!transport || transportCache[session] === transport) {
|
|
1272
1426
|
delete transportCache[session];
|
|
1273
1427
|
}
|
|
1274
1428
|
this.deviceProtocol.delete(session);
|
|
1429
|
+
this.probingProtocols.delete(session);
|
|
1430
|
+
this.staleBondErrors.delete(session);
|
|
1275
1431
|
this.deviceProtocolHints.delete(session);
|
|
1432
|
+
this.sessionProtocols.delete(session);
|
|
1433
|
+
this.protocolReprobeFailures.delete(session);
|
|
1276
1434
|
this.protocolV2Assemblers.delete(session);
|
|
1277
1435
|
this.resetProtocolV2Frames(session);
|
|
1278
1436
|
try {
|
|
1279
|
-
(
|
|
1280
|
-
name: (_e = transport === null || transport === void 0 ? void 0 : transport.device) === null || _e === void 0 ? void 0 : _e.name,
|
|
1281
|
-
id: session,
|
|
1282
|
-
connectId: session,
|
|
1283
|
-
});
|
|
1437
|
+
this.emitDeviceDisconnect(session, (_c = transport === null || transport === void 0 ? void 0 : transport.device) === null || _c === void 0 ? void 0 : _c.name, monitorToken);
|
|
1284
1438
|
}
|
|
1285
1439
|
catch (e) {
|
|
1286
1440
|
Log === null || Log === void 0 ? void 0 : Log.error('resetSession: emit disconnect event error: ', e);
|
|
1287
1441
|
}
|
|
1442
|
+
if (monitorToken !== undefined && this.monitorTokens.get(session) === monitorToken) {
|
|
1443
|
+
this.monitorTokens.delete(session);
|
|
1444
|
+
}
|
|
1445
|
+
yield this.runNativeTeardown(session, manager, () => __awaiter(this, void 0, void 0, function* () {
|
|
1446
|
+
const operations = [];
|
|
1447
|
+
if (manager) {
|
|
1448
|
+
operations.push(this.runBestEffortNativeOperation('disconnect: cancel transaction', () => manager.cancelTransaction(session)));
|
|
1449
|
+
operations.push(this.runBestEffortNativeOperation('disconnect: cancel device connection', () => manager.cancelDeviceConnection(session)));
|
|
1450
|
+
}
|
|
1451
|
+
if (transport === null || transport === void 0 ? void 0 : transport.device) {
|
|
1452
|
+
operations.push(this.runBestEffortNativeOperation('disconnect: device cancel connection', () => transport.device.cancelConnection()));
|
|
1453
|
+
}
|
|
1454
|
+
yield Promise.all(operations);
|
|
1455
|
+
}));
|
|
1288
1456
|
yield new Promise(resolve => setTimeout(() => resolve(), 100));
|
|
1289
1457
|
});
|
|
1290
1458
|
}
|
|
1459
|
+
runNativeTeardown(uuid, manager, teardown) {
|
|
1460
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1461
|
+
let timer;
|
|
1462
|
+
let timedOut = false;
|
|
1463
|
+
const pending = Promise.resolve()
|
|
1464
|
+
.then(teardown)
|
|
1465
|
+
.catch(error => {
|
|
1466
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('BLE native teardown error (ignored): ', (error === null || error === void 0 ? void 0 : error.message) || error);
|
|
1467
|
+
});
|
|
1468
|
+
try {
|
|
1469
|
+
yield Promise.race([
|
|
1470
|
+
pending,
|
|
1471
|
+
new Promise(resolve => {
|
|
1472
|
+
timer = setTimeout(() => {
|
|
1473
|
+
timedOut = true;
|
|
1474
|
+
resolve();
|
|
1475
|
+
}, BLE_NATIVE_TEARDOWN_TIMEOUT_MS);
|
|
1476
|
+
}),
|
|
1477
|
+
]);
|
|
1478
|
+
}
|
|
1479
|
+
finally {
|
|
1480
|
+
if (timer)
|
|
1481
|
+
clearTimeout(timer);
|
|
1482
|
+
}
|
|
1483
|
+
if (timedOut) {
|
|
1484
|
+
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE native teardown timed out:', uuid);
|
|
1485
|
+
if (this.blePlxManager === manager) {
|
|
1486
|
+
this.resetPlxManager();
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
1491
|
+
runBestEffortNativeOperation(label, operation) {
|
|
1492
|
+
return Promise.resolve()
|
|
1493
|
+
.then(operation)
|
|
1494
|
+
.catch(error => {
|
|
1495
|
+
Log === null || Log === void 0 ? void 0 : Log.debug(`${label} error (ignored): `, (error === null || error === void 0 ? void 0 : error.message) || error);
|
|
1496
|
+
});
|
|
1497
|
+
}
|
|
1498
|
+
runLifecycleOperation(uuid, operation) {
|
|
1499
|
+
var _a;
|
|
1500
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1501
|
+
const previousOperation = (_a = this.lifecycleOperations.get(uuid)) !== null && _a !== void 0 ? _a : Promise.resolve();
|
|
1502
|
+
let completeOperation;
|
|
1503
|
+
const operationGate = new Promise(resolve => {
|
|
1504
|
+
completeOperation = resolve;
|
|
1505
|
+
});
|
|
1506
|
+
const operationTail = previousOperation.catch(() => undefined).then(() => operationGate);
|
|
1507
|
+
this.lifecycleOperations.set(uuid, operationTail);
|
|
1508
|
+
yield previousOperation.catch(() => undefined);
|
|
1509
|
+
try {
|
|
1510
|
+
return yield operation();
|
|
1511
|
+
}
|
|
1512
|
+
finally {
|
|
1513
|
+
completeOperation();
|
|
1514
|
+
if (this.lifecycleOperations.get(uuid) === operationTail) {
|
|
1515
|
+
this.lifecycleOperations.delete(uuid);
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
});
|
|
1519
|
+
}
|
|
1291
1520
|
cancel() {
|
|
1292
1521
|
Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native transport cancel');
|
|
1293
1522
|
if (this.runPromise) ;
|
|
1294
1523
|
this.runPromise = null;
|
|
1524
|
+
this.runPromiseDeviceId = null;
|
|
1525
|
+
}
|
|
1526
|
+
connectWithTimeout(uuid, connect) {
|
|
1527
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1528
|
+
let timer;
|
|
1529
|
+
let timedOut = false;
|
|
1530
|
+
const pending = connect();
|
|
1531
|
+
pending.catch(() => undefined);
|
|
1532
|
+
try {
|
|
1533
|
+
const result = yield Promise.race([
|
|
1534
|
+
pending,
|
|
1535
|
+
new Promise((_, reject) => {
|
|
1536
|
+
timer = setTimeout(() => {
|
|
1537
|
+
timedOut = true;
|
|
1538
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `BLE connect timeout after ${BLE_CONNECT_TIMEOUT_MS}ms for ${uuid}`));
|
|
1539
|
+
}, BLE_CONNECT_TIMEOUT_MS);
|
|
1540
|
+
}),
|
|
1541
|
+
]);
|
|
1542
|
+
return result;
|
|
1543
|
+
}
|
|
1544
|
+
catch (error) {
|
|
1545
|
+
if (timedOut || isNativeOperationTimeoutError(error)) {
|
|
1546
|
+
const resetManager = this.abandonStalledConnection(uuid, timedOut ? 'connect-backstop' : 'connect-native');
|
|
1547
|
+
if (resetManager) {
|
|
1548
|
+
throw this.createWedgedBleSetupError();
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
throw error;
|
|
1552
|
+
}
|
|
1553
|
+
finally {
|
|
1554
|
+
if (timer)
|
|
1555
|
+
clearTimeout(timer);
|
|
1556
|
+
}
|
|
1557
|
+
});
|
|
1558
|
+
}
|
|
1559
|
+
resolveCharacteristicsWithTimeout(uuid, device) {
|
|
1560
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1561
|
+
let timer;
|
|
1562
|
+
let timedOut = false;
|
|
1563
|
+
const pending = this.resolveCharacteristics(device);
|
|
1564
|
+
pending.catch(() => undefined);
|
|
1565
|
+
try {
|
|
1566
|
+
const result = yield Promise.race([
|
|
1567
|
+
pending,
|
|
1568
|
+
new Promise((_, reject) => {
|
|
1569
|
+
timer = setTimeout(() => {
|
|
1570
|
+
timedOut = true;
|
|
1571
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `BLE GATT setup timeout after ${BLE_GATT_SETUP_TIMEOUT_MS}ms for ${uuid}`));
|
|
1572
|
+
}, BLE_GATT_SETUP_TIMEOUT_MS);
|
|
1573
|
+
}),
|
|
1574
|
+
]);
|
|
1575
|
+
this.connectionSetupTimeoutCounts.delete(uuid);
|
|
1576
|
+
return result;
|
|
1577
|
+
}
|
|
1578
|
+
catch (error) {
|
|
1579
|
+
if (timedOut || isNativeOperationTimeoutError(error)) {
|
|
1580
|
+
const resetManager = this.abandonStalledConnection(uuid, timedOut ? 'gatt-backstop' : 'gatt-native');
|
|
1581
|
+
if (resetManager) {
|
|
1582
|
+
throw this.createWedgedBleSetupError();
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
throw error;
|
|
1586
|
+
}
|
|
1587
|
+
finally {
|
|
1588
|
+
if (timer)
|
|
1589
|
+
clearTimeout(timer);
|
|
1590
|
+
}
|
|
1591
|
+
});
|
|
1592
|
+
}
|
|
1593
|
+
abandonStalledConnection(uuid, stage) {
|
|
1594
|
+
var _a, _b;
|
|
1595
|
+
const timeouts = ((_a = this.connectionSetupTimeoutCounts.get(uuid)) !== null && _a !== void 0 ? _a : 0) + 1;
|
|
1596
|
+
this.connectionSetupTimeoutCounts.set(uuid, timeouts);
|
|
1597
|
+
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE setup timed out:', uuid, {
|
|
1598
|
+
stage,
|
|
1599
|
+
setupTimeoutsSinceSuccess: timeouts,
|
|
1600
|
+
});
|
|
1601
|
+
(_b = this.blePlxManager) === null || _b === void 0 ? void 0 : _b.cancelDeviceConnection(uuid).catch(() => {
|
|
1602
|
+
});
|
|
1603
|
+
const stalled = transportCache[uuid];
|
|
1604
|
+
if (stalled) {
|
|
1605
|
+
delete transportCache[uuid];
|
|
1606
|
+
}
|
|
1607
|
+
this.deviceProtocol.delete(uuid);
|
|
1608
|
+
this.probingProtocols.delete(uuid);
|
|
1609
|
+
this.staleBondErrors.delete(uuid);
|
|
1610
|
+
this.protocolV2Assemblers.delete(uuid);
|
|
1611
|
+
this.resetProtocolV2Frames(uuid);
|
|
1612
|
+
if (timeouts >= BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD) {
|
|
1613
|
+
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE setup wedged repeatedly, resetting BLE manager');
|
|
1614
|
+
this.resetPlxManager();
|
|
1615
|
+
this.connectionSetupTimeoutCounts.delete(uuid);
|
|
1616
|
+
return true;
|
|
1617
|
+
}
|
|
1618
|
+
return false;
|
|
1619
|
+
}
|
|
1620
|
+
createWedgedBleSetupError() {
|
|
1621
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.PollingTimeout, BLE_SETUP_WEDGED_MESSAGE);
|
|
1295
1622
|
}
|
|
1296
1623
|
getCachedTransport(uuid) {
|
|
1297
1624
|
const transport = transportCache[uuid];
|
|
@@ -1300,22 +1627,149 @@ class ReactNativeBleTransport {
|
|
|
1300
1627
|
}
|
|
1301
1628
|
return transport;
|
|
1302
1629
|
}
|
|
1303
|
-
|
|
1304
|
-
return
|
|
1630
|
+
writeBlePacket(uuid, data, write, isCurrentOwner) {
|
|
1631
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1632
|
+
let timer;
|
|
1633
|
+
let timedOut = false;
|
|
1634
|
+
try {
|
|
1635
|
+
yield Promise.race([
|
|
1636
|
+
write(data),
|
|
1637
|
+
new Promise((_, reject) => {
|
|
1638
|
+
timer = setTimeout(() => {
|
|
1639
|
+
timedOut = true;
|
|
1640
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError, `BLE write timeout after ${BLE_WRITE_PACKET_TIMEOUT_MS}ms`));
|
|
1641
|
+
}, BLE_WRITE_PACKET_TIMEOUT_MS);
|
|
1642
|
+
}),
|
|
1643
|
+
]);
|
|
1644
|
+
this.writeTimeoutCounts.delete(uuid);
|
|
1645
|
+
}
|
|
1646
|
+
catch (error) {
|
|
1647
|
+
if (timedOut) {
|
|
1648
|
+
if (isCurrentOwner && !isCurrentOwner()) {
|
|
1649
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] stale BLE write timed out, link kept:', uuid);
|
|
1650
|
+
}
|
|
1651
|
+
else {
|
|
1652
|
+
this.tearDownWedgedLink(uuid);
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
throw error;
|
|
1656
|
+
}
|
|
1657
|
+
finally {
|
|
1658
|
+
if (timer)
|
|
1659
|
+
clearTimeout(timer);
|
|
1660
|
+
}
|
|
1661
|
+
});
|
|
1662
|
+
}
|
|
1663
|
+
tearDownWedgedLink(uuid) {
|
|
1664
|
+
var _a;
|
|
1665
|
+
const timeouts = ((_a = this.writeTimeoutCounts.get(uuid)) !== null && _a !== void 0 ? _a : 0) + 1;
|
|
1666
|
+
this.writeTimeoutCounts.set(uuid, timeouts);
|
|
1667
|
+
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE write timed out, tearing down link:', uuid, {
|
|
1668
|
+
consecutiveWriteTimeouts: timeouts,
|
|
1669
|
+
});
|
|
1670
|
+
const wedged = transportCache[uuid];
|
|
1671
|
+
this.disconnect(uuid).catch(error => {
|
|
1672
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] wedged link teardown failed (ignored):', error);
|
|
1673
|
+
});
|
|
1674
|
+
if (wedged && transportCache[uuid] === wedged) {
|
|
1675
|
+
delete transportCache[uuid];
|
|
1676
|
+
}
|
|
1677
|
+
this.deviceProtocol.delete(uuid);
|
|
1678
|
+
this.probingProtocols.delete(uuid);
|
|
1679
|
+
this.staleBondErrors.delete(uuid);
|
|
1680
|
+
this.protocolV2Assemblers.delete(uuid);
|
|
1681
|
+
this.resetProtocolV2Frames(uuid);
|
|
1682
|
+
if (timeouts >= BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD) {
|
|
1683
|
+
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE writes wedged repeatedly, resetting BLE manager');
|
|
1684
|
+
this.resetPlxManager();
|
|
1685
|
+
this.writeTimeoutCounts.delete(uuid);
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
resetPlxManager() {
|
|
1689
|
+
const manager = this.blePlxManager;
|
|
1690
|
+
this.blePlxManager = undefined;
|
|
1691
|
+
const reason = 'React Native BLE manager reset';
|
|
1692
|
+
Object.entries(transportCache).forEach(([uuid, cachedTransport]) => {
|
|
1693
|
+
var _a, _b, _c, _d;
|
|
1694
|
+
try {
|
|
1695
|
+
(_a = cachedTransport.disconnectSubscription) === null || _a === void 0 ? void 0 : _a.remove();
|
|
1696
|
+
}
|
|
1697
|
+
catch (error) {
|
|
1698
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('BLE manager reset disconnect subscription removal failed:', error);
|
|
1699
|
+
}
|
|
1700
|
+
cachedTransport.disconnectSubscription = undefined;
|
|
1701
|
+
try {
|
|
1702
|
+
(_b = cachedTransport.notifySubscription) === null || _b === void 0 ? void 0 : _b.remove();
|
|
1703
|
+
}
|
|
1704
|
+
catch (error) {
|
|
1705
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('BLE manager reset notify subscription removal failed:', error);
|
|
1706
|
+
}
|
|
1707
|
+
cachedTransport.notifySubscription = undefined;
|
|
1708
|
+
this.rejectProtocolV2Frames(uuid, new Error(reason));
|
|
1709
|
+
try {
|
|
1710
|
+
this.emitDeviceDisconnect(uuid, (_c = cachedTransport.device) === null || _c === void 0 ? void 0 : _c.name, (_d = cachedTransport.monitorToken) !== null && _d !== void 0 ? _d : this.monitorTokens.get(uuid));
|
|
1711
|
+
}
|
|
1712
|
+
catch (error) {
|
|
1713
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('BLE manager reset disconnect event failed:', error);
|
|
1714
|
+
}
|
|
1715
|
+
delete transportCache[uuid];
|
|
1716
|
+
});
|
|
1717
|
+
this.protocolV2Links.invalidateAllLinks(reason).catch(error => {
|
|
1718
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE manager link invalidation failed:', error);
|
|
1719
|
+
});
|
|
1720
|
+
this.deviceProtocol.clear();
|
|
1721
|
+
this.probingProtocols.clear();
|
|
1722
|
+
this.staleBondErrors.clear();
|
|
1723
|
+
this.sessionProtocols.clear();
|
|
1724
|
+
this.confirmedProtocolV2.clear();
|
|
1725
|
+
this.protocolReprobeFailures.clear();
|
|
1726
|
+
this.writeTimeoutCounts.clear();
|
|
1727
|
+
this.connectionSetupTimeoutCounts.clear();
|
|
1728
|
+
this.monitorTokens.clear();
|
|
1729
|
+
this.protocolV2Assemblers.clear();
|
|
1730
|
+
try {
|
|
1731
|
+
manager === null || manager === void 0 ? void 0 : manager.destroy();
|
|
1732
|
+
}
|
|
1733
|
+
catch (error) {
|
|
1734
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE manager destroy failed (ignored):', error);
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
createProtocolMismatchError(expected, uuid) {
|
|
1738
|
+
const isStaleV2Bond = expected === 'V2' && this.confirmedProtocolV2.has(uuid);
|
|
1739
|
+
return hdShared.ERRORS.TypedError(isStaleV2Bond ? hdShared.HardwareErrorCode.BleDeviceBondError : hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
|
|
1305
1740
|
}
|
|
1306
1741
|
createProtocolDetectionError() {
|
|
1307
|
-
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1
|
|
1742
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1 GetFeatures or Protocol V2 Ping');
|
|
1308
1743
|
}
|
|
1309
1744
|
clearProbeProtocol(uuid, protocol) {
|
|
1745
|
+
if (this.probingProtocols.get(uuid) === protocol) {
|
|
1746
|
+
this.probingProtocols.delete(uuid);
|
|
1747
|
+
}
|
|
1310
1748
|
if (this.deviceProtocol.get(uuid) === protocol) {
|
|
1311
1749
|
this.deviceProtocol.delete(uuid);
|
|
1312
1750
|
}
|
|
1313
1751
|
}
|
|
1314
|
-
|
|
1752
|
+
getActiveProtocol(uuid) {
|
|
1753
|
+
var _a;
|
|
1754
|
+
return (_a = this.deviceProtocol.get(uuid)) !== null && _a !== void 0 ? _a : this.probingProtocols.get(uuid);
|
|
1755
|
+
}
|
|
1756
|
+
detectProtocol(uuid, expectedProtocol, protocolHint, rebuildTransport) {
|
|
1757
|
+
var _a;
|
|
1315
1758
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1759
|
+
if (reactNative.Platform.OS === 'ios' && expectedProtocol === 'V1') {
|
|
1760
|
+
this.deviceProtocol.set(uuid, expectedProtocol);
|
|
1761
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol selected', {
|
|
1762
|
+
deviceId: uuid,
|
|
1763
|
+
protocol: expectedProtocol,
|
|
1764
|
+
source: 'expected',
|
|
1765
|
+
});
|
|
1766
|
+
return expectedProtocol;
|
|
1767
|
+
}
|
|
1768
|
+
this.throwIfStaleBondError(uuid);
|
|
1316
1769
|
if (expectedProtocol === 'V1') {
|
|
1317
1770
|
if (yield this.probeProtocolV1(uuid)) {
|
|
1318
1771
|
this.deviceProtocol.set(uuid, 'V1');
|
|
1772
|
+
this.sessionProtocols.set(uuid, 'V1');
|
|
1319
1773
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
|
|
1320
1774
|
deviceId: uuid,
|
|
1321
1775
|
protocol: 'V1',
|
|
@@ -1323,26 +1777,48 @@ class ReactNativeBleTransport {
|
|
|
1323
1777
|
});
|
|
1324
1778
|
return 'V1';
|
|
1325
1779
|
}
|
|
1326
|
-
throw this.createProtocolMismatchError(expectedProtocol);
|
|
1780
|
+
throw this.createProtocolMismatchError(expectedProtocol, uuid);
|
|
1327
1781
|
}
|
|
1328
1782
|
if (expectedProtocol === 'V2') {
|
|
1329
|
-
this.
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1783
|
+
if (yield this.probeProtocolV2(uuid)) {
|
|
1784
|
+
this.deviceProtocol.set(uuid, 'V2');
|
|
1785
|
+
this.sessionProtocols.set(uuid, 'V2');
|
|
1786
|
+
this.confirmedProtocolV2.add(uuid);
|
|
1787
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
|
|
1788
|
+
deviceId: uuid,
|
|
1789
|
+
protocol: 'V2',
|
|
1790
|
+
source: 'expected',
|
|
1791
|
+
});
|
|
1792
|
+
return 'V2';
|
|
1793
|
+
}
|
|
1794
|
+
throw this.createProtocolMismatchError(expectedProtocol, uuid);
|
|
1795
|
+
}
|
|
1796
|
+
const sessionProtocol = this.sessionProtocols.get(uuid);
|
|
1797
|
+
const reprobeFailures = (_a = this.protocolReprobeFailures.get(uuid)) !== null && _a !== void 0 ? _a : 0;
|
|
1798
|
+
const fullProbeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
|
|
1799
|
+
const trustSessionProtocol = sessionProtocol !== undefined &&
|
|
1800
|
+
!protocolHint &&
|
|
1801
|
+
reprobeFailures < PROTOCOL_REPROBE_FALLBACK_ATTEMPTS;
|
|
1802
|
+
const probeOrder = trustSessionProtocol ? [sessionProtocol] : fullProbeOrder;
|
|
1338
1803
|
for (let i = 0; i < probeOrder.length; i += 1) {
|
|
1339
1804
|
const protocol = probeOrder[i];
|
|
1340
1805
|
if (i > 0) {
|
|
1341
1806
|
yield this.resetProbeStateAfterProtocolProbe(uuid, probeOrder[i - 1]);
|
|
1807
|
+
if (!transportCache[uuid]) {
|
|
1808
|
+
if (!rebuildTransport) {
|
|
1809
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
|
|
1810
|
+
}
|
|
1811
|
+
yield rebuildTransport();
|
|
1812
|
+
}
|
|
1342
1813
|
}
|
|
1343
1814
|
const detected = protocol === 'V1' ? yield this.probeProtocolV1(uuid) : yield this.probeProtocolV2(uuid);
|
|
1344
1815
|
if (detected) {
|
|
1345
1816
|
this.deviceProtocol.set(uuid, protocol);
|
|
1817
|
+
this.sessionProtocols.set(uuid, protocol);
|
|
1818
|
+
if (protocol === 'V2') {
|
|
1819
|
+
this.confirmedProtocolV2.add(uuid);
|
|
1820
|
+
}
|
|
1821
|
+
this.protocolReprobeFailures.delete(uuid);
|
|
1346
1822
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
|
|
1347
1823
|
deviceId: uuid,
|
|
1348
1824
|
protocol,
|
|
@@ -1351,7 +1827,14 @@ class ReactNativeBleTransport {
|
|
|
1351
1827
|
return protocol;
|
|
1352
1828
|
}
|
|
1353
1829
|
}
|
|
1830
|
+
if (trustSessionProtocol) {
|
|
1831
|
+
this.protocolReprobeFailures.set(uuid, reprobeFailures + 1);
|
|
1832
|
+
}
|
|
1833
|
+
else {
|
|
1834
|
+
this.protocolReprobeFailures.delete(uuid);
|
|
1835
|
+
}
|
|
1354
1836
|
this.deviceProtocol.delete(uuid);
|
|
1837
|
+
this.probingProtocols.delete(uuid);
|
|
1355
1838
|
throw this.createProtocolDetectionError();
|
|
1356
1839
|
});
|
|
1357
1840
|
}
|
|
@@ -1403,13 +1886,17 @@ class ReactNativeBleTransport {
|
|
|
1403
1886
|
return false;
|
|
1404
1887
|
}
|
|
1405
1888
|
try {
|
|
1406
|
-
this.
|
|
1407
|
-
yield this.callProtocolV1(uuid, '
|
|
1889
|
+
this.probingProtocols.set(uuid, 'V1');
|
|
1890
|
+
yield this.callProtocolV1(uuid, 'GetFeatures', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
|
|
1891
|
+
this.probingProtocols.delete(uuid);
|
|
1408
1892
|
return true;
|
|
1409
1893
|
}
|
|
1410
1894
|
catch (error) {
|
|
1411
1895
|
this.clearProbeProtocol(uuid, 'V1');
|
|
1412
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1
|
|
1896
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe failed:', error);
|
|
1897
|
+
if (isWedgedWriteError(error) || hdShared.isBleStaleBondHardwareError(error)) {
|
|
1898
|
+
throw error;
|
|
1899
|
+
}
|
|
1413
1900
|
return false;
|
|
1414
1901
|
}
|
|
1415
1902
|
});
|
|
@@ -1420,8 +1907,9 @@ class ReactNativeBleTransport {
|
|
|
1420
1907
|
if (!this._messages || !this._messagesV2) {
|
|
1421
1908
|
return false;
|
|
1422
1909
|
}
|
|
1423
|
-
this.
|
|
1910
|
+
this.probingProtocols.set(uuid, 'V2');
|
|
1424
1911
|
(_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1912
|
+
this.throwIfStaleBondError(uuid);
|
|
1425
1913
|
const detected = yield transport.probeProtocolV2({
|
|
1426
1914
|
call: (name, data, options) => this.callProtocolV2(uuid, name, data, options),
|
|
1427
1915
|
timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT_MS,
|
|
@@ -1432,10 +1920,14 @@ class ReactNativeBleTransport {
|
|
|
1432
1920
|
(_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1433
1921
|
this.resetProtocolV2Frames(uuid);
|
|
1434
1922
|
},
|
|
1923
|
+
shouldRethrow: hdShared.isBleStaleBondHardwareError,
|
|
1435
1924
|
});
|
|
1436
1925
|
if (!detected) {
|
|
1437
1926
|
this.clearProbeProtocol(uuid, 'V2');
|
|
1438
1927
|
}
|
|
1928
|
+
else {
|
|
1929
|
+
this.probingProtocols.delete(uuid);
|
|
1930
|
+
}
|
|
1439
1931
|
return detected;
|
|
1440
1932
|
});
|
|
1441
1933
|
}
|
|
@@ -1478,16 +1970,8 @@ class ReactNativeBleTransport {
|
|
|
1478
1970
|
}
|
|
1479
1971
|
this.getProtocolV2FrameQueue(uuid).push(frame);
|
|
1480
1972
|
}
|
|
1481
|
-
rejectAllProtocolV2Frames(error) {
|
|
1482
|
-
this.protocolV2FrameQueues.clear();
|
|
1483
|
-
for (const framePromise of this.protocolV2FramePromises.values()) {
|
|
1484
|
-
framePromise.reject(error);
|
|
1485
|
-
}
|
|
1486
|
-
this.protocolV2FramePromises.clear();
|
|
1487
|
-
}
|
|
1488
1973
|
resetProtocolV2Frames(uuid) {
|
|
1489
|
-
this.
|
|
1490
|
-
this.protocolV2FramePromises.delete(uuid);
|
|
1974
|
+
this.rejectProtocolV2Frames(uuid, new Error(`Protocol V2 frame state reset for ${uuid}`));
|
|
1491
1975
|
}
|
|
1492
1976
|
rejectProtocolV2Frames(uuid, error) {
|
|
1493
1977
|
this.protocolV2FrameQueues.delete(uuid);
|
|
@@ -1497,6 +1981,19 @@ class ReactNativeBleTransport {
|
|
|
1497
1981
|
framePromise.reject(error);
|
|
1498
1982
|
}
|
|
1499
1983
|
}
|
|
1984
|
+
rememberStaleBondError(uuid, error) {
|
|
1985
|
+
this.staleBondErrors.set(uuid, error);
|
|
1986
|
+
this.rejectProtocolV2Frames(uuid, error);
|
|
1987
|
+
if (this.runPromise && this.runPromiseDeviceId === uuid) {
|
|
1988
|
+
this.runPromise.reject(error);
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
throwIfStaleBondError(uuid) {
|
|
1992
|
+
const error = this.staleBondErrors.get(uuid);
|
|
1993
|
+
if (error) {
|
|
1994
|
+
throw error;
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1500
1997
|
readProtocolV2Frame(uuid) {
|
|
1501
1998
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1502
1999
|
const queuedFrame = this.getProtocolV2FrameQueue(uuid).shift();
|
|
@@ -1515,20 +2012,75 @@ class ReactNativeBleTransport {
|
|
|
1515
2012
|
}
|
|
1516
2013
|
});
|
|
1517
2014
|
}
|
|
1518
|
-
|
|
2015
|
+
writeProtocolV2Packet(uuid, transport, base64, context, assertCurrentGeneration) {
|
|
2016
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2017
|
+
const shouldUseWriteWithResponse = shouldWriteProtocolV2WithResponse({
|
|
2018
|
+
platform: reactNative.Platform.OS,
|
|
2019
|
+
highThroughput: context.highThroughput,
|
|
2020
|
+
requestedWithResponse: context.writeWithResponse,
|
|
2021
|
+
characteristic: transport.writeCharacteristic,
|
|
2022
|
+
});
|
|
2023
|
+
let attempt = 0;
|
|
2024
|
+
for (;;) {
|
|
2025
|
+
assertCurrentGeneration();
|
|
2026
|
+
if (context.signal.aborted) {
|
|
2027
|
+
throw new Error(`Protocol V2 BLE write aborted for ${context.messageName}`);
|
|
2028
|
+
}
|
|
2029
|
+
try {
|
|
2030
|
+
yield this.writeBlePacket(uuid, base64, payload => shouldUseWriteWithResponse
|
|
2031
|
+
? transport.writeCharacteristic.writeWithResponse(payload)
|
|
2032
|
+
: transport.writeCharacteristic.writeWithoutResponse(payload), () => {
|
|
2033
|
+
try {
|
|
2034
|
+
assertCurrentGeneration();
|
|
2035
|
+
return !context.signal.aborted;
|
|
2036
|
+
}
|
|
2037
|
+
catch (_a) {
|
|
2038
|
+
return false;
|
|
2039
|
+
}
|
|
2040
|
+
});
|
|
2041
|
+
assertCurrentGeneration();
|
|
2042
|
+
return;
|
|
2043
|
+
}
|
|
2044
|
+
catch (error) {
|
|
2045
|
+
if (isNativeBleStaleBondError(error) || hdShared.isBleStaleBondHardwareError(error)) {
|
|
2046
|
+
const bondError = toBleStaleBondHardwareError(error);
|
|
2047
|
+
this.rememberStaleBondError(uuid, bondError);
|
|
2048
|
+
throw bondError;
|
|
2049
|
+
}
|
|
2050
|
+
if (getFirmwareUploadWriteRetryType(error) !== 'congested' ||
|
|
2051
|
+
attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
|
|
2052
|
+
throw error;
|
|
2053
|
+
}
|
|
2054
|
+
const delayMs = resolveFirmwareUploadRetryDelay(attempt);
|
|
2055
|
+
attempt += 1;
|
|
2056
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 congested write retry:', {
|
|
2057
|
+
name: context.messageName,
|
|
2058
|
+
attempt,
|
|
2059
|
+
delayMs,
|
|
2060
|
+
});
|
|
2061
|
+
yield delay(delayMs);
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
});
|
|
2065
|
+
}
|
|
2066
|
+
writeProtocolV2Frame(uuid, transport$1, frame, context, assertCurrentGeneration) {
|
|
1519
2067
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1520
2068
|
const tuning = getProtocolV2BleTuning();
|
|
1521
2069
|
const packetCapacity = resolveProtocolV2PacketCapacity({
|
|
1522
2070
|
platform: reactNative.Platform.OS,
|
|
1523
2071
|
iosPacketLength: tuning.iosPacketLength,
|
|
1524
2072
|
androidPacketLength: tuning.androidPacketLength,
|
|
1525
|
-
mtu:
|
|
2073
|
+
mtu: transport$1.mtuSize,
|
|
2074
|
+
});
|
|
2075
|
+
yield transport.writeProtocolV2BleFrame({
|
|
2076
|
+
frame,
|
|
2077
|
+
packetCapacity,
|
|
2078
|
+
assertActive: assertCurrentGeneration,
|
|
2079
|
+
signal: context.signal,
|
|
2080
|
+
abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
|
|
2081
|
+
wait: delay,
|
|
2082
|
+
writePacket: packet => this.writeProtocolV2Packet(uuid, transport$1, buffer.Buffer.from(packet).toString('base64'), context, assertCurrentGeneration),
|
|
1526
2083
|
});
|
|
1527
|
-
for (let offset = 0; offset < frame.length; offset += packetCapacity) {
|
|
1528
|
-
const chunk = frame.slice(offset, offset + packetCapacity);
|
|
1529
|
-
const base64 = buffer.Buffer.from(chunk).toString('base64');
|
|
1530
|
-
yield transport.writeCharacteristic.writeWithoutResponse(base64);
|
|
1531
|
-
}
|
|
1532
2084
|
});
|
|
1533
2085
|
}
|
|
1534
2086
|
callProtocolV2(uuid, name, data, options) {
|
|
@@ -1537,15 +2089,40 @@ class ReactNativeBleTransport {
|
|
|
1537
2089
|
if (!this._messages || !this._messagesV2) {
|
|
1538
2090
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
1539
2091
|
}
|
|
1540
|
-
const callOptions =
|
|
1541
|
-
const
|
|
1542
|
-
if (
|
|
2092
|
+
const callOptions = options;
|
|
2093
|
+
const highThroughputWrite = transport.isProtocolV2HighThroughputCall(name);
|
|
2094
|
+
if (highThroughputWrite) {
|
|
2095
|
+
yield this.ensureProtocolV2HighThroughputMtu(uuid);
|
|
1543
2096
|
const tuning = getProtocolV2BleTuning();
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
2097
|
+
const currentTransport = this.getCachedTransport(uuid);
|
|
2098
|
+
const writeWithResponse = shouldWriteProtocolV2WithResponse({
|
|
2099
|
+
platform: reactNative.Platform.OS,
|
|
2100
|
+
highThroughput: true,
|
|
2101
|
+
requestedWithResponse: options === null || options === void 0 ? void 0 : options.writeWithResponse,
|
|
2102
|
+
characteristic: currentTransport.writeCharacteristic,
|
|
1548
2103
|
});
|
|
2104
|
+
const packetCapacity = resolveProtocolV2PacketCapacity({
|
|
2105
|
+
platform: reactNative.Platform.OS,
|
|
2106
|
+
iosPacketLength: tuning.iosPacketLength,
|
|
2107
|
+
androidPacketLength: tuning.androidPacketLength,
|
|
2108
|
+
mtu: currentTransport.mtuSize,
|
|
2109
|
+
});
|
|
2110
|
+
const writeMode = writeWithResponse ? 'withResponse' : 'withoutResponse';
|
|
2111
|
+
const logSignature = `${name}:${writeMode}:${String(currentTransport.mtuSize)}:${packetCapacity}`;
|
|
2112
|
+
const loggedSignatures = (_a = this.protocolV2HighVolumeLogSignatures.get(uuid)) !== null && _a !== void 0 ? _a : new Set();
|
|
2113
|
+
if (!loggedSignatures.has(logSignature)) {
|
|
2114
|
+
loggedSignatures.add(logSignature);
|
|
2115
|
+
this.protocolV2HighVolumeLogSignatures.set(uuid, loggedSignatures);
|
|
2116
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume write configured', {
|
|
2117
|
+
name,
|
|
2118
|
+
writeMode,
|
|
2119
|
+
reportedMtu: currentTransport.mtuSize,
|
|
2120
|
+
packetCapacity,
|
|
2121
|
+
});
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
if (highThroughputWrite) {
|
|
2125
|
+
yield this.enableAndroidHighConnectionPriority(uuid);
|
|
1549
2126
|
}
|
|
1550
2127
|
try {
|
|
1551
2128
|
return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, callOptions);
|
|
@@ -1554,6 +2131,85 @@ class ReactNativeBleTransport {
|
|
|
1554
2131
|
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] Protocol V2 call error:', e);
|
|
1555
2132
|
throw e;
|
|
1556
2133
|
}
|
|
2134
|
+
finally {
|
|
2135
|
+
if (highThroughputWrite) {
|
|
2136
|
+
this.scheduleAndroidBalancedConnectionPriority(uuid);
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
});
|
|
2140
|
+
}
|
|
2141
|
+
ensureProtocolV2HighThroughputMtu(uuid) {
|
|
2142
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2143
|
+
const transport = this.getCachedTransport(uuid);
|
|
2144
|
+
if (!shouldRefreshNegotiatedMtu(transport.mtuSize))
|
|
2145
|
+
return;
|
|
2146
|
+
const refreshedDevice = yield requestNegotiatedMtu(transport.device, 'highThroughput', 1);
|
|
2147
|
+
transport.device = refreshedDevice;
|
|
2148
|
+
transport.mtuSize =
|
|
2149
|
+
typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport.mtuSize;
|
|
2150
|
+
if (shouldRefreshNegotiatedMtu(transport.mtuSize)) {
|
|
2151
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `Protocol V2 high-throughput BLE MTU unavailable: ${String(transport.mtuSize)}`);
|
|
2152
|
+
}
|
|
2153
|
+
});
|
|
2154
|
+
}
|
|
2155
|
+
clearAndroidPriorityResetTimer(uuid) {
|
|
2156
|
+
const timerId = this.androidPriorityResetTimers.get(uuid);
|
|
2157
|
+
if (timerId !== undefined) {
|
|
2158
|
+
clearTimeout(timerId);
|
|
2159
|
+
this.androidPriorityResetTimers.delete(uuid);
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
enableAndroidHighConnectionPriority(uuid) {
|
|
2163
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2164
|
+
if (reactNative.Platform.OS !== 'android')
|
|
2165
|
+
return;
|
|
2166
|
+
this.clearAndroidPriorityResetTimer(uuid);
|
|
2167
|
+
if (this.androidHighPriorityDevices.has(uuid))
|
|
2168
|
+
return;
|
|
2169
|
+
const transport = transportCache[uuid];
|
|
2170
|
+
if (!transport)
|
|
2171
|
+
return;
|
|
2172
|
+
try {
|
|
2173
|
+
transport.device = yield transport.device.requestConnectionPriority(reactNativeBlePlx.ConnectionPriority.High);
|
|
2174
|
+
this.androidHighPriorityDevices.add(uuid);
|
|
2175
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE connection priority changed', {
|
|
2176
|
+
priority: 'high',
|
|
2177
|
+
});
|
|
2178
|
+
}
|
|
2179
|
+
catch (error) {
|
|
2180
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE high priority request failed', {
|
|
2181
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2182
|
+
});
|
|
2183
|
+
}
|
|
2184
|
+
});
|
|
2185
|
+
}
|
|
2186
|
+
scheduleAndroidBalancedConnectionPriority(uuid) {
|
|
2187
|
+
if (reactNative.Platform.OS !== 'android' || !this.androidHighPriorityDevices.has(uuid))
|
|
2188
|
+
return;
|
|
2189
|
+
this.clearAndroidPriorityResetTimer(uuid);
|
|
2190
|
+
const timerId = setTimeout(() => {
|
|
2191
|
+
this.androidPriorityResetTimers.delete(uuid);
|
|
2192
|
+
this.restoreAndroidConnectionPriority(uuid, transportCache[uuid]).catch(error => Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE priority restore failed', error));
|
|
2193
|
+
}, ANDROID_HIGH_PRIORITY_IDLE_MS);
|
|
2194
|
+
this.androidPriorityResetTimers.set(uuid, timerId);
|
|
2195
|
+
}
|
|
2196
|
+
restoreAndroidConnectionPriority(uuid, transport) {
|
|
2197
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2198
|
+
this.clearAndroidPriorityResetTimer(uuid);
|
|
2199
|
+
if (reactNative.Platform.OS !== 'android' || !this.androidHighPriorityDevices.delete(uuid) || !transport) {
|
|
2200
|
+
return;
|
|
2201
|
+
}
|
|
2202
|
+
try {
|
|
2203
|
+
transport.device = yield transport.device.requestConnectionPriority(reactNativeBlePlx.ConnectionPriority.Balanced);
|
|
2204
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE connection priority changed', {
|
|
2205
|
+
priority: 'balanced',
|
|
2206
|
+
});
|
|
2207
|
+
}
|
|
2208
|
+
catch (error) {
|
|
2209
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE balanced priority request failed', {
|
|
2210
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2211
|
+
});
|
|
2212
|
+
}
|
|
1557
2213
|
});
|
|
1558
2214
|
}
|
|
1559
2215
|
createProtocolV2Adapter(uuid) {
|
|
@@ -1574,10 +2230,10 @@ class ReactNativeBleTransport {
|
|
|
1574
2230
|
(_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1575
2231
|
this.resetProtocolV2Frames(uuid);
|
|
1576
2232
|
},
|
|
1577
|
-
writeFrame: (frame) => __awaiter(this, void 0, void 0, function* () {
|
|
2233
|
+
writeFrame: (frame, context) => __awaiter(this, void 0, void 0, function* () {
|
|
1578
2234
|
assertCurrentGeneration();
|
|
1579
2235
|
const currentTransport = this.getCachedTransport(uuid);
|
|
1580
|
-
yield this.writeProtocolV2Frame(currentTransport, frame);
|
|
2236
|
+
yield this.writeProtocolV2Frame(uuid, currentTransport, frame, context, assertCurrentGeneration);
|
|
1581
2237
|
}),
|
|
1582
2238
|
readFrame: () => __awaiter(this, void 0, void 0, function* () {
|
|
1583
2239
|
assertCurrentGeneration();
|
|
@@ -1589,6 +2245,8 @@ class ReactNativeBleTransport {
|
|
|
1589
2245
|
}),
|
|
1590
2246
|
reset: (reason) => {
|
|
1591
2247
|
var _a;
|
|
2248
|
+
if (this.monitorTokens.get(uuid) !== generation)
|
|
2249
|
+
return;
|
|
1592
2250
|
(_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1593
2251
|
this.rejectProtocolV2Frames(uuid, new Error(reason));
|
|
1594
2252
|
},
|
|
@@ -1598,11 +2256,20 @@ class ReactNativeBleTransport {
|
|
|
1598
2256
|
};
|
|
1599
2257
|
}
|
|
1600
2258
|
getProtocolType(path) {
|
|
1601
|
-
return this.
|
|
2259
|
+
return this.getActiveProtocol(path);
|
|
1602
2260
|
}
|
|
1603
2261
|
}
|
|
1604
2262
|
|
|
2263
|
+
exports.BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD;
|
|
2264
|
+
exports.BLE_CONNECT_TIMEOUT_MS = BLE_CONNECT_TIMEOUT_MS;
|
|
2265
|
+
exports.BLE_GATT_SETUP_TIMEOUT_MS = BLE_GATT_SETUP_TIMEOUT_MS;
|
|
2266
|
+
exports.BLE_NATIVE_TEARDOWN_TIMEOUT_MS = BLE_NATIVE_TEARDOWN_TIMEOUT_MS;
|
|
2267
|
+
exports.BLE_SETUP_WEDGED_MESSAGE = BLE_SETUP_WEDGED_MESSAGE;
|
|
2268
|
+
exports.BLE_WRITE_PACKET_TIMEOUT_MS = BLE_WRITE_PACKET_TIMEOUT_MS;
|
|
2269
|
+
exports.BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD;
|
|
2270
|
+
exports.PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = PROTOCOL_REPROBE_FALLBACK_ATTEMPTS;
|
|
1605
2271
|
exports.configureProtocolV2BleTuning = configureProtocolV2BleTuning;
|
|
1606
2272
|
exports["default"] = ReactNativeBleTransport;
|
|
2273
|
+
exports.getFirmwareUploadWriteRetryType = getFirmwareUploadWriteRetryType;
|
|
1607
2274
|
exports.getProtocolV2BleTuning = getProtocolV2BleTuning;
|
|
1608
2275
|
exports.resetProtocolV2BleTuning = resetProtocolV2BleTuning;
|