藍芽上層協議,服務端的啟動,獲取以及藍芽裝置的連線
1,藍芽協議/服務端的啟動
上一篇文章中,分析了Bluetooth.apk啟動過程,啟動Bluetooth.apk時,一般啟動了AdapterService這一對應的服務。檢視package/app/Bluetooth的原始碼,裡面主要是一些具體的協議,其中每一個協議對應一個具體的服務。那麼,這些服務是何時以及如何啟動的呢?
在android 5.1 中,開啟藍芽時,在AdapterService的setProfileServiceState中就會逐個啟動支援的服務,方法如下:
private void setProfileServiceState(Class[] services, int state) { if (state != BluetoothAdapter.STATE_ON && state != BluetoothAdapter.STATE_OFF) { debugLog("setProfileServiceState() - Invalid state, leaving..."); return; } int expectedCurrentState= BluetoothAdapter.STATE_OFF; int pendingState = BluetoothAdapter.STATE_TURNING_ON; if (state == BluetoothAdapter.STATE_OFF) { expectedCurrentState= BluetoothAdapter.STATE_ON; pendingState = BluetoothAdapter.STATE_TURNING_OFF; } for (int i=0; i <services.length;i++) { String serviceName = services[i].getName(); Integer serviceState = mProfileServicesState.get(serviceName); if(serviceState != null && serviceState != expectedCurrentState) { debugLog("setProfileServiceState() - Unable to " + (state == BluetoothAdapter.STATE_OFF ? "start" : "stop" ) + " service " + serviceName + ". Invalid state: " + serviceState); continue; } debugLog("setProfileServiceState() - " + (state == BluetoothAdapter.STATE_OFF ? "Stopping" : "Starting") + " service " + serviceName); if (state == BluetoothAdapter.STATE_ON && mDisabledProfiles.contains(serviceName)) { Log.i(TAG, "skipping " + serviceName + " (disabled)"); continue; } if (DBG) { Log.w(TAG, (state == BluetoothAdapter.STATE_OFF? "Stopping" : "Starting" ) +" service " + serviceName); } mProfileServicesState.put(serviceName,pendingState); Intent intent = new Intent(this,services[i]); intent.putExtra(EXTRA_ACTION,ACTION_SERVICE_STATE_CHANGED); intent.putExtra(BluetoothAdapter.EXTRA_STATE,state); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); startService(intent); } }
但是在android 6.0 中,在開啟藍芽,最後呼叫AdapterService的
setGattProfileServiceState的僅僅啟動GattService這一服務,緊接著的呼叫
BluetoothAdapter的enable方法, notifyUserAction方法最後會呼叫setProfileServiceState方法啟動其他的服務,但是這樣做好像意義不大。
public boolean enable() { android.util.SeempLog.record(56); int state = STATE_OFF; if (isEnabled() == true){ if (DBG) Log.d(TAG, "enable(): BT is already enabled..!"); return true; } //Use service interface to get the exact state if (mService != null) { try { state = mService.getState(); } catch (RemoteException e) {Log.e(TAG, "", e);} } if (state == BluetoothAdapter.STATE_BLE_ON) { // 藍芽已經開啟 Log.e(TAG, "BT is in BLE_ON State"); notifyUserAction(true); // 發出通知 return true; } try { return mManagerService.enable(); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; }
在android 6.0中,其他協議的服務啟動我有些疑惑,到底在什麼時候啟動的呢?
請看下節。
2,設定裡服務端的啟動
在手機裡面,這些服務應該都有獲取,並且服務端和客戶端已經繫結,那麼程式碼在哪兒呢?
在這裡android 5.1和android 6.0還是有些微小的區別,程式碼路徑不一樣,在android6.0 中,設定裡面之外,還有一個資料夾,路徑如下,
frameworks\base\packages\SettingsLib\src\com\android\settingslib\bluetooth
包含的檔案:
基本上每一個profile檔案就對應;而在android 5.1 中,這些檔案都包含在
packages\apps\Settings\src\com\android\settings\bluetooth之中。其他的基本原理是完全一樣的。
啟動設定,開啟藍芽設定,設定裡的協議客戶端就會建立,然後啟動服務端並且和服務端繫結在一起,流程圖如下:
這裡以PanProfile為例來說明, PanProfile的建構函式如下,
PanProfile(Context context) {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
adapter.getProfileProxy(context, new PanServiceListener(),
BluetoothProfile.PAN);
}
private BluetoothPan mService;
private boolean mIsProfileReady;
private final class PanServiceListener implements BluetoothProfile.ServiceListener { public void onServiceConnected(int profile, BluetoothProfile proxy) { if (V) Log.d(TAG,"Bluetooth service connected"); mService = (BluetoothPan) proxy; mIsProfileReady=true; } public void onServiceDisconnected(int profile) { if (V) Log.d(TAG,"Bluetooth service disconnected"); mIsProfileReady=false; } }
getProfileProxy方法下節再論述。
3,藍芽協議/客戶端的獲取
在藍芽開發中,服務端的各種協議已經封裝好了,如果是我們自己開發,我們怎麼樣才能呼叫呢?對應的客戶端是啥呢?開發時,比如現在想進行藍芽通話,獲取藍芽通話的客戶端物件。
private final BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
private BluetoothHeadsetClient mHfpClient = null;
一般在元件的oncreate方法中呼叫enableHFP方法來獲取藍芽通話的客戶端物件,程式碼如下:
private void enableHFP() {
mBluetoothAdapter.getProfileProxy(getApplicationContext(), new ServiceListener() {
public void onServiceConnected(int profile,BluetoothProfile proxy) {
if (profile == BluetoothProfile.HEADSET_CLIENT) {
android.util.Log.d("fang ", "init mBluetoothHeadset");
mHfpClient = (BluetoothHeadsetClient) proxy;
}
}
public void onServiceDisconnected(int profile) {
if (profile == BluetoothProfile.HEADSET_CLIENT) {
mHfpClient = null;
}
}
}, BluetoothProfile.HEADSET_CLIENT);
}
首先看看getProfileProxy方法
public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,
int profile) {
if (context == null || listener == null) return false;
if (profile == BluetoothProfile.HEADSET) {
BluetoothHeadset headset = new BluetoothHeadset(context, listener);
return true;
} else if (profile == BluetoothProfile.A2DP) {
BluetoothA2dp a2dp = new BluetoothA2dp(context, listener);
return true;
} else if (profile == BluetoothProfile.A2DP_SINK) {
BluetoothA2dpSink a2dpSink = new BluetoothA2dpSink(context, listener);
return true;
} else if (profile == BluetoothProfile.AVRCP_CONTROLLER) {
BluetoothAvrcpController avrcp = new BluetoothAvrcpController(context, listener);
return true;
} else if (profile == BluetoothProfile.INPUT_DEVICE) {
BluetoothInputDevice iDev = new BluetoothInputDevice(context, listener);
return true;
} else if (profile == BluetoothProfile.PAN) {
BluetoothPan pan = new BluetoothPan(context, listener);
return true;
} else if (profile == BluetoothProfile.DUN) {
BluetoothDun dun = new BluetoothDun(context, listener);
return true;
} else if (profile == BluetoothProfile.HEALTH) {
BluetoothHealth health = new BluetoothHealth(context, listener);
return true;
} else if (profile == BluetoothProfile.MAP) {
BluetoothMap map = new BluetoothMap(context, listener);
return true;
} else if (profile == BluetoothProfile.HEADSET_CLIENT) {
BluetoothHeadsetClient headsetClient = new BluetoothHeadsetClient(context, listener);
return true;
} else if (profile == BluetoothProfile.SAP) {
BluetoothSap sap = new BluetoothSap(context, listener);
return true;
} else if (profile == BluetoothProfile.HID_DEVICE) {
BluetoothHidDevice hidd = new BluetoothHidDevice(context, listener);
return true;
} else {
return false;
}
}
這一共有12個服務,對應12個協議。這些客戶端的類都是繼承自
BluetoothProfile 典型的工廠模式,得到不同的例項物件。以
BluetoothHeadsetClient協議為例,論述如下流程,
BluetoothHeadsetClient的建構函式如下:
BluetoothHeadsetClient(Context context, ServiceListener l) {
mContext = context;
mServiceListener = l;
mAdapter = BluetoothAdapter.getDefaultAdapter();
IBluetoothManager mgr = mAdapter.getBluetoothManager();
if (mgr != null) {
try {
mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
} catch (RemoteException e) {
Log.e(TAG,"",e);
}
}
doBind();
}
boolean doBind() {
Intent intent = new Intent(IBluetoothHeadsetClient.class.getName());
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
android.os.Process.myUserHandle())) {
Log.e(TAG, "Could not bind to Bluetooth Headset Client Service with " + intent);
return false;
}
return true;
}
AndroidManifest.xml中的HeadsetClientService定義如下,
<service
android:process="@string/process"
android:name = ".hfpclient.HeadsetClientService"
android:enabled="@bool/profile_supported_hfpclient">
<intent-filter>
<action android:name="android.bluetooth.IBluetoothHeadsetClient" />
</intent-filter>
</service>
bindServiceAsUser掛進程繫結在此就不論述了,直接看mConnection物件的方法,
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "Proxy object connected");
mService = IBluetoothHeadsetClient.Stub.asInterface(service);
if (mServiceListener != null) {
mServiceListener.onServiceConnected(BluetoothProfile.HEADSET_CLIENT,
BluetoothHeadsetClient.this);
}
}
@Override
public void onServiceDisconnected(ComponentName className) {
if (DBG) Log.d(TAG, "Proxy object disconnected");
mService = null;
if (mServiceListener != null) {
mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET_CLIENT);
}
}
};
所以,最後的結果是將運行於第三方apk的BluetoothHeadsetClient物件和運行於Bluetooth.apk的HeadsetClientService對應起來了。
mServiceListener就是enableHFP方法中的匿名ServiceListener類的物件。看到此,恍然大悟了, getProfileProxy方法根據第三個引數首先建立對應的客戶端類,然後將客戶端和Bluetooth.apk的服務端進行連線,最後返回客戶端,這是典型的C/S模式。這樣,12個客戶端和服務端協議一一對應了,但是上層藍芽不止這12個協議。好了,現在每個協議的客戶端可以獲取了,萬事俱備了,接下來看看每個協議的使用場景和功能吧。
4,藍芽裝置的連線
最開始的時候,每個協議的客戶端都有一個connect方法,但是我一直都不知道是什麼時候呼叫的,裝置到底是如何連線的。過了很久很久之後才發現,還是在設定裡,點選已配對的裝置就可以進行連線,最後是每個協議獨立的連線。這是基於android6.0,但是5.1的幾乎完全一樣,還是以BluetoothPan為例說明連線的部分流程,流程圖如下
步驟其實很簡單,就是從協議的客戶端跨程序呼叫到對應的服務端,然後通過JNI機制呼叫C/C++最後來完成連線。只有裝置的協議連線起來了,才可以進行進一步的操作。