blob: 0ca39f169a721931d06e89ce379bfa139480cfca [file] [log] [blame]
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001/*
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08002 * Copyright (C) 2011 The Android Open Source Project
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07003 *
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
17package android.bluetooth;
18
19import android.annotation.SdkConstant;
20import android.annotation.SdkConstant.SdkConstantType;
Matthew Xiebf246ef2012-03-21 23:15:06 -070021import android.content.ComponentName;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070022import android.content.Context;
Matthew Xiebf246ef2012-03-21 23:15:06 -070023import android.content.Intent;
24import android.content.ServiceConnection;
Jeff Sharkey0a17db12016-11-04 11:23:46 -060025import android.os.Binder;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070026import android.os.IBinder;
27import android.os.RemoteException;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070028import android.util.Log;
29
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -070030import java.util.ArrayList;
31import java.util.List;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070032
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -080033
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070034/**
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -080035 * This class provides the public APIs to control the Bluetooth Input
36 * Device Profile.
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070037 *
Hansong Zhangc26c76c2017-10-20 15:55:59 -070038 * <p>BluetoothHidHost is a proxy object for controlling the Bluetooth
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -080039 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
Hansong Zhangc26c76c2017-10-20 15:55:59 -070040 * the BluetoothHidHost proxy object.
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070041 *
Jack Hea355e5e2017-08-22 16:06:54 -070042 * <p>Each method is protected with its appropriate permission.
43 *
44 * @hide
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070045 */
Hansong Zhangc26c76c2017-10-20 15:55:59 -070046public final class BluetoothHidHost implements BluetoothProfile {
47 private static final String TAG = "BluetoothHidHost";
fredc0f420372012-04-12 00:02:00 -070048 private static final boolean DBG = true;
Matthew Xie563e4142012-10-09 22:10:37 -070049 private static final boolean VDBG = false;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070050
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -080051 /**
52 * Intent used to broadcast the change in connection state of the Input
53 * Device profile.
54 *
55 * <p>This intent will have 3 extras:
56 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -070057 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
58 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
59 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -080060 * </ul>
61 *
62 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
63 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
64 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
65 *
66 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
67 * receive.
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070068 */
69 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -080070 public static final String ACTION_CONNECTION_STATE_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -070071 "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED";
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070072
Jaikumar Ganeshfbe807d2011-01-19 13:59:32 -080073 /**
Priti Aghera349e62f2012-04-09 12:13:17 -070074 * @hide
75 */
76 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
77 public static final String ACTION_PROTOCOL_MODE_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -070078 "android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED";
Priti Aghera349e62f2012-04-09 12:13:17 -070079
Mike J. Chend96d5cf2014-01-27 17:55:40 -080080 /**
81 * @hide
82 */
83 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Mike J. Chen8faffa42014-03-04 17:27:16 -080084 public static final String ACTION_HANDSHAKE =
Jack Hea355e5e2017-08-22 16:06:54 -070085 "android.bluetooth.input.profile.action.HANDSHAKE";
Mike J. Chen8faffa42014-03-04 17:27:16 -080086
87 /**
88 * @hide
89 */
90 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Mike J. Chend96d5cf2014-01-27 17:55:40 -080091 public static final String ACTION_REPORT =
Jack Hea355e5e2017-08-22 16:06:54 -070092 "android.bluetooth.input.profile.action.REPORT";
Priti Aghera349e62f2012-04-09 12:13:17 -070093
94 /**
95 * @hide
96 */
97 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
98 public static final String ACTION_VIRTUAL_UNPLUG_STATUS =
Jack Hea355e5e2017-08-22 16:06:54 -070099 "android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS";
Priti Aghera349e62f2012-04-09 12:13:17 -0700100
Hemant Guptacef9ce32013-07-30 16:09:20 +0530101 /**
102 * @hide
103 */
104 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
105 public static final String ACTION_IDLE_TIME_CHANGED =
Jack Hea355e5e2017-08-22 16:06:54 -0700106 "android.bluetooth.input.profile.action.IDLE_TIME_CHANGED";
Priti Aghera349e62f2012-04-09 12:13:17 -0700107
108 /**
Jaikumar Ganeshfbe807d2011-01-19 13:59:32 -0800109 * Return codes for the connect and disconnect Bluez / Dbus calls.
Jack Hea355e5e2017-08-22 16:06:54 -0700110 *
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800111 * @hide
Jaikumar Ganeshfbe807d2011-01-19 13:59:32 -0800112 */
113 public static final int INPUT_DISCONNECT_FAILED_NOT_CONNECTED = 5000;
114
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800115 /**
116 * @hide
117 */
Jaikumar Ganeshfbe807d2011-01-19 13:59:32 -0800118 public static final int INPUT_CONNECT_FAILED_ALREADY_CONNECTED = 5001;
119
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800120 /**
121 * @hide
122 */
Jaikumar Ganeshfbe807d2011-01-19 13:59:32 -0800123 public static final int INPUT_CONNECT_FAILED_ATTEMPT_FAILED = 5002;
124
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800125 /**
126 * @hide
127 */
Jaikumar Ganeshfbe807d2011-01-19 13:59:32 -0800128 public static final int INPUT_OPERATION_GENERIC_FAILURE = 5003;
129
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800130 /**
131 * @hide
132 */
Jaikumar Ganeshfbe807d2011-01-19 13:59:32 -0800133 public static final int INPUT_OPERATION_SUCCESS = 5004;
134
Priti Aghera349e62f2012-04-09 12:13:17 -0700135 /**
136 * @hide
137 */
138 public static final int PROTOCOL_REPORT_MODE = 0;
139
140 /**
141 * @hide
142 */
143 public static final int PROTOCOL_BOOT_MODE = 1;
144
145 /**
146 * @hide
147 */
148 public static final int PROTOCOL_UNSUPPORTED_MODE = 255;
149
150 /* int reportType, int reportType, int bufferSize */
151 /**
152 * @hide
153 */
Mike J. Chen1b47f7c2014-01-27 16:27:04 -0800154 public static final byte REPORT_TYPE_INPUT = 1;
Priti Aghera349e62f2012-04-09 12:13:17 -0700155
156 /**
157 * @hide
158 */
Mike J. Chen1b47f7c2014-01-27 16:27:04 -0800159 public static final byte REPORT_TYPE_OUTPUT = 2;
Priti Aghera349e62f2012-04-09 12:13:17 -0700160
161 /**
162 * @hide
163 */
Mike J. Chen1b47f7c2014-01-27 16:27:04 -0800164 public static final byte REPORT_TYPE_FEATURE = 3;
Priti Aghera349e62f2012-04-09 12:13:17 -0700165
166 /**
167 * @hide
168 */
169 public static final int VIRTUAL_UNPLUG_STATUS_SUCCESS = 0;
170
171 /**
172 * @hide
173 */
174 public static final int VIRTUAL_UNPLUG_STATUS_FAIL = 1;
175
176 /**
177 * @hide
178 */
Jack Hea355e5e2017-08-22 16:06:54 -0700179 public static final String EXTRA_PROTOCOL_MODE =
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700180 "android.bluetooth.BluetoothHidHost.extra.PROTOCOL_MODE";
Priti Aghera349e62f2012-04-09 12:13:17 -0700181
182 /**
183 * @hide
184 */
Jack Hea355e5e2017-08-22 16:06:54 -0700185 public static final String EXTRA_REPORT_TYPE =
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700186 "android.bluetooth.BluetoothHidHost.extra.REPORT_TYPE";
Priti Aghera349e62f2012-04-09 12:13:17 -0700187
188 /**
189 * @hide
190 */
Jack Hea355e5e2017-08-22 16:06:54 -0700191 public static final String EXTRA_REPORT_ID =
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700192 "android.bluetooth.BluetoothHidHost.extra.REPORT_ID";
Priti Aghera349e62f2012-04-09 12:13:17 -0700193
194 /**
195 * @hide
196 */
Jack Hea355e5e2017-08-22 16:06:54 -0700197 public static final String EXTRA_REPORT_BUFFER_SIZE =
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700198 "android.bluetooth.BluetoothHidHost.extra.REPORT_BUFFER_SIZE";
Priti Aghera349e62f2012-04-09 12:13:17 -0700199
200 /**
201 * @hide
202 */
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700203 public static final String EXTRA_REPORT = "android.bluetooth.BluetoothHidHost.extra.REPORT";
Priti Aghera349e62f2012-04-09 12:13:17 -0700204
205 /**
206 * @hide
207 */
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700208 public static final String EXTRA_STATUS = "android.bluetooth.BluetoothHidHost.extra.STATUS";
Mike J. Chen8faffa42014-03-04 17:27:16 -0800209
210 /**
211 * @hide
212 */
Jack Hea355e5e2017-08-22 16:06:54 -0700213 public static final String EXTRA_VIRTUAL_UNPLUG_STATUS =
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700214 "android.bluetooth.BluetoothHidHost.extra.VIRTUAL_UNPLUG_STATUS";
Priti Aghera349e62f2012-04-09 12:13:17 -0700215
Hemant Guptacef9ce32013-07-30 16:09:20 +0530216 /**
217 * @hide
218 */
Jack Hea355e5e2017-08-22 16:06:54 -0700219 public static final String EXTRA_IDLE_TIME =
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700220 "android.bluetooth.BluetoothHidHost.extra.IDLE_TIME";
Hemant Guptacef9ce32013-07-30 16:09:20 +0530221
Matthew Xiebf246ef2012-03-21 23:15:06 -0700222 private Context mContext;
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800223 private ServiceListener mServiceListener;
224 private BluetoothAdapter mAdapter;
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700225 private volatile IBluetoothHidHost mService;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700226
Jack He2992cd02017-08-22 21:21:23 -0700227 private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
fredc0f420372012-04-12 00:02:00 -0700228 new IBluetoothStateChangeCallback.Stub() {
229 public void onBluetoothStateChange(boolean up) {
230 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
231 if (!up) {
Jack Hea355e5e2017-08-22 16:06:54 -0700232 if (VDBG) Log.d(TAG, "Unbinding service...");
fredc0f420372012-04-12 00:02:00 -0700233 synchronized (mConnection) {
234 try {
235 mService = null;
236 mContext.unbindService(mConnection);
237 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -0700238 Log.e(TAG, "", re);
fredc0f420372012-04-12 00:02:00 -0700239 }
240 }
241 } else {
242 synchronized (mConnection) {
243 try {
244 if (mService == null) {
Jack Hea355e5e2017-08-22 16:06:54 -0700245 if (VDBG) Log.d(TAG, "Binding service...");
Dianne Hackborn221ea892013-08-04 16:50:16 -0700246 doBind();
fredc0f420372012-04-12 00:02:00 -0700247 }
248 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -0700249 Log.e(TAG, "", re);
fredc0f420372012-04-12 00:02:00 -0700250 }
251 }
252 }
253 }
Jack Hea355e5e2017-08-22 16:06:54 -0700254 };
fredc0f420372012-04-12 00:02:00 -0700255
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700256 /**
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700257 * Create a BluetoothHidHost proxy object for interacting with the local
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800258 * Bluetooth Service which handles the InputDevice profile
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700259 */
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700260 /*package*/ BluetoothHidHost(Context context, ServiceListener l) {
Matthew Xiebf246ef2012-03-21 23:15:06 -0700261 mContext = context;
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800262 mServiceListener = l;
263 mAdapter = BluetoothAdapter.getDefaultAdapter();
fredc0f420372012-04-12 00:02:00 -0700264
265 IBluetoothManager mgr = mAdapter.getBluetoothManager();
266 if (mgr != null) {
267 try {
268 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
269 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700270 Log.e(TAG, "", e);
fredc0f420372012-04-12 00:02:00 -0700271 }
272 }
273
Dianne Hackborn221ea892013-08-04 16:50:16 -0700274 doBind();
275 }
276
277 boolean doBind() {
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700278 Intent intent = new Intent(IBluetoothHidHost.class.getName());
Dianne Hackborn221ea892013-08-04 16:50:16 -0700279 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
280 intent.setComponent(comp);
Dianne Hackborn466ce962014-03-19 18:06:58 -0700281 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
Jeff Sharkeyad357d12018-02-02 13:25:31 -0700282 mContext.getUser())) {
Dianne Hackborn221ea892013-08-04 16:50:16 -0700283 Log.e(TAG, "Could not bind to Bluetooth HID Service with " + intent);
284 return false;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700285 }
Dianne Hackborn221ea892013-08-04 16:50:16 -0700286 return true;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700287 }
288
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800289 /*package*/ void close() {
Matthew Xie563e4142012-10-09 22:10:37 -0700290 if (VDBG) log("close()");
fredc0f420372012-04-12 00:02:00 -0700291 IBluetoothManager mgr = mAdapter.getBluetoothManager();
292 if (mgr != null) {
293 try {
294 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
295 } catch (Exception e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700296 Log.e(TAG, "", e);
fredc0f420372012-04-12 00:02:00 -0700297 }
298 }
299
300 synchronized (mConnection) {
301 if (mService != null) {
302 try {
303 mService = null;
304 mContext.unbindService(mConnection);
305 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -0700306 Log.e(TAG, "", re);
fredc0f420372012-04-12 00:02:00 -0700307 }
Jack Hea355e5e2017-08-22 16:06:54 -0700308 }
Matthew Xiebf246ef2012-03-21 23:15:06 -0700309 }
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800310 mServiceListener = null;
311 }
312
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800313 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700314 * Initiate connection to a profile of the remote bluetooth device.
315 *
316 * <p> The system supports connection to multiple input devices.
317 *
318 * <p> This API returns false in scenarios like the profile on the
319 * device is already connected or Bluetooth is not turned on.
320 * When this API returns true, it is guaranteed that
321 * connection state intent for the profile will be broadcasted with
322 * the state. Users can get the connection state of the profile
323 * from this intent.
324 *
325 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
326 * permission.
327 *
328 * @param device Remote Bluetooth Device
Jack Hea355e5e2017-08-22 16:06:54 -0700329 * @return false on immediate error, true otherwise
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800330 * @hide
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700331 */
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800332 public boolean connect(BluetoothDevice device) {
333 if (DBG) log("connect(" + device + ")");
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700334 final IBluetoothHidHost service = mService;
Jack He16eeac32017-08-17 12:11:18 -0700335 if (service != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800336 try {
Jack He16eeac32017-08-17 12:11:18 -0700337 return service.connect(device);
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800338 } catch (RemoteException e) {
339 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
340 return false;
Matthew Xiebf246ef2012-03-21 23:15:06 -0700341 }
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700342 }
Jack He16eeac32017-08-17 12:11:18 -0700343 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700344 return false;
345 }
346
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800347 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700348 * Initiate disconnection from a profile
349 *
350 * <p> This API will return false in scenarios like the profile on the
351 * Bluetooth device is not in connected state etc. When this API returns,
352 * true, it is guaranteed that the connection state change
353 * intent will be broadcasted with the state. Users can get the
354 * disconnection state of the profile from this intent.
355 *
356 * <p> If the disconnection is initiated by a remote device, the state
357 * will transition from {@link #STATE_CONNECTED} to
358 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
359 * host (local) device the state will transition from
360 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
361 * state {@link #STATE_DISCONNECTED}. The transition to
362 * {@link #STATE_DISCONNECTING} can be used to distinguish between the
363 * two scenarios.
364 *
365 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
366 * permission.
367 *
368 * @param device Remote Bluetooth Device
Jack Hea355e5e2017-08-22 16:06:54 -0700369 * @return false on immediate error, true otherwise
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700370 * @hide
371 */
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800372 public boolean disconnect(BluetoothDevice device) {
373 if (DBG) log("disconnect(" + device + ")");
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700374 final IBluetoothHidHost service = mService;
Jack He16eeac32017-08-17 12:11:18 -0700375 if (service != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800376 try {
Jack He16eeac32017-08-17 12:11:18 -0700377 return service.disconnect(device);
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800378 } catch (RemoteException e) {
379 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
380 return false;
Matthew Xiebf246ef2012-03-21 23:15:06 -0700381 }
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700382 }
Jack He16eeac32017-08-17 12:11:18 -0700383 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800384 return false;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700385 }
386
387 /**
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800388 * {@inheritDoc}
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700389 */
Jack He2992cd02017-08-22 21:21:23 -0700390 @Override
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800391 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xie563e4142012-10-09 22:10:37 -0700392 if (VDBG) log("getConnectedDevices()");
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700393 final IBluetoothHidHost service = mService;
Jack He16eeac32017-08-17 12:11:18 -0700394 if (service != null && isEnabled()) {
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800395 try {
Jack He16eeac32017-08-17 12:11:18 -0700396 return service.getConnectedDevices();
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800397 } catch (RemoteException e) {
398 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
399 return new ArrayList<BluetoothDevice>();
Matthew Xiebf246ef2012-03-21 23:15:06 -0700400 }
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700401 }
Jack He16eeac32017-08-17 12:11:18 -0700402 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800403 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700404 }
405
406 /**
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800407 * {@inheritDoc}
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700408 */
Jack He2992cd02017-08-22 21:21:23 -0700409 @Override
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800410 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xie563e4142012-10-09 22:10:37 -0700411 if (VDBG) log("getDevicesMatchingStates()");
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700412 final IBluetoothHidHost service = mService;
Jack He16eeac32017-08-17 12:11:18 -0700413 if (service != null && isEnabled()) {
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800414 try {
Jack He16eeac32017-08-17 12:11:18 -0700415 return service.getDevicesMatchingConnectionStates(states);
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800416 } catch (RemoteException e) {
417 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
418 return new ArrayList<BluetoothDevice>();
Matthew Xiebf246ef2012-03-21 23:15:06 -0700419 }
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700420 }
Jack He16eeac32017-08-17 12:11:18 -0700421 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800422 return new ArrayList<BluetoothDevice>();
423 }
424
425 /**
426 * {@inheritDoc}
427 */
Jack He2992cd02017-08-22 21:21:23 -0700428 @Override
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800429 public int getConnectionState(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700430 if (VDBG) log("getState(" + device + ")");
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700431 final IBluetoothHidHost service = mService;
Jack He16eeac32017-08-17 12:11:18 -0700432 if (service != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800433 try {
Jack He16eeac32017-08-17 12:11:18 -0700434 return service.getConnectionState(device);
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800435 } catch (RemoteException e) {
436 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
437 return BluetoothProfile.STATE_DISCONNECTED;
Matthew Xiebf246ef2012-03-21 23:15:06 -0700438 }
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800439 }
Jack He16eeac32017-08-17 12:11:18 -0700440 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800441 return BluetoothProfile.STATE_DISCONNECTED;
442 }
443
444 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700445 * Set priority of the profile
446 *
447 * <p> The device should already be paired.
Jack Hea355e5e2017-08-22 16:06:54 -0700448 * Priority can be one of {@link #PRIORITY_ON} or
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700449 * {@link #PRIORITY_OFF},
450 *
451 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
452 * permission.
453 *
454 * @param device Paired bluetooth device
455 * @param priority
456 * @return true if priority is set, false on error
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800457 * @hide
458 */
459 public boolean setPriority(BluetoothDevice device, int priority) {
460 if (DBG) log("setPriority(" + device + ", " + priority + ")");
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700461 final IBluetoothHidHost service = mService;
Jack He16eeac32017-08-17 12:11:18 -0700462 if (service != null && isEnabled() && isValidDevice(device)) {
Jack He2992cd02017-08-22 21:21:23 -0700463 if (priority != BluetoothProfile.PRIORITY_OFF
464 && priority != BluetoothProfile.PRIORITY_ON) {
Jack Hea355e5e2017-08-22 16:06:54 -0700465 return false;
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800466 }
467 try {
Jack He16eeac32017-08-17 12:11:18 -0700468 return service.setPriority(device, priority);
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800469 } catch (RemoteException e) {
470 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
471 return false;
Matthew Xiebf246ef2012-03-21 23:15:06 -0700472 }
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800473 }
Jack He16eeac32017-08-17 12:11:18 -0700474 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800475 return false;
476 }
477
478 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700479 * Get the priority of the profile.
480 *
481 * <p> The priority can be any of:
482 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
483 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
484 *
485 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
486 *
487 * @param device Bluetooth device
488 * @return priority of the device
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800489 * @hide
490 */
491 public int getPriority(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700492 if (VDBG) log("getPriority(" + device + ")");
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700493 final IBluetoothHidHost service = mService;
Jack He16eeac32017-08-17 12:11:18 -0700494 if (service != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800495 try {
Jack He16eeac32017-08-17 12:11:18 -0700496 return service.getPriority(device);
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800497 } catch (RemoteException e) {
498 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
499 return BluetoothProfile.PRIORITY_OFF;
Matthew Xiebf246ef2012-03-21 23:15:06 -0700500 }
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800501 }
Jack He16eeac32017-08-17 12:11:18 -0700502 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800503 return BluetoothProfile.PRIORITY_OFF;
504 }
505
Matthew Xie9b693992013-10-10 11:21:40 -0700506 private final ServiceConnection mConnection = new ServiceConnection() {
Matthew Xiebf246ef2012-03-21 23:15:06 -0700507 public void onServiceConnected(ComponentName className, IBinder service) {
508 if (DBG) Log.d(TAG, "Proxy object connected");
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700509 mService = IBluetoothHidHost.Stub.asInterface(Binder.allowBlocking(service));
Matthew Xiebf246ef2012-03-21 23:15:06 -0700510
511 if (mServiceListener != null) {
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700512 mServiceListener.onServiceConnected(BluetoothProfile.HID_HOST,
513 BluetoothHidHost.this);
Matthew Xiebf246ef2012-03-21 23:15:06 -0700514 }
515 }
Jack Hea355e5e2017-08-22 16:06:54 -0700516
Matthew Xiebf246ef2012-03-21 23:15:06 -0700517 public void onServiceDisconnected(ComponentName className) {
518 if (DBG) Log.d(TAG, "Proxy object disconnected");
519 mService = null;
520 if (mServiceListener != null) {
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700521 mServiceListener.onServiceDisconnected(BluetoothProfile.HID_HOST);
Matthew Xiebf246ef2012-03-21 23:15:06 -0700522 }
523 }
524 };
525
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800526 private boolean isEnabled() {
Jack He16eeac32017-08-17 12:11:18 -0700527 return mAdapter.getState() == BluetoothAdapter.STATE_ON;
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800528 }
529
Jack He16eeac32017-08-17 12:11:18 -0700530 private static boolean isValidDevice(BluetoothDevice device) {
531 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700532 }
533
Priti Aghera349e62f2012-04-09 12:13:17 -0700534 /**
535 * Initiate virtual unplug for a HID input device.
536 *
537 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
538 *
539 * @param device Remote Bluetooth Device
Jack Hea355e5e2017-08-22 16:06:54 -0700540 * @return false on immediate error, true otherwise
Priti Aghera349e62f2012-04-09 12:13:17 -0700541 * @hide
542 */
543 public boolean virtualUnplug(BluetoothDevice device) {
544 if (DBG) log("virtualUnplug(" + device + ")");
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700545 final IBluetoothHidHost service = mService;
Jack He16eeac32017-08-17 12:11:18 -0700546 if (service != null && isEnabled() && isValidDevice(device)) {
Priti Aghera349e62f2012-04-09 12:13:17 -0700547 try {
Jack He16eeac32017-08-17 12:11:18 -0700548 return service.virtualUnplug(device);
Priti Aghera349e62f2012-04-09 12:13:17 -0700549 } catch (RemoteException e) {
550 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
551 return false;
552 }
553 }
554
Jack He16eeac32017-08-17 12:11:18 -0700555 if (service == null) Log.w(TAG, "Proxy not attached to service");
Priti Aghera349e62f2012-04-09 12:13:17 -0700556 return false;
557
558 }
559
560 /**
Jack Hea355e5e2017-08-22 16:06:54 -0700561 * Send Get_Protocol_Mode command to the connected HID input device.
562 *
563 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
564 *
565 * @param device Remote Bluetooth Device
566 * @return false on immediate error, true otherwise
567 * @hide
568 */
Priti Aghera349e62f2012-04-09 12:13:17 -0700569 public boolean getProtocolMode(BluetoothDevice device) {
Matthew Xie563e4142012-10-09 22:10:37 -0700570 if (VDBG) log("getProtocolMode(" + device + ")");
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700571 final IBluetoothHidHost service = mService;
Jack He16eeac32017-08-17 12:11:18 -0700572 if (service != null && isEnabled() && isValidDevice(device)) {
Priti Aghera349e62f2012-04-09 12:13:17 -0700573 try {
Jack He16eeac32017-08-17 12:11:18 -0700574 return service.getProtocolMode(device);
Priti Aghera349e62f2012-04-09 12:13:17 -0700575 } catch (RemoteException e) {
576 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
577 return false;
578 }
579 }
Jack He16eeac32017-08-17 12:11:18 -0700580 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jack Hea355e5e2017-08-22 16:06:54 -0700581 return false;
Priti Aghera349e62f2012-04-09 12:13:17 -0700582 }
583
584 /**
585 * Send Set_Protocol_Mode command to the connected HID input device.
586 *
587 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
588 *
589 * @param device Remote Bluetooth Device
Jack Hea355e5e2017-08-22 16:06:54 -0700590 * @return false on immediate error, true otherwise
Priti Aghera349e62f2012-04-09 12:13:17 -0700591 * @hide
592 */
593 public boolean setProtocolMode(BluetoothDevice device, int protocolMode) {
594 if (DBG) log("setProtocolMode(" + device + ")");
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700595 final IBluetoothHidHost service = mService;
Jack He16eeac32017-08-17 12:11:18 -0700596 if (service != null && isEnabled() && isValidDevice(device)) {
Priti Aghera349e62f2012-04-09 12:13:17 -0700597 try {
Jack He16eeac32017-08-17 12:11:18 -0700598 return service.setProtocolMode(device, protocolMode);
Priti Aghera349e62f2012-04-09 12:13:17 -0700599 } catch (RemoteException e) {
600 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
601 return false;
602 }
603 }
Jack He16eeac32017-08-17 12:11:18 -0700604 if (service == null) Log.w(TAG, "Proxy not attached to service");
Priti Aghera349e62f2012-04-09 12:13:17 -0700605 return false;
606 }
607
608 /**
609 * Send Get_Report command to the connected HID input device.
610 *
611 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
612 *
613 * @param device Remote Bluetooth Device
614 * @param reportType Report type
615 * @param reportId Report ID
616 * @param bufferSize Report receiving buffer size
Jack Hea355e5e2017-08-22 16:06:54 -0700617 * @return false on immediate error, true otherwise
Priti Aghera349e62f2012-04-09 12:13:17 -0700618 * @hide
619 */
Jack Hea355e5e2017-08-22 16:06:54 -0700620 public boolean getReport(BluetoothDevice device, byte reportType, byte reportId,
621 int bufferSize) {
622 if (VDBG) {
Jack He16eeac32017-08-17 12:11:18 -0700623 log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId
624 + "bufferSize=" + bufferSize);
Jack Hea355e5e2017-08-22 16:06:54 -0700625 }
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700626 final IBluetoothHidHost service = mService;
Jack He16eeac32017-08-17 12:11:18 -0700627 if (service != null && isEnabled() && isValidDevice(device)) {
Priti Aghera349e62f2012-04-09 12:13:17 -0700628 try {
Jack He16eeac32017-08-17 12:11:18 -0700629 return service.getReport(device, reportType, reportId, bufferSize);
Priti Aghera349e62f2012-04-09 12:13:17 -0700630 } catch (RemoteException e) {
631 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
632 return false;
633 }
634 }
Jack He16eeac32017-08-17 12:11:18 -0700635 if (service == null) Log.w(TAG, "Proxy not attached to service");
Priti Aghera349e62f2012-04-09 12:13:17 -0700636 return false;
637 }
638
639 /**
640 * Send Set_Report command to the connected HID input device.
641 *
642 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
643 *
644 * @param device Remote Bluetooth Device
645 * @param reportType Report type
646 * @param report Report receiving buffer size
Jack Hea355e5e2017-08-22 16:06:54 -0700647 * @return false on immediate error, true otherwise
Priti Aghera349e62f2012-04-09 12:13:17 -0700648 * @hide
649 */
650 public boolean setReport(BluetoothDevice device, byte reportType, String report) {
Mike J. Chen8faffa42014-03-04 17:27:16 -0800651 if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report);
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700652 final IBluetoothHidHost service = mService;
Jack He16eeac32017-08-17 12:11:18 -0700653 if (service != null && isEnabled() && isValidDevice(device)) {
Priti Aghera349e62f2012-04-09 12:13:17 -0700654 try {
Jack He16eeac32017-08-17 12:11:18 -0700655 return service.setReport(device, reportType, report);
Priti Aghera349e62f2012-04-09 12:13:17 -0700656 } catch (RemoteException e) {
657 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
658 return false;
659 }
660 }
Jack He16eeac32017-08-17 12:11:18 -0700661 if (service == null) Log.w(TAG, "Proxy not attached to service");
Priti Aghera349e62f2012-04-09 12:13:17 -0700662 return false;
663 }
664
665 /**
666 * Send Send_Data command to the connected HID input device.
667 *
668 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
669 *
670 * @param device Remote Bluetooth Device
Ang Li707fd392014-06-25 13:16:01 -0700671 * @param report Report to send
Jack Hea355e5e2017-08-22 16:06:54 -0700672 * @return false on immediate error, true otherwise
Priti Aghera349e62f2012-04-09 12:13:17 -0700673 * @hide
674 */
675 public boolean sendData(BluetoothDevice device, String report) {
676 if (DBG) log("sendData(" + device + "), report=" + report);
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700677 final IBluetoothHidHost service = mService;
Jack He16eeac32017-08-17 12:11:18 -0700678 if (service != null && isEnabled() && isValidDevice(device)) {
Priti Aghera349e62f2012-04-09 12:13:17 -0700679 try {
Jack He16eeac32017-08-17 12:11:18 -0700680 return service.sendData(device, report);
Priti Aghera349e62f2012-04-09 12:13:17 -0700681 } catch (RemoteException e) {
682 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
683 return false;
684 }
685 }
Jack He16eeac32017-08-17 12:11:18 -0700686 if (service == null) Log.w(TAG, "Proxy not attached to service");
Priti Aghera349e62f2012-04-09 12:13:17 -0700687 return false;
688 }
Hemant Guptacef9ce32013-07-30 16:09:20 +0530689
690 /**
691 * Send Get_Idle_Time command to the connected HID input device.
692 *
693 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
694 *
695 * @param device Remote Bluetooth Device
Jack Hea355e5e2017-08-22 16:06:54 -0700696 * @return false on immediate error, true otherwise
Hemant Guptacef9ce32013-07-30 16:09:20 +0530697 * @hide
698 */
699 public boolean getIdleTime(BluetoothDevice device) {
700 if (DBG) log("getIdletime(" + device + ")");
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700701 final IBluetoothHidHost service = mService;
Jack He16eeac32017-08-17 12:11:18 -0700702 if (service != null && isEnabled() && isValidDevice(device)) {
Hemant Guptacef9ce32013-07-30 16:09:20 +0530703 try {
Jack He16eeac32017-08-17 12:11:18 -0700704 return service.getIdleTime(device);
Hemant Guptacef9ce32013-07-30 16:09:20 +0530705 } catch (RemoteException e) {
706 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
707 return false;
708 }
709 }
Jack He16eeac32017-08-17 12:11:18 -0700710 if (service == null) Log.w(TAG, "Proxy not attached to service");
Hemant Guptacef9ce32013-07-30 16:09:20 +0530711 return false;
712 }
713
714 /**
715 * Send Set_Idle_Time command to the connected HID input device.
716 *
717 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
718 *
719 * @param device Remote Bluetooth Device
720 * @param idleTime Idle time to be set on HID Device
Jack Hea355e5e2017-08-22 16:06:54 -0700721 * @return false on immediate error, true otherwise
Hemant Guptacef9ce32013-07-30 16:09:20 +0530722 * @hide
723 */
724 public boolean setIdleTime(BluetoothDevice device, byte idleTime) {
725 if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime);
Hansong Zhangc26c76c2017-10-20 15:55:59 -0700726 final IBluetoothHidHost service = mService;
Jack He16eeac32017-08-17 12:11:18 -0700727 if (service != null && isEnabled() && isValidDevice(device)) {
Hemant Guptacef9ce32013-07-30 16:09:20 +0530728 try {
Jack He16eeac32017-08-17 12:11:18 -0700729 return service.setIdleTime(device, idleTime);
Hemant Guptacef9ce32013-07-30 16:09:20 +0530730 } catch (RemoteException e) {
731 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
732 return false;
733 }
734 }
Jack He16eeac32017-08-17 12:11:18 -0700735 if (service == null) Log.w(TAG, "Proxy not attached to service");
Hemant Guptacef9ce32013-07-30 16:09:20 +0530736 return false;
737 }
738
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700739 private static void log(String msg) {
Jack Hea355e5e2017-08-22 16:06:54 -0700740 Log.d(TAG, msg);
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700741 }
742}