From baca867094b6fa17c0113e31c53a4560f00c85a7 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Tue, 3 Apr 2018 12:12:53 -0700 Subject: [PATCH 1/2] Bluetooth: Add hidden API to get current user of HID Device (1/3) Bug: 69136526 Test: test with apps using HID Device profile Change-Id: If0e49840257c877c975c2da176a08e613668cbc3 (cherry picked from commit 347ef4099e0d2b1334efd22e6ca7d5cc5464b4eb) --- .../android/bluetooth/BluetoothHidDevice.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java index af99bf7dfd8ec..3bc8544ebf872 100644 --- a/core/java/android/bluetooth/BluetoothHidDevice.java +++ b/core/java/android/bluetooth/BluetoothHidDevice.java @@ -700,6 +700,28 @@ public final class BluetoothHidDevice implements BluetoothProfile { return result; } + /** + * Gets the application name of the current HidDeviceService user. + * + * @return the current user name, or empty string if cannot get the name + * {@hide} + */ + public String getUserAppName() { + final IBluetoothHidDevice service = mService; + + if (service != null) { + try { + return service.getUserAppName(); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return ""; + } + /** * Initiates connection to host which is currently paired with this device. If the application * is not registered, #connect(BluetoothDevice) will fail. The connection state should be From dcae293e94517488cdfd946bbc07b030f1cdfdbc Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Mon, 13 Nov 2017 10:56:23 -0800 Subject: [PATCH 2/2] 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 --- .../bluetooth/HidDeviceProfile.java | 200 ++++++++++++++++++ .../settingslib/bluetooth/HidProfile.java | 4 +- .../LocalBluetoothProfileManager.java | 14 +- 3 files changed, 215 insertions(+), 3 deletions(-) create mode 100644 packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java new file mode 100644 index 0000000000000..941964a57eb44 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java @@ -0,0 +1,200 @@ +/* + * 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 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 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); + } + } + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java index 213002fb97261..93c4017fdaf86 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java @@ -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); } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java index 34a099cb7ea07..5b202dc58579f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java @@ -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) &&