Android藍芽開發【六】hfp連線
本文主要內容是藍芽手機音訊的連線、斷開流程分析,對應藍芽HFP profile。
該文章是基於Android原始碼4.3的
1 hfp簡單介紹 |
HFP (Hands-free Profile),讓藍芽裝置(如藍芽耳機)可以控制電話,如接聽、結束通話、拒接、語音撥號等,拒接、語音撥號要看藍芽耳機及電話是否支援。
HFP定義了音訊閘道器(AG)和擴音元件(HF)兩個角色:
音訊閘道器(AG) – 該裝置為音訊(特別是手機)的輸入/輸出閘道器。
擴音元件(HF) – 該裝置作為音訊閘道器的遠端音訊輸入/輸出機制,並可提供若干遙控功能。
2 手機音訊連線 |
對於手機音訊的使用,首先連線的藍芽裝置需要支援hfp協議,並且需要與該裝置進行配對,如何進行藍芽配對這裡就不細說了,可以參照我的其他文章。主要分析下其連線過程。
對於系統自帶應用Settings中已配對的藍芽裝置介面(如下圖所示),
其對應檔案路徑:
packages/apps/Settings/src/com/android/settings/bluetooth/DeviceProfilesSettings.java
點選手機音訊進行連線,呼叫onPreferenceChange。
public boolean onPreferenceChange(Preference preference, Object newValue) { if (preference == mDeviceNamePref) { //重新命名 mCachedDevice.setName((String) newValue); } else if (preference instanceof CheckBoxPreference) {//check box LocalBluetoothProfile prof = getProfileOf(preference); //獲取對應的profile onProfileClicked(prof, (CheckBoxPreference) preference); return false; // checkbox will update from onDeviceAttributesChanged() callback } else { return false; } return true; }
接著看onProfileClicked()函式處理
private void onProfileClicked(LocalBluetoothProfile profile, CheckBoxPreference profilePref) { BluetoothDevice device = mCachedDevice.getDevice(); //獲取配對的藍芽裝置 int status = profile.getConnectionStatus(device); //獲取profile的連線狀態 boolean isConnected = status == BluetoothProfile.STATE_CONNECTED; if (isConnected) { //如果是連線狀態則斷開連線 askDisconnect(getActivity(), profile); } else { //沒有連線 if (profile.isPreferred(device)) { //獲取profile是否是首選 // profile is preferred but not connected: disable auto-connect profile.setPreferred(device, false); //設定對應profile的PRIORITY 為off,防止自動連線 refreshProfilePreference(profilePref, profile); //重新整理check box狀態 } else { profile.setPreferred(device, true); //設定對應profile的PRIORITY 為on mCachedDevice.connectProfile(profile); //連線指定profile } } }
接著檢視CachedBluetoothDevice中的connectProfile函式連線某一profile。
void connectProfile(LocalBluetoothProfile profile) {
mConnectAttempted = SystemClock.elapsedRealtime();
// Reset the only-show-one-error-dialog tracking variable
mIsConnectingErrorPossible = true;
connectInt(profile); //連線profile
refresh(); // 重新整理ui
}
synchronized void connectInt(LocalBluetoothProfile profile) {
//檢視是否配對,如果沒有配對則進行配對,配對後進行連線,
//如果配對則直接連線
if (!ensurePaired()) {
return;
}
if (profile.connect(mDevice)) {//連線
return;
}
}
connectProfile() ——>connectInt() connectInt()函式中會先判斷是否配對,如果沒有配對則開始配對,配對成功後連線profile。
如果已經配對則直接連線profile。
對於profile.connect(mDevice)會根據profile呼叫各自對應的connect方法。(如手機音訊則對應HeadsetProfile,媒體音訊對應A2dpProfile)。這裡檢視手機音訊的連線HeadsetProfile。
public boolean connect(BluetoothDevice device) {
if (mService == null) return false;
//獲取連線hfp的裝置
List<BluetoothDevice> sinks = mService.getConnectedDevices();
if (sinks != null) {
for (BluetoothDevice sink : sinks) {
mService.disconnect(sink); //斷開連線
}
} //連線hfp。
return mService.connect(device);
}
HeadsetProfile.java中的connect()方法,mService是通過getProfileProxy獲取的BluetoothHeadset代理物件,通過其進行hfp相關操作。
mService.connect跳到Bluetooth應用中,
程式碼路徑:packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetService.java
先呼叫到內部類BluetoothHeadsetBinder的connect方法。
public boolean connect(BluetoothDevice device) {
HeadsetService service = getService();
if (service == null) return false;
return service.connect(device);
}
該方法中很明顯是去呼叫HeadsetService的connect方法。
public boolean connect(BluetoothDevice device) {
enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permission");
if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
return false; //檢查priority
}
int connectionState = mStateMachine.getConnectionState(device);
if (connectionState == BluetoothProfile.STATE_CONNECTED ||
connectionState == BluetoothProfile.STATE_CONNECTING) {
return false; //檢查連線狀態
}
mStateMachine.sendMessage(HeadsetStateMachine.CONNECT, device);
return true;
}
HeadsetService的connect()函式會對priority和連線狀態進行必要的檢查,不符合條件則返回false。符合條件則向狀態機發送訊息HeadsetStateMachine.CONNECT。
此時HeadsetStateMachine中狀態應該是Disconnected,所以檢視Disconnected state中的處理
BluetoothDevice device = (BluetoothDevice) message.obj;
//傳送廣播,正在連線hfp
broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
BluetoothProfile.STATE_DISCONNECTED);
//連線遠端裝置。
if (!connectHfpNative(getByteAddress(device)) ) {
//連線失敗,向外傳送連線失敗廣播
broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_CONNECTING);
break;
}
synchronized (HeadsetStateMachine.this) {
mTargetDevice = device;
transitionTo(mPending); //切換到pending狀態
}
sendMessageDelayed(CONNECT_TIMEOUT, 30000);
HeadsetStateMachine呼叫connectHfpNative()函式來進行手機音訊的連線。connectHfpNative是native方法,跳轉到com_android_bluetooth_hfp.cpp中,呼叫對應的方法connectHfpNativestatic jboolean connectHfpNative(JNIEnv *env, jobject object, jbyteArray address) {
jbyte *addr;
bt_status_t status;
if (!sBluetoothHfpInterface) return JNI_FALSE;
addr = env->GetByteArrayElements(address, NULL);
if (!addr) {
jniThrowIOException(env, EINVAL);
return JNI_FALSE;
}
if ((status = sBluetoothHfpInterface->connect((bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
ALOGE("Failed HF connection, status: %d", status);
}
env->ReleaseByteArrayElements(address, addr, 0);
return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
其中sBluetoothHfpInterface->connect會跳到藍芽協議棧進行連線,協議棧就先不進行分析了。
3 連線狀態 |
當協議棧連線狀態改變會回撥com_android_bluetooth_hfp.cpp中的方法connection_state_callback()。
static void connection_state_callback(bthf_connection_state_t state, bt_bdaddr_t* bd_addr) {
jbyteArray addr;
CHECK_CALLBACK_ENV
addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
if (!addr) {
checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
return;
}
sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged,
(jint) state, addr);
checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
sCallbackEnv->DeleteLocalRef(addr);
}
在connection_state_callback方法中會從cpp層呼叫到java層,對應於HeadsetStateMachine中的onConnectionStateChanged函式private void onConnectionStateChanged(int state, byte[] address) {
StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED);
event.valueInt = state;
event.device = getDevice(address);
sendMessage(STACK_EVENT, event);
}
onConnectionStateChanged函式中傳送訊息STACK_EVENT(攜帶狀態和藍芽地址),此時是Pending state,收到該訊息呼叫processConnectionEvent。
正常連線成功應該會先收到HeadsetHalConstants.CONNECTION_STATE_CONNECTING狀態,然後收到HeadsetHalConstants.CONNECTION_STATE_CONNECTED狀態。
//傳送廣播,連線成功
broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_CONNECTING);
synchronized (HeadsetStateMachine.this) {
mCurrentDevice = mTargetDevice; //mCurrentDevice表示已連線的裝置
mTargetDevice = null; //mTargetDevice表示要連線的裝置
transitionTo(mConnected); //切換到Connected狀態
}
收到HeadsetHalConstants.CONNECTION_STATE_CONNECTED狀態,後向外發送連線成功的廣播,狀態機切換到Connected狀態
private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) {
/* Notifying the connection state change of the profile before sending the intent for
connection state change, as it was causing a race condition, with the UI not being
updated with the correct connection state. */
mService.notifyProfileConnectionStateChanged(device, BluetoothProfile.HEADSET,newState, prevState);
Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM);
}
在mService.notifyProfileConnectionStateChanged中會將手機音訊的proirty設定為auto_connect,並且向外傳送BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED廣播。
在其他應用中可以通過廣播接收者註冊BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED該廣播,用來監聽hfp的連線狀態。
4 更新ui |
當手機音訊連線成功後,Settings應用中會更新ui介面。
LocalBluetoothProfileManager中會對所有的profile進行管理,其將hfp的profile新增到BluetoothEventManager中,BluetoothEventManager會註冊藍芽狀態改變、各profile狀態改變等廣播。
當BluetoothEventManager收到BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED廣播後,會根據action獲取對應的handler,呼叫對應handler的onReceive方法。
接收到該廣播跳到LocalBluetoothProfileManager內部類StateChangedHandler.onReceive->CachedBluetoothDevice.onProfileStateChanged ->refresh ->dispatchAttributesChanged
接著跳到DeviceProfilesSettings中的onDeviceAttributesChanged ->refresh.這裡會對介面進行更新,顯示其連線狀態資訊。
hfp連線過程已經分析完了,而斷開連線到過程和連線整體相差不多,就不再細說了。
相關推薦
Android藍芽開發【六】hfp連線
本文主要內容是藍芽手機音訊的連線、斷開流程分析,對應藍芽HFP profile。 該文章是基於Android原始碼4.3的 1 hfp簡單介紹 HFP (Hands-free Profile),讓藍芽裝置(如藍芽耳機)可以控制電話,如接聽、結束通話、拒接、語音撥號等,
Android 藍芽開發(八)hfp接聽、結束通話電話
本文已授權微信公眾號 fanfan程式媛 獨家釋出 掃一掃文章底部的二維碼或在微信搜尋 fanfan程式媛 即可關注 繼續研究hfp相關功能。藍芽耳機可以控制手機接聽、拒接、結束通話電話,撥打電話等功能。本文主要分析下起這些操作的大致流程。 在
Android 藍芽開發(七)hfp音訊連線
本文已授權微信公眾號 fanfan程式媛 獨家釋出 掃一掃文章底部的二維碼或在微信搜尋 fanfan程式媛 即可關注 接著上一篇hfp連線繼續,檢視藍芽通話時如何進行處理的。hfp連線有兩個連線,一個是hfp連線(在設定介面顯示的是手機音訊),另
Android 藍芽開發常用UUID表
// Sample Services. attributes.put("0000180d-0
Android藍芽開發之低功耗藍芽(藍芽4.0)開發全記錄
主要內容概況 前面我記錄了開發藍芽2.0的過程,今天準備介紹現在的主流藍芽技術,藍芽4.0的開發,該藍芽技術是在Android4.3(API級別18)版本引入的API。 官方文件 具體的區別主要以下幾點: 1.新的藍芽技術提供了連線服務的方法,以前是沒有提供連線藍芽的方法
Android藍芽開發之經典藍芽(藍芽2.0)開發全記錄
前言部分 最近因為需要開始藍芽相關開發,所以在網上搜索了很多內容,並且結合自己的開發過程做了一個總結,先儲備上,也許可能幫到正在做藍芽開發的同學。 藍芽很早就是android裝置上基本通訊功能了,只是以前的沒有那麼多藍芽裝置,現在藍芽裝置種類繁多,所以經常會有人遇到藍芽相關的開發
1 android藍芽開發---與藍芽模組進行通訊
原文地址 http://www.cnblogs.com/wenjiang/p/3200138.html 近半個月來一直在搞android藍芽這方面,主要是專案需要與藍芽模組進行通訊。開頭的進展很順利,但因為藍芽模組不在我這裡,所以只能用手機測試。一開頭就發現手機的藍芽
Android 藍芽開發 —— BLE
藍芽——BLE 介紹 1.BLE 是 Bluetooth Low Energy 的縮寫,意思為低功耗藍芽。由藍芽技術聯盟(Bluetooth SIG)設計的無線通訊技術,主要用於醫療,健身,安全和家庭娛樂行業。 與傳統藍芽相比,藍芽低功耗旨在大幅降低功耗
Android 藍芽開發之搜尋、配對、連線、通訊大全
藍芽( Bluetooth®):是一種無線技術標準,可實現固定裝置、移動裝置和樓宇個人域網之間的短距離資料交換(使用2.4—2.485GHz的ISM波段的UHF無線電波)。藍芽裝置最多可以同時和7個其它藍芽裝置建立連線,進行通訊,當然並不是每一個藍芽都可以達到
android 藍芽開發基本流程
原文連結 http://blog.csdn.net/q610098308/article/details/45248423 侵刪 此例子基於 android demo 對於一般的軟體開發人員來說,藍芽是很少用到的,尤其是Android的藍芽開發,國內的例子很
Android藍芽開發教程(一)——搜尋藍芽裝置
Android藍芽功能的開發一直是很多新手頭疼的問題,網上雖然也有很多教程貼,但大多都寫得不全面,不能讓我們真正掌握藍芽開發的基本知識。本教程主要講解搜尋藍芽裝置、藍芽裝置之間的連線和藍芽之間的通訊三個主要模組。掌握了這三個,基本就能進行簡單的藍芽開發了。
Android藍芽開發(一)之開啟藍芽和裝置搜尋
Android藍芽開發系列目錄: 一、判斷是否系統是否支援藍芽 在使用藍芽之前,我們首先要判斷手機裝置是否支援藍芽,雖然現在基本都支援藍芽了,但是為了程式碼的嚴謹性我們還是需要在程式碼中判斷: private BluetoothManager bluetoothma
android藍芽開發-與藍芽模組通訊
因為我馬上要離職了,在很多官網上看到招聘android基本都要會藍芽開發,wifi的經驗,所以就來學習下藍芽! 於是,我得到了很好的教訓:請確保專案中的最關鍵因素是否在我們的掌握中。像是藍芽模組這種東西,應該今早催促對方拿過來才是,而不是自己一個人在那邊瞎搞。 嘮叨話就先到
android藍芽開發 藍芽裝置的查詢和連線
Android對於藍芽開發從2.0版本的sdk才開始支援,而且模擬器不支援,測試至少需要兩部手機,所以制約了很多技術人員的開發。 1. 首先,要操作藍芽,先要在AndroidManifest.xml里加入許可權 // 管理藍芽裝置的許可權 <
Android 藍芽開發,藍芽連印表機。
public synchronized void connect(String macAddress, BluetoothDevice device) { if (mThread != null) { mThread.interrupt(); m
關於android藍芽開發的一些總結
最近三個月一直在忙著公司的一個產品。這個產品主要是藍芽和微控制器之間的資料交換。當時在選擇通訊方式的時候有2種選擇。一種是wifi,另外一種就是後來選擇的藍芽。期間遇到了很多問題,包括藍芽連線問題啊,android版本問題,bluesocket連線問題等等。 1
android 藍芽開發常見問題總結
1、主機和從機有什麼區別 主機是主動搜尋的,從機是被搜尋的,主機可以記憶從機地址、密碼,也可以放棄這些記憶 重新搜尋。 (HC-06)主機如果配對過從機,那麼就會記憶該從機的地址和密碼,下次上電就
Android藍芽開發(二) BLE4.0低功耗藍芽
一、BLE4.0低功耗藍芽 Bluetooth Low Energy,藍芽低功耗,是從藍芽4.0開始支援的技術。相較傳統藍芽,傳輸速度更快、覆蓋範圍廣、安全性高、延時短、耗電低等特點。 二、關鍵術語 1.GATT(通用屬性配置):通用屬性配置檔案,用於ble鏈路上傳送和接
Android 藍芽開發(九)A2DP基本功能
本文主要是Android做為Audio Source端,A2DP的基本操作:包括連線、斷開連線、設定優先順序、獲取優先順序、獲取A2DP連線狀態、獲取A2DP連線的裝置列表等功能。 1 簡介 A2DP全名是Advanced Audio Distrib
Android 藍芽開發(十)A2DP原始碼分析
上一篇說了下A2DP的一些基本操作,這篇分析下系統應用、系統原始碼是如何操作A2DP的。尤其是其連線過程,基於Android4.3原始碼。Andorid手機一般都是做為A2DP Audio Source端。 1 連線過程 媒體音訊也就是A2DP,首先連線的藍芽裝置需要