【Settings开发】蓝牙模块(二)

  • Post author:
  • Post category:其他




蓝牙列表点击

在蓝牙设置页面,打开搜索到的蓝牙设备是以BluetoothDevicePreference的形式展示在PreferenceScreen中,当我们点击蓝牙列表中的蓝牙设备时,需根据此设备状态的不同做出不同的响应。

    void onClicked() {
        int bondState = mCachedDevice.getBondState();
        //获取蓝牙设备的连接状态
        if (mCachedDevice.isConnected()) {
            askDisconnect();
            //匹配状态---历史配对设备
        } else if (bondState == BluetoothDevice.BOND_BONDED) {
            //调用各自的connect
            mCachedDevice.connect(true);
        } else if (bondState == BluetoothDevice.BOND_NONE) {
            //开始配对流程
            pair();
        }
    }

在以上代码中,可以很清晰的看到根据被点击蓝牙设备状态的不同,触发了不同的处理方式。

  • 当前蓝牙设备已经处于连接状态(通过BluetoothProfile的getConnectionStatus判断)。如果已经处于连接状态则弹出对话框询问用户是否取消当前蓝牙设备的连接(内部主要通过取消当前已连接蓝牙设备的所有profile连接,在取消profile连接时为防止触发profile自动连接功能,通过BluetoothProfile的setPriority()置为PRIORITY_ON关掉自动连接)。
    boolean isConnected() {
        for (LocalBluetoothProfile profile : mProfiles) {
            int status = getProfileConnectionState(profile);
            if (status == BluetoothProfile.STATE_CONNECTED) {
                return true;
            }
        }

        return false;
    }
  • 如果当前设备是历史配对设备(即通过BOND状态判断如果是已配对过,则在本地应该保留有pin/passkey),则尝试通过profile连接蓝牙设备。
    synchronized void connectInt(LocalBluetoothProfile profile) {
        if (!ensurePaired()) {
            return;
        }
        if (profile.connect(mDevice)) {
            if (Utils.D) {
                Log.d(TAG, "Command sent successfully:CONNECT " + describe(profile));
            }
            return;
        }
        Log.i(TAG, "Failed to connect " + profile.toString() + " to " + mName);
    }
  • 如果当前蓝牙设备未曾配对过(BOND_NONE),则进入配对流程。在配对时需要如果发现当前正在执行扫描,则需取消扫描任务,并在配对成功后实现自动连接。
    boolean startPairing() {
        // Pairing is unreliable while scanning, so cancel discovery
        if (mLocalAdapter.isDiscovering()) {
            mLocalAdapter.cancelDiscovery();
        }

        if (!mDevice.createBond()) {
            return false;
        }

        mConnectAfterPairing = true;  //配对ok后自动连接
        return true;
    }

在配对过程蓝牙设备对应的action变化为:

graph LR
BOND_NONE-->BOND_BONDING
BOND_BONDING-->BOND_BONDED

配对状态在蓝牙设置模块中是在BluetoothEventManager中统一进行的,当device配对状态发生变化,则在BondStateChangeHandler的onReceive中调用device的onBondingStateChanged方法。

public void onReceive(Context context, Intent intent,
                BluetoothDevice device) {
            ...
            int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
                                               BluetoothDevice.ERROR);
            CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
            ...
            cachedDevice.onBondingStateChanged(bondState);
            ...
        }

在onBondingStateChanged(位于CachedBluetoothDevice类)方法中则对已绑定和未绑定这两种情形进行了处理。当蓝牙处于绑定状态时则调用connect方法去连接,在connect中通过层层调用,最终执行了profile的connect方法,那么profile又是什么呢?,这个问题在下边解答。

    void onBondingStateChanged(int bondState) {
        if (bondState == BluetoothDevice.BOND_NONE) {
            mProfiles.clear();//清除所有基于此设备的profile协议
            mConnectAfterPairing = false;  //取消自动连接
            setPhonebookPermissionChoice(ACCESS_UNKNOWN);//设置蓝牙设备获取本机通讯录的权限为unknown
            setMessagePermissionChoice(ACCESS_UNKNOWN);//设置蓝牙设备获取本机消息的权限为unknown
            mMessageRejectionCount = 0;
            saveMessageRejectionCount();//重置设备拒绝权限次数
        }
        //刷新该设备UI
        refresh();
        //如果当前蓝牙设备处于已配对状态
        if (bondState == BluetoothDevice.BOND_BONDED) {
            //BluetoothDock?
            if (mDevice.isBluetoothDock()) {
                onBondingDockConnect();//内部也是调用的connect方法
            } else if (mConnectAfterPairing) {
                connect(false);
            }
            //自动连接设为false。
            mConnectAfterPairing = false;
        }
    }



蓝牙接收到配对请求

蓝牙配对分为:

  • 本机请求配对其他设备。主动配对上边已说明,即通过调用BluetoothDevice的createBond方法进行。
  • 本机接收到其他设备的配对请求。关于本机接收到其他蓝牙设备的蓝牙配对的action为ACTION_PAIRING_REQUEST(配对请求)和ACTION_PAIRING_CANCEL(配对取消)。对应的处理的类为BluetoothPairingRequest。
    <receiver android:name=".bluetooth.BluetoothPairingRequest">
        <intent-filter>
            <action android:name="android.bluetooth.device.action.PAIRING_REQUEST" />
            <action android:name="android.bluetooth.device.action.PAIRING_CANCEL" />
            <action android:name="android.bluetooth.device.action.BOND_STATE_CHANGED" />
        </intent-filter>
    </receiver>

在BluetoothPairingRequest中接收到配对请求时,根据蓝牙VARIANT类型取出其中的pairingKey并根据当前屏幕状态及是否处于蓝牙设置页面决定是弹窗形式还是通知形式处理。

在BluetoothPairingRequest中接收到配对取消时,则取消显示在通知栏中的配对通知。需要说明的是,用户点击此通知时也会进行弹窗处理(其实就是上边intent的pending)。

在BluetoothPairingRequest中接收到绑定状态变化时,如果不是处于BOND_BONDED状态,则同样取消显示在通知栏中的配对通知。

蓝牙配对弹窗BluetoothPairingDialog继承自AlertActivity,其内部主要根据从BluetoothPairingDialog传过来的安全配对类型type显示不同的Dialog。安全配对类型分为数字比较(Numeric Comparison)、使用简单(Just Works)、 带外(Out Of Band)、秘钥接入(Passkey Entry)。在弹窗中分别对这四种type创建不同的弹窗view。同时从onCreate对pin/passkey的解析中可知,pin码为4位数而passkey则是6位数。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Intent intent = getIntent();
        //配对action检查
        if (!intent.getAction().equals(BluetoothDevice.ACTION_PAIRING_REQUEST))
        {
            Log.e(TAG, "Error: this activity may be started only with intent " +
                  BluetoothDevice.ACTION_PAIRING_REQUEST);
            finish();
            return;
        }

        //此处如果为空则表明本机蓝牙功能不可用
        mBluetoothManager = LocalBluetoothManager.getInstance(this);
        if (mBluetoothManager == null) {
            Log.e(TAG, "Error: BluetoothAdapter not supported by system");
            finish();
            return;
        }
        mCachedDeviceManager = mBluetoothManager.getCachedDeviceManager();

        //需要配对的设备
        mDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
        //配对类型
        mType = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.ERROR);
        /**
         * 安全简单配对采用了四个关联的模型,这四个模式是
         * 数字比较(Numeric Comparison),
         * 使用简单(Just Works),
         * 带外(Out Of Band),
         * 秘钥接入(Passkey Entry
         */
        switch (mType) {
            //提示用户输入pin码或passkey   ------秘钥接入
            case BluetoothDevice.PAIRING_VARIANT_PIN:
            case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
                createUserEntryDialog();
                break;
            //提示用户确定显示在屏幕上的passkey   ------使用简单
            case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
                int passkey =
                    intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, BluetoothDevice.ERROR);
                if (passkey == BluetoothDevice.ERROR) {
                    Log.e(TAG, "Invalid Confirmation Passkey received, not showing any dialog");
                    return;
                }
                mPairingKey = String.format(Locale.US, "%06d", passkey);
                createConfirmationDialog();
                break;
            //用户将被提示接受或拒绝传入的配对请求。  ------带外数据
            case BluetoothDevice.PAIRING_VARIANT_CONSENT:
            //用户将被提示接受或拒绝传入的OOB配对请求。---out of band(带外数据)
            case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
                createConsentDialog();
                break;
            //用户将被提示输入显示在屏幕上的passkey/pin   ------数字比较
            case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
            case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
                int pairingKey =
                    intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, BluetoothDevice.ERROR);
                if (pairingKey == BluetoothDevice.ERROR) {
                    Log.e(TAG, "Invalid Confirmation Passkey or PIN received, not showing any dialog");
                    return;
                }
                if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY) {
                    mPairingKey = String.format("%06d", pairingKey);
                } else {
                    mPairingKey = String.format("%04d", pairingKey);
                }
                createDisplayPasskeyOrPinDialog();
                break;

            default:
                Log.e(TAG, "Incorrect pairing type received, not showing any dialog");
        }

        /*
         * Leave this registered through pause/resume since we still want to
         * finish the activity in the background if pairing is canceled.
         */
        registerReceiver(mReceiver, new IntentFilter(BluetoothDevice.ACTION_PAIRING_CANCEL));
        registerReceiver(mReceiver, new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED));
    }

正如BluetoothPairingRequest中所声明的那样,携带秘钥的type类型只有PAIRING_VARIANT_PASSKEY_CONFIRMATION、PAIRING_VARIANT_DISPLAY_PASSKEY、PAIRING_VARIANT_DISPLAY_PIN,而后两种只在低版本的蓝牙协议中出现。而且注册了配对取消与BOND状态监听,当远程蓝牙设备取消配对或者当前处于BOND_BONDED|BOND_NONE时就关闭弹窗。

当用户点击弹窗时,如果点击确认按钮则进入onPair。点击取消则弹窗消失同时进入onCancel。

    public void onClick(DialogInterface dialog, int which) {
        switch (which) {
            //确认按钮
            case BUTTON_POSITIVE:
                //本地并无passkey/pin,需要远程用户告知秘钥的弹窗类型中才有mPairingView
                if (mPairingView != null) {
                    onPair(mPairingView.getText().toString());
                } else {
                    onPair(null);
                }
                break;
            //弹窗消失时--取消按钮
            case BUTTON_NEGATIVE:
            default:
                onCancel();
                break;
        }
    }

在onPair中,如果本地没有pin,需要远程提供秘钥的PAIRING_VARIANT_PIN,则把用户填入的内容转换为utf-8格式的byte数组(此数组的长度需在16之内)通过BluetoothDevice的setPin方法传给目标蓝牙设备;如果如果是PAIRING_VARIANT_PASSKEY,则是本地没有passkey,需远程提供秘钥,则把用户填入的值转换为int值通过BluetoothDevice的setPasskey方法传给目标蓝牙设备;如果仅是需要用户确认配对,则调用setPairingConfirmation(true)。

    private void onPair(String value) {
        //允许访问本地通讯录
        allowPhonebookAccess();

        switch (mType) {
            //本地无pin,需远程设备提供秘钥
            case BluetoothDevice.PAIRING_VARIANT_PIN:
                byte[] pinBytes = BluetoothDevice.convertPinToBytes(value);
                if (pinBytes == null) {
                    return;
                }
                mDevice.setPin(pinBytes);
                break;
            //本地无passkey,需远程设备提供秘钥
            case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
                int passkey = Integer.parseInt(value);
                mDevice.setPasskey(passkey);
                break;
            //用户确定显示在屏幕上的passkey
            case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
            //用户接受传入的配对请求
            case BluetoothDevice.PAIRING_VARIANT_CONSENT:
                mDevice.setPairingConfirmation(true);
                break;
            //???此处感觉应该进行比对
            case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
            case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
                // Do nothing.
                break;
            //out of band
            case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
                mDevice.setRemoteOutOfBandData();
                break;

            default:
                Log.e(TAG, "Incorrect pairing type received");
        }
    }

    private void onCancel() {
        mDevice.cancelPairingUserInput();
    }

当用户点击已经建立连接的蓝牙设备时,需要询问是否取消连接,篇头处的askDisconnect方法,其内部会调用具体建立连接的profile的disconnect方法取消连接。

    void disconnect() {
        for (LocalBluetoothProfile profile : mProfiles) {
            disconnect(profile);
        }
        // Disconnect  PBAP server in case its connected
        // This is to ensure all the profiles are disconnected as some CK/Hs do not
        // disconnect  PBAP connection when HF connection is brought down
        PbapServerProfile PbapProfile = mProfileManager.getPbapProfile();
        if (PbapProfile.getConnectionStatus(mDevice) == BluetoothProfile.STATE_CONNECTED)
        {
            PbapProfile.disconnect(mDevice);
        }
    }

当用户点击历史配对列表(即bond状态为BOND_BONDED)时,会弹出弹框询问是否忘记当前蓝牙设备。如果用户点击忘记按钮,则进入BluetoothDevice的unpair流程。

    void unpair() {
        int state = getBondState();
        //如果当前正在绑定中则取消绑定
        if (state == BluetoothDevice.BOND_BONDING) {
            mDevice.cancelBondProcess();
        }
        //调用device的removeBond方法取消配对。
        if (state != BluetoothDevice.BOND_NONE) {
            final BluetoothDevice dev = mDevice;
            if (dev != null) {
                final boolean successful = dev.removeBond();
                if (successful) {
                    if (Utils.D) {
                        Log.d(TAG, "Command sent successfully:REMOVE_BOND " + describe(null));
                    }
                } else if (Utils.V) {
                    Log.v(TAG, "Framework rejected command immediately:REMOVE_BOND " +
                            describe(null));
                }
            }
        }
    }



BluetoothClass

BluetoothClass描述了蓝牙设备的通用特性及其能提供的服务功能。可通过BluetoothDevice的getBluetoothClass()或监听ACTION_CLASS_CHANGED广播获知蓝牙设备的类型变化。

在其内部类中,Device则表示蓝牙设备的各种类别,Service则描述当前蓝牙设备所能提供的服务。一个蓝牙设备可能有零或多个Service服务功能,但只能有一个确定的Device类型。

获取BluetoothDevice所能提供的服务可通过BluetoothClass的hasService(int)判断,如果返回true则代表此BluetoothClass能够提供此项服务。获取BluetoothDevice的设备类型可通过BluetoothClass的getDeviceClass(),其返回值在内部类Device中定义,当然,为了节省代码量,可先通过getMajorDeviceClass()获取其所属主类型,在通过getDeviceClass获取其所在主类中具体的设备类型。

  • Service 服务类型

    • LIMITED_DISCOVERABILITY,有限可发现模式。
    • POSITIONING,定位服务。
    • NETWORKING,网络服务,如LAN、点对点等。
    • RENDER,渲染服务,如打印、扬声器等。
    • CAPTURE,捕捉服务,如扫描仪、麦克风等。
    • OBJECT_TRANSFER,对象传输服务。
    • AUDIO,音频服务,如扬声器、麦克风、耳机等。
    • TELEPHONY,电话服务,如蜂窝电话、耳机等。
    • INFORMATION,信息服务。
  • Device 设备类型

    • MISC,其他杂项设备。
    • COMPUTER,电脑设备,如台式机、笔记本、PAD等。
    • PHONE,电话设备,如蜂窝电话、智能电话等。
    • NETWORKING,网络设备。
    • AUDIO_VIDEO,音视频设备,如耳机、VCR、摄像头等。
    • PERIPHERAL,计算机外围设备,如鼠标、键盘等。
    • IMAGING,成像设备,如打印机、扫描仪等。
    • WEARABLE,可穿戴设备,如手表、眼镜等。
    • TOY 玩具,如机器人、玩具车等。
    • HEALTH,健康设备,如血压、温度、脉搏等蓝牙设备。
    • UNCATEGORIZED,未分类。



Android蓝牙的profile

以下是通过BluetoothAdapter.getProfileProxy()可获取到的profile。

  • HEADSET,蓝牙耳机协议规范。具有振铃,接听电话,挂断和调节音量的能力。
  • A2DP,蓝牙音频传输协议规范,此处是手机作为音频输出方(source)。
  • A2DP_SINK,蓝牙音频传输协议规范,此处则是手机作为音频输入方(sink)。
  • AVRCP_CONTROLLER,远程控制协议规范,此处作为控制方,充当遥控器。
  • INPUT_DEVICE,蓝牙输入设备协议规范。
  • PAN,个人局域网。
  • HEALTH,蓝牙健康设备传输协议规范,如血压计等。
  • MAP,message access,设备间短信息交换协议规范。
  • HEADSET_CLIENT,主要是蓝牙电话client端协议规范。
  • SAP,sim access profile,此配置文件允许具有内置GSM收发器的车载电话等设备连接到蓝牙手机中的SIM卡,因此车载电话本身不需要单独的SIM卡。
  • PBAP_CLIENT,电话簿传输client端协议规范。
  • MAP_CLIENT,短信传输client端协议规范。
  • INPUT_HOST。

不同的profile具有对应的uuid标识此配置协议的唯一性,在android中可通过BluetoothAdapter.getUuids()获取当前设备正在使用的profile规范。

设置应用中监听profile变化是通过LocalBluetoothProfileManager完成的,在其构造方法中,先通过getUuids获取当前正在使用的profile协议并在updateLocalProfiles方法中监听对应的profile的变化(ACTION_CONNECTION_STATE_CHANGED)。当然,构造中也默认监听一些profile如人机接口、短信接入、个人局域网等profile。

    private final Map<String, LocalBluetoothProfile>
            mProfileNameMap = new HashMap<String, LocalBluetoothProfile>();

    LocalBluetoothProfileManager(Context context,
            LocalBluetoothAdapter adapter,
            CachedBluetoothDeviceManager deviceManager,
            BluetoothEventManager eventManager) {
        mContext = context;

        mLocalAdapter = adapter;
        mDeviceManager = deviceManager;
        mEventManager = eventManager;
        // pass this reference to adapter and event manager (circular dependency)
        mLocalAdapter.setProfileManager(this);
        mEventManager.setProfileManager(this);

        ParcelUuid[] uuids = adapter.getUuids();

        // uuids may be null if Bluetooth is turned off
        if (uuids != null) {
            updateLocalProfiles(uuids);
        }

        // Always add HID and PAN profiles
        mHidProfile = new HidProfile(context, mLocalAdapter, mDeviceManager, this);
        addProfile(mHidProfile, HidProfile.NAME,
                BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED);

        mPanProfile = new PanProfile(context);
        addPanProfile(mPanProfile, PanProfile.NAME,
                BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);

        if(DEBUG) Log.d(TAG, "Adding local MAP profile");
        mMapProfile = new MapProfile(mContext, mLocalAdapter,
                mDeviceManager, this);
        addProfile(mMapProfile, MapProfile.NAME,
                BluetoothMap.ACTION_CONNECTION_STATE_CHANGED);

       //Create PBAP server profile, but do not add it to list of profiles
       // as we do not need to monitor the profile as part of profile list
        mPbapProfile = new PbapServerProfile(context);

        if (DEBUG) Log.d(TAG, "LocalBluetoothProfileManager construction complete");
    }

如果远程设备(BluetoothDevice)profile的连接状态发生变化,则通过上边的监听回调通知蓝牙列表中对应的蓝牙设备(CachedBluetoothDevice)。具体通过onProfileStateChanged方法回调。

        public void onReceive(Context context, Intent intent, BluetoothDevice device) {
            CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
            if (cachedDevice == null) {
                Log.w(TAG, "StateChangedHandler found new device: " + device);
                cachedDevice = mDeviceManager.addDevice(mLocalAdapter,
                        LocalBluetoothProfileManager.this, device);
            }
            int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
            int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
            if (newState == BluetoothProfile.STATE_DISCONNECTED &&
                    oldState == BluetoothProfile.STATE_CONNECTING) {
                Log.i(TAG, "Failed to connect " + mProfile + " device");
            }

            cachedDevice.onProfileStateChanged(mProfile, newState);
            cachedDevice.refresh();
        }

在CachedBluetoothDevice的onProfileStateChanged中主要是对当前处于连接状态的profile集合(mProfiles)进行管理,通过此集合我们可知此蓝牙设备各个子协议(profile规范)的连接情况,以方便在蓝牙设备取消连接时通知其取消各个处于连接状态的profile的连接。

当然,除了通过LocalBluetoothProfileManager监听的几种profile的ACTION_CONNECTION_STATE_CHANGED之外,蓝牙设置还在BluetoothEventManager中监听了uuid的改变(uuid标识某远程蓝牙设备的profile协议使用情况发生变化)。

    BluetoothEventManager(LocalBluetoothAdapter adapter,
            CachedBluetoothDeviceManager deviceManager, Context context) {
            
        ...
        addHandler(BluetoothDevice.ACTION_UUID, new UuidChangedHandler());
        ...
        mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter);
    }

在监听到远程蓝牙设备uuid发生改变时通知CacheBluetoothDeviceManager的onUuidChanged方法。

    private class UuidChangedHandler implements Handler {
        public void onReceive(Context context, Intent intent,
                BluetoothDevice device) {
            mDeviceManager.onUuidChanged(device);
        }
    }

而onUuidChanged在CacheBluetoothDeviceManager中则交由CacheBluetoothDevice自己处理,而uuid发生变化的远程蓝牙设备则获取自己的uuids与本机设备使用到的uuids交由LocalBluetoothProfileManager的updateProfiles方法去更新设备的mProfiles(其最终调用BluetoothDevicePreference的onDeviceAttributesChanged去更新列表中该蓝牙设备标识其设备用途的icon,此icon一般在蓝牙列表条目的列首)。

    private boolean updateProfiles() {
        ParcelUuid[] uuids = mDevice.getUuids();
        if (uuids == null) return false;

        ParcelUuid[] localUuids = mLocalAdapter.getUuids();
        if (localUuids == null) return false;

        mProfileManager.updateProfiles(uuids, localUuids, mProfiles, mRemovedProfiles,
                                       mLocalNapRoleConnected, mDevice);

        ...
        return true;
    }

在updateProfiles中,第一个参数是远程蓝牙设备使用的uuids,第二个参数是本机设备使用的uuids。在此方法中依据此两种uuids的对称性情况来确定远程蓝牙设备的profile协议使用情况(如HEADSET则要确定本机uuids中有HSP_AG且远程设备的uuids中有HSP才能确定远程蓝牙设备正使用HSP协议与本机通讯)。

    synchronized void updateProfiles(ParcelUuid[] uuids, ParcelUuid[] localUuids,
            Collection<LocalBluetoothProfile> profiles,
            Collection<LocalBluetoothProfile> removedProfiles,
            boolean isPanNapConnected, BluetoothDevice device) {
        // Copy previous profile list into removedProfiles
        removedProfiles.clear();
        removedProfiles.addAll(profiles);
        profiles.clear();

        if (uuids == null) {
            return;
        }

        if (mHeadsetProfile != null) {
            if ((BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.HSP_AG) &&
                    BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP)) ||
                    (BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.Handsfree_AG) &&
                            BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree))) {
                profiles.add(mHeadsetProfile);
                removedProfiles.remove(mHeadsetProfile);
            }
        }

        if (BluetoothUuid.containsAnyUuid(uuids, A2dpProfile.SINK_UUIDS) &&
            mA2dpProfile != null) {
            profiles.add(mA2dpProfile);
            removedProfiles.remove(mA2dpProfile);
        }

        if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush) &&
            mOppProfile != null) {
            profiles.add(mOppProfile);
            removedProfiles.remove(mOppProfile);
        }

        if ((BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hid) ||
             BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hogp)) &&
            mHidProfile != null) {
            profiles.add(mHidProfile);
            removedProfiles.remove(mHidProfile);
        }

        if(isPanNapConnected)
            if(DEBUG) Log.d(TAG, "Valid PAN-NAP connection exists.");
        if ((BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.NAP) &&
            mPanProfile != null) || isPanNapConnected) {
            profiles.add(mPanProfile);
            removedProfiles.remove(mPanProfile);
        }

        if ((mMapProfile != null) &&
            (mMapProfile.getConnectionStatus(device) == BluetoothProfile.STATE_CONNECTED)) {
            profiles.add(mMapProfile);
            removedProfiles.remove(mMapProfile);
            mMapProfile.setPreferred(device, true);
        }
    }

在蓝牙设置中只关心HSP+HFP(蓝牙耳机)、A2DP(音频输出)、OPP(对象传输)、HIP(人机接口)、PAN(个人局域网)、MAP(短信传输)这几种profile协议的变化。因为蓝牙列表需要根据profile来更新远程蓝牙设备的列首图标。



版权声明:本文为qq_33859911原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。