(轉)android 4.0 藍牙服務開啓流程分析

轉自:http://www.cnblogs.com/chenbin7/archive/2012/09/05/2670652.htmlhtml

第1章 Android藍牙系統

1.1 藍牙技術簡介

藍牙(Bleuetooth)原是十世紀統一了丹麥的一個國王的名字,現取其「統一」的含義,用來意在統一無線局域網通信的標準的藍牙技術。藍牙技術是愛立信,IBM,Intel等世界5家著名大公司在1998年聯合推出的一項無線通信規範。隨後成立的藍牙技術特殊興趣組織(SIG)來負責該技術的開發和技術協議的制定,現在全世界已有1800多家公司加盟該組織,最近微軟公司也正式加盟併成爲SIG組織的領導成員之一。它以低成本的近距離無線鏈接爲基礎,爲移動通訊設備創建一個短程無線鏈接。其實質內容是創建通用的無線電空中接口,使計算機和通訊設備進一步結合,讓不一樣的廠家生產便攜式設備在沒有電纜或電線相互鏈接的狀況下,能在近距離範圍內具備相互通訊的一種技術。java

1.2 藍牙技術的特色

  • 全球範圍適用

藍牙技術使用無需通過許可的工業、科研和醫療(ISM)波段(2.4至2.485 GHz),使用展頻、調頻、全雙工信號,標稱速率爲1600跳/秒。在大多數國家,無需通過許可即可使用2.4 GHz ISM波段。linux

  • 抗干擾

藍牙技術的適配跳頻(AFH)能力的設計目的是爲了減小共用2.4 GHz頻譜的無線技術之間出現的干擾。該功能會在頻譜中尋找並沒有被佔用的頻帶以供藍牙技術使用。AFH的工做原理是識別該頻譜中的其餘設備並避開這些設備所用的頻帶。跳頻功能以1 MHz的頻率在79個頻段中進行切換,從而得到了較高的抗干擾能力,同時使該頻譜中可以實現更加高效的傳輸。有了跳頻功能,儘管其餘技術與藍牙技術同時使用,但藍牙技術的用戶仍能享有優質的性能表現。android

  • 射程

射程根據不一樣的具體應用而定,儘管核心規格規定了最低射程,但這並不是限制,製造商仍可根據其具體用例調整射程應用。數組

根據具體應用中使用的射頻種類,射程將有所不一樣:安全

第三類射頻 – 射程最高1米或3英尺app

第二類射頻 – 最多見於移動設備,射程爲10米或33英尺socket

第一類射頻 – 主要用於工業用例,射程爲100米或300英尺函數

  • 低功耗

最經常使用的射頻爲第二類,其能耗爲2.5 mW。藍牙技術的設計能耗很是之低。此外,規格容許射頻處於非活躍狀態時能夠斷電則進一步下降了能耗。3.0版HS中的通用替代MAC/PHY可以發現高速設備的AMP,並僅在須要進行數據傳輸時開啓射頻,實現了節能優點,同時加強了射頻的安全性。對於無需高速數據傳輸率但須要最大限度延長電池壽命的設備而言,藍牙低耗能技術爲其實現了優化效果,其耗電量僅爲傳統藍牙技術的1/2至1/100。工具

 

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

好了,到這裏藍牙應該就能正常工做了。上面的代碼都是基於android 4.0。

continue my dream...
相關文章
相關標籤/搜索