@onekeyfe/hd-transport-react-native 1.1.34-alpha.2 → 1.1.34-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,12 +1,13 @@
1
1
  'use strict';
2
2
 
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
3
5
  var reactNative = require('react-native');
4
6
  var buffer = require('buffer');
5
7
  var reactNativeBlePlx = require('react-native-ble-plx');
6
8
  var ByteBuffer = require('bytebuffer');
7
9
  var transport = require('@onekeyfe/hd-transport');
8
10
  var hdShared = require('@onekeyfe/hd-shared');
9
- var hdCore = require('@onekeyfe/hd-core');
10
11
  var BleUtils = require('@onekeyfe/react-native-ble-utils');
11
12
 
12
13
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
@@ -45,44 +46,116 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
45
46
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
46
47
  };
47
48
 
48
- const Logger = hdCore.getLogger(hdCore.LoggerNames.HdBleTransport);
49
+ let activeLogger;
50
+ const setBleLogger = (logger) => {
51
+ activeLogger = logger;
52
+ };
53
+ const bleLogger = {
54
+ debug: (...args) => { var _a; return (_a = activeLogger === null || activeLogger === void 0 ? void 0 : activeLogger.debug) === null || _a === void 0 ? void 0 : _a.call(activeLogger, ...args); },
55
+ error: (...args) => { var _a; return (_a = activeLogger === null || activeLogger === void 0 ? void 0 : activeLogger.error) === null || _a === void 0 ? void 0 : _a.call(activeLogger, ...args); },
56
+ warn: (...args) => { var _a; return (_a = activeLogger === null || activeLogger === void 0 ? void 0 : activeLogger.warn) === null || _a === void 0 ? void 0 : _a.call(activeLogger, ...args); },
57
+ };
58
+
59
+ const Logger = bleLogger;
49
60
  const getConnectedDeviceIds = (serviceUuids) => BleUtils__default["default"].getConnectedPeripherals(serviceUuids);
50
61
  const pairDevice = (macAddress) => BleUtils__default["default"].pairDevice(macAddress);
51
62
  const onDeviceBondState = (bleMacAddress) => new Promise((resolve, reject) => {
52
- let timeout;
53
- const cleanup = (cleanupListener) => {
63
+ const cleanup = () => {
54
64
  if (timeout) {
55
65
  clearTimeout(timeout);
56
66
  }
57
67
  if (cleanupListener)
58
68
  cleanupListener();
59
69
  };
70
+ const timeout = setTimeout(() => {
71
+ cleanup();
72
+ reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'device is not bonded'));
73
+ }, 60 * 1000);
60
74
  const cleanupListener = BleUtils__default["default"].onDeviceBondState(peripheral => {
61
75
  var _a;
62
76
  if (((_a = peripheral.id) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== bleMacAddress.toLowerCase()) {
63
77
  return;
64
78
  }
65
79
  const { bondState } = peripheral;
66
- if (bondState.preState === 'BOND_NONE' && bondState.state === 'BOND_BONDING') {
67
- timeout = setTimeout(() => {
68
- cleanup(cleanupListener);
69
- reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'device is not bonded'));
70
- }, 60 * 1000);
71
- }
72
80
  const hasBonded = bondState.preState === 'BOND_BONDING' && bondState.state === 'BOND_BONDED';
73
81
  const hasCanceled = bondState.preState === 'BOND_BONDING' && bondState.state === 'BOND_NONE';
74
82
  Logger.debug('onDeviceBondState bondState:', bondState);
75
83
  if (hasBonded) {
76
- cleanup(cleanupListener);
84
+ cleanup();
77
85
  resolve(peripheral);
78
86
  }
79
87
  else if (hasCanceled) {
80
- cleanup(cleanupListener);
88
+ cleanup();
81
89
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceBondedCanceled, 'bonding canceled'));
82
90
  }
83
91
  });
84
92
  });
85
93
 
94
+ const IOS_PACKET_LENGTH = 128;
95
+ const ANDROID_PACKET_LENGTH = 192;
96
+ const ANDROID_DEFAULT_MTU = 23;
97
+ const normalizeBleUuid = (uuid) => (uuid !== null && uuid !== void 0 ? uuid : '').replace(/-/g, '').toLowerCase();
98
+ const getBleUuidKey = (uuid) => {
99
+ const normalized = normalizeBleUuid(uuid);
100
+ return normalized.length >= 8 ? normalized.substring(4, 8) : normalized;
101
+ };
102
+ const ClassicServiceUUID = '00000001-0000-1000-8000-00805f9b34fb';
103
+ const Pro2ServiceUUID = 'fffd';
104
+ const OneKeyServices = {
105
+ classic: {
106
+ [ClassicServiceUUID]: {
107
+ serviceUuid: ClassicServiceUUID,
108
+ writeUuid: '00000002-0000-1000-8000-00805f9b34fb',
109
+ notifyUuid: '00000003-0000-1000-8000-00805f9b34fb',
110
+ },
111
+ [Pro2ServiceUUID]: {
112
+ serviceUuid: Pro2ServiceUUID,
113
+ writeUuid: '00000002-0000-1000-8000-00805f9b34fb',
114
+ notifyUuid: '00000003-0000-1000-8000-00805f9b34fb',
115
+ },
116
+ },
117
+ };
118
+ const bluetoothServices = [];
119
+ for (const deviceType of Object.keys(OneKeyServices)) {
120
+ const services = OneKeyServices[deviceType];
121
+ bluetoothServices.push(...Object.keys(services));
122
+ }
123
+ const getBluetoothServiceUuids = () => bluetoothServices;
124
+ const getInfosForServiceUuid = (serviceUuid, deviceType) => {
125
+ var _a;
126
+ const services = OneKeyServices[deviceType];
127
+ if (!services) {
128
+ return null;
129
+ }
130
+ const normalizedServiceUuid = normalizeBleUuid(serviceUuid);
131
+ const service = (_a = services[serviceUuid]) !== null && _a !== void 0 ? _a : Object.values(services).find(item => normalizeBleUuid(item.serviceUuid) === normalizedServiceUuid ||
132
+ getBleUuidKey(item.serviceUuid) === getBleUuidKey(serviceUuid));
133
+ if (!service) {
134
+ return null;
135
+ }
136
+ return service;
137
+ };
138
+ const isSameBleUuid = (left, right) => {
139
+ const normalizedLeft = normalizeBleUuid(left);
140
+ const normalizedRight = normalizeBleUuid(right);
141
+ return (normalizedLeft === normalizedRight ||
142
+ (getBleUuidKey(left) !== '' && getBleUuidKey(left) === getBleUuidKey(right)));
143
+ };
144
+
145
+ function hasWritableCapability(characteristic) {
146
+ return !!(characteristic.isWritableWithResponse || characteristic.isWritableWithoutResponse);
147
+ }
148
+ function resolveProtocolV2PacketCapacity({ platform, iosPacketLength = IOS_PACKET_LENGTH, androidPacketLength = ANDROID_PACKET_LENGTH, mtu, }) {
149
+ if (platform === 'ios') {
150
+ return iosPacketLength;
151
+ }
152
+ if (platform === 'android') {
153
+ const payloadLength = Math.max((mtu !== null && mtu !== void 0 ? mtu : ANDROID_DEFAULT_MTU) - 3, 1);
154
+ return Math.min(androidPacketLength, payloadLength);
155
+ }
156
+ return androidPacketLength;
157
+ }
158
+
86
159
  const timer = process.env.NODE_ENV === 'development'
87
160
  ? {
88
161
  timeout: (fn, ms) => {
@@ -108,7 +181,6 @@ const timer = process.env.NODE_ENV === 'development'
108
181
  const subscribeBleOn = (bleManager, ms = 1000) => new Promise((resolve, reject) => {
109
182
  let done = false;
110
183
  const subscription = bleManager.onStateChange(state => {
111
- console.log('ble state -> ', state);
112
184
  if (state === 'PoweredOn') {
113
185
  if (done)
114
186
  return;
@@ -126,58 +198,27 @@ const subscribeBleOn = (bleManager, ms = 1000) => new Promise((resolve, reject)
126
198
  }, ms);
127
199
  });
128
200
 
129
- const IOS_PACKET_LENGTH = 128;
130
- const ANDROID_PACKET_LENGTH = 192;
131
- const ClassicServiceUUID = '00000001-0000-1000-8000-00805f9b34fb';
132
- const OneKeyServices = {
133
- classic: {
134
- [ClassicServiceUUID]: {
135
- serviceUuid: ClassicServiceUUID,
136
- writeUuid: '00000002-0000-1000-8000-00805f9b34fb',
137
- notifyUuid: '00000003-0000-1000-8000-00805f9b34fb',
138
- },
139
- },
140
- };
141
- const bluetoothServices = [];
142
- for (const deviceType of Object.keys(OneKeyServices)) {
143
- const services = OneKeyServices[deviceType];
144
- bluetoothServices.push(...Object.keys(services));
145
- }
146
- const getBluetoothServiceUuids = () => bluetoothServices;
147
- const getInfosForServiceUuid = (serviceUuid, deviceType) => {
148
- const services = OneKeyServices[deviceType];
149
- if (!services) {
150
- return null;
151
- }
152
- const service = services[serviceUuid];
153
- if (!service) {
154
- return null;
155
- }
156
- return service;
157
- };
158
-
159
201
  const isHeaderChunk = (chunk) => {
160
202
  if (chunk.length < 9)
161
203
  return false;
162
204
  const [MagicQuestionMark, sharp1, sharp2] = chunk;
163
- if (String.fromCharCode(MagicQuestionMark) === String.fromCharCode(transport.MESSAGE_TOP_CHAR) &&
164
- String.fromCharCode(sharp1) === String.fromCharCode(transport.MESSAGE_HEADER_BYTE) &&
165
- String.fromCharCode(sharp2) === String.fromCharCode(transport.MESSAGE_HEADER_BYTE)) {
205
+ if (String.fromCharCode(MagicQuestionMark) === String.fromCharCode(transport.PROTOCOL_V1_REPORT_ID) &&
206
+ String.fromCharCode(sharp1) === String.fromCharCode(transport.PROTOCOL_V1_HEADER_BYTE) &&
207
+ String.fromCharCode(sharp2) === String.fromCharCode(transport.PROTOCOL_V1_HEADER_BYTE)) {
166
208
  return true;
167
209
  }
168
210
  return false;
169
211
  };
170
212
 
171
- const Log$1 = hdCore.getLogger(hdCore.LoggerNames.HdBleTransport);
213
+ const Log$1 = bleLogger;
172
214
  class BleTransport {
173
215
  constructor(device, writeCharacteristic, notifyCharacteristic) {
174
216
  this.name = 'ReactNativeBleTransport';
175
- this.mtuSize = 20;
217
+ this.mtuSize = 23;
176
218
  this.id = device.id;
177
219
  this.device = device;
178
220
  this.writeCharacteristic = writeCharacteristic;
179
221
  this.notifyCharacteristic = notifyCharacteristic;
180
- console.log(`BleTransport(${String(this.id)}) new instance`);
181
222
  }
182
223
  writeWithRetry(data, retryCount = BleTransport.MAX_RETRIES) {
183
224
  return __awaiter(this, void 0, void 0, function* () {
@@ -187,7 +228,7 @@ class BleTransport {
187
228
  catch (error) {
188
229
  Log$1 === null || Log$1 === void 0 ? void 0 : Log$1.debug(`Write retry attempt ${BleTransport.MAX_RETRIES - retryCount + 1}, error: ${error}`);
189
230
  if (retryCount > 0) {
190
- yield hdCore.wait(BleTransport.RETRY_DELAY);
231
+ yield hdShared.wait(BleTransport.RETRY_DELAY);
191
232
  if (error.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected ||
192
233
  error.errorCode === reactNativeBlePlx.BleErrorCode.CharacteristicNotFound) {
193
234
  try {
@@ -211,8 +252,20 @@ class BleTransport {
211
252
  BleTransport.MAX_RETRIES = 5;
212
253
  BleTransport.RETRY_DELAY = 2000;
213
254
 
214
- const { check, buildBuffers, receiveOne, parseConfigure } = transport__default["default"];
215
- const Log = hdCore.getLogger(hdCore.LoggerNames.HdBleTransport);
255
+ function createTransportCallLog(name, protocol, data) {
256
+ if (name === 'ResourceUpdate' || name === 'ResourceAck') {
257
+ return {
258
+ name,
259
+ protocol,
260
+ file_name: data.file_name,
261
+ hash: data.hash,
262
+ };
263
+ }
264
+ return { name, protocol };
265
+ }
266
+
267
+ const { check, ProtocolV1, parseConfigure } = transport__default["default"];
268
+ const Log = bleLogger;
216
269
  const transportCache = {};
217
270
  const FIRMWARE_UPLOAD_WRITE_BURST_SIZE = reactNative.Platform.OS === 'ios' ? 4 : 5;
218
271
  const FIRMWARE_UPLOAD_WRITE_PAUSE_MS = reactNative.Platform.OS === 'ios' ? 8 : 10;
@@ -222,11 +275,6 @@ const FIRMWARE_UPLOAD_RECONNECT_RETRY_DELAY_MS = 2000;
222
275
  const ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH = 192;
223
276
  const FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY = reactNative.Platform.OS === 'ios' ? IOS_PACKET_LENGTH : ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH;
224
277
  const ANDROID_GATT_CONGESTED_STATUS = 143;
225
- const getBleIdentityName = (device) => {
226
- var _a, _b;
227
- const localName = device === null || device === void 0 ? void 0 : device.localName;
228
- return (_b = (_a = device === null || device === void 0 ? void 0 : device.name) !== null && _a !== void 0 ? _a : localName) !== null && _b !== void 0 ? _b : null;
229
- };
230
278
  const delay = (ms) => new Promise(resolve => {
231
279
  setTimeout(resolve, ms);
232
280
  });
@@ -248,20 +296,83 @@ const getFirmwareUploadWriteRetryType = (error) => {
248
296
  return /GATT_CONGESTED|status\s*[:=]?\s*143/.test(text) ? 'congested' : null;
249
297
  };
250
298
  const resolveFirmwareUploadRetryDelay = (attempt, baseDelayMs = 200, maxDelayMs = 1200) => Math.min(baseDelayMs * Math.pow(2, attempt), maxDelayMs);
251
- let connectOptions = {
252
- requestMTU: 256,
299
+ const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
300
+ const PROTOCOL_V2_PROBE_TIMEOUT_MS = 10000;
301
+ const DEVICE_SCAN_TIMEOUT_MS = 8000;
302
+ const IOS_NOTIFY_READY_DELAY_MS = 150;
303
+ const ANDROID_NOTIFY_READY_DELAY_MS = 300;
304
+ const DEFAULT_PROTOCOL_V2_BLE_TUNING = {
305
+ iosPacketLength: IOS_PACKET_LENGTH,
306
+ androidPacketLength: ANDROID_PACKET_LENGTH,
307
+ };
308
+ let protocolV2BleTuning = Object.assign({}, DEFAULT_PROTOCOL_V2_BLE_TUNING);
309
+ const normalizePositiveInteger = (value, fallback) => {
310
+ const normalized = Number(value);
311
+ if (!Number.isFinite(normalized) || normalized <= 0)
312
+ return fallback;
313
+ return Math.floor(normalized);
314
+ };
315
+ function configureProtocolV2BleTuning(tuning = {}) {
316
+ protocolV2BleTuning = {
317
+ iosPacketLength: normalizePositiveInteger(tuning.iosPacketLength, protocolV2BleTuning.iosPacketLength),
318
+ androidPacketLength: normalizePositiveInteger(tuning.androidPacketLength, protocolV2BleTuning.androidPacketLength),
319
+ };
320
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE tuning configured', protocolV2BleTuning);
321
+ }
322
+ function resetProtocolV2BleTuning() {
323
+ protocolV2BleTuning = Object.assign({}, DEFAULT_PROTOCOL_V2_BLE_TUNING);
324
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] BLE tuning reset', protocolV2BleTuning);
325
+ }
326
+ function getProtocolV2BleTuning() {
327
+ return Object.assign({}, protocolV2BleTuning);
328
+ }
329
+ function inferProtocolHintFromDeviceName(name) {
330
+ return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
331
+ }
332
+ function getDeviceDisplayName(device) {
333
+ return (device === null || device === void 0 ? void 0 : device.name) || (device === null || device === void 0 ? void 0 : device.localName) || null;
334
+ }
335
+ function isGenericBleService(uuid) {
336
+ return ['1800', '1801', '180a'].includes(getBleUuidKey(uuid));
337
+ }
338
+ function hasKnownOneKeyService(device) {
339
+ var _a;
340
+ return ((_a = device === null || device === void 0 ? void 0 : device.serviceUUIDs) !== null && _a !== void 0 ? _a : []).some(serviceUuid => getInfosForServiceUuid(serviceUuid, 'classic'));
341
+ }
342
+ const ANDROID_REQUEST_MTU = 256;
343
+ const connectOptions = {
344
+ requestMTU: ANDROID_REQUEST_MTU,
253
345
  timeout: 3000,
254
346
  refreshGatt: 'OnConnected',
255
347
  };
256
348
  const tryToGetConfiguration = (device) => {
257
349
  if (!device || !device.serviceUUIDs)
258
350
  return null;
259
- const [serviceUUID] = device.serviceUUIDs;
351
+ const serviceUUID = device.serviceUUIDs.find(uuid => getInfosForServiceUuid(uuid, 'classic'));
352
+ if (!serviceUUID)
353
+ return null;
260
354
  const infos = getInfosForServiceUuid(serviceUUID, 'classic');
261
355
  if (!infos)
262
356
  return null;
263
357
  return infos;
264
358
  };
359
+ const requestAndroidMtu = (device) => __awaiter(void 0, void 0, void 0, function* () {
360
+ if (reactNative.Platform.OS !== 'android')
361
+ return device;
362
+ try {
363
+ const mtuDevice = yield device.requestMTU(ANDROID_REQUEST_MTU);
364
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] MTU configured', {
365
+ deviceId: device.id,
366
+ requested: ANDROID_REQUEST_MTU,
367
+ actual: mtuDevice.mtu,
368
+ });
369
+ return mtuDevice;
370
+ }
371
+ catch (error) {
372
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Android MTU request failed:', error);
373
+ return device;
374
+ }
375
+ });
265
376
  function remapError(error) {
266
377
  var _a;
267
378
  if (error instanceof reactNativeBlePlx.BleError) {
@@ -286,12 +397,41 @@ class ReactNativeBleTransport {
286
397
  this.name = 'ReactNativeBleTransport';
287
398
  this.configured = false;
288
399
  this.stopped = false;
289
- this.scanTimeout = 3000;
400
+ this.scanTimeout = DEVICE_SCAN_TIMEOUT_MS;
290
401
  this.runPromise = null;
291
402
  this.firmwareUploadWriteRecoveryIds = new Set();
292
- this.scanTimeout = (_a = options.scanTimeout) !== null && _a !== void 0 ? _a : 3000;
403
+ this.deviceProtocol = new Map();
404
+ this.deviceProtocolHints = new Map();
405
+ this.protocolV2Assemblers = new Map();
406
+ this.protocolV2FrameQueues = new Map();
407
+ this.protocolV2FramePromises = new Map();
408
+ this.protocolV2Links = new transport.ProtocolV2LinkManager({
409
+ getSchemas: () => {
410
+ if (!this._messages || !this._messagesV2) {
411
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
412
+ }
413
+ return {
414
+ protocolV1: this._messages,
415
+ protocolV2: this._messagesV2,
416
+ };
417
+ },
418
+ classifyError: () => 'link-fatal',
419
+ onLinkInvalidated: (uuid, reason) => __awaiter(this, void 0, void 0, function* () {
420
+ var _b;
421
+ (_b = this.protocolV2Assemblers.get(uuid)) === null || _b === void 0 ? void 0 : _b.reset();
422
+ this.rejectProtocolV2Frames(uuid, new Error(reason));
423
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 link invalidated:', uuid, reason);
424
+ if (reason.startsWith('Protocol V2 link-fatal error:')) {
425
+ yield this.release(uuid, true);
426
+ }
427
+ }),
428
+ });
429
+ this.monitorTokens = new Map();
430
+ this.nextMonitorToken = 1;
431
+ this.scanTimeout = (_a = options.scanTimeout) !== null && _a !== void 0 ? _a : DEVICE_SCAN_TIMEOUT_MS;
293
432
  }
294
- init(_logger, emitter) {
433
+ init(logger, emitter) {
434
+ setBleLogger(logger);
295
435
  this.emitter = emitter;
296
436
  }
297
437
  configure(signedData) {
@@ -299,6 +439,12 @@ class ReactNativeBleTransport {
299
439
  this.configured = true;
300
440
  this._messages = messages;
301
441
  }
442
+ configureProtocolV2(signedData) {
443
+ this._messagesV2 = parseConfigure(signedData);
444
+ this.protocolV2Links
445
+ .invalidateAllLinks('Protocol V2 schema reconfigured')
446
+ .catch(error => Log === null || Log === void 0 ? void 0 : Log.debug('Protocol V2 schema link cleanup failed:', error));
447
+ }
302
448
  listen() {
303
449
  }
304
450
  getPlxManager() {
@@ -308,6 +454,7 @@ class ReactNativeBleTransport {
308
454
  return Promise.resolve(this.blePlxManager);
309
455
  }
310
456
  resolveCharacteristics(device) {
457
+ var _a, _b, _c, _d;
311
458
  return __awaiter(this, void 0, void 0, function* () {
312
459
  yield device.discoverAllServicesAndCharacteristics();
313
460
  let infos = tryToGetConfiguration(device);
@@ -324,7 +471,19 @@ class ReactNativeBleTransport {
324
471
  }
325
472
  }
326
473
  }
474
+ let fallbackServiceUuid;
327
475
  if (!infos) {
476
+ const services = yield device.services();
477
+ 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));
478
+ const knownService = services.find(service => getInfosForServiceUuid(service.uuid, 'classic'));
479
+ const fallbackService = (_a = knownService !== null && knownService !== void 0 ? knownService : services.find(service => !isGenericBleService(service.uuid))) !== null && _a !== void 0 ? _a : services[0];
480
+ if (fallbackService) {
481
+ fallbackServiceUuid = fallbackService.uuid;
482
+ characteristics = yield device.characteristicsForService(fallbackService.uuid);
483
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Using fallback BLE service:', fallbackService.uuid);
484
+ }
485
+ }
486
+ if (!infos && !fallbackServiceUuid) {
328
487
  try {
329
488
  Log === null || Log === void 0 ? void 0 : Log.debug('cancel connection when service not found');
330
489
  yield device.cancelConnection();
@@ -334,7 +493,12 @@ class ReactNativeBleTransport {
334
493
  }
335
494
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
336
495
  }
337
- const { serviceUuid, writeUuid, notifyUuid } = infos;
496
+ const serviceUuid = (_b = infos === null || infos === void 0 ? void 0 : infos.serviceUuid) !== null && _b !== void 0 ? _b : fallbackServiceUuid;
497
+ const writeUuid = (_c = infos === null || infos === void 0 ? void 0 : infos.writeUuid) !== null && _c !== void 0 ? _c : '00000002-0000-1000-8000-00805f9b34fb';
498
+ const notifyUuid = (_d = infos === null || infos === void 0 ? void 0 : infos.notifyUuid) !== null && _d !== void 0 ? _d : '00000003-0000-1000-8000-00805f9b34fb';
499
+ if (!serviceUuid) {
500
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
501
+ }
338
502
  if (!characteristics) {
339
503
  characteristics = yield device.characteristicsForService(serviceUuid);
340
504
  }
@@ -344,10 +508,10 @@ class ReactNativeBleTransport {
344
508
  let writeCharacteristic;
345
509
  let notifyCharacteristic;
346
510
  for (const c of characteristics) {
347
- if (c.uuid === writeUuid) {
511
+ if (isSameBleUuid(c.uuid, writeUuid)) {
348
512
  writeCharacteristic = c;
349
513
  }
350
- else if (c.uuid === notifyUuid) {
514
+ else if (isSameBleUuid(c.uuid, notifyUuid)) {
351
515
  notifyCharacteristic = c;
352
516
  }
353
517
  }
@@ -357,7 +521,7 @@ class ReactNativeBleTransport {
357
521
  if (!notifyCharacteristic) {
358
522
  throw hdShared.ERRORS.TypedError('BLECharacteristicNotFound: notify characteristic not found');
359
523
  }
360
- if (!writeCharacteristic.isWritableWithResponse) {
524
+ if (!hasWritableCapability(writeCharacteristic)) {
361
525
  throw hdShared.ERRORS.TypedError('BLECharacteristicNotWritable: write characteristic not writable');
362
526
  }
363
527
  if (!notifyCharacteristic.isNotifiable) {
@@ -378,6 +542,10 @@ class ReactNativeBleTransport {
378
542
  Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored during FirmwareUpload write recovery: ', uuid);
379
543
  return;
380
544
  }
545
+ if (transportCache[uuid] !== transport) {
546
+ Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored for stale transport: ', device === null || device === void 0 ? void 0 : device.id);
547
+ return;
548
+ }
381
549
  try {
382
550
  Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect: ', device === null || device === void 0 ? void 0 : device.id);
383
551
  (_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit('device-disconnect', {
@@ -386,14 +554,16 @@ class ReactNativeBleTransport {
386
554
  connectId: device === null || device === void 0 ? void 0 : device.id,
387
555
  });
388
556
  if (this.runPromise) {
389
- this.runPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError));
557
+ const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError);
558
+ this.runPromise.reject(error);
559
+ this.rejectAllProtocolV2Frames(error);
390
560
  }
391
561
  }
392
562
  catch (e) {
393
563
  Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect error: ', e);
394
564
  }
395
565
  finally {
396
- this.release(uuid);
566
+ this.release(uuid, true);
397
567
  }
398
568
  });
399
569
  }
@@ -415,7 +585,6 @@ class ReactNativeBleTransport {
415
585
  catch (e) {
416
586
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
417
587
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
418
- connectOptions = {};
419
588
  device = yield device.connect();
420
589
  }
421
590
  else if (e.errorCode !== reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
@@ -427,7 +596,13 @@ class ReactNativeBleTransport {
427
596
  transport.device = device;
428
597
  transport.writeCharacteristic = writeCharacteristic;
429
598
  transport.notifyCharacteristic = notifyCharacteristic;
430
- transport.notifySubscription = this._monitorCharacteristic(notifyCharacteristic, uuid);
599
+ const monitorToken = this.nextMonitorToken;
600
+ this.nextMonitorToken += 1;
601
+ const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
602
+ transport.monitorToken = monitorToken;
603
+ transport.notifyTransactionId = notifyTransactionId;
604
+ this.monitorTokens.set(uuid, monitorToken);
605
+ transport.notifySubscription = this._monitorCharacteristic(notifyCharacteristic, uuid, monitorToken, notifyTransactionId);
431
606
  this.attachDisconnectSubscription(transport, device, uuid);
432
607
  }
433
608
  finally {
@@ -462,11 +637,11 @@ class ReactNativeBleTransport {
462
637
  }
463
638
  }
464
639
  blePlxManager.startDeviceScan(getBluetoothServiceUuids(), {
640
+ allowDuplicates: true,
465
641
  scanMode: reactNativeBlePlx.ScanMode.LowLatency,
466
642
  }, (error, device) => {
467
- var _a;
643
+ var _a, _b, _c;
468
644
  if (error) {
469
- Log === null || Log === void 0 ? void 0 : Log.debug('ble scan manager: ', blePlxManager);
470
645
  Log === null || Log === void 0 ? void 0 : Log.debug('ble scan error: ', error);
471
646
  if ([reactNativeBlePlx.BleErrorCode.BluetoothPoweredOff, reactNativeBlePlx.BleErrorCode.BluetoothInUnknownState].includes(error.errorCode)) {
472
647
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePermissionError));
@@ -485,12 +660,20 @@ class ReactNativeBleTransport {
485
660
  }
486
661
  return;
487
662
  }
488
- if (hdShared.isOnekeyDevice(getBleIdentityName(device), device === null || device === void 0 ? void 0 : device.id)) {
489
- Log === null || Log === void 0 ? void 0 : Log.debug('search device start ======================');
490
- const { name, localName, id } = device !== null && device !== void 0 ? device : {};
491
- 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 : ''}`);
663
+ const displayName = getDeviceDisplayName(device);
664
+ const isOneKey = hdShared.isOnekeyDevice((_b = device === null || device === void 0 ? void 0 : device.name) !== null && _b !== void 0 ? _b : null, device === null || device === void 0 ? void 0 : device.id) ||
665
+ hdShared.isOnekeyDevice((_c = device === null || device === void 0 ? void 0 : device.localName) !== null && _c !== void 0 ? _c : null, device === null || device === void 0 ? void 0 : device.id) ||
666
+ hasKnownOneKeyService(device);
667
+ if (isOneKey) {
492
668
  addDevice(device);
493
- Log === null || Log === void 0 ? void 0 : Log.debug('search device end ======================\n');
669
+ }
670
+ else if (displayName && /\bpro\s*2\b/i.test(displayName)) {
671
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Pro2-like BLE device was not accepted:', {
672
+ name: device === null || device === void 0 ? void 0 : device.name,
673
+ localName: device === null || device === void 0 ? void 0 : device.localName,
674
+ id: device === null || device === void 0 ? void 0 : device.id,
675
+ serviceUUIDs: device === null || device === void 0 ? void 0 : device.serviceUUIDs,
676
+ });
494
677
  }
495
678
  });
496
679
  getConnectedDeviceIds(reactNative.Platform.OS === 'ios' ? getBluetoothServiceUuids() : []).then(devices => {
@@ -499,14 +682,25 @@ class ReactNativeBleTransport {
499
682
  const hasCachedServiceUuid = Boolean(serviceUUIDs === null || serviceUUIDs === void 0 ? void 0 : serviceUUIDs.length);
500
683
  const keepDevice = reactNative.Platform.OS === 'ios' || hasCachedServiceUuid;
501
684
  if (keepDevice) {
502
- Log === null || Log === void 0 ? void 0 : Log.debug('search connected peripheral: ', device.id);
503
685
  addDevice(device);
504
686
  }
505
687
  }
506
688
  });
507
689
  const addDevice = (device) => {
690
+ var _a;
508
691
  if (deviceList.every(d => d.id !== device.id)) {
509
- deviceList.push(Object.assign(Object.assign({}, device), { commType: 'ble' }));
692
+ const displayName = (_a = getDeviceDisplayName(device)) !== null && _a !== void 0 ? _a : 'Unknown BLE Device';
693
+ const protocolHint = inferProtocolHintFromDeviceName(displayName);
694
+ if (protocolHint) {
695
+ this.deviceProtocolHints.set(device.id, protocolHint);
696
+ }
697
+ deviceList.push(Object.assign(Object.assign({}, device), { name: displayName, commType: 'ble' }));
698
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] OneKey BLE device discovered', {
699
+ deviceId: device.id,
700
+ name: displayName,
701
+ serviceUUIDs: device.serviceUUIDs,
702
+ protocolHint,
703
+ });
510
704
  }
511
705
  };
512
706
  timer.timeout(() => {
@@ -517,19 +711,31 @@ class ReactNativeBleTransport {
517
711
  });
518
712
  }
519
713
  acquire(input) {
520
- var _a;
714
+ var _a, _b;
521
715
  return __awaiter(this, void 0, void 0, function* () {
522
- const { uuid, forceCleanRunPromise } = input;
716
+ const { uuid, forceCleanRunPromise, expectedProtocol } = input;
523
717
  if (!uuid) {
524
718
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleRequiredUUID);
525
719
  }
526
- let device = null;
527
- if (transportCache[uuid]) {
528
- Log === null || Log === void 0 ? void 0 : Log.debug('transport not be released, will release: ', uuid);
529
- yield this.release(uuid);
720
+ const cachedTransport = transportCache[uuid];
721
+ if (cachedTransport) {
722
+ const cachedProtocol = this.deviceProtocol.get(uuid);
723
+ const isCachedDeviceConnected = yield cachedTransport.device.isConnected().catch(() => false);
724
+ if (isCachedDeviceConnected &&
725
+ cachedProtocol &&
726
+ (!expectedProtocol || cachedProtocol === expectedProtocol)) {
727
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] reuse cached BLE transport:', uuid, cachedProtocol);
728
+ return { uuid, protocolType: cachedProtocol };
729
+ }
730
+ Log === null || Log === void 0 ? void 0 : Log.debug('transport not reusable, will release: ', uuid);
731
+ yield this.release(uuid, true);
530
732
  }
733
+ let device = null;
531
734
  if (forceCleanRunPromise && this.runPromise) {
532
- this.runPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise));
735
+ const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
736
+ this.runPromise.reject(error);
737
+ this.rejectAllProtocolV2Frames(error);
738
+ this.runPromise = null;
533
739
  Log === null || Log === void 0 ? void 0 : Log.debug('Force clean Bluetooth run promise, forceCleanRunPromise: ', forceCleanRunPromise);
534
740
  }
535
741
  const blePlxManager = yield this.getPlxManager();
@@ -545,6 +751,9 @@ class ReactNativeBleTransport {
545
751
  if (bondState.bonding) {
546
752
  yield onDeviceBondState(uuid);
547
753
  }
754
+ else if (!bondState.bonded) {
755
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'device is not bonded');
756
+ }
548
757
  }
549
758
  if (!device) {
550
759
  const devices = yield blePlxManager.devices([uuid]);
@@ -565,7 +774,6 @@ class ReactNativeBleTransport {
565
774
  Log === null || Log === void 0 ? void 0 : Log.debug('try to connect to device has error: ', e);
566
775
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
567
776
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
568
- connectOptions = {};
569
777
  Log === null || Log === void 0 ? void 0 : Log.debug('first try to reconnect without params');
570
778
  device = yield blePlxManager.connectToDevice(uuid);
571
779
  }
@@ -584,23 +792,22 @@ class ReactNativeBleTransport {
584
792
  if (!(yield device.isConnected())) {
585
793
  Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device: ', uuid);
586
794
  try {
587
- yield device.connect(connectOptions);
795
+ device = yield device.connect(connectOptions);
588
796
  }
589
797
  catch (e) {
590
798
  Log === null || Log === void 0 ? void 0 : Log.debug('not connected, try to connect to device has error: ', e);
591
799
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
592
800
  e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
593
- connectOptions = {};
594
801
  Log === null || Log === void 0 ? void 0 : Log.debug('second try to reconnect without params');
595
802
  try {
596
- yield device.connect();
803
+ device = yield device.connect();
597
804
  }
598
805
  catch (e) {
599
806
  Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect error: ', e);
600
807
  if (e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
601
808
  Log === null || Log === void 0 ? void 0 : Log.debug('last try to reconnect');
602
809
  yield device.cancelConnection();
603
- yield device.connect();
810
+ device = yield device.connect();
604
811
  }
605
812
  }
606
813
  }
@@ -609,58 +816,124 @@ class ReactNativeBleTransport {
609
816
  }
610
817
  }
611
818
  }
819
+ device = yield requestAndroidMtu(device);
612
820
  const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristics(device);
613
- yield this.release(uuid);
614
- const transport = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
615
- transport.notifySubscription = this._monitorCharacteristic(transport.notifyCharacteristic, uuid);
616
- transportCache[uuid] = transport;
617
- (_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit('device-connect', {
821
+ const protocolHint = expectedProtocol
822
+ ? undefined
823
+ : (_a = this.deviceProtocolHints.get(uuid)) !== null && _a !== void 0 ? _a : inferProtocolHintFromDeviceName(getDeviceDisplayName(device));
824
+ yield this.release(uuid, true);
825
+ if (protocolHint) {
826
+ this.deviceProtocolHints.set(uuid, protocolHint);
827
+ }
828
+ const transport$1 = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
829
+ if (reactNative.Platform.OS === 'android') {
830
+ transport$1.mtuSize = typeof device.mtu === 'number' ? device.mtu : transport$1.mtuSize;
831
+ }
832
+ const monitorToken = this.nextMonitorToken;
833
+ this.nextMonitorToken += 1;
834
+ const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
835
+ transport$1.monitorToken = monitorToken;
836
+ transport$1.notifyTransactionId = notifyTransactionId;
837
+ this.monitorTokens.set(uuid, monitorToken);
838
+ transport$1.notifySubscription = this._monitorCharacteristic(transport$1.notifyCharacteristic, uuid, monitorToken, notifyTransactionId);
839
+ transportCache[uuid] = transport$1;
840
+ this.protocolV2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler());
841
+ if (reactNative.Platform.OS === 'ios') {
842
+ yield new Promise(resolve => {
843
+ setTimeout(resolve, IOS_NOTIFY_READY_DELAY_MS);
844
+ });
845
+ }
846
+ else if (reactNative.Platform.OS === 'android') {
847
+ yield delay(ANDROID_NOTIFY_READY_DELAY_MS);
848
+ }
849
+ const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint);
850
+ (_b = this.emitter) === null || _b === void 0 ? void 0 : _b.emit('device-connect', {
618
851
  name: device.name,
619
852
  id: device.id,
620
853
  connectId: device.id,
621
854
  });
622
- this.attachDisconnectSubscription(transport, device, uuid);
623
- return { uuid };
855
+ this.attachDisconnectSubscription(transport$1, device, uuid);
856
+ return { uuid, protocolType };
624
857
  });
625
858
  }
626
- _monitorCharacteristic(characteristic, uuid) {
859
+ _monitorCharacteristic(characteristic, uuid, monitorToken, notifyTransactionId) {
627
860
  let bufferLength = 0;
628
861
  let buffer$1 = [];
629
862
  const subscription = characteristic.monitor((error, c) => {
630
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
863
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
864
+ const isCurrentMonitor = this.monitorTokens.get(uuid) === monitorToken;
631
865
  if (error) {
632
866
  Log === null || Log === void 0 ? void 0 : Log.debug(`error monitor ${characteristic.uuid}, deviceId: ${characteristic.deviceID}: ${error}`);
633
867
  if (this.firmwareUploadWriteRecoveryIds.has(uuid)) {
634
868
  Log === null || Log === void 0 ? void 0 : Log.debug('notify error ignored during FirmwareUpload write recovery: ', uuid);
635
869
  return;
636
870
  }
637
- if (this.runPromise) {
638
- let ERROR = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
871
+ if (!isCurrentMonitor) {
872
+ Log === null || Log === void 0 ? void 0 : Log.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
873
+ return;
874
+ }
875
+ if (this.deviceProtocol.get(uuid) === 'V2') {
876
+ let errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
639
877
  if ((_a = error.reason) === null || _a === void 0 ? void 0 : _a.includes('The connection has timed out unexpectedly')) {
640
- ERROR = hdShared.HardwareErrorCode.BleTimeoutError;
878
+ errorCode = hdShared.HardwareErrorCode.BleTimeoutError;
641
879
  }
642
- if ((_b = error.reason) === null || _b === void 0 ? void 0 : _b.includes('Encryption is insufficient')) {
643
- ERROR = hdShared.HardwareErrorCode.BleDeviceBondError;
880
+ else if ((_b = error.reason) === null || _b === void 0 ? void 0 : _b.includes('Encryption is insufficient')) {
881
+ errorCode = hdShared.HardwareErrorCode.BleDeviceBondError;
644
882
  }
645
- if (((_c = error.reason) === null || _c === void 0 ? void 0 : _c.includes('Cannot write client characteristic config descriptor')) ||
883
+ else if (((_c = error.reason) === null || _c === void 0 ? void 0 : _c.includes('Cannot write client characteristic config descriptor')) ||
646
884
  ((_d = error.reason) === null || _d === void 0 ? void 0 : _d.includes('Cannot find client characteristic config descriptor')) ||
647
885
  ((_e = error.reason) === null || _e === void 0 ? void 0 : _e.includes('The handle is invalid')) ||
648
886
  ((_f = error.reason) === null || _f === void 0 ? void 0 : _f.includes('Writing is not permitted')) ||
649
887
  ((_g = error.reason) === null || _g === void 0 ? void 0 : _g.includes('notify change failed for device'))) {
650
- this.runPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure));
888
+ errorCode = hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure;
889
+ }
890
+ this.rejectProtocolV2Frames(uuid, hdShared.ERRORS.TypedError(errorCode));
891
+ return;
892
+ }
893
+ if (this.runPromise) {
894
+ let ERROR = hdShared.HardwareErrorCode.BleCharacteristicNotifyError;
895
+ if ((_h = error.reason) === null || _h === void 0 ? void 0 : _h.includes('The connection has timed out unexpectedly')) {
896
+ ERROR = hdShared.HardwareErrorCode.BleTimeoutError;
897
+ }
898
+ if ((_j = error.reason) === null || _j === void 0 ? void 0 : _j.includes('Encryption is insufficient')) {
899
+ ERROR = hdShared.HardwareErrorCode.BleDeviceBondError;
900
+ }
901
+ if (((_k = error.reason) === null || _k === void 0 ? void 0 : _k.includes('Cannot write client characteristic config descriptor')) ||
902
+ ((_l = error.reason) === null || _l === void 0 ? void 0 : _l.includes('Cannot find client characteristic config descriptor')) ||
903
+ ((_m = error.reason) === null || _m === void 0 ? void 0 : _m.includes('The handle is invalid')) ||
904
+ ((_o = error.reason) === null || _o === void 0 ? void 0 : _o.includes('Writing is not permitted')) ||
905
+ ((_p = error.reason) === null || _p === void 0 ? void 0 : _p.includes('notify change failed for device'))) {
906
+ const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure);
907
+ this.runPromise.reject(notifyError);
908
+ this.rejectAllProtocolV2Frames(notifyError);
651
909
  Log === null || Log === void 0 ? void 0 : Log.debug(`${hdShared.HardwareErrorCode.BleCharacteristicNotifyChangeFailure} ${error.message} ${error.reason}`);
652
910
  return;
653
911
  }
654
- this.runPromise.reject(hdShared.ERRORS.TypedError(ERROR));
912
+ const notifyError = hdShared.ERRORS.TypedError(ERROR);
913
+ this.runPromise.reject(notifyError);
914
+ this.rejectAllProtocolV2Frames(notifyError);
655
915
  Log === null || Log === void 0 ? void 0 : Log.debug(': monitor notify error, and has unreleased Promise', Error);
656
916
  }
657
917
  return;
658
918
  }
919
+ if (!isCurrentMonitor) {
920
+ Log === null || Log === void 0 ? void 0 : Log.debug('monitor data ignored for stale transport: ', uuid, notifyTransactionId);
921
+ return;
922
+ }
659
923
  if (!c) {
660
924
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleMonitorError);
661
925
  }
662
926
  try {
663
927
  const data = buffer.Buffer.from(c.value, 'base64');
928
+ const protocol = this.deviceProtocol.get(uuid);
929
+ if (!protocol) {
930
+ Log === null || Log === void 0 ? void 0 : Log.debug('monitor data ignored before protocol detection: ', uuid);
931
+ return;
932
+ }
933
+ if (protocol === 'V2') {
934
+ this.handleProtocolV2Notification(uuid, monitorToken, new Uint8Array(data));
935
+ return;
936
+ }
664
937
  if (isHeaderChunk(data)) {
665
938
  bufferLength = data.readInt32BE(5);
666
939
  buffer$1 = [...data.subarray(3)];
@@ -668,33 +941,74 @@ class ReactNativeBleTransport {
668
941
  else {
669
942
  buffer$1 = buffer$1.concat([...data]);
670
943
  }
671
- if (buffer$1.length - transport.COMMON_HEADER_SIZE >= bufferLength) {
944
+ if (buffer$1.length - transport.PROTOCOL_V1_MESSAGE_HEADER_SIZE >= bufferLength) {
672
945
  const value = buffer.Buffer.from(buffer$1);
673
946
  bufferLength = 0;
674
947
  buffer$1 = [];
675
- (_h = this.runPromise) === null || _h === void 0 ? void 0 : _h.resolve(value.toString('hex'));
948
+ (_q = this.runPromise) === null || _q === void 0 ? void 0 : _q.resolve(value.toString('hex'));
676
949
  }
677
950
  }
678
951
  catch (error) {
679
952
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor data error: ', error);
680
- (_j = this.runPromise) === null || _j === void 0 ? void 0 : _j.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError));
953
+ const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
954
+ if (this.deviceProtocol.get(uuid) === 'V2') {
955
+ this.rejectProtocolV2Frames(uuid, notifyError);
956
+ }
957
+ else {
958
+ (_r = this.runPromise) === null || _r === void 0 ? void 0 : _r.reject(notifyError);
959
+ }
681
960
  }
682
- }, uuid);
961
+ }, notifyTransactionId);
683
962
  return subscription;
684
963
  }
685
- release(uuid) {
686
- var _a, _b, _c;
964
+ release(uuid, onclose = false) {
965
+ var _a, _b, _c, _d, _e, _f, _g;
687
966
  return __awaiter(this, void 0, void 0, function* () {
688
967
  const transport = transportCache[uuid];
968
+ yield this.protocolV2Links.invalidateLink(uuid, 'React Native BLE transport released');
969
+ if (this.runPromise) {
970
+ const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
971
+ this.runPromise.reject(error);
972
+ this.runPromise = null;
973
+ this.rejectAllProtocolV2Frames(error);
974
+ }
975
+ else {
976
+ this.resetProtocolV2Frames(uuid);
977
+ }
978
+ if (reactNative.Platform.OS === 'android' && !onclose && transport) {
979
+ (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
980
+ this.resetProtocolV2Frames(uuid);
981
+ return Promise.resolve(true);
982
+ }
689
983
  if (transport) {
984
+ if (this.monitorTokens.get(uuid) === transport.monitorToken) {
985
+ this.monitorTokens.delete(uuid);
986
+ }
690
987
  Log === null || Log === void 0 ? void 0 : Log.debug('release: removing disconnect subscription for device: ', uuid);
691
- (_a = transport.disconnectSubscription) === null || _a === void 0 ? void 0 : _a.remove();
988
+ (_b = transport.disconnectSubscription) === null || _b === void 0 ? void 0 : _b.remove();
692
989
  transport.disconnectSubscription = undefined;
693
- Log === null || Log === void 0 ? void 0 : Log.debug('release: removing notify subscription, characteristic: ', (_b = transport.notifyCharacteristic) === null || _b === void 0 ? void 0 : _b.uuid);
694
- (_c = transport.notifySubscription) === null || _c === void 0 ? void 0 : _c.remove();
990
+ 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);
991
+ (_d = transport.notifySubscription) === null || _d === void 0 ? void 0 : _d.remove();
695
992
  transport.notifySubscription = undefined;
993
+ if (transport.notifyTransactionId) {
994
+ try {
995
+ yield ((_e = this.blePlxManager) === null || _e === void 0 ? void 0 : _e.cancelTransaction(transport.notifyTransactionId));
996
+ }
997
+ catch (e) {
998
+ 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);
999
+ }
1000
+ }
696
1001
  delete transportCache[uuid];
697
- if (reactNative.Platform.OS === 'android') ;
1002
+ }
1003
+ this.deviceProtocol.delete(uuid);
1004
+ (_f = this.protocolV2Assemblers.get(uuid)) === null || _f === void 0 ? void 0 : _f.reset();
1005
+ this.protocolV2Assemblers.delete(uuid);
1006
+ this.resetProtocolV2Frames(uuid);
1007
+ try {
1008
+ yield ((_g = this.blePlxManager) === null || _g === void 0 ? void 0 : _g.cancelTransaction(uuid));
1009
+ }
1010
+ catch (e) {
1011
+ Log === null || Log === void 0 ? void 0 : Log.debug('release: cancel transaction error (ignored): ', (e === null || e === void 0 ? void 0 : e.message) || e);
698
1012
  }
699
1013
  return Promise.resolve(true);
700
1014
  });
@@ -704,7 +1018,7 @@ class ReactNativeBleTransport {
704
1018
  yield this.call(session, name, data);
705
1019
  });
706
1020
  }
707
- call(uuid, name, data) {
1021
+ call(uuid, name, data, options) {
708
1022
  return __awaiter(this, void 0, void 0, function* () {
709
1023
  if (this.stopped) {
710
1024
  return Promise.reject(hdShared.ERRORS.TypedError('Transport stopped.'));
@@ -712,30 +1026,33 @@ class ReactNativeBleTransport {
712
1026
  if (this._messages == null) {
713
1027
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
714
1028
  }
1029
+ const protocol = this.getProtocolType(uuid);
1030
+ if (!protocol) {
1031
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${uuid}`);
1032
+ }
1033
+ Log === null || Log === void 0 ? void 0 : Log.debug('transport call', createTransportCallLog(name, protocol, data));
1034
+ if (protocol === 'V2') {
1035
+ return this.callProtocolV2(uuid, name, data, options);
1036
+ }
715
1037
  const forceRun = name === 'Initialize' || name === 'Cancel';
716
- Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native call this.runPromise', this.runPromise);
717
1038
  if (this.runPromise && !forceRun) {
718
1039
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportCallInProgress);
719
1040
  }
720
- const transport$1 = transportCache[uuid];
721
- if (!transport$1) {
722
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
1041
+ return this.callProtocolV1(uuid, name, data, options);
1042
+ });
1043
+ }
1044
+ callProtocolV1(uuid, name, data, options) {
1045
+ return __awaiter(this, void 0, void 0, function* () {
1046
+ if (!this._messages) {
1047
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
723
1048
  }
724
- this.runPromise = hdShared.createDeferred();
1049
+ const transport = this.getCachedTransport(uuid);
1050
+ const runPromise = hdShared.createDeferred();
1051
+ runPromise.promise.catch(() => undefined);
1052
+ this.runPromise = runPromise;
725
1053
  const messages = this._messages;
726
- if (name === 'ResourceUpdate' || name === 'ResourceAck') {
727
- Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native', 'call-', ' name: ', name, ' data: ', {
728
- file_name: data === null || data === void 0 ? void 0 : data.file_name,
729
- hash: data === null || data === void 0 ? void 0 : data.hash,
730
- });
731
- }
732
- else if (transport.LogBlockCommand.has(name)) {
733
- Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native', 'call-', ' name: ', name);
734
- }
735
- else {
736
- Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native', 'call-', ' name: ', name, ' data: ', data);
737
- }
738
- const buffers = buildBuffers(messages, name, data);
1054
+ const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
1055
+ let timeout;
739
1056
  function writeChunkedData(buffers, writeFunction, onError) {
740
1057
  return __awaiter(this, void 0, void 0, function* () {
741
1058
  const packetCapacity = reactNative.Platform.OS === 'ios' ? IOS_PACKET_LENGTH : ANDROID_PACKET_LENGTH;
@@ -790,13 +1107,13 @@ class ReactNativeBleTransport {
790
1107
  });
791
1108
  }
792
1109
  if (name === 'EmmcFileWrite') {
793
- yield writeChunkedData(buffers, data => transport$1.writeWithRetry(data), e => {
1110
+ yield writeChunkedData(buffers, data => transport.writeWithRetry(data), e => {
794
1111
  this.runPromise = null;
795
1112
  Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
796
1113
  });
797
1114
  }
798
1115
  else if (name === 'FirmwareUpload') {
799
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] FirmwareUpload write uses throttled BLE packets:', {
1116
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Firmware upload transport configured', {
800
1117
  packetCapacity: FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY,
801
1118
  burstSize: FIRMWARE_UPLOAD_WRITE_BURST_SIZE,
802
1119
  pauseMs: FIRMWARE_UPLOAD_WRITE_PAUSE_MS,
@@ -807,7 +1124,7 @@ class ReactNativeBleTransport {
807
1124
  let attempt = 0;
808
1125
  while (true) {
809
1126
  try {
810
- yield transport$1.writeCharacteristic.writeWithoutResponse(data);
1127
+ yield transport.writeCharacteristic.writeWithoutResponse(data);
811
1128
  return;
812
1129
  }
813
1130
  catch (error) {
@@ -832,7 +1149,7 @@ class ReactNativeBleTransport {
832
1149
  attempt += 1;
833
1150
  if (shouldReconnect) {
834
1151
  try {
835
- yield this.reconnectFirmwareUploadTransport(uuid, transport$1);
1152
+ yield this.reconnectFirmwareUploadTransport(uuid, transport);
836
1153
  }
837
1154
  catch (e) {
838
1155
  Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] FirmwareUpload reconnect error:', e);
@@ -852,7 +1169,7 @@ class ReactNativeBleTransport {
852
1169
  for (const o of buffers) {
853
1170
  const outData = o.toString('base64');
854
1171
  try {
855
- yield transport$1.writeCharacteristic.writeWithoutResponse(outData);
1172
+ yield transport.writeCharacteristic.writeWithoutResponse(outData);
856
1173
  }
857
1174
  catch (e) {
858
1175
  Log === null || Log === void 0 ? void 0 : Log.debug('writeCharacteristic write error: ', e);
@@ -870,20 +1187,39 @@ class ReactNativeBleTransport {
870
1187
  }
871
1188
  }
872
1189
  try {
873
- const response = yield this.runPromise.promise;
1190
+ const response = yield Promise.race([
1191
+ runPromise.promise,
1192
+ new Promise((_, reject) => {
1193
+ if (options === null || options === void 0 ? void 0 : options.timeoutMs) {
1194
+ timeout = setTimeout(() => {
1195
+ const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, `BLE response timeout after ${options.timeoutMs}ms for ${name}`);
1196
+ runPromise.reject(error);
1197
+ reject(error);
1198
+ }, options.timeoutMs);
1199
+ }
1200
+ }),
1201
+ ]);
874
1202
  if (typeof response !== 'string') {
875
1203
  throw new Error('Returning data is not string.');
876
1204
  }
877
- Log === null || Log === void 0 ? void 0 : Log.debug('receive data: ', response);
878
- const jsonData = receiveOne(messages, response);
1205
+ const jsonData = ProtocolV1.decodeMessage(messages, response);
879
1206
  return check.call(jsonData);
880
1207
  }
881
1208
  catch (e) {
882
- Log === null || Log === void 0 ? void 0 : Log.error('call error: ', e);
1209
+ if (name === 'Initialize' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS) {
1210
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 Initialize probe call failed:', e);
1211
+ }
1212
+ else {
1213
+ Log === null || Log === void 0 ? void 0 : Log.error('call error: ', e);
1214
+ }
883
1215
  throw e;
884
1216
  }
885
1217
  finally {
886
- this.runPromise = null;
1218
+ if (timeout)
1219
+ clearTimeout(timeout);
1220
+ if (this.runPromise === runPromise) {
1221
+ this.runPromise = null;
1222
+ }
887
1223
  }
888
1224
  });
889
1225
  }
@@ -893,7 +1229,7 @@ class ReactNativeBleTransport {
893
1229
  disconnect(session) {
894
1230
  var _a, _b, _c, _d, _e;
895
1231
  return __awaiter(this, void 0, void 0, function* () {
896
- Log === null || Log === void 0 ? void 0 : Log.debug('transport-react-native transport resetSession: ', session);
1232
+ yield this.protocolV2Links.invalidateLink(session, 'React Native BLE transport disconnected');
897
1233
  const transport = transportCache[session];
898
1234
  if (transport === null || transport === void 0 ? void 0 : transport.disconnectSubscription) {
899
1235
  try {
@@ -940,6 +1276,10 @@ class ReactNativeBleTransport {
940
1276
  if (transportCache[session]) {
941
1277
  delete transportCache[session];
942
1278
  }
1279
+ this.deviceProtocol.delete(session);
1280
+ this.deviceProtocolHints.delete(session);
1281
+ this.protocolV2Assemblers.delete(session);
1282
+ this.resetProtocolV2Frames(session);
943
1283
  try {
944
1284
  (_d = this.emitter) === null || _d === void 0 ? void 0 : _d.emit('device-disconnect', {
945
1285
  name: (_e = transport === null || transport === void 0 ? void 0 : transport.device) === null || _e === void 0 ? void 0 : _e.name,
@@ -958,6 +1298,315 @@ class ReactNativeBleTransport {
958
1298
  if (this.runPromise) ;
959
1299
  this.runPromise = null;
960
1300
  }
1301
+ getCachedTransport(uuid) {
1302
+ const transport = transportCache[uuid];
1303
+ if (!transport) {
1304
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotFound);
1305
+ }
1306
+ return transport;
1307
+ }
1308
+ createProtocolMismatchError(expected) {
1309
+ return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
1310
+ }
1311
+ createProtocolDetectionError() {
1312
+ return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1 Initialize or Protocol V2 Ping');
1313
+ }
1314
+ clearProbeProtocol(uuid, protocol) {
1315
+ if (this.deviceProtocol.get(uuid) === protocol) {
1316
+ this.deviceProtocol.delete(uuid);
1317
+ }
1318
+ }
1319
+ detectProtocol(uuid, expectedProtocol, protocolHint) {
1320
+ return __awaiter(this, void 0, void 0, function* () {
1321
+ if (expectedProtocol === 'V1') {
1322
+ if (yield this.probeProtocolV1(uuid)) {
1323
+ this.deviceProtocol.set(uuid, 'V1');
1324
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1325
+ deviceId: uuid,
1326
+ protocol: 'V1',
1327
+ source: 'expected',
1328
+ });
1329
+ return 'V1';
1330
+ }
1331
+ throw this.createProtocolMismatchError(expectedProtocol);
1332
+ }
1333
+ if (expectedProtocol === 'V2') {
1334
+ this.deviceProtocol.set(uuid, 'V2');
1335
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1336
+ deviceId: uuid,
1337
+ protocol: 'V2',
1338
+ source: 'expected',
1339
+ });
1340
+ return 'V2';
1341
+ }
1342
+ const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
1343
+ for (let i = 0; i < probeOrder.length; i += 1) {
1344
+ const protocol = probeOrder[i];
1345
+ if (i > 0) {
1346
+ yield this.resetProbeStateAfterProtocolProbe(uuid, probeOrder[i - 1]);
1347
+ }
1348
+ const detected = protocol === 'V1' ? yield this.probeProtocolV1(uuid) : yield this.probeProtocolV2(uuid);
1349
+ if (detected) {
1350
+ this.deviceProtocol.set(uuid, protocol);
1351
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] protocol detected', {
1352
+ deviceId: uuid,
1353
+ protocol,
1354
+ source: 'probe',
1355
+ });
1356
+ return protocol;
1357
+ }
1358
+ }
1359
+ this.deviceProtocol.delete(uuid);
1360
+ throw this.createProtocolDetectionError();
1361
+ });
1362
+ }
1363
+ resetProbeStateAfterProtocolProbe(uuid, protocol) {
1364
+ var _a, _b, _c;
1365
+ return __awaiter(this, void 0, void 0, function* () {
1366
+ const transport = transportCache[uuid];
1367
+ yield this.protocolV2Links.invalidateLink(uuid, `Reset notify state after Protocol ${protocol} probe`);
1368
+ (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1369
+ this.resetProtocolV2Frames(uuid);
1370
+ if (this.runPromise) {
1371
+ const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
1372
+ this.runPromise.reject(error);
1373
+ this.runPromise = null;
1374
+ }
1375
+ if (!transport)
1376
+ return;
1377
+ const previousNotifyTransactionId = transport.notifyTransactionId;
1378
+ if (this.monitorTokens.get(uuid) === transport.monitorToken) {
1379
+ this.monitorTokens.delete(uuid);
1380
+ }
1381
+ (_b = transport.notifySubscription) === null || _b === void 0 ? void 0 : _b.remove();
1382
+ transport.notifySubscription = undefined;
1383
+ if (previousNotifyTransactionId) {
1384
+ try {
1385
+ yield ((_c = this.blePlxManager) === null || _c === void 0 ? void 0 : _c.cancelTransaction(previousNotifyTransactionId));
1386
+ }
1387
+ catch (error) {
1388
+ Log === null || Log === void 0 ? void 0 : Log.debug(`[ReactNativeBleTransport] cancel notify after Protocol ${protocol} probe failed:`, (error === null || error === void 0 ? void 0 : error.message) || error);
1389
+ }
1390
+ }
1391
+ const monitorToken = this.nextMonitorToken;
1392
+ this.nextMonitorToken += 1;
1393
+ const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
1394
+ transport.monitorToken = monitorToken;
1395
+ transport.notifyTransactionId = notifyTransactionId;
1396
+ this.monitorTokens.set(uuid, monitorToken);
1397
+ transport.notifySubscription = this._monitorCharacteristic(transport.notifyCharacteristic, uuid, monitorToken, notifyTransactionId);
1398
+ if (reactNative.Platform.OS === 'ios') {
1399
+ yield new Promise(resolve => {
1400
+ setTimeout(resolve, IOS_NOTIFY_READY_DELAY_MS);
1401
+ });
1402
+ }
1403
+ });
1404
+ }
1405
+ probeProtocolV1(uuid) {
1406
+ return __awaiter(this, void 0, void 0, function* () {
1407
+ if (!this._messages) {
1408
+ return false;
1409
+ }
1410
+ try {
1411
+ this.deviceProtocol.set(uuid, 'V1');
1412
+ yield this.callProtocolV1(uuid, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
1413
+ return true;
1414
+ }
1415
+ catch (error) {
1416
+ this.clearProbeProtocol(uuid, 'V1');
1417
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V1 Initialize probe failed:', error);
1418
+ return false;
1419
+ }
1420
+ });
1421
+ }
1422
+ probeProtocolV2(uuid) {
1423
+ var _a;
1424
+ return __awaiter(this, void 0, void 0, function* () {
1425
+ if (!this._messages || !this._messagesV2) {
1426
+ return false;
1427
+ }
1428
+ this.deviceProtocol.set(uuid, 'V2');
1429
+ (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1430
+ const detected = yield transport.probeProtocolV2({
1431
+ call: (name, data, options) => this.callProtocolV2(uuid, name, data, options),
1432
+ timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT_MS,
1433
+ logger: Log,
1434
+ logPrefix: 'ProtocolV2 RN-BLE',
1435
+ onProbeFailed: () => {
1436
+ var _a;
1437
+ (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1438
+ this.resetProtocolV2Frames(uuid);
1439
+ },
1440
+ });
1441
+ if (!detected) {
1442
+ this.clearProbeProtocol(uuid, 'V2');
1443
+ }
1444
+ return detected;
1445
+ });
1446
+ }
1447
+ handleProtocolV2Notification(uuid, monitorToken, data) {
1448
+ try {
1449
+ if (this.monitorTokens.get(uuid) !== monitorToken)
1450
+ return;
1451
+ if (data.length === 0)
1452
+ return;
1453
+ const assembler = this.protocolV2Assemblers.get(uuid);
1454
+ if (!assembler)
1455
+ return;
1456
+ for (const frameData of assembler.drain(data)) {
1457
+ this.resolveProtocolV2Frame(uuid, frameData);
1458
+ }
1459
+ }
1460
+ catch (error) {
1461
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 notification error:', error);
1462
+ const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1463
+ this.rejectProtocolV2Frames(uuid, notifyError);
1464
+ this.protocolV2Links
1465
+ .invalidateLink(uuid, `Protocol V2 notification error: ${error}`)
1466
+ .catch(invalidateError => Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 notify cleanup failed:', invalidateError));
1467
+ }
1468
+ }
1469
+ getProtocolV2FrameQueue(uuid) {
1470
+ let queue = this.protocolV2FrameQueues.get(uuid);
1471
+ if (!queue) {
1472
+ queue = [];
1473
+ this.protocolV2FrameQueues.set(uuid, queue);
1474
+ }
1475
+ return queue;
1476
+ }
1477
+ resolveProtocolV2Frame(uuid, frame) {
1478
+ const framePromise = this.protocolV2FramePromises.get(uuid);
1479
+ if (framePromise) {
1480
+ framePromise.resolve(frame);
1481
+ this.protocolV2FramePromises.delete(uuid);
1482
+ return;
1483
+ }
1484
+ this.getProtocolV2FrameQueue(uuid).push(frame);
1485
+ }
1486
+ rejectAllProtocolV2Frames(error) {
1487
+ this.protocolV2FrameQueues.clear();
1488
+ for (const framePromise of this.protocolV2FramePromises.values()) {
1489
+ framePromise.reject(error);
1490
+ }
1491
+ this.protocolV2FramePromises.clear();
1492
+ }
1493
+ resetProtocolV2Frames(uuid) {
1494
+ this.protocolV2FrameQueues.delete(uuid);
1495
+ this.protocolV2FramePromises.delete(uuid);
1496
+ }
1497
+ rejectProtocolV2Frames(uuid, error) {
1498
+ this.protocolV2FrameQueues.delete(uuid);
1499
+ const framePromise = this.protocolV2FramePromises.get(uuid);
1500
+ if (framePromise) {
1501
+ this.protocolV2FramePromises.delete(uuid);
1502
+ framePromise.reject(error);
1503
+ }
1504
+ }
1505
+ readProtocolV2Frame(uuid) {
1506
+ return __awaiter(this, void 0, void 0, function* () {
1507
+ const queuedFrame = this.getProtocolV2FrameQueue(uuid).shift();
1508
+ if (queuedFrame) {
1509
+ return queuedFrame;
1510
+ }
1511
+ const framePromise = hdShared.createDeferred();
1512
+ this.protocolV2FramePromises.set(uuid, framePromise);
1513
+ try {
1514
+ return yield framePromise.promise;
1515
+ }
1516
+ finally {
1517
+ if (this.protocolV2FramePromises.get(uuid) === framePromise) {
1518
+ this.protocolV2FramePromises.delete(uuid);
1519
+ }
1520
+ }
1521
+ });
1522
+ }
1523
+ writeProtocolV2Frame(transport, frame) {
1524
+ return __awaiter(this, void 0, void 0, function* () {
1525
+ const tuning = getProtocolV2BleTuning();
1526
+ const packetCapacity = resolveProtocolV2PacketCapacity({
1527
+ platform: reactNative.Platform.OS,
1528
+ iosPacketLength: tuning.iosPacketLength,
1529
+ androidPacketLength: tuning.androidPacketLength,
1530
+ mtu: reactNative.Platform.OS === 'android' ? transport.mtuSize : undefined,
1531
+ });
1532
+ for (let offset = 0; offset < frame.length; offset += packetCapacity) {
1533
+ const chunk = frame.slice(offset, offset + packetCapacity);
1534
+ const base64 = buffer.Buffer.from(chunk).toString('base64');
1535
+ yield transport.writeCharacteristic.writeWithoutResponse(base64);
1536
+ }
1537
+ });
1538
+ }
1539
+ callProtocolV2(uuid, name, data, options) {
1540
+ return __awaiter(this, void 0, void 0, function* () {
1541
+ if (!this._messages || !this._messagesV2) {
1542
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
1543
+ }
1544
+ const callOptions = Object.assign(Object.assign({}, options), { timeoutMs: options === null || options === void 0 ? void 0 : options.timeoutMs });
1545
+ const highVolumeWrite = transport.LogBlockCommand.has(name);
1546
+ if (highVolumeWrite) {
1547
+ const tuning = getProtocolV2BleTuning();
1548
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Protocol V2 high-volume write configured', {
1549
+ name,
1550
+ writeMode: 'withoutResponse',
1551
+ packetCapacity: reactNative.Platform.OS === 'ios' ? tuning.iosPacketLength : tuning.androidPacketLength,
1552
+ });
1553
+ }
1554
+ try {
1555
+ return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, callOptions);
1556
+ }
1557
+ catch (e) {
1558
+ Log === null || Log === void 0 ? void 0 : Log.error('[ReactNativeBleTransport] Protocol V2 call error:', e);
1559
+ throw e;
1560
+ }
1561
+ });
1562
+ }
1563
+ createProtocolV2Adapter(uuid) {
1564
+ var _a;
1565
+ const generation = (_a = this.monitorTokens.get(uuid)) !== null && _a !== void 0 ? _a : 0;
1566
+ const assertCurrentGeneration = () => {
1567
+ if (this.monitorTokens.get(uuid) !== generation) {
1568
+ throw new Error(`Protocol V2 monitor generation changed for ${uuid}`);
1569
+ }
1570
+ };
1571
+ return {
1572
+ router: transport.PROTOCOL_V2_CHANNEL_BLE_UART,
1573
+ maxFrameBytes: transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES,
1574
+ generation,
1575
+ prepareCall: () => {
1576
+ var _a;
1577
+ assertCurrentGeneration();
1578
+ (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1579
+ this.resetProtocolV2Frames(uuid);
1580
+ },
1581
+ writeFrame: (frame) => __awaiter(this, void 0, void 0, function* () {
1582
+ assertCurrentGeneration();
1583
+ const currentTransport = this.getCachedTransport(uuid);
1584
+ yield this.writeProtocolV2Frame(currentTransport, frame);
1585
+ }),
1586
+ readFrame: () => __awaiter(this, void 0, void 0, function* () {
1587
+ assertCurrentGeneration();
1588
+ const rxFrame = yield this.readProtocolV2Frame(uuid);
1589
+ if (!(rxFrame instanceof Uint8Array)) {
1590
+ throw new Error('Protocol V2 response is not Uint8Array');
1591
+ }
1592
+ return rxFrame;
1593
+ }),
1594
+ reset: (reason) => {
1595
+ var _a;
1596
+ (_a = this.protocolV2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1597
+ this.rejectProtocolV2Frames(uuid, new Error(reason));
1598
+ },
1599
+ logger: Log,
1600
+ logPrefix: 'ProtocolV2 RN-BLE',
1601
+ createTimeoutError: (messageName, timeout) => hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, `BLE response timeout after ${timeout}ms for ${messageName}`),
1602
+ };
1603
+ }
1604
+ getProtocolType(path) {
1605
+ return this.deviceProtocol.get(path);
1606
+ }
961
1607
  }
962
1608
 
963
- module.exports = ReactNativeBleTransport;
1609
+ exports.configureProtocolV2BleTuning = configureProtocolV2BleTuning;
1610
+ exports["default"] = ReactNativeBleTransport;
1611
+ exports.getProtocolV2BleTuning = getProtocolV2BleTuning;
1612
+ exports.resetProtocolV2BleTuning = resetProtocolV2BleTuning;