@onekeyfe/hd-transport-react-native 1.1.27-alpha.41 → 1.1.27-alpha.43

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.
@@ -1 +1 @@
1
- {"version":3,"file":"BleManager.d.ts","sourceRoot":"","sources":["../src/BleManager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC;AAWnE,eAAO,MAAM,qBAAqB,iBAAkB,MAAM,EAAE,0BACZ,CAAC;AAEjD,eAAO,MAAM,gBAAgB,6BAAwC,CAAC;AAEtE,eAAO,MAAM,UAAU,eAAgB,MAAM;;;EAAoC,CAAC;AAElF,eAAO,MAAM,iBAAiB,kBAAmB,MAAM,KAAG,QAAQ,UAAU,GAAG,SAAS,CAkCpF,CAAC"}
1
+ {"version":3,"file":"BleManager.d.ts","sourceRoot":"","sources":["../src/BleManager.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC;AASnE,eAAO,MAAM,qBAAqB,iBAAkB,MAAM,EAAE,0BACZ,CAAC;AAEjD,eAAO,MAAM,gBAAgB,6BAAwC,CAAC;AAEtE,eAAO,MAAM,UAAU,eAAgB,MAAM;;;EAAoC,CAAC;AAElF,eAAO,MAAM,iBAAiB,kBAAmB,MAAM,KAAG,QAAQ,UAAU,GAAG,SAAS,CA+BpF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"BleTransport.d.ts","sourceRoot":"","sources":["../src/BleTransport.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAMjF,MAAM,CAAC,OAAO,OAAO,YAAY;IAC/B,EAAE,EAAE,MAAM,CAAC;IAEX,IAAI,SAA6B;IAEjC,MAAM,EAAE,MAAM,CAAC;IAEf,OAAO,SAAM;IAEb,mBAAmB,EAAE,cAAc,CAAC;IAEpC,oBAAoB,EAAE,cAAc,CAAC;IAErC,kBAAkB,CAAC,EAAE,YAAY,CAAC;IAElC,sBAAsB,CAAC,EAAE,YAAY,CAAC;IAEtC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAE7B,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,MAAM,CAAC,WAAW,SAAK;IAEvB,MAAM,CAAC,WAAW,SAAQ;gBAGxB,MAAM,EAAE,MAAM,EACd,mBAAmB,EAAE,cAAc,EACnC,oBAAoB,EAAE,cAAc;IAchC,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,SAA2B,GAAG,OAAO,CAAC,IAAI,CAAC;CA2BzF"}
1
+ {"version":3,"file":"BleTransport.d.ts","sourceRoot":"","sources":["../src/BleTransport.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAIjF,MAAM,CAAC,OAAO,OAAO,YAAY;IAC/B,EAAE,EAAE,MAAM,CAAC;IAEX,IAAI,SAA6B;IAEjC,MAAM,EAAE,MAAM,CAAC;IAEf,OAAO,SAAM;IAEb,mBAAmB,EAAE,cAAc,CAAC;IAEpC,oBAAoB,EAAE,cAAc,CAAC;IAErC,kBAAkB,CAAC,EAAE,YAAY,CAAC;IAElC,sBAAsB,CAAC,EAAE,YAAY,CAAC;IAEtC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAE7B,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,MAAM,CAAC,WAAW,SAAK;IAEvB,MAAM,CAAC,WAAW,SAAQ;gBAGxB,MAAM,EAAE,MAAM,EACd,mBAAmB,EAAE,cAAc,EACnC,oBAAoB,EAAE,cAAc;IAchC,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,SAA2B,GAAG,OAAO,CAAC,IAAI,CAAC;CA2BzF"}
@@ -1 +1 @@
1
- {"version":3,"file":"bleStrategy.d.ts","sourceRoot":"","sources":["../src/bleStrategy.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,SAAS,GAAG,MAAM,CAAC;AAErD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,sBAAsB,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACxC,yBAAyB,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,cAAc,GAAG,iBAAiB,CAAC;AAE9D,wBAAgB,qBAAqB,CAAC,cAAc,EAAE,kBAAkB,WAIvE;AAED,wBAAgB,mBAAmB,CACjC,cAAc,EAAE,kBAAkB,EAClC,aAAa,GAAE,YAAgC,GAC9C,YAAY,CAkBd;AAED,wBAAgB,+BAA+B,CAAC,EAC9C,QAAQ,EACR,eAAmC,EACnC,mBAA2C,EAC3C,GAAG,GACJ,EAAE;IACD,QAAQ,EAAE,WAAW,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB,UAWA"}
1
+ {"version":3,"file":"bleStrategy.d.ts","sourceRoot":"","sources":["../src/bleStrategy.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,SAAS,GAAG,MAAM,CAAC;AAErD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,sBAAsB,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACxC,yBAAyB,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,cAAc,GAAG,iBAAiB,CAAC;AAE9D,wBAAgB,qBAAqB,CAAC,cAAc,EAAE,kBAAkB,WAEvE;AAED,wBAAgB,mBAAmB,CACjC,cAAc,EAAE,kBAAkB,EAClC,aAAa,GAAE,YAAgC,GAC9C,YAAY,CAkBd;AAED,wBAAgB,+BAA+B,CAAC,EAC9C,QAAQ,EACR,eAAmC,EACnC,mBAA2C,EAC3C,GAAG,GACJ,EAAE;IACD,QAAQ,EAAE,WAAW,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB,UAWA"}
package/dist/index.d.ts CHANGED
@@ -1,9 +1,26 @@
1
1
  import * as transport from '@onekeyfe/hd-transport';
2
2
  import transport__default, { ProtocolType, OneKeyDeviceInfoBase, TransportCallOptions } from '@onekeyfe/hd-transport';
3
- import { Device, BleManager, Characteristic, Subscription } from 'react-native-ble-plx';
3
+ import { Device, Characteristic, Subscription, BleManager } from 'react-native-ble-plx';
4
4
  import { Deferred } from '@onekeyfe/hd-shared';
5
5
  import EventEmitter from 'events';
6
6
 
7
+ declare class BleTransport {
8
+ id: string;
9
+ name: string;
10
+ device: Device;
11
+ mtuSize: number;
12
+ writeCharacteristic: Characteristic;
13
+ notifyCharacteristic: Characteristic;
14
+ notifySubscription?: Subscription;
15
+ disconnectSubscription?: Subscription;
16
+ notifyTransactionId?: string;
17
+ monitorToken?: number;
18
+ static MAX_RETRIES: number;
19
+ static RETRY_DELAY: number;
20
+ constructor(device: Device, writeCharacteristic: Characteristic, notifyCharacteristic: Characteristic);
21
+ writeWithRetry(data: string, retryCount?: number): Promise<void>;
22
+ }
23
+
7
24
  type TransportOptions = {
8
25
  scanTimeout?: number;
9
26
  };
@@ -13,6 +30,10 @@ type BleAcquireInput = {
13
30
  expectedProtocol?: ProtocolType;
14
31
  };
15
32
 
33
+ type ResolvedBleCharacteristics = {
34
+ writeCharacteristic: Characteristic;
35
+ notifyCharacteristic: Characteristic;
36
+ };
16
37
  type ProtocolV2BleTuning = {
17
38
  iosPacketLength?: number;
18
39
  androidPacketLength?: number;
@@ -42,6 +63,7 @@ declare class ReactNativeBleTransport {
42
63
  scanTimeout: number;
43
64
  runPromise: Deferred<any> | null;
44
65
  emitter?: EventEmitter;
66
+ firmwareUploadWriteRecoveryIds: Set<string>;
45
67
  private deviceProtocol;
46
68
  private deviceProtocolHints;
47
69
  private protocolV2Assemblers;
@@ -57,6 +79,9 @@ declare class ReactNativeBleTransport {
57
79
  configureProtocolV2(signedData: any): void;
58
80
  listen(): void;
59
81
  getPlxManager(): Promise<BleManager>;
82
+ resolveCharacteristics(device: Device): Promise<ResolvedBleCharacteristics>;
83
+ attachDisconnectSubscription(transport: BleTransport, device: Device, uuid: string): void;
84
+ reconnectFirmwareUploadTransport(uuid: string, transport: BleTransport): Promise<void>;
60
85
  enumerate(): Promise<IOneKeyDevice[]>;
61
86
  acquire(input: BleAcquireInput): Promise<{
62
87
  uuid: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -60,8 +60,6 @@ const Logger = bleLogger;
60
60
  const getConnectedDeviceIds = (serviceUuids) => BleUtils__default["default"].getConnectedPeripherals(serviceUuids);
61
61
  const pairDevice = (macAddress) => BleUtils__default["default"].pairDevice(macAddress);
62
62
  const onDeviceBondState = (bleMacAddress) => new Promise((resolve, reject) => {
63
- let timeout;
64
- let cleanupListener;
65
63
  const cleanup = () => {
66
64
  if (timeout) {
67
65
  clearTimeout(timeout);
@@ -69,11 +67,11 @@ const onDeviceBondState = (bleMacAddress) => new Promise((resolve, reject) => {
69
67
  if (cleanupListener)
70
68
  cleanupListener();
71
69
  };
72
- timeout = setTimeout(() => {
70
+ const timeout = setTimeout(() => {
73
71
  cleanup();
74
72
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceNotBonded, 'device is not bonded'));
75
73
  }, 60 * 1000);
76
- cleanupListener = BleUtils__default["default"].onDeviceBondState(peripheral => {
74
+ const cleanupListener = BleUtils__default["default"].onDeviceBondState(peripheral => {
77
75
  var _a;
78
76
  if (((_a = peripheral.id) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== bleMacAddress.toLowerCase()) {
79
77
  return;
@@ -266,6 +264,35 @@ BleTransport.RETRY_DELAY = 2000;
266
264
  const { check, ProtocolV1, parseConfigure } = transport__default["default"];
267
265
  const Log = bleLogger;
268
266
  const transportCache = {};
267
+ const FIRMWARE_UPLOAD_WRITE_BURST_SIZE = reactNative.Platform.OS === 'ios' ? 4 : 5;
268
+ const FIRMWARE_UPLOAD_WRITE_PAUSE_MS = reactNative.Platform.OS === 'ios' ? 8 : 10;
269
+ const FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS = reactNative.Platform.OS === 'ios' ? 24 : 30;
270
+ const FIRMWARE_UPLOAD_WRITE_MAX_RETRIES = 8;
271
+ const FIRMWARE_UPLOAD_RECONNECT_RETRY_DELAY_MS = 2000;
272
+ const ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH = 192;
273
+ const FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY = reactNative.Platform.OS === 'ios' ? IOS_PACKET_LENGTH : ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH;
274
+ const ANDROID_GATT_CONGESTED_STATUS = 143;
275
+ const delay = (ms) => new Promise(resolve => {
276
+ setTimeout(resolve, ms);
277
+ });
278
+ const getFirmwareUploadWriteRetryType = (error) => {
279
+ if (!error || typeof error !== 'object')
280
+ return null;
281
+ const bleWriteError = error;
282
+ if (bleWriteError.errorCode === reactNativeBlePlx.BleErrorCode.DeviceDisconnected ||
283
+ bleWriteError.errorCode === reactNativeBlePlx.BleErrorCode.CharacteristicNotFound) {
284
+ return 'reconnectable';
285
+ }
286
+ if (bleWriteError.androidErrorCode === ANDROID_GATT_CONGESTED_STATUS ||
287
+ bleWriteError.status === ANDROID_GATT_CONGESTED_STATUS) {
288
+ return 'congested';
289
+ }
290
+ const text = [bleWriteError.reason, bleWriteError.message, bleWriteError.name]
291
+ .filter(value => typeof value === 'string')
292
+ .join(' ');
293
+ return /GATT_CONGESTED|status\s*[:=]?\s*143/.test(text) ? 'congested' : null;
294
+ };
295
+ const resolveFirmwareUploadRetryDelay = (attempt, baseDelayMs = 200, maxDelayMs = 1200) => Math.min(baseDelayMs * Math.pow(2, attempt), maxDelayMs);
269
296
  const BLE_RESPONSE_TIMEOUT_MS = 30000;
270
297
  const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
271
298
  const PROTOCOL_V2_PROBE_TIMEOUT_MS = 10000;
@@ -275,9 +302,6 @@ const ANDROID_NOTIFY_READY_DELAY_MS = 300;
275
302
  const HIGH_VOLUME_WRITE_BURST_SIZE = reactNative.Platform.OS === 'ios' ? 4 : 6;
276
303
  const HIGH_VOLUME_WRITE_PAUSE_MS = reactNative.Platform.OS === 'ios' ? 6 : 2;
277
304
  const HIGH_VOLUME_WRITE_FLUSH_DELAY_MS = reactNative.Platform.OS === 'ios' ? 20 : 8;
278
- const delay = (ms) => new Promise(resolve => {
279
- setTimeout(resolve, ms);
280
- });
281
305
  const DEFAULT_PROTOCOL_V2_BLE_TUNING = {
282
306
  iosPacketLength: IOS_PACKET_LENGTH,
283
307
  androidPacketLength: ANDROID_PACKET_LENGTH,
@@ -384,6 +408,7 @@ class ReactNativeBleTransport {
384
408
  this.stopped = false;
385
409
  this.scanTimeout = DEVICE_SCAN_TIMEOUT_MS;
386
410
  this.runPromise = null;
411
+ this.firmwareUploadWriteRecoveryIds = new Set();
387
412
  this.deviceProtocol = new Map();
388
413
  this.deviceProtocolHints = new Map();
389
414
  this.protocolV2Assemblers = new Map();
@@ -416,6 +441,163 @@ class ReactNativeBleTransport {
416
441
  this.blePlxManager = new reactNativeBlePlx.BleManager();
417
442
  return Promise.resolve(this.blePlxManager);
418
443
  }
444
+ resolveCharacteristics(device) {
445
+ var _a, _b, _c, _d;
446
+ return __awaiter(this, void 0, void 0, function* () {
447
+ yield device.discoverAllServicesAndCharacteristics();
448
+ let infos = tryToGetConfiguration(device);
449
+ let characteristics;
450
+ if (!infos) {
451
+ for (const serviceUuid of getBluetoothServiceUuids()) {
452
+ try {
453
+ characteristics = yield device.characteristicsForService(serviceUuid);
454
+ infos = getInfosForServiceUuid(serviceUuid, 'classic');
455
+ break;
456
+ }
457
+ catch (e) {
458
+ Log === null || Log === void 0 ? void 0 : Log.error(e);
459
+ }
460
+ }
461
+ }
462
+ let fallbackServiceUuid;
463
+ if (!infos) {
464
+ const services = yield device.services();
465
+ 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));
466
+ const knownService = services.find(service => getInfosForServiceUuid(service.uuid, 'classic'));
467
+ const fallbackService = (_a = knownService !== null && knownService !== void 0 ? knownService : services.find(service => !isGenericBleService(service.uuid))) !== null && _a !== void 0 ? _a : services[0];
468
+ if (fallbackService) {
469
+ fallbackServiceUuid = fallbackService.uuid;
470
+ characteristics = yield device.characteristicsForService(fallbackService.uuid);
471
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Using fallback BLE service:', fallbackService.uuid);
472
+ }
473
+ }
474
+ if (!infos && !fallbackServiceUuid) {
475
+ try {
476
+ Log === null || Log === void 0 ? void 0 : Log.debug('cancel connection when service not found');
477
+ yield device.cancelConnection();
478
+ }
479
+ catch (e) {
480
+ Log === null || Log === void 0 ? void 0 : Log.debug('cancel connection error when service not found: ', e.message || e.reason);
481
+ }
482
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
483
+ }
484
+ const serviceUuid = (_b = infos === null || infos === void 0 ? void 0 : infos.serviceUuid) !== null && _b !== void 0 ? _b : fallbackServiceUuid;
485
+ const writeUuid = (_c = infos === null || infos === void 0 ? void 0 : infos.writeUuid) !== null && _c !== void 0 ? _c : '00000002-0000-1000-8000-00805f9b34fb';
486
+ const notifyUuid = (_d = infos === null || infos === void 0 ? void 0 : infos.notifyUuid) !== null && _d !== void 0 ? _d : '00000003-0000-1000-8000-00805f9b34fb';
487
+ if (!serviceUuid) {
488
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
489
+ }
490
+ if (!characteristics) {
491
+ characteristics = yield device.characteristicsForService(serviceUuid);
492
+ }
493
+ if (!characteristics) {
494
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotFound);
495
+ }
496
+ let writeCharacteristic;
497
+ let notifyCharacteristic;
498
+ for (const c of characteristics) {
499
+ if (isSameBleUuid(c.uuid, writeUuid)) {
500
+ writeCharacteristic = c;
501
+ }
502
+ else if (isSameBleUuid(c.uuid, notifyUuid)) {
503
+ notifyCharacteristic = c;
504
+ }
505
+ }
506
+ if (!writeCharacteristic) {
507
+ throw hdShared.ERRORS.TypedError('BLECharacteristicNotFound: write characteristic not found');
508
+ }
509
+ if (!notifyCharacteristic) {
510
+ throw hdShared.ERRORS.TypedError('BLECharacteristicNotFound: notify characteristic not found');
511
+ }
512
+ if (!hasWritableCapability(writeCharacteristic)) {
513
+ throw hdShared.ERRORS.TypedError('BLECharacteristicNotWritable: write characteristic not writable');
514
+ }
515
+ if (!notifyCharacteristic.isNotifiable) {
516
+ throw hdShared.ERRORS.TypedError('BLECharacteristicNotNotifiable: notify characteristic not notifiable');
517
+ }
518
+ return {
519
+ writeCharacteristic,
520
+ notifyCharacteristic,
521
+ };
522
+ });
523
+ }
524
+ attachDisconnectSubscription(transport, device, uuid) {
525
+ var _a;
526
+ (_a = transport.disconnectSubscription) === null || _a === void 0 ? void 0 : _a.remove();
527
+ transport.disconnectSubscription = device.onDisconnected(() => {
528
+ var _a;
529
+ if (this.firmwareUploadWriteRecoveryIds.has(uuid)) {
530
+ Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored during FirmwareUpload write recovery: ', uuid);
531
+ return;
532
+ }
533
+ if (transportCache[uuid] !== transport) {
534
+ Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored for stale transport: ', device === null || device === void 0 ? void 0 : device.id);
535
+ return;
536
+ }
537
+ try {
538
+ Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect: ', device === null || device === void 0 ? void 0 : device.id);
539
+ (_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit('device-disconnect', {
540
+ name: device === null || device === void 0 ? void 0 : device.name,
541
+ id: device === null || device === void 0 ? void 0 : device.id,
542
+ connectId: device === null || device === void 0 ? void 0 : device.id,
543
+ });
544
+ if (this.runPromise) {
545
+ const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError);
546
+ this.runPromise.reject(error);
547
+ this.rejectAllProtocolV2Frames(error);
548
+ }
549
+ }
550
+ catch (e) {
551
+ Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect error: ', e);
552
+ }
553
+ finally {
554
+ this.release(uuid, true);
555
+ }
556
+ });
557
+ }
558
+ reconnectFirmwareUploadTransport(uuid, transport) {
559
+ var _a, _b;
560
+ return __awaiter(this, void 0, void 0, function* () {
561
+ this.firmwareUploadWriteRecoveryIds.add(uuid);
562
+ try {
563
+ (_a = transport.disconnectSubscription) === null || _a === void 0 ? void 0 : _a.remove();
564
+ transport.disconnectSubscription = undefined;
565
+ (_b = transport.notifySubscription) === null || _b === void 0 ? void 0 : _b.remove();
566
+ transport.notifySubscription = undefined;
567
+ let { device } = transport;
568
+ const isConnected = yield device.isConnected().catch(() => false);
569
+ if (!isConnected) {
570
+ try {
571
+ device = yield device.connect(connectOptions);
572
+ }
573
+ catch (e) {
574
+ if (e.errorCode === reactNativeBlePlx.BleErrorCode.DeviceMTUChangeFailed ||
575
+ e.errorCode === reactNativeBlePlx.BleErrorCode.OperationCancelled) {
576
+ device = yield device.connect();
577
+ }
578
+ else if (e.errorCode !== reactNativeBlePlx.BleErrorCode.DeviceAlreadyConnected) {
579
+ throw e;
580
+ }
581
+ }
582
+ }
583
+ const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristics(device);
584
+ transport.device = device;
585
+ transport.writeCharacteristic = writeCharacteristic;
586
+ transport.notifyCharacteristic = notifyCharacteristic;
587
+ const monitorToken = this.nextMonitorToken;
588
+ this.nextMonitorToken += 1;
589
+ const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
590
+ transport.monitorToken = monitorToken;
591
+ transport.notifyTransactionId = notifyTransactionId;
592
+ this.monitorTokens.set(uuid, monitorToken);
593
+ transport.notifySubscription = this._monitorCharacteristic(notifyCharacteristic, uuid, monitorToken, notifyTransactionId);
594
+ this.attachDisconnectSubscription(transport, device, uuid);
595
+ }
596
+ finally {
597
+ this.firmwareUploadWriteRecoveryIds.delete(uuid);
598
+ }
599
+ });
600
+ }
419
601
  enumerate() {
420
602
  return __awaiter(this, void 0, void 0, function* () {
421
603
  return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
@@ -522,7 +704,7 @@ class ReactNativeBleTransport {
522
704
  });
523
705
  }
524
706
  acquire(input) {
525
- var _a, _b, _c, _d, _e, _f;
707
+ var _a, _b;
526
708
  return __awaiter(this, void 0, void 0, function* () {
527
709
  const { uuid, forceCleanRunPromise, expectedProtocol } = input;
528
710
  if (!uuid) {
@@ -629,82 +811,10 @@ class ReactNativeBleTransport {
629
811
  }
630
812
  }
631
813
  device = yield requestAndroidMtu(device);
632
- yield device.discoverAllServicesAndCharacteristics();
633
- let infos = tryToGetConfiguration(device);
634
- let characteristics;
635
- let fallbackServiceUuid;
636
- if (!infos) {
637
- for (const serviceUuid of getBluetoothServiceUuids()) {
638
- try {
639
- characteristics = yield device.characteristicsForService(serviceUuid);
640
- infos = getInfosForServiceUuid(serviceUuid, 'classic');
641
- break;
642
- }
643
- catch (e) {
644
- Log === null || Log === void 0 ? void 0 : Log.error(e);
645
- }
646
- }
647
- }
648
- if (!infos) {
649
- const services = yield device.services();
650
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Known OneKey service UUID not found, discovered services:', services === null || services === void 0 ? void 0 : services.map(service => service.uuid));
651
- const knownService = services.find(service => getInfosForServiceUuid(service.uuid, 'classic'));
652
- const fallbackService = (_a = knownService !== null && knownService !== void 0 ? knownService : services.find(service => !isGenericBleService(service.uuid))) !== null && _a !== void 0 ? _a : services[0];
653
- if (fallbackService) {
654
- fallbackServiceUuid = fallbackService.uuid;
655
- characteristics = yield device.characteristicsForService(fallbackService.uuid);
656
- Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] Using fallback BLE service:', fallbackService.uuid);
657
- }
658
- }
659
- if (!infos) {
660
- if (!fallbackServiceUuid) {
661
- try {
662
- Log === null || Log === void 0 ? void 0 : Log.debug('cancel connection when service not found');
663
- yield device.cancelConnection();
664
- }
665
- catch (e) {
666
- Log === null || Log === void 0 ? void 0 : Log.debug('cancel connection error when service not found: ', e.message || e.reason);
667
- }
668
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
669
- }
670
- }
671
- const serviceUuid = (_b = infos === null || infos === void 0 ? void 0 : infos.serviceUuid) !== null && _b !== void 0 ? _b : fallbackServiceUuid;
672
- const writeUuid = (_c = infos === null || infos === void 0 ? void 0 : infos.writeUuid) !== null && _c !== void 0 ? _c : '00000002-0000-1000-8000-00805f9b34fb';
673
- const notifyUuid = (_d = infos === null || infos === void 0 ? void 0 : infos.notifyUuid) !== null && _d !== void 0 ? _d : '00000003-0000-1000-8000-00805f9b34fb';
674
- if (!serviceUuid) {
675
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleServiceNotFound);
676
- }
677
- if (!characteristics) {
678
- characteristics = yield device.characteristicsForService(serviceUuid);
679
- }
680
- if (!characteristics) {
681
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleCharacteristicNotFound);
682
- }
683
- let writeCharacteristic;
684
- let notifyCharacteristic;
685
- for (const c of characteristics) {
686
- if (isSameBleUuid(c.uuid, writeUuid)) {
687
- writeCharacteristic = c;
688
- }
689
- else if (isSameBleUuid(c.uuid, notifyUuid)) {
690
- notifyCharacteristic = c;
691
- }
692
- }
693
- if (!writeCharacteristic) {
694
- throw hdShared.ERRORS.TypedError('BLECharacteristicNotFound: write characteristic not found');
695
- }
696
- if (!notifyCharacteristic) {
697
- throw hdShared.ERRORS.TypedError('BLECharacteristicNotFound: notify characteristic not found');
698
- }
699
- if (!hasWritableCapability(writeCharacteristic)) {
700
- throw hdShared.ERRORS.TypedError('BLECharacteristicNotWritable: write characteristic not writable');
701
- }
702
- if (!notifyCharacteristic.isNotifiable) {
703
- throw hdShared.ERRORS.TypedError('BLECharacteristicNotNotifiable: notify characteristic not notifiable');
704
- }
814
+ const { writeCharacteristic, notifyCharacteristic } = yield this.resolveCharacteristics(device);
705
815
  const protocolHint = expectedProtocol
706
816
  ? undefined
707
- : (_e = this.deviceProtocolHints.get(uuid)) !== null && _e !== void 0 ? _e : inferProtocolHintFromDeviceName(getDeviceDisplayName(device));
817
+ : (_a = this.deviceProtocolHints.get(uuid)) !== null && _a !== void 0 ? _a : inferProtocolHintFromDeviceName(getDeviceDisplayName(device));
708
818
  yield this.release(uuid, true);
709
819
  if (protocolHint) {
710
820
  this.deviceProtocolHints.set(uuid, protocolHint);
@@ -731,37 +841,12 @@ class ReactNativeBleTransport {
731
841
  yield delay(ANDROID_NOTIFY_READY_DELAY_MS);
732
842
  }
733
843
  const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint);
734
- (_f = this.emitter) === null || _f === void 0 ? void 0 : _f.emit('device-connect', {
844
+ (_b = this.emitter) === null || _b === void 0 ? void 0 : _b.emit('device-connect', {
735
845
  name: device.name,
736
846
  id: device.id,
737
847
  connectId: device.id,
738
848
  });
739
- transport$1.disconnectSubscription = device.onDisconnected(() => {
740
- var _a;
741
- if (transportCache[uuid] !== transport$1) {
742
- Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect ignored for stale transport: ', device === null || device === void 0 ? void 0 : device.id);
743
- return;
744
- }
745
- try {
746
- Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect: ', device === null || device === void 0 ? void 0 : device.id);
747
- (_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit('device-disconnect', {
748
- name: device === null || device === void 0 ? void 0 : device.name,
749
- id: device === null || device === void 0 ? void 0 : device.id,
750
- connectId: device === null || device === void 0 ? void 0 : device.id,
751
- });
752
- if (this.runPromise) {
753
- const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError);
754
- this.runPromise.reject(error);
755
- this.rejectAllProtocolV2Frames(error);
756
- }
757
- }
758
- catch (e) {
759
- Log === null || Log === void 0 ? void 0 : Log.debug('device disconnect error: ', e);
760
- }
761
- finally {
762
- this.release(uuid, true);
763
- }
764
- });
849
+ this.attachDisconnectSubscription(transport$1, device, uuid);
765
850
  return { uuid, protocolType };
766
851
  });
767
852
  }
@@ -773,6 +858,10 @@ class ReactNativeBleTransport {
773
858
  const isCurrentMonitor = this.monitorTokens.get(uuid) === monitorToken;
774
859
  if (error) {
775
860
  Log === null || Log === void 0 ? void 0 : Log.debug(`error monitor ${characteristic.uuid}, deviceId: ${characteristic.deviceID}: ${error}`);
861
+ if (this.firmwareUploadWriteRecoveryIds.has(uuid)) {
862
+ Log === null || Log === void 0 ? void 0 : Log.debug('notify error ignored during FirmwareUpload write recovery: ', uuid);
863
+ return;
864
+ }
776
865
  if (!isCurrentMonitor) {
777
866
  Log === null || Log === void 0 ? void 0 : Log.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
778
867
  return;
@@ -975,6 +1064,36 @@ class ReactNativeBleTransport {
975
1064
  }
976
1065
  });
977
1066
  }
1067
+ function writeFirmwareUploadChunkedData(buffers, writeFunction, onError) {
1068
+ return __awaiter(this, void 0, void 0, function* () {
1069
+ let index = 0;
1070
+ let packetsWritten = 0;
1071
+ let chunk = ByteBuffer__default["default"].allocate(FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY);
1072
+ while (index < buffers.length) {
1073
+ const buffer = buffers[index].toBuffer();
1074
+ chunk.append(buffer);
1075
+ index += 1;
1076
+ if (chunk.offset === FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY || index >= buffers.length) {
1077
+ chunk.reset();
1078
+ try {
1079
+ yield writeFunction(chunk.toString('base64'));
1080
+ packetsWritten += 1;
1081
+ chunk = ByteBuffer__default["default"].allocate(FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY);
1082
+ if (packetsWritten % FIRMWARE_UPLOAD_WRITE_BURST_SIZE === 0 && index < buffers.length) {
1083
+ yield delay(FIRMWARE_UPLOAD_WRITE_PAUSE_MS);
1084
+ }
1085
+ }
1086
+ catch (e) {
1087
+ onError(e);
1088
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1089
+ }
1090
+ }
1091
+ }
1092
+ if (packetsWritten > 0) {
1093
+ yield delay(FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS);
1094
+ }
1095
+ });
1096
+ }
978
1097
  if (name === 'EmmcFileWrite') {
979
1098
  yield writeChunkedData(buffers, data => transport.writeWithRetry(data), e => {
980
1099
  this.runPromise = null;
@@ -982,8 +1101,53 @@ class ReactNativeBleTransport {
982
1101
  });
983
1102
  }
984
1103
  else if (name === 'FirmwareUpload') {
985
- yield writeChunkedData(buffers, (data) => __awaiter(this, void 0, void 0, function* () {
986
- yield transport.writeCharacteristic.writeWithoutResponse(data);
1104
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] FirmwareUpload write uses throttled BLE packets:', {
1105
+ packetCapacity: FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY,
1106
+ burstSize: FIRMWARE_UPLOAD_WRITE_BURST_SIZE,
1107
+ pauseMs: FIRMWARE_UPLOAD_WRITE_PAUSE_MS,
1108
+ flushDelayMs: FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS,
1109
+ maxRetries: FIRMWARE_UPLOAD_WRITE_MAX_RETRIES,
1110
+ });
1111
+ yield writeFirmwareUploadChunkedData(buffers, (data) => __awaiter(this, void 0, void 0, function* () {
1112
+ let attempt = 0;
1113
+ while (true) {
1114
+ try {
1115
+ yield transport.writeCharacteristic.writeWithoutResponse(data);
1116
+ return;
1117
+ }
1118
+ catch (error) {
1119
+ const retryType = getFirmwareUploadWriteRetryType(error);
1120
+ if (!retryType || attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
1121
+ throw error;
1122
+ }
1123
+ const shouldReconnect = retryType === 'reconnectable';
1124
+ const delayMs = shouldReconnect
1125
+ ? FIRMWARE_UPLOAD_RECONNECT_RETRY_DELAY_MS
1126
+ : resolveFirmwareUploadRetryDelay(attempt);
1127
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] FirmwareUpload write retry:', {
1128
+ attempt: attempt + 1,
1129
+ delayMs,
1130
+ reconnect: shouldReconnect,
1131
+ error,
1132
+ });
1133
+ if (shouldReconnect) {
1134
+ this.firmwareUploadWriteRecoveryIds.add(uuid);
1135
+ }
1136
+ yield delay(delayMs);
1137
+ attempt += 1;
1138
+ if (shouldReconnect) {
1139
+ try {
1140
+ yield this.reconnectFirmwareUploadTransport(uuid, transport);
1141
+ }
1142
+ catch (e) {
1143
+ Log === null || Log === void 0 ? void 0 : Log.debug('[ReactNativeBleTransport] FirmwareUpload reconnect error:', e);
1144
+ if (attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
1145
+ throw e;
1146
+ }
1147
+ }
1148
+ }
1149
+ }
1150
+ }
987
1151
  }), e => {
988
1152
  this.runPromise = null;
989
1153
  Log === null || Log === void 0 ? void 0 : Log.error('writeCharacteristic write error: ', e);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-react-native",
3
- "version": "1.1.27-alpha.41",
3
+ "version": "1.1.27-alpha.43",
4
4
  "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -19,11 +19,11 @@
19
19
  "lint:fix": "eslint . --fix"
20
20
  },
21
21
  "dependencies": {
22
- "@onekeyfe/hd-core": "1.1.27-alpha.41",
23
- "@onekeyfe/hd-shared": "1.1.27-alpha.41",
24
- "@onekeyfe/hd-transport": "1.1.27-alpha.41",
22
+ "@onekeyfe/hd-core": "1.1.27-alpha.43",
23
+ "@onekeyfe/hd-shared": "1.1.27-alpha.43",
24
+ "@onekeyfe/hd-transport": "1.1.27-alpha.43",
25
25
  "@onekeyfe/react-native-ble-utils": "^0.1.4",
26
26
  "react-native-ble-plx": "3.5.1"
27
27
  },
28
- "gitHead": "09cece9f0c9d85f55ae2f7f0458ed7d8833483d2"
28
+ "gitHead": "c2ec7ce56bc9d9807763ac68ab6a8a8e42fbbada"
29
29
  }
package/src/BleManager.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import BleUtils from '@onekeyfe/react-native-ble-utils';
2
2
  import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
3
3
 
4
- import type { Peripheral } from '@onekeyfe/react-native-ble-utils';
5
-
6
4
  import { bleLogger } from './logger';
7
5
 
6
+ import type { Peripheral } from '@onekeyfe/react-native-ble-utils';
7
+
8
8
  const Logger = bleLogger;
9
9
 
10
10
  /**
@@ -21,9 +21,6 @@ export const pairDevice = (macAddress: string) => BleUtils.pairDevice(macAddress
21
21
 
22
22
  export const onDeviceBondState = (bleMacAddress: string): Promise<Peripheral | undefined> =>
23
23
  new Promise((resolve, reject) => {
24
- let timeout: ReturnType<typeof setTimeout> | undefined;
25
- let cleanupListener: (() => void) | undefined;
26
-
27
24
  const cleanup = () => {
28
25
  if (timeout) {
29
26
  clearTimeout(timeout);
@@ -31,12 +28,12 @@ export const onDeviceBondState = (bleMacAddress: string): Promise<Peripheral | u
31
28
  if (cleanupListener) cleanupListener();
32
29
  };
33
30
 
34
- timeout = setTimeout(() => {
31
+ const timeout = setTimeout(() => {
35
32
  cleanup();
36
33
  reject(ERRORS.TypedError(HardwareErrorCode.BleDeviceNotBonded, 'device is not bonded'));
37
34
  }, 60 * 1000);
38
35
 
39
- cleanupListener = BleUtils.onDeviceBondState(peripheral => {
36
+ const cleanupListener = BleUtils.onDeviceBondState(peripheral => {
40
37
  if (peripheral.id?.toLowerCase() !== bleMacAddress.toLowerCase()) {
41
38
  return;
42
39
  }
@@ -1,10 +1,10 @@
1
1
  import { BleErrorCode } from 'react-native-ble-plx';
2
2
  import { wait } from '@onekeyfe/hd-shared';
3
3
 
4
- import type { Characteristic, Device, Subscription } from 'react-native-ble-plx';
5
-
6
4
  import { bleLogger } from './logger';
7
5
 
6
+ import type { Characteristic, Device, Subscription } from 'react-native-ble-plx';
7
+
8
8
  const Log = bleLogger;
9
9
 
10
10
  export default class BleTransport {
@@ -10,9 +10,7 @@ export type BleWriteCapability = {
10
10
  export type BleWriteMode = 'withResponse' | 'withoutResponse';
11
11
 
12
12
  export function hasWritableCapability(characteristic: BleWriteCapability) {
13
- return !!(
14
- characteristic.isWritableWithResponse || characteristic.isWritableWithoutResponse
15
- );
13
+ return !!(characteristic.isWritableWithResponse || characteristic.isWritableWithoutResponse);
16
14
  }
17
15
 
18
16
  export function resolveBleWriteMode(
package/src/index.ts CHANGED
@@ -51,6 +51,60 @@ const { check, ProtocolV1, parseConfigure } = transport;
51
51
  const Log = bleLogger;
52
52
 
53
53
  const transportCache: Record<string, BleTransport> = {};
54
+ const FIRMWARE_UPLOAD_WRITE_BURST_SIZE = Platform.OS === 'ios' ? 4 : 5;
55
+ const FIRMWARE_UPLOAD_WRITE_PAUSE_MS = Platform.OS === 'ios' ? 8 : 10;
56
+ const FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS = Platform.OS === 'ios' ? 24 : 30;
57
+ const FIRMWARE_UPLOAD_WRITE_MAX_RETRIES = 8;
58
+ const FIRMWARE_UPLOAD_RECONNECT_RETRY_DELAY_MS = 2000;
59
+ const ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH = 192;
60
+ const FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY =
61
+ Platform.OS === 'ios' ? IOS_PACKET_LENGTH : ANDROID_FIRMWARE_UPLOAD_PACKET_LENGTH;
62
+ const ANDROID_GATT_CONGESTED_STATUS = 143;
63
+
64
+ type FirmwareUploadWriteRetryType = 'congested' | 'reconnectable';
65
+ type ResolvedBleCharacteristics = {
66
+ writeCharacteristic: Characteristic;
67
+ notifyCharacteristic: Characteristic;
68
+ };
69
+
70
+ const delay = (ms: number) =>
71
+ new Promise<void>(resolve => {
72
+ setTimeout(resolve, ms);
73
+ });
74
+
75
+ const getFirmwareUploadWriteRetryType = (error: unknown): FirmwareUploadWriteRetryType | null => {
76
+ if (!error || typeof error !== 'object') return null;
77
+ const bleWriteError = error as {
78
+ androidErrorCode?: unknown;
79
+ status?: unknown;
80
+ errorCode?: unknown;
81
+ reason?: unknown;
82
+ message?: unknown;
83
+ name?: unknown;
84
+ };
85
+
86
+ if (
87
+ bleWriteError.errorCode === BleErrorCode.DeviceDisconnected ||
88
+ bleWriteError.errorCode === BleErrorCode.CharacteristicNotFound
89
+ ) {
90
+ return 'reconnectable';
91
+ }
92
+
93
+ if (
94
+ bleWriteError.androidErrorCode === ANDROID_GATT_CONGESTED_STATUS ||
95
+ bleWriteError.status === ANDROID_GATT_CONGESTED_STATUS
96
+ ) {
97
+ return 'congested';
98
+ }
99
+
100
+ const text = [bleWriteError.reason, bleWriteError.message, bleWriteError.name]
101
+ .filter(value => typeof value === 'string')
102
+ .join(' ');
103
+ return /GATT_CONGESTED|status\s*[:=]?\s*143/.test(text) ? 'congested' : null;
104
+ };
105
+
106
+ const resolveFirmwareUploadRetryDelay = (attempt: number, baseDelayMs = 200, maxDelayMs = 1200) =>
107
+ Math.min(baseDelayMs * 2 ** attempt, maxDelayMs);
54
108
  const BLE_RESPONSE_TIMEOUT_MS = 30_000;
55
109
  const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
56
110
  const PROTOCOL_V2_PROBE_TIMEOUT_MS = 10_000;
@@ -61,11 +115,6 @@ const HIGH_VOLUME_WRITE_BURST_SIZE = Platform.OS === 'ios' ? 4 : 6;
61
115
  const HIGH_VOLUME_WRITE_PAUSE_MS = Platform.OS === 'ios' ? 6 : 2;
62
116
  const HIGH_VOLUME_WRITE_FLUSH_DELAY_MS = Platform.OS === 'ios' ? 20 : 8;
63
117
 
64
- const delay = (ms: number) =>
65
- new Promise<void>(resolve => {
66
- setTimeout(resolve, ms);
67
- });
68
-
69
118
  export type ProtocolV2BleTuning = {
70
119
  iosPacketLength?: number;
71
120
  androidPacketLength?: number;
@@ -235,6 +284,8 @@ export default class ReactNativeBleTransport {
235
284
 
236
285
  emitter?: EventEmitter;
237
286
 
287
+ firmwareUploadWriteRecoveryIds = new Set<string>();
288
+
238
289
  /** Per-device protocol type detected by active wire-level probe after connect. */
239
290
  private deviceProtocol: Map<string, ProtocolType> = new Map();
240
291
 
@@ -284,6 +335,187 @@ export default class ReactNativeBleTransport {
284
335
  return Promise.resolve(this.blePlxManager);
285
336
  }
286
337
 
338
+ async resolveCharacteristics(device: Device): Promise<ResolvedBleCharacteristics> {
339
+ await device.discoverAllServicesAndCharacteristics();
340
+ let infos = tryToGetConfiguration(device);
341
+ let characteristics: Characteristic[] | undefined;
342
+
343
+ if (!infos) {
344
+ for (const serviceUuid of getBluetoothServiceUuids()) {
345
+ try {
346
+ characteristics = await device.characteristicsForService(serviceUuid);
347
+ infos = getInfosForServiceUuid(serviceUuid, 'classic');
348
+ break;
349
+ } catch (e) {
350
+ Log?.error(e);
351
+ }
352
+ }
353
+ }
354
+
355
+ let fallbackServiceUuid: string | undefined;
356
+
357
+ if (!infos) {
358
+ const services = await device.services();
359
+ Log?.debug(
360
+ '[ReactNativeBleTransport] Known OneKey service UUID not found, discovered services:',
361
+ services?.map(service => service.uuid)
362
+ );
363
+
364
+ const knownService = services.find(service =>
365
+ getInfosForServiceUuid(service.uuid, 'classic')
366
+ );
367
+ const fallbackService =
368
+ knownService ?? services.find(service => !isGenericBleService(service.uuid)) ?? services[0];
369
+
370
+ if (fallbackService) {
371
+ fallbackServiceUuid = fallbackService.uuid;
372
+ characteristics = await device.characteristicsForService(fallbackService.uuid);
373
+ Log?.debug('[ReactNativeBleTransport] Using fallback BLE service:', fallbackService.uuid);
374
+ }
375
+ }
376
+
377
+ if (!infos && !fallbackServiceUuid) {
378
+ try {
379
+ Log?.debug('cancel connection when service not found');
380
+ await device.cancelConnection();
381
+ } catch (e) {
382
+ Log?.debug('cancel connection error when service not found: ', e.message || e.reason);
383
+ }
384
+ throw ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound);
385
+ }
386
+
387
+ const serviceUuid = infos?.serviceUuid ?? fallbackServiceUuid;
388
+ const writeUuid = infos?.writeUuid ?? '00000002-0000-1000-8000-00805f9b34fb';
389
+ const notifyUuid = infos?.notifyUuid ?? '00000003-0000-1000-8000-00805f9b34fb';
390
+
391
+ if (!serviceUuid) {
392
+ throw ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound);
393
+ }
394
+
395
+ if (!characteristics) {
396
+ characteristics = await device.characteristicsForService(serviceUuid);
397
+ }
398
+
399
+ if (!characteristics) {
400
+ throw ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound);
401
+ }
402
+
403
+ let writeCharacteristic;
404
+ let notifyCharacteristic;
405
+ for (const c of characteristics) {
406
+ if (isSameBleUuid(c.uuid, writeUuid)) {
407
+ writeCharacteristic = c;
408
+ } else if (isSameBleUuid(c.uuid, notifyUuid)) {
409
+ notifyCharacteristic = c;
410
+ }
411
+ }
412
+
413
+ if (!writeCharacteristic) {
414
+ throw ERRORS.TypedError('BLECharacteristicNotFound: write characteristic not found');
415
+ }
416
+
417
+ if (!notifyCharacteristic) {
418
+ throw ERRORS.TypedError('BLECharacteristicNotFound: notify characteristic not found');
419
+ }
420
+
421
+ if (!hasWritableCapability(writeCharacteristic)) {
422
+ throw ERRORS.TypedError('BLECharacteristicNotWritable: write characteristic not writable');
423
+ }
424
+
425
+ if (!notifyCharacteristic.isNotifiable) {
426
+ throw ERRORS.TypedError(
427
+ 'BLECharacteristicNotNotifiable: notify characteristic not notifiable'
428
+ );
429
+ }
430
+
431
+ return {
432
+ writeCharacteristic,
433
+ notifyCharacteristic,
434
+ };
435
+ }
436
+
437
+ attachDisconnectSubscription(transport: BleTransport, device: Device, uuid: string) {
438
+ transport.disconnectSubscription?.remove();
439
+ transport.disconnectSubscription = device.onDisconnected(() => {
440
+ if (this.firmwareUploadWriteRecoveryIds.has(uuid)) {
441
+ Log?.debug('device disconnect ignored during FirmwareUpload write recovery: ', uuid);
442
+ return;
443
+ }
444
+ if (transportCache[uuid] !== transport) {
445
+ Log?.debug('device disconnect ignored for stale transport: ', device?.id);
446
+ return;
447
+ }
448
+
449
+ try {
450
+ Log?.debug('device disconnect: ', device?.id);
451
+ this.emitter?.emit('device-disconnect', {
452
+ name: device?.name,
453
+ id: device?.id,
454
+ connectId: device?.id,
455
+ });
456
+ if (this.runPromise) {
457
+ const error = ERRORS.TypedError(HardwareErrorCode.BleConnectedError);
458
+ this.runPromise.reject(error);
459
+ this.rejectAllProtocolV2Frames(error);
460
+ }
461
+ } catch (e) {
462
+ Log?.debug('device disconnect error: ', e);
463
+ } finally {
464
+ this.release(uuid, true);
465
+ }
466
+ });
467
+ }
468
+
469
+ async reconnectFirmwareUploadTransport(uuid: string, transport: BleTransport) {
470
+ this.firmwareUploadWriteRecoveryIds.add(uuid);
471
+ try {
472
+ transport.disconnectSubscription?.remove();
473
+ transport.disconnectSubscription = undefined;
474
+ transport.notifySubscription?.remove();
475
+ transport.notifySubscription = undefined;
476
+
477
+ let { device } = transport;
478
+ const isConnected = await device.isConnected().catch(() => false);
479
+ if (!isConnected) {
480
+ try {
481
+ device = await device.connect(connectOptions);
482
+ } catch (e) {
483
+ if (
484
+ e.errorCode === BleErrorCode.DeviceMTUChangeFailed ||
485
+ e.errorCode === BleErrorCode.OperationCancelled
486
+ ) {
487
+ device = await device.connect();
488
+ } else if (e.errorCode !== BleErrorCode.DeviceAlreadyConnected) {
489
+ throw e;
490
+ }
491
+ }
492
+ }
493
+
494
+ const { writeCharacteristic, notifyCharacteristic } = await this.resolveCharacteristics(
495
+ device
496
+ );
497
+
498
+ transport.device = device;
499
+ transport.writeCharacteristic = writeCharacteristic;
500
+ transport.notifyCharacteristic = notifyCharacteristic;
501
+ const monitorToken = this.nextMonitorToken;
502
+ this.nextMonitorToken += 1;
503
+ const notifyTransactionId = `${uuid}:notify:${monitorToken}`;
504
+ transport.monitorToken = monitorToken;
505
+ transport.notifyTransactionId = notifyTransactionId;
506
+ this.monitorTokens.set(uuid, monitorToken);
507
+ transport.notifySubscription = this._monitorCharacteristic(
508
+ notifyCharacteristic,
509
+ uuid,
510
+ monitorToken,
511
+ notifyTransactionId
512
+ );
513
+ this.attachDisconnectSubscription(transport, device, uuid);
514
+ } finally {
515
+ this.firmwareUploadWriteRecoveryIds.delete(uuid);
516
+ }
517
+ }
518
+
287
519
  /**
288
520
  * 获取设备列表
289
521
  * 在搜索超过超时时间或设备数量大于 5 台时,返回 OneKey 设备,
@@ -435,11 +667,7 @@ export default class ReactNativeBleTransport {
435
667
  cachedProtocol &&
436
668
  (!expectedProtocol || cachedProtocol === expectedProtocol)
437
669
  ) {
438
- Log?.debug(
439
- '[ReactNativeBleTransport] reuse cached BLE transport:',
440
- uuid,
441
- cachedProtocol
442
- );
670
+ Log?.debug('[ReactNativeBleTransport] reuse cached BLE transport:', uuid, cachedProtocol);
443
671
  return { uuid, protocolType: cachedProtocol };
444
672
  }
445
673
 
@@ -547,99 +775,7 @@ export default class ReactNativeBleTransport {
547
775
  }
548
776
 
549
777
  device = await requestAndroidMtu(device);
550
-
551
- await device.discoverAllServicesAndCharacteristics();
552
- let infos = tryToGetConfiguration(device);
553
- let characteristics;
554
- let fallbackServiceUuid: string | undefined;
555
-
556
- if (!infos) {
557
- for (const serviceUuid of getBluetoothServiceUuids()) {
558
- try {
559
- characteristics = await device.characteristicsForService(serviceUuid);
560
- infos = getInfosForServiceUuid(serviceUuid, 'classic');
561
- break;
562
- } catch (e) {
563
- Log?.error(e);
564
- }
565
- }
566
- }
567
-
568
- if (!infos) {
569
- const services = await device.services();
570
- Log?.debug(
571
- '[ReactNativeBleTransport] Known OneKey service UUID not found, discovered services:',
572
- services?.map(service => service.uuid)
573
- );
574
-
575
- const knownService = services.find(service =>
576
- getInfosForServiceUuid(service.uuid, 'classic')
577
- );
578
- const fallbackService =
579
- knownService ?? services.find(service => !isGenericBleService(service.uuid)) ?? services[0];
580
-
581
- if (fallbackService) {
582
- fallbackServiceUuid = fallbackService.uuid;
583
- characteristics = await device.characteristicsForService(fallbackService.uuid);
584
- Log?.debug('[ReactNativeBleTransport] Using fallback BLE service:', fallbackService.uuid);
585
- }
586
- }
587
-
588
- if (!infos) {
589
- if (!fallbackServiceUuid) {
590
- try {
591
- Log?.debug('cancel connection when service not found');
592
- await device.cancelConnection();
593
- } catch (e) {
594
- Log?.debug('cancel connection error when service not found: ', e.message || e.reason);
595
- }
596
- throw ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound);
597
- }
598
- }
599
-
600
- const serviceUuid = infos?.serviceUuid ?? fallbackServiceUuid;
601
- const writeUuid = infos?.writeUuid ?? '00000002-0000-1000-8000-00805f9b34fb';
602
- const notifyUuid = infos?.notifyUuid ?? '00000003-0000-1000-8000-00805f9b34fb';
603
-
604
- if (!serviceUuid) {
605
- throw ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound);
606
- }
607
-
608
- if (!characteristics) {
609
- characteristics = await device.characteristicsForService(serviceUuid);
610
- }
611
-
612
- if (!characteristics) {
613
- throw ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound);
614
- }
615
-
616
- let writeCharacteristic;
617
- let notifyCharacteristic;
618
- for (const c of characteristics) {
619
- if (isSameBleUuid(c.uuid, writeUuid)) {
620
- writeCharacteristic = c;
621
- } else if (isSameBleUuid(c.uuid, notifyUuid)) {
622
- notifyCharacteristic = c;
623
- }
624
- }
625
-
626
- if (!writeCharacteristic) {
627
- throw ERRORS.TypedError('BLECharacteristicNotFound: write characteristic not found');
628
- }
629
-
630
- if (!notifyCharacteristic) {
631
- throw ERRORS.TypedError('BLECharacteristicNotFound: notify characteristic not found');
632
- }
633
-
634
- if (!hasWritableCapability(writeCharacteristic)) {
635
- throw ERRORS.TypedError('BLECharacteristicNotWritable: write characteristic not writable');
636
- }
637
-
638
- if (!notifyCharacteristic.isNotifiable) {
639
- throw ERRORS.TypedError(
640
- 'BLECharacteristicNotNotifiable: notify characteristic not notifiable'
641
- );
642
- }
778
+ const { writeCharacteristic, notifyCharacteristic } = await this.resolveCharacteristics(device);
643
779
 
644
780
  const protocolHint = expectedProtocol
645
781
  ? undefined
@@ -688,30 +824,7 @@ export default class ReactNativeBleTransport {
688
824
  connectId: device.id,
689
825
  });
690
826
 
691
- transport.disconnectSubscription = device.onDisconnected(() => {
692
- if (transportCache[uuid] !== transport) {
693
- Log?.debug('device disconnect ignored for stale transport: ', device?.id);
694
- return;
695
- }
696
-
697
- try {
698
- Log?.debug('device disconnect: ', device?.id);
699
- this.emitter?.emit('device-disconnect', {
700
- name: device?.name,
701
- id: device?.id,
702
- connectId: device?.id,
703
- });
704
- if (this.runPromise) {
705
- const error = ERRORS.TypedError(HardwareErrorCode.BleConnectedError);
706
- this.runPromise.reject(error);
707
- this.rejectAllProtocolV2Frames(error);
708
- }
709
- } catch (e) {
710
- Log?.debug('device disconnect error: ', e);
711
- } finally {
712
- this.release(uuid, true);
713
- }
714
- });
827
+ this.attachDisconnectSubscription(transport, device, uuid);
715
828
 
716
829
  return { uuid, protocolType };
717
830
  }
@@ -732,6 +845,10 @@ export default class ReactNativeBleTransport {
732
845
  error as unknown as string
733
846
  }`
734
847
  );
848
+ if (this.firmwareUploadWriteRecoveryIds.has(uuid)) {
849
+ Log?.debug('notify error ignored during FirmwareUpload write recovery: ', uuid);
850
+ return;
851
+ }
735
852
  if (!isCurrentMonitor) {
736
853
  Log?.debug('monitor error ignored for stale transport: ', uuid, notifyTransactionId);
737
854
  return;
@@ -995,6 +1112,41 @@ export default class ReactNativeBleTransport {
995
1112
  }
996
1113
  }
997
1114
 
1115
+ async function writeFirmwareUploadChunkedData(
1116
+ buffers: ByteBuffer[],
1117
+ writeFunction: (data: string) => Promise<void>,
1118
+ onError: (e: any) => void
1119
+ ) {
1120
+ let index = 0;
1121
+ let packetsWritten = 0;
1122
+ let chunk = ByteBuffer.allocate(FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY);
1123
+
1124
+ while (index < buffers.length) {
1125
+ const buffer = buffers[index].toBuffer();
1126
+ chunk.append(buffer);
1127
+ index += 1;
1128
+
1129
+ if (chunk.offset === FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY || index >= buffers.length) {
1130
+ chunk.reset();
1131
+ try {
1132
+ await writeFunction(chunk.toString('base64'));
1133
+ packetsWritten += 1;
1134
+ chunk = ByteBuffer.allocate(FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY);
1135
+ if (packetsWritten % FIRMWARE_UPLOAD_WRITE_BURST_SIZE === 0 && index < buffers.length) {
1136
+ await delay(FIRMWARE_UPLOAD_WRITE_PAUSE_MS);
1137
+ }
1138
+ } catch (e) {
1139
+ onError(e);
1140
+ throw ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError);
1141
+ }
1142
+ }
1143
+ }
1144
+
1145
+ if (packetsWritten > 0) {
1146
+ await delay(FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS);
1147
+ }
1148
+ }
1149
+
998
1150
  if (name === 'EmmcFileWrite') {
999
1151
  await writeChunkedData(
1000
1152
  buffers,
@@ -1005,10 +1157,57 @@ export default class ReactNativeBleTransport {
1005
1157
  }
1006
1158
  );
1007
1159
  } else if (name === 'FirmwareUpload') {
1008
- await writeChunkedData(
1160
+ Log?.debug('[ReactNativeBleTransport] FirmwareUpload write uses throttled BLE packets:', {
1161
+ packetCapacity: FIRMWARE_UPLOAD_WRITE_PACKET_CAPACITY,
1162
+ burstSize: FIRMWARE_UPLOAD_WRITE_BURST_SIZE,
1163
+ pauseMs: FIRMWARE_UPLOAD_WRITE_PAUSE_MS,
1164
+ flushDelayMs: FIRMWARE_UPLOAD_WRITE_FLUSH_DELAY_MS,
1165
+ maxRetries: FIRMWARE_UPLOAD_WRITE_MAX_RETRIES,
1166
+ });
1167
+
1168
+ await writeFirmwareUploadChunkedData(
1009
1169
  buffers,
1010
1170
  async data => {
1011
- await transport.writeCharacteristic.writeWithoutResponse(data);
1171
+ let attempt = 0;
1172
+ // Retry only congestion. Other write errors should surface immediately.
1173
+ // GATT_CONGESTED is usually transient backpressure from the Android BLE queue.
1174
+ // eslint-disable-next-line no-constant-condition
1175
+ while (true) {
1176
+ try {
1177
+ await transport.writeCharacteristic.writeWithoutResponse(data);
1178
+ return;
1179
+ } catch (error) {
1180
+ const retryType = getFirmwareUploadWriteRetryType(error);
1181
+ if (!retryType || attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
1182
+ throw error;
1183
+ }
1184
+ const shouldReconnect = retryType === 'reconnectable';
1185
+ const delayMs = shouldReconnect
1186
+ ? FIRMWARE_UPLOAD_RECONNECT_RETRY_DELAY_MS
1187
+ : resolveFirmwareUploadRetryDelay(attempt);
1188
+ Log?.debug('[ReactNativeBleTransport] FirmwareUpload write retry:', {
1189
+ attempt: attempt + 1,
1190
+ delayMs,
1191
+ reconnect: shouldReconnect,
1192
+ error,
1193
+ });
1194
+ if (shouldReconnect) {
1195
+ this.firmwareUploadWriteRecoveryIds.add(uuid);
1196
+ }
1197
+ await delay(delayMs);
1198
+ attempt += 1;
1199
+ if (shouldReconnect) {
1200
+ try {
1201
+ await this.reconnectFirmwareUploadTransport(uuid, transport);
1202
+ } catch (e) {
1203
+ Log?.debug('[ReactNativeBleTransport] FirmwareUpload reconnect error:', e);
1204
+ if (attempt >= FIRMWARE_UPLOAD_WRITE_MAX_RETRIES) {
1205
+ throw e;
1206
+ }
1207
+ }
1208
+ }
1209
+ }
1210
+ }
1012
1211
  },
1013
1212
  e => {
1014
1213
  this.runPromise = null;