@onekeyfe/hd-transport-react-native 1.2.0-alpha.9 → 1.2.0-alpha.90
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/bleStrategy.d.ts +7 -2
- 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 +114 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +750 -312
- package/dist/subscribeBleOn.d.ts.map +1 -1
- package/dist/transportLog.d.ts +2 -0
- package/dist/transportLog.d.ts.map +1 -0
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/jest.config.js +10 -0
- package/package.json +7 -6
- package/src/BleTransport.ts +10 -42
- package/src/__tests__/BleTransport.test.ts +114 -0
- package/src/__tests__/bleStrategy.test.ts +89 -6
- package/src/__tests__/connectTimeout.test.ts +292 -0
- package/src/__tests__/constants.test.ts +20 -0
- package/src/__tests__/enumerate.test.ts +132 -0
- package/src/__tests__/protocolReprobe.test.ts +132 -0
- package/src/__tests__/protocolV1SchemaFixture.ts +39 -0
- package/src/__tests__/protocolV2Link.test.ts +758 -46
- package/src/__tests__/staleCallTimeout.test.ts +210 -0
- package/src/__tests__/writePacketTimeout.test.ts +305 -0
- package/src/bleStrategy.ts +26 -36
- package/src/constants.ts +9 -14
- package/src/index.ts +990 -330
- package/src/subscribeBleOn.ts +0 -2
- package/src/transportLog.ts +1 -0
- 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,51 +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
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
return 'withResponse';
|
|
147
|
-
}
|
|
148
|
-
if (characteristic.isWritableWithoutResponse) {
|
|
149
|
-
return 'withoutResponse';
|
|
150
|
-
}
|
|
151
|
-
if (characteristic.isWritableWithResponse) {
|
|
152
|
-
return 'withResponse';
|
|
153
|
-
}
|
|
154
|
-
return preferredMode;
|
|
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);
|
|
155
143
|
}
|
|
156
|
-
function
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if (
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
return
|
|
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);
|
|
165
153
|
}
|
|
166
154
|
|
|
167
155
|
const timer = process.env.NODE_ENV === 'development'
|
|
@@ -189,7 +177,6 @@ const timer = process.env.NODE_ENV === 'development'
|
|
|
189
177
|
const subscribeBleOn = (bleManager, ms = 1000) => new Promise((resolve, reject) => {
|
|
190
178
|
let done = false;
|
|
191
179
|
const subscription = bleManager.onStateChange(state => {
|
|
192
|
-
console.log('ble state -> ', state);
|
|
193
180
|
if (state === 'PoweredOn') {
|
|
194
181
|
if (done)
|
|
195
182
|
return;
|
|
@@ -219,47 +206,20 @@ const isHeaderChunk = (chunk) => {
|
|
|
219
206
|
return false;
|
|
220
207
|
};
|
|
221
208
|
|
|
222
|
-
const Log$1 = bleLogger;
|
|
223
209
|
class BleTransport {
|
|
224
210
|
constructor(device, writeCharacteristic, notifyCharacteristic) {
|
|
225
211
|
this.name = 'ReactNativeBleTransport';
|
|
226
|
-
this.mtuSize = 23;
|
|
227
212
|
this.id = device.id;
|
|
228
213
|
this.device = device;
|
|
229
214
|
this.writeCharacteristic = writeCharacteristic;
|
|
230
215
|
this.notifyCharacteristic = notifyCharacteristic;
|
|
231
216
|
}
|
|
232
|
-
writeWithRetry(data
|
|
217
|
+
writeWithRetry(data) {
|
|
233
218
|
return __awaiter(this, void 0, void 0, function* () {
|
|
234
|
-
|
|
235
|
-
yield this.writeCharacteristic.writeWithoutResponse(data);
|
|
236
|
-
}
|
|
237
|
-
catch (error) {
|
|
238
|
-
Log$1 === null || Log$1 === void 0 ? void 0 : Log$1.debug(`Write retry attempt ${BleTransport.MAX_RETRIES - retryCount + 1}, error: ${error}`);
|
|
239
|
-
if (retryCount > 0) {
|
|
240
|
-
yield hdShared.wait(BleTransport.RETRY_DELAY);
|
|
241
|
-
if (error.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected ||
|
|
242
|
-
error.errorCode === reactNativeBlePlx.BleErrorCode.CharacteristicNotFound) {
|
|
243
|
-
try {
|
|
244
|
-
yield this.device.connect();
|
|
245
|
-
yield this.device.discoverAllServicesAndCharacteristics();
|
|
246
|
-
}
|
|
247
|
-
catch (e) {
|
|
248
|
-
Log$1 === null || Log$1 === void 0 ? void 0 : Log$1.debug(`Connect or discoverAllServicesAndCharacteristics error: ${e}`);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
Log$1 === null || Log$1 === void 0 ? void 0 : Log$1.debug(`writeCharacteristic error: ${error}`);
|
|
253
|
-
}
|
|
254
|
-
return this.writeWithRetry(data, retryCount - 1);
|
|
255
|
-
}
|
|
256
|
-
throw error;
|
|
257
|
-
}
|
|
219
|
+
yield this.writeCharacteristic.writeWithoutResponse(data);
|
|
258
220
|
});
|
|
259
221
|
}
|
|
260
222
|
}
|
|
261
|
-
BleTransport.MAX_RETRIES = 5;
|
|
262
|
-
BleTransport.RETRY_DELAY = 2000;
|
|
263
223
|
|
|
264
224
|
const { check, ProtocolV1, parseConfigure } = transport__default["default"];
|
|
265
225
|
const Log = bleLogger;
|
|
@@ -268,10 +228,35 @@ const FIRMWARE_UPLOAD_WRITE_BURST_SIZE = reactNative.Platform.OS === 'ios' ? 4 :
|
|
|
268
228
|
const FIRMWARE_UPLOAD_WRITE_PAUSE_MS = reactNative.Platform.OS === 'ios' ? 8 : 10;
|
|
269
229
|
const FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS = reactNative.Platform.OS === 'ios' ? 24 : 30;
|
|
270
230
|
const FIRMWARE_UPLOAD_WRITE_MAX_RETRIES = 8;
|
|
271
|
-
const FIRMWARE_UPLOAD_RECONNECT_RETRY_DELAY_MS = 2000;
|
|
272
231
|
const ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH = 192;
|
|
273
232
|
const FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY = reactNative.Platform.OS === 'ios' ? IOS_PACKET_LENGTH : ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH;
|
|
274
233
|
const ANDROID_GATT_CONGESTED_STATUS = 143;
|
|
234
|
+
const isAsciiWhitespace = (code) => code === 0x09 ||
|
|
235
|
+
code === 0x0a ||
|
|
236
|
+
code === 0x0b ||
|
|
237
|
+
code === 0x0c ||
|
|
238
|
+
code === 0x0d ||
|
|
239
|
+
code === 0x20;
|
|
240
|
+
const hasGattCongestedStatus = (text) => {
|
|
241
|
+
let searchFrom = 0;
|
|
242
|
+
while (searchFrom < text.length) {
|
|
243
|
+
const statusIndex = text.indexOf('status', searchFrom);
|
|
244
|
+
if (statusIndex < 0)
|
|
245
|
+
return false;
|
|
246
|
+
let cursor = statusIndex + 'status'.length;
|
|
247
|
+
while (cursor < text.length && isAsciiWhitespace(text.charCodeAt(cursor)))
|
|
248
|
+
cursor += 1;
|
|
249
|
+
if (text[cursor] === ':' || text[cursor] === '=') {
|
|
250
|
+
cursor += 1;
|
|
251
|
+
while (cursor < text.length && isAsciiWhitespace(text.charCodeAt(cursor)))
|
|
252
|
+
cursor += 1;
|
|
253
|
+
}
|
|
254
|
+
if (text.startsWith(String(ANDROID_GATT_CONGESTED_STATUS), cursor))
|
|
255
|
+
return true;
|
|
256
|
+
searchFrom = statusIndex + 'status'.length;
|
|
257
|
+
}
|
|
258
|
+
return false;
|
|
259
|
+
};
|
|
275
260
|
const delay = (ms) => new Promise(resolve => {
|
|
276
261
|
setTimeout(resolve, ms);
|
|
277
262
|
});
|
|
@@ -279,10 +264,6 @@ const getFirmwareUploadWriteRetryType = (error) => {
|
|
|
279
264
|
if (!error || typeof error !== 'object')
|
|
280
265
|
return null;
|
|
281
266
|
const bleWriteError = error;
|
|
282
|
-
if (bleWriteError.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected ||
|
|
283
|
-
bleWriteError.errorCode === reactNativeBlePlx.BleErrorCode.CharacteristicNotFound) {
|
|
284
|
-
return 'reconnectable';
|
|
285
|
-
}
|
|
286
267
|
if (bleWriteError.androidErrorCode === ANDROID_GATT_CONGESTED_STATUS ||
|
|
287
268
|
bleWriteError.status === ANDROID_GATT_CONGESTED_STATUS) {
|
|
288
269
|
return 'congested';
|
|
@@ -290,25 +271,23 @@ const getFirmwareUploadWriteRetryType = (error) => {
|
|
|
290
271
|
const text = [bleWriteError.reason, bleWriteError.message, bleWriteError.name]
|
|
291
272
|
.filter(value => typeof value === 'string')
|
|
292
273
|
.join(' ');
|
|
293
|
-
return
|
|
274
|
+
return text.includes('GATT_CONGESTED') || hasGattCongestedStatus(text) ? 'congested' : null;
|
|
294
275
|
};
|
|
295
276
|
const resolveFirmwareUploadRetryDelay = (attempt, baseDelayMs = 200, maxDelayMs = 1200) => Math.min(baseDelayMs * Math.pow(2, attempt), maxDelayMs);
|
|
296
|
-
const BLE_RESPONSE_TIMEOUT_MS = 30000;
|
|
297
277
|
const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
|
|
298
278
|
const PROTOCOL_V2_PROBE_TIMEOUT_MS = 10000;
|
|
299
|
-
const
|
|
279
|
+
const BLE_WRITE_PACKET_TIMEOUT_MS = 10000;
|
|
280
|
+
const WEDGED_WRITE_MESSAGE = 'BLE write timeout after';
|
|
281
|
+
const isWedgedWriteError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleWriteCharacteristicError &&
|
|
282
|
+
typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' &&
|
|
283
|
+
error.message.startsWith(WEDGED_WRITE_MESSAGE);
|
|
284
|
+
const BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
|
|
285
|
+
const DEVICE_SCAN_TIMEOUT_MS = 3000;
|
|
300
286
|
const IOS_NOTIFY_READY_DELAY_MS = 150;
|
|
301
287
|
const ANDROID_NOTIFY_READY_DELAY_MS = 300;
|
|
302
|
-
const HIGH_VOLUME_WRITE_BURST_SIZE = reactNative.Platform.OS === 'ios' ? 4 : 6;
|
|
303
|
-
const HIGH_VOLUME_WRITE_PAUSE_MS = reactNative.Platform.OS === 'ios' ? 6 : 2;
|
|
304
|
-
const HIGH_VOLUME_WRITE_FLUSH_DELAY_MS = reactNative.Platform.OS === 'ios' ? 20 : 8;
|
|
305
288
|
const DEFAULT_PROTOCOL_V2_BLE_TUNING = {
|
|
306
|
-
iosPacketLength:
|
|
307
|
-
androidPacketLength:
|
|
308
|
-
highVolumeWriteBurstSize: HIGH_VOLUME_WRITE_BURST_SIZE,
|
|
309
|
-
highVolumeWritePauseMs: HIGH_VOLUME_WRITE_PAUSE_MS,
|
|
310
|
-
highVolumeWriteFlushDelayMs: HIGH_VOLUME_WRITE_FLUSH_DELAY_MS,
|
|
311
|
-
highVolumeWriteWithResponse: false,
|
|
289
|
+
iosPacketLength: IOS_PROTOCOL_V2_PACKET_LENGTH,
|
|
290
|
+
androidPacketLength: ANDROID_PROTOCOL_V2_PACKET_LENGTH,
|
|
312
291
|
};
|
|
313
292
|
let protocolV2BleTuning = Object.assign({}, DEFAULT_PROTOCOL_V2_BLE_TUNING);
|
|
314
293
|
const normalizePositiveInteger = (value, fallback) => {
|
|
@@ -318,20 +297,15 @@ const normalizePositiveInteger = (value, fallback) => {
|
|
|
318
297
|
return Math.floor(normalized);
|
|
319
298
|
};
|
|
320
299
|
function configureProtocolV2BleTuning(tuning = {}) {
|
|
321
|
-
var _a;
|
|
322
300
|
protocolV2BleTuning = {
|
|
323
301
|
iosPacketLength: normalizePositiveInteger(tuning.iosPacketLength, protocolV2BleTuning.iosPacketLength),
|
|
324
302
|
androidPacketLength: normalizePositiveInteger(tuning.androidPacketLength, protocolV2BleTuning.androidPacketLength),
|
|
325
|
-
highVolumeWriteBurstSize: normalizePositiveInteger(tuning.highVolumeWriteBurstSize, protocolV2BleTuning.highVolumeWriteBurstSize),
|
|
326
|
-
highVolumeWritePauseMs: normalizePositiveInteger(tuning.highVolumeWritePauseMs, protocolV2BleTuning.highVolumeWritePauseMs),
|
|
327
|
-
highVolumeWriteFlushDelayMs: normalizePositiveInteger(tuning.highVolumeWriteFlushDelayMs, protocolV2BleTuning.highVolumeWriteFlushDelayMs),
|
|
328
|
-
highVolumeWriteWithResponse: (_a = tuning.highVolumeWriteWithResponse) !== null && _a !== void 0 ? _a : protocolV2BleTuning.highVolumeWriteWithResponse,
|
|
329
303
|
};
|
|
330
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport]
|
|
304
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE tuning configured', protocolV2BleTuning);
|
|
331
305
|
}
|
|
332
306
|
function resetProtocolV2BleTuning() {
|
|
333
307
|
protocolV2BleTuning = Object.assign({}, DEFAULT_PROTOCOL_V2_BLE_TUNING);
|
|
334
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport]
|
|
308
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE tuning reset', protocolV2BleTuning);
|
|
335
309
|
}
|
|
336
310
|
function getProtocolV2BleTuning() {
|
|
337
311
|
return Object.assign({}, protocolV2BleTuning);
|
|
@@ -342,19 +316,29 @@ function inferProtocolHintFromDeviceName(name) {
|
|
|
342
316
|
function getDeviceDisplayName(device) {
|
|
343
317
|
return (device === null || device === void 0 ? void 0 : device.name) || (device === null || device === void 0 ? void 0 : device.localName) || null;
|
|
344
318
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
}
|
|
352
|
-
const ANDROID_REQUEST_MTU = 256;
|
|
319
|
+
const IOS_REQUEST_MTU = 247;
|
|
320
|
+
const ANDROID_REQUEST_MTU = 517;
|
|
321
|
+
const BLE_MTU_REFRESH_RETRY_DELAY_MS = 200;
|
|
322
|
+
const ANDROID_HIGH_PRIORITY_IDLE_MS = 1000;
|
|
323
|
+
const getRequestedBleMtu = () => reactNative.Platform.OS === 'android' ? ANDROID_REQUEST_MTU : IOS_REQUEST_MTU;
|
|
324
|
+
const BLE_NATIVE_CONNECT_TIMEOUT_MS = 3000;
|
|
353
325
|
const connectOptions = {
|
|
354
|
-
requestMTU:
|
|
355
|
-
timeout:
|
|
326
|
+
requestMTU: getRequestedBleMtu(),
|
|
327
|
+
timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
|
|
356
328
|
refreshGatt: 'OnConnected',
|
|
357
329
|
};
|
|
330
|
+
const fallbackConnectOptions = {
|
|
331
|
+
timeout: BLE_NATIVE_CONNECT_TIMEOUT_MS,
|
|
332
|
+
};
|
|
333
|
+
const BLE_CONNECT_TIMEOUT_MS = BLE_NATIVE_CONNECT_TIMEOUT_MS * 2 + 2000;
|
|
334
|
+
const BLE_GATT_SETUP_TIMEOUT_MS = 10000;
|
|
335
|
+
const PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = 3;
|
|
336
|
+
const BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = 2;
|
|
337
|
+
const CONNECT_TIMEOUT_MESSAGE = 'BLE connect timeout after';
|
|
338
|
+
const isConnectTimeoutError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === hdShared.HardwareErrorCode.BleConnectedError &&
|
|
339
|
+
typeof (error === null || error === void 0 ? void 0 : error.message) === 'string' &&
|
|
340
|
+
error.message.startsWith(CONNECT_TIMEOUT_MESSAGE);
|
|
341
|
+
const isNativeOperationTimeoutError = (error) => (error === null || error === void 0 ? void 0 : error.errorCode) === reactNativeBlePlx.BleErrorCode.OperationTimedOut;
|
|
358
342
|
const tryToGetConfiguration = (device) => {
|
|
359
343
|
if (!device || !device.serviceUUIDs)
|
|
360
344
|
return null;
|
|
@@ -366,22 +350,25 @@ const tryToGetConfiguration = (device) => {
|
|
|
366
350
|
return null;
|
|
367
351
|
return infos;
|
|
368
352
|
};
|
|
369
|
-
const
|
|
370
|
-
if (reactNative.Platform.OS !== 'android')
|
|
353
|
+
const requestNegotiatedMtu = (device, stage, attempt) => __awaiter(void 0, void 0, void 0, function* () {
|
|
354
|
+
if (reactNative.Platform.OS !== 'ios' && reactNative.Platform.OS !== 'android')
|
|
371
355
|
return device;
|
|
372
356
|
try {
|
|
373
|
-
const mtuDevice = yield device.requestMTU(
|
|
374
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android MTU requested:', {
|
|
375
|
-
requested: ANDROID_REQUEST_MTU,
|
|
376
|
-
mtu: mtuDevice.mtu,
|
|
377
|
-
});
|
|
357
|
+
const mtuDevice = yield device.requestMTU(getRequestedBleMtu());
|
|
378
358
|
return mtuDevice;
|
|
379
359
|
}
|
|
380
360
|
catch (error) {
|
|
381
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport]
|
|
361
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] MTU refresh failed, continuing with current value', {
|
|
362
|
+
platform: reactNative.Platform.OS,
|
|
363
|
+
stage,
|
|
364
|
+
attempt,
|
|
365
|
+
actual: device.mtu,
|
|
366
|
+
error: error instanceof Error ? error.message : String(error),
|
|
367
|
+
});
|
|
382
368
|
return device;
|
|
383
369
|
}
|
|
384
370
|
});
|
|
371
|
+
const resolveNegotiatedMtu = (device) => requestNegotiatedMtu(device, 'connected', 0);
|
|
385
372
|
function remapError(error) {
|
|
386
373
|
var _a;
|
|
387
374
|
if (error instanceof reactNativeBlePlx.BleError) {
|
|
@@ -408,9 +395,15 @@ class ReactNativeBleTransport {
|
|
|
408
395
|
this.stopped = false;
|
|
409
396
|
this.scanTimeout = DEVICE_SCAN_TIMEOUT_MS;
|
|
410
397
|
this.runPromise = null;
|
|
398
|
+
this.runPromiseDeviceId = null;
|
|
411
399
|
this.firmwareUploadWriteRecoveryIds = new Set();
|
|
412
400
|
this.deviceProtocol = new Map();
|
|
401
|
+
this.probingProtocols = new Map();
|
|
402
|
+
this.writeTimeoutCounts = new Map();
|
|
403
|
+
this.connectionSetupTimeoutCounts = new Map();
|
|
413
404
|
this.deviceProtocolHints = new Map();
|
|
405
|
+
this.sessionProtocols = new Map();
|
|
406
|
+
this.protocolReprobeFailures = new Map();
|
|
414
407
|
this.protocolV2Assemblers = new Map();
|
|
415
408
|
this.protocolV2FrameQueues = new Map();
|
|
416
409
|
this.protocolV2FramePromises = new Map();
|
|
@@ -431,11 +424,15 @@ class ReactNativeBleTransport {
|
|
|
431
424
|
this.rejectProtocolV2Frames(uuid, new Error(reason));
|
|
432
425
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 link invalidated:', uuid, reason);
|
|
433
426
|
if (reason.startsWith('Protocol V2 link-fatal error:')) {
|
|
434
|
-
yield this.
|
|
427
|
+
yield this.releaseNative(uuid, true);
|
|
435
428
|
}
|
|
436
429
|
}),
|
|
437
430
|
});
|
|
438
431
|
this.monitorTokens = new Map();
|
|
432
|
+
this.disconnectEventTokens = new Map();
|
|
433
|
+
this.protocolV2HighVolumeLogSignatures = new Map();
|
|
434
|
+
this.androidHighPriorityDevices = new Set();
|
|
435
|
+
this.androidPriorityResetTimers = new Map();
|
|
439
436
|
this.nextMonitorToken = 1;
|
|
440
437
|
this.scanTimeout = (_a = options.scanTimeout) !== null && _a !== void 0 ? _a : DEVICE_SCAN_TIMEOUT_MS;
|
|
441
438
|
}
|
|
@@ -449,11 +446,18 @@ class ReactNativeBleTransport {
|
|
|
449
446
|
this._messages = messages;
|
|
450
447
|
}
|
|
451
448
|
configureProtocolV2(signedData) {
|
|
449
|
+
const configuration = typeof signedData === 'string' ? signedData : JSON.stringify(signedData);
|
|
450
|
+
if (this.protocolV2SchemaConfiguration === configuration) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
const isReconfiguration = this.protocolV2SchemaConfiguration !== undefined;
|
|
452
454
|
this._messagesV2 = parseConfigure(signedData);
|
|
453
|
-
this.
|
|
454
|
-
|
|
455
|
-
.
|
|
456
|
-
|
|
455
|
+
this.protocolV2SchemaConfiguration = configuration;
|
|
456
|
+
if (isReconfiguration) {
|
|
457
|
+
this.protocolV2Links
|
|
458
|
+
.invalidateAllLinks('Protocol V2 schema reconfigured')
|
|
459
|
+
.catch(error => Log === null || Log === void 0 ? void 0 : Log.debug('Protocol V2 schema link cleanup failed:', error));
|
|
460
|
+
}
|
|
457
461
|
}
|
|
458
462
|
listen() {
|
|
459
463
|
}
|
|
@@ -464,7 +468,6 @@ class ReactNativeBleTransport {
|
|
|
464
468
|
return Promise.resolve(this.blePlxManager);
|
|
465
469
|
}
|
|
466
470
|
resolveCharacteristics(device) {
|
|
467
|
-
var _a, _b, _c, _d;
|
|
468
471
|
return __awaiter(this, void 0, void 0, function* () {
|
|
469
472
|
yield device.discoverAllServicesAndCharacteristics();
|
|
470
473
|
let infos = tryToGetConfiguration(device);
|
|
@@ -481,19 +484,11 @@ class ReactNativeBleTransport {
|
|
|
481
484
|
}
|
|
482
485
|
}
|
|
483
486
|
}
|
|
484
|
-
let fallbackServiceUuid;
|
|
485
487
|
if (!infos) {
|
|
486
488
|
const services = yield device.services();
|
|
487
489
|
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));
|
|
488
|
-
const knownService = services.find(service => getInfosForServiceUuid(service.uuid, 'classic'));
|
|
489
|
-
const fallbackService = (_a = knownService !== null && knownService !== void 0 ? knownService : services.find(service => !isGenericBleService(service.uuid))) !== null && _a !== void 0 ? _a : services[0];
|
|
490
|
-
if (fallbackService) {
|
|
491
|
-
fallbackServiceUuid = fallbackService.uuid;
|
|
492
|
-
characteristics = yield device.characteristicsForService(fallbackService.uuid);
|
|
493
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Using fallback BLE service:', fallbackService.uuid);
|
|
494
|
-
}
|
|
495
490
|
}
|
|
496
|
-
if (!infos
|
|
491
|
+
if (!infos) {
|
|
497
492
|
try {
|
|
498
493
|
Log === null || Log === void 0 ? void 0 : Log.debug('cancel connection when service not found');
|
|
499
494
|
yield device.cancelConnection();
|
|
@@ -503,9 +498,7 @@ class ReactNativeBleTransport {
|
|
|
503
498
|
}
|
|
504
499
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
|
|
505
500
|
}
|
|
506
|
-
const serviceUuid
|
|
507
|
-
const writeUuid = (_c = infos === null || infos === void 0 ? void 0 : infos.writeUuid) !== null && _c !== void 0 ? _c : '00000002-0000-1000-8000-00805f9b34fb';
|
|
508
|
-
const notifyUuid = (_d = infos === null || infos === void 0 ? void 0 : infos.notifyUuid) !== null && _d !== void 0 ? _d : '00000003-0000-1000-8000-00805f9b34fb';
|
|
501
|
+
const { serviceUuid, writeUuid, notifyUuid } = infos;
|
|
509
502
|
if (!serviceUuid) {
|
|
510
503
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
|
|
511
504
|
}
|
|
@@ -546,8 +539,8 @@ class ReactNativeBleTransport {
|
|
|
546
539
|
attachDisconnectSubscription(transport, device, uuid) {
|
|
547
540
|
var _a;
|
|
548
541
|
(_a = transport.disconnectSubscription) === null || _a === void 0 ? void 0 : _a.remove();
|
|
542
|
+
const { monitorToken } = transport;
|
|
549
543
|
transport.disconnectSubscription = device.onDisconnected(() => {
|
|
550
|
-
var _a;
|
|
551
544
|
if (this.firmwareUploadWriteRecoveryIds.has(uuid)) {
|
|
552
545
|
Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored during FirmwareUpload write recovery: ', uuid);
|
|
553
546
|
return;
|
|
@@ -556,17 +549,16 @@ class ReactNativeBleTransport {
|
|
|
556
549
|
Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored for stale transport: ', device === null || device === void 0 ? void 0 : device.id);
|
|
557
550
|
return;
|
|
558
551
|
}
|
|
552
|
+
if (this.monitorTokens.get(uuid) !== monitorToken) {
|
|
553
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored for stale generation: ', device === null || device === void 0 ? void 0 : device.id);
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
559
556
|
try {
|
|
560
557
|
Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect: ', device === null || device === void 0 ? void 0 : device.id);
|
|
561
|
-
(
|
|
562
|
-
|
|
563
|
-
id: device === null || device === void 0 ? void 0 : device.id,
|
|
564
|
-
connectId: device === null || device === void 0 ? void 0 : device.id,
|
|
565
|
-
});
|
|
566
|
-
if (this.runPromise) {
|
|
558
|
+
this.emitDeviceDisconnect(uuid, device === null || device === void 0 ? void 0 : device.name, monitorToken);
|
|
559
|
+
if (this.runPromise && this.runPromiseDeviceId === uuid) {
|
|
567
560
|
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError);
|
|
568
561
|
this.runPromise.reject(error);
|
|
569
|
-
this.rejectAllProtocolV2Frames(error);
|
|
570
562
|
}
|
|
571
563
|
}
|
|
572
564
|
catch (e) {
|
|
@@ -577,6 +569,22 @@ class ReactNativeBleTransport {
|
|
|
577
569
|
}
|
|
578
570
|
});
|
|
579
571
|
}
|
|
572
|
+
emitDeviceDisconnect(uuid, name, token) {
|
|
573
|
+
var _a;
|
|
574
|
+
if (token === undefined || this.disconnectEventTokens.get(uuid) === token) {
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
if (this.monitorTokens.get(uuid) !== token) {
|
|
578
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect event ignored for stale generation: ', uuid);
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
this.disconnectEventTokens.set(uuid, token);
|
|
582
|
+
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit(transport.TRANSPORT_EVENT.DEVICE_DISCONNECT, {
|
|
583
|
+
name,
|
|
584
|
+
id: uuid,
|
|
585
|
+
connectId: uuid,
|
|
586
|
+
});
|
|
587
|
+
}
|
|
580
588
|
reconnectFirmwareUploadTransport(uuid, transport) {
|
|
581
589
|
var _a, _b;
|
|
582
590
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -590,19 +598,19 @@ class ReactNativeBleTransport {
|
|
|
590
598
|
const isConnected = yield device.isConnected().catch(() => false);
|
|
591
599
|
if (!isConnected) {
|
|
592
600
|
try {
|
|
593
|
-
device = yield device.connect(connectOptions);
|
|
601
|
+
device = yield this.connectWithTimeout(uuid, () => device.connect(connectOptions));
|
|
594
602
|
}
|
|
595
603
|
catch (e) {
|
|
596
604
|
if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
|
|
597
605
|
e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
|
|
598
|
-
device = yield device.connect();
|
|
606
|
+
device = yield this.connectWithTimeout(uuid, () => device.connect());
|
|
599
607
|
}
|
|
600
608
|
else if (e.errorCode !== reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
|
|
601
609
|
throw e;
|
|
602
610
|
}
|
|
603
611
|
}
|
|
604
612
|
}
|
|
605
|
-
const { writeCharacteristic, notifyCharacteristic } = yield this.
|
|
613
|
+
const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, device);
|
|
606
614
|
transport.device = device;
|
|
607
615
|
transport.writeCharacteristic = writeCharacteristic;
|
|
608
616
|
transport.notifyCharacteristic = notifyCharacteristic;
|
|
@@ -646,13 +654,12 @@ class ReactNativeBleTransport {
|
|
|
646
654
|
return;
|
|
647
655
|
}
|
|
648
656
|
}
|
|
649
|
-
blePlxManager.startDeviceScan(
|
|
657
|
+
blePlxManager.startDeviceScan(getBluetoothServiceUuids(), {
|
|
650
658
|
allowDuplicates: true,
|
|
651
659
|
scanMode: reactNativeBlePlx.ScanMode.LowLatency,
|
|
652
660
|
}, (error, device) => {
|
|
653
|
-
var _a
|
|
661
|
+
var _a;
|
|
654
662
|
if (error) {
|
|
655
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('ble scan manager: ', blePlxManager);
|
|
656
663
|
Log === null || Log === void 0 ? void 0 : Log.debug('ble scan error: ', error);
|
|
657
664
|
if ([reactNativeBlePlx.BleErrorCode.BluetoothPoweredOff, reactNativeBlePlx.BleErrorCode.BluetoothInUnknownState].includes(error.errorCode)) {
|
|
658
665
|
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePermissionError));
|
|
@@ -672,25 +679,19 @@ class ReactNativeBleTransport {
|
|
|
672
679
|
return;
|
|
673
680
|
}
|
|
674
681
|
const displayName = getDeviceDisplayName(device);
|
|
675
|
-
const
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
const
|
|
679
|
-
|
|
680
|
-
|
|
682
|
+
const isUnnamedIOSPeripheral = reactNative.Platform.OS === 'ios' && !(displayName === null || displayName === void 0 ? void 0 : displayName.trim());
|
|
683
|
+
const isFindMyPeripheral = hdShared.isPro2FindMyAdvertisementName(device === null || device === void 0 ? void 0 : device.name) ||
|
|
684
|
+
hdShared.isPro2FindMyAdvertisementName(device === null || device === void 0 ? void 0 : device.localName);
|
|
685
|
+
const isOneKey = !isUnnamedIOSPeripheral &&
|
|
686
|
+
!isFindMyPeripheral &&
|
|
687
|
+
hdShared.isOnekeyBluetoothDevice({
|
|
688
|
+
id: device === null || device === void 0 ? void 0 : device.id,
|
|
681
689
|
name: device === null || device === void 0 ? void 0 : device.name,
|
|
682
690
|
localName: device === null || device === void 0 ? void 0 : device.localName,
|
|
683
|
-
|
|
684
|
-
serviceUUIDs: device === null || device === void 0 ? void 0 : device.serviceUUIDs,
|
|
685
|
-
accepted: isOneKey,
|
|
691
|
+
serviceUuids: device === null || device === void 0 ? void 0 : device.serviceUUIDs,
|
|
686
692
|
});
|
|
687
|
-
}
|
|
688
693
|
if (isOneKey) {
|
|
689
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('search device start ======================');
|
|
690
|
-
const { name, localName, id, serviceUUIDs } = device !== null && device !== void 0 ? device : {};
|
|
691
|
-
Log === null || Log === void 0 ? void 0 : Log.debug(`device name: ${name !== null && name !== void 0 ? name : ''}\nlocalName: ${localName !== null && localName !== void 0 ? localName : ''}\nid: ${id !== null && id !== void 0 ? id : ''}\nserviceUUIDs: ${(serviceUUIDs !== null && serviceUUIDs !== void 0 ? serviceUUIDs : []).join(',')}`);
|
|
692
694
|
addDevice(device);
|
|
693
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('search device end ======================\n');
|
|
694
695
|
}
|
|
695
696
|
else if (displayName && /\bpro\s*2\b/i.test(displayName)) {
|
|
696
697
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Pro2-like BLE device was not accepted:', {
|
|
@@ -701,10 +702,23 @@ class ReactNativeBleTransport {
|
|
|
701
702
|
});
|
|
702
703
|
}
|
|
703
704
|
});
|
|
704
|
-
getConnectedDeviceIds(getBluetoothServiceUuids()).then(devices => {
|
|
705
|
+
getConnectedDeviceIds(reactNative.Platform.OS === 'ios' ? getBluetoothServiceUuids() : []).then(devices => {
|
|
705
706
|
for (const device of devices) {
|
|
706
|
-
|
|
707
|
-
|
|
707
|
+
const localName = 'localName' in device && typeof device.localName === 'string'
|
|
708
|
+
? device.localName
|
|
709
|
+
: null;
|
|
710
|
+
const isFindMyPeripheral = hdShared.isPro2FindMyAdvertisementName(device.name) ||
|
|
711
|
+
hdShared.isPro2FindMyAdvertisementName(localName);
|
|
712
|
+
if (!isFindMyPeripheral &&
|
|
713
|
+
hdShared.isOnekeyBluetoothDevice({
|
|
714
|
+
id: device.id,
|
|
715
|
+
name: device.name,
|
|
716
|
+
localName,
|
|
717
|
+
serviceUuids: device.serviceUUIDs,
|
|
718
|
+
})) {
|
|
719
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('search connected peripheral: ', device.id);
|
|
720
|
+
addDevice(device);
|
|
721
|
+
}
|
|
708
722
|
}
|
|
709
723
|
});
|
|
710
724
|
const addDevice = (device) => {
|
|
@@ -716,6 +730,12 @@ class ReactNativeBleTransport {
|
|
|
716
730
|
this.deviceProtocolHints.set(device.id, protocolHint);
|
|
717
731
|
}
|
|
718
732
|
deviceList.push(Object.assign(Object.assign({}, device), { name: displayName, commType: 'ble' }));
|
|
733
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] OneKey BLE device discovered', {
|
|
734
|
+
deviceId: device.id,
|
|
735
|
+
name: displayName,
|
|
736
|
+
serviceUUIDs: device.serviceUUIDs,
|
|
737
|
+
protocolHint,
|
|
738
|
+
});
|
|
719
739
|
}
|
|
720
740
|
};
|
|
721
741
|
timer.timeout(() => {
|
|
@@ -725,6 +745,57 @@ class ReactNativeBleTransport {
|
|
|
725
745
|
}));
|
|
726
746
|
});
|
|
727
747
|
}
|
|
748
|
+
installTransportForAcquire(uuid, device, characteristics) {
|
|
749
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
750
|
+
const { writeCharacteristic, notifyCharacteristic } = characteristics !== null && characteristics !== void 0 ? characteristics : (yield this.resolveCharacteristicsWithTimeout(uuid, device));
|
|
751
|
+
const transport$1 = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
|
|
752
|
+
transport$1.mtuSize = typeof device.mtu === 'number' ? device.mtu : undefined;
|
|
753
|
+
const monitorToken = this.nextMonitorToken;
|
|
754
|
+
this.nextMonitorToken += 1;
|
|
755
|
+
const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
|
|
756
|
+
transport$1.monitorToken = monitorToken;
|
|
757
|
+
transport$1.notifyTransactionId = notifyTransactionId;
|
|
758
|
+
this.monitorTokens.set(uuid, monitorToken);
|
|
759
|
+
transport$1.notifySubscription = this._monitorCharacteristic(transport$1.notifyCharacteristic, uuid, monitorToken, notifyTransactionId);
|
|
760
|
+
transportCache[uuid] = transport$1;
|
|
761
|
+
this.protocolV2HighVolumeLogSignatures.set(uuid, new Set());
|
|
762
|
+
this.protocolV2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES));
|
|
763
|
+
if (reactNative.Platform.OS === 'ios') {
|
|
764
|
+
yield new Promise(resolve => {
|
|
765
|
+
setTimeout(resolve, IOS_NOTIFY_READY_DELAY_MS);
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
else if (reactNative.Platform.OS === 'android') {
|
|
769
|
+
yield delay(ANDROID_NOTIFY_READY_DELAY_MS);
|
|
770
|
+
}
|
|
771
|
+
const initialMtu = transport$1.mtuSize;
|
|
772
|
+
let refreshAttempts = 0;
|
|
773
|
+
if ((reactNative.Platform.OS === 'ios' || reactNative.Platform.OS === 'android') &&
|
|
774
|
+
shouldRefreshNegotiatedMtu(transport$1.mtuSize)) {
|
|
775
|
+
refreshAttempts += 1;
|
|
776
|
+
let refreshedDevice = yield requestNegotiatedMtu(transport$1.device, 'servicesAndNotifyReady', 1);
|
|
777
|
+
transport$1.device = refreshedDevice;
|
|
778
|
+
transport$1.mtuSize =
|
|
779
|
+
typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport$1.mtuSize;
|
|
780
|
+
if (shouldRefreshNegotiatedMtu(transport$1.mtuSize)) {
|
|
781
|
+
yield delay(BLE_MTU_REFRESH_RETRY_DELAY_MS);
|
|
782
|
+
refreshAttempts += 1;
|
|
783
|
+
refreshedDevice = yield requestNegotiatedMtu(transport$1.device, 'servicesAndNotifyReady', 2);
|
|
784
|
+
transport$1.device = refreshedDevice;
|
|
785
|
+
transport$1.mtuSize =
|
|
786
|
+
typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport$1.mtuSize;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE MTU ready', {
|
|
790
|
+
platform: reactNative.Platform.OS,
|
|
791
|
+
requested: getRequestedBleMtu(),
|
|
792
|
+
initial: initialMtu,
|
|
793
|
+
actual: transport$1.mtuSize,
|
|
794
|
+
refreshAttempts,
|
|
795
|
+
});
|
|
796
|
+
return transport$1;
|
|
797
|
+
});
|
|
798
|
+
}
|
|
728
799
|
acquire(input) {
|
|
729
800
|
var _a, _b;
|
|
730
801
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -749,8 +820,8 @@ class ReactNativeBleTransport {
|
|
|
749
820
|
if (forceCleanRunPromise && this.runPromise) {
|
|
750
821
|
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
|
|
751
822
|
this.runPromise.reject(error);
|
|
752
|
-
this.rejectAllProtocolV2Frames(error);
|
|
753
823
|
this.runPromise = null;
|
|
824
|
+
this.runPromiseDeviceId = null;
|
|
754
825
|
Log === null || Log === void 0 ? void 0 : Log.debug('Force clean Bluetooth run promise, forceCleanRunPromise: ', forceCleanRunPromise);
|
|
755
826
|
}
|
|
756
827
|
const blePlxManager = yield this.getPlxManager();
|
|
@@ -783,14 +854,17 @@ class ReactNativeBleTransport {
|
|
|
783
854
|
if (!device) {
|
|
784
855
|
Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device: ', uuid);
|
|
785
856
|
try {
|
|
786
|
-
device = yield blePlxManager.connectToDevice(uuid, connectOptions);
|
|
857
|
+
device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, connectOptions));
|
|
787
858
|
}
|
|
788
859
|
catch (e) {
|
|
789
860
|
Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device has error: ', e);
|
|
861
|
+
if (isConnectTimeoutError(e)) {
|
|
862
|
+
throw e;
|
|
863
|
+
}
|
|
790
864
|
if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
|
|
791
865
|
e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
|
|
792
866
|
Log === null || Log === void 0 ? void 0 : Log.debug('first try to reconnect without params');
|
|
793
|
-
device = yield blePlxManager.connectToDevice(uuid);
|
|
867
|
+
device = yield this.connectWithTimeout(uuid, () => blePlxManager.connectToDevice(uuid, fallbackConnectOptions));
|
|
794
868
|
}
|
|
795
869
|
else if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
|
|
796
870
|
Log === null || Log === void 0 ? void 0 : Log.debug('device already connected');
|
|
@@ -806,23 +880,27 @@ class ReactNativeBleTransport {
|
|
|
806
880
|
}
|
|
807
881
|
if (!(yield device.isConnected())) {
|
|
808
882
|
Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device: ', uuid);
|
|
883
|
+
const disconnectedDevice = device;
|
|
809
884
|
try {
|
|
810
|
-
device = yield
|
|
885
|
+
device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(connectOptions));
|
|
811
886
|
}
|
|
812
887
|
catch (e) {
|
|
813
888
|
Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device has error: ', e);
|
|
889
|
+
if (isConnectTimeoutError(e)) {
|
|
890
|
+
throw e;
|
|
891
|
+
}
|
|
814
892
|
if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
|
|
815
893
|
e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
|
|
816
894
|
Log === null || Log === void 0 ? void 0 : Log.debug('second try to reconnect without params');
|
|
817
895
|
try {
|
|
818
|
-
device = yield
|
|
896
|
+
device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
|
|
819
897
|
}
|
|
820
898
|
catch (e) {
|
|
821
899
|
Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect error: ', e);
|
|
822
900
|
if (e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
|
|
823
901
|
Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect');
|
|
824
|
-
yield
|
|
825
|
-
device = yield
|
|
902
|
+
yield disconnectedDevice.cancelConnection();
|
|
903
|
+
device = yield this.connectWithTimeout(uuid, () => disconnectedDevice.connect(fallbackConnectOptions));
|
|
826
904
|
}
|
|
827
905
|
}
|
|
828
906
|
}
|
|
@@ -831,44 +909,35 @@ class ReactNativeBleTransport {
|
|
|
831
909
|
}
|
|
832
910
|
}
|
|
833
911
|
}
|
|
834
|
-
device = yield
|
|
835
|
-
const
|
|
912
|
+
device = yield resolveNegotiatedMtu(device);
|
|
913
|
+
const acquiredDevice = device;
|
|
914
|
+
const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristicsWithTimeout(uuid, acquiredDevice);
|
|
836
915
|
const protocolHint = expectedProtocol
|
|
837
916
|
? undefined
|
|
838
|
-
: (_a = this.deviceProtocolHints.get(uuid)) !== null &&
|
|
917
|
+
: (_b = (_a = input.protocolHint) !== null && _a !== void 0 ? _a : this.deviceProtocolHints.get(uuid)) !== null && _b !== void 0 ? _b : inferProtocolHintFromDeviceName(getDeviceDisplayName(acquiredDevice));
|
|
839
918
|
yield this.release(uuid, true);
|
|
840
919
|
if (protocolHint) {
|
|
841
920
|
this.deviceProtocolHints.set(uuid, protocolHint);
|
|
842
921
|
}
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
yield new Promise(resolve => {
|
|
858
|
-
setTimeout(resolve, IOS_NOTIFY_READY_DELAY_MS);
|
|
859
|
-
});
|
|
922
|
+
yield this.installTransportForAcquire(uuid, acquiredDevice, {
|
|
923
|
+
writeCharacteristic,
|
|
924
|
+
notifyCharacteristic,
|
|
925
|
+
});
|
|
926
|
+
try {
|
|
927
|
+
const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint, () => __awaiter(this, void 0, void 0, function* () {
|
|
928
|
+
yield this.installTransportForAcquire(uuid, acquiredDevice);
|
|
929
|
+
}));
|
|
930
|
+
const currentTransport = transportCache[uuid];
|
|
931
|
+
if (!currentTransport) {
|
|
932
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
|
|
933
|
+
}
|
|
934
|
+
this.attachDisconnectSubscription(currentTransport, currentTransport.device, uuid);
|
|
935
|
+
return { uuid, protocolType };
|
|
860
936
|
}
|
|
861
|
-
|
|
862
|
-
yield
|
|
937
|
+
catch (error) {
|
|
938
|
+
yield this.release(uuid, true);
|
|
939
|
+
throw error;
|
|
863
940
|
}
|
|
864
|
-
const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint);
|
|
865
|
-
(_b = this.emitter) === null || _b === void 0 ? void 0 : _b.emit('device-connect', {
|
|
866
|
-
name: device.name,
|
|
867
|
-
id: device.id,
|
|
868
|
-
connectId: device.id,
|
|
869
|
-
});
|
|
870
|
-
this.attachDisconnectSubscription(transport$1, device, uuid);
|
|
871
|
-
return { uuid, protocolType };
|
|
872
941
|
});
|
|
873
942
|
}
|
|
874
943
|
_monitorCharacteristic(characteristic, uuid, monitorToken, notifyTransactionId) {
|
|
@@ -887,7 +956,7 @@ class ReactNativeBleTransport {
|
|
|
887
956
|
Log === null || Log === void 0 ? void 0 : Log.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
|
|
888
957
|
return;
|
|
889
958
|
}
|
|
890
|
-
if (this.
|
|
959
|
+
if (this.getActiveProtocol(uuid) === 'V2') {
|
|
891
960
|
let errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
|
|
892
961
|
if ((_a = error.reason) === null || _a === void 0 ? void 0 : _a.includes('The connection has timed out unexpectedly')) {
|
|
893
962
|
errorCode = hdShared.HardwareErrorCode.BleTimeoutError;
|
|
@@ -905,7 +974,7 @@ class ReactNativeBleTransport {
|
|
|
905
974
|
this.rejectProtocolV2Frames(uuid, hdShared.ERRORS.TypedError(errorCode));
|
|
906
975
|
return;
|
|
907
976
|
}
|
|
908
|
-
if (this.runPromise) {
|
|
977
|
+
if (this.runPromise && this.runPromiseDeviceId === uuid) {
|
|
909
978
|
let ERROR = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
|
|
910
979
|
if ((_h = error.reason) === null || _h === void 0 ? void 0 : _h.includes('The connection has timed out unexpectedly')) {
|
|
911
980
|
ERROR = hdShared.HardwareErrorCode.BleTimeoutError;
|
|
@@ -920,13 +989,11 @@ class ReactNativeBleTransport {
|
|
|
920
989
|
((_p = error.reason) === null || _p === void 0 ? void 0 : _p.includes('notify change failed for device'))) {
|
|
921
990
|
const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure);
|
|
922
991
|
this.runPromise.reject(notifyError);
|
|
923
|
-
this.rejectAllProtocolV2Frames(notifyError);
|
|
924
992
|
Log === null || Log === void 0 ? void 0 : Log.debug(`${hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure} ${error.message} ${error.reason}`);
|
|
925
993
|
return;
|
|
926
994
|
}
|
|
927
995
|
const notifyError = hdShared.ERRORS.TypedError(ERROR);
|
|
928
996
|
this.runPromise.reject(notifyError);
|
|
929
|
-
this.rejectAllProtocolV2Frames(notifyError);
|
|
930
997
|
Log === null || Log === void 0 ? void 0 : Log.debug(': monitor notify error, and has unreleased Promise', Error);
|
|
931
998
|
}
|
|
932
999
|
return;
|
|
@@ -940,7 +1007,7 @@ class ReactNativeBleTransport {
|
|
|
940
1007
|
}
|
|
941
1008
|
try {
|
|
942
1009
|
const data = buffer.Buffer.from(c.value, 'base64');
|
|
943
|
-
const protocol = this.
|
|
1010
|
+
const protocol = this.getActiveProtocol(uuid);
|
|
944
1011
|
if (!protocol) {
|
|
945
1012
|
Log === null || Log === void 0 ? void 0 : Log.debug('monitor data ignored before protocol detection: ', uuid);
|
|
946
1013
|
return;
|
|
@@ -960,16 +1027,18 @@ class ReactNativeBleTransport {
|
|
|
960
1027
|
const value = buffer.Buffer.from(buffer$1);
|
|
961
1028
|
bufferLength = 0;
|
|
962
1029
|
buffer$1 = [];
|
|
963
|
-
(
|
|
1030
|
+
if (this.runPromiseDeviceId === uuid) {
|
|
1031
|
+
(_q = this.runPromise) === null || _q === void 0 ? void 0 : _q.resolve(value.toString('hex'));
|
|
1032
|
+
}
|
|
964
1033
|
}
|
|
965
1034
|
}
|
|
966
1035
|
catch (error) {
|
|
967
1036
|
Log === null || Log === void 0 ? void 0 : Log.debug('monitor data error: ', error);
|
|
968
1037
|
const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
|
|
969
|
-
if (this.
|
|
1038
|
+
if (this.getActiveProtocol(uuid) === 'V2') {
|
|
970
1039
|
this.rejectProtocolV2Frames(uuid, notifyError);
|
|
971
1040
|
}
|
|
972
|
-
else {
|
|
1041
|
+
else if (this.runPromiseDeviceId === uuid) {
|
|
973
1042
|
(_r = this.runPromise) === null || _r === void 0 ? void 0 : _r.reject(notifyError);
|
|
974
1043
|
}
|
|
975
1044
|
}
|
|
@@ -977,15 +1046,21 @@ class ReactNativeBleTransport {
|
|
|
977
1046
|
return subscription;
|
|
978
1047
|
}
|
|
979
1048
|
release(uuid, onclose = false) {
|
|
1049
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1050
|
+
yield this.protocolV2Links.invalidateLink(uuid, 'React Native BLE transport released');
|
|
1051
|
+
return this.releaseNative(uuid, onclose);
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
releaseNative(uuid, onclose = false) {
|
|
980
1055
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
981
1056
|
return __awaiter(this, void 0, void 0, function* () {
|
|
982
1057
|
const transport = transportCache[uuid];
|
|
983
|
-
|
|
984
|
-
if (this.runPromise) {
|
|
1058
|
+
if (this.runPromise && this.runPromiseDeviceId === uuid) {
|
|
985
1059
|
const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
|
|
986
1060
|
this.runPromise.reject(error);
|
|
987
1061
|
this.runPromise = null;
|
|
988
|
-
this.
|
|
1062
|
+
this.runPromiseDeviceId = null;
|
|
1063
|
+
this.rejectProtocolV2Frames(uuid, error);
|
|
989
1064
|
}
|
|
990
1065
|
else {
|
|
991
1066
|
this.resetProtocolV2Frames(uuid);
|
|
@@ -995,6 +1070,7 @@ class ReactNativeBleTransport {
|
|
|
995
1070
|
this.resetProtocolV2Frames(uuid);
|
|
996
1071
|
return Promise.resolve(true);
|
|
997
1072
|
}
|
|
1073
|
+
yield this.restoreAndroidConnectionPriority(uuid, transport);
|
|
998
1074
|
if (transport) {
|
|
999
1075
|
if (this.monitorTokens.get(uuid) === transport.monitorToken) {
|
|
1000
1076
|
this.monitorTokens.delete(uuid);
|
|
@@ -1015,7 +1091,9 @@ class ReactNativeBleTransport {
|
|
|
1015
1091
|
}
|
|
1016
1092
|
delete transportCache[uuid];
|
|
1017
1093
|
}
|
|
1094
|
+
this.protocolV2HighVolumeLogSignatures.delete(uuid);
|
|
1018
1095
|
this.deviceProtocol.delete(uuid);
|
|
1096
|
+
this.probingProtocols.delete(uuid);
|
|
1019
1097
|
(_f = this.protocolV2Assemblers.get(uuid)) === null || _f === void 0 ? void 0 : _f.reset();
|
|
1020
1098
|
this.protocolV2Assemblers.delete(uuid);
|
|
1021
1099
|
this.resetProtocolV2Frames(uuid);
|
|
@@ -1045,23 +1123,11 @@ class ReactNativeBleTransport {
|
|
|
1045
1123
|
if (!protocol) {
|
|
1046
1124
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${uuid}`);
|
|
1047
1125
|
}
|
|
1048
|
-
|
|
1049
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native', 'call-', ' name: ', name, ' data: ', {
|
|
1050
|
-
file_name: data === null || data === void 0 ? void 0 : data.file_name,
|
|
1051
|
-
hash: data === null || data === void 0 ? void 0 : data.hash,
|
|
1052
|
-
});
|
|
1053
|
-
}
|
|
1054
|
-
else if (transport.LogBlockCommand.has(name)) {
|
|
1055
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native', 'call-', ' name: ', name, ' protocol: ', protocol);
|
|
1056
|
-
}
|
|
1057
|
-
else {
|
|
1058
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native', 'call-', ' name: ', name, ' data: ', data, ' protocol: ', protocol);
|
|
1059
|
-
}
|
|
1126
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('transport call', transport.createTransportCallLog(name, protocol, data));
|
|
1060
1127
|
if (protocol === 'V2') {
|
|
1061
1128
|
return this.callProtocolV2(uuid, name, data, options);
|
|
1062
1129
|
}
|
|
1063
1130
|
const forceRun = name === 'Initialize' || name === 'Cancel';
|
|
1064
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native call this.runPromise', this.runPromise);
|
|
1065
1131
|
if (this.runPromise && !forceRun) {
|
|
1066
1132
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportCallInProgress);
|
|
1067
1133
|
}
|
|
@@ -1076,7 +1142,19 @@ class ReactNativeBleTransport {
|
|
|
1076
1142
|
const transport = this.getCachedTransport(uuid);
|
|
1077
1143
|
const runPromise = hdShared.createDeferred();
|
|
1078
1144
|
runPromise.promise.catch(() => undefined);
|
|
1145
|
+
const supersededRunPromise = this.runPromise;
|
|
1146
|
+
if (supersededRunPromise) {
|
|
1147
|
+
supersededRunPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise));
|
|
1148
|
+
}
|
|
1079
1149
|
this.runPromise = runPromise;
|
|
1150
|
+
this.runPromiseDeviceId = uuid;
|
|
1151
|
+
const releaseOwnershipIfCurrent = () => {
|
|
1152
|
+
if (this.runPromise === runPromise) {
|
|
1153
|
+
this.runPromise = null;
|
|
1154
|
+
this.runPromiseDeviceId = null;
|
|
1155
|
+
}
|
|
1156
|
+
};
|
|
1157
|
+
const isCurrentOwner = () => this.runPromise === runPromise;
|
|
1080
1158
|
const messages = this._messages;
|
|
1081
1159
|
const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
|
|
1082
1160
|
let timeout;
|
|
@@ -1097,6 +1175,9 @@ class ReactNativeBleTransport {
|
|
|
1097
1175
|
}
|
|
1098
1176
|
catch (e) {
|
|
1099
1177
|
onError(e);
|
|
1178
|
+
if (isWedgedWriteError(e)) {
|
|
1179
|
+
throw e;
|
|
1180
|
+
}
|
|
1100
1181
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
|
|
1101
1182
|
}
|
|
1102
1183
|
}
|
|
@@ -1124,6 +1205,9 @@ class ReactNativeBleTransport {
|
|
|
1124
1205
|
}
|
|
1125
1206
|
catch (e) {
|
|
1126
1207
|
onError(e);
|
|
1208
|
+
if (isWedgedWriteError(e)) {
|
|
1209
|
+
throw e;
|
|
1210
|
+
}
|
|
1127
1211
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
|
|
1128
1212
|
}
|
|
1129
1213
|
}
|
|
@@ -1134,13 +1218,13 @@ class ReactNativeBleTransport {
|
|
|
1134
1218
|
});
|
|
1135
1219
|
}
|
|
1136
1220
|
if (name === 'EmmcFileWrite') {
|
|
1137
|
-
yield writeChunkedData(buffers, data => transport.writeWithRetry(
|
|
1138
|
-
|
|
1221
|
+
yield writeChunkedData(buffers, data => this.writeBlePacket(uuid, data, payload => transport.writeWithRetry(payload), isCurrentOwner), e => {
|
|
1222
|
+
releaseOwnershipIfCurrent();
|
|
1139
1223
|
Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
|
|
1140
1224
|
});
|
|
1141
1225
|
}
|
|
1142
1226
|
else if (name === 'FirmwareUpload') {
|
|
1143
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport]
|
|
1227
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Firmware upload transport configured', {
|
|
1144
1228
|
packetCapacity: FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY,
|
|
1145
1229
|
burstSize: FIRMWARE_UPLOAD_WRITE_BURST_SIZE,
|
|
1146
1230
|
pauseMs: FIRMWARE_UPLOAD_WRITE_PAUSE_MS,
|
|
@@ -1151,7 +1235,7 @@ class ReactNativeBleTransport {
|
|
|
1151
1235
|
let attempt = 0;
|
|
1152
1236
|
while (true) {
|
|
1153
1237
|
try {
|
|
1154
|
-
yield transport.
|
|
1238
|
+
yield this.writeBlePacket(uuid, data, payload => transport.writeWithRetry(payload), isCurrentOwner);
|
|
1155
1239
|
return;
|
|
1156
1240
|
}
|
|
1157
1241
|
catch (error) {
|
|
@@ -1159,36 +1243,18 @@ class ReactNativeBleTransport {
|
|
|
1159
1243
|
if (!retryType || attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
|
|
1160
1244
|
throw error;
|
|
1161
1245
|
}
|
|
1162
|
-
const
|
|
1163
|
-
const delayMs = shouldReconnect
|
|
1164
|
-
? FIRMWARE_UPLOAD_RECONNECT_RETRY_DELAY_MS
|
|
1165
|
-
: resolveFirmwareUploadRetryDelay(attempt);
|
|
1246
|
+
const delayMs = resolveFirmwareUploadRetryDelay(attempt);
|
|
1166
1247
|
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] FirmwareUpload write retry:', {
|
|
1167
1248
|
attempt: attempt + 1,
|
|
1168
1249
|
delayMs,
|
|
1169
|
-
reconnect: shouldReconnect,
|
|
1170
1250
|
error,
|
|
1171
1251
|
});
|
|
1172
|
-
if (shouldReconnect) {
|
|
1173
|
-
this.firmwareUploadWriteRecoveryIds.add(uuid);
|
|
1174
|
-
}
|
|
1175
1252
|
yield delay(delayMs);
|
|
1176
1253
|
attempt += 1;
|
|
1177
|
-
if (shouldReconnect) {
|
|
1178
|
-
try {
|
|
1179
|
-
yield this.reconnectFirmwareUploadTransport(uuid, transport);
|
|
1180
|
-
}
|
|
1181
|
-
catch (e) {
|
|
1182
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] FirmwareUpload reconnect error:', e);
|
|
1183
|
-
if (attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
|
|
1184
|
-
throw e;
|
|
1185
|
-
}
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
1254
|
}
|
|
1189
1255
|
}
|
|
1190
1256
|
}), e => {
|
|
1191
|
-
|
|
1257
|
+
releaseOwnershipIfCurrent();
|
|
1192
1258
|
Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
|
|
1193
1259
|
});
|
|
1194
1260
|
}
|
|
@@ -1196,11 +1262,17 @@ class ReactNativeBleTransport {
|
|
|
1196
1262
|
for (const o of buffers) {
|
|
1197
1263
|
const outData = o.toString('base64');
|
|
1198
1264
|
try {
|
|
1199
|
-
|
|
1265
|
+
const shouldUseWriteWithResponse = reactNative.Platform.OS === 'ios' && transport.writeCharacteristic.isWritableWithResponse;
|
|
1266
|
+
yield this.writeBlePacket(uuid, outData, payload => shouldUseWriteWithResponse
|
|
1267
|
+
? transport.writeCharacteristic.writeWithResponse(payload)
|
|
1268
|
+
: transport.writeCharacteristic.writeWithoutResponse(payload), isCurrentOwner);
|
|
1200
1269
|
}
|
|
1201
1270
|
catch (e) {
|
|
1202
1271
|
Log === null || Log === void 0 ? void 0 : Log.debug('writeCharacteristic write error: ', e);
|
|
1203
|
-
|
|
1272
|
+
releaseOwnershipIfCurrent();
|
|
1273
|
+
if (isWedgedWriteError(e)) {
|
|
1274
|
+
throw e;
|
|
1275
|
+
}
|
|
1204
1276
|
if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected) {
|
|
1205
1277
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded);
|
|
1206
1278
|
}
|
|
@@ -1229,17 +1301,23 @@ class ReactNativeBleTransport {
|
|
|
1229
1301
|
if (typeof response !== 'string') {
|
|
1230
1302
|
throw new Error('Returning data is not string.');
|
|
1231
1303
|
}
|
|
1232
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('receive data: ', response);
|
|
1233
1304
|
const jsonData = ProtocolV1.decodeMessage(messages, response);
|
|
1234
1305
|
return check.call(jsonData);
|
|
1235
1306
|
}
|
|
1236
1307
|
catch (e) {
|
|
1237
|
-
if (name === '
|
|
1238
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1
|
|
1308
|
+
if (name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS) {
|
|
1309
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe call failed:', e);
|
|
1239
1310
|
}
|
|
1240
1311
|
else {
|
|
1241
1312
|
Log === null || Log === void 0 ? void 0 : Log.error('call error: ', e);
|
|
1242
1313
|
}
|
|
1314
|
+
const isProbeTimeout = name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS;
|
|
1315
|
+
const isStaleCall = this.runPromise !== runPromise;
|
|
1316
|
+
if (!isProbeTimeout &&
|
|
1317
|
+
!isStaleCall &&
|
|
1318
|
+
(e === null || e === void 0 ? void 0 : e.errorCode) === hdShared.HardwareErrorCode.BleTimeoutError) {
|
|
1319
|
+
yield this.disconnect(uuid);
|
|
1320
|
+
}
|
|
1243
1321
|
throw e;
|
|
1244
1322
|
}
|
|
1245
1323
|
finally {
|
|
@@ -1247,6 +1325,7 @@ class ReactNativeBleTransport {
|
|
|
1247
1325
|
clearTimeout(timeout);
|
|
1248
1326
|
if (this.runPromise === runPromise) {
|
|
1249
1327
|
this.runPromise = null;
|
|
1328
|
+
this.runPromiseDeviceId = null;
|
|
1250
1329
|
}
|
|
1251
1330
|
}
|
|
1252
1331
|
});
|
|
@@ -1257,9 +1336,9 @@ class ReactNativeBleTransport {
|
|
|
1257
1336
|
disconnect(session) {
|
|
1258
1337
|
var _a, _b, _c, _d, _e;
|
|
1259
1338
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1260
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native transport resetSession: ', session);
|
|
1261
1339
|
yield this.protocolV2Links.invalidateLink(session, 'React Native BLE transport disconnected');
|
|
1262
1340
|
const transport = transportCache[session];
|
|
1341
|
+
const monitorToken = (_a = transport === null || transport === void 0 ? void 0 : transport.monitorToken) !== null && _a !== void 0 ? _a : this.monitorTokens.get(session);
|
|
1263
1342
|
if (transport === null || transport === void 0 ? void 0 : transport.disconnectSubscription) {
|
|
1264
1343
|
try {
|
|
1265
1344
|
Log === null || Log === void 0 ? void 0 : Log.debug('disconnect: removing disconnect subscription');
|
|
@@ -1272,7 +1351,7 @@ class ReactNativeBleTransport {
|
|
|
1272
1351
|
}
|
|
1273
1352
|
if (transport === null || transport === void 0 ? void 0 : transport.notifySubscription) {
|
|
1274
1353
|
try {
|
|
1275
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('disconnect: removing notify subscription, characteristic: ', (
|
|
1354
|
+
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);
|
|
1276
1355
|
transport.notifySubscription.remove();
|
|
1277
1356
|
transport.notifySubscription = undefined;
|
|
1278
1357
|
}
|
|
@@ -1282,7 +1361,7 @@ class ReactNativeBleTransport {
|
|
|
1282
1361
|
}
|
|
1283
1362
|
if (session) {
|
|
1284
1363
|
try {
|
|
1285
|
-
yield ((
|
|
1364
|
+
yield ((_c = this.blePlxManager) === null || _c === void 0 ? void 0 : _c.cancelTransaction(session));
|
|
1286
1365
|
}
|
|
1287
1366
|
catch (e) {
|
|
1288
1367
|
Log === null || Log === void 0 ? void 0 : Log.debug('resetSession: cancel transaction error (ignored): ', (e === null || e === void 0 ? void 0 : e.message) || e);
|
|
@@ -1297,7 +1376,7 @@ class ReactNativeBleTransport {
|
|
|
1297
1376
|
}
|
|
1298
1377
|
}
|
|
1299
1378
|
try {
|
|
1300
|
-
yield ((
|
|
1379
|
+
yield ((_d = this.blePlxManager) === null || _d === void 0 ? void 0 : _d.cancelDeviceConnection(session));
|
|
1301
1380
|
}
|
|
1302
1381
|
catch (e) {
|
|
1303
1382
|
Log === null || Log === void 0 ? void 0 : Log.debug('resetSession: manager.cancelDeviceConnection error (ignored): ', (e === null || e === void 0 ? void 0 : e.message) || e);
|
|
@@ -1306,19 +1385,21 @@ class ReactNativeBleTransport {
|
|
|
1306
1385
|
delete transportCache[session];
|
|
1307
1386
|
}
|
|
1308
1387
|
this.deviceProtocol.delete(session);
|
|
1388
|
+
this.probingProtocols.delete(session);
|
|
1309
1389
|
this.deviceProtocolHints.delete(session);
|
|
1390
|
+
this.sessionProtocols.delete(session);
|
|
1391
|
+
this.protocolReprobeFailures.delete(session);
|
|
1310
1392
|
this.protocolV2Assemblers.delete(session);
|
|
1311
1393
|
this.resetProtocolV2Frames(session);
|
|
1312
1394
|
try {
|
|
1313
|
-
(
|
|
1314
|
-
name: (_e = transport === null || transport === void 0 ? void 0 : transport.device) === null || _e === void 0 ? void 0 : _e.name,
|
|
1315
|
-
id: session,
|
|
1316
|
-
connectId: session,
|
|
1317
|
-
});
|
|
1395
|
+
this.emitDeviceDisconnect(session, (_e = transport === null || transport === void 0 ? void 0 : transport.device) === null || _e === void 0 ? void 0 : _e.name, monitorToken);
|
|
1318
1396
|
}
|
|
1319
1397
|
catch (e) {
|
|
1320
1398
|
Log === null || Log === void 0 ? void 0 : Log.error('resetSession: emit disconnect event error: ', e);
|
|
1321
1399
|
}
|
|
1400
|
+
if (monitorToken !== undefined && this.monitorTokens.get(session) === monitorToken) {
|
|
1401
|
+
this.monitorTokens.delete(session);
|
|
1402
|
+
}
|
|
1322
1403
|
yield new Promise(resolve => setTimeout(() => resolve(), 100));
|
|
1323
1404
|
});
|
|
1324
1405
|
}
|
|
@@ -1326,6 +1407,92 @@ class ReactNativeBleTransport {
|
|
|
1326
1407
|
Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native transport cancel');
|
|
1327
1408
|
if (this.runPromise) ;
|
|
1328
1409
|
this.runPromise = null;
|
|
1410
|
+
this.runPromiseDeviceId = null;
|
|
1411
|
+
}
|
|
1412
|
+
connectWithTimeout(uuid, connect) {
|
|
1413
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1414
|
+
let timer;
|
|
1415
|
+
let timedOut = false;
|
|
1416
|
+
const pending = connect();
|
|
1417
|
+
pending.catch(() => undefined);
|
|
1418
|
+
try {
|
|
1419
|
+
const result = yield Promise.race([
|
|
1420
|
+
pending,
|
|
1421
|
+
new Promise((_, reject) => {
|
|
1422
|
+
timer = setTimeout(() => {
|
|
1423
|
+
timedOut = true;
|
|
1424
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `BLE connect timeout after ${BLE_CONNECT_TIMEOUT_MS}ms for ${uuid}`));
|
|
1425
|
+
}, BLE_CONNECT_TIMEOUT_MS);
|
|
1426
|
+
}),
|
|
1427
|
+
]);
|
|
1428
|
+
return result;
|
|
1429
|
+
}
|
|
1430
|
+
catch (error) {
|
|
1431
|
+
if (timedOut || isNativeOperationTimeoutError(error)) {
|
|
1432
|
+
this.abandonStalledConnection(uuid, timedOut ? 'connect-backstop' : 'connect-native');
|
|
1433
|
+
}
|
|
1434
|
+
throw error;
|
|
1435
|
+
}
|
|
1436
|
+
finally {
|
|
1437
|
+
if (timer)
|
|
1438
|
+
clearTimeout(timer);
|
|
1439
|
+
}
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1442
|
+
resolveCharacteristicsWithTimeout(uuid, device) {
|
|
1443
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1444
|
+
let timer;
|
|
1445
|
+
let timedOut = false;
|
|
1446
|
+
const pending = this.resolveCharacteristics(device);
|
|
1447
|
+
pending.catch(() => undefined);
|
|
1448
|
+
try {
|
|
1449
|
+
const result = yield Promise.race([
|
|
1450
|
+
pending,
|
|
1451
|
+
new Promise((_, reject) => {
|
|
1452
|
+
timer = setTimeout(() => {
|
|
1453
|
+
timedOut = true;
|
|
1454
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `BLE GATT setup timeout after ${BLE_GATT_SETUP_TIMEOUT_MS}ms for ${uuid}`));
|
|
1455
|
+
}, BLE_GATT_SETUP_TIMEOUT_MS);
|
|
1456
|
+
}),
|
|
1457
|
+
]);
|
|
1458
|
+
this.connectionSetupTimeoutCounts.delete(uuid);
|
|
1459
|
+
return result;
|
|
1460
|
+
}
|
|
1461
|
+
catch (error) {
|
|
1462
|
+
if (timedOut || isNativeOperationTimeoutError(error)) {
|
|
1463
|
+
this.abandonStalledConnection(uuid, timedOut ? 'gatt-backstop' : 'gatt-native');
|
|
1464
|
+
}
|
|
1465
|
+
throw error;
|
|
1466
|
+
}
|
|
1467
|
+
finally {
|
|
1468
|
+
if (timer)
|
|
1469
|
+
clearTimeout(timer);
|
|
1470
|
+
}
|
|
1471
|
+
});
|
|
1472
|
+
}
|
|
1473
|
+
abandonStalledConnection(uuid, stage) {
|
|
1474
|
+
var _a, _b;
|
|
1475
|
+
const timeouts = ((_a = this.connectionSetupTimeoutCounts.get(uuid)) !== null && _a !== void 0 ? _a : 0) + 1;
|
|
1476
|
+
this.connectionSetupTimeoutCounts.set(uuid, timeouts);
|
|
1477
|
+
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE setup timed out:', uuid, {
|
|
1478
|
+
stage,
|
|
1479
|
+
setupTimeoutsSinceSuccess: timeouts,
|
|
1480
|
+
});
|
|
1481
|
+
(_b = this.blePlxManager) === null || _b === void 0 ? void 0 : _b.cancelDeviceConnection(uuid).catch(() => {
|
|
1482
|
+
});
|
|
1483
|
+
const stalled = transportCache[uuid];
|
|
1484
|
+
if (stalled) {
|
|
1485
|
+
delete transportCache[uuid];
|
|
1486
|
+
}
|
|
1487
|
+
this.deviceProtocol.delete(uuid);
|
|
1488
|
+
this.probingProtocols.delete(uuid);
|
|
1489
|
+
this.protocolV2Assemblers.delete(uuid);
|
|
1490
|
+
this.resetProtocolV2Frames(uuid);
|
|
1491
|
+
if (timeouts >= BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD) {
|
|
1492
|
+
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE setup wedged repeatedly, resetting BLE manager');
|
|
1493
|
+
this.resetPlxManager();
|
|
1494
|
+
this.connectionSetupTimeoutCounts.delete(uuid);
|
|
1495
|
+
}
|
|
1329
1496
|
}
|
|
1330
1497
|
getCachedTransport(uuid) {
|
|
1331
1498
|
const transport = transportCache[uuid];
|
|
@@ -1334,46 +1501,179 @@ class ReactNativeBleTransport {
|
|
|
1334
1501
|
}
|
|
1335
1502
|
return transport;
|
|
1336
1503
|
}
|
|
1504
|
+
writeBlePacket(uuid, data, write, isCurrentOwner) {
|
|
1505
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1506
|
+
let timer;
|
|
1507
|
+
let timedOut = false;
|
|
1508
|
+
try {
|
|
1509
|
+
yield Promise.race([
|
|
1510
|
+
write(data),
|
|
1511
|
+
new Promise((_, reject) => {
|
|
1512
|
+
timer = setTimeout(() => {
|
|
1513
|
+
timedOut = true;
|
|
1514
|
+
reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError, `BLE write timeout after ${BLE_WRITE_PACKET_TIMEOUT_MS}ms`));
|
|
1515
|
+
}, BLE_WRITE_PACKET_TIMEOUT_MS);
|
|
1516
|
+
}),
|
|
1517
|
+
]);
|
|
1518
|
+
this.writeTimeoutCounts.delete(uuid);
|
|
1519
|
+
}
|
|
1520
|
+
catch (error) {
|
|
1521
|
+
if (timedOut) {
|
|
1522
|
+
if (isCurrentOwner && !isCurrentOwner()) {
|
|
1523
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] stale BLE write timed out, link kept:', uuid);
|
|
1524
|
+
}
|
|
1525
|
+
else {
|
|
1526
|
+
this.tearDownWedgedLink(uuid);
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
throw error;
|
|
1530
|
+
}
|
|
1531
|
+
finally {
|
|
1532
|
+
if (timer)
|
|
1533
|
+
clearTimeout(timer);
|
|
1534
|
+
}
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1537
|
+
tearDownWedgedLink(uuid) {
|
|
1538
|
+
var _a;
|
|
1539
|
+
const timeouts = ((_a = this.writeTimeoutCounts.get(uuid)) !== null && _a !== void 0 ? _a : 0) + 1;
|
|
1540
|
+
this.writeTimeoutCounts.set(uuid, timeouts);
|
|
1541
|
+
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE write timed out, tearing down link:', uuid, {
|
|
1542
|
+
consecutiveWriteTimeouts: timeouts,
|
|
1543
|
+
});
|
|
1544
|
+
const wedged = transportCache[uuid];
|
|
1545
|
+
this.disconnect(uuid).catch(error => {
|
|
1546
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] wedged link teardown failed (ignored):', error);
|
|
1547
|
+
});
|
|
1548
|
+
if (wedged && transportCache[uuid] === wedged) {
|
|
1549
|
+
delete transportCache[uuid];
|
|
1550
|
+
}
|
|
1551
|
+
this.deviceProtocol.delete(uuid);
|
|
1552
|
+
this.probingProtocols.delete(uuid);
|
|
1553
|
+
this.protocolV2Assemblers.delete(uuid);
|
|
1554
|
+
this.resetProtocolV2Frames(uuid);
|
|
1555
|
+
if (timeouts >= BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD) {
|
|
1556
|
+
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] BLE writes wedged repeatedly, resetting BLE manager');
|
|
1557
|
+
this.resetPlxManager();
|
|
1558
|
+
this.writeTimeoutCounts.delete(uuid);
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
resetPlxManager() {
|
|
1562
|
+
const manager = this.blePlxManager;
|
|
1563
|
+
this.blePlxManager = undefined;
|
|
1564
|
+
Object.keys(transportCache).forEach(key => {
|
|
1565
|
+
delete transportCache[key];
|
|
1566
|
+
});
|
|
1567
|
+
this.deviceProtocol.clear();
|
|
1568
|
+
this.probingProtocols.clear();
|
|
1569
|
+
this.sessionProtocols.clear();
|
|
1570
|
+
this.protocolReprobeFailures.clear();
|
|
1571
|
+
this.writeTimeoutCounts.clear();
|
|
1572
|
+
this.connectionSetupTimeoutCounts.clear();
|
|
1573
|
+
this.monitorTokens.clear();
|
|
1574
|
+
this.protocolV2Assemblers.clear();
|
|
1575
|
+
try {
|
|
1576
|
+
manager === null || manager === void 0 ? void 0 : manager.destroy();
|
|
1577
|
+
}
|
|
1578
|
+
catch (error) {
|
|
1579
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE manager destroy failed (ignored):', error);
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1337
1582
|
createProtocolMismatchError(expected) {
|
|
1338
1583
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
|
|
1339
1584
|
}
|
|
1340
1585
|
createProtocolDetectionError() {
|
|
1341
|
-
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1
|
|
1586
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1 GetFeatures or Protocol V2 Ping');
|
|
1342
1587
|
}
|
|
1343
1588
|
clearProbeProtocol(uuid, protocol) {
|
|
1589
|
+
if (this.probingProtocols.get(uuid) === protocol) {
|
|
1590
|
+
this.probingProtocols.delete(uuid);
|
|
1591
|
+
}
|
|
1344
1592
|
if (this.deviceProtocol.get(uuid) === protocol) {
|
|
1345
1593
|
this.deviceProtocol.delete(uuid);
|
|
1346
1594
|
}
|
|
1347
1595
|
}
|
|
1348
|
-
|
|
1596
|
+
getActiveProtocol(uuid) {
|
|
1597
|
+
var _a;
|
|
1598
|
+
return (_a = this.deviceProtocol.get(uuid)) !== null && _a !== void 0 ? _a : this.probingProtocols.get(uuid);
|
|
1599
|
+
}
|
|
1600
|
+
detectProtocol(uuid, expectedProtocol, protocolHint, rebuildTransport) {
|
|
1601
|
+
var _a;
|
|
1349
1602
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1603
|
+
if (reactNative.Platform.OS === 'ios' && expectedProtocol) {
|
|
1604
|
+
this.deviceProtocol.set(uuid, expectedProtocol);
|
|
1605
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol selected', {
|
|
1606
|
+
deviceId: uuid,
|
|
1607
|
+
protocol: expectedProtocol,
|
|
1608
|
+
source: 'expected',
|
|
1609
|
+
});
|
|
1610
|
+
return expectedProtocol;
|
|
1611
|
+
}
|
|
1350
1612
|
if (expectedProtocol === 'V1') {
|
|
1351
1613
|
if (yield this.probeProtocolV1(uuid)) {
|
|
1352
1614
|
this.deviceProtocol.set(uuid, 'V1');
|
|
1353
|
-
|
|
1615
|
+
this.sessionProtocols.set(uuid, 'V1');
|
|
1616
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
|
|
1617
|
+
deviceId: uuid,
|
|
1618
|
+
protocol: 'V1',
|
|
1619
|
+
source: 'expected',
|
|
1620
|
+
});
|
|
1354
1621
|
return 'V1';
|
|
1355
1622
|
}
|
|
1356
1623
|
throw this.createProtocolMismatchError(expectedProtocol);
|
|
1357
1624
|
}
|
|
1358
1625
|
if (expectedProtocol === 'V2') {
|
|
1359
|
-
this.
|
|
1360
|
-
|
|
1361
|
-
|
|
1626
|
+
if (yield this.probeProtocolV2(uuid)) {
|
|
1627
|
+
this.deviceProtocol.set(uuid, 'V2');
|
|
1628
|
+
this.sessionProtocols.set(uuid, 'V2');
|
|
1629
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
|
|
1630
|
+
deviceId: uuid,
|
|
1631
|
+
protocol: 'V2',
|
|
1632
|
+
source: 'expected',
|
|
1633
|
+
});
|
|
1634
|
+
return 'V2';
|
|
1635
|
+
}
|
|
1636
|
+
throw this.createProtocolMismatchError(expectedProtocol);
|
|
1362
1637
|
}
|
|
1363
|
-
const
|
|
1638
|
+
const sessionProtocol = this.sessionProtocols.get(uuid);
|
|
1639
|
+
const reprobeFailures = (_a = this.protocolReprobeFailures.get(uuid)) !== null && _a !== void 0 ? _a : 0;
|
|
1640
|
+
const fullProbeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
|
|
1641
|
+
const trustSessionProtocol = sessionProtocol !== undefined &&
|
|
1642
|
+
!protocolHint &&
|
|
1643
|
+
reprobeFailures < PROTOCOL_REPROBE_FALLBACK_ATTEMPTS;
|
|
1644
|
+
const probeOrder = trustSessionProtocol ? [sessionProtocol] : fullProbeOrder;
|
|
1364
1645
|
for (let i = 0; i < probeOrder.length; i += 1) {
|
|
1365
1646
|
const protocol = probeOrder[i];
|
|
1366
1647
|
if (i > 0) {
|
|
1367
1648
|
yield this.resetProbeStateAfterProtocolProbe(uuid, probeOrder[i - 1]);
|
|
1649
|
+
if (!transportCache[uuid]) {
|
|
1650
|
+
if (!rebuildTransport) {
|
|
1651
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
|
|
1652
|
+
}
|
|
1653
|
+
yield rebuildTransport();
|
|
1654
|
+
}
|
|
1368
1655
|
}
|
|
1369
1656
|
const detected = protocol === 'V1' ? yield this.probeProtocolV1(uuid) : yield this.probeProtocolV2(uuid);
|
|
1370
1657
|
if (detected) {
|
|
1371
1658
|
this.deviceProtocol.set(uuid, protocol);
|
|
1372
|
-
|
|
1659
|
+
this.sessionProtocols.set(uuid, protocol);
|
|
1660
|
+
this.protocolReprobeFailures.delete(uuid);
|
|
1661
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
|
|
1662
|
+
deviceId: uuid,
|
|
1663
|
+
protocol,
|
|
1664
|
+
source: 'probe',
|
|
1665
|
+
});
|
|
1373
1666
|
return protocol;
|
|
1374
1667
|
}
|
|
1375
1668
|
}
|
|
1669
|
+
if (trustSessionProtocol) {
|
|
1670
|
+
this.protocolReprobeFailures.set(uuid, reprobeFailures + 1);
|
|
1671
|
+
}
|
|
1672
|
+
else {
|
|
1673
|
+
this.protocolReprobeFailures.delete(uuid);
|
|
1674
|
+
}
|
|
1376
1675
|
this.deviceProtocol.delete(uuid);
|
|
1676
|
+
this.probingProtocols.delete(uuid);
|
|
1377
1677
|
throw this.createProtocolDetectionError();
|
|
1378
1678
|
});
|
|
1379
1679
|
}
|
|
@@ -1425,13 +1725,17 @@ class ReactNativeBleTransport {
|
|
|
1425
1725
|
return false;
|
|
1426
1726
|
}
|
|
1427
1727
|
try {
|
|
1428
|
-
this.
|
|
1429
|
-
yield this.callProtocolV1(uuid, '
|
|
1728
|
+
this.probingProtocols.set(uuid, 'V1');
|
|
1729
|
+
yield this.callProtocolV1(uuid, 'GetFeatures', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
|
|
1730
|
+
this.probingProtocols.delete(uuid);
|
|
1430
1731
|
return true;
|
|
1431
1732
|
}
|
|
1432
1733
|
catch (error) {
|
|
1433
1734
|
this.clearProbeProtocol(uuid, 'V1');
|
|
1434
|
-
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1
|
|
1735
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 GetFeatures probe failed:', error);
|
|
1736
|
+
if (isWedgedWriteError(error)) {
|
|
1737
|
+
throw error;
|
|
1738
|
+
}
|
|
1435
1739
|
return false;
|
|
1436
1740
|
}
|
|
1437
1741
|
});
|
|
@@ -1442,7 +1746,7 @@ class ReactNativeBleTransport {
|
|
|
1442
1746
|
if (!this._messages || !this._messagesV2) {
|
|
1443
1747
|
return false;
|
|
1444
1748
|
}
|
|
1445
|
-
this.
|
|
1749
|
+
this.probingProtocols.set(uuid, 'V2');
|
|
1446
1750
|
(_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
1447
1751
|
const detected = yield transport.probeProtocolV2({
|
|
1448
1752
|
call: (name, data, options) => this.callProtocolV2(uuid, name, data, options),
|
|
@@ -1458,6 +1762,9 @@ class ReactNativeBleTransport {
|
|
|
1458
1762
|
if (!detected) {
|
|
1459
1763
|
this.clearProbeProtocol(uuid, 'V2');
|
|
1460
1764
|
}
|
|
1765
|
+
else {
|
|
1766
|
+
this.probingProtocols.delete(uuid);
|
|
1767
|
+
}
|
|
1461
1768
|
return detected;
|
|
1462
1769
|
});
|
|
1463
1770
|
}
|
|
@@ -1500,16 +1807,8 @@ class ReactNativeBleTransport {
|
|
|
1500
1807
|
}
|
|
1501
1808
|
this.getProtocolV2FrameQueue(uuid).push(frame);
|
|
1502
1809
|
}
|
|
1503
|
-
rejectAllProtocolV2Frames(error) {
|
|
1504
|
-
this.protocolV2FrameQueues.clear();
|
|
1505
|
-
for (const framePromise of this.protocolV2FramePromises.values()) {
|
|
1506
|
-
framePromise.reject(error);
|
|
1507
|
-
}
|
|
1508
|
-
this.protocolV2FramePromises.clear();
|
|
1509
|
-
}
|
|
1510
1810
|
resetProtocolV2Frames(uuid) {
|
|
1511
|
-
this.
|
|
1512
|
-
this.protocolV2FramePromises.delete(uuid);
|
|
1811
|
+
this.rejectProtocolV2Frames(uuid, new Error(`Protocol V2 frame state reset for ${uuid}`));
|
|
1513
1812
|
}
|
|
1514
1813
|
rejectProtocolV2Frames(uuid, error) {
|
|
1515
1814
|
this.protocolV2FrameQueues.delete(uuid);
|
|
@@ -1537,38 +1836,70 @@ class ReactNativeBleTransport {
|
|
|
1537
1836
|
}
|
|
1538
1837
|
});
|
|
1539
1838
|
}
|
|
1540
|
-
|
|
1839
|
+
writeProtocolV2Packet(uuid, transport, base64, context, assertCurrentGeneration) {
|
|
1541
1840
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1542
|
-
const
|
|
1543
|
-
const packetCapacity = resolveProtocolV2PacketCapacity({
|
|
1841
|
+
const shouldUseWriteWithResponse = shouldWriteProtocolV2WithResponse({
|
|
1544
1842
|
platform: reactNative.Platform.OS,
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1843
|
+
highThroughput: context.highThroughput,
|
|
1844
|
+
requestedWithResponse: context.writeWithResponse,
|
|
1845
|
+
characteristic: transport.writeCharacteristic,
|
|
1548
1846
|
});
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
const chunk = frame.slice(offset, offset + packetCapacity);
|
|
1555
|
-
const base64 = buffer.Buffer.from(chunk).toString('base64');
|
|
1556
|
-
if (writeMode === 'withResponse') {
|
|
1557
|
-
yield transport.writeCharacteristic.writeWithResponse(base64);
|
|
1847
|
+
let attempt = 0;
|
|
1848
|
+
for (;;) {
|
|
1849
|
+
assertCurrentGeneration();
|
|
1850
|
+
if (context.signal.aborted) {
|
|
1851
|
+
throw new Error(`Protocol V2 BLE write aborted for ${context.messageName}`);
|
|
1558
1852
|
}
|
|
1559
|
-
|
|
1560
|
-
yield
|
|
1853
|
+
try {
|
|
1854
|
+
yield this.writeBlePacket(uuid, base64, payload => shouldUseWriteWithResponse
|
|
1855
|
+
? transport.writeCharacteristic.writeWithResponse(payload)
|
|
1856
|
+
: transport.writeCharacteristic.writeWithoutResponse(payload), () => {
|
|
1857
|
+
try {
|
|
1858
|
+
assertCurrentGeneration();
|
|
1859
|
+
return !context.signal.aborted;
|
|
1860
|
+
}
|
|
1861
|
+
catch (_a) {
|
|
1862
|
+
return false;
|
|
1863
|
+
}
|
|
1864
|
+
});
|
|
1865
|
+
assertCurrentGeneration();
|
|
1866
|
+
return;
|
|
1561
1867
|
}
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1868
|
+
catch (error) {
|
|
1869
|
+
if (getFirmwareUploadWriteRetryType(error) !== 'congested' ||
|
|
1870
|
+
attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
|
|
1871
|
+
throw error;
|
|
1872
|
+
}
|
|
1873
|
+
const delayMs = resolveFirmwareUploadRetryDelay(attempt);
|
|
1874
|
+
attempt += 1;
|
|
1875
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 congested write retry:', {
|
|
1876
|
+
name: context.messageName,
|
|
1877
|
+
attempt,
|
|
1878
|
+
delayMs,
|
|
1879
|
+
});
|
|
1880
|
+
yield delay(delayMs);
|
|
1567
1881
|
}
|
|
1568
1882
|
}
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1883
|
+
});
|
|
1884
|
+
}
|
|
1885
|
+
writeProtocolV2Frame(uuid, transport$1, frame, context, assertCurrentGeneration) {
|
|
1886
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1887
|
+
const tuning = getProtocolV2BleTuning();
|
|
1888
|
+
const packetCapacity = resolveProtocolV2PacketCapacity({
|
|
1889
|
+
platform: reactNative.Platform.OS,
|
|
1890
|
+
iosPacketLength: tuning.iosPacketLength,
|
|
1891
|
+
androidPacketLength: tuning.androidPacketLength,
|
|
1892
|
+
mtu: transport$1.mtuSize,
|
|
1893
|
+
});
|
|
1894
|
+
yield transport.writeProtocolV2BleFrame({
|
|
1895
|
+
frame,
|
|
1896
|
+
packetCapacity,
|
|
1897
|
+
assertActive: assertCurrentGeneration,
|
|
1898
|
+
signal: context.signal,
|
|
1899
|
+
abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
|
|
1900
|
+
wait: delay,
|
|
1901
|
+
writePacket: packet => this.writeProtocolV2Packet(uuid, transport$1, buffer.Buffer.from(packet).toString('base64'), context, assertCurrentGeneration),
|
|
1902
|
+
});
|
|
1572
1903
|
});
|
|
1573
1904
|
}
|
|
1574
1905
|
callProtocolV2(uuid, name, data, options) {
|
|
@@ -1577,17 +1908,40 @@ class ReactNativeBleTransport {
|
|
|
1577
1908
|
if (!this._messages || !this._messagesV2) {
|
|
1578
1909
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
1579
1910
|
}
|
|
1580
|
-
const callOptions =
|
|
1581
|
-
const
|
|
1582
|
-
if (
|
|
1911
|
+
const callOptions = options;
|
|
1912
|
+
const highThroughputWrite = transport.isProtocolV2HighThroughputCall(name);
|
|
1913
|
+
if (highThroughputWrite) {
|
|
1914
|
+
yield this.ensureProtocolV2HighThroughputMtu(uuid);
|
|
1583
1915
|
const tuning = getProtocolV2BleTuning();
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1916
|
+
const currentTransport = this.getCachedTransport(uuid);
|
|
1917
|
+
const writeWithResponse = shouldWriteProtocolV2WithResponse({
|
|
1918
|
+
platform: reactNative.Platform.OS,
|
|
1919
|
+
highThroughput: true,
|
|
1920
|
+
requestedWithResponse: options === null || options === void 0 ? void 0 : options.writeWithResponse,
|
|
1921
|
+
characteristic: currentTransport.writeCharacteristic,
|
|
1590
1922
|
});
|
|
1923
|
+
const packetCapacity = resolveProtocolV2PacketCapacity({
|
|
1924
|
+
platform: reactNative.Platform.OS,
|
|
1925
|
+
iosPacketLength: tuning.iosPacketLength,
|
|
1926
|
+
androidPacketLength: tuning.androidPacketLength,
|
|
1927
|
+
mtu: currentTransport.mtuSize,
|
|
1928
|
+
});
|
|
1929
|
+
const writeMode = writeWithResponse ? 'withResponse' : 'withoutResponse';
|
|
1930
|
+
const logSignature = `${name}:${writeMode}:${String(currentTransport.mtuSize)}:${packetCapacity}`;
|
|
1931
|
+
const loggedSignatures = (_a = this.protocolV2HighVolumeLogSignatures.get(uuid)) !== null && _a !== void 0 ? _a : new Set();
|
|
1932
|
+
if (!loggedSignatures.has(logSignature)) {
|
|
1933
|
+
loggedSignatures.add(logSignature);
|
|
1934
|
+
this.protocolV2HighVolumeLogSignatures.set(uuid, loggedSignatures);
|
|
1935
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume write configured', {
|
|
1936
|
+
name,
|
|
1937
|
+
writeMode,
|
|
1938
|
+
reportedMtu: currentTransport.mtuSize,
|
|
1939
|
+
packetCapacity,
|
|
1940
|
+
});
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
if (highThroughputWrite) {
|
|
1944
|
+
yield this.enableAndroidHighConnectionPriority(uuid);
|
|
1591
1945
|
}
|
|
1592
1946
|
try {
|
|
1593
1947
|
return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, callOptions);
|
|
@@ -1596,6 +1950,85 @@ class ReactNativeBleTransport {
|
|
|
1596
1950
|
Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] Protocol V2 call error:', e);
|
|
1597
1951
|
throw e;
|
|
1598
1952
|
}
|
|
1953
|
+
finally {
|
|
1954
|
+
if (highThroughputWrite) {
|
|
1955
|
+
this.scheduleAndroidBalancedConnectionPriority(uuid);
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
});
|
|
1959
|
+
}
|
|
1960
|
+
ensureProtocolV2HighThroughputMtu(uuid) {
|
|
1961
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1962
|
+
const transport = this.getCachedTransport(uuid);
|
|
1963
|
+
if (!shouldRefreshNegotiatedMtu(transport.mtuSize))
|
|
1964
|
+
return;
|
|
1965
|
+
const refreshedDevice = yield requestNegotiatedMtu(transport.device, 'highThroughput', 1);
|
|
1966
|
+
transport.device = refreshedDevice;
|
|
1967
|
+
transport.mtuSize =
|
|
1968
|
+
typeof refreshedDevice.mtu === 'number' ? refreshedDevice.mtu : transport.mtuSize;
|
|
1969
|
+
if (shouldRefreshNegotiatedMtu(transport.mtuSize)) {
|
|
1970
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `Protocol V2 high-throughput BLE MTU unavailable: ${String(transport.mtuSize)}`);
|
|
1971
|
+
}
|
|
1972
|
+
});
|
|
1973
|
+
}
|
|
1974
|
+
clearAndroidPriorityResetTimer(uuid) {
|
|
1975
|
+
const timerId = this.androidPriorityResetTimers.get(uuid);
|
|
1976
|
+
if (timerId !== undefined) {
|
|
1977
|
+
clearTimeout(timerId);
|
|
1978
|
+
this.androidPriorityResetTimers.delete(uuid);
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
enableAndroidHighConnectionPriority(uuid) {
|
|
1982
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1983
|
+
if (reactNative.Platform.OS !== 'android')
|
|
1984
|
+
return;
|
|
1985
|
+
this.clearAndroidPriorityResetTimer(uuid);
|
|
1986
|
+
if (this.androidHighPriorityDevices.has(uuid))
|
|
1987
|
+
return;
|
|
1988
|
+
const transport = transportCache[uuid];
|
|
1989
|
+
if (!transport)
|
|
1990
|
+
return;
|
|
1991
|
+
try {
|
|
1992
|
+
transport.device = yield transport.device.requestConnectionPriority(reactNativeBlePlx.ConnectionPriority.High);
|
|
1993
|
+
this.androidHighPriorityDevices.add(uuid);
|
|
1994
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE connection priority changed', {
|
|
1995
|
+
priority: 'high',
|
|
1996
|
+
});
|
|
1997
|
+
}
|
|
1998
|
+
catch (error) {
|
|
1999
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE high priority request failed', {
|
|
2000
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2001
|
+
});
|
|
2002
|
+
}
|
|
2003
|
+
});
|
|
2004
|
+
}
|
|
2005
|
+
scheduleAndroidBalancedConnectionPriority(uuid) {
|
|
2006
|
+
if (reactNative.Platform.OS !== 'android' || !this.androidHighPriorityDevices.has(uuid))
|
|
2007
|
+
return;
|
|
2008
|
+
this.clearAndroidPriorityResetTimer(uuid);
|
|
2009
|
+
const timerId = setTimeout(() => {
|
|
2010
|
+
this.androidPriorityResetTimers.delete(uuid);
|
|
2011
|
+
this.restoreAndroidConnectionPriority(uuid, transportCache[uuid]).catch(error => Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE priority restore failed', error));
|
|
2012
|
+
}, ANDROID_HIGH_PRIORITY_IDLE_MS);
|
|
2013
|
+
this.androidPriorityResetTimers.set(uuid, timerId);
|
|
2014
|
+
}
|
|
2015
|
+
restoreAndroidConnectionPriority(uuid, transport) {
|
|
2016
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2017
|
+
this.clearAndroidPriorityResetTimer(uuid);
|
|
2018
|
+
if (reactNative.Platform.OS !== 'android' || !this.androidHighPriorityDevices.delete(uuid) || !transport) {
|
|
2019
|
+
return;
|
|
2020
|
+
}
|
|
2021
|
+
try {
|
|
2022
|
+
transport.device = yield transport.device.requestConnectionPriority(reactNativeBlePlx.ConnectionPriority.Balanced);
|
|
2023
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE connection priority changed', {
|
|
2024
|
+
priority: 'balanced',
|
|
2025
|
+
});
|
|
2026
|
+
}
|
|
2027
|
+
catch (error) {
|
|
2028
|
+
Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android BLE balanced priority request failed', {
|
|
2029
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2030
|
+
});
|
|
2031
|
+
}
|
|
1599
2032
|
});
|
|
1600
2033
|
}
|
|
1601
2034
|
createProtocolV2Adapter(uuid) {
|
|
@@ -1619,9 +2052,7 @@ class ReactNativeBleTransport {
|
|
|
1619
2052
|
writeFrame: (frame, context) => __awaiter(this, void 0, void 0, function* () {
|
|
1620
2053
|
assertCurrentGeneration();
|
|
1621
2054
|
const currentTransport = this.getCachedTransport(uuid);
|
|
1622
|
-
yield this.writeProtocolV2Frame(currentTransport, frame,
|
|
1623
|
-
highVolume: context.highVolume,
|
|
1624
|
-
});
|
|
2055
|
+
yield this.writeProtocolV2Frame(uuid, currentTransport, frame, context, assertCurrentGeneration);
|
|
1625
2056
|
}),
|
|
1626
2057
|
readFrame: () => __awaiter(this, void 0, void 0, function* () {
|
|
1627
2058
|
assertCurrentGeneration();
|
|
@@ -1642,11 +2073,18 @@ class ReactNativeBleTransport {
|
|
|
1642
2073
|
};
|
|
1643
2074
|
}
|
|
1644
2075
|
getProtocolType(path) {
|
|
1645
|
-
return this.
|
|
2076
|
+
return this.getActiveProtocol(path);
|
|
1646
2077
|
}
|
|
1647
2078
|
}
|
|
1648
2079
|
|
|
2080
|
+
exports.BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_CONNECT_TIMEOUT_MANAGER_RESET_THRESHOLD;
|
|
2081
|
+
exports.BLE_CONNECT_TIMEOUT_MS = BLE_CONNECT_TIMEOUT_MS;
|
|
2082
|
+
exports.BLE_GATT_SETUP_TIMEOUT_MS = BLE_GATT_SETUP_TIMEOUT_MS;
|
|
2083
|
+
exports.BLE_WRITE_PACKET_TIMEOUT_MS = BLE_WRITE_PACKET_TIMEOUT_MS;
|
|
2084
|
+
exports.BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD = BLE_WRITE_TIMEOUT_MANAGER_RESET_THRESHOLD;
|
|
2085
|
+
exports.PROTOCOL_REPROBE_FALLBACK_ATTEMPTS = PROTOCOL_REPROBE_FALLBACK_ATTEMPTS;
|
|
1649
2086
|
exports.configureProtocolV2BleTuning = configureProtocolV2BleTuning;
|
|
1650
2087
|
exports["default"] = ReactNativeBleTransport;
|
|
2088
|
+
exports.getFirmwareUploadWriteRetryType = getFirmwareUploadWriteRetryType;
|
|
1651
2089
|
exports.getProtocolV2BleTuning = getProtocolV2BleTuning;
|
|
1652
2090
|
exports.resetProtocolV2BleTuning = resetProtocolV2BleTuning;
|