Skip to content

Commit 84e638b

Browse files
fix: ble connect error (#634)
1 parent 2c691ae commit 84e638b

File tree

6 files changed

+145
-30
lines changed

6 files changed

+145
-30
lines changed

packages/core/src/core/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import semver from 'semver';
22
import EventEmitter from 'events';
33
import {
4+
ERROR_CODES_REQUIRE_RELEASE,
45
ERRORS,
56
HardwareError,
67
HardwareErrorCode,
@@ -533,6 +534,14 @@ const onCallDevice = async (
533534
messageResponse = createResponseMessage(method.responseID, false, { error });
534535
requestQueue.resolveRequest(method.responseID, messageResponse);
535536
completeMethodRequestContext(method, error);
537+
538+
// Re-throw errors that need to trigger device release/disconnect in Device._runInner
539+
if (
540+
error instanceof HardwareError &&
541+
ERROR_CODES_REQUIRE_RELEASE.includes(error.errorCode as any)
542+
) {
543+
throw error;
544+
}
536545
}
537546
};
538547
Log.debug('Call API - Device Run: ', device.mainId);

packages/core/src/device/Device.ts

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
import EventEmitter from 'events';
22
import semver from 'semver';
3-
import { OneKeyDeviceInfo as DeviceDescriptor, Enum_Capability } from '@onekeyfe/hd-transport';
3+
import { Enum_Capability } from '@onekeyfe/hd-transport';
44
import {
5-
createDeferred,
6-
Deferred,
75
EDeviceType,
6+
ERROR_CODES_REQUIRE_DISCONNECT,
7+
ERROR_CODES_REQUIRE_RELEASE,
88
ERRORS,
99
HardwareError,
1010
HardwareErrorCode,
11+
createDeferred,
1112
} from '@onekeyfe/hd-shared';
13+
1214
import {
15+
LoggerNames,
1316
getDeviceBLEFirmwareVersion,
1417
getDeviceBleName,
1518
getDeviceFirmwareVersion,
@@ -18,38 +21,39 @@ import {
1821
getDeviceUUID,
1922
getLogger,
2023
getMethodVersionRange,
21-
LoggerNames,
2224
} from '../utils';
2325
import {
2426
fixFeaturesFirmwareVersion,
2527
getPassphraseStateWithRefreshDeviceInfo,
2628
} from '../utils/deviceFeaturesUtils';
2729
import { generateInstanceId } from '../utils/tracing';
2830

29-
import type DeviceConnector from './DeviceConnector';
3031
// eslint-disable-next-line import/no-cycle
31-
import { DeviceCommands, PassphrasePromptResponse } from './DeviceCommands';
32-
32+
import { DeviceCommands } from './DeviceCommands';
3333
import {
3434
type DeviceFirmwareRange,
35-
EOneKeyDeviceMode,
3635
type Device as DeviceTyped,
36+
EOneKeyDeviceMode,
3737
type Features,
3838
type UnavailableCapabilities,
3939
} from '../types';
40-
import {
41-
DEVICE,
42-
DeviceButtonRequestPayload,
43-
DeviceFeaturesPayload,
44-
PassphraseRequestPayload,
45-
UI_REQUEST,
46-
} from '../events';
47-
import { PROTO } from '../constants';
40+
import { DEVICE, UI_REQUEST } from '../events';
4841
import { DataManager } from '../data-manager';
4942
import TransportManager from '../data-manager/TransportManager';
5043
import { toHardened } from '../api/helpers/pathUtils';
5144
import { existCapability } from '../utils/capabilitieUtils';
5245

46+
import type { PROTO } from '../constants';
47+
import type {
48+
DeviceButtonRequestPayload,
49+
DeviceFeaturesPayload,
50+
PassphraseRequestPayload,
51+
} from '../events';
52+
import type { PassphrasePromptResponse } from './DeviceCommands';
53+
import type { Deferred } from '@onekeyfe/hd-shared';
54+
import type { OneKeyDeviceInfo as DeviceDescriptor } from '@onekeyfe/hd-transport';
55+
import type DeviceConnector from './DeviceConnector';
56+
5357
export type InitOptions = {
5458
initSession?: boolean;
5559
deviceId?: string;
@@ -595,15 +599,11 @@ export class Device extends EventEmitter {
595599

596600
if (
597601
e instanceof HardwareError &&
598-
(e.errorCode === HardwareErrorCode.DeviceInitializeFailed ||
599-
e.errorCode === HardwareErrorCode.DeviceInterruptedFromOutside ||
600-
e.errorCode === HardwareErrorCode.DeviceInterruptedFromUser ||
601-
e.errorCode === HardwareErrorCode.DeviceCheckPassphraseStateError ||
602-
e.errorCode === HardwareErrorCode.ResponseUnexpectTypeError ||
603-
e.errorCode === HardwareErrorCode.PinInvalid ||
604-
e.errorCode === HardwareErrorCode.PinCancelled ||
605-
e.errorCode === HardwareErrorCode.UnexpectPassphrase)
602+
ERROR_CODES_REQUIRE_RELEASE.includes(e.errorCode as any)
606603
) {
604+
if (ERROR_CODES_REQUIRE_DISCONNECT.includes(e.errorCode as any)) {
605+
await this.deviceConnector?.disconnect(this.mainId);
606+
}
607607
await this.release();
608608
Log.debug(`error code ${e.errorCode} release device, mainId: ${this.mainId}`);
609609
}

packages/core/src/device/DeviceConnector.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import { Transport, OneKeyDeviceInfo as DeviceDescriptor } from '@onekeyfe/hd-transport';
21
import { safeThrowError } from '../constants';
32
import { DataManager } from '../data-manager';
43
import TransportManager from '../data-manager/TransportManager';
5-
import { DevicePool, DeviceDescriptorDiff } from './DevicePool';
4+
import { DevicePool } from './DevicePool';
65
import { resolveAfter } from '../utils/promiseUtils';
7-
import { getLogger, LoggerNames } from '../utils';
6+
import { LoggerNames, getLogger } from '../utils';
7+
8+
import type { DeviceDescriptorDiff } from './DevicePool';
9+
import type { OneKeyDeviceInfo as DeviceDescriptor, Transport } from '@onekeyfe/hd-transport';
810

911
const Log = getLogger(LoggerNames.DeviceConnector);
1012

@@ -99,6 +101,16 @@ export default class DeviceConnector {
99101
}
100102
}
101103

104+
async disconnect(session: string | undefined | null) {
105+
try {
106+
if (this.transport.disconnect && !!session) {
107+
await this.transport.disconnect(session);
108+
}
109+
} catch (error) {
110+
safeThrowError(error);
111+
}
112+
}
113+
102114
promptDeviceAccess(): Promise<USBDevice | BluetoothDevice | null> {
103115
if (!this.transport.promptDeviceAccess) {
104116
return Promise.resolve(null);

packages/hd-transport-react-native/src/index.ts

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,10 @@ export default class ReactNativeBleTransport {
405405
await this.release(uuid);
406406

407407
const transport = new BleTransport(device, writeCharacteristic, notifyCharacteristic);
408-
transport.nofitySubscription = this._monitorCharacteristic(transport.notifyCharacteristic);
408+
transport.nofitySubscription = this._monitorCharacteristic(
409+
transport.notifyCharacteristic,
410+
uuid
411+
);
409412
transportCache[uuid] = transport;
410413

411414
this.emitter?.emit('device-connect', {
@@ -436,7 +439,7 @@ export default class ReactNativeBleTransport {
436439
return { uuid };
437440
}
438441

439-
_monitorCharacteristic(characteristic: Characteristic) {
442+
_monitorCharacteristic(characteristic: Characteristic, uuid: string) {
440443
let bufferLength = 0;
441444
let buffer: any[] = [];
442445
const subscription = characteristic.monitor((error, c) => {
@@ -510,7 +513,7 @@ export default class ReactNativeBleTransport {
510513
this.Log.debug('monitor data error: ', error);
511514
this.runPromise?.reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError));
512515
}
513-
});
516+
}, uuid);
514517

515518
return () => {
516519
this.Log.debug('remove characteristic monitor: ', characteristic.uuid);
@@ -664,6 +667,67 @@ export default class ReactNativeBleTransport {
664667
this.stopped = true;
665668
}
666669

670+
async disconnect(session: string) {
671+
this.Log.debug('transport-react-native transport resetSession: ', session);
672+
const transport = transportCache[session] as BleTransport;
673+
674+
// cancel the notify subscription
675+
if (transport?.nofitySubscription) {
676+
try {
677+
transport.nofitySubscription();
678+
transport.nofitySubscription = undefined;
679+
} catch (e) {
680+
this.Log.error('resetSession: remove notify subscription error: ', e);
681+
}
682+
}
683+
684+
// cancel the ble transaction
685+
if (session) {
686+
try {
687+
await this.blePlxManager?.cancelTransaction(session);
688+
} catch (e) {
689+
this.Log.debug('resetSession: cancel transaction error (ignored): ', e?.message || e);
690+
}
691+
}
692+
693+
// disconnect the device via the device object
694+
if (transport?.device) {
695+
try {
696+
await transport.device.cancelConnection();
697+
} catch (e) {
698+
this.Log.debug('resetSession: device.cancelConnection error (ignored): ', e?.message || e);
699+
}
700+
}
701+
702+
// disconnect the device via the ble manager
703+
try {
704+
await this.blePlxManager?.cancelDeviceConnection(session);
705+
} catch (e) {
706+
this.Log.debug(
707+
'resetSession: manager.cancelDeviceConnection error (ignored): ',
708+
e?.message || e
709+
);
710+
}
711+
712+
// clear the transport cache
713+
if (transportCache[session]) {
714+
delete transportCache[session];
715+
}
716+
717+
// emit the disconnect event
718+
try {
719+
this.emitter?.emit('device-disconnect', {
720+
name: transport?.device?.name,
721+
id: session,
722+
connectId: session,
723+
});
724+
} catch (e) {
725+
this.Log.error('resetSession: emit disconnect event error: ', e);
726+
}
727+
// eslint-disable-next-line no-promise-executor-return
728+
await new Promise<void>(resolve => setTimeout(() => resolve(), 100));
729+
}
730+
667731
cancel() {
668732
this.Log.debug('transport-react-native transport cancel');
669733
if (this.runPromise) {

packages/hd-transport/src/types/transport.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ export type Transport = {
5959
read(session: string): Promise<MessageFromOneKey>;
6060
cancel(): Promise<void>;
6161

62+
// reset the session of the transport
63+
// used to reset the session of the transport when the session is not valid
64+
disconnect?: (session: string) => Promise<void>;
65+
6266
// web-usb, web-bluetooth request device
6367
promptDeviceAccess?: () => Promise<USBDevice | BluetoothDevice | null>;
6468

packages/shared/src/constants.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { HardwareErrorCode } from './HardwareError';
2+
13
export const ONEKEY_WEBUSB_FILTER = [
24
{ vendorId: 0x1209, productId: 0x53c0 }, // Classic Boot、Classic1s Boot、Mini Boot
35
{ vendorId: 0x1209, productId: 0x53c1 }, // Classic Firmware、Classic1s Firmware、Mini Firmware、Pro Firmware、Touch Firmware
@@ -7,6 +9,30 @@ export const ONEKEY_WEBUSB_FILTER = [
79
// { vendorId: 0x1209, productId: 0x4f50 }, // Touch Board
810
];
911

12+
/**
13+
* Error codes that require device release after occurrence
14+
* These errors indicate the device is in an invalid state and needs to be released
15+
*/
16+
export const ERROR_CODES_REQUIRE_RELEASE = [
17+
HardwareErrorCode.DeviceInitializeFailed,
18+
HardwareErrorCode.DeviceInterruptedFromOutside,
19+
HardwareErrorCode.DeviceInterruptedFromUser,
20+
HardwareErrorCode.DeviceCheckPassphraseStateError,
21+
HardwareErrorCode.ResponseUnexpectTypeError,
22+
HardwareErrorCode.PinInvalid,
23+
HardwareErrorCode.PinCancelled,
24+
HardwareErrorCode.UnexpectPassphrase,
25+
] as const;
26+
27+
/**
28+
* Error codes that require device disconnect before release
29+
* These errors indicate a communication failure that requires full reconnection
30+
*/
31+
export const ERROR_CODES_REQUIRE_DISCONNECT = [
32+
HardwareErrorCode.DeviceInitializeFailed,
33+
HardwareErrorCode.ResponseUnexpectTypeError,
34+
] as const;
35+
1036
// BLE IPC communication message types
1137
export enum EOneKeyBleMessageKeys {
1238
// BLE device selection related

0 commit comments

Comments
 (0)