Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dcae293e authored by Hansong Zhang's avatar Hansong Zhang
Browse files

Show Bluetooth HID Device connection status in Settings

* Add the HidDeviceProfile class, which wraps the BluetoothHidProfile in
Settings. HidDeviceProfile will be added to local Bluetooth profiles
once the HID Device Service connection is in use. This will allow the
connection status of HID Device profile to be displayed in Settings >
Connected devices > Bluetooth > Device details, and in the device list.
* Rename InputDeviceServiceListener to HidHostServiceListener to
eliminate consufion.

Bug: 69136526
Test: apps with HID Device.
Change-Id: I99bef6745ae58fe7f522432e7841a5910388a352
parent baca8670
Loading
Loading
Loading
Loading
+200 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settingslib.bluetooth;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHidDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.util.Log;

import com.android.settingslib.R;

import java.util.Collection;
import java.util.List;

/**
 * HidProfile handles Bluetooth HID profile.
 */
public class HidDeviceProfile implements LocalBluetoothProfile {
    private static final String TAG = "HidDeviceProfile";
    // Order of this profile in device profiles list
    private static final int ORDINAL = 18;
    // HID Device Profile is always preferred.
    private static final int PREFERRED_VALUE = -1;
    private static final boolean DEBUG = true;

    private final LocalBluetoothAdapter mLocalAdapter;
    private final CachedBluetoothDeviceManager mDeviceManager;
    private final LocalBluetoothProfileManager mProfileManager;
    static final String NAME = "HID DEVICE";

    private BluetoothHidDevice mService;
    private boolean mIsProfileReady;

    HidDeviceProfile(Context context, LocalBluetoothAdapter adapter,
            CachedBluetoothDeviceManager deviceManager,
            LocalBluetoothProfileManager profileManager) {
        mLocalAdapter = adapter;
        mDeviceManager = deviceManager;
        mProfileManager = profileManager;
        adapter.getProfileProxy(context, new HidDeviceServiceListener(),
                BluetoothProfile.HID_DEVICE);
    }

    // These callbacks run on the main thread.
    private final class HidDeviceServiceListener
            implements BluetoothProfile.ServiceListener {

        public void onServiceConnected(int profile, BluetoothProfile proxy) {
            if (DEBUG) {
                Log.d(TAG,"Bluetooth service connected :-)");
            }
            mService = (BluetoothHidDevice) proxy;
            // We just bound to the service, so refresh the UI for any connected HID devices.
            List<BluetoothDevice> deviceList = mService.getConnectedDevices();
            for (BluetoothDevice nextDevice : deviceList) {
                CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
                // we may add a new device here, but generally this should not happen
                if (device == null) {
                    Log.w(TAG, "HidProfile found new device: " + nextDevice);
                    device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
                }
                Log.d(TAG, "Connection status changed: " + device);
                device.onProfileStateChanged(HidDeviceProfile.this,
                        BluetoothProfile.STATE_CONNECTED);
                device.refresh();
            }
            mIsProfileReady = true;
        }

        public void onServiceDisconnected(int profile) {
            if (DEBUG) {
                Log.d(TAG, "Bluetooth service disconnected");
            }
            mIsProfileReady = false;
        }
    }

    @Override
    public boolean isProfileReady() {
        return mIsProfileReady;
    }

    @Override
    public boolean isConnectable() {
        return true;
    }

    @Override
    public boolean isAutoConnectable() {
        return false;
    }

    @Override
    public boolean connect(BluetoothDevice device) {
        return false;
    }

    @Override
    public boolean disconnect(BluetoothDevice device) {
        if (mService == null) {
            return false;
        }
        return mService.disconnect(device);
    }

    @Override
    public int getConnectionStatus(BluetoothDevice device) {
        if (mService == null) {
            return BluetoothProfile.STATE_DISCONNECTED;
        }
        List<BluetoothDevice> deviceList = mService.getConnectedDevices();

        return !deviceList.isEmpty() && deviceList.contains(device)
                ? mService.getConnectionState(device)
                : BluetoothProfile.STATE_DISCONNECTED;
    }

    @Override
    public boolean isPreferred(BluetoothDevice device) {
        return getConnectionStatus(device) != BluetoothProfile.STATE_DISCONNECTED;
    }

    @Override
    public int getPreferred(BluetoothDevice device) {
        return PREFERRED_VALUE;
    }

    @Override
    public void setPreferred(BluetoothDevice device, boolean preferred) {
        // if set preferred to false, then disconnect to the current device
        if (!preferred) {
            mService.disconnect(device);
        }
    }

    @Override
    public String toString() {
        return NAME;
    }

    @Override
    public int getOrdinal() {
        return ORDINAL;
    }

    @Override
    public int getNameResource(BluetoothDevice device) {
        return R.string.bluetooth_profile_hid;
    }

    @Override
    public int getSummaryResourceForDevice(BluetoothDevice device) {
        final int state = getConnectionStatus(device);
        switch (state) {
            case BluetoothProfile.STATE_DISCONNECTED:
                return R.string.bluetooth_hid_profile_summary_use_for;
            case BluetoothProfile.STATE_CONNECTED:
                return R.string.bluetooth_hid_profile_summary_connected;
            default:
                return Utils.getConnectionStateSummary(state);
        }
    }

    @Override
    public int getDrawableResource(BluetoothClass btClass) {
        return R.drawable.ic_bt_misc_hid;
    }

    protected void finalize() {
        if (DEBUG) {
            Log.d(TAG, "finalize()");
        }
        if (mService != null) {
            try {
                BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.HID_DEVICE,
                        mService);
                mService = null;
            } catch (Throwable t) {
                Log.w(TAG, "Error cleaning up HID proxy", t);
            }
        }
    }
}
+2 −2
Original line number Diff line number Diff line
@@ -48,7 +48,7 @@ public class HidProfile implements LocalBluetoothProfile {
    private static final int ORDINAL = 3;

    // These callbacks run on the main thread.
    private final class InputDeviceServiceListener
    private final class HidHostServiceListener
            implements BluetoothProfile.ServiceListener {

        public void onServiceConnected(int profile, BluetoothProfile proxy) {
@@ -86,7 +86,7 @@ public class HidProfile implements LocalBluetoothProfile {
        mLocalAdapter = adapter;
        mDeviceManager = deviceManager;
        mProfileManager = profileManager;
        adapter.getProfileProxy(context, new InputDeviceServiceListener(),
        adapter.getProfileProxy(context, new HidHostServiceListener(),
                BluetoothProfile.HID_HOST);
    }

+13 −1
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHeadsetClient;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothHidDevice;
import android.bluetooth.BluetoothHidHost;
import android.bluetooth.BluetoothMap;
import android.bluetooth.BluetoothMapClient;
@@ -86,6 +87,7 @@ public class LocalBluetoothProfileManager {
    private MapProfile mMapProfile;
    private MapClientProfile mMapClientProfile;
    private final HidProfile mHidProfile;
    private HidDeviceProfile mHidDeviceProfile;
    private OppProfile mOppProfile;
    private final PanProfile mPanProfile;
    private PbapClientProfile mPbapClientProfile;
@@ -123,7 +125,7 @@ public class LocalBluetoothProfileManager {
            updateLocalProfiles(uuids);
        }

        // Always add HID and PAN profiles
        // Always add HID host, HID device, and PAN profiles
        mHidProfile = new HidProfile(context, mLocalAdapter, mDeviceManager, this);
        addProfile(mHidProfile, HidProfile.NAME,
                BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED);
@@ -132,6 +134,10 @@ public class LocalBluetoothProfileManager {
        addPanProfile(mPanProfile, PanProfile.NAME,
                BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);

        mHidDeviceProfile = new HidDeviceProfile(context, mLocalAdapter, mDeviceManager, this);
        addProfile(mHidDeviceProfile, HidDeviceProfile.NAME,
                BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED);

        if(DEBUG) Log.d(TAG, "Adding local MAP profile");
        if (mUseMapClient) {
            mMapClientProfile = new MapClientProfile(mContext, mLocalAdapter, mDeviceManager, this);
@@ -505,6 +511,12 @@ public class LocalBluetoothProfileManager {
            removedProfiles.remove(mHidProfile);
        }

        if (mHidProfile != null && mHidDeviceProfile.getConnectionStatus(device)
                != BluetoothProfile.STATE_DISCONNECTED) {
            profiles.add(mHidDeviceProfile);
            removedProfiles.remove(mHidDeviceProfile);
        }

        if(isPanNapConnected)
            if(DEBUG) Log.d(TAG, "Valid PAN-NAP connection exists.");
        if ((BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.NAP) &&