轉自:http://www.cnblogs.com/chenbin7/archive/2012/09/05/2670652.html
第1章 Android藍牙系統
1.1 藍牙技術簡介
藍牙(Bleuetooth)原是十世紀統一了丹麥的一個國王的名字,現取其“統一”的含義,用來意在統一無線局域網通訊的標准的藍牙技術。藍牙技術是愛立信,IBM,Intel等世界5家著名大公司在1998年聯合推出的一項無線通訊規范。隨后成立的藍牙技術特殊興趣組織(SIG)來負責該技術的開發和技術協議的制定,如今全世界已有1800多家公司加盟該組織,最近微軟公司也正式加盟並成為SIG組織的領導成員之一。它以低成本的近距離無線連接為基礎,為移動通信設備建立一個短程無線連接。其實質內容是建立通用的無線電空中接口,使計算機和通信設備進一步結合,讓不同的廠家生產便攜式設備在沒有電纜或電線相互連接的情況下,能在近距離范圍內具有相互通信的一種技術。
1.2 藍牙技術的特點
- 全球范圍適用
藍牙技術使用無需經過許可的工業、科研和醫療(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。
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。