1 /* 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 17 package android.bluetooth; 18 19 import android.annotation.SdkConstant; 20 import android.annotation.SdkConstant.SdkConstantType; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.ServiceConnection; 25 import android.os.Binder; 26 import android.os.IBinder; 27 import android.os.RemoteException; 28 import android.util.Log; 29 30 import java.util.ArrayList; 31 import java.util.List; 32 33 34 /** 35 * This class provides the public APIs to control the Bluetooth Input 36 * Device Profile. 37 * 38 * <p>BluetoothHidHost is a proxy object for controlling the Bluetooth 39 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get 40 * the BluetoothHidHost proxy object. 41 * 42 * <p>Each method is protected with its appropriate permission. 43 * 44 * @hide 45 */ 46 public final class BluetoothHidHost implements BluetoothProfile { 47 private static final String TAG = "BluetoothHidHost"; 48 private static final boolean DBG = true; 49 private static final boolean VDBG = false; 50 51 /** 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> 57 * <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> 60 * </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. 68 */ 69 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 70 public static final String ACTION_CONNECTION_STATE_CHANGED = 71 "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED"; 72 73 /** 74 * @hide 75 */ 76 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 77 public static final String ACTION_PROTOCOL_MODE_CHANGED = 78 "android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED"; 79 80 /** 81 * @hide 82 */ 83 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 84 public static final String ACTION_HANDSHAKE = 85 "android.bluetooth.input.profile.action.HANDSHAKE"; 86 87 /** 88 * @hide 89 */ 90 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 91 public static final String ACTION_REPORT = 92 "android.bluetooth.input.profile.action.REPORT"; 93 94 /** 95 * @hide 96 */ 97 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 98 public static final String ACTION_VIRTUAL_UNPLUG_STATUS = 99 "android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS"; 100 101 /** 102 * @hide 103 */ 104 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 105 public static final String ACTION_IDLE_TIME_CHANGED = 106 "android.bluetooth.input.profile.action.IDLE_TIME_CHANGED"; 107 108 /** 109 * Return codes for the connect and disconnect Bluez / Dbus calls. 110 * 111 * @hide 112 */ 113 public static final int INPUT_DISCONNECT_FAILED_NOT_CONNECTED = 5000; 114 115 /** 116 * @hide 117 */ 118 public static final int INPUT_CONNECT_FAILED_ALREADY_CONNECTED = 5001; 119 120 /** 121 * @hide 122 */ 123 public static final int INPUT_CONNECT_FAILED_ATTEMPT_FAILED = 5002; 124 125 /** 126 * @hide 127 */ 128 public static final int INPUT_OPERATION_GENERIC_FAILURE = 5003; 129 130 /** 131 * @hide 132 */ 133 public static final int INPUT_OPERATION_SUCCESS = 5004; 134 135 /** 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 */ 154 public static final byte REPORT_TYPE_INPUT = 1; 155 156 /** 157 * @hide 158 */ 159 public static final byte REPORT_TYPE_OUTPUT = 2; 160 161 /** 162 * @hide 163 */ 164 public static final byte REPORT_TYPE_FEATURE = 3; 165 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 */ 179 public static final String EXTRA_PROTOCOL_MODE = 180 "android.bluetooth.BluetoothHidHost.extra.PROTOCOL_MODE"; 181 182 /** 183 * @hide 184 */ 185 public static final String EXTRA_REPORT_TYPE = 186 "android.bluetooth.BluetoothHidHost.extra.REPORT_TYPE"; 187 188 /** 189 * @hide 190 */ 191 public static final String EXTRA_REPORT_ID = 192 "android.bluetooth.BluetoothHidHost.extra.REPORT_ID"; 193 194 /** 195 * @hide 196 */ 197 public static final String EXTRA_REPORT_BUFFER_SIZE = 198 "android.bluetooth.BluetoothHidHost.extra.REPORT_BUFFER_SIZE"; 199 200 /** 201 * @hide 202 */ 203 public static final String EXTRA_REPORT = "android.bluetooth.BluetoothHidHost.extra.REPORT"; 204 205 /** 206 * @hide 207 */ 208 public static final String EXTRA_STATUS = "android.bluetooth.BluetoothHidHost.extra.STATUS"; 209 210 /** 211 * @hide 212 */ 213 public static final String EXTRA_VIRTUAL_UNPLUG_STATUS = 214 "android.bluetooth.BluetoothHidHost.extra.VIRTUAL_UNPLUG_STATUS"; 215 216 /** 217 * @hide 218 */ 219 public static final String EXTRA_IDLE_TIME = 220 "android.bluetooth.BluetoothHidHost.extra.IDLE_TIME"; 221 222 private Context mContext; 223 private ServiceListener mServiceListener; 224 private BluetoothAdapter mAdapter; 225 private volatile IBluetoothHidHost mService; 226 227 private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = 228 new IBluetoothStateChangeCallback.Stub() { 229 public void onBluetoothStateChange(boolean up) { 230 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); 231 if (!up) { 232 if (VDBG) Log.d(TAG, "Unbinding service..."); 233 synchronized (mConnection) { 234 try { 235 mService = null; 236 mContext.unbindService(mConnection); 237 } catch (Exception re) { 238 Log.e(TAG, "", re); 239 } 240 } 241 } else { 242 synchronized (mConnection) { 243 try { 244 if (mService == null) { 245 if (VDBG) Log.d(TAG, "Binding service..."); 246 doBind(); 247 } 248 } catch (Exception re) { 249 Log.e(TAG, "", re); 250 } 251 } 252 } 253 } 254 }; 255 256 /** 257 * Create a BluetoothHidHost proxy object for interacting with the local 258 * Bluetooth Service which handles the InputDevice profile 259 */ BluetoothHidHost(Context context, ServiceListener l)260 /*package*/ BluetoothHidHost(Context context, ServiceListener l) { 261 mContext = context; 262 mServiceListener = l; 263 mAdapter = BluetoothAdapter.getDefaultAdapter(); 264 265 IBluetoothManager mgr = mAdapter.getBluetoothManager(); 266 if (mgr != null) { 267 try { 268 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); 269 } catch (RemoteException e) { 270 Log.e(TAG, "", e); 271 } 272 } 273 274 doBind(); 275 } 276 doBind()277 boolean doBind() { 278 Intent intent = new Intent(IBluetoothHidHost.class.getName()); 279 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); 280 intent.setComponent(comp); 281 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, 282 mContext.getUser())) { 283 Log.e(TAG, "Could not bind to Bluetooth HID Service with " + intent); 284 return false; 285 } 286 return true; 287 } 288 close()289 /*package*/ void close() { 290 if (VDBG) log("close()"); 291 IBluetoothManager mgr = mAdapter.getBluetoothManager(); 292 if (mgr != null) { 293 try { 294 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); 295 } catch (Exception e) { 296 Log.e(TAG, "", e); 297 } 298 } 299 300 synchronized (mConnection) { 301 if (mService != null) { 302 try { 303 mService = null; 304 mContext.unbindService(mConnection); 305 } catch (Exception re) { 306 Log.e(TAG, "", re); 307 } 308 } 309 } 310 mServiceListener = null; 311 } 312 313 /** 314 * 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 329 * @return false on immediate error, true otherwise 330 * @hide 331 */ connect(BluetoothDevice device)332 public boolean connect(BluetoothDevice device) { 333 if (DBG) log("connect(" + device + ")"); 334 final IBluetoothHidHost service = mService; 335 if (service != null && isEnabled() && isValidDevice(device)) { 336 try { 337 return service.connect(device); 338 } catch (RemoteException e) { 339 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 340 return false; 341 } 342 } 343 if (service == null) Log.w(TAG, "Proxy not attached to service"); 344 return false; 345 } 346 347 /** 348 * 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 369 * @return false on immediate error, true otherwise 370 * @hide 371 */ disconnect(BluetoothDevice device)372 public boolean disconnect(BluetoothDevice device) { 373 if (DBG) log("disconnect(" + device + ")"); 374 final IBluetoothHidHost service = mService; 375 if (service != null && isEnabled() && isValidDevice(device)) { 376 try { 377 return service.disconnect(device); 378 } catch (RemoteException e) { 379 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 380 return false; 381 } 382 } 383 if (service == null) Log.w(TAG, "Proxy not attached to service"); 384 return false; 385 } 386 387 /** 388 * {@inheritDoc} 389 */ 390 @Override getConnectedDevices()391 public List<BluetoothDevice> getConnectedDevices() { 392 if (VDBG) log("getConnectedDevices()"); 393 final IBluetoothHidHost service = mService; 394 if (service != null && isEnabled()) { 395 try { 396 return service.getConnectedDevices(); 397 } catch (RemoteException e) { 398 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 399 return new ArrayList<BluetoothDevice>(); 400 } 401 } 402 if (service == null) Log.w(TAG, "Proxy not attached to service"); 403 return new ArrayList<BluetoothDevice>(); 404 } 405 406 /** 407 * {@inheritDoc} 408 */ 409 @Override getDevicesMatchingConnectionStates(int[] states)410 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 411 if (VDBG) log("getDevicesMatchingStates()"); 412 final IBluetoothHidHost service = mService; 413 if (service != null && isEnabled()) { 414 try { 415 return service.getDevicesMatchingConnectionStates(states); 416 } catch (RemoteException e) { 417 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 418 return new ArrayList<BluetoothDevice>(); 419 } 420 } 421 if (service == null) Log.w(TAG, "Proxy not attached to service"); 422 return new ArrayList<BluetoothDevice>(); 423 } 424 425 /** 426 * {@inheritDoc} 427 */ 428 @Override getConnectionState(BluetoothDevice device)429 public int getConnectionState(BluetoothDevice device) { 430 if (VDBG) log("getState(" + device + ")"); 431 final IBluetoothHidHost service = mService; 432 if (service != null && isEnabled() && isValidDevice(device)) { 433 try { 434 return service.getConnectionState(device); 435 } catch (RemoteException e) { 436 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 437 return BluetoothProfile.STATE_DISCONNECTED; 438 } 439 } 440 if (service == null) Log.w(TAG, "Proxy not attached to service"); 441 return BluetoothProfile.STATE_DISCONNECTED; 442 } 443 444 /** 445 * Set priority of the profile 446 * 447 * <p> The device should already be paired. 448 * Priority can be one of {@link #PRIORITY_ON} or 449 * {@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 457 * @hide 458 */ setPriority(BluetoothDevice device, int priority)459 public boolean setPriority(BluetoothDevice device, int priority) { 460 if (DBG) log("setPriority(" + device + ", " + priority + ")"); 461 final IBluetoothHidHost service = mService; 462 if (service != null && isEnabled() && isValidDevice(device)) { 463 if (priority != BluetoothProfile.PRIORITY_OFF 464 && priority != BluetoothProfile.PRIORITY_ON) { 465 return false; 466 } 467 try { 468 return service.setPriority(device, priority); 469 } catch (RemoteException e) { 470 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 471 return false; 472 } 473 } 474 if (service == null) Log.w(TAG, "Proxy not attached to service"); 475 return false; 476 } 477 478 /** 479 * 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 489 * @hide 490 */ getPriority(BluetoothDevice device)491 public int getPriority(BluetoothDevice device) { 492 if (VDBG) log("getPriority(" + device + ")"); 493 final IBluetoothHidHost service = mService; 494 if (service != null && isEnabled() && isValidDevice(device)) { 495 try { 496 return service.getPriority(device); 497 } catch (RemoteException e) { 498 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 499 return BluetoothProfile.PRIORITY_OFF; 500 } 501 } 502 if (service == null) Log.w(TAG, "Proxy not attached to service"); 503 return BluetoothProfile.PRIORITY_OFF; 504 } 505 506 private final ServiceConnection mConnection = new ServiceConnection() { 507 public void onServiceConnected(ComponentName className, IBinder service) { 508 if (DBG) Log.d(TAG, "Proxy object connected"); 509 mService = IBluetoothHidHost.Stub.asInterface(Binder.allowBlocking(service)); 510 511 if (mServiceListener != null) { 512 mServiceListener.onServiceConnected(BluetoothProfile.HID_HOST, 513 BluetoothHidHost.this); 514 } 515 } 516 517 public void onServiceDisconnected(ComponentName className) { 518 if (DBG) Log.d(TAG, "Proxy object disconnected"); 519 mService = null; 520 if (mServiceListener != null) { 521 mServiceListener.onServiceDisconnected(BluetoothProfile.HID_HOST); 522 } 523 } 524 }; 525 isEnabled()526 private boolean isEnabled() { 527 return mAdapter.getState() == BluetoothAdapter.STATE_ON; 528 } 529 isValidDevice(BluetoothDevice device)530 private static boolean isValidDevice(BluetoothDevice device) { 531 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); 532 } 533 534 /** 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 540 * @return false on immediate error, true otherwise 541 * @hide 542 */ virtualUnplug(BluetoothDevice device)543 public boolean virtualUnplug(BluetoothDevice device) { 544 if (DBG) log("virtualUnplug(" + device + ")"); 545 final IBluetoothHidHost service = mService; 546 if (service != null && isEnabled() && isValidDevice(device)) { 547 try { 548 return service.virtualUnplug(device); 549 } catch (RemoteException e) { 550 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 551 return false; 552 } 553 } 554 555 if (service == null) Log.w(TAG, "Proxy not attached to service"); 556 return false; 557 558 } 559 560 /** 561 * 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 */ getProtocolMode(BluetoothDevice device)569 public boolean getProtocolMode(BluetoothDevice device) { 570 if (VDBG) log("getProtocolMode(" + device + ")"); 571 final IBluetoothHidHost service = mService; 572 if (service != null && isEnabled() && isValidDevice(device)) { 573 try { 574 return service.getProtocolMode(device); 575 } catch (RemoteException e) { 576 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 577 return false; 578 } 579 } 580 if (service == null) Log.w(TAG, "Proxy not attached to service"); 581 return false; 582 } 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 590 * @return false on immediate error, true otherwise 591 * @hide 592 */ setProtocolMode(BluetoothDevice device, int protocolMode)593 public boolean setProtocolMode(BluetoothDevice device, int protocolMode) { 594 if (DBG) log("setProtocolMode(" + device + ")"); 595 final IBluetoothHidHost service = mService; 596 if (service != null && isEnabled() && isValidDevice(device)) { 597 try { 598 return service.setProtocolMode(device, protocolMode); 599 } catch (RemoteException e) { 600 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 601 return false; 602 } 603 } 604 if (service == null) Log.w(TAG, "Proxy not attached to service"); 605 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 617 * @return false on immediate error, true otherwise 618 * @hide 619 */ getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize)620 public boolean getReport(BluetoothDevice device, byte reportType, byte reportId, 621 int bufferSize) { 622 if (VDBG) { 623 log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId 624 + "bufferSize=" + bufferSize); 625 } 626 final IBluetoothHidHost service = mService; 627 if (service != null && isEnabled() && isValidDevice(device)) { 628 try { 629 return service.getReport(device, reportType, reportId, bufferSize); 630 } catch (RemoteException e) { 631 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 632 return false; 633 } 634 } 635 if (service == null) Log.w(TAG, "Proxy not attached to service"); 636 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 647 * @return false on immediate error, true otherwise 648 * @hide 649 */ setReport(BluetoothDevice device, byte reportType, String report)650 public boolean setReport(BluetoothDevice device, byte reportType, String report) { 651 if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report); 652 final IBluetoothHidHost service = mService; 653 if (service != null && isEnabled() && isValidDevice(device)) { 654 try { 655 return service.setReport(device, reportType, report); 656 } catch (RemoteException e) { 657 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 658 return false; 659 } 660 } 661 if (service == null) Log.w(TAG, "Proxy not attached to service"); 662 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 671 * @param report Report to send 672 * @return false on immediate error, true otherwise 673 * @hide 674 */ sendData(BluetoothDevice device, String report)675 public boolean sendData(BluetoothDevice device, String report) { 676 if (DBG) log("sendData(" + device + "), report=" + report); 677 final IBluetoothHidHost service = mService; 678 if (service != null && isEnabled() && isValidDevice(device)) { 679 try { 680 return service.sendData(device, report); 681 } catch (RemoteException e) { 682 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 683 return false; 684 } 685 } 686 if (service == null) Log.w(TAG, "Proxy not attached to service"); 687 return false; 688 } 689 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 696 * @return false on immediate error, true otherwise 697 * @hide 698 */ getIdleTime(BluetoothDevice device)699 public boolean getIdleTime(BluetoothDevice device) { 700 if (DBG) log("getIdletime(" + device + ")"); 701 final IBluetoothHidHost service = mService; 702 if (service != null && isEnabled() && isValidDevice(device)) { 703 try { 704 return service.getIdleTime(device); 705 } catch (RemoteException e) { 706 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 707 return false; 708 } 709 } 710 if (service == null) Log.w(TAG, "Proxy not attached to service"); 711 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 721 * @return false on immediate error, true otherwise 722 * @hide 723 */ setIdleTime(BluetoothDevice device, byte idleTime)724 public boolean setIdleTime(BluetoothDevice device, byte idleTime) { 725 if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime); 726 final IBluetoothHidHost service = mService; 727 if (service != null && isEnabled() && isValidDevice(device)) { 728 try { 729 return service.setIdleTime(device, idleTime); 730 } catch (RemoteException e) { 731 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 732 return false; 733 } 734 } 735 if (service == null) Log.w(TAG, "Proxy not attached to service"); 736 return false; 737 } 738 log(String msg)739 private static void log(String msg) { 740 Log.d(TAG, msg); 741 } 742 } 743