Android蓝牙系统

蓝牙技术的特点

  • 全球范围适用:

蓝牙技术使用无需经过许可的工业、科研和医疗(ISM)波段(2.4至2.485 GHz),使用展频、调频、全双工信号,标称速率为1600跳/秒。在大多数国家,无需经过许可便可使用2.4 GHz ISM波段。

  • 抗干扰:

蓝牙技术的适配跳频(AFH)能力的设计目的是为了减少共用2.4 GHz频谱的无线技术之间出现的干扰。该功能会在频谱中寻找并未被占用的频带以供蓝牙技术使用。AFH的工作原理是识别该频谱中的其他设备并避开这些设备所用的频带。跳频功能以1 MHz的频率在79个频段中进行切换,从而获得了较高的抗干扰能力,同时使该频谱中能够实现更加高效的传输。有了跳频功能,尽管其他技术与蓝牙技术同时使用,但蓝牙技术的用户仍能享有优质的性能表现。

  • 射程:

射程根据不同的具体应用而定,尽管核心规格规定了最低射程,但这并非限制,制造商仍可根据其具体用例调整射程应用。

根据具体应用中使用的射频种类,射程将有所不同:

第三类射频 – 射程最高1米或3英尺

第二类射频 – 最常见于移动设备,射程为10米或33英尺

第一类射频 – 主要用于工业用例,射程为100米或300英尺

最常用的射频为第二类,其能耗为2.5 mW。蓝牙技术的设计能耗非常之低。此外,规格允许射频处于非活跃状态时可以断电则进一步降低了能耗。3.0版HS中的通用替代MAC/PHY能够发现高速设备的AMP,并仅在需要进行数据传输时开启射频,实现了节能优势,同时增强了射频的安全性。对于无需高速数据传输率但需要最大限度延长电池寿命的设备而言,蓝牙低耗能技术为其实现了优化效果,其耗电量仅为传统蓝牙技术的1/2至1/100。

蓝牙技术在android中的应用

在前面章节android启动过程中介绍到android服务的启动,init进程中,启动Zygote后,然后由SystemServer启动一系列服务,蓝牙服务就是在这个时候启动的。详细见代码:
[html]  view plain copy
  1.  /framework/base/services/java/com/android/server/SystemServer.java  
  2.   
  3. if (SystemProperties.get("ro.kernel.qemu").equals("1")) {  
  4.   
  5. } else {  
  6.   
  7.   bluetooth = new BluetoothService(context);  
  8.   ……  
  9.   bluetoothA2dp = new BluetoothA2dpService(context, bluetooth);  
  10.   ……  
  11. if (airplaneModeOn == 0 && bluetoothOn != 0) {  
  12.   bluetooth.enable();  
  13.  }    

Bluetooth服务的代码首先通过SystemProperties的get方法来判断系统是不是使用模拟器内核,如果是使用模拟器内核来启动android的系统,那么就会跳过蓝牙服务的启动,也就是说Android 4.0模拟器是不支持蓝牙系统的。否则就是一个实在的设备产品(ro.kernel.qemu=0)。就是构造一个bluetooth的服务(BluetoothService)和一个蓝牙耳机服务(BluetoothA2dpService)。

代码段最后一部分是判断开机是否要启用蓝牙,通过函数我们可以看到如果设备的飞行模式是关闭的并且bluetooth的那个开关是在on。就是调用bluetoothService的enable方法使得我们设备开机的时候就将蓝牙开启。飞行模式就是那些使用无线频谱的模块都必须关掉,譬如:wifi,Bluetooth,GPS等。接下来就是BluetoothService的enable方法了。


[html]  view plain copy
  1. framework/base/core/java/android/server/BluetoothService.java  
  2.   
  3. public synchronized boolean enable(boolean saveSetting) {  
  4.   
  5. mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,"Need BLUETOOTH_ADMIN permission");  
  6.   
  7. if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {  
  8.  return false;  
  9.  }     
  10.  mBluetoothState.sendMessage(BluetoothAdapterStateMachine.USER_TURN_ON, saveSetting);  
  11.  return true;  
  12.  }     

蓝牙服务的enable的方法会先判断进程有没有操作权限,需要蓝牙管理的权限才能去enable蓝牙模块,然后还会再次判断系统的飞行模式有没有打开,如果此时飞行模式是on的话,那么会返回,还是不能打开服务。在确保拥有权限并且不是出于飞行模式的情况下,就会往蓝牙状态机发送一个USER_TURN_ON的命令。下面介绍一下android中的蓝牙状态机。

  • Poweroff

这就是蓝牙模块没有初始化的状态,这时候硬件模块是出于没有上电的状态。

  • Warmup

这个状态就是给设备上电,使设备能够从没电到待机状态。

  • Hotoff

Hotoff我个人理解就是在模块上电了,出于一种待命的状态,如果收到了turn_on_coninue的命令时候就会去将蓝牙模块切换到工作状态。如果接收到了turn_cold的命令时候,设备就会断电进入poweroff状态。

  • Switching

这也是一个中间状态,需要继续接收命令。

  • Bluetoothon

这时蓝牙模块出于正常工作的状态。


根据android中蓝牙状态的源码中,具体的各个状态机相互转换图如下:

[html]  view plain copy
  1.  /framework/base/core/java/android/server/BluetoothAdapterStateMachine.java  
  2.   
  3. private class PowerOff extends State {  
  4.   
  5. public void enter() {  
  6.  if (DBG) log("Enter PowerOff: " + getCurrentMessage().what);  
  7.    }   
  8. ……  
  9. case USER_TURN_ON:  
  10.  broadcastState(BluetoothAdapter.STATE_TURNING_ON);  
  11.  transitionTo(mWarmUp);  
  12. …  
  13. if (prepareBluetooth()) {  
  14. if ((Boolean) message.obj) {  
  15.    persistSwitchSetting(true);  
  16. }  
  17. 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方法。代码如下:


[html]  view plain copy
  1.  /framework/base/core/java/android/server/BluetoothAdapterStateMachine.java  
  2.   
  3. private boolean prepareBluetooth() {  
  4.   if (mBluetoothService.enableNative() != 0) {  
  5.           return false;  
  6.    }  
  7. ……  
  8. int retryCount = 2;  
  9. boolean eventLoopStarted = false;  
  10.   
  11. while ((retryCount-- > 0) && !eventLoopStarted) {  
  12.  mEventLoop.start();  
  13.  while ((pollCount-- > 0) && !eventLoopStarted) {  
  14.          if (mEventLoop.isEventLoopRunning()) {  
  15.                  eventLoopStarted = true;  
  16.                  break;  
  17.       }  

在preprareBluetooth方法中,首先就是调用了BluetoothService的enableNative()的方法,只要一看到这种带Native的方法,JNI的代码是少不了的。由于enableNative方法走的路有点多,所以先直接到BluetoothService的代码中寻找enableNative()看个究竟。
[html]  view plain copy
  1.  framework/base/core/java/android/server/BluetoothService.java  
  2.   
  3. /*package*/ native int enableNative();  
  4.   
  5. Framework/base/core/jni/ android_server_BluetoothService.cpp  
  6.   
  7.    
  8.   
  9. static JNINativeMethod sMethods[] = {  
  10.   
  11. ……  
  12.   
  13. {"enableNative", "()I", (void *)enableNative},  
  14.   
  15. ……  
  16.   
  17. }  
  18.   
  19. static jint enableNative(JNIEnv *env, jobject object) {  
  20.   
  21. return bt_enable();  
  22.   
  23. }  

从上面的代码可以看出,BluetoothService的enableNative就是直接调用了JNI的代码,JNI是java native interface的 缩写,中文叫java本地接口。Android上层跑的java代码,而底层代码都是c语言。以android的一贯作风是通过JNI代码调用HAL层,然后就可以直接调用驱动代码或者经由内核达到操作驱动代码。enableNative的代码很简单,就是调用了bt_enable。我们可以继续找到这个函数的实现。

[html]  view plain copy
  1. System/bluetooth/bluedroid/bluetooth.c  
  2.   
  3. int bt_enable() {  
  4.     LOGV(__FUNCTION__);  
  5.   
  6.     int ret = -1;  
  7.     int hci_sock = -1;  
  8.     int attempt;  
  9.   
  10.     if (set_bluetooth_power(1) < 0) goto out;  
  11.   
  12.     LOGI("Starting hciattach daemon");  
  13.     if (property_set("ctl.start", "hciattach") < 0) {  
  14.         LOGE("Failed to start hciattach");  
  15.         goto out;  
  16.     }  
  17.   
  18.     // Try for 10 seconds, this can only succeed once hciattach has sent the  
  19.     // firmware and then turned on hci device via HCIUARTSETPROTO ioctl  
  20.     for (attempt = 1000; attempt > 0;  attempt--) {  
  21.         hci_sock = create_hci_sock();  
  22.         if (hci_sock < 0) goto out;  
  23.   
  24.         if (!ioctl(hci_sock, HCIDEVUP, HCI_DEV_ID)) {  
  25.             break;  
  26.         }  
  27.         close(hci_sock);  
  28.         usleep(10000);  // 10 ms retry delay  
  29.     }  
  30.     if (attempt == 0) {  
  31.         LOGE("%s: Timeout waiting for HCI device to come up", __FUNCTION__);  
  32.         goto out;  
  33.     }  
  34.   
  35.     LOGI("Starting bluetoothd deamon");  
  36.     if (property_set("ctl.start", "bluetoothd") < 0) {  
  37.         LOGE("Failed to start bluetoothd");  
  38.         goto out;  
  39.     }  
  40.     sleep(HCID_START_DELAY_SEC);  
  41.   
  42.     ret = 0;  
  43.   
  44. out:  
  45.     if(ret) set_bluetooth_power(0);  
  46.     if (hci_sock >= 0) close(hci_sock);  
  47.     return ret;  
  48. }  

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了。

[html]  view plain copy
  1.  Kernel/net/bluetooth/hci_sock.c  
  2. static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg){  
  3. …  
  4. case HCIDEVUP:  
  5.                 if (!capable(CAP_NET_ADMIN))  
  6.                         return -EACCES;  
  7.                 return hci_dev_open(arg);  
  8. ……  
  9. }  

从上面的ioctl下来的代码可以看出,当函数第二个参数cmd为HCIDEVUP时,就会直接调用hci_dev_open(arg)方法。这个函数就好比我们在终端下面使用蓝牙调试工具hciconfig,执行了 #hciconfig hci0 up。

[html]  view plain copy
  1. Kernel/net/bluetooth/hci_core.c  
  2. int hci_dev_open(__u16 dev)  
  3. {  
  4. ……  
  5. hdev = hci_dev_get(dev);  
  6. ……  
  7. if (hdev->open(hdev)) {  
  8.                 ret = -EIO;  
  9.                 goto done;  
  10.   }  
  11. }  
在kernel的bluez调用hci_dev_open,而在这个函数中又hdev->open(hdev),这个就是我们驱动注册时候的回调函数open。由于我们平台使用的是usb的蓝牙接入方式,我就以usb的蓝牙驱动为例,看看驱动的open函数。
[html]  view plain copy
  1. /* ------- Interface to HCI layer ------ */  
  2. /* Initialize device */  
  3. static int hci_uart_open(struct hci_dev *hdev)  
  4. {  
  5.         BT_DBG("%s %p", hdev->name, hdev);  
  6.         /* Nothing to do for UART driver */  
  7.         set_bit(HCI_RUNNING, &hdev->flags);  
  8.         return 0;  
  9. }  
或者
[html]  view plain copy
  1.  Kernel/driver/bluetooth/btusb.c  
  2.   
  3. static int btusb_open(struct hci_dev *hdev){  
  4. ……  
  5. err = btusb_submit_intr_urb(hdev, GFP_KERNEL);  
  6. ……  
  7. err = btusb_submit_bulk_urb(hdev, GFP_KERNEL);  
  8. ……  
  9. }  
USB hci设备打开后,首先将设备的interface配置为HCI_RUNNING状态,然后为数据传输初始化设备的端点和管道,初始化和填充urb。代码到这,蓝牙设备就算是真正打开了。
回到之前的蓝牙状态机的代码:
[html]  view plain copy
  1.  /framework/base/core/java/android/server/BluetoothAdapterStateMachine.java  
  2.   
  3. private boolean prepareBluetooth() {  
  4.   if (mBluetoothService.enableNative() != 0) {  
  5.           return false;  
  6.    }  
  7. ……  
  8. int retryCount = 2;  
  9. boolean eventLoopStarted = false;  
  10. while ((retryCount-- > 0) && !eventLoopStarted) {  
  11. mEventLoop.start();  
  12. while ((pollCount-- > 0) && !eventLoopStarted) {  
  13.          if (mEventLoop.isEventLoopRunning()) {  
  14.                  eventLoopStarted = true;  
  15.                  break;  
  16.       }  

mBluetoothService在enableNative()函数主要功能就是通过一系列代码来打开蓝牙设备。如果设备驱动代码没有问题的话,我们enableNative()返回的将会是true。在实际调试蓝牙设备时候,我们可以通过在linux或者android的终端下面使用自带的工具命令(hciconfig),执行:# hciconfig –a,如果驱动能够和设备绑定的话,我们就会看到蓝牙设备的一些比较重要信息,如:蓝牙的物理地址,总线类型,协议类型等。

上面的代码接下来会是一个while循环,执行2次。mEventLoop.start()。也就是说调用了EventLoop的start方法。

[html]  view plain copy
  1.  /framework/base/core/java/android/server/BluetoothEventLoop.java  
  2. /* package */ void start() {  
  3.    if (!isEventLoopRunningNative()) {  
  4.             if (DBG) log("Starting Event Loop thread");  
  5.             startEventLoopNative();  
  6.         }     
  7. }  
第一次进入这个函数isEventLoopRunningNative肯定是返回false的,所以直接进入了startEventLoopNative(),前面说过了一般带native的函数结尾的函数都是JNI。看到这里又要进JNI了。
[html]  view plain copy
  1.  Framework/base/core/jni/android_server_BluetoothEventLoop.cpp  
  2. static JNINativeMethod sMethods[] = {  
  3. ……  
  4. {"startEventLoopNative", "()V", (void *)startEventLoopNative},  
  5. ……  
  6. }  
  7. static jboolean <strong>startEventLoopNative</strong>(JNIEnv *env, jobject object) {  
  8. ……  
  9. nat->pollData = (struct pollfd *)malloc(sizeof(struct pollfd) * DEFAULT_INITIAL_POLLFD_COUNT);  
  10. ……  
  11. nat->watchData = (DBusWatch **)malloc(sizeof(DBusWatch *) * DEFAULT_INITIAL_POLLFD_COUNT);  
  12. ……  
  13. if (socketpair(AF_LOCAL, SOCK_STREAM, 0, &(nat->controlFdR))) {  
  14.         LOGE("Error getting BT control socket");  
  15.         goto done;  
  16. }  
  17. ……  
  18. if (setUpEventLoop(nat) != JNI_TRUE) {  
  19.         LOGE("failure setting up Event Loop!");  
  20.         goto done;  
  21.  }  
  22. pthread_create(&(nat->thread), NULL, eventLoopMain, nat);  
  23. ……  
  24. }  
为socket文件描述符分配内存数据,同时为DBusWatch结构体分配内存,socketpair创建了一对套接字(AF_LOCAL域中使用),这个描述符可以是单双工也可以是全双工的,这里是单双工的,也就是只能从这个描述符中读取数据,而不能写数据。如果socketpair的第四个参数是个数组,也可以实现一个描述符读,另外一个描述符写。从而实现全双工。然后就是setUpEventLoop函数,最后就是 创建了eventLoopMain的线程
[html]  view plain copy
  1.  Framework/base/core/jni/android_server_BluetoothEventLoop.cpp  
  2. static jboolean setUpEventLoop(native_data_t *nat) {  
  3. ……  
  4. dbus_threads_init_default();  
  5. ……  
  6. dbus_error_init(&err);  
  7. ……  
  8. if (!dbus_connection_add_filter(nat->conn, event_filter, nat, NULL)){  
  9.             return JNI_FALSE;  
  10.         }  
  11. ……  
  12. dbus_bus_add_match(nat->conn, "type='signal',interface='<strong>org.freedesktop.DBus</strong>'",   &err);  
  13. dbus_bus_add_match(nat->conn, "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Adapter'",&err);  
  14. ……  
  15. }  

这里是初始化dbus,是bluez能够挂接上dbus。建立一个dbus连接之后,为这个dbus连接起名,为我们将要进行的消息循环添加匹配条件(就是通过信号名和信号接口名来进行匹配控制的) -- dbus_bus_add_match()。我们进入等待循环后,只需要对信号名,信号接口名进行判断就可以分别处理各种信号了。在各个处理分支上。我们可以分离出消息中的参数。对参数类型进行判断和其他的处理。具体对dbus感兴趣的话可以参照:http://dbus.freedesktop.org

[html]  view plain copy
  1.  Framework/base/core/jni/android_server_BluetoothEventLoop.cpp  
  2.   
  3. static void *eventLoopMain(void *ptr) {  
  4.   
  5. ……  
  6.   
  7. while (1) {  
  8. ……  
  9. if (nat->pollData[i].fd == nat->controlFdR) {  
  10.   
  11. while (recv(nat->controlFdR, &data, sizeof(char), MSG_DONTWAIT) != -1) {  
  12. ……  
  13. switch (data) {  
  14. case EVENT_LOOP_EXIT:  
  15.    dbus_connection_set_watch_functions(nat->conn,NULL, NULL, NULL, NULL, NULL);  
  16.    tearDownEventLoop(nat);  
  17.    nat->vm->DetachCurrentThread();  
  18. ……  
  19. case EVENT_LOOP_ADD:  
  20.     {  
  21.      handleWatchAdd(nat);  
  22.      break;  
  23.      }  
  24.   
  25. case EVENT_LOOP_REMOVE:  
  26.      {  
  27.       handleWatchRemove(nat);  
  28.       break;  
  29.       }  

以轮训的方式从socket的描述符中不断的接收数据,如果有数据到来,就根据数据的类型做相应的处理。到这里dbus就和bluez建立连接。还是回到之前我们prepareBluetooth的函数。
[html]  view plain copy
  1.  /framework/base/core/java/android/server/BluetoothAdapterStateMachine.java  
  2.   
  3. private class PowerOff extends State {  
  4. public void enter() {  
  5.    if (DBG) log("Enter PowerOff: " + getCurrentMessage().what);  
  6.    }   
  7. ……  
  8. case USER_TURN_ON:  
  9.   broadcastState(BluetoothAdapter.STATE_TURNING_ON);  
  10.   transitionTo(mWarmUp);  
  11.   ……  
  12. if (prepareBluetooth()) {  
  13. if ((Boolean) message.obj) {  
  14.    persistSwitchSetting(true);  
  15. }  
  16.   
  17. deferMessage(obtainMessage(TURN_ON_CONTINUE));  

前面的代码我们分析完了prepareBluetooth(),如果没有问题就进入了persistSwitchSetting()。然后就是讲蓝牙状态机切换到mWarnUp状态。并向蓝牙状态机发送了一个TURN_ON_CONTINUE的命令。

[cpp]  view plain copy
  1.  /framework/base/core/java/android/server/BluetoothAdapterStateMachine.java  
  2. private class WarmUp extends State {  
  3. ……  
  4. public boolean processMessage(Message message) {  
  5.    log("WarmUp process message: " + message.what);  
  6. ……  
  7. switch(message.what) {  
  8. case TURN_ON_CONTINUE:  
在HotOff状态机中,接收到TURN_ON_CONTINUE命令后,先调用了BluetoothService的switchConnectable(true);然后将蓝牙的状态机切换到Switching状态。
[cpp]  view plain copy
  1. /framework/base/core/java/android/server/BluetoothService.java  
  2. *package*/ synchronized void switchConnectable(boolean on) {  
  3.        setAdapterPropertyBooleanNative("Powered", on ? 1 : 0);  
  4.    }  
又到了以Native结尾的函数,还是到JNI里面找到它的实现吧:
[cpp]  view plain copy
  1.  Framework/base/core/jni/android_server_BluetoothService.cpp  
  2. static jboolean setAdapterPropertyNative(JNIEnv *env, jobject object, jstring key,  
  3.                                          void *value, jint type) {  
  4. ……  
  5. msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,get_adapter_path(env, object),  
  6. DBUS_ADAPTER_IFACE, "SetProperty");  
  7. ……  
  8. dbus_message_append_args(msg, DBUS_TYPE_STRING, &c_key, DBUS_TYPE_INVALID);  
  9. dbus_message_iter_init_append(msg, &iter);  
  10. ……  
  11. reply = dbus_connection_send_with_reply(nat->conn, msg, NULL, -1);  
  12. ……  
  13. }  
通过Dbus向bluez发送SetPropery的信息(message),当成功的时候,我们在另外一端就会收到powerChanged的信号。具体处理如下的代码:
[cpp]  view plain copy
  1.  Framework/base/core/java/android/server/BluetoothEventLoop.java  
  2. /*package*/ void onPropertyChanged(String[] propValues) {  
  3. ……  
  4. BluetoothAdapterProperties adapterProperties = mBluetoothService.getAdapterProperties();  
  5. ……  
  6. else if (name.equals("Pairable") || name.equals("Discoverable")) {  
  7.  adapterProperties.setProperty(name, propValues[1]);  
  8. if (name.equals("Discoverable")) {  
  9.           mBluetoothState.sendMessage(BluetoothAdapterStateMachine.SCAN_MODE_CHANGED);  
  10.             }   
  11. ……  
  12. else if (name.equals("Powered")) {  
  13.           mBluetoothState.sendMessage(BluetoothAdapterStateMachine.POWER_STATE_CHANGED,  
  14.                 propValues[1].equals("true") ? new Boolean(true) : new Boolean(false));  

当有蓝牙AdapterProperies发生变化时,在BluetoothEventLoop.java中就会有个onProperyChanged方法来处理。首先通过BluetoothService的getAdapterProperties来获取蓝牙适配器的所有属性,都有哪些属性,在实际开发过程中我们通过调试可以看到按顺序依次是:power,Pairable,class,device,UUID,Discoverable。通过将power的value设置为true,就会向蓝牙状态发送一个POWER_STATE_CHAGED,通过Discoverable的属性来向蓝牙状态机发送一个SCAN_MODE_CHANGED的命令。

[cpp]  view plain copy
  1. /framework/base/core/java/android/server/BluetoothAdapterStateMachine.java  
  2. ase POWER_STATE_CHANGED:  
  3.               removeMessages(POWER_DOWN_TIMEOUT);  
  4.                    if (!((Boolean) message.obj)) {  
  5.                        if (mPublicState == BluetoothAdapter.STATE_TURNING_OFF) {  
  6.                            transitionTo(mHotOff);  
  7.                            finishSwitchingOff();  
  8.                            if (!mContext.getResources().getBoolean  
  9.                            (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {  
  10.                                deferMessage(obtainMessage(TURN_COLD));  
  11.                           }    
  12.                        }    
  13.                    } else {  
  14.                        if (mPublicState != BluetoothAdapter.STATE_TURNING_ON) {  
  15.                            if (mContext.getResources().getBoolean  
  16.                            (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {  
  17.                                recoverStateMachine(TURN_HOT, null);  
  18.                            } else {  
  19.                                recoverStateMachine(TURN_COLD, null);  
  20.                            }    
  21.                        }    
  22.                    }    

在上一个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。

[cpp]  view plain copy
  1.  /framework/base/core/java/android/server/BluetoothAdapterStateMachine.java  
  2. case SCAN_MODE_CHANGED:  
  3.      if (mPublicState == BluetoothAdapter.STATE_TURNING_ON) {  
  4.         mBluetoothService.setPairable();  
  5.         mBluetoothService.initBluetoothAfterTurningOn();  
  6.         transitionTo(mBluetoothOn);  
  7.         broadcastState(BluetoothAdapter.STATE_ON);  
  8.         mBluetoothService.runBluetooth();  
  9. }  
根据第一个命令,mPublicState的值是STATE_TURNING_ON,这里又要和BluetoothService来交互了,先调用了setPairable和initBluetoothAfterTurningOn,runBluetooth并将蓝牙状态机切换到BluetoothOn的状态。接下来到bluetoothService看这个setPairable方法。
[cpp]  view plain copy
  1. framework/base/core/java/android/server/BluetoothService.java  
  2. /*package*/ synchronized void setPairable() {  
  3.         String pairableString = getProperty("Pairable"false);  
  4.         if (pairableString == null) {  
  5.             Log.e(TAG, "null pairableString");  
  6.             return;  
  7.         }     
  8.         if (pairableString.equals("false")) {  
  9.             setAdapterPropertyBooleanNative("Pairable", 1);  
  10.         }     
  11.     }     
这个过程和上面的设置POWER的过程是类似的,先通过getPropery获取Pairable的状态,如果是false的话,就需要调用JNI的方法setAdapterPropertyBooleanNative来通过dbus来向bluez来设置蓝牙适配器的Pairable的值。如果设置成功的话,同样还会调用BluetoothEventLoop中的onProperyChanged方法。继续跟进代码initBluetoothAfterTurningOn:
[cpp]  view plain copy
  1.  framework/base/core/java/android/server/BluetoothService.java  
  2. /*package*/ void initBluetoothAfterTurningOn() {  
  3.         String discoverable = getProperty("Discoverable"false);  
  4.         String timeout = getProperty("DiscoverableTimeout"false);  
  5.         if (discoverable.equals("true") && Integer.valueOf(timeout) != 0) {  
  6.             setAdapterPropertyBooleanNative("Discoverable", 0);  
  7.         }  
  8.         mBondState.initBondState();  
  9.         initProfileState();  
  10.         getProfileProxy();  
  11. }  

这个函数首先还是像设置power,Parirable的属性差不多,设置Discoverable的属性。当蓝牙模块打开和蓝牙适配器配对(Pairable)之后。剩下的initProfileState可以获取蓝牙的物理地址。getProfileProxy直接调用了Adapter的getProfileProxy。得到的ProfileProxy可以是HEADSET,A2DP,INPUT_DEVICE,PAN,HEALTH。

[cpp]  view plain copy
  1.  framework/base/core/java/android/server/BluetoothService.java  
  2. /*package*/ void runBluetooth() {  
  3. ……  
  4. autoConnect();  
  5. }  
  6. private void autoConnect() {  
  7. String[] bonds = getKnownDevices();  
  8.         if (bonds == null) {  
  9.             return;  
  10.         }  
  11. ……  
  12. for (String path : bonds) {  
  13.             String address = getAddressFromObjectPath(path);  
  14.             BluetoothDeviceProfileState state = mDeviceProfileState.get(address);  
  15.             if (state != null) {  
  16.                 Message msg = new Message();  
  17.                 msg.what = BluetoothDeviceProfileState.AUTO_CONNECT_PROFILES;  
  18.                 state.sendMessage(msg);  
  19.             }  
  20.         }  
  21.     }  

在autoConnect中,就会扫描附近的设备,并获取设备的地址和名字。这是我们看到就是能看到了一系列扫描出来的附近的设备。此时蓝牙的状态出于正常运行。到这里蓝牙模块就在Android中工作起来了。

转载于:https://my.oschina.net/u/994235/blog/112942

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值