天天看点

Android之蓝牙驱动开发总结

一Bluetooth基本概念 3

二AndroidBluetooth架构 3

2.1Bluetooth架构图 3

2.2Bluetooth代码层次结构 3

三Bluetooth协议栈分析 4

3.1蓝牙协议栈 4

3.2Android与蓝牙协议栈的关系 6

四Bluetooth之HCI层分析 6

4.1HCI层与基带的通信方式 6

4.2包的分析及研究 7

4.3通信过程的研究与分析 8

五Bluetooth之编程实现 8

5.1HCI层编程 8

5.2L2CAP层编程 11

5.3SDP层编程 13

六Bluetooth之启动过程实现 14

6.1Bluetooth启动步骤 14

6.2Bluetooth启动流程 15

6.3Bluetooth数据流向 15

6.4Bluez控制流程 15

6.5Bluetooth启动过程分析 16

七Bluetooth之驱动移植 17

7.1android系统配置 17

7.2启动项修改 17

7.3电源管理rfkill驱动 17

7.4Rebuild Android image and reboot 18

7.5实现BT睡眠唤醒机制 18

7.6系统集成 19

八Bluetooth之调试与编译 19

8.1Bluetooth驱动调试 19

8.2Bluetooth调试工具 20

九Bluetooth之应用程序开发 20

9.1Bluetooth的API开发 20

9.2The Basics开发 21

9.3Bluetooth Permissions开发 22

9.4Setting Up Bluetooth服务 22

9.5Finding Devices服务 23

9.6Connecting Devices服务 25

9.7Managing a Connection服务 31

9.8Working with Profiles服务 34

十总结与疑问 36

一Bluetooth基本概念

蓝牙是无线数据和语音传输的开放式标准,它将各种通信设备、计算机及其终端设备、各种数字数据系统、甚至家用电器采用无线方式联接起来。它的传输距离为10cm~10m,如果增加功率或是加上某些外设便可达到100m的传输距离。它采用2.4GHzISM频段和调频、跳频技术,使用权向纠错编码、ARQ、TDD和基带协议。TDMA每时隙为0.625μs,基带符合速率为1Mb/s。蓝牙支持64kb/s实时语音传输和数据传输,语音编码为CVSD,发射功率分别为1mW、2.5mW和100mW,并使用全球统一的48比特的设备识别码。由于蓝牙采用无线接口来代替有线电缆连接,具有很强的移植性,并且适用于多种场合,加上该技术功耗低、对人体危害小,而且应用简单、容易实现,所以易于推广。

蓝牙技术的系统结构分为三大部分:底层硬件模块、中间协议层和高层应用。底层硬件部分包括无线跳频(RF)、基带(BB)和链路管理(LM)。无线跳频层通过2.4GHz无需授权的ISM频段的微波,实现数据位流的过滤和传输,本层协议主要定义了蓝牙收发器在此频带正常工作所需要满足的条件。基带负责跳频以及蓝牙数据和信息帧的传输。链路管理负责连接、建立和拆除链路并进行安全控制。

二AndroidBluetooth架构

2.1 Bluetooth架构图

Android蓝牙系统分为四个层次,内核层、BlueZ库、BlueTooth的适配库、BlueTooth的JNI部分、Java框架层、应用层。下面先来分析Android的蓝牙协议栈。

图1面向库的架构视图

Linuxkernel层:

bluez协议栈、uart驱动,h4协议,hci,l2cap, sco, rfcomm

bluez层:

这是bluez用户空间的库,开源的bluetooth代码,包括很多协议,生成libbluetooth.so。

library层:

libbluedroid.so等

framework层:

实现了Headset/Handsfree和A2DP/AVRCPprofile,但其实现方式不同Handset/Handfree是直接在bluez的RFCOMMSocket上开发的,没有利用bluez的audioplugin,而A2DP/AVRCP是在bluez的audioplugin基础上开发的,大大降低了实现的难度。

Android的蓝牙协议栈采用BlueZ来实现,BlueZ分为两部分:内核代码和用户态程序及工具集。

图2面向进程的架构视图

2.2 Bluetooth代码层次结构

(1)JAVA层

frameworks/base/core/java/android/bluetooth/

包含了bluetooth的JAVA类。

(2)JNI层

frameworks/base/core/jni/android_bluetooth_开头的文件

定义了bluez通过JNI到上层的接口。

frameworks/base/core/jni/android_server_bluetoothservice.cpp

调用硬件适配层的接口system/bluetooth/bluedroid/bluetooth.c

(3)bluez库

external/bluez/

这是bluez用户空间的库,开源的bluetooth代码,包括很多协议,生成libbluetooth.so。

(4)硬件适配层

system/bluetooth/bluedroid/bluetooth.c

包含了对硬件操作的接口

system/bluetooth/data

publicvoid cancel() {

try{

mmServerSocket.close();

}catch (IOException e) { }

}

}

本例中,仅仅只接受一个进来的连接,一旦连接被接受获取到BluetoothSocket,就发送获取到的BluetoothSocket给一个单独的线程,然后关闭BluetoothServerSocket并跳出循环。

注意:accept()返回BluetoothSocket后,socket已经连接了,所以在客户端不应该呼叫connnect()。

manageConnectedSocket()是一个虚方法,用来初始化线程好传输数据。

通常应该在处理完侦听到的连接后立即关闭BluetoothServerSocket。在本例中,close()在得到BluetoothSocket后马上被调用。还需要在线程中提供一个公共的方法来关闭私有的BluetoothSocket,停止服务端socket的侦听。

(2)Connectingas a client

为了实现与远程设备的连接,你必须首先获得一个代表远程设备BluetoothDevice对象。然后使用BluetoothDevice对象来获取一个BluetoothSocket来实现来接。

下面是基本的步骤:

①用BluetoothDevice调用createRfcommSocketToServiceRecord(UUID)获取一个BluetoothSocket对象。

这个初始化的BluetoothSocket会连接到BluetoothDevice。UUID必须匹配服务器设备在打开BluetoothServerSocket时用到的UUID(用listenUsingRfcommWithServiceRecord(String,UUID))。可以简单的生成一个UUID串然后在服务器和客户端都使用该UUID。

② 调用connect()完成连接

当调用这个方法的时候,系统会在远程设备上完成一个SDP查找来匹配UUID。如果查找成功并且远程设备接受连接,就共享RFCOMM信道,connect()会返回。这也是一个阻塞的调用,不管连接失败还是超时(12秒)都会抛出异常。

注意:要确保在调用connect()时没有同时做设备查找,如果在查找设备,该连接尝试会显著的变慢,慢得类似失败了。

实例:

下面是一个完成Bluetooth连接的样例线程:

privateclass ConnectThread extends Thread {

privatefinal BluetoothSocket mmSocket;

privatefinal BluetoothDevice mmDevice;

publicConnectThread(BluetoothDevice device) {

//Use a temporary object that is later assigned to mmSocket,

//because mmSocket is final

BluetoothSockettmp = null;

mmDevice= device;

//Get a BluetoothSocket to connect with the given BluetoothDevice

try{

//MY_UUID is the app's UUID string, also used by the server code

tmp= device.createRfcommSocketToServiceRecord(MY_UUID);

}catch (IOException e) { }

mmSocket= tmp;

}

publicvoid run() {

//Cancel discovery because it will slow down the connection

mBluetoothAdapter.cancelDiscovery();

try{

//Connect the device through the socket. This will block

//until it succeeds or throws an exception

mmSocket.connect();

}catch (IOException connectException) {

//Unable to connect; close the socket and get out

try{

mmSocket.close();

}catch (IOException closeException) { }

return;

}

//Do work to manage the connection (in a separate thread)

manageConnectedSocket(mmSocket);

}

publicvoid cancel() {

try{

mmSocket.close();

}catch (IOException e) { }

}

}

注意到cancelDiscovery()在连接操作前被调用。在连接之前,不管搜索有没有进行,该调用都是安全的,不需要确认(当然如果有要确认的需求,可以调用isDiscovering())。

manageConnectedSocket()是一个虚方法,用来初始化线程好传输数据。

在对BluetoothSocket的处理完成后,记得调用close()来关闭连接的socket和清理所有的内部资源。

9.7 Managing aConnection服务

如果已经连接了两个设备,他们都已经拥有各自的连接好的BluetoothSocket对象。那就是一个有趣的开始,因为你可以在设备间共享数据了。使用BluetoothSocket,传输任何数据通常来说都很容易了:

(1)通过socket获取输入输出流来处理传输(分别使用getInputStream()和getOutputStream())。

(2)用read(byte[])和write(byte[])来实现读写。

仅此而已。

当然,还是有很多细节需要考虑的。首要的,需要用一个专门的线程来实现流的读写。只是很重要的,因为read(byte[])和write(byte[])都是阻塞的调用。read(byte[])会阻塞直到流中有数据可读。write(byte[])通常不会阻塞,但是如果远程设备调用read(byte[])不够快导致中间缓冲区满,它也可能阻塞。所以线程中的主循环应该用于读取InputStream。线程中也应该有单独的方法用来完成写OutputStream。

示例:

下面是一个如上面描述那样的例子:

privateclass ConnectedThread extends Thread {

privatefinal BluetoothSocket mmSocket;

privatefinal InputStream mmInStream;

privatefinal OutputStream mmOutStream;

publicConnectedThread(BluetoothSocket socket) {

mmSocket= socket;

InputStreamtmpIn = null;

OutputStreamtmpOut = null;

//Get the input and output streams, using temp objects because

//member streams are final

try{

tmpIn= socket.getInputStream();

tmpOut= socket.getOutputStream();

}catch (IOException e) { }

mmInStream= tmpIn;

mmOutStream= tmpOut;

}

publicvoid run() {

byte[]buffer = new byte[1024]; // buffer store for the stream

intbytes; // bytes returned from read()

//Keep listening to the InputStream until an exception occurs

while(true) {

try{

//Read from the InputStream

bytes= mmInStream.read(buffer);

//Send the obtained bytes to the UI Activity

mHandler.obtainMessage(MESSAGE_READ,bytes, -1, buffer)

.sendToTarget();

}catch (IOException e) {

break;

}

}

}

publicvoid write(byte[] bytes) {

try{

mmOutStream.write(bytes);

}catch (IOException e) { }

}

publicvoid cancel() {

try{

mmSocket.close();

}catch (IOException e) { }

}

}

构造函数中得到需要的流,一旦执行,线程会等待从InputStream来的数据。当read(byte[])返回从流中读到的字节后,数据通过父类的成员Handler被送到主Activity,然后继续等待读取流中的数据。

向外发送数据只需简单的调用线程的write()方法。

线程的cancel()方法时很重要的,以便连接可以在任何时候通过关闭BluetoothSocket来终止。它应该总在处理完Bluetooth连接后被调用。

9.8 Working withProfiles服务

从Android3.0开始,BluetoothAPI就包含了对Bluetoothprofiles的支持。Bluetoothprofile是基于蓝牙的设备之间通信的无线接口规范。例如Hands-Freeprofile(免提模式)。如果移动电话要连接一个无线耳机,他们都要支持Hands-Freeprofile。

你在你的类里可以完成BluetoothProfile接口来支持某一Bluetoothprofiles。AndroidBluetooth API完成了下面的Bluetoothprofile:

Headset:Headsetprofile提供了移动电话上的Bluetooth耳机支持。Android提供了BluetoothHeadset类,它是一个协议,用来通过IPC(interprocesscommunication)控制BluetoothHeadset Service。BluetoothHeadset既包含BluetoothHeadset profile也包含Hands-Freeprofile,还包括对AT命令的支持。

A2DP:AdvancedAudio Distribution Profile (A2DP)profile,高级音频传输模式。Android提供了BluetoothA2dp类,这是一个通过IPC来控制BluetoothA2DP的协议。

下面是使用profile的基本步骤:

① 获取默认的Bluetooth适配器。

②使用getProfileProxy()来建立一个与profile相关的profile协议对象的连接。在下面的例子中,profile协议对象是BluetoothHeadset的一个实例。

③设置BluetoothProfile.ServiceListener。该listener通知BluetoothProfileIPC客户端,当客户端连接或断连服务器的时候。

④在onServiceConnected()内,得到一个profile协议对象的句柄。

⑤一旦拥有了profile协议对象,就可以用它来监控连接的状态,完成于该profile相关的其他操作。

例如,下面的代码片段显示如何连接到一个BluetoothHeadset协议对象,用来控制Headsetprofile:

BluetoothHeadsetmBluetoothHeadset;

//Get the default adapter

BluetoothAdaptermBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

//Establish connection to the proxy.

mBluetoothAdapter.getProfileProxy(context,mProfileListener, BluetoothProfile.HEADSET);

privateBluetoothProfile.ServiceListener mProfileListener = newBluetoothProfile.ServiceListener() {

publicvoid onServiceConnected(int profile, BluetoothProfile proxy) {

if(profile == BluetoothProfile.HEADSET) {

mBluetoothHeadset= (BluetoothHeadset) proxy;

}

}

publicvoid onServiceDisconnected(int profile) {

if(profile == BluetoothProfile.HEADSET) {

mBluetoothHeadset= null;

}

}

};

//... call functions on mBluetoothHeadset

//Close proxy connection after use.

mBluetoothAdapter.closeProfileProxy(mBluetoothHeadset);

十 总结与疑问

1.蓝牙驱动的上下电是怎么完成的?是通过操作寄存器,还是其他的方式?

2.bccmd命令主要用来初始化蓝牙,比如说上电,设置波特率,下载firmware,它是如何实现的?

3.如何设置PSKEY键值?

4.蓝牙协议栈bluez是如何通过dbus总线与framework层进行数据交互的?

文章转载:http://www.cnblogs.com/MMLoveMeMM/articles/4025508.html