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

2020-12-25 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流程。聯繫人/通話記錄依賴原生contactsprovider
music音樂相關,走原生Mediasession
settings藍牙設置相關,用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面向連接,套接字,基於RFCOMM

2.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拷貝已配對設備List
onDeviceDisappeared設備消失
onDeviceNameUpdated設備名稱更新
findDevice存儲的兩個list中查找
addDevice添加設備到對應list/map
isPairAddedInCache是否在配對列表中
getHearingAidPairDeviceSummary已配對助聽描述
addDeviceNotaddedInMap添加到map
updateHearingAidsDevices助聽設備刷新狀態後更新列表
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
    代碼來源於Android P,本文相關代碼:client:frameworks/base/core/java/android/bluetooth/*system/bt/binder/android/bluetooth/**.aidlservie:framework/base/services/core/java/com/android/server/BluetoothService.java
  • android啟動頁設計專題及常見問題 - CSDN
    轉載請註明出處:http://blog.csdn.net/wangjihuanghun/article/details/63255144啟動頁幾乎成為了每個app的標配,有些商家在啟動頁中增加了開屏廣告以此帶來更多的收入。
  • android 不同大小的屏幕專題及常見問題 - CSDN
    轉載請註明出處:http://blog.csdn.net/guolin_blog/article/details/8830286原文地址為:http://developer.android.com/training/multiscreen/screensizes.html
  • android tv放大專題及常見問題 - CSDN
    ;import android.os.Bundle;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.view.WindowManager;import android.widget.BaseAdapter;import android.widget.ImageView
  • android布局詳解專題及常見問題 - CSDN
    <include android:id=」@+id/cell3 layout=」@layout/workspace_screen」 /></LinearLayout>  上面的代碼中的<include>標籤還使用了一個android:id屬性,實際上,該屬性指定的是workspace_screen.xml布局文件中的根節點的android
  • android 從後臺啟動頁面專題及常見問題 - CSDN
    layout.xml首先創建此頁面的布局文件:<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent">
  • android 復用 布局優化專題及常見問題 - CSDN
    1、布局重用<include /> <include />標籤能夠重用布局文件,簡單的使用如下:     <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"           android:orientation
  • android 進度條顯示時間專題及常見問題 - CSDN
    定義一個attrs.xml自定義CircleProgressViewpackage com.sample.circleprogressview.widget;import android.animation.ValueAnimator;import android.content.Context
  • android通過代碼實現的多布局專題及常見問題 - CSDN
    但是這樣就遇到了很多問題。 首先是SeekBar設置setProgressDrawable問題。因為我們的是視頻播放器,所以這個SeekBar需要有背景、緩衝進度和播放進度,最好的方法就是用layer-list 的xml布局實現,類似這樣:<?xml version="1.0" encoding="utf-8"?
  • android app被殺原因專題及常見問題 - CSDN
    分析長按HOME鍵清理App最終會執行到ActivityManagerService.cleanUpRemovedTaskLocked方法中,ActivityManagerService類在文件"frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java"中,
  • android 首次打開判斷專題及常見問題 - CSDN
    前言用真機運行appium代碼,首次打開app有的手機會出現權限彈窗問題,一般這種彈窗都是在引導頁前面或者引導頁後面出現。權限彈窗上面的按鈕都是固定的,只需要定位到「始終允許」按鈕,點擊確定就可以了。還有一個問題是這種彈窗的個數不確定,有的app是2個有的是3個,為了解決這個問題,可以專門寫個判斷方法。
  • Android經典藍牙開發簡介
    此外本文只涉及經典藍牙(Classic Bluetooth)的開發,並不涉及低功耗藍牙(BLE)的開發。2.基礎關於經典藍牙(以下簡稱藍牙)開發所用到的API都來自於android.bluetooth包中,本部分主要介紹相關類的概要說明。2.1BluetoothAdapterBluetoothAdapter類的對象代表本地的藍牙適配器。
  • android 服務啟動後專題及常見問題 - CSDN
    (ActivityManagerService.this) { mDidUpdate = true; } showBootMessage(mContext.getText( R.string.android_upgrading_complete
  • android中實例化類專題及常見問題 - CSDN
    轉自:http://www.android123.com.cn/androidkaifa/687.html三、在JNI中構造和實例化Java類 public class AndroidJniDemo4{ public static native
  • android 傳統藍牙開發 (附示例源碼)
    作者QiaoJimhttp://blog.csdn.net/qiao_jim/article/details/73008695
  • android 前攝像頭對焦專題及常見問題 - CSDN
    * * Rotate, scale and translate touch rectangle using matrix configured in* {@link SurfaceHolder.Callback#surfaceChanged(android.view.SurfaceHolder, int, int, int)}*/
  • android 自定義view大小專題及常見問題 - CSDN
    ScrollView嵌套ListView問題?layout系統為什麼要有layout過程?layout過程都幹了點什麼事?draw系統為什麼要有draw過程?draw過程都幹了點什麼事?-- 使用系統預置的屬性 -->        <attr name="android:layout_gravity"/>    </declare-styleable></resources>繼承MarginLayoutpublic static class LayoutParams extends
  • android audio 焦點專題及常見問題 - CSDN
    我們android系統裡面會安裝各種多媒體軟體,如果不制定一個有效合理的規則,各個應用各自為政,那麼可能就會出現各種播放器、軟體的混音。音頻焦點機制規定某一時刻只能有一個應用獲取到聲音的焦點,這個時候就可以發出聲音。當然,在這個應用獲取到焦點之前,需要通知其他所用的應用失去焦點。
  • androidaudio焦點專題及常見問題 - CSDN
    我們android系統裡面會安裝各種多媒體軟體,如果不制定一個有效合理的規則,各個應用各自為政,那麼可能就會出現各種播放器、軟體的混音。音頻焦點機制規定某一時刻只能有一個應用獲取到聲音的焦點,這個時候就可以發出聲音。當然,在這個應用獲取到焦點之前,需要通知其他所用的應用失去焦點。
  • bootstrap 寬度 自適應布局專題及常見問題 - CSDN
    一共5種: [1]float [2]inline-block [3]table [4]absolute [5]flex- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -思路一: float說起兩列布局,最常見的就是使用