蓝牙流程介绍

1 基础知识介绍

1.1 缩略语

BTIF: Bluetooth Interface

BTU: Bluetooth Upper Layer

BTM: Bluetooth Manager

BTE: Bluetooth embedded system

BTA:Blueetooth application layer

CO: call out\CI: call in

HF: Handsfree Profile

HH: HID Host Profile

HL: Health Device Profile

AV:audio\vidio

ag: audio gateway

ar: audio/video registration

gattc: GATT client

BLE: Bluetooth Low Energy

1.2 android蓝牙结构

再把左边的部分展开来看:

再把它摊开来看:

代码分布:

frameworks/base/core/java/Android/Bluetooth:

frameworks/base/services/java/com/android/server/BluetoothManagerService.java:

里面提供java层使用的一些类和一些aidl文件,供其他进程调用。打开和关闭蓝牙的公共接口也在这里面。

packages/apps/Bluetooth:对应Bluetooth.apk,作为系统的蓝牙的核心进程而存在,com.android.bluetooth,调用framework的打开蓝牙接口后会启动该进程。其内部实现了多种上层蓝牙模式:opp,hfp,a2dp,hdp,hid等,并通过JNI调用与hal层完成联系。

/hardware/libhardware/include/hardware/bluetooth.h: hal层接口头文件

external/bluetooth/bluedroid:bluedroid官方协议栈

packages/apps/Settings/src/com/android/settings/Bluetooth: setting中蓝牙部分,界面相关

bluedroid蓝牙的调用方式:从apk到framework,framework再通过binder调用bluetooth应用,在通过应用层利用jni调用hal层实现蓝牙的各种请求。

1.3 协议简介

Core Specification(核心规范),用于规定蓝牙设备必须实现的通用功能和协议层次。它由软件和硬件模块组成,两个模块之间的信息和数据通过主机控制接口(HCI)的解释才能进行传递。

Profiles(蓝牙应用规范),它从应用场景的角度为蓝牙技术的使用制定了不同的规范。这也是和大众日常生活接触最多的一部分。蓝牙支持很多Profiles,下文将介绍几种使用最广泛的蓝牙应用规范

OPP:文件传输规范

hfp:和电话相关,接听、挂断电话,以及连接sco通路

a2dp:蓝牙立体声规范,其中包含avrcp规范,avrcp规范实现了听歌时暂停、上下歌曲选择等控制模式。目前蓝牙耳机一般都支持这两种规范。

hid:人机交互规范,蓝牙鼠标键盘等

phap:电话号码簿访问协议

hdp:蓝牙医疗相关规范

2 打开蓝牙

2.1 文件路径

文件路径:

frameworks/base/core/java/android/bluetooth/BluetoothAdapter.java

frameworks/base/services/java/com/android/server/BluetoothManagerService.java

packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java

packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterState.java

packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp

hardware/libhardware/include/hardware/bluetooth.h

external/bluetooth/bluedroid/btif/src/bluetooth.c

2.2 上层打开流程

先从framework提供的公共接口开始:

打开蓝牙使用BluetoothAdapter.java提供的方法getDefaultAdapter(),该方法中先获取BluetoothManagerService.java提供的binder对象,并且用该对象创建BluetoothAdapter实例。

获取到BluetoothAdapter实例用,调用enable方法,BluetoothAdapter的enable方法会调用BluetoothManagerService.java的enable,直接看BluetoothManagerService.java,在其中enable方法会启动一个service,这个service就是AdapterService。在启动这个service之后,先不看这个service的具体动作,继续看BluetoothManagerService会做什么。

在启动这个AdapterService之后,先不看这个AdapterService的具体动作,继续看BluetoothManagerService中的执行流程。在启动AdapterService,会回调mConnection的onServiceConnected()方法,在该方法中发送MESSAGE_BLUETOOTH_SERVICE_CONNECTED 消息,在处理该消息时,做了下面三件事:

1、得到AdapterService返回的IBluetooth.aidl接口的实现类。

mBluetooth = IBluetooth.Stub.asInterface(service)

2.调用IBluetooth.aidl提供的接口注册AdapterService的回调方法。

这样,不仅BluetoothManagerService可以通过aidl调用AdapterService中的方法,也实现了AdapterService可以回调BluetoothManagerService中的方法。回调方法如下所示,记住这个回调方法,后面会讲到。

mBluetooth.registerCallback(mBluetoothCallback)

3、第三件事就是调用IBluetooth.aidl提供的enable方法。下面就进入到AdapterService.java中。

下面就进入了package/apps下:

首先看onBind方法,返回了一个对象mBinder。执行其中的enable方法,

调用mAdapterStateMachine的sendMessage方法,发送USER_TURN_ON消息。。AdapterState.java是一个状态机,有以下几种状态:

mOffState:关闭

mPendingCommandState:活动,打开中

mOnState:打开

初始状态是mOffState。

发送消息后,在OffState状态下处理消息,首先切换状态到mPendingCommandState,然后调用adapterService.processStart()方法。

继续回到adapterService。执行如下代码,启动手机支持的所有profile,

手机支持的所有profile如下:

挨个启动上面所有的服务。每个服务启动之后都会调用adapterService中的notifyProfileServiceStateChanged方法。该方法中发送MESSAGE_PROFILE_SERVICE_STATE_CHANGED消息。消息处理如下:

如果所有服务都起来之后,给状态机AdapterState发送消息AdapterState.STARTED。

根据以上流程,现在状态机在mPendingCommandState状态,在该状态下处理STARTED消息。代码如下:

终于看到native方法。

对应的native方法在com_android_bluetooth_btservice_AdapterService.cpp中。调用了sBluetoothInterface结构体中的enable方法。该结构体的定义在bluetooth.h中,具体实现在bluetooth.c中。后面的代码这里不做具体分析。

2.3 底层回调流程

回调主要使用

HAL_CBACK(bt_hal_cbacks, device_found_cb, num_properties, properties); HAL_CBACK就是一宏定义,就是调用结构体中对应的方法。

这里重点看一下一个bt_callbacks_t结构体,定义在bluetooth.h中,如下

初始化在bluetooth.c中,初始化的流程。

回到AdapterService.java,在服务启动时调用了initNative方法。看下jni中的initNative方法:

还是sBluetoothInterface结构体,继续看bluetooth.c文件,可以看到init方法中将地址sBluetoothCallbacks赋值给bt_callbacks_t,也就是,回调结构体的地址指向了sBluetoothCallbacks,看下sBluetoothCallbacks的定义。任然在jni中定义。这样在启动AdapterService时将回调地址传入了hal层。

最后,在驱动层完成蓝牙打开之后,会执行以下两个回调方法:

adapter_properties_callback:返回手机蓝牙设备的地址、名称、UUID等。

adapter_state_change_callback:更新AdapterProperties中蓝牙状态,发送广播通知蓝牙状态变化。

具体分析下adapter_state_change_callback。对应的jni方法为:adapter_state_change_callback,方法执行以下方法。

callbackEnv->CallVoidMethod(sJniCallbacksObj, method_stateChangeCallback, (jint)status);

也就是回调java层代码:JniCallback.java文件中stateChangeCallback方法。这时跳转到AdapterState.java中,执行stateChangeCallback()方法;发送了ENABLED_READY消息。根据以上分析,这时状态机还处于PendingCommandState,在该状态下处理ENABLED_READY消息,

做下面两个动作:状态切换到mOnState;更新adapterProperties中的蓝牙状态信息;通知蓝牙状态变为打开。具体看下notifyAdapterStateChange方法。主要是调用了adapterService类的方法。

adapterService.updateAdapterState(oldState, newState);

来到adapterService类。

可以看到,看是执行该服务中注册的回调方法。应该还能记得,之前在打开蓝牙操作初期,在BluetoothManagerService中注册了回调方法。因此又跳转到framework中,执行回调方法。在该回调方法中,发送广播通知蓝牙状态变化。

2.4 总结

蓝牙打开的流程到这里,蓝牙打开从framework公共接口开始调用enable方法,执行到bluetooth.apk中,在该应用中通过jni注册回调方法和调用hal层打开蓝牙方法,在驱动层完成蓝牙上电等操作后,通过hal-jni回调到应用层中,应用通过aidl回调通知framework蓝牙状态变化,framework发送广播通知大家蓝牙打开。

3 搜索蓝牙

3.1 新增代码路径

packages/apps/Bluetooth/src/com/android/bluetooth/btservice/RemoteDevices.java

packages/apps/Settings/src/com/android/settings/Bluetooth/*

3.2 搜索过程

BluetoothSettings.java中的startScanning命令,开始搜索。

如果a2dp没有在播放,就调用BluetoothAdapter.java中的startDiscovery()方法。

然后又回到AdapterService中,调用startDiscovery方法。在该方法中直接进入到jni层。

在jni层的函数中执行如下语句,依然是该结构体。

int ret = sBluetoothInterface->start_discovery();

具体的底层搜索方法暂不往下追踪。驱动层完成搜索之后,返回状态,根据返回的消息进行处理。回调消息分如下几种:包括搜索结果返回消息、搜索完成消息、搜索取消等。

BTA_DM_INQ_RES_EVT

BTA_DM_INQ_CMPL_EVT

BTA_DM_DISC_RES_EVT

BTA_DM_DISC_BLE_RES_EVT

BTA_DM_DISC_CMPL_EVT

BTA_DM_DI_DISC_CMPL_EVT

BTA_DM_SEARCH_CANCEL_CMPL_EVT

当收到搜索结果放回消息之后,会执行以下回调方法。

其中后面两个参数:设备属性个数和设备属性。其中设备属性包括:地址、名称、信号强度等属性。根据上面所讲的回调方法初始化,直接找到jni层的对应回调方法,可以看到会先后调用以下两个方法。

其中devicePropertyChangedCallback方法更新RemoteDevice类中的变量值。

deviceFoundCallback方法发送BluetoothDevice.ACTION_FOUND广播,通知setting中要更新界面显示该设备。

4 蓝牙配对

4.1 新增代码路径

packages/apps/Settings/src/com/android/settings/Bluetooth/*

packages/apps/Bluetooth/src/com/android/bluetooth/btservice/ BondStateMachine.java

packages/apps/Settings/src/com/android/settings/Bluetooth/BluetoothEventManager.java

packages/apps/Settings/src/com/android/settings/Bluetooth/CachedBluetoothDevice.java

4.2 配对过程分析

在setting中点击设备之后开始进行配对。调用BluetoothDevice的createBond方法,走到AdapterService.java中的createBond方法。驱动BondStateMachine中的状态从StableState到

PendingCommandState。然后调用jni层createBondNative方法。又来到hal层接口sBluetoothInterface:

sBluetoothInterface->create_bond((bt_bdaddr_t *)addr);

同样,在底层完成配对之后进行回调:

找到对应的回调方法,JniCallback.java中的

void bondStateChangeCallback(int status, byte[] address, int newState) {

mBondStateMachine.bondStateChangeCallback(status, address, newState);

}

5 Opp文件传输

只有opp的弹框是bluetooth里,其他都是setting中弹框。

5.1 发送文件

点击用蓝牙分享文件后,会调用BluetoothOppLauncherActivity.java,其中会判断当前蓝牙是否打开,如果没打开则进入BluetoothOppBtEnableActivity.java打开蓝牙,如果已经打开,则直接进入BluetoothDevicePicker.java选择传输文件设备选择。

6 Map消息访问profile

蓝牙开启后会启动BluetoothMapService.java

在BluetoothMapService.java调用了BluetoothMapMasInstance.java的startRfcommSocketListener()方法,监听rfcomm端的连接请求,如果有连接请求,调用BluetoothMapService 的 onConnect()方法,在该方法里发送广播到setting中,setting弹框,用户确认后发送广播ACTION_CONNECTION_ACCESS_REPLY通知BluetoothMapService,BluetoothMapService收到广播后调用onConnectHandler方法,调用startObexServerSession方法,启动Obex传输通道。

7 AVRCP

手机侧主动改变状态。注册了一个RemoteControllerWeak(RemoteController),当手机端的播放器暂停、快进、快退、切歌等操作后,会调用远程控制接口更新播放状态、歌曲信息。mTrackChangedNT、mPlayStatusChangedNT、mPlayPosChangedNT三个变量的值决定是否会通知对端进行同步;这三个值有jni回调改变,即对端回调改变。

对端主动改变状态:1)快进、快退底层有回调handlePassthroughCmd,来通知avrcp实时更新进度条,并同步给对端。部分车载会上报快进、快退的keycode给手机端,播放器利用RemoteController更新状态。2)播放、暂停,对端上报keycode,播放器RemoteController控制avrcp中状态切换

8 Pbap

类似map,连接后弹框都是广播给setting弹框

9 功耗

之前的audiopath比较复杂,要到DSP里面去硬解码。现在比较简单了,从驱动角度看,收到AF写下来的PCM数据后,经协议栈SBC编码后经HCI接口写入到蓝牙芯片,然后就发出去了。

SBC编码是针对蓝牙设备的一种音频编码方式,压缩率中等,但是cpu消耗低。电话对应HFP profile,音乐对应Profile。电话使用的是同步链路SCO,音乐时异步链路ACL,使用A2DP profile。另外还有其它profile,传问题,获取电话本,短信同步等。

使用A2DP时,先是进行音乐的解码,然后再把解码后的PCM进行SBC编码。A2DP支持下行audio编码采样率范围为8k-48k。在高通的大多数方案里面,SBC编码是在DSP里做的。

HFP,有两个角色AG和HF,正常情况下手机作为AG,耳机作为HF。但是手机代码中也有HF部分的代码

手机蓝牙芯片是三合一的,包括BT,FM和WLAN,使用一根天线。蓝牙耳机和蓝牙模块之间通过AT命令进行通信,分为master和slave,谁先连对方谁就是master。蓝牙功耗待机1mA左右,传文件要100多mA,听音乐60左右,打电话30。

--------------------------------------------------------

转自:https://blog.csdn.net/pashanhu6402/article/details/80183495

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容