android -- 藍牙 bluetooth (五)接電話與聽音樂

1.藍牙耳機接聽電話

        這個就對應HFP(Hands-freeProfile),Free your Hand,藍牙的初衷之一。先來看這個功能的場景,手機來電,手機與藍牙耳機已鏈接,這時會優先觸發藍牙接聽電話的代碼流程,起步代碼在phone\src\com\android\phone\nCallScreen.java的connectBluetoothAudio() /disconnectBluetoothAudio(),只看鏈接部分好了,注意下面代碼裏的註釋,html

[java] view plaincopyjava

  1. /* package */ void connectBluetoothAudio() {  
  2.   if (VDBG) log("connectBluetoothAudio()...");  
  3.   if (mBluetoothHeadset != null) {  
  4.       // TODO(BT) check return  
  5.       mBluetoothHeadset.connectAudio();  
  6.   }  
  7.   // Watch out: The bluetooth connection doesn't happen instantly;  
  8.   // the connectAudio() call returns instantly but does its real  
  9.   // work in another thread.  The mBluetoothConnectionPending flag  
  10.   // is just a little trickery to ensure that the onscreen UI updates  
  11.   // instantly. (See isBluetoothAudioConnectedOrPending() above.)  
  12.   mBluetoothConnectionPending = true;  
  13.   mBluetoothConnectionRequestTime = SystemClock.elapsedRealtime();  

         接下來就跳到藍牙應用的管轄範圍,代碼在packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetService.java,[java] view plaincopyandroid

  1. public boolean connectAudio() {  
  2.     HeadsetService service = getService();  
  3.     if (service == null) return false;  
  4.     return service.connectAudio();  
  5. }  

        很明顯下一個目標是HeadsetService,直接看具體實現,這部分代碼跳轉都比較清晰,下面代碼會先判斷當前狀態是否正確,關於HeadsetStateMachine幾個狀態能夠參持這個/packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetStateMachine.java的最前的代碼註釋。[java] view plaincopyapp

  1. boolean connectAudio() {  
  2.      // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission  
  3.      enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");  
  4.      if (!mStateMachine.isConnected()) {  
  5.          return false;  
  6.      }  
  7.      if (mStateMachine.isAudioOn()) {  
  8.          return false;  
  9.      }  
  10.      mStateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO);  
  11.      return true;  
  12.  }  

       走進HeadsetStateMachine狀態機,找到CONNECT_AUDIO分支,就看帶Native的方法connectAudioNative(getByteAddress(mCurrentDevice));[java] view plaincopyspa

  1. static jboolean connectAudioNative(JNIEnv *env, jobject object, jbyteArray address) {  
  2.     jbyte *addr;  
  3.     bt_status_t status;  
  4.   
  5.     if (!sBluetoothHfpInterface) return JNI_FALSE;  
  6.   
  7.     addr = env->GetByteArrayElements(address, NULL);  
  8.     if (!addr) {  
  9.         jniThrowIOException(env, EINVAL);  
  10.         return JNI_FALSE;  
  11.     }  
  12. //鏈接在這裏  
  13.     if ( (status = sBluetoothHfpInterface->connect_audio((bt_bdaddr_t *)addr)) !=    
  14.          BT_STATUS_SUCCESS) {  
  15.         ALOGE("Failed HF audio connection, status: %d", status);  
  16.     }  
  17.     env->ReleaseByteArrayElements(address, addr, 0);  
  18.     return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;  
  19. }  

       上面代碼還能夠進一步跟到下面/external/bluetooth/bluedroid/btif/src/btif_hf.c,到了這裏其實流程已經結束了,對於這裏消息流轉估計要放到之後再寫了[java] view plaincopy.net

  1. static bt_status_t connect_audio( bt_bdaddr_t *bd_addr )  
  2. {  
  3.     CHECK_BTHF_INIT();  
  4.     if (is_connected(bd_addr))  
  5.     {  
  6.         BTA_AgAudioOpen(btif_hf_cb.handle);  
  7.         /* Inform the application that the audio connection has been initiated successfully */  
  8.         btif_transfer_context(btif_in_hf_generic_evt, BTIF_HFP_CB_AUDIO_CONNECTING,  
  9.                               (char *)bd_addr, sizeof(bt_bdaddr_t), NULL);  
  10.         return BT_STATUS_SUCCESS;  
  11.     }  
  12.     return BT_STATUS_FAIL;  
  13. }  

 

 2.在藍牙列表中鏈接藍牙耳機

     A2dp的鏈接過程,在藍牙搜索結果列表鏈接一個藍牙耳機,既然是從設備列表開始,因此起步代碼天然是這個了orm

[java] view plaincopyhtm

  1. DevicePickerFragment.java (settings\src\com\android\settings\bluetooth)     3884     2013-6-26  
  2.     void onClicked() {  
  3.       int bondState = mCachedDevice.getBondState();  
  4.       if (mCachedDevice.isConnected()) {  
  5.           askDisconnect();  
  6.       } else if (bondState == BluetoothDevice.BOND_BONDED) {  
  7.           mCachedDevice.connect(true);  
  8.       } .......  
  9.   }  
  10.   
  11.     void connect(boolean connectAllProfiles) {  
  12.       if (!ensurePaired()) {  //要先確保配對  
  13.           return;  
  14.       }  
  15.       mConnectAttempted = SystemClock.elapsedRealtime();  
  16.       connectWithoutResettingTimer(connectAllProfiles);//沒別的了,只能看到這裏  
  17.   }  

      代碼路徑這裏packages/apps/Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java,具體代碼看下面[java] view plaincopyblog

  1. // Try to initialize the profiles if they were not.  
  2.      ...........  
  3.       // Reset the only-show-one-error-dialog tracking variable  
  4.       mIsConnectingErrorPossible = true;  
  5.   
  6.       int preferredProfiles = 0;  
  7.       for (LocalBluetoothProfile profile : mProfiles) {  
  8.           if (connectAllProfiles ? profile.isConnectable() : profile.isAutoConnectable()) {  
  9.               if (profile.isPreferred(mDevice)) {  
  10.                   ++preferredProfiles;  
  11.                   connectInt(profile);//鏈接在這裏,  
  12.               }  
  13.           }  
  14.       }  
  15.      .............  

       connectInt的實現很簡單,直接跳過看裏面的profile.connect(mDevice),這裏的profile是指A2dpProfile,因此connet()方法的具體實如今[java] view plaincopyget

  1. public boolean connect(BluetoothDevice device) {  
  2.     if (mService == null) return false;  
  3.     List<BluetoothDevice> sinks = getConnectedDevices();  
  4.     if (sinks != null) {  
  5.         for (BluetoothDevice sink : sinks) {  
  6.             mService.disconnect(sink);  
  7.     }}  
  8.     return mService.connect(device);  
  9. }  

        下面是 BluetoothA2dp.java (frameworks\base\core\java\android\bluetooth)  ,爲何是這樣看下這個private BluetoothA2dp mService;就知道了[java] view plaincopy

  1. public boolean connect(BluetoothDevice device) {  
  2.       if (mService != null && isEnabled() &&  
  3.           isValidDevice(device)) {  
  4.           try {  
  5.               return mService.connect(device);  
  6.           } catch (RemoteException e) {  
  7.               Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));  
  8.               return false;  
  9.           }  
  10.       }...........  
  11.       return false;  
  12.        
  13.       Binder跳轉  
  14.       public boolean connect(BluetoothDevice device) {  
  15.           A2dpService service = getService();  
  16.           if (service == null) return false;  
  17.           return service.connect(device);  
  18.       }  
  19.        
  20.   }  

        以後的跳轉和第一部分藍牙接聽電話跳轉過程相似,就不重複了,最後會來到packages/apps/Bluetooth/jni/com_android_bluetooth_a2dp.cpp的connectA2dpNative,一樣到下面的代碼,咱們能看到的開放的代碼也就是這些,再下面要看vendor的具體實現了。[java] view plaincopy

  1. static jboolean connectA2dpNative(JNIEnv *env, jobject object, jbyteArray address) {  
  2.   jbyte *addr;  
  3.   bt_bdaddr_t * btAddr;  
  4.   bt_status_t status;  
  5.   
  6.   ALOGI("%s: sBluetoothA2dpInterface: %p", __FUNCTION__, sBluetoothA2dpInterface);  
  7.   if (!sBluetoothA2dpInterface) return JNI_FALSE;  
  8.   
  9.   addr = env->GetByteArrayElements(address, NULL);  
  10.   btAddr = (bt_bdaddr_t *) addr;  
  11.   if (!addr) {  
  12.       jniThrowIOException(env, EINVAL);  
  13.       return JNI_FALSE;  
  14.   }  
  15.   if ((status = sBluetoothA2dpInterface->connect((bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {  
  16.       ALOGE("Failed HF connection, status: %d", status);  
  17.   }  
  18.   env->ReleaseByteArrayElements(address, addr, 0);  
  19.   return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;  

分類: android,android_bluetooth

相關文章
相關標籤/搜索