https://blog.csdn.net/zrf1335348191/article/details/51734102

在了解蓝牙源码之前,你需要提前熟悉Preference相关的类http://122.152.205.50/wordpress/?p=4870

一、蓝牙的入口界面

/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothSettings.java

  • BluetoothDeviceFilter
    设备过滤器,用来匹配某种类型的设备。

  • BluetoothEnabler.java
    对蓝牙的开关逻辑作一层封装

  • DeviceListPreferenceFragment
    在onCreate方法里获取LocalBluetoothManager的单例

mLocalManager = Utils.getLocalBtManager(getActivity());

设备列表,调用BluetoothDevicePreference类进行设备的配对、连接与断开等操作。
BluetoothSettings继承自这个类。

一个CachedBluetoothDevice创建一个BluetoothDevicePreference,也就是设备列表的Item,并用Map建立之间的映射,方便维护。
    public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
        ...
        createDevicePreference(cachedDevice);
    }

    void createDevicePreference(CachedBluetoothDevice cachedDevice) {
        if (mDeviceListGroup == null) {
            Log.w(TAG, "Trying to create a device preference before the list group/category "
                    + "exists!");
            return;
        }

        BluetoothDevicePreference preference = new BluetoothDevicePreference(
                getActivity(), cachedDevice);

        initDevicePreference(preference);
        mDeviceListGroup.addPreference(preference);
        mDevicePreferenceMap.put(cachedDevice, preference);
    }
  • BluetoothDevicePreference.java
    调用CachedBluetoothDevice的方法,进行配对、连接、取消连接、设备信息展示等操作。
 void onClicked() {
        int bondState = mCachedDevice.getBondState();

        if (mCachedDevice.isConnected()) {
            //取消连接
            askDisconnect();
        } else if (bondState == BluetoothDevice.BOND_BONDED) {
            //连接
            mCachedDevice.connect(true);
        } else if (bondState == BluetoothDevice.BOND_NONE) {
            //配对
            pair();
        }
 }

获取设备信息绑定Preference

public void onDeviceAttributesChanged() {
        /*
         * The preference framework takes care of making sure the value has
         * changed before proceeding. It will also call notifyChanged() if
         * any preference info has changed from the previous value.
         */
        //Preference标题,也就是设备的名称。
        setTitle(mCachedDevice.getName());

        //Preference副标题,也就是设备的连接状态。
        int summaryResId = mCachedDevice.getConnectionSummary();
        if (summaryResId != 0) {
            setSummary(summaryResId);
        } else {
            setSummary(null);   // empty summary for unpaired devices
        }

        //Preference图标,也就是设备类型。
        int iconResId = getBtClassDrawable();
        if (iconResId != 0) {
            setIcon(iconResId);
        }

        // Used to gray out the item
        setEnabled(!mCachedDevice.isBusy());

        // This could affect ordering, so notify that
        notifyHierarchyChanged();
    }

打开设备详情弹框

源码追踪:

  • CachedBluetoothDevice
    官方说明:CachedBluetoothDevice represents a remote Bluetooth device. It contains
    attributes of the device (such as the address, name, RSSI, etc.) and
    functionality that can be performed on the device (connect, pair, disconnect,
    etc.).

    一个CachedBluetoothDevice对应多种协议
    存储的各个协议及协议的连接状态等信息

    设备的连接状态:getConnectionSummary()

    通过研究这个方法可以发现,判断设备是否连接实际是判断设备支持的协议的连接状态,至少有一种协议是连接的,设备就是处于连接状态。

  • BluetoothEventManager
    源码位置:
    /frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
    此类主要是封装了蓝牙相关的广播注册与监听,然后将相关状态绑定到CachedBluetoothDevice上。

    重要动作1 :通过监听BluetoothDevice.ACTION_FOUND广播发现设备,通过CachedBluetoothDeviceManager将设备保存起来并调用BluetoothEventManager的dispatchDeviceAdded回调事件。

 public CachedBluetoothDevice addDevice(LocalBluetoothAdapter adapter,
                                           LocalBluetoothProfileManager profileManager,
                                           BluetoothDevice device) {
        CachedBluetoothDevice newDevice = new CachedBluetoothDevice(mContext, adapter,
                profileManager, device);
        synchronized (mCachedDevices) {
            mCachedDevices.add(newDevice);
            mBtManager.getEventManager().dispatchDeviceAdded(newDevice);
        }
        return newDevice;
    }

二、核心类

  • LocalBluetoothManager
    源码位置:
    /frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothManager.java
    官方说明:
    LocalBluetoothManager provides a simplified interface on top of a subset of the Bluetooth API.
    此类会创建几个十分重要的类的实例:LocalBluetoothAdapter、CachedBluetoothDeviceManager、LocalBluetoothProfileManager
 private final LocalBluetoothAdapter mLocalAdapter;
 private final CachedBluetoothDeviceManager mCachedDeviceManager;
 /** The Bluetooth profile manager. */
 private final LocalBluetoothProfileManager mProfileManager;
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);
    }
  • LocalBluetoothAdapter
    源码位置:
    /frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
    官方释义:
    LocalBluetoothAdapter provides an interface between the Settings app and the functionality of the local {@link BluetoothAdapter}, specifically those related to state transitions of the adapter itself.
    主要是对BluetoothAdapter做了封装

  • CachedBluetoothDeviceManager
    源码位置:
    /frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
    官方释义:
    CachedBluetoothDeviceManager manages the set of remote Bluetooth devices
    主要是对远程的蓝牙设备进行管理

  • LocalBluetoothProfileManager
    源码位置:
    /frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
    官方释义:
    LocalBluetoothProfileManager provides access to the LocalBluetoothProfile objects for the available Bluetooth profiles.

依赖于LocalBluetoothAdapter、CachedBluetoothDeviceManager、BluetoothEventManager。

LocalBluetoothProfileManager(Context context,
            LocalBluetoothAdapter adapter,
            CachedBluetoothDeviceManager deviceManager,
            BluetoothEventManager eventManager)

根据uuids解析出对应的协议(每种协议的ACTION都是唯一的),然后通过BluetoothEventManager注册监听各个协议的连接状态的变化,最后把结果绑定到CachedBluetoothDevice上。

void updateLocalProfiles(ParcelUuid[] uuids) {
...
}

三、已配对与可配对列表是如何更新的?

这个方法会在多处触发:onResume、蓝牙开关状态变化、设备绑定状态变化。

BluetoothSettings.java

 private void updateContent(int bluetoothState) {
        final PreferenceScreen preferenceScreen = getPreferenceScreen();
        int messageId = 0;

        switch (bluetoothState) {
            case BluetoothAdapter.STATE_ON:
                ...
                // Paired devices category
                if (mPairedDevicesCategory == null) {
                    mPairedDevicesCategory = new PreferenceCategory(getActivity());
                } else {
                    mPairedDevicesCategory.removeAll();
                }
                addDeviceCategory(mPairedDevicesCategory,
                        R.string.bluetooth_preference_paired_devices,
                        BluetoothDeviceFilter.BONDED_DEVICE_FILTER, true);
                ...

                // Available devices category
                if (mAvailableDevicesCategory == null) {
                    mAvailableDevicesCategory = new BluetoothProgressCategory(getActivity());
                    mAvailableDevicesCategory.setSelectable(false);
                } else {
                    mAvailableDevicesCategory.removeAll();
                }
                addDeviceCategory(mAvailableDevicesCategory,
                        R.string.bluetooth_preference_found_devices,
                        BluetoothDeviceFilter.UNBONDED_DEVICE_FILTER, mInitialScanStarted);

                if (!mInitialScanStarted) {
                    startScanning();
                }

                if (mMyDevicePreference == null) {
                    mMyDevicePreference = new Preference(getActivity());
                }

                mMyDevicePreference.setSummary(getResources().getString(
                        R.string.bluetooth_is_visible_message, mLocalAdapter.getName()));
                mMyDevicePreference.setSelectable(false);
                preferenceScreen.addPreference(mMyDevicePreference);

                return; // not break
                ...
        }
    }

界面是如何监听“蓝牙开关状态变化”、“设备绑定状态变化”的?

DeviceListPreferenceFragment的onResume方法会调用BluetoothEventManager的registerCallback方法注册监听。

 @Override
    public void onResume() {
        ...
        mLocalManager.getEventManager().registerCallback(this);
        ...
    }

而前面已经说到,BluetoothEventManager封装了蓝牙的各种广播。

四、规律

设置应用蓝牙相关的类,基本都是Local开头的,这个与应用开发十分不同,它们位于 /frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/下,通过分析SettingsLib下的mk文件可知,整个SettingsLib下的类最终会打包成SettingsLib.jar,而这个jar包应用层是无法访问到的,因为android sdk里并不会将这个jar打包进去。

五、普通应用开发

由于SDK里没有SettingsLib.jar,无法使用CachedBluetoothDevice那些简便的方法来操作远程设备,只能通过广播和BluetoothDevice类及BluetoothProfile相关的协议类来操作设备,而且BluetoothDevice类的很多方法都是系统方法或者hide方法,需要进行反射。

  • 配对
  @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    @Override
    public void pair(Context context, BluetoothDevice device) {
        BtUtil.cancelDiscovery();
        device.createBond();
    }
  • 取消配对
@Override
    public void unpair(Context context, BluetoothDevice device) {
        int state = device.getBondState();

        if (state == BluetoothDevice.BOND_BONDING) {
//            device.cancelBondProcess();
            ReflectManger.invokeMethod(BluetoothDevice.class, device, "cancelBondProcess", null, null);
        }

        if (state != BluetoothDevice.BOND_NONE) {
            boolean successful = (boolean) ReflectManger.invokeMethod(BluetoothDevice.class, device, "removeBond", null, null);
            if (successful) {
                Log.d(TAG, "unpair: removeBond success ");
            } else {
                Log.d(TAG, "unpair: removeBond failure ");
            }
        }
    }
  • 连接、取消连接、是否连接
    连接、取消连接、是否连接等需要结合具体的协议,所以需要自行封装。通过BluetoothAdapter的getProfileProxy方法获取对应协议的BluetoothProfile,然后反射其相关方法实现对设备的操作。

  • 获取和设置远程设备的别名
    设置别名:

Method method = device.getClass().getMethod("setAlias", String.class);
    if(method != null) {
        method.invoke(device, "new_device_name");
    }

获取别名:

 Method method = device.getClass().getMethod("getAlias");
    if(method != null) {
        deviceAlias = (String)method.invoke(device);
    }

有的设备获取别名为null,为什么?

分类: 蓝牙

0 条评论

发表回复

您的电子邮箱地址不会被公开。