1.3 蓝牙技术在android中的应用
1.3.1 蓝牙服务的启动
在前面章节android启动过程中介绍到android服务的启动,init进程中,启动Zygote后,然后由SystemServer启动一系列服务,蓝牙服务就是在这个时候启动的。详细见代码:
/framework/base/services/java/com/android/server/SystemServer.java
if (SystemProperties.get("ro.kernel.qemu").equals("1")) {
} else {
……
bluetooth = new BluetoothService(context);
……
bluetoothA2dp = new BluetoothA2dpService(context, bluetooth);
……
if (airplaneModeOn == 0 && bluetoothOn != 0) {
bluetooth.enable();
}
Bluetooth服务的代码首先通过SystemProperties的get方法来判断系统是不是使用模拟器内核,如果是使用模拟器内核来启动android的系统,那么就会跳过蓝牙服务的启动,也就是说Android 4.0模拟器是不支持蓝牙系统的。否则就是一个实在的设备产品(ro.kernel.qemu=0)
就是构造一个bluetooth的服务(BluetoothService)和一个蓝牙耳机服务(BluetoothA2dpService)。
代码段最后一部分是判断开机是否要启用蓝牙,通过函数我们可以看到如果设备的飞行模式是关闭的并且bluetooth的那个开关是在on。就是调用bluetoothService的enable方法使得我们设备开机的时候就将蓝牙开启。飞行模式就是那些使用无线频谱的模块都必须关掉,譬如:wifi,Bluetooth,GPS等。接下来就是BluetoothService的enable方法了。
/framework/base/core/java/android/server/BluetoothService.java
public synchronized boolean enable(boolean saveSetting) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,"Need BLUETOOTH_ADMIN permission");
if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {
return false;
}
mBluetoothState.sendMessage(BluetoothAdapterStateMachine.USER_TURN_ON, saveSetting);
return true;
}
蓝牙服务的enable的方法会先判断进程有没有操作权限,需要蓝牙管理的权限才能去enable蓝牙模块,然后还会再次判断系统的飞行模式有没有打开,如果此时飞行模式是on的话,那么会返回,还是不能打开服务。在确保拥有权限并且不是出于飞行模式的情况下,就会往蓝牙状态机发送一个USER_TURN_ON的命令。下面介绍一下android中的蓝牙状态机。
?Poweroff
这就是蓝牙模块没有初始化的状态,这时候硬件模块是出于没有上电的状态。
?Warmup
这个状态就是给设备上电,使设备能够从没电到待机状态。
?Hotoff
Hotoff我个人理解就是在模块上电了,出于一种待命的状态,如果收到了turn_on_coninue的命令时候就会去将蓝牙模块切换到工作状态。如果接收到了turn_cold的命令时候,设备就会断电进入poweroff状态。
?Switching
这也是一个中间状态,需要继续接收命令。
?Bluetoothon
这时蓝牙模块出于正常工作的状态。
根据android中蓝牙状态的源码中,具体的各个状态机相互转换图如下:
/framework/base/core/java/android/server/BluetoothAdapterStateMachine.java
private class PowerOff extends State {
public void enter() {
if (DBG) log("Enter PowerOff: " + getCurrentMessage().what);
}
……
case USER_TURN_ON:
broadcastState(BluetoothAdapter.STATE_TURNING_ON);
transitionTo(mWarmUp);
……
if (prepareBluetooth()) {
if ((Boolean) message.obj) {
persistSwitchSetting(true);
}
deferMessage(obtainMessage(TURN_ON_CONTINUE));
蓝牙状态机初始化时PowerOff的,从上面的BluetoothService的enable函数中USER_TURN_ON命令。从上面代码中可以看出蓝牙状态机在接收到USER_TURN_ON后,首先就像蓝牙适配器广播蓝牙正处于STATE_TRUNING_ON的状态,蓝牙的适配器的蓝牙状态有四个:
分别是,state_off(10),state_turning_on(11),state_on(12),state_turning_off(14)。由于我们刚开机所以蓝牙适配器的状态必然是从10->11。然后将蓝牙状态机的状态切换到mWaremUp状态。
接下来调用了prepareBluetooth()方法。接下来看看prepareBluetooth方法。代码如下:
/framework/base/core/java/android/server/BluetoothAdapterStateMachine.java
private boolean prepareBluetooth() {
if (mBluetoothService.enableNative() != 0) {
return false;
}
……
int retryCount = 2;
boolean eventLoopStarted = false;
while ((retryCount-- > 0) && !eventLoopStarted) {
mEventLoop.start();
while ((pollCount-- > 0) && !eventLoopStarted) {
if (mEventLoop.isEventLoopRunning()) {
eventLoopStarted = true;
break;
}
在preprareBluetooth方法中,首先就是调用了BluetoothService的enableNative()的方法,只要一看到这种带Native的方法,JNI的代码是少不了的。由于enableNative方法走的路有点多,所以先直接到BluetoothService的代码中寻找enableNative()看个究竟。
framework/base/core/java/android/server/BluetoothService.java
/*package*/ native int enableNative();
Framework/base/core/jni/ android_server_BluetoothService.cpp
static JNINativeMethod sMethods[] = {
……
{"enableNative", "()I", (void *)enableNative},
……
}
static jint enableNative(JNIEnv *env, jobject object) {
return bt_enable();
}
从上面的代码可以看出,BluetoothService的enableNative就是直接调用了JNI的代码,JNI是java native interface的 缩写,中文叫java本地接口。Android上层跑的java代码,而底层代码都是c语言。以android的一贯作风是通过JNI代码调用HAL层,然后就可以直接调用驱动代码或者经由内核达到操作驱动代码。enableNative的代码很简单,就是调用了bt_enable。我们可以继续找到这个函数的实现。
System/bluetooth/bluedroid/bluetooth.c
int bt_enable() {
……
if (set_bluetooth_power(1) < 0) goto out
……
if (property_set("ctl.start", "hciattach") < 0)
……
for (attempt = 1000; attempt > 0; attempt--) {
hci_sock = create_hci_sock();
……
ret = ioctl(hci_sock, HCIDEVUP, HCI_DEV_ID);
……
}
Set_bluetooth_power()函数会根据蓝牙的硬件开关,也就是hci设备注册的时候会同时在linux内核中注册一个rfkill类,比如我们在电脑键盘上面可能会看见一个按键来开关蓝牙或者wifi之类的。这里会去读这个键值,如果是1代表可以开启蓝牙的,否则是没法使用蓝牙的,在开发过程中如果没这样的按键,可将这行代码拿掉。Propery_set(“ctl.start”,hciattach)。这个函数会去启动hciattach服务,具体这个服务是以二进制文件存储在系统system/bin目录下面的。
我们可以从andriod启动脚本文件找到名字叫hciattach服务。当然这个是针对接串口的蓝牙来说需要启动服务,如果我们的设备是通过USB总线接入系统的话,其实这个服务也是可以不启动的。剩下的代码是一个for循环,先建立一个bluetooth的套接字,然后通过ioctl来和bluez的代码来打开蓝牙设备,可以重试1000次。接下来的代码就要跑到内核的BlueZ了。
Kernel/net/bluetooth/hci_sock.c
static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg){
……
case HCIDEVUP:
if (!capable(CAP_NET_ADMIN))
return -EACCES;
return hci_dev_open(arg);
……
}
从上面的ioctl下来的代码可以看出,当函数第二个参数cmd为HCIDEVUP时,就会直接调用hci_dev_open(arg)方法。这个函数就好比我们在终端下面使用蓝牙调试工具hciconfig,执行了
#hciconfig hci0 up。
Kernel/net/bluetooth/hci_core.c
int hci_dev_open(__u16 dev)
{
……
hdev = hci_dev_get(dev);
……
if (hdev->open(hdev)) {
ret = -EIO;
goto done;
}
}
在kernel的bluez调用hci_dev_open,而在这个函数中又hdev->open(hdev),这个就是我们驱动注册时候的回调函数open。由于我们平台使用的是usb的蓝牙接入方式,我就以usb的蓝牙驱动为例,看看驱动的open函数。
Kernel/driver/bluetooth/btusb.c
static int btusb_open(struct hci_dev *hdev){
……
err = btusb_submit_intr_urb(hdev, GFP_KERNEL);
……
err = btusb_submit_bulk_urb(hdev, GFP_KERNEL);
……
}
USB hci设备打开后,首先将设备的interface配置为HCI_RUNNING状态,然后为数据传输初始化设备的端点和管道,初始化和填充urb。代码到这,蓝牙设备就算是真正打开了。
回到之前的蓝牙状态机的代码:
/framework/base/core/java/android/server/BluetoothAdapterStateMachine.java
private boolean prepareBluetooth() {
if (mBluetoothService.enableNative() != 0) {
return false;
}
……
int retryCount = 2;
boolean eventLoopStarted = false;
while ((retryCount-- > 0) && !eventLoopStarted) {
mEventLoop.start();
while ((pollCount-- > 0) && !eventLoopStarted) {
if (mEventLoop.isEventLoopRunning()) {
eventLoopStarted = true;
break;
}
mBluetoothService在enableNative()函数主要功能就是通过一系列代码来打开蓝牙设备。如果设备驱动代码没有问题的话,我们enableNative()返回的将会是true。在实际调试蓝牙设备时候,我们可以通过在linux或者android的终端下面使用自带的工具命令(hciconfig),执行:
# hciconfig –a如果驱动能够和设备绑定的话,我们就会看到蓝牙设备的一些比较重要信息,如:蓝牙的物理地址,总线类型,协议类型等。
上面的代码接下来会是一个while循环,执行2次。mEventLoop.start()。也就是说调用了EventLoop的start方法。
/framework/base/core/java/android/server/BluetoothEventLoop.java
/* package */ void start() {
if (!isEventLoopRunningNative()) {
if (DBG) log("Starting Event Loop thread");
startEventLoopNative();
}
}
第一次进入这个函数isEventLoopRunningNative肯定是返回false的,所以直接进入了startEventLoopNative(),前面说过了一般带native的函数结尾的函数都是JNI。看到这里又要进JNI了。
Framework/base/core/jni/android_server_BluetoothEventLoop.cpp
static JNINativeMethod sMethods[] = {
……
{"startEventLoopNative", "()V", (void *)startEventLoopNative},
……
}
static jboolean startEventLoopNative(JNIEnv *env, jobject object) {
……
nat->pollData = (struct pollfd *)malloc(sizeof(struct pollfd) *
DEFAULT_INITIAL_POLLFD_COUNT);
……
nat->watchData = (DBusWatch **)malloc(sizeof(DBusWatch *) *
DEFAULT_INITIAL_POLLFD_COUNT);
……
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, &(nat->controlFdR))) {
LOGE("Error getting BT control socket");
goto done;
}
……
if (setUpEventLoop(nat) != JNI_TRUE) {
LOGE("failure setting up Event Loop!");
goto done;
}
pthread_create(&(nat->thread), NULL, eventLoopMain, nat);
……
}
为socket文件描述符分配内存数据,同时为DBusWatch结构体分配内存,socketpair创建了一对套接字(AF_LOCAL域中使用),这个描述符可以是单双工也可以是全双工的,这里是单双工的,也就是只能从这个描述符中读取数据,而不能写数据。如果socketpair的第四个参数是个数组,也可以实现一个描述符读,另外一个描述符写。从而实现全双工。然后就是setUpEventLoop函数,最后就是创建了eventLoopMain的线程。
Framework/base/core/jni/android_server_BluetoothEventLoop.cpp
static jboolean setUpEventLoop(native_data_t *nat) {
……
dbus_threads_init_default();
……
dbus_error_init(&err);
……
if (!dbus_connection_add_filter(nat->conn, event_filter, nat, NULL)){
return JNI_FALSE;
}
……
dbus_bus_add_match(nat->conn,
"type='signal',interface='org.freedesktop.DBus'", &err);
dbus_bus_add_match(nat->conn,
"type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Adapter'",&err);
……
}
这里是初始化dbus,是bluez能够挂接上dbus。建立一个dbus连接之后,为这个dbus连接起名,为我们将要进行的消息循环添加匹配条件(就是通过信号名和信号接口名来进行匹配控制的) -- dbus_bus_add_match()。我们进入等待循环后,只需要对信号名,信号接口名进行判断就可以分别处理各种信号了。在各个处理分支上。我们可以分离出消息中的参数。对参数类型进行判断和其他的处理。具体对dbus感兴趣的话可以参照:http://dbus.freedesktop.org。
Framework/base/core/jni/android_server_BluetoothEventLoop.cpp
static void *eventLoopMain(void *ptr) {
……
while (1) {
……
if (nat->pollData[i].fd == nat->controlFdR) {
while (recv(nat->controlFdR, &data, sizeof(char), MSG_DONTWAIT) != -1) {
……
switch (data) {
case EVENT_LOOP_EXIT:
dbus_connection_set_watch_functions(nat->conn,NULL, NULL, NULL, NULL, NULL);
tearDownEventLoop(nat);
nat->vm->DetachCurrentThread();
……
case EVENT_LOOP_ADD:
{
handleWatchAdd(nat);
break;
}
case EVENT_LOOP_REMOVE:
{
handleWatchRemove(nat);
break;
}
以轮训的方式从socket的描述符中不断的接收数据,如果有数据到来,就根据数据的类型做相应的处理。到这里dbus就和bluez建立连接。还是回到之前我们prepareBluetooth的函数。
/framework/base/core/java/android/server/BluetoothAdapterStateMachine.java
private class PowerOff extends State {
public void enter() {
if (DBG) log("Enter PowerOff: " + getCurrentMessage().what);
}
……
case USER_TURN_ON:
broadcastState(BluetoothAdapter.STATE_TURNING_ON);
transitionTo(mWarmUp);
……
if (prepareBluetooth()) {
if ((Boolean) message.obj) {
persistSwitchSetting(true);
}
deferMessage(obtainMessage(TURN_ON_CONTINUE));
前面的代码我们分析完了prepareBluetooth(),如果没有问题就进入了persistSwitchSetting()。
然后就是讲蓝牙状态机切换到mWarnUp状态。并向蓝牙状态机发送了一个TURN_ON_CONTINUE的命令。
/framework/base/core/java/android/server/BluetoothAdapterStateMachine.java
private class WarmUp extends State {
……
public boolean processMessage(Message message) {
log("WarmUp process message: " + message.what);
……
switch(message.what) {
case TURN_ON_CONTINUE:
这个命令在WarmUp状态里面什么也没做。直接通过deferMessage()到HotOff状态里面重新发送了TURN_ON_CONTINUE的命令。那我们HotOff状态机。
/framework/base/core/java/android/server/BluetoothAdapterStateMachine.java
private class HotOff extends State {
……
public boolean processMessage(Message message) {
log("HotOff process message: " + message.what);
……
switch(message.what) {
case TURN_ON_CONTINUE:
int retryCount = 5
……
mBluetoothService.switchConnectable(true);
transitionTo(mSwitching);
……
}
在HotOff状态机中,接收到TURN_ON_CONTINUE命令后,先调用了BluetoothService的switchConnectable(true);然后将蓝牙的状态机切换到Switching状态。
/framework/base/core/java/android/server/BluetoothService.java
/*package*/ synchronized void switchConnectable(boolean on) {
setAdapterPropertyBooleanNative("Powered", on ? 1 : 0);
}
又到了以Native结尾的函数,还是到JNI里面找到它的实现吧。
Framework/base/core/jni/android_server_BluetoothService.cpp
static jboolean setAdapterPropertyNative(JNIEnv *env, jobject object, jstring key,
void *value, jint type) {
……
msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,get_adapter_path(env, object),
DBUS_ADAPTER_IFACE, "SetProperty");
……
dbus_message_append_args(msg, DBUS_TYPE_STRING, &c_key, DBUS_TYPE_INVALID);
dbus_message_iter_init_append(msg, &iter);
……
reply = dbus_connection_send_with_reply(nat->conn, msg, NULL, -1);
……
}
通过Dbus向bluez发送SetPropery的信息(message),当成功的时候,我们在另外一端就会收到powerChanged的信号。具体处理如下的代码:
Framework/base/core/java/android/server/BluetoothEventLoop.java
/*package*/ void onPropertyChanged(String[] propValues) {
……
BluetoothAdapterProperties adapterProperties = mBluetoothService.getAdapterProperties();
……
else if (name.equals("Pairable") || name.equals("Discoverable")) {
adapterProperties.setProperty(name, propValues[1]);
if (name.equals("Discoverable")) {
mBluetoothState.sendMessage(BluetoothAdapterStateMachine.SCAN_MODE_CHANGED);
}
……
else if (name.equals("Powered")) {
mBluetoothState.sendMessage(BluetoothAdapterStateMachine.POWER_STATE_CHANGED,
propValues[1].equals("true") ? new Boolean(true) : new Boolean(false));
当有蓝牙AdapterProperies发生变化时,在BluetoothEventLoop.java中就会有个onProperyCha
nged方法来处理。首先通过BluetoothService的getAdapterProperties来获取蓝牙适配器的所有属性,都有哪些属性,在实际开发过程中我们通过调试可以看到按顺序依次是:power,
Pairable,class,device,UUID,Discoverable。通过将power的value设置为true,就会向蓝牙状态发送一个POWER_STATE_CHAGED,通过Discoverable的属性来向蓝牙状态机发送一个
SCAN_MODE_CHANGED的命令。
/framework/base/core/java/android/server/BluetoothAdapterStateMachine.java
case POWER_STATE_CHANGED:
removeMessages(POWER_DOWN_TIMEOUT);
if (!((Boolean) message.obj)) {
if (mPublicState == BluetoothAdapter.STATE_TURNING_OFF) {
transitionTo(mHotOff);
finishSwitchingOff();
if (!mContext.getResources().getBoolean
(com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {
deferMessage(obtainMessage(TURN_COLD));
}
}
} else {
if (mPublicState != BluetoothAdapter.STATE_TURNING_ON) {
if (mContext.getResources().getBoolean
(com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {
recoverStateMachine(TURN_HOT, null);
} else {
recoverStateMachine(TURN_COLD, null);
}
}
}
在上一个HotOff的时候,已经将蓝牙状态机切换到了Switching了。所以直接在Switching这个状态里面来处理命令。第一个power_state_changed的命令很简单。在蓝牙状态机里面有个叫mPublicState的全局变量来记录蓝牙适配器的状态。如果是power的值为true,那么就将这个变量的值变为STATE_TURNING_ON,否则就是STATE_TURNING_OFF。在前面介绍过了蓝牙适配器总共有四个状态:State_off(10),state_turning_on(11),state_on(12),state_turning_off(
13)。那么继续来看第二个命令,scan_mode_changed。
/framework/base/core/java/android/server/BluetoothAdapterStateMachine.java
case SCAN_MODE_CHANGED:
if (mPublicState == BluetoothAdapter.STATE_TURNING_ON) {
mBluetoothService.setPairable();
mBluetoothService.initBluetoothAfterTurningOn();
transitionTo(mBluetoothOn);
broadcastState(BluetoothAdapter.STATE_ON);
mBluetoothService.runBluetooth();
}
根据第一个命令,mPublicState的值是STATE_TURNING_ON,这里又要和BluetoothService来交互了,先调用了setPairable和initBluetoothAfterTurningOn,runBluetooth并将蓝牙状态机切换到BluetoothOn的状态。接下来到bluetoothService看这个setPairable方法。
/framework/base/core/java/android/server/BluetoothService.java
/*package*/ synchronized void setPairable() {
String pairableString = getProperty("Pairable", false);
if (pairableString == null) {
Log.e(TAG, "null pairableString");
return;
}
if (pairableString.equals("false")) {
setAdapterPropertyBooleanNative("Pairable", 1);
}
}
这个过程和上面的设置POWER的过程是类似的,先通过getPropery获取Pairable的状态,如果是false的话,就需要调用JNI的方法setAdapterPropertyBooleanNative来通过dbus来向bluez来设置蓝牙适配器的Pairable的值。如果设置成功的话,同样还会调用BluetoothEventLoop中的onProperyChanged方法。继续跟进代码initBluetoothAfterTurningOn:
framework/base/core/java/android/server/BluetoothService.java
/*package*/ void initBluetoothAfterTurningOn() {
String discoverable = getProperty("Discoverable", false);
String timeout = getProperty("DiscoverableTimeout", false);
if (discoverable.equals("true") && Integer.valueOf(timeout) != 0) {
setAdapterPropertyBooleanNative("Discoverable", 0);
}
mBondState.initBondState();
initProfileState();
getProfileProxy();
}
这个函数首先还是像设置power,Parirable的属性差不多,设置Discoverable的属性。当蓝牙模块打开和蓝牙适配器配对(Pairable)之后。剩下的initProfileState可以获取蓝牙的物理地址。
getProfileProxy直接调用了Adapter的getProfileProxy。得到俄ProfileProxy可以是HEADSET,
A2DP,INPUT_DEVICE,PAN,HEALTH。
framework/base/core/java/android/server/BluetoothService.java
/*package*/ void runBluetooth() {
……
autoConnect();
}
private void autoConnect() {
String[] bonds = getKnownDevices();
if (bonds == null) {
return;
}
……
for (String path : bonds) {
String address = getAddressFromObjectPath(path);
BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
if (state != null) {
Message msg = new Message();
msg.what = BluetoothDeviceProfileState.AUTO_CONNECT_PROFILES;
state.sendMessage(msg);
}
}
}
在autoConnect中,就会扫描附近的设备,并获取设备的地址和名字。这是我们看到就是能看到了一系列扫描出来的附近的设备。此时蓝牙的状态出于正常运行。到这里蓝牙模块就在
Android中工作起来了。
1.4 蓝牙开发在android中的调试
1.4.1 内核和驱动的支持
作为是linux内核的Android系统,必须在编译内核过程中将bluez编译的config选上。
CONFIG_BT =y
CONFIG_BT_RFCOMM =y
CONFIG_BT_BNEP = y
CONFIG_BT_CMTP =y
CONFIG_BT_L2CAP=y
CONFIG_BT_SCO=y
然后根据我们的驱动使用的接入方式,常见的有串口(uart),USB,SDIO总线等。如果我们的驱动能够正常工作工作的话,我们在linux的终端通过下面命令就可以看见hci设备了。
root@android:/ # hciconfig -a
hci0: Type: BR/EDR Bus: USB
BD Address: 74:2F:68:CE:13:57 ACL MTU: 1022:8 SCO MTU: 183:5
DOWN
RX bytes:505 acl:0 sco:0 events:22 errors:0
TX bytes:99 acl:0 sco:0 commands:22 errors:0
Features: 0xff 0xfe 0x0d 0xfe 0xd8 0x7f 0x7b 0x87
Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3
Link policy: RSWITCH HOLD SNIFF
Link mode: SLAVE ACCEPT
如果能够像上面可以看见蓝牙的类型,总线类型,物理地址等信息。说明蓝牙设备已经在内核中注册成功了,但能不能使用还要继续使用下面命令,我们注意到蓝牙模块状态时DOWN的。
root@android:/ # hciconfig hci0 up
root@android:/ # hciconfig -a
hci0: Type: BR/EDR Bus: USB
BD Address: 74:2F:68:CE:13:57 ACL MTU: 1022:8 SCO MTU: 183:5
UP RUNNING
RX bytes:994 acl:0 sco:0 events:42 errors:0
TX bytes:185 acl:0 sco:0 commands:42 errors:0
Features: 0xff 0xfe 0x0d 0xfe 0xd8 0x7f 0x7b 0x87
Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3
Link policy: RSWITCH HOLD SNIFF
Link mode: SLAVE ACCEPT
Name: 'Bluetooth USB Host Controller'
Class: 0x000000
Service Classes: Unspecified
Device Class: Miscellaneous,
HCI Version: 4.0 (0x6) Revision: 0x102
LMP Version: 4.0 (0x6) Subversion: 0x1
Manufacturer: Atheros Communications, Inc. (69)
通过hciconfig hci0 up命令让蓝牙模块的状态从DOWN变成UP状态。这个时候还不能就确定蓝牙驱动是能正常工作的。需要继续看看我们的蓝牙能不能扫描其他的蓝牙设备,如果能够扫描到其他的设备,就可以说明我们的蓝牙设备在内核态是可以正常工作的。
root@android:/ # hcitool scan
Scanning ...
00:1C:26:D5:3E:D6 DAWEIYAN-MOBL
00:1E:4C:F3:BC:FA ZHILONGX-MOBL
00:1F:3A:F1:94:CD JUELIUX-MOBL
00:27:13:D6:66:D9 QWANG29-MOBL2
50:63:13:C7:83:6D YANCHAOY-MOBL
00:1C:26:FD:11:3A ZWANG16X-MOBL
好了如果能够扫描出设备的物理地址和名字的话,那我们的设备在linux 内核态就ok了。
1.4.2 Android Boradconfig和服务的支持
在BoardConfig.mk中添加:BOARD_HAVE_BLUETOOTH := true。因为在framework中的代码很多函数是需要这个宏的,如果这个宏没有打开的话,很多代码是走不过的。在android的添加蓝牙工作必要的服务,Dbus-daemon,bluetoothd,
/init.rc
service dbus /system/bin/dbus-daemon --system --nofork
class main
socket dbus stream 660 bluetooth bluetooth
user bluetooth
group bluetooth net_bt_admin
service bluetoothd /system/bin/bluetoothd -n
class main
socket bluetooth stream 660 bluetooth bluetooth
socket dbus_bluetooth stream 660 bluetooth Bluetooth
service hciattach /system/bin/hciattach 当然这个服务只针对你的蓝牙接入方式是串口的,向我们这里是USB的话,这个服务还是可以省掉的。
service hfag /system/bin/sdptool add --channel=10 HFAG
user bluetooth
group bluetooth net_bt_admin
disabled
oneshot
service hsag /system/bin/sdptool add --channel=11 HSAG
user bluetooth
group bluetooth net_bt_admin
disabled
oneshot
service opush /system/bin/sdptool add --channel=12 OPUSH
user bluetooth
group bluetooth net_bt_admin
disabled
oneshot
service pbap /system/bin/sdptool add --channel=19 PBAP
user bluetooth
group bluetooth net_bt_admin
disabled
oneshot