android藍牙相關框架專題及常見問題 - CSDN

2021-01-11 CSDN技術社區
一、前言

本文側重點:Android中藍牙代碼結構分析。
代碼來源於Android P,本文相關代碼:
client:
frameworks/base/core/java/android/bluetooth/*
system/bt/binder/android/bluetooth/**.aidl
servie:
framework/base/services/core/java/com/android/server/BluetoothService.java
framework/base/services/core/java/com/android/server/BluetoothManagerService.java
bluetooth:
package/app/bluetooth
(上面是aosp源碼提供的藍牙實現。而晶片廠商提供的代碼一般這部分是沒有源碼的,比如mtkbluetooth.apk直接替換此apk。但aosp提供的源碼供我們學習還是可以的。)
settingslib:
vendor/mediatek/proprietary/packages/apps/SettingsLib/src/com/android/settingslib/bluetooth/
(SettingsLib原生frameworks/base/packages/SettingsLib也有,上面是MTK定製,主要區別是對協議的修改添加一些支持等)

上面四個部分是藍牙核心的地方。對於系統的應用從AndroidP開始。AOSP已經原生支持對藍牙的各種接口了。只要晶片廠商對接好系統藍牙接口我們就可以依賴原生接口開發自己想要的應用app,下面是功能應用的表格:

功能依賴項Diar通話相關,來去電走Android原生telecom流程。聯繫人/通話記錄依賴原生contactsprovidermusic音樂相關,走原生Mediasessionsettings藍牙設置相關,用settingsLib控制,開關連接等其他其他應用依賴於Android自身支持的協議(profile)比如GATT低功耗藍牙,OPP文件傳輸,等等

註:
1、本文以分析整體為主。讀者需要對framework中service/client結構熟悉(此文有介紹這種結構:PackageManagerService服務框架詳解)。另外建議閱讀時去翻翻對應路徑下的源碼,便於理解。
2、本文以整體框架講解的思想來闡述。特殊流程會源碼講解,講解時只列出源碼關鍵代碼行。大部分只總結它的作用。需要讀者自行去結合源碼理解。

二、功能簡單介紹

首先藍牙服務和AMS、PMS等系統眾多服務一樣,也是service/client結構。不理解S/C結構的同學可以直接理解為普通的API調用,直接調用最終在BluetoothManagerService.java代碼裡,方法名大部分都一樣。

2.1、四部分(client、service、bluetooth、settinglib)

藍牙的代碼主要分為標題的四個部分

2.1.1、client

客戶端主要代碼是BluetoothAdapter,我們平時開發時都是通過操作BluetoothAdapter的公開api來實現我們的功能。
除開BluetoothAdapter起到核心作用之外在com.android.bluetooth下還默認提供一些默認協議API級的支持。這些協議都是一個獨立的profile實現。這些profile使我們可以控制藍牙工作於我們想要的場景下。通常這部分三方apk使用較多。

名稱簡單介紹A2dp音頻Gatt低功耗Headset藍牙耳機Health健康Socket面向連接,套接字,基於RFCOMM2.2.2、service

代碼:BluetoothManagerService.javaservice管理:
BluetoothManagerService裡主要是framework層實現藍牙功能的地方。我們從BluetoothAdapter調用方法都會調用到BluetoothManagerService裡,而BluetoothManagerService裡的大部分實現又是通過綁定bluetooth apk裡的service(AdapterService)來實現。這樣BluetoothManagerService既起到了統一framework藍牙實現的地方,又讓Bluetooth apk可以有豐富的profile具體功能實現。Profile理解:
一個藍牙硬體模塊根據藍牙規範(比如藍牙4.0)。會默認實現很多自帶的協議。由於制定協議的人多個組等其他原因。藍牙規範是有很多個細分的協議協同工作。可以理解為一種通信規範。一個標準的藍牙模塊肯定得把藍牙規範裡的協議都實現。而Profile的存在的意義就是。不管你標準有多少個協議,我只要我想要的功能,別的我不管。比如A2dp音頻協議。我只需要藍牙工作於A2dp就可以了。當然A2dp可能是基於其中某幾個協議上的協議。但是別的協議它沒有。用JavaScript的思想來理解Profile,它就是一個切面。

2.2.3、bluetooth

bluetooth apk由於是具體的實現,所以它會實現所有的協議。
除開client中提供的profile,還有settingslib中系統藍牙操作功能實現的Profile。這些豐富的profile會在下一小節列出。

2.2.4、settingslib

settingsLib和其他三個部分是相對而言比較獨立的一個部分。因為它只是封裝操作。以便settings可以更方便的控制管理。
settingslib主要是服務系統app:settings使用,編譯時一般也是編譯settings的時候一起編譯settingslib。
只有系統級權限(集成到系統中,system/app、framework等)才可以調用settingslib,普通三方應用開發者無法使用
settingslib中藍牙代碼相當於也是操作BluetoothAdapter,BluetoothAdapter間接調用BluetoothManagerService來實現功能.
在SettingsLib\src\com\android\settingslib\bluetooth中我們還能看到像com.android.bluetooth路徑下那些協議之外的一些協議

名稱簡單介紹名稱簡單介紹A2dp音頻Headset藍牙耳機HearingAid助聽器Hfp免提Hid人機接口設備Map信息訪問Opp對象推送Pan個人區域網Pbap電話薄Sap會話通知

看到這些協議,settingslib服務於系統就很好理解了。一個手機連上藍牙。那麼手機設置支持藍牙相關的操作也就是這些協議支持的功能

三、詳解四大部分(client、service、bluetooth、settinglib)

文章第二段對Android藍牙框架代碼已經有了一個簡單的介紹。就是這四個部分代碼支撐著藍牙的各種功能。接下來將詳細介紹四個部分比較核心的內容。

3.1、BluetoothAdapter詳細介紹(client)

BluetoothAdapter部分主要是api使用,所以這部分以表格方式列出信息方便查閱
第一個表是BluetoothAdapter定義的一些狀態和通知:

作用類型關鍵字介紹開關通知ACTION_STATE_CHANGED上次狀態和當前狀態開關狀態定義AdapterState描述當前藍牙狀態請求掃描ACTION_REQUEST_DISCOVERABLE請求掃描,默認120秒,可帶時間參數 activity#resulet請求掃描ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE一直允許掃描 activity#resulet請求打開/關閉ACTION_REQUEST_ENABLE、ACTION_REQUEST_DISABLE請求打開/關閉activity#resulet掃描狀態通知ACTION_SCAN_MODE_CHANGED上次狀態和當前狀態掃描狀態定義ScanMode描述掃描狀態掃描開始/結束通知ACTION_DISCOVERY_STARTED、ACTION_DISCOVERY_FINISHED名字改變通知ACTION_LOCAL_NAME_CHANGED帶名字參數連接狀態通知ACTION_CONNECTION_STATE_CHANGED帶當前和上次狀態LE下狀態通知ACTION_BLE_STATE_CHANGED藍牙只在低功耗模式時狀態變化mac地址變化ACTION_BLUETOOTH_ADDRESS_CHANGED帶參數連接類型ACTION_BLE_ACL_CONNECTED斷開連接類型ACTION_BLE_ACL_CONNECTED

上面這些豐富的廣播通知是在bluetooth apk裡的實現的。bluetooth中的btservice中收到狀態的時候直接發出廣播

第二個表是BluetoothAdapter.java內部方法(方法只是提及,不包括所有,類似或者不重要的省略):

方法作用方法作用getDefaultAdapter拿對象getRemoteDevice遠端設備getBluetoothLeAdvertiserLE廣播數據getPeriodicAdvertisingManagerLE註冊管理getBluetoothLeScanner掃描isEnabled開getState狀態getLeStateLE狀態enable打開getAddress地址setName名字factoryReset出廠設置getUuidsuuidgetBluetoothClass遠端設備信息判斷設備類型是否提供某個service等setScanMode掃描模式setDiscoverableTimeout超時時間cancelDiscovery取消isDiscovering是否掃描isLe***LE設備功能支持判斷getMaxConnectedAudioDevices最大audio設備數requestControllerActivityEnergyInfo獲取藍牙信息比如電量getBondedDevices已配對設備getSupportedProfiles支持的協議getConnectionState連接狀態getProfileConnectionState協議連接狀態listen***創建service監聽例如開氣socket服務getProfileProxy客戶端拿到服務比如pbapcloseProfileProxy關閉連接enableNoAutoConnect打開checkBluetoothAddress有效地址判斷getBluetoothManager獲得bluetoothmanagerservicegetBluetoothService獲得藍牙服務startLeScanLE開始掃描stopLeScan停止le掃描

BluetoothAdapter小結
1、BluetoothAdapter的重要信息上面基本都列出來了。除了常規的開關監聽等操作外。還有很多掃描連接狀態等得廣播通知發送出來。
2、我們的apk客戶端想要和某個profile服務綁定時,通過getProfileProxy來拿到服務和監聽
3、低功耗LE設備也提供了一些操作方法

3.2、BluetoothManagerService詳細介紹(service)

BluetoothManagerService功能實現比較分散,下面以講解比較重要的幾個代碼流程邏輯為主。
BluetoothManagerService這部分主要是功能的詳細實現。而BluetoothManagerService和其他系統service不太一樣。它這裡的實現也只是表面封裝一下。具體的實現是通過綁定bluetooth apk裡的AdapterService,然後通過AdapterService來實現。下面我們看下怎麼調用到AdapterService的。

3.2.1、系統api調用流程

BluetoothManagerService調用到package/app/bluetooth
BluetoothAdapter->BluetoothManagerService->AdapterService(bluetooth apk)上面client部分列出的方法大部分都是這個操作流程,走到bluetooth裡的具體實現。

查看BluetoothAdapter調用邏輯,和其他系統api一樣,藍牙也是S/C結構。那麼具體實現就都會集中到BluetoothManagerService。查看BluetoothManagerService代碼我們很容發現裡面的詳細實現主要有兩個間接調用mManagerService和mBluetooth 。我們跟一下這兩個對象。
BluetoothManagerService.java代碼片段:

IBluetoothManager mManagerService =  IBluetoothManager.Stub.asInterface(ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE)); private void handleEnable(boolean quietMode) {       ...                Intent i = new Intent(IBluetooth.class.getName());                if (!doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,                        UserHandle.CURRENT)) {                   ...                }private class BluetoothServiceConnection implements ServiceConnection {        public void onServiceConnected(ComponentName componentName, IBinder service) {            ...            msg.obj = service;            mHandler.sendMessage(msg);        }mBluetooth = IBluetooth.Stub.asInterface(Binder.allowBlocking(service));

mManagerService很好理解就是綁定到了我們的BluetoothManagerService服務。
mManagerService在打開藍牙的時候會間接調用到handleEnable方法,handleEnable的dobind會綁定BluetoothService,回調到BluetoothServiceConnection方法中把service賦值給mBluetooth,這樣我們就可以拿到Bluetooth app裡的service(AdapterService)進行操作了。

3.2.2、BluetoothAdapter和Bluetooth apk其他協議綁定調用

這個流程和BluetoothManagerService沒什麼關係,但是和3.2.1極其相似,所以放在這裡講。
普通三方apk可以通過BluetoothAdapter#getProfileProxy來拿到協議,並通過協議進行具體操作。操作實際也會操作到Bluetooth apk裡。我們以settingslib連接使用profile來講解。整體流程大致如下:settingslib->settingslib#setBluetoothStateOn->bluetoothadater#getProfileProxy->bluetooth apk profile service1、settinglib中LocalBluetoothAdapter打開藍牙
首先除了默認的BluetoothAdapter#enable可以打開外,settinglib中LocalBluetoothAdapter#enable也可以打開,打開時代碼會走到LocalBluetoothProfileManager#setBluetoothStateOn。我們以HidProfile為代表來講

// Called from LocalBluetoothAdapter when state changes to ON    void setBluetoothStateOn() {        if (mHidProfile == null) {        mHidProfile = new HidProfile(mContext, mLocalAdapter, mDeviceManager, this);        addProfile(mHidProfile, HidProfile.NAME,                BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED);       }

2、HidProfile拿到bluetooth apk中的HidProfile服務

public class HidProfile implements LocalBluetoothProfile {   public void onServiceConnected(int profile, BluetoothProfile proxy) {            if (V) Log.d(TAG,"Bluetooth service connected");            mService = (BluetoothHidHost) proxy;          ....     HidProfile(Context context, LocalBluetoothAdapter adapter,        CachedBluetoothDeviceManager deviceManager,        LocalBluetoothProfileManager profileManager) {        ...        adapter.getProfileProxy(context, new HidHostServiceListener(),                BluetoothProfile.HID_HOST);    }

這裡的HidProfile代碼在settinglib中,創建時核心的調用到了adapter.getProfileProxy。這裡就是framework層通過BluetoothAdapter拿到bluetooth apk中的profile service核心邏輯
BluetoothAdapter#getProfileProxy

public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,            int profile) {        ....        if (profile == BluetoothProfile.HEADSET) {            BluetoothHeadset headset = new BluetoothHeadset(context, listener);            return true;        }         ....        else if (profile == BluetoothProfile.HID_HOST) {            BluetoothHidHost iDev = new BluetoothHidHost(context, listener);            return true;        }

這裡又會新創建一個HID的profile對象BluetoothHidHost。(這裡容易和settingslib中的HidProfile混淆,這個HID還好名字有區別,別的profile名字極其類似。)BluetoothHidHost在路徑frameworks\base\core\java\android\bluetooth

private final ServiceConnection mConnection = new ServiceConnection() {        public void onServiceConnected(ComponentName className, IBinder service) {            if (DBG) Log.d(TAG, "Proxy object connected");            mService = IBluetoothHealth.Stub.asInterface(Binder.allowBlocking(service));            if (mServiceListener != null) {                mServiceListener.onServiceConnected(BluetoothProfile.HEALTH, BluetoothHealth.this);            }        }      ...BluetoothHidHost(Context context, ServiceListener l) {        ...        doBind();    }boolean doBind() {        Intent intent = new Intent(IBluetoothHidHost.class.getName());        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);        intent.setComponent(comp);        if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,                mContext.getUser())) {            Log.e(TAG, "Could not bind to Bluetooth HID Service with " + intent);            return false;        }        return true;    }

dobind時,就和上面講解的API調用流程比較類似,綁定服務,並把bluetooth apk裡的service,回調給mConnection。這樣BluetoothHidHost就綁定了bluetooth apk裡的HIDProfile service 並獲得代理對象。在mConnection中,又通過剛才HidProfile 傳入的listenner回調傳回service,讓HidProfile 也擁有了Bluetooth apk中的這個service。3、流程概述
<1>、1,2流程下來。settinglib中打開時就會創建支持的profile,這些profile創建的時候,都大同小異的讓BluetoothAdapter也創一個名字類似的profile,讓兩個profile都拿到bluetooth apk中對應協議的service。當profile拿到service了,有了bluetooth apk具體的實現了,就能調用到具體的功能上了。
<2>、注意這裡的service和協議的service/client中的service要區分開。client也是在要bluetooth apk運行一個服務來供系統使用。
<3>、三方應用可以BluetoothAdapter#getProfileProxy來拿到profile操作。一般profile實現的方法也不多,只能調用一些簡單的方法。有哪些公開api直接打開某個profile就能看到

BluetoothManagerService小結
由於service的特性,就是各個功能的具體實現。所以對於service的分析一般都是流程為主。BluetoothManagerService的功能和系統其他服務比相對比較簡單。基本就是綁定bluetoothapk 的service,具體的實現還是都在Bluetooth apk裡。

3.3、bluetooth apk

這部分代碼晶片廠商一般有自己的私有定製,未開放源碼,學習這部分參考AOSP源碼package/app/bluetooth
Bluetooth apk裡就是我們藍牙功能具體的實現了。常規打開關閉功能在AdapterService入口實現。這些方法最後跟蹤都會跟蹤到native方法上。由於方法流程很多,這裡以打開流程來舉例介紹

3.3.1、AdapterService#enable

enable就是打開的入口,我們跟一下打開流程1、狀態機開始工作

private AdapterState mAdapterStateMachine; public synchronized boolean enable(boolean quietMode) {        ...        mAdapterStateMachine.sendMessage(AdapterState.BLE_TURN_ON);    }AdapterState.java代碼片段    private TurningOnState mTurningOnState = new TurningOnState();    private TurningBleOnState mTurningBleOnState = new TurningBleOnState();    private TurningOffState mTurningOffState = new TurningOffState();    private TurningBleOffState mTurningBleOffState = new TurningBleOffState();    private OnState mOnState = new OnState();    private OffState mOffState = new OffState();    private BleOnState mBleOnState = new BleOnState();private AdapterState(AdapterService service) {        super(TAG);        addState(mOnState);        addState(mBleOnState);        addState(mOffState);        addState(mTurningOnState);        addState(mTurningOffState);        addState(mTurningBleOnState);        addState(mTurningBleOffState);        mAdapterService = service;        setInitialState(mOffState);    }

打開的工作交給了AdapterState。AdapterState 是一個狀態機,狀態機改變狀態時就會執行類的一些行為。Android的狀態機制一般用於複雜狀態+複雜操作。這裡你可以簡單理解為狀態切一下就會去執行對應操作。
構造函數默認是mOffState,收到BLE_TURN_ON消息。那麼第一個地方就是OffState的processMessage處理BLE_TURN_ON,消息也是再直接傳遞到TurningBleOnState

private class OffState extends BaseAdapterState {        ...        @Override        public boolean processMessage(Message msg) {            switch (msg.what) {                case BLE_TURN_ON:                    transitionTo(mTurningBleOnState);                    break;        }    }

2、把打開消息傳給GattService

private class TurningBleOnState extends BaseAdapterState {        ...        @Override        public void enter() {            super.enter();            sendMessageDelayed(BLE_START_TIMEOUT, BLE_START_TIMEOUT_DELAY);            mAdapterService.bringUpBle();        }      ...        @Override        public boolean processMessage(Message msg) {            switch (msg.what) {                case BLE_STARTED:                    transitionTo(mBleOnState);                    break;    void bringUpBle() {        ...        //Start Gatt service        setProfileServiceState(GattService.class, BluetoothAdapter.STATE_ON);    }  class AdapterServiceHandler extends Handler {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case MESSAGE_PROFILE_SERVICE_STATE_CHANGED:                    processProfileServiceStateChanged((ProfileService) msg.obj, msg.arg1);                    break;

3、GattService打開藍牙

private void processProfileServiceStateChanged(ProfileService profile, int state) {            switch (state) {                case BluetoothAdapter.STATE_ON:                    ...                    if (GattService.class.getSimpleName().equals(profile.getName())) {                        enableNativeWithGuestFlag();                    } private void enableNativeWithGuestFlag() {        boolean isGuest = UserManager.get(this).isGuestUser();        if (!enableNative(isGuest)) {            Log.e(TAG, "enableNative() returned false");        }    }

這樣就調用到了底層實現的native方法

3.3.2、其他協議啟動

上面3.3.1講解BluetoothAdapter#enable時,BluetoothManagerService代碼從BluetoothManagerService調用到bluetooh apk的Adapterservice最後一步BluetoothServiceConnection 回調MESSAGE_BLUETOOTH_SERVICE_CONNECTED信息,代碼從這裡接著開始。1、framwork調用到bluetooth apk裡

private class BluetoothHandler extends Handler {        ...        @Override        public void handleMessage(Message msg) {case MESSAGE_BLUETOOTH_SERVICE_CONNECTED: {                    IBinder service = (IBinder) msg.obj;                    try {                        mBluetoothLock.writeLock().lock();                        if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {                            mBluetoothGatt = IBluetoothGatt.Stub.asInterface(Binder.allowBlocking(service));                            continueFromBleOnState();                            break;                        }     private void continueFromBleOnState() {         ...         mBluetooth.onLeServiceUp();    }

BluetoothManagerService#enable講解最後這裡拿到了service。同時也是這裡的continueFromBleOnState,開起了bluetooth apk裡其他所有支持的profile的service。2、Adapterservice#startProfileServices
接著又是狀態機一頓切換,切換流程和上面狀態機一樣,這裡簡略

void onLeServiceUp() {        mAdapterStateMachine.sendMessage(AdapterState.USER_TURN_ON);    } private class TurningOnState extends BaseAdapterState {        @Override        public void enter() {            ...            mAdapterService.startProfileServices();        }void startProfileServices() {        Class[] supportedProfileServices = Config.getSupportedProfiles();        ...            setAllProfileServiceStates(supportedProfileServices, BluetoothAdapter.STATE_ON);        }    }private void setAllProfileServiceStates(Class[] services, int state) {        for (Class service : services) {            if (GattService.class.getSimpleName().equals(service.getSimpleName())) {                continue;            }            setProfileServiceState(service, state);        }    } private void setProfileServiceState(Class service, int state) {        Intent intent = new Intent(this, service);        intent.putExtra(EXTRA_ACTION, ACTION_SERVICE_STATE_CHANGED);        intent.putExtra(BluetoothAdapter.EXTRA_STATE, state);        startService(intent);    }

Config.getSupportedProfiles拿到的是配置文件中列出來支持的所有協議。然後全部把profile的狀態置為STATE_ON最後一個for循環遍歷startService開起所有支持的服務

bluetooth apk小結
1、bluetooth apk是藍牙功能實現的地方。
2、bluetooth apk裡的代碼主要靠adapterservice工作運行。
3、adapterservice提供了底層so庫的入口。也提供了供framework使用的方法。
4、adapterservice藍牙操作相關主要靠AdapterState狀態機來切換

3.4、SettingsLib

1、SettingsLib和bluetoothAdapter類似以API運用為主,這部分以表格方式
2、首先SettingsLib包含了很多功能,它的目的是封裝一些操作。專注服務於settings app。本文只對settingslib中bluetooth部分分析。本文開頭部分簡單介紹settingsLib的時候對路徑下的profile進行了表格統計,並說明了他們的功能。settingslib中的bletooth代碼除了這部分協議,剩下幾個LocalManager對藍牙的操作封裝處理。這個Manager我們也先以表格的形式簡單統計說明。

名稱簡單介紹LocalBluetoothAdapter絕大部分都是對BluetoothAdapter間接調用CachedBluetoothDeviceManager管理已配對設備列表BluetoothEventManager接收藍牙相關廣播和藍牙的一些回調,並根據UI操作執行到對應的事件LocalBluetoothProfileManager對外提供可用profile的訪問LocalBluetoothManager統一管理CachedBluetoothDeviceManager、LocalBluetoothProfileManager、BluetoothEventManager創建和獲取

有了前面三個部分的講解,settingslib的代碼看起來就很簡單了。接下來分開解析

3.4.1、LocalBluetoothAdapter裝飾者

LocalBluetoothAdapter用的是裝飾者模式,代理了BluetoothAdapter的一些方法,並擴展了極少功能。通篇LocalBluetoothAdapter的代碼除了代理外就把藍牙打開狀態傳給了LocalBluetoothProfileManager

public boolean enable() {        return mAdapter.enable();    } synchronized void setBluetoothStateInt(int state) {        mState = state;        if (state == BluetoothAdapter.STATE_ON) {            ...            if (mProfileManager != null) {                mProfileManager.setBluetoothStateOn();            }        }    }

3.4.2、CachedBluetoothDeviceManager配對設備

CachedBluetoothDeviceManager管理已連接設備,裡邊用兩個ArrayList一個Map來存儲。助聽器設備單獨用了一個list存儲。

對象作用List<CachedBluetoothDevice> mCachedDevices已配對設備List<CachedBluetoothDevice> mHearingAidDevicesNotAddedInCache助聽器列表供UI顯示final Map<Long, CachedBluetoothDevice> mCachedDevicesMapForHearingAids助聽器是兩個設備時,另一個設備存在這個list裡

下面是CachedBluetoothDeviceManager提供的方法列表

方法作用getCachedDevicesCopy拷貝已配對設備ListonDeviceDisappeared設備消失onDeviceNameUpdated設備名稱更新findDevice存儲的兩個list中查找addDevice添加設備到對應list/mapisPairAddedInCache是否在配對列表中getHearingAidPairDeviceSummary已配對助聽描述addDeviceNotaddedInMap添加到mapupdateHearingAidsDevices助聽設備刷新狀態後更新列表getName有名字返回名字,沒有名字返回mac地址clearNonBondedDevices從三個列表中移除沒有綁定過狀態的設備onScanningStateChanged開始掃描更新排序狀態onBtClassChanged藍牙設備描述變化onUuidChangeduuid變化onBluetoothStateChanged藍牙開關,關閉需清空列表,打卡需刷新信息onActiveDeviceChangedprofile是否存活onHiSyncIdChanged助聽設備類型變化getHearingAidOtherDevice獲得助聽設備hearingAidSwitchDisplayDevice助聽設備一對,選擇哪個顯示到UI列表onProfileConnectionStateChanged協議監聽刷新助聽設備列表onDeviceUnpaired取消配對,更新列表dispatchAudioModeChangedaudio狀態變化3.4.3、BluetoothEventManager處理Event變化

BluetoothEventManager接收藍牙相關廣播和藍牙的一些回調,並根據UI操作執行到對應的事件。
BluetoothEventManager設計思想也很簡單,就是監聽所有需要關心的藍牙廣播。收到狀態後把傳給CallBack或者其他Manager1、構造函數監聽廣播

BluetoothEventManager(LocalBluetoothAdapter adapter,            CachedBluetoothDeviceManager deviceManager, Context context) {        ...        // 藍牙開關        addHandler(BluetoothAdapter.ACTION_STATE_CHANGED, new AdapterStateChangedHandler());        // 藍牙連接        addHandler(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED,                new ConnectionStateChangedHandler());        // 藍牙發現廣播        addHandler(BluetoothAdapter.ACTION_DISCOVERY_STARTED, new ScanningStateChangedHandler(true));        addHandler(BluetoothAdapter.ACTION_DISCOVERY_FINISHED, new ScanningStateChangedHandler(false));        addHandler(BluetoothDevice.ACTION_FOUND, new DeviceFoundHandler());        addHandler(BluetoothDevice.ACTION_DISAPPEARED, new DeviceDisappearedHandler());        addHandler(BluetoothDevice.ACTION_NAME_CHANGED, new NameChangedHandler());        addHandler(BluetoothDevice.ACTION_ALIAS_CHANGED, new NameChangedHandler());        //配對        addHandler(BluetoothDevice.ACTION_BOND_STATE_CHANGED, new BondStateChangedHandler());        // 遠端設備描述信息        addHandler(BluetoothDevice.ACTION_CLASS_CHANGED, new ClassChangedHandler());        addHandler(BluetoothDevice.ACTION_UUID, new UuidChangedHandler());        addHandler(BluetoothDevice.ACTION_BATTERY_LEVEL_CHANGED, new BatteryLevelChangedHandler());        // 藍牙底座設備狀態,比如車載電源充電狀態        addHandler(Intent.ACTION_DOCK_EVENT, new DockEventHandler());        //藍牙協議開始活動廣播        addHandler(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED,                   new ActiveDeviceChangedHandler());        addHandler(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED,                   new ActiveDeviceChangedHandler());        addHandler(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED,                   new ActiveDeviceChangedHandler());        // 音頻策略,聯繫人        addHandler(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED,                new AudioModeChangedHandler());        addHandler(TelephonyManager.ACTION_PHONE_STATE_CHANGED,                new AudioModeChangedHandler());       ...    }

2、回調給監聽
廣播來了,就遍歷回調Callback,給manager設置狀態。以StateChanged舉例:

private class AdapterStateChangedHandler implements Handler {        public void onReceive(Context context, Intent intent,                BluetoothDevice device) {            int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,                                    BluetoothAdapter.ERROR);            // Reregister Profile Broadcast Receiver as part of TURN OFF            if (state == BluetoothAdapter.STATE_OFF)            {                context.unregisterReceiver(mProfileBroadcastReceiver);                registerProfileIntentReceiver();            }            // update local profiles and get paired devices            mLocalAdapter.setBluetoothStateInt(state);            // send callback to update UI and possibly start scanning            synchronized (mCallbacks) {                for (BluetoothCallback callback : mCallbacks) {                    callback.onBluetoothStateChanged(state);                }            }            // Inform CachedDeviceManager that the adapter state has changed            mDeviceManager.onBluetoothStateChanged(state);        }    }

**3、BluetoothCallback **
親切的BluetoothCallback ,我們監聽都是從這兒來監聽

public interface BluetoothCallback {    void onBluetoothStateChanged(int bluetoothState);    void onScanningStateChanged(boolean started);    void onDeviceAdded(CachedBluetoothDevice cachedDevice);    void onDeviceDeleted(CachedBluetoothDevice cachedDevice);    void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState);    void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state);    void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile);    void onAudioModeChanged();    default void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice,            int state, int bluetoothProfile) {    }}

3.4.4、LocalBluetoothProfileManager

LocalBluetoothProfileManager是統一管理settings支持的profile的地方,提供profile的訪問和狀態變化監聽。profile創建和綁定的流程在BluetoothManagerService部分已經分析。這些profile也是文章開頭部分列出settinglib中的profile。用了Map<String, LocalBluetoothProfile>mProfileNameMap 來存儲。

private A2dpProfile mA2dpProfile;    private A2dpSinkProfile mA2dpSinkProfile;    private HeadsetProfile mHeadsetProfile;    private HfpClientProfile mHfpClientProfile;    private MapProfile mMapProfile;    private MapClientProfile mMapClientProfile;    private HidProfile mHidProfile;    private HidDeviceProfile mHidDeviceProfile;    private OppProfile mOppProfile;    private PanProfile mPanProfile;    private PbapClientProfile mPbapClientProfile;    private PbapServerProfile mPbapProfile;    private final boolean mUsePbapPce;    private final boolean mUseMapClient;    private HearingAidProfile mHearingAidProfile;

創建的代碼都在打開藍牙的時候調用setBluetoothStateOn

void setBluetoothStateOn() {        if (mHidProfile == null) {        mHidProfile = new HidProfile(mContext, mLocalAdapter, mDeviceManager, this);        addProfile(mHidProfile, HidProfile.NAME,                BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED);       }       if (mPanProfile == null) {        mPanProfile = new PanProfile(mContext, mLocalAdapter);        addPanProfile(mPanProfile, PanProfile.NAME,                BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);       }       if (mHidDeviceProfile == null) {       mHidDeviceProfile = new HidDeviceProfile(mContext, mLocalAdapter, mDeviceManager, this);       addProfile(mHidDeviceProfile, HidDeviceProfile.NAME,                BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED);       }    ....    //等等其他profile創建

3.4.5、LocalBluetoothManager

LocalBluetoothManager是這個manager裡最簡單的了就是創建著幾個manager,方便對外獲取manager。

private LocalBluetoothManager(LocalBluetoothAdapter adapter, Context context) {        mContext = context;        mLocalAdapter = adapter;        mCachedDeviceManager = new CachedBluetoothDeviceManager(context, this);        mEventManager = new BluetoothEventManager(mLocalAdapter,                mCachedDeviceManager, context);        mProfileManager = new LocalBluetoothProfileManager(context,                mLocalAdapter, mCachedDeviceManager, mEventManager);        mEventManager.readPairedDevices();    }

SettingsLib小結
1、SettingsLib主要供settings使用,封裝一些操作。
2、四個部分(代理Adapter、管理配對設備、管理profile、監聽藍牙狀態)

四、寫在最後

1、通篇文章下來,我們可以看到Android藍牙的架構並不複雜。client提供對外接口,service通過綁定Bluetooth中Adapterservice對接上具體實現。最後settingslib封裝一些操作供settings使用更便捷。層次分明。不像其他系統service和別的系統service有很多相互作用操作。
2、框架層的講解為的是幫助大家對Android藍牙整體的理解。往細了講,service、Bluetooth、settings裡邊還有很多細節代碼可以扣。每個profile還有很具體的用法用例。
3、源碼真香

Read the fucking source code!

相關焦點

  • android啟動頁設計專題及常見問題 - CSDN
    轉載請註明出處:http://blog.csdn.net/wangjihuanghun/article/details/63255144啟動頁幾乎成為了每個app的標配,有些商家在啟動頁中增加了開屏廣告以此帶來更多的收入。
  • Android上玩玩Hook:Cydia Substrate實戰
    如果您想投稿、參與內容翻譯工作,或尋求近匠報導,請發送郵件至tangxy#csdn.net(請把#改成@)。 這就使我們希望通過一個程序改變其他程序的某些行為的想法不能直接實現,但是Hook的出現給我們開拓了解決此類問題的道路。當然,根據Hook對象與Hook後處理的事件方式不同,Hook還分為不同的種類,如消息Hook、API Hook等。
  • 約束布局不顯示 - CSDN
    參考:https://blog.csdn.net/murongbingxiao/article/details/78414248?
  • 一發不可收出自_stm32串口一發一接收 - CSDN
    5 個 Android 開發中比較常見的內存洩漏問題及解決辦法 - Android - 掘金在Android開發中,內存洩漏是比較常見的問題,有過一些Android編程經歷的童鞋應該都遇到過,但為什麼會出現內存洩漏呢?內存洩漏又有什麼影響呢? 在Android程序開發中,當一個對象已經不需要再使用了,本該被回收時,而另外一個正在使用的對象持有它...
  • 卡方檢驗結果分析專題及常見問題 - CSDN
    一般常見的卡方分析是2x2列聯表形式,例如服用某種藥物是否對死亡率有影響:(自己編的數據)簡單統計之後,得到總數和死亡率:原假設是:服藥組和未服藥組之間頻數無顯著差異(服藥對死亡率無影響)先計算自由度:(行數-1)*(列數-1)=1這個是卡方的計算公式,R代表實際值,T代表理論值,理論值需要進一步計算才可以知道。
  • arduino 低功耗模式專題及常見問題 - CSDN
    藍牙基礎知識1. 藍牙4.0和BLE的區分通常在了解一點藍牙的朋友看來,往往將BLE等同於藍牙4.0,其實不然。藍牙4.0是協議,4.0是協議版本號,藍牙4.0是2010年6月由SIG(Special Interest Group)發布的藍牙標準,它有2種模式:BLE(Bluetooth low energy)只能與4.0協議設備通信,適應節能且僅收發少量數據的設備(如家用電子);BR/EDR(Basic Rate / Enhanced Data Rate),向下兼容
  • f檢驗 matlab專題及常見問題 - CSDN
    15.71985 15.91986 15.71987 16.71988 15.31989 16.11990 16.2MATLAB實現參考網上多個代碼可得https://www.ilovematlab.cn/thread-246993-1-1.htmlhttps://blog.csdn.net
  • 卡方檢驗相關性專題及常見問題 - CSDN
    線性相關係數       我們已經知道了什麼是協方差以及協方差公式是怎麼來的,如果知道兩個變量 X 與 Y 的協方差與零的關係,我們就能推斷出 X 與 Y 是正相關、負相關還是不相關。那麼有一個問題:協方差數值大小是否代表了相關程度呢?也就是說如果協方差為 100 是否一定比協方差為 10 的正相關性強呢?
  • 藍牙電子菸方案原理框架圖
    打開APP 藍牙電子菸方案原理框架圖 發表於 2018-10-12 10:08:11 昇潤科技作為資深藍牙方案商自然不例外,通過在電子菸內增加藍牙功能,可以和手機建立連接,做一系列的設定,實現監測與控制。
  • 基於RFID與Android平臺的物品清點系統
    國內智能家居行業起步較晚,智能家居產品存在系統複雜、價格昂貴、不切實際等問題。智能家居企業所銷售的並不是產品,而是為特定的某部分人量身定做的一套完整的個性化服務,價格昂貴,是面向富人群體的消費,且系統設計複雜,產品也不夠多元化。這將不利於智能家居的長期發展[4]。  因此,在當前客戶個性化需求強烈的市場狀況下,開發適合大眾消費、操作方便的智能家居產品具有重要意義。
  • ref vue 獲取文本專題及常見問題 - CSDN
    ref的官網介紹https://cn.vuejs.org/v2/api/#ref需求在普通的js操作中,一般都是直接操作dom元素,但是對於Vue.js框架來說,一般是不允許直接操作dom元素的。那麼其實Vue.js框架提供了ref獲取dom元素,以及組件引用。
  • 2014 非常好用的開源 Android 測試工具
    每個工具都會有相應的簡短介紹,還有一些相關的資源。Android 測試工具列表是按照字母來排序的,最後還會介紹幾個不是特別活躍的 Android 測試相關的開源項目。OSChina URL: http://www.oschina.net/p/android-test-kit相關資源* Android application testing with the Android test framework – Tutorial* Espresso for Android is here!
  • Android SDK Document 框架導讀的翻譯和註解[7]——Intents and...
    0; i < list.size(); i++) { ResolveInfo info = list.get(i); Log.i("Test Intent---", info.activityInfo.name); } } AndroidManifest.xml中定義為: <activity android
  • c使用sql server專題及常見問題 - CSDN
    CI框架連接SQLserver 200R2首先,你必須修改讓你的SQLserver 允許使用ip連接!!!  這步至關重要!其次, 那就是配置CI框架的config/database.php文件了如下操作:$db['default'] = [ 'dsn' => '', 'hostname' => '192.168.1.110,1433', //埠號可寫可不寫 'username' => '用戶名,一般默認是sa', 'password' => '用戶密碼
  • Ubuntu下adb的使用及常見問題處理
    adb help或者adb正常情況下,可看到adb的版本信息,以及adb支持的相關指令和說明。二、adb使用常見問題處理1、no permissions (user in plugdev group; are your udev rules wrong?)
  • Android壓力測試Monkey工具
    最近在Android程序測試過程中接觸到了自動化測試方法,對其中的一些工具、方法和框架做了一些簡單的整理,其中包括android測試框架、CTS、Monkey、Monkeyrunner其它test tool等等。因接觸時間很短,很多地方有不足之處,希望能和大家多多交流和指點在這裡簡單對monkey做簡單的介紹吧。
  • 方差檢驗專題及常見問題 - CSDN
    4 相關統計量這裡涉及到的統計量:合併方差(加權方差):T:             自由度 : 5 問題總結無
  • python 顯著性水平專題及常見問題 - CSDN
    R2 = RSS/TSSprint(R2)'''0.987979715684'''T-Distribution統計測驗表明塔的傾斜程度與年份有關係,一個常見的統計顯著性測試是student t-test。這個測試的基礎是T分布,和正態分布很相似,都是鍾型但是峰值較低。
  • f p 線性回歸專題及常見問題 - CSDN
    對於一個樣本\(x_i\),它的輸出值是其特徵的線性組合:\[\begin{equation}f(x_i) = \sum_{m=1}^{p}w_m x_{im}+w_0={w}^T{x_i}\end{equation}\]線性回歸的目標是用預測結果儘可能地擬合目標label,用最常見的Least square作為loss function:\[\begin{equation}
  • c++ 槽函數專題及常見問題 - CSDN
    其實Qt也提供了它自己的信號和槽機制,那個是非常的靈活和好用的,但是它依賴於Qt的框架,所以退而求其次,選擇了Boost提供了signals2;signals2庫位於命名空間boost::signals2中,為了使用它,需要包含頭文件<boost/signals2.hpp>;信號(Signal)signal