本文正在参加星光计划3.0–夏日挑战赛 作者:巴延兴

1. 简介

​ 随着现代移动通信和物联网的快速发展,万物互联已经越来越成为一个基本需求,而近距离的无线通信更是在移动设备之间有着及其广泛的应用。蓝牙技术是一种无线数据和语音传输的开放式标准,它是基于低成本的近距离无线连接,为固定和移动设备建立通信的一种近距离无线技术连接。蓝牙技术正广泛运用于汽车领域、工业生产、医疗领域。

1.1 OpenHarmony架构图

​ 蓝牙子系统所属的分布式软总线在整个鸿蒙系统中的位置在如下红框处:

1.png

2. 基础知识

2.1 蓝牙概述

​ 蓝牙是一种支持设备短距离通信(一般10m内)的无线电技术,能在包括移动电话、PDA、无线耳机、笔记本电脑、相关外设等众多具有蓝牙模块的设备之间进行短距离无线信息交换,使得数据传输变得更加迅速高效。它是爱立信、IBM等5家公司在1998年联合推出的一项无线通信技术,随后成立的蓝牙技术特殊兴趣组织(SIG)来负责该技术的开发和技术协议的制定,如今全世界已有1800多家公司加盟该组织。

​ 蓝牙工作在全球通用的2.4GHz ISM(即工业、科学、医学)频段,使用IEEE802.15协议。作为一种的短距离无线通信技术,在现在的生活中,正扮演着极其重要的角色。当然,在操作系统中,蓝牙模块正扮演着必不可少的基础功能。

2.2 蓝牙架构图

2.png

​ 在蓝牙技术中,有**Application**,**Host**和**Controller**三个角色。这三个角色可以位于不同的设备,也可以位于同一个设备上。在一个系统中,Host只有一个,但Controller可以一个也可以有多个。

​ Application:由不同的Profiles来限定,蓝牙系统中的应用程序互操作性是由profiles完成的。profiles定义了蓝牙系统中从PHY到L2CAP以及核心规范之外的任何其他协议所需的每层功能和特性。配置文件定义了层之间的**垂直**交互以及**设备之间特定层**的点对点交互。此外,应用程序的**行为**和**数据格式**也由profiles定义。

​ Host:负责在逻辑链路的基础上,进行更为友好的封装,这样就可以屏蔽掉蓝牙技术的细节,让Bluetooth Application更为方便的使用。

​ Controller:负责定义RF、Baseband等底层的规范,并在这之上抽象出用于通信的逻辑链路。

2.3 蓝牙子系统代码架构图

​ 下图为OpenHamony蓝牙子系统的代码结构,图中可以看到从上层到底层的整个过程。

3.png ​ APP:蓝牙应用程序,即使用蓝牙API的程序,一般是在设备的settings里实现。通过调用bluetooth的interfaces下的对应接口来实现应用程序的功能。

​ bluetooth:蓝牙框架层,主要包括interfaces和services,本篇主要针对该部分做详细解读。

​ HDF:硬件驱动框架,bluetooth的设备驱动的开发是基于该框架的基础上,结合操作系统适配层(OSAL)和平台驱动接口(比如I2C/SPI/UART总线等平台资源)能力,屏蔽不同操作系统和平台总线资源差异,实现bluetooth驱动"一次开发,多系统部署"的目标。

​ HardWare:各类具有蓝牙模块硬件实体的硬件设备,如移动电话、PDA、无线耳机、笔记本电脑、相关外设等众多设备。

2.4 蓝牙子系统代码目录

​ bluetooth 框架层的代码目录结构如下:


/foundation/communication/bluetooth

├── interfaces # 接口代码

│ └── innerkits # 系统服务接口存放目录

│ ├── native\_c # C接口存放目录

│ │ └── include # C接口定义目录

│ └── native\_cpp # C++接口存放目录

│── sa\_profile # 蓝牙服务定义目录

│── services # 蓝牙服务代码目录

└── LICENSE # 版权声明文件

​ interfaces,负责向上层应用程序提供相应的功能接口,使应用开发者实现具体蓝牙业务功能,目前只提供c/c++接口。

​ services负责interfaces接口的实现。系统提供C/C++接口定义及服务和协议栈的代码,目前提供的只有BLE相关的接口,包括BLE设备GATT相关的操作,以及BLE广播、扫描等功能,其它A2DP,AVRCP,HFP等相关接口在后续增量发布。

​ services部分主要是通过bluetooth_standard目录下的相关模块来实现interfaces接口。相关的模块有common、hardware、external、etc、ipc、stack、service、server。模块间利用c++的相关特性,完成了各自的分层功能,最终实现了蓝牙不同协议的场景功能。代码架构如下:


└── services

├── bluetooth

│   └── BUILD.gn

└── bluetooth\_standard

├── common #定义蓝牙相关的数据结构,供其他子模块调用

├── etc #蓝牙服务的配置文件存放处,蓝牙设备、profile的配置文件

├── external #扩展目录

├── hardware #定义HCI的相关接口供其他子模块调用

├── ipc #进程间通信,不同协议的proxy和stub之间的数据传递等

├── server #主从模式的server端代码存放处

├── service #不同协议的服务接口及实现,供server调用

└── stack #常规stack、list、queue,信号量,互斥量的操作,不同协议栈的相关数据结构的创建、初始化等

2.5 蓝牙子系统协议

​ 蓝牙相关协议栈在stack目录下按照目录进行协议分类,如下:


└── stack

└── src

├── att 属性协议

├── avctp 音视频控制传输协议

├── avdtp 音视频分发协议

├── btm (Bluetooth manage蓝牙配对与链路管理)

├── gap (Generic Access Profile通用访问协议)

├── hci 主机控制接口协议

├── l2cap 逻辑链路控制和适配协议

├── rfcomm 串口仿真协议

├── sdp 服务发现协议

└── smp(Security Manage Protoco蓝牙安全管理协议)

3. 源码分析

​ @ohos.bluetooth.d.ts文件提供了供应用侧调用的接口,我们以最常用的打开蓝牙开关的操作来展示代码的时序流程。

​ 应用侧调用了@ohos.bluetooth.d.ts中示例的enableBluetooth()接口来开启蓝牙,如下:


function enableBluetooth(): boolean;

​ 实际上d.ts中的接口在代码中仅仅只是用来示例,真正起作用的是NAPI的接口EnableBluetooth,该接口对应的文件为napi_bluetooth_host.cpp,如下:


napi\_value EnableBluetooth(napi\_env env, napi\_callback\_info info)

{

HILOGI("EnableBluetooth start");

BluetoothHost \*host = &BluetoothHost::GetDefaultHost();

bool enabled = host-\>EnableBt();

enabled |= host-\>EnableBle();

napi\_value result = nullptr;

napi\_get\_boolean(env, enabled, &result);

HILOGI("EnableBluetooth end");

return result;

}

​ napi接口调用的是BluetoothHost中的EnableBt()和EnableBle()方法分别打开经典蓝牙和低功耗蓝牙开关。我们以打开低功耗蓝牙为例分析代码流程。

3.1 打开低功耗蓝牙代码流程

​ BluetoothHost::EnableBle()接口如下:


bool BluetoothHost::EnableBle()

{

HILOGD("BluetoothHost::Enable BLE starts");

if (!pimpl) {

HILOGE("BluetoothHost::Enable BLE fails: no pimpl");

return false;

}

if (pimpl-\>proxy\_ == nullptr) {

HILOGE("BluetoothHost::Enable fails: no proxy");

return false;

}

return pimpl-\>proxy\_-\>EnableBle();

}

​ 调用的是BluetoothHostProxy中的EnableBle()接口。如下所示。此时已经从interface目录进入到service目录下的ipc目录。


bool BluetoothHostProxy::EnableBle()

{

MessageParcel data;

if (!data.WriteInterfaceToken(BluetoothHostProxy::GetDescriptor())) {

HILOGE("BluetoothHostProxy::EnableBle WriteInterfaceToken error");

return false;

}

MessageParcel reply;

MessageOption option = {MessageOption::TF\_SYNC};

int32\_t error = InnerTransact(IBluetoothHost::Code::BT\_ENABLE\_BLE, option, data, reply);

if (error != NO\_ERROR) {

HILOGE("BluetoothHostProxy::EnableBle done fail, error: %{public}d", error);

return false;

}

return reply.ReadBool();

}

​ Proxy端发送的消息ID为BT_ENABLE_BLE,并封装为消息序列MessageParcel对象。

​ BluetoothHostStub的OnRemoteRequest将会收到code为9即携带BT_ENABLE_BLE的消息序列,并在BluetoothHostStub中解析处理,如下:


int32\_t BluetoothHostStub::OnRemoteRequest(

uint32\_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option)

{

HILOGD("BluetoothHostStub::OnRemoteRequest, cmd = %{public}d, flags= %{public}d", code, option.GetFlags());

std::u16string descriptor = BluetoothHostStub::GetDescriptor();

std::u16string remoteDescriptor = data.ReadInterfaceToken();

if (descriptor != remoteDescriptor) {

HILOGE("BluetoothHostStub::OnRemoteRequest, local descriptor is not equal to remote");

return ERR\_INVALID\_STATE;

}

auto itFunc = memberFuncMap\_.find(code);

if (itFunc != memberFuncMap\_.end()) {

auto memberFunc = itFunc-\>second;

if (memberFunc != nullptr) {

return memberFunc(this, data, reply);

}

}

HILOGW("BluetoothHostStub::OnRemoteRequest, default case, need check.");

return IPCObjectStub::OnRemoteRequest(code, data, reply, option);

}

​ 对应的映射关系如下:


const std::map\<uint32\_t, std::function\<ErrCode(BluetoothHostStub \*, MessageParcel &, MessageParcel &)\>\>

BluetoothHostStub::memberFuncMap\_ = {

...

{BluetoothHostStub::BT\_ENABLE\_BLE,

std::bind(&BluetoothHostStub::EnableBleInner, std::placeholders::\_1, std::placeholders::\_2,

std::placeholders::\_3)},

​ 即将在EnableBleInner函数中向下调用:


ErrCode BluetoothHostStub::EnableBleInner(MessageParcel &data, MessageParcel &reply)

{

bool result = EnableBle();

bool ret = reply.WriteBool(result);

if (!ret) {

HILOGE("BluetoothHostStub: reply writing failed in: %{public}s.", \_\_func\_\_);

return TRANSACTION\_ERR;

}

return NO\_ERROR;

}

​ 其中EnableBle()函数的实现是在BluetoothHostServer中,此时已经从ipc目录执行到了server目录,如下:


bool BluetoothHostServer::EnableBle()

{

HILOGD("[%{public}s]: %{public}s(): Enter!", \_\_FILE\_\_, \_\_FUNCTION\_\_);

return IAdapterManager::GetInstance()-\>Enable(BTTransport::ADAPTER\_BLE);

}

​ AdapterManager是IAdapterManager的实现类,AdapterManager中的Enable方法如下,其中传入的参数为BTTransport::ADAPTER_BLE。此时,已经通过server目录进入到了service核心目录。


bool AdapterManager::Enable(const BTTransport transport) const

{

LOG\_DEBUG("%{public}s start transport is %{public}d", \_\_PRETTY\_FUNCTION\_\_, transport);

std::lock\_guard\<std::recursive\_mutex\> lock(pimpl-\>syncMutex\_);

if (PermissionUtils::VerifyDiscoverBluetoothPermission() == PERMISSION\_DENIED) {

LOG\_ERROR("Enable() false, check permission failed");

return false;

}

if (GetSysState() != SYS\_STATE\_STARTED) {

LOG\_ERROR("AdapterManager system is stoped");

return false;

}

if (pimpl-\>adapters\_[transport] == nullptr) {

LOG\_INFO("%{public}s BTTransport not register", \_\_PRETTY\_FUNCTION\_\_);

return false;

}

if (GetState(transport) == BTStateID::STATE\_TURN\_OFF) {

utility::Message msg(AdapterStateMachine::MSG\_USER\_ENABLE\_REQ);

pimpl-\>dispatcher\_-\>PostTask(std::bind(&AdapterManager::impl::ProcessMessage, pimpl.get(), transport, msg));

return true;

} else if (GetState(transport) == BTStateID::STATE\_TURN\_ON) {

LOG\_INFO("%{public}s is turn on", \_\_PRETTY\_FUNCTION\_\_);

return false;

} else {

LOG\_INFO("%{public}s is turning state %{public}d", \_\_PRETTY\_FUNCTION\_\_, GetState(transport));

return false;

}

}

​ 此时蓝牙为关闭状态,即BTStateID::STATE_TURN_OF,因此进入子线程的ProcessMessage函数中处理。其中传入的参数msg封装了AdapterStateMachine::MSG_USER_ENABLE_REQ消息。其中AdapterStateMachine是继承了StateMachine的状态机。


void AdapterManager::impl::ProcessMessage(const BTTransport transport, const utility::Message &msg)

{

std::lock\_guard\<std::recursive\_mutex\> lock(syncMutex\_);

if (adapters\_[transport] == nullptr) {

LOG\_DEBUG("%{public}s adapter is nullptr", \_\_PRETTY\_FUNCTION\_\_);

return;

}

if (adapters\_[transport]-\>stateMachine\_ == nullptr) {

LOG\_DEBUG("%{public}s stateMachine\_ is nullptr", \_\_PRETTY\_FUNCTION\_\_);

return;

}

adapters\_[transport]-\>stateMachine\_-\>ProcessMessage(msg);

}

​ AdapterStateMachine状态机里面的ProcessMessage函数没有重写处理,会进入StateMachine的默认ProcessMessage函数进行分发。AdapterState继承了utility::StateMachine::State,AdapterState的子类将对AdapterStateMachine::MSG_USER_ENABLE_REQ消息处理。AdapterStateMachine状态机的初始化状态为TURN_OFF_STATE,也是当前的状态,最终在AdapterState的子类AdapterTurnOffState中分发AdapterStateMachine::MSG_USER_ENABLE_REQ消息。

​ AdapterTurnOffState的Dispatch方法处理如下:


bool AdapterTurnOffState::Dispatch(const utility::Message &msg)

{

switch (msg.what\_) {

case AdapterStateMachine::MSG\_USER\_ENABLE\_REQ:

Transition(TURNING\_ON\_STATE);

return true;

default:

return false;

}

}

​ AdapterTurnOffState对AdapterStateMachine::MSG_USER_ENABLE_REQ消息的处理就是将当前状态从TURN_OFF_STATE状态变为TURNING_ON_STATE状态,因此状态机自动进入AdapterTurningOnState状态的Entry()函数。如下:


void AdapterTurningOnState::Entry()

{

BTTransport transport =

(adapter\_.GetContext()-\>Name() == ADAPTER\_NAME\_CLASSIC) ? BTTransport::ADAPTER\_BREDR : BTTransport::ADAPTER\_BLE;

AdapterManager::GetInstance()-\>OnAdapterStateChange(transport, BTStateID::STATE\_TURNING\_ON);

LOG\_DEBUG("AdapterStateMachine::Timer enable adapter start transport is %{public}d", transport);

adapterTimer\_-\>Start(ENABLE\_DISABLE\_TIMEOUT\_TIME, false);

adapter\_.GetContext()-\>Enable();

}

​ IAdapterBle和IAdapterClassic都是IAdapter的子类,当前adapter_对象是IAdapterBle的子类BleAdapter对象,因此进入BleAdapter的Enable()方法:


void BleAdapter::Enable()

{

LOG\_DEBUG("[BleAdapter] %{public}s:%{public}s", \_\_func\_\_, Name().c\_str());

GetDispatcher()-\>PostTask(std::bind(&BleAdapter::EnableTask, this));

}

​ 此时进入任务线程中执行EnableTask方法,如下:


bool BleAdapter::EnableTask()

{

LOG\_DEBUG("[BleAdapter] %{public}s", \_\_func\_\_);

std::lock\_guard\<std::recursive\_mutex\> lk(pimpl-\>syncMutex\_);

bool ret = (BTM\_Enable(LE\_CONTROLLER) == BT\_NO\_ERROR);

if (!ret) {

pimpl-\>btmEnableFlag\_ = false;

LOG\_ERROR("[BleAdapter] %{public}s:BTM enable failed!", \_\_func\_\_);

} else {

pimpl-\>btmEnableFlag\_ = true;

LoadConfig();

ret = (InitBtmAndGap() == BT\_NO\_ERROR);

LOG\_DEBUG("[BleAdapter] %{public}s:BTM enable successfully!", \_\_func\_\_);

}

GetContext()-\>OnEnable(ADAPTER\_NAME\_BLE, ret);

return ret;

}

​ 其中GetContext()->OnEnable(ADAPTER_NAME_BLE, ret)方法是对最终是否打开低功耗蓝牙开关的一个回调触发。而BTM_Enable方法是在stack目录下的btm.c文件中实现,将继续实现打开低功耗蓝牙操作。此时已经从service目录进入到stack目录了。


int BTM\_Enable(int controller)

{

LOG\_DEBUG("%{public}s start", \_\_FUNCTION\_\_);

if (controller != BREDR\_CONTROLLER && controller != LE\_CONTROLLER) {

return BT\_BAD\_PARAM;

}

if (!IS\_INITIALIZED()) {

return BT\_BAD\_STATUS;

}

int result = BT\_NO\_ERROR;

MutexLock(g\_modeLock);

if (controller == BREDR\_CONTROLLER) {

if (g\_currentMode == MODE\_NONE) {

result = BtmEnableBrEdrAndSharedModules();

} else if (g\_currentMode == MODE\_LE) {

result = BtmEnableBrEdrModules();

}

if (result == BT\_NO\_ERROR) {

g\_currentMode |= MODE\_BREDR;

}

} else if (controller == LE\_CONTROLLER) {

if (g\_currentMode == MODE\_NONE) {

result = BtmEnableLeAndSharedModules();

} else if (g\_currentMode == MODE\_BREDR) {

result = BtmEnableLeModules();

}

if (result == BT\_NO\_ERROR) {

g\_currentMode |= MODE\_LE;

}

}

#ifdef DEBUG

BtmOutputCurrentStatus();

#endif

MutexUnlock(g\_modeLock);

LOG\_DEBUG("%{public}s end", \_\_FUNCTION\_\_);

return result;

}

​ btm.c的流程相对繁琐,后面3.3节用时序图介绍。

3.2 流程时序图

​ 上面3.1对应的时序流程,如下:

​ 1. napi调用host中的EnableBle()开始,最后在proxy中调用SendRequest,触发stub中的OnRemoteRequest()。

4.png

​ 2. Bluetooth_host_stub->i_bluetooth_host:stub中的OnRemoteRequest()会调用server中的EnableBle()的实现。

5.png

  1. bluetooth_host_server->state_machine:server调用adapter_manager的实现部分,最终调用状态机的transition()。

6.png

  1. state_machine->btm:adapter_state_machine通过调用ble_adapter的Enable()来实现对btm中的BTM_enable()的调用。

7.png

3.3 stack目录下btm使能时序图

​ btm.c的BTM_Enable方法的详细过程,请参考如下的流程图:

8.png

​ 启动ble的所有modules;初始化HCI接口前,首先调用libbluetooth_hal.z.so,初始化提供的接口函数。

​ 之后,初始化HCI:创建相关list,如Failure,Cmd,Event,Acl;初始化hci发送、接收队列;初始化hciHal,HciInitHal信号量。如下:

9.png

​ BtmController的初始化,startAcl,startLeSecurity,startWhiteList。

10.png

​ 启动whitelist的详细步如下:

11.png

​ whitelist启动后,再启动所有的btm modules,则BTM_Enable完成。

4. 蓝牙接口说明

​ 当前提供了基本的蓝牙连接和profile 功能,LTS3.0.1仅包括A2DP, BLE, GATT, SPP等基本接口。其他的功能将会在后续版本中完善和提供。

| 接口文件 | 功能 | 说明 |

| ------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |

| bluetooth_a2dp_src.h | interface and callback function of a2dp source | A2DP,负责传输音频的profile,手机和蓝牙耳机建立连接后传输音频就是通过A2DP |

| bluetooth_ble_advertiser.h | Defines advertiser, including avertise data and callbacks, and advertiser functions | BLE 中心设备、外围设备相关回调,功能 |

| bluetooth_ble_central_manager.h | Central manager common functions | |

| bluetooth_def.h | Defined here are various status codes | bluetooth中的业务常量定义及枚举定义 |

| bluetooth_device_class.h | Bluetooth device class | BluetoothDeviceClass的API接口类 |

| bluetooth_device.h | Bluetooth device major class | bluetooth device major class的static常量定义 |

| bluetooth_gatt_characteristic.h | Bluetooth gatt characteristic interface. | Gatt 相关的接口文件 |

| bluetooth_gatt_client.h | Bluetooth gatt client interface | |

| bluetooth_gatt_descriptor.h | Bluetooth gatt descriptor interface | |

| bluetooth_gatt_manager.h | gatt manager interface | |

| bluetooth_gatt_server.h | gatt server interface | |

| bluetooth_gatt_service.h | gatt service interface | |

| bluetooth_host.h | bluetooth host, including observer and common functions | host本机管理相关接口,如enable |

| bluetooth_remote_device.h | Bluetooth Remote Device API | 远端设备的API接口 |

| bluetooth_socket.h | spp client socket functions | socket,串口相关功能 |

| bluetooth_socket_inputstream.h | spp inputstream framework functions, including basic functions | |

| bluetooth_socket_outputstream.h | spp outputstream framework functions, including basic functions | |

| bluetooth_types.h | macro definition | bluetooth中模板相关的宏定义 |

| uuid.h | framework uuid interface. | uuid类,提供uuid相关接口功能。如字符串和uuid之间的相互转化 |

5. 蓝牙部分业务应用场景

​ LTS3.0.1蓝牙部分目前仅提供c/c++应用开发的接口,由于底层的驱动程序尚待完善,目前提供的只有BLE相关的接口,包括BLE设备gatt相关的操作,以及BLE广播、扫描等功能,其它A2DP,AVRCP,HFP等相关接口在后续增量发布。

5.1 Host管理

​ 蓝牙host管理主要是针对蓝牙本机的基本操作,包括打开和关闭蓝牙(包括传统蓝牙和BLE),设置和获取本机蓝牙名称,传统蓝牙的扫描和取消扫描周边蓝牙设备、获取本机蓝牙profile service列表,获取对其他设备的连接状态,获取、移除本机蓝牙(包括传统蓝牙和BLE)已配对的蓝牙设备或列表等。

​ 主要接口如下表:

| 接口名 | 功能描述 |

| ------------------------------------------------------------ | ------------------------------------------------------------ |

| GetDefaultHost(); | 获取BluetoothHost实例,去管理本机蓝牙操作。 |

| EnableBt(); | 打开本机蓝牙。 |

| DisableBt(); | 关闭本机蓝牙。 |

| GetBtState() const; | 获取本机传统蓝牙状态: <br />* BTStateID::STATE_TURNING_ON; <br />* BTStateID::STATE_TURN_ON; <br />* BTStateID::STATE_TURNING_OFF; <br />* BTStateID::STATE_TURN_OFF. |

| SetLocalName(const std::string &name); | 设置本机蓝牙名称。 |

| GetLocalName() | 获取本机蓝牙名称。 |

| int GetBtConnectionState() const; | bluetooth connects state. <br /> * BTConnectState::CONNECTING; <br /> * BTConnectState::CONNECTED; <br /> * BTConnectState::DISCONNECTING; <br /> * BTConnectState::DISCONNECTED. |

| startBtDiscovery() | 发起蓝牙设备扫描。 |

| cancelBtDiscovery() | 取消蓝牙设备扫描。 |

| isBtDiscovering() | 检查蓝牙是否在扫描设备中。 |

| std::vector<uint32_t> GetProfileList() | 获取 profile service ID 列表 |

| GetBtProfileConnState(uint32_t profileId) | 获取本机蓝牙profile对其他设备的连接状态。BTConnectState |

| getPairedDevices(int transport) | 获取本机蓝牙已配对的蓝牙设备列。 |

| bool BluetoothFactoryReset(); | Factory reset bluetooth service |

| GetLocalDeviceClass() const; | 获取本机device class |

| SetLocalDeviceClass(const BluetoothDeviceClass &deviceClass); | 设置本机device class |

| DisableBle(); | 关闭本机BLE |

| EnableBle(); | 打开本机ble |

| IsBleEnabled() const; | 获取ble状态 |

5.1.1 打开/关闭传统蓝牙

​ 主要步骤:

​ 1. 调用BluetoothHost的GetDefaultHost()接口,获取BluetoothHost实例,管理host蓝牙操作;

​ 2. 打开蓝牙;

​ 3. 查蓝牙状态,关闭蓝牙。

5.1.2 本机传统蓝牙扫描

​ 主要步骤:

​ 1. 调用BluetoothHost的GetDefaultHost()接口,获取BluetoothHost实例,管理host蓝牙操作;

​ 2. 打开蓝牙;

​ 3. 开始扫描;

​ 4. 验证扫描状态,最后关闭蓝牙。

5.2 BLE扫描/广播

​ OpenHarmony低功耗蓝牙的BLE设备交互时,会分为不同的角色,即中心设备和外围设备。其中,中心设备负责扫描外围设备,发现广播信息;而外围设备负责发送广播信息。

​ 主要功能是BLE广播和扫描。根据指定状态获取外围设备;启动或停止BLE扫描、广播。

5.2.1 BLE广播

​ 主要步骤:

​ 1. 进行BLE广播前需要先继承*Bluetooth::BleAdvertiseCallback*类实现OnStartResultEvent(int result)回调,用于获取广播结果;

​ 2. 获取广播对象,构造广播参数和广播数据;

​ 3. 调用StartAdvertising(const BleAdvertiserSettings &settings, const BleAdvertiserData &advData,

​ const BleAdvertiserData &scanResponse, BleAdvertiseCallback &callback)接口开始BLE广播。

5.2.2 BLE扫描

​ 主要步骤:

​ 1. 进行BLE扫描之前先要继承BleCentralManagerCallback类实现scanResultEvent和scanFailedEvent回调函数,用于接收扫描结果;

​ 2. 调用BleCentralManager(BleCentralManagerCallback &callback)构造函数,获取中心设备管理对象;

​ 3. 构造扫描过滤器(或不使用过滤器扫描);

​ 4. 调用startScan()/StartScan(const BleScanSettings &settings)开始扫描BLE设备,在回调中获取扫描到的BLE设备

5.3 GATT 服务端、客户端

​ BLE外围设备和中心设备建立GATT连接,通过该连接中心设备可以获取外围设备所支持的Service、Characteristic、Descriptor等数据。

同时,中心设备可以向外围设备进行数据请求,并向外围设备写入Characteristic、Descriptor等特征值数据。

​ 两台设备建立连接后,其中一台作为GATT服务端,另一台作为GATT客户端。通常发送广播的外围设备作为服务端,负责扫描的中心设备作为客户端。

5.3.1 GATT服务端

​ BLE外围设备作为服务端,可以接收来自中心设备(客户端)的GATT连接请求,应答来自中心设备的特征值内容读取和写入请求,并向中心设备提供数据,从而实现信息交互和消息同步。同时外围设备还可以主动向中心设备发送数据。

​ 主要步骤:

​ 1. 和BLE类似,要想正常调用相关的接口调用,首先得实现GattServerCallback的中的相关的回调函数接口

​ 2. 调用接口创建外围设备服务端并开启服务。

​ 3. 调用*GattService(UUID uuid, boolean isPrimary)*接口创建服务对象,向外围设备添加服务。

​ 4. 从回调接口*OnCharacteristicWriteRequest*中获取中心设备发送来的消息,调用*NotifyCharacteristicChanged*接口向中心设备发送通知。

5.3.2 GATT客户端

​ BLE外围设备和中心设备建立GATT连接,通过该连接,中心设备可以获取外围设备所支持的Service、Characteristic、Descriptor等数据。另外,中心设备也可以向外围设备进行数据请求,并向外围设备写入Characteristic、Descriptor等特征值数据。

​ 主要步骤:

​ 1. 调相关接口启动BLE扫描来获取外围设备(可参考BLE扫描部分);

​ 2. 获取外围设备后,初始化GattClient对象;

​ 3. 调用Connect(GattClientCallback &callback, bool isAutoConnect, int transport)接口,建立与外围BLE设备的GATT连接,boolean参数isAutoConnect用于设置是否允许设备在可发现距离内自动建立GATT连接。

​ 4. 启动GATT连接后,会触发OnConnectionStateChanged(int connectionState, int ret)回调,根据回调结果判断是否连接GATT成功。

​ 5. 在GATT连接成功时,中心设备可以调用DiscoverServices()接口,获取外围设备支持的Services、Characteristics等特征值,在回调OnServicesDiscovered(int status)中获取外围设备支持的服务和特征值,并根据UUID判断是什么服务。

​ 6. 根据获取到的服务和特征值,调用read和write方法可以读取或者写入对应特征值数据。

5.4 SPP

​ OpenHarmony蓝牙 SPP分为服务端和客户端程序。蓝牙串行端口基于SPP协议(Serial Port Profile),能在蓝牙设备之间创建串口进行数据传输,手机一般以客户端的角色主动连接SPP协议设备。主要类对象有:SppClientSocket、SppServerSocket以及SocketFactory。

5.4.1 服务端

​ 程序步骤:

​ 1. 首先打开蓝牙;

​ 2. 创建SPP服务端对象指针,创建对象,并且打开监听状态

​ 3. Accept一定条件的客户端的请求

​ 4. 满足某些条件,关闭客户端程序

5.4.2 客户端

​ 程序步骤:

​ 1. 打开蓝牙;

​ 2. 创建SppClientSocket对象(或指针);

​ 3. 连接server,connect();

​ 4. 断开连接,close()

更多原创内容请关注:深开鸿技术团队

入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。

想了解更多关于开源的内容,请访问:

51CTO 开源基础软件社区

https://ost.51cto.com/#bkwz