/*
* Copyright (c) Huawei Technologies Co., Ltd. 2024-2025. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import bluetoothManager from '@ohos.enterprise.bluetoothManager';
import access from '@ohos.bluetooth';
import { BusinessError } from '@ohos.base';
import { ResourceUtil } from './ResourceUtil';
import { LogUtil } from './LogUtil';
import { CheckEmptyUtils } from './CheckEmptyUtils';
import { HiSysEventUtil } from '../systemEvent/HiSysEventUtil';
import { SettingsDataUtils } from './SettingsDataUtils';
import { LanguageUtils } from './LanguageUtils';
import { SWITCH_OFF_VAL, SWITCH_ON_VAL, }
from './Consts';
import { HiSysBluetoothEventGroup } from '../systemEvent/BehaviorEventConsts';
const TAG: string = 'BluetoothUtils : ';
export const BLUETOOTH_SWITCH_KEY: string = 'bluetooth_switch';
export const BLUETOOTH_BONDED_DEVICES_GROUP_KEY: string = 'bluetooth_bonded_devices_group';
export const BLUETOOTH_LIST_GROUP_KEY: string = 'bluetooth_list_group';
export const BLUETOOTH_SEARCH_MESSAGE_KEY: string = 'bluetooth_search_message_menu_key';
export const BLUETOOTH_DEVICE_SYNC_VOLUME_WITH_PHONE_KEY: string = 'bluetooth_device_sync_volume_with_phone_menu_key';
export const BLUETOOTH_DEVICE_NAME_COMP_ID: string = 'Setting.Bluetooth.UpdateNameGroup.UpdateNameItem';
const AIR_PLANE_MODE: string = 'settings.telephony.airplanemode';
/**
* 已配对蓝牙设备设备类型枚举值
*/
export enum BluetoothBondedDeviceType {
// 默认值
DEVICE_TYPE_DEFAULT = 0,
// 汽车
DEVICE_TYPE_CAR = 1,
// 耳机
DEVICE_TYPE_HEADSET = 2,
// 助听器
DEVICE_TYPE_HEARING = 3,
// 眼镜
DEVICE_TYPE_GLASSES = 4,
// 手表
DEVICE_TYPE_WATCH = 5,
// 音响
DEVICE_TYPE_SPEAKER = 6,
}
/**
* 蓝牙工具类
*
* @since 2022-04-25
*/
export class BluetoothUtils {
public static readonly MAC_REGEX_PATTERN: RegExp = new RegExp('([A-Fa-f0-9]{2}[-,:]){5}[A-Fa-f0-9]{2}');
public static readonly MAC_WITH_HALF_DENOMINATOR: number = 2;
public static readonly MAC_PRINT_WITH_PRE: number = 5;
public static readonly MAC_PRINT_WITH_SUF: number = 3;
public static readonly ANONYMOUS_MAC: string = ':**:**:**';
public static enhancedServicesState: string | undefined = undefined;
/**
* 实时查蓝牙开关状态
*/
static isBluetoothOn(): boolean {
try {
let bluetoothState = access.getState();
LogUtil.info(`${TAG} bluetoothState: ${bluetoothState}`);
return BluetoothUtils.isBluetoothStateOn(bluetoothState);
} catch (error) {
LogUtil.info(`${TAG} isBluetoothOn error: ${(error as BusinessError).message}`);
}
return false;
}
/**
* 打开蓝牙开关
*/
static enableBlueTooth(): boolean {
try {
access.enableBluetooth();
return true;
} catch (error) {
LogUtil.info(`${TAG} enableBlueTooth error: ${error?.message}`);
}
return false;
}
static async enableBlueToothAsync(): Promise<boolean> {
try {
await access.enableBluetooth();
return true;
} catch (error) {
LogUtil.info(`${TAG} enableBlueTooth error: ${error?.message}`);
}
return false;
}
/**
* 关闭蓝牙开关
*/
static disableBlueTooth(): boolean {
// 飞行模式开启
let value: string = SettingsDataUtils.getSettingsData(AIR_PLANE_MODE, '0');
if (value !== '0') {
try {
access.disableBluetooth();
HiSysEventUtil.reportDefaultBehaviorEventByUE(HiSysBluetoothEventGroup.CLICK_BLUETOOTH_BUTTON, 'false');
LogUtil.info(`${TAG} airPlane on, Disabling Bluetooth.`);
return true;
} catch (error) {
LogUtil.error(`${TAG} disable failed, ${error?.message}`);
return false;
}
}
try {
access.disableBluetooth();
HiSysEventUtil.reportDefaultBehaviorEventByUE(HiSysBluetoothEventGroup.CLICK_BLUETOOTH_BUTTON, 'false');
LogUtil.info(`${TAG} Disabling Bluetooth.`);
return true;
} catch (error) {
LogUtil.error(`${TAG} disable failed, ${error?.message}`);
return false;
}
}
/**
* 实时查蓝牙开关状态值
*/
static getCurrentBluetoothState(): number {
try {
let bluetoothState = access.getState();
LogUtil.info(`${TAG} getState: ${bluetoothState}`);
return bluetoothState;
} catch (error) {
LogUtil.error(`${TAG} isBluetoothOn error: $(error.message)`);
return access.BluetoothState.STATE_OFF;
}
}
static isBluetoothDiscoveryOn(bluetoothState: number): boolean {
return bluetoothState === access.BluetoothState.STATE_ON;
}
/**
* 判断当前给的状态是否开启过程中
*
* @param bluetoothState 待判断的状态
*/
static isBluetoothStateOpening(bluetoothState: number): boolean {
return bluetoothState === access.BluetoothState.STATE_TURNING_ON ||
bluetoothState === access.BluetoothState.STATE_BLE_TURNING_ON;
}
/**
* 判断当前状态值是否处于中间态(即开启或者关闭以外的状态)
*
* @param bluetoothState 待判断的状态
* @returns 返回状态值
*/
static isBluetoothMiddleState(bluetoothState: number): boolean {
return bluetoothState !== access.BluetoothState.STATE_ON &&
bluetoothState !== access.BluetoothState.STATE_BLE_ON &&
bluetoothState !== access.BluetoothState.STATE_OFF;
}
/**
* 判断当前给的状态是否开启状态
*
* @param bluetoothState 待判断的状态
* @returns 返回状态值
*/
static isBluetoothStateOn(bluetoothState: number): boolean {
if (bluetoothState === access.BluetoothState.STATE_ON) {
return true;
}
if (BluetoothUtils.enhancedServicesState === SWITCH_OFF_VAL &&
bluetoothState == access.BluetoothState.STATE_BLE_ON) {
return true;
}
return false;
}
/**
* 判断当前给的状态是否关闭过程中
*
* @param bluetoothState 待判断的状态STATE_TURNING_OFF
*/
static isBluetoothStateClosing(bluetoothState: number): boolean {
return bluetoothState === access.BluetoothState.STATE_TURNING_OFF ||
bluetoothState === access.BluetoothState.STATE_BLE_TURNING_OFF;
}
/**
* 判断当前给的状态是否关闭状态
*
* @param bluetoothState 待判断的状态
* @returns 返回状态值
*/
static isBluetoothStateOff(bluetoothState: number): boolean {
if (bluetoothState === access.BluetoothState.STATE_OFF) {
return true;
}
return false
}
/**
* 当前设备ID格式是否正常
*
* @param macStr 设备ID字符串
*/
static isValidMac(macStr: string): boolean {
if (!macStr) {
return false;
}
return macStr.match(BluetoothUtils.MAC_REGEX_PATTERN) !== null;
}
/**
* 获取可以打印的掩码后设备ID
*
* @param macStr 设备ID字符串
*/
static getLogMAC(macStr: string): string {
if (!BluetoothUtils.isValidMac(macStr)) {
return '';
}
let substring: string | null = null;
substring = macStr.substring(0, BluetoothUtils.MAC_PRINT_WITH_PRE);
return substring + BluetoothUtils.ANONYMOUS_MAC +
macStr.substring(macStr.length - BluetoothUtils.MAC_PRINT_WITH_SUF, macStr.length);
}
static getPreMac(macStr: string): string {
if (!BluetoothUtils.isValidMac(macStr)) {
return '';
}
return macStr.substring(0, BluetoothUtils.MAC_PRINT_WITH_PRE)?.replace(':', '');
}
static getSufMac(macStr: string): string {
if (!BluetoothUtils.isValidMac(macStr)) {
return '';
}
return macStr.substring(macStr.length - 2, macStr.length);
}
/**
* 获取可以打印的掩码后设备uuid
*
* @param uuidStr 设备uuid字符串
*/
static getLogUuid(uuidStr: string[]): string {
if (CheckEmptyUtils.isEmptyArr(uuidStr)) {
return '';
}
let result: string = '';
uuidStr.forEach((uuid: string) => {
let substring = uuid.substring(0, uuid.length / BluetoothUtils.MAC_WITH_HALF_DENOMINATOR);
result += `${substring}${BluetoothUtils.ANONYMOUS_MAC}, `
})
return result;
}
/**
* 根据设备类型枚举值,返回对应字串
*
* @param deviceType 设备类型枚举值
* @returns 设备类型字串
*/
static getBondDeviceType(deviceType: number): string {
let deviceTypeString = '';
switch (deviceType) {
case BluetoothBondedDeviceType.DEVICE_TYPE_CAR:
deviceTypeString = ResourceUtil.getStringSync($r('app.string.device_type_car'));
break;
case BluetoothBondedDeviceType.DEVICE_TYPE_HEADSET:
deviceTypeString = ResourceUtil.getStringSync($r('app.string.device_type_headset'));
break;
case BluetoothBondedDeviceType.DEVICE_TYPE_HEARING:
deviceTypeString = ResourceUtil.getStringSync($r('app.string.device_type_hearing'));
break;
case BluetoothBondedDeviceType.DEVICE_TYPE_GLASSES:
deviceTypeString = ResourceUtil.getStringSync($r('app.string.device_type_glasses'));
break;
case BluetoothBondedDeviceType.DEVICE_TYPE_WATCH:
deviceTypeString = ResourceUtil.getStringSync($r('app.string.device_type_watch'));
break;
case BluetoothBondedDeviceType.DEVICE_TYPE_SPEAKER:
deviceTypeString = ResourceUtil.getStringSync($r('app.string.device_type_speaker'));
break;
default:
deviceTypeString = '';
break;
}
return deviceTypeString;
}
/**
* 根据错误码返回对应的错误信息
*
* @param deviceName 设备名称
* @param errorCode 错误码
* @returns 错误信息
*/
static getPairFailedMessage(deviceName: string, errorCode: number): string {
let errorMessage = '';
switch (errorCode) {
case UnbondCause.USER_REMOVED:
errorMessage = ResourceUtil.getFormatStringSync($r('app.string.blue_tooth_pair_failed2'), deviceName);
break
case UnbondCause.REMOTE_DEVICE_DOWN:
errorMessage = ResourceUtil.getAnyStrFormatStringSync($r('app.string.blue_tooth_pair_failed1'),
deviceName, LanguageUtils.getNumberFormat(1), LanguageUtils.getNumberFormat(2),
LanguageUtils.getNumberFormat(10), LanguageUtils.getNumberFormat(3));
break;
case UnbondCause.AUTH_FAILURE:
errorMessage = ResourceUtil.getFormatStringSync($r('app.string.blue_tooth_pair_failed2'), deviceName);
break;
case UnbondCause.AUTH_REJECTED:
errorMessage = ResourceUtil.getFormatStringSync($r('app.string.blue_tooth_pair_failed3'), deviceName);
break;
default:
errorMessage = ResourceUtil.getStringSync($r('app.string.bt_pair_failed_message'));
break;
}
return errorMessage;
}
/**
* 根据错误码返回对应的错误信息
*
* @param errorCode 错误码
* @returns 错误信息
*/
static getConnectFailedMessage(errorCode: number): string {
let errorMessage = '';
switch (errorCode) {
case DisconnectCause.CONNECT_FROM_KEYBOARD:
errorMessage = ResourceUtil.getStringSync($r('app.string.blue_tooth_connect_failed1'));
break;
case DisconnectCause.CONNECT_FROM_MOUSE:
errorMessage = ResourceUtil.getStringSync($r('app.string.blue_tooth_connect_failed2'));
break;
case DisconnectCause.CONNECT_FROM_CAR:
errorMessage = ResourceUtil.getStringSync($r('app.string.blue_tooth_connect_failed3'));
break;
case DisconnectCause.TOO_MANY_CONNECTED_DEVICES:
errorMessage = ResourceUtil.getStringSync($r('app.string.blue_tooth_connect_failed4'));
break;
default:
errorMessage = '';
break;
}
return errorMessage;
}
/**
* 外领域是否禁用蓝牙
*
* @returns 禁用状态
*/
static isBluetoothManagerEnable(): boolean {
let bluetoothDisabled: boolean = false;
try {
bluetoothDisabled = bluetoothManager?.isBluetoothDisabled(null);
LogUtil.info(`${TAG} bluetoothManager Success result : ${bluetoothDisabled}`);
} catch (err) {
LogUtil.error(`${TAG} bluetoothManager Failed result, Code: ${err?.code}, message: ${err?.message}`);
}
return bluetoothDisabled;
}
}
export enum UnbondCause {
// 用户主动取消配对
USER_REMOVED = 0,
// 远端设备关闭
REMOTE_DEVICE_DOWN = 1,
// PIN 码或配对密钥错误
AUTH_FAILURE = 2,
// 远端设备鉴权拒绝
AUTH_REJECTED = 3,
// 其他原因
INTERNAL_ERROR = 4
}
export enum DisconnectCause {
// 用户主动断开连接
USER_DISCONNECT = 0,
// 手机发起向键盘重新连接请求
CONNECT_FROM_KEYBOARD = 1,
// 手机发起向鼠标重新连接请求
CONNECT_FROM_MOUSE = 2,
// 手机发起向车机重新连接请求
CONNECT_FROM_CAR = 3,
// 设备连接数超限
TOO_MANY_CONNECTED_DEVICES = 4,
// 其他
CONNECT_FAIL_INTERNAL = 5
}