1 /*
2  * Copyright (C) 2017 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_FORBIDDEN;
20 
21 import android.bluetooth.BluetoothAdapter;
22 import android.bluetooth.BluetoothClass;
23 import android.bluetooth.BluetoothDevice;
24 import android.bluetooth.BluetoothHidDevice;
25 import android.bluetooth.BluetoothProfile;
26 import android.content.Context;
27 import android.util.Log;
28 
29 import com.android.settingslib.R;
30 
31 import java.util.List;
32 
33 /**
34  * HidDeviceProfile handles Bluetooth HID Device role
35  */
36 public class HidDeviceProfile implements LocalBluetoothProfile {
37     private static final String TAG = "HidDeviceProfile";
38     // Order of this profile in device profiles list
39     private static final int ORDINAL = 18;
40     // HID Device Profile is always preferred.
41     private static final int PREFERRED_VALUE = -1;
42 
43     private final CachedBluetoothDeviceManager mDeviceManager;
44     private final LocalBluetoothProfileManager mProfileManager;
45     static final String NAME = "HID DEVICE";
46 
47     private BluetoothHidDevice mService;
48     private boolean mIsProfileReady;
49 
HidDeviceProfile(Context context,CachedBluetoothDeviceManager deviceManager, LocalBluetoothProfileManager profileManager)50     HidDeviceProfile(Context context,CachedBluetoothDeviceManager deviceManager,
51             LocalBluetoothProfileManager profileManager) {
52         mDeviceManager = deviceManager;
53         mProfileManager = profileManager;
54         BluetoothAdapter.getDefaultAdapter().getProfileProxy(context,
55                 new HidDeviceServiceListener(), BluetoothProfile.HID_DEVICE);
56     }
57 
58     // These callbacks run on the main thread.
59     private final class HidDeviceServiceListener
60             implements BluetoothProfile.ServiceListener {
61 
onServiceConnected(int profile, BluetoothProfile proxy)62         public void onServiceConnected(int profile, BluetoothProfile proxy) {
63             mService = (BluetoothHidDevice) proxy;
64             // We just bound to the service, so refresh the UI for any connected HID devices.
65             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
66             for (BluetoothDevice nextDevice : deviceList) {
67                 CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
68                 // we may add a new device here, but generally this should not happen
69                 if (device == null) {
70                     Log.w(TAG, "HidProfile found new device: " + nextDevice);
71                     device = mDeviceManager.addDevice(nextDevice);
72                 }
73                 Log.d(TAG, "Connection status changed: " + device);
74                 device.onProfileStateChanged(HidDeviceProfile.this,
75                         BluetoothProfile.STATE_CONNECTED);
76                 device.refresh();
77             }
78             mIsProfileReady = true;
79         }
80 
onServiceDisconnected(int profile)81         public void onServiceDisconnected(int profile) {
82             mIsProfileReady = false;
83         }
84     }
85 
86     @Override
isProfileReady()87     public boolean isProfileReady() {
88         return mIsProfileReady;
89     }
90 
91     @Override
getProfileId()92     public int getProfileId() {
93         return BluetoothProfile.HID_DEVICE;
94     }
95 
96     @Override
accessProfileEnabled()97     public boolean accessProfileEnabled() {
98         return true;
99     }
100 
101     @Override
isAutoConnectable()102     public boolean isAutoConnectable() {
103         return false;
104     }
105 
106     @Override
getConnectionStatus(BluetoothDevice device)107     public int getConnectionStatus(BluetoothDevice device) {
108         if (mService == null) {
109             return BluetoothProfile.STATE_DISCONNECTED;
110         }
111         return mService.getConnectionState(device);
112     }
113 
114     @Override
isEnabled(BluetoothDevice device)115     public boolean isEnabled(BluetoothDevice device) {
116         return getConnectionStatus(device) != BluetoothProfile.STATE_DISCONNECTED;
117     }
118 
119     @Override
getConnectionPolicy(BluetoothDevice device)120     public int getConnectionPolicy(BluetoothDevice device) {
121         return PREFERRED_VALUE;
122     }
123 
124     @Override
setEnabled(BluetoothDevice device, boolean enabled)125     public boolean setEnabled(BluetoothDevice device, boolean enabled) {
126         boolean isEnabled = false;
127         // if set preferred to false, then disconnect to the current device
128         if (!enabled) {
129             isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
130         }
131 
132         return isEnabled;
133     }
134 
135     @Override
toString()136     public String toString() {
137         return NAME;
138     }
139 
140     @Override
getOrdinal()141     public int getOrdinal() {
142         return ORDINAL;
143     }
144 
145     @Override
getNameResource(BluetoothDevice device)146     public int getNameResource(BluetoothDevice device) {
147         return R.string.bluetooth_profile_hid;
148     }
149 
150     @Override
getSummaryResourceForDevice(BluetoothDevice device)151     public int getSummaryResourceForDevice(BluetoothDevice device) {
152         final int state = getConnectionStatus(device);
153         switch (state) {
154             case BluetoothProfile.STATE_DISCONNECTED:
155                 return R.string.bluetooth_hid_profile_summary_use_for;
156             case BluetoothProfile.STATE_CONNECTED:
157                 return R.string.bluetooth_hid_profile_summary_connected;
158             default:
159                 return BluetoothUtils.getConnectionStateSummary(state);
160         }
161     }
162 
163     @Override
getDrawableResource(BluetoothClass btClass)164     public int getDrawableResource(BluetoothClass btClass) {
165         return com.android.internal.R.drawable.ic_bt_misc_hid;
166     }
167 
finalize()168     protected void finalize() {
169         Log.d(TAG, "finalize()");
170         if (mService != null) {
171             try {
172                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.HID_DEVICE,
173                         mService);
174                 mService = null;
175             } catch (Throwable t) {
176                 Log.w(TAG, "Error cleaning up HID proxy", t);
177             }
178         }
179     }
180 }
181