1. 程式人生 > >藍芽上層協議,服務端的啟動,獲取以及藍芽裝置的連線

藍芽上層協議,服務端的啟動,獲取以及藍芽裝置的連線

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++最後來完成連線。只有裝置的協議連線起來了,才可以進行進一步的操作。