blob: 4afb382c066050fb13df414a74f81bc206b86514 [file] [log] [blame]
Rahul Ravikumar05336002019-10-14 15:04:32 -07001/*
2 * Copyright (C) 2011 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
17package android.bluetooth;
18
19import android.annotation.SdkConstant;
20import android.annotation.SdkConstant.SdkConstantType;
21import android.content.Context;
22import android.os.Binder;
23import android.os.IBinder;
24import android.os.RemoteException;
25import android.util.Log;
26
27import java.util.ArrayList;
28import java.util.List;
29
30
31/**
32 * This class provides the public APIs to control the Bluetooth Input
33 * Device Profile.
34 *
35 * <p>BluetoothHidHost is a proxy object for controlling the Bluetooth
36 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
37 * the BluetoothHidHost proxy object.
38 *
39 * <p>Each method is protected with its appropriate permission.
40 *
41 * @hide
42 */
43public final class BluetoothHidHost implements BluetoothProfile {
44 private static final String TAG = "BluetoothHidHost";
45 private static final boolean DBG = true;
46 private static final boolean VDBG = false;
47
48 /**
49 * Intent used to broadcast the change in connection state of the Input
50 * Device profile.
51 *
52 * <p>This intent will have 3 extras:
53 * <ul>
54 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
55 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
56 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
57 * </ul>
58 *
59 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
60 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
61 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
62 *
63 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
64 * receive.
65 */
66 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
67 public static final String ACTION_CONNECTION_STATE_CHANGED =
68 "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED";
69
70 /**
71 * @hide
72 */
73 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
74 public static final String ACTION_PROTOCOL_MODE_CHANGED =
75 "android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED";
76
77 /**
78 * @hide
79 */
80 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
81 public static final String ACTION_HANDSHAKE =
82 "android.bluetooth.input.profile.action.HANDSHAKE";
83
84 /**
85 * @hide
86 */
87 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
88 public static final String ACTION_REPORT =
89 "android.bluetooth.input.profile.action.REPORT";
90
91 /**
92 * @hide
93 */
94 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
95 public static final String ACTION_VIRTUAL_UNPLUG_STATUS =
96 "android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS";
97
98 /**
99 * @hide
100 */
101 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
102 public static final String ACTION_IDLE_TIME_CHANGED =
103 "android.bluetooth.input.profile.action.IDLE_TIME_CHANGED";
104
105 /**
106 * Return codes for the connect and disconnect Bluez / Dbus calls.
107 *
108 * @hide
109 */
110 public static final int INPUT_DISCONNECT_FAILED_NOT_CONNECTED = 5000;
111
112 /**
113 * @hide
114 */
115 public static final int INPUT_CONNECT_FAILED_ALREADY_CONNECTED = 5001;
116
117 /**
118 * @hide
119 */
120 public static final int INPUT_CONNECT_FAILED_ATTEMPT_FAILED = 5002;
121
122 /**
123 * @hide
124 */
125 public static final int INPUT_OPERATION_GENERIC_FAILURE = 5003;
126
127 /**
128 * @hide
129 */
130 public static final int INPUT_OPERATION_SUCCESS = 5004;
131
132 /**
133 * @hide
134 */
135 public static final int PROTOCOL_REPORT_MODE = 0;
136
137 /**
138 * @hide
139 */
140 public static final int PROTOCOL_BOOT_MODE = 1;
141
142 /**
143 * @hide
144 */
145 public static final int PROTOCOL_UNSUPPORTED_MODE = 255;
146
147 /* int reportType, int reportType, int bufferSize */
148 /**
149 * @hide
150 */
151 public static final byte REPORT_TYPE_INPUT = 1;
152
153 /**
154 * @hide
155 */
156 public static final byte REPORT_TYPE_OUTPUT = 2;
157
158 /**
159 * @hide
160 */
161 public static final byte REPORT_TYPE_FEATURE = 3;
162
163 /**
164 * @hide
165 */
166 public static final int VIRTUAL_UNPLUG_STATUS_SUCCESS = 0;
167
168 /**
169 * @hide
170 */
171 public static final int VIRTUAL_UNPLUG_STATUS_FAIL = 1;
172
173 /**
174 * @hide
175 */
176 public static final String EXTRA_PROTOCOL_MODE =
177 "android.bluetooth.BluetoothHidHost.extra.PROTOCOL_MODE";
178
179 /**
180 * @hide
181 */
182 public static final String EXTRA_REPORT_TYPE =
183 "android.bluetooth.BluetoothHidHost.extra.REPORT_TYPE";
184
185 /**
186 * @hide
187 */
188 public static final String EXTRA_REPORT_ID =
189 "android.bluetooth.BluetoothHidHost.extra.REPORT_ID";
190
191 /**
192 * @hide
193 */
194 public static final String EXTRA_REPORT_BUFFER_SIZE =
195 "android.bluetooth.BluetoothHidHost.extra.REPORT_BUFFER_SIZE";
196
197 /**
198 * @hide
199 */
200 public static final String EXTRA_REPORT = "android.bluetooth.BluetoothHidHost.extra.REPORT";
201
202 /**
203 * @hide
204 */
205 public static final String EXTRA_STATUS = "android.bluetooth.BluetoothHidHost.extra.STATUS";
206
207 /**
208 * @hide
209 */
210 public static final String EXTRA_VIRTUAL_UNPLUG_STATUS =
211 "android.bluetooth.BluetoothHidHost.extra.VIRTUAL_UNPLUG_STATUS";
212
213 /**
214 * @hide
215 */
216 public static final String EXTRA_IDLE_TIME =
217 "android.bluetooth.BluetoothHidHost.extra.IDLE_TIME";
218
219 private BluetoothAdapter mAdapter;
220 private final BluetoothProfileConnector<IBluetoothHidHost> mProfileConnector =
221 new BluetoothProfileConnector(this, BluetoothProfile.HID_HOST,
222 "BluetoothHidHost", IBluetoothHidHost.class.getName()) {
223 @Override
224 public IBluetoothHidHost getServiceInterface(IBinder service) {
225 return IBluetoothHidHost.Stub.asInterface(Binder.allowBlocking(service));
226 }
227 };
228
229 /**
230 * Create a BluetoothHidHost proxy object for interacting with the local
231 * Bluetooth Service which handles the InputDevice profile
232 */
233 /*package*/ BluetoothHidHost(Context context, ServiceListener listener) {
234 mAdapter = BluetoothAdapter.getDefaultAdapter();
235 mProfileConnector.connect(context, listener);
236 }
237
238 /*package*/ void close() {
239 if (VDBG) log("close()");
240 mProfileConnector.disconnect();
241 }
242
243 private IBluetoothHidHost getService() {
244 return mProfileConnector.getService();
245 }
246
247 /**
248 * Initiate connection to a profile of the remote bluetooth device.
249 *
250 * <p> The system supports connection to multiple input devices.
251 *
252 * <p> This API returns false in scenarios like the profile on the
253 * device is already connected or Bluetooth is not turned on.
254 * When this API returns true, it is guaranteed that
255 * connection state intent for the profile will be broadcasted with
256 * the state. Users can get the connection state of the profile
257 * from this intent.
258 *
259 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
260 * permission.
261 *
262 * @param device Remote Bluetooth Device
263 * @return false on immediate error, true otherwise
264 * @hide
265 */
266 public boolean connect(BluetoothDevice device) {
267 if (DBG) log("connect(" + device + ")");
268 final IBluetoothHidHost service = getService();
269 if (service != null && isEnabled() && isValidDevice(device)) {
270 try {
271 return service.connect(device);
272 } catch (RemoteException e) {
273 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
274 return false;
275 }
276 }
277 if (service == null) Log.w(TAG, "Proxy not attached to service");
278 return false;
279 }
280
281 /**
282 * Initiate disconnection from a profile
283 *
284 * <p> This API will return false in scenarios like the profile on the
285 * Bluetooth device is not in connected state etc. When this API returns,
286 * true, it is guaranteed that the connection state change
287 * intent will be broadcasted with the state. Users can get the
288 * disconnection state of the profile from this intent.
289 *
290 * <p> If the disconnection is initiated by a remote device, the state
291 * will transition from {@link #STATE_CONNECTED} to
292 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
293 * host (local) device the state will transition from
294 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
295 * state {@link #STATE_DISCONNECTED}. The transition to
296 * {@link #STATE_DISCONNECTING} can be used to distinguish between the
297 * two scenarios.
298 *
299 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
300 * permission.
301 *
302 * @param device Remote Bluetooth Device
303 * @return false on immediate error, true otherwise
304 * @hide
305 */
306 public boolean disconnect(BluetoothDevice device) {
307 if (DBG) log("disconnect(" + device + ")");
308 final IBluetoothHidHost service = getService();
309 if (service != null && isEnabled() && isValidDevice(device)) {
310 try {
311 return service.disconnect(device);
312 } catch (RemoteException e) {
313 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
314 return false;
315 }
316 }
317 if (service == null) Log.w(TAG, "Proxy not attached to service");
318 return false;
319 }
320
321 /**
322 * {@inheritDoc}
323 */
324 @Override
325 public List<BluetoothDevice> getConnectedDevices() {
326 if (VDBG) log("getConnectedDevices()");
327 final IBluetoothHidHost service = getService();
328 if (service != null && isEnabled()) {
329 try {
330 return service.getConnectedDevices();
331 } catch (RemoteException e) {
332 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
333 return new ArrayList<BluetoothDevice>();
334 }
335 }
336 if (service == null) Log.w(TAG, "Proxy not attached to service");
337 return new ArrayList<BluetoothDevice>();
338 }
339
340 /**
341 * {@inheritDoc}
342 */
343 @Override
344 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
345 if (VDBG) log("getDevicesMatchingStates()");
346 final IBluetoothHidHost service = getService();
347 if (service != null && isEnabled()) {
348 try {
349 return service.getDevicesMatchingConnectionStates(states);
350 } catch (RemoteException e) {
351 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
352 return new ArrayList<BluetoothDevice>();
353 }
354 }
355 if (service == null) Log.w(TAG, "Proxy not attached to service");
356 return new ArrayList<BluetoothDevice>();
357 }
358
359 /**
360 * {@inheritDoc}
361 */
362 @Override
363 public int getConnectionState(BluetoothDevice device) {
364 if (VDBG) log("getState(" + device + ")");
365 final IBluetoothHidHost service = getService();
366 if (service != null && isEnabled() && isValidDevice(device)) {
367 try {
368 return service.getConnectionState(device);
369 } catch (RemoteException e) {
370 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
371 return BluetoothProfile.STATE_DISCONNECTED;
372 }
373 }
374 if (service == null) Log.w(TAG, "Proxy not attached to service");
375 return BluetoothProfile.STATE_DISCONNECTED;
376 }
377
378 /**
379 * Set priority of the profile
380 *
381 * <p> The device should already be paired.
382 * Priority can be one of {@link #PRIORITY_ON} or
383 * {@link #PRIORITY_OFF},
384 *
385 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
386 * permission.
387 *
388 * @param device Paired bluetooth device
389 * @param priority
390 * @return true if priority is set, false on error
391 * @hide
392 */
393 public boolean setPriority(BluetoothDevice device, int priority) {
394 if (DBG) log("setPriority(" + device + ", " + priority + ")");
395 final IBluetoothHidHost service = getService();
396 if (service != null && isEnabled() && isValidDevice(device)) {
397 if (priority != BluetoothProfile.PRIORITY_OFF
398 && priority != BluetoothProfile.PRIORITY_ON) {
399 return false;
400 }
401 try {
402 return service.setPriority(device, priority);
403 } catch (RemoteException e) {
404 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
405 return false;
406 }
407 }
408 if (service == null) Log.w(TAG, "Proxy not attached to service");
409 return false;
410 }
411
412 /**
413 * Get the priority of the profile.
414 *
415 * <p> The priority can be any of:
416 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
417 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
418 *
419 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
420 *
421 * @param device Bluetooth device
422 * @return priority of the device
423 * @hide
424 */
425 public int getPriority(BluetoothDevice device) {
426 if (VDBG) log("getPriority(" + device + ")");
427 final IBluetoothHidHost service = getService();
428 if (service != null && isEnabled() && isValidDevice(device)) {
429 try {
430 return service.getPriority(device);
431 } catch (RemoteException e) {
432 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
433 return BluetoothProfile.PRIORITY_OFF;
434 }
435 }
436 if (service == null) Log.w(TAG, "Proxy not attached to service");
437 return BluetoothProfile.PRIORITY_OFF;
438 }
439
440 private boolean isEnabled() {
441 return mAdapter.getState() == BluetoothAdapter.STATE_ON;
442 }
443
444 private static boolean isValidDevice(BluetoothDevice device) {
445 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
446 }
447
448 /**
449 * Initiate virtual unplug for a HID input device.
450 *
451 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
452 *
453 * @param device Remote Bluetooth Device
454 * @return false on immediate error, true otherwise
455 * @hide
456 */
457 public boolean virtualUnplug(BluetoothDevice device) {
458 if (DBG) log("virtualUnplug(" + device + ")");
459 final IBluetoothHidHost service = getService();
460 if (service != null && isEnabled() && isValidDevice(device)) {
461 try {
462 return service.virtualUnplug(device);
463 } catch (RemoteException e) {
464 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
465 return false;
466 }
467 }
468
469 if (service == null) Log.w(TAG, "Proxy not attached to service");
470 return false;
471
472 }
473
474 /**
475 * Send Get_Protocol_Mode command to the connected HID input device.
476 *
477 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
478 *
479 * @param device Remote Bluetooth Device
480 * @return false on immediate error, true otherwise
481 * @hide
482 */
483 public boolean getProtocolMode(BluetoothDevice device) {
484 if (VDBG) log("getProtocolMode(" + device + ")");
485 final IBluetoothHidHost service = getService();
486 if (service != null && isEnabled() && isValidDevice(device)) {
487 try {
488 return service.getProtocolMode(device);
489 } catch (RemoteException e) {
490 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
491 return false;
492 }
493 }
494 if (service == null) Log.w(TAG, "Proxy not attached to service");
495 return false;
496 }
497
498 /**
499 * Send Set_Protocol_Mode command to the connected HID input device.
500 *
501 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
502 *
503 * @param device Remote Bluetooth Device
504 * @return false on immediate error, true otherwise
505 * @hide
506 */
507 public boolean setProtocolMode(BluetoothDevice device, int protocolMode) {
508 if (DBG) log("setProtocolMode(" + device + ")");
509 final IBluetoothHidHost service = getService();
510 if (service != null && isEnabled() && isValidDevice(device)) {
511 try {
512 return service.setProtocolMode(device, protocolMode);
513 } catch (RemoteException e) {
514 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
515 return false;
516 }
517 }
518 if (service == null) Log.w(TAG, "Proxy not attached to service");
519 return false;
520 }
521
522 /**
523 * Send Get_Report command to the connected HID input device.
524 *
525 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
526 *
527 * @param device Remote Bluetooth Device
528 * @param reportType Report type
529 * @param reportId Report ID
530 * @param bufferSize Report receiving buffer size
531 * @return false on immediate error, true otherwise
532 * @hide
533 */
534 public boolean getReport(BluetoothDevice device, byte reportType, byte reportId,
535 int bufferSize) {
536 if (VDBG) {
537 log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId
538 + "bufferSize=" + bufferSize);
539 }
540 final IBluetoothHidHost service = getService();
541 if (service != null && isEnabled() && isValidDevice(device)) {
542 try {
543 return service.getReport(device, reportType, reportId, bufferSize);
544 } catch (RemoteException e) {
545 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
546 return false;
547 }
548 }
549 if (service == null) Log.w(TAG, "Proxy not attached to service");
550 return false;
551 }
552
553 /**
554 * Send Set_Report command to the connected HID input device.
555 *
556 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
557 *
558 * @param device Remote Bluetooth Device
559 * @param reportType Report type
560 * @param report Report receiving buffer size
561 * @return false on immediate error, true otherwise
562 * @hide
563 */
564 public boolean setReport(BluetoothDevice device, byte reportType, String report) {
565 if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report);
566 final IBluetoothHidHost service = getService();
567 if (service != null && isEnabled() && isValidDevice(device)) {
568 try {
569 return service.setReport(device, reportType, report);
570 } catch (RemoteException e) {
571 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
572 return false;
573 }
574 }
575 if (service == null) Log.w(TAG, "Proxy not attached to service");
576 return false;
577 }
578
579 /**
580 * Send Send_Data command to the connected HID input device.
581 *
582 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
583 *
584 * @param device Remote Bluetooth Device
585 * @param report Report to send
586 * @return false on immediate error, true otherwise
587 * @hide
588 */
589 public boolean sendData(BluetoothDevice device, String report) {
590 if (DBG) log("sendData(" + device + "), report=" + report);
591 final IBluetoothHidHost service = getService();
592 if (service != null && isEnabled() && isValidDevice(device)) {
593 try {
594 return service.sendData(device, report);
595 } catch (RemoteException e) {
596 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
597 return false;
598 }
599 }
600 if (service == null) Log.w(TAG, "Proxy not attached to service");
601 return false;
602 }
603
604 /**
605 * Send Get_Idle_Time command to the connected HID input device.
606 *
607 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
608 *
609 * @param device Remote Bluetooth Device
610 * @return false on immediate error, true otherwise
611 * @hide
612 */
613 public boolean getIdleTime(BluetoothDevice device) {
614 if (DBG) log("getIdletime(" + device + ")");
615 final IBluetoothHidHost service = getService();
616 if (service != null && isEnabled() && isValidDevice(device)) {
617 try {
618 return service.getIdleTime(device);
619 } catch (RemoteException e) {
620 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
621 return false;
622 }
623 }
624 if (service == null) Log.w(TAG, "Proxy not attached to service");
625 return false;
626 }
627
628 /**
629 * Send Set_Idle_Time command to the connected HID input device.
630 *
631 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
632 *
633 * @param device Remote Bluetooth Device
634 * @param idleTime Idle time to be set on HID Device
635 * @return false on immediate error, true otherwise
636 * @hide
637 */
638 public boolean setIdleTime(BluetoothDevice device, byte idleTime) {
639 if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime);
640 final IBluetoothHidHost service = getService();
641 if (service != null && isEnabled() && isValidDevice(device)) {
642 try {
643 return service.setIdleTime(device, idleTime);
644 } catch (RemoteException e) {
645 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
646 return false;
647 }
648 }
649 if (service == null) Log.w(TAG, "Proxy not attached to service");
650 return false;
651 }
652
653 private static void log(String msg) {
654 Log.d(TAG, msg);
655 }
656}