1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settingslib.bluetooth; 18 19 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; 20 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; 21 22 import android.bluetooth.BluetoothAdapter; 23 import android.bluetooth.BluetoothClass; 24 import android.bluetooth.BluetoothDevice; 25 import android.bluetooth.BluetoothHidHost; 26 import android.bluetooth.BluetoothProfile; 27 import android.content.Context; 28 import android.util.Log; 29 30 import androidx.annotation.NonNull; 31 32 import com.android.settingslib.R; 33 34 import java.util.List; 35 36 /** 37 * HidProfile handles Bluetooth HID Host role. 38 */ 39 public class HidProfile implements LocalBluetoothProfile { 40 private static final String TAG = "HidProfile"; 41 42 private BluetoothHidHost mService; 43 private boolean mIsProfileReady; 44 45 private final CachedBluetoothDeviceManager mDeviceManager; 46 private final LocalBluetoothProfileManager mProfileManager; 47 48 static final String NAME = "HID"; 49 50 // Order of this profile in device profiles list 51 private static final int ORDINAL = 3; 52 53 // These callbacks run on the main thread. 54 private final class HidHostServiceListener 55 implements BluetoothProfile.ServiceListener { 56 onServiceConnected(int profile, BluetoothProfile proxy)57 public void onServiceConnected(int profile, BluetoothProfile proxy) { 58 mService = (BluetoothHidHost) proxy; 59 // We just bound to the service, so refresh the UI for any connected HID devices. 60 List<BluetoothDevice> deviceList = mService.getConnectedDevices(); 61 while (!deviceList.isEmpty()) { 62 BluetoothDevice nextDevice = deviceList.remove(0); 63 CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice); 64 // we may add a new device here, but generally this should not happen 65 if (device == null) { 66 Log.w(TAG, "HidProfile found new device: " + nextDevice); 67 device = mDeviceManager.addDevice(nextDevice); 68 } 69 device.onProfileStateChanged(HidProfile.this, BluetoothProfile.STATE_CONNECTED); 70 device.refresh(); 71 } 72 mIsProfileReady=true; 73 } 74 onServiceDisconnected(int profile)75 public void onServiceDisconnected(int profile) { 76 mIsProfileReady=false; 77 } 78 } 79 isProfileReady()80 public boolean isProfileReady() { 81 return mIsProfileReady; 82 } 83 84 @Override getProfileId()85 public int getProfileId() { 86 return BluetoothProfile.HID_HOST; 87 } 88 HidProfile(Context context, CachedBluetoothDeviceManager deviceManager, LocalBluetoothProfileManager profileManager)89 HidProfile(Context context, 90 CachedBluetoothDeviceManager deviceManager, 91 LocalBluetoothProfileManager profileManager) { 92 mDeviceManager = deviceManager; 93 mProfileManager = profileManager; 94 BluetoothAdapter.getDefaultAdapter().getProfileProxy(context, new HidHostServiceListener(), 95 BluetoothProfile.HID_HOST); 96 } 97 accessProfileEnabled()98 public boolean accessProfileEnabled() { 99 return true; 100 } 101 isAutoConnectable()102 public boolean isAutoConnectable() { 103 return true; 104 } 105 getConnectionStatus(BluetoothDevice device)106 public int getConnectionStatus(BluetoothDevice device) { 107 if (mService == null) { 108 return BluetoothProfile.STATE_DISCONNECTED; 109 } 110 return mService.getConnectionState(device); 111 } 112 113 @Override isEnabled(BluetoothDevice device)114 public boolean isEnabled(BluetoothDevice device) { 115 if (mService == null) { 116 return false; 117 } 118 return mService.getConnectionPolicy(device) != CONNECTION_POLICY_FORBIDDEN; 119 } 120 121 @Override getConnectionPolicy(BluetoothDevice device)122 public int getConnectionPolicy(BluetoothDevice device) { 123 if (mService == null) { 124 return CONNECTION_POLICY_FORBIDDEN; 125 } 126 return mService.getConnectionPolicy(device); 127 } 128 129 @Override setEnabled(BluetoothDevice device, boolean enabled)130 public boolean setEnabled(BluetoothDevice device, boolean enabled) { 131 boolean isSuccessful = false; 132 if (mService == null) { 133 return false; 134 } 135 if (enabled) { 136 if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { 137 isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); 138 } 139 } else { 140 isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); 141 } 142 143 return isSuccessful; 144 } 145 toString()146 public String toString() { 147 return NAME; 148 } 149 getOrdinal()150 public int getOrdinal() { 151 return ORDINAL; 152 } 153 getNameResource(BluetoothDevice device)154 public int getNameResource(BluetoothDevice device) { 155 // TODO: distinguish between keyboard and mouse? 156 return R.string.bluetooth_profile_hid; 157 } 158 getSummaryResourceForDevice(BluetoothDevice device)159 public int getSummaryResourceForDevice(BluetoothDevice device) { 160 int state = getConnectionStatus(device); 161 switch (state) { 162 case BluetoothProfile.STATE_DISCONNECTED: 163 return R.string.bluetooth_hid_profile_summary_use_for; 164 165 case BluetoothProfile.STATE_CONNECTED: 166 return R.string.bluetooth_hid_profile_summary_connected; 167 168 default: 169 return BluetoothUtils.getConnectionStateSummary(state); 170 } 171 } 172 getDrawableResource(BluetoothClass btClass)173 public int getDrawableResource(BluetoothClass btClass) { 174 if (btClass == null) { 175 return com.android.internal.R.drawable.ic_lockscreen_ime; 176 } 177 return getHidClassDrawable(btClass); 178 } 179 getHidClassDrawable(BluetoothClass btClass)180 public static int getHidClassDrawable(BluetoothClass btClass) { 181 switch (btClass.getDeviceClass()) { 182 case BluetoothClass.Device.PERIPHERAL_KEYBOARD: 183 case BluetoothClass.Device.PERIPHERAL_KEYBOARD_POINTING: 184 return com.android.internal.R.drawable.ic_lockscreen_ime; 185 case BluetoothClass.Device.PERIPHERAL_POINTING: 186 return com.android.internal.R.drawable.ic_bt_pointing_hid; 187 default: 188 return com.android.internal.R.drawable.ic_bt_misc_hid; 189 } 190 } 191 192 /** Set preferred transport for the device */ setPreferredTransport(@onNull BluetoothDevice device, int transport)193 public boolean setPreferredTransport(@NonNull BluetoothDevice device, int transport) { 194 if (mService != null) { 195 mService.setPreferredTransport(device, transport); 196 } 197 return false; 198 } 199 finalize()200 protected void finalize() { 201 Log.d(TAG, "finalize()"); 202 if (mService != null) { 203 try { 204 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.HID_HOST, 205 mService); 206 mService = null; 207 }catch (Throwable t) { 208 Log.w(TAG, "Error cleaning up HID proxy", t); 209 } 210 } 211 } 212 } 213