1 /* 2 * Copyright (C) 2016 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.IBinder; 26 import android.os.RemoteException; 27 import android.util.Log; 28 29 import java.util.ArrayList; 30 import java.util.List; 31 import java.util.concurrent.Executor; 32 33 /** 34 * Provides the public APIs to control the Bluetooth HID Device profile. 35 * 36 * <p>BluetoothHidDevice is a proxy object for controlling the Bluetooth HID Device Service via IPC. 37 * Use {@link BluetoothAdapter#getProfileProxy} to get the BluetoothHidDevice proxy object. 38 */ 39 public final class BluetoothHidDevice implements BluetoothProfile { 40 private static final String TAG = BluetoothHidDevice.class.getSimpleName(); 41 42 /** 43 * Intent used to broadcast the change in connection state of the Input Host profile. 44 * 45 * <p>This intent will have 3 extras: 46 * 47 * <ul> 48 * <li>{@link #EXTRA_STATE} - The current state of the profile. 49 * <li>{@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. 50 * <li>{@link BluetoothDevice#EXTRA_DEVICE} - The remote device. 51 * </ul> 52 * 53 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of {@link 54 * #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, {@link #STATE_CONNECTED}, {@link 55 * #STATE_DISCONNECTING}. 56 * 57 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to receive. 58 */ 59 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 60 public static final String ACTION_CONNECTION_STATE_CHANGED = 61 "android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED"; 62 63 /** 64 * Constant representing unspecified HID device subclass. 65 * 66 * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, 67 * BluetoothHidDeviceAppQosSettings, Executor, Callback) 68 */ 69 public static final byte SUBCLASS1_NONE = (byte) 0x00; 70 /** 71 * Constant representing keyboard subclass. 72 * 73 * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, 74 * BluetoothHidDeviceAppQosSettings, Executor, Callback) 75 */ 76 public static final byte SUBCLASS1_KEYBOARD = (byte) 0x40; 77 /** 78 * Constant representing mouse subclass. 79 * 80 * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, 81 * BluetoothHidDeviceAppQosSettings, Executor, Callback) 82 */ 83 public static final byte SUBCLASS1_MOUSE = (byte) 0x80; 84 /** 85 * Constant representing combo keyboard and mouse subclass. 86 * 87 * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, 88 * BluetoothHidDeviceAppQosSettings, Executor, Callback) 89 */ 90 public static final byte SUBCLASS1_COMBO = (byte) 0xC0; 91 92 /** 93 * Constant representing uncategorized HID device subclass. 94 * 95 * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, 96 * BluetoothHidDeviceAppQosSettings, Executor, Callback) 97 */ 98 public static final byte SUBCLASS2_UNCATEGORIZED = (byte) 0x00; 99 /** 100 * Constant representing joystick subclass. 101 * 102 * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, 103 * BluetoothHidDeviceAppQosSettings, Executor, Callback) 104 */ 105 public static final byte SUBCLASS2_JOYSTICK = (byte) 0x01; 106 /** 107 * Constant representing gamepad subclass. 108 * 109 * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, 110 * BluetoothHidDeviceAppQosSettings, Executor, Callback) 111 */ 112 public static final byte SUBCLASS2_GAMEPAD = (byte) 0x02; 113 /** 114 * Constant representing remote control subclass. 115 * 116 * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, 117 * BluetoothHidDeviceAppQosSettings, Executor, Callback) 118 */ 119 public static final byte SUBCLASS2_REMOTE_CONTROL = (byte) 0x03; 120 /** 121 * Constant representing sensing device subclass. 122 * 123 * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, 124 * BluetoothHidDeviceAppQosSettings, Executor, Callback) 125 */ 126 public static final byte SUBCLASS2_SENSING_DEVICE = (byte) 0x04; 127 /** 128 * Constant representing digitizer tablet subclass. 129 * 130 * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, 131 * BluetoothHidDeviceAppQosSettings, Executor, Callback) 132 */ 133 public static final byte SUBCLASS2_DIGITIZER_TABLET = (byte) 0x05; 134 /** 135 * Constant representing card reader subclass. 136 * 137 * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, 138 * BluetoothHidDeviceAppQosSettings, Executor, Callback) 139 */ 140 public static final byte SUBCLASS2_CARD_READER = (byte) 0x06; 141 142 /** 143 * Constant representing HID Input Report type. 144 * 145 * @see Callback#onGetReport(BluetoothDevice, byte, byte, int) 146 * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) 147 * @see Callback#onInterruptData(BluetoothDevice, byte, byte[]) 148 */ 149 public static final byte REPORT_TYPE_INPUT = (byte) 1; 150 /** 151 * Constant representing HID Output Report type. 152 * 153 * @see Callback#onGetReport(BluetoothDevice, byte, byte, int) 154 * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) 155 * @see Callback#onInterruptData(BluetoothDevice, byte, byte[]) 156 */ 157 public static final byte REPORT_TYPE_OUTPUT = (byte) 2; 158 /** 159 * Constant representing HID Feature Report type. 160 * 161 * @see Callback#onGetReport(BluetoothDevice, byte, byte, int) 162 * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) 163 * @see Callback#onInterruptData(BluetoothDevice, byte, byte[]) 164 */ 165 public static final byte REPORT_TYPE_FEATURE = (byte) 3; 166 167 /** 168 * Constant representing success response for Set Report. 169 * 170 * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) 171 */ 172 public static final byte ERROR_RSP_SUCCESS = (byte) 0; 173 /** 174 * Constant representing error response for Set Report due to "not ready". 175 * 176 * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) 177 */ 178 public static final byte ERROR_RSP_NOT_READY = (byte) 1; 179 /** 180 * Constant representing error response for Set Report due to "invalid report ID". 181 * 182 * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) 183 */ 184 public static final byte ERROR_RSP_INVALID_RPT_ID = (byte) 2; 185 /** 186 * Constant representing error response for Set Report due to "unsupported request". 187 * 188 * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) 189 */ 190 public static final byte ERROR_RSP_UNSUPPORTED_REQ = (byte) 3; 191 /** 192 * Constant representing error response for Set Report due to "invalid parameter". 193 * 194 * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) 195 */ 196 public static final byte ERROR_RSP_INVALID_PARAM = (byte) 4; 197 /** 198 * Constant representing error response for Set Report with unknown reason. 199 * 200 * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) 201 */ 202 public static final byte ERROR_RSP_UNKNOWN = (byte) 14; 203 204 /** 205 * Constant representing boot protocol mode used set by host. Default is always {@link 206 * #PROTOCOL_REPORT_MODE} unless notified otherwise. 207 * 208 * @see Callback#onSetProtocol(BluetoothDevice, byte) 209 */ 210 public static final byte PROTOCOL_BOOT_MODE = (byte) 0; 211 /** 212 * Constant representing report protocol mode used set by host. Default is always {@link 213 * #PROTOCOL_REPORT_MODE} unless notified otherwise. 214 * 215 * @see Callback#onSetProtocol(BluetoothDevice, byte) 216 */ 217 public static final byte PROTOCOL_REPORT_MODE = (byte) 1; 218 219 /** 220 * The template class that applications use to call callback functions on events from the HID 221 * host. Callback functions are wrapped in this class and registered to the Android system 222 * during app registration. 223 */ 224 public abstract static class Callback { 225 226 private static final String TAG = "BluetoothHidDevCallback"; 227 228 /** 229 * Callback called when application registration state changes. Usually it's called due to 230 * either {@link BluetoothHidDevice#registerApp (String, String, String, byte, byte[], 231 * Executor, Callback)} or {@link BluetoothHidDevice#unregisterApp()} , but can be also 232 * unsolicited in case e.g. Bluetooth was turned off in which case application is 233 * unregistered automatically. 234 * 235 * @param pluggedDevice {@link BluetoothDevice} object which represents host that currently 236 * has Virtual Cable established with device. Only valid when application is registered, 237 * can be <code>null</code>. 238 * @param registered <code>true</code> if application is registered, <code>false</code> 239 * otherwise. 240 */ onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered)241 public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) { 242 Log.d( 243 TAG, 244 "onAppStatusChanged: pluggedDevice=" 245 + pluggedDevice 246 + " registered=" 247 + registered); 248 } 249 250 /** 251 * Callback called when connection state with remote host was changed. Application can 252 * assume than Virtual Cable is established when called with {@link 253 * BluetoothProfile#STATE_CONNECTED} <code>state</code>. 254 * 255 * @param device {@link BluetoothDevice} object representing host device which connection 256 * state was changed. 257 * @param state Connection state as defined in {@link BluetoothProfile}. 258 */ onConnectionStateChanged(BluetoothDevice device, int state)259 public void onConnectionStateChanged(BluetoothDevice device, int state) { 260 Log.d(TAG, "onConnectionStateChanged: device=" + device + " state=" + state); 261 } 262 263 /** 264 * Callback called when GET_REPORT is received from remote host. Should be replied by 265 * application using {@link BluetoothHidDevice#replyReport(BluetoothDevice, byte, byte, 266 * byte[])}. 267 * 268 * @param type Requested Report Type. 269 * @param id Requested Report Id, can be 0 if no Report Id are defined in descriptor. 270 * @param bufferSize Requested buffer size, application shall respond with at least given 271 * number of bytes. 272 */ onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize)273 public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) { 274 Log.d( 275 TAG, 276 "onGetReport: device=" 277 + device 278 + " type=" 279 + type 280 + " id=" 281 + id 282 + " bufferSize=" 283 + bufferSize); 284 } 285 286 /** 287 * Callback called when SET_REPORT is received from remote host. In case received data are 288 * invalid, application shall respond with {@link 289 * BluetoothHidDevice#reportError(BluetoothDevice, byte)}. 290 * 291 * @param type Report Type. 292 * @param id Report Id. 293 * @param data Report data. 294 */ onSetReport(BluetoothDevice device, byte type, byte id, byte[] data)295 public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) { 296 Log.d(TAG, "onSetReport: device=" + device + " type=" + type + " id=" + id); 297 } 298 299 /** 300 * Callback called when SET_PROTOCOL is received from remote host. Application shall use 301 * this information to send only reports valid for given protocol mode. By default, {@link 302 * BluetoothHidDevice#PROTOCOL_REPORT_MODE} shall be assumed. 303 * 304 * @param protocol Protocol Mode. 305 */ onSetProtocol(BluetoothDevice device, byte protocol)306 public void onSetProtocol(BluetoothDevice device, byte protocol) { 307 Log.d(TAG, "onSetProtocol: device=" + device + " protocol=" + protocol); 308 } 309 310 /** 311 * Callback called when report data is received over interrupt channel. Report Type is 312 * assumed to be {@link BluetoothHidDevice#REPORT_TYPE_OUTPUT}. 313 * 314 * @param reportId Report Id. 315 * @param data Report data. 316 */ onInterruptData(BluetoothDevice device, byte reportId, byte[] data)317 public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) { 318 Log.d(TAG, "onInterruptData: device=" + device + " reportId=" + reportId); 319 } 320 321 /** 322 * Callback called when Virtual Cable is removed. After this callback is received connection 323 * will be disconnected automatically. 324 */ onVirtualCableUnplug(BluetoothDevice device)325 public void onVirtualCableUnplug(BluetoothDevice device) { 326 Log.d(TAG, "onVirtualCableUnplug: device=" + device); 327 } 328 } 329 330 private Context mContext; 331 private ServiceListener mServiceListener; 332 private volatile IBluetoothHidDevice mService; 333 private BluetoothAdapter mAdapter; 334 335 private static class CallbackWrapper extends IBluetoothHidDeviceCallback.Stub { 336 337 private final Executor mExecutor; 338 private final Callback mCallback; 339 CallbackWrapper(Executor executor, Callback callback)340 CallbackWrapper(Executor executor, Callback callback) { 341 mExecutor = executor; 342 mCallback = callback; 343 } 344 345 @Override onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered)346 public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) { 347 clearCallingIdentity(); 348 mExecutor.execute(() -> mCallback.onAppStatusChanged(pluggedDevice, registered)); 349 } 350 351 @Override onConnectionStateChanged(BluetoothDevice device, int state)352 public void onConnectionStateChanged(BluetoothDevice device, int state) { 353 clearCallingIdentity(); 354 mExecutor.execute(() -> mCallback.onConnectionStateChanged(device, state)); 355 } 356 357 @Override onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize)358 public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) { 359 clearCallingIdentity(); 360 mExecutor.execute(() -> mCallback.onGetReport(device, type, id, bufferSize)); 361 } 362 363 @Override onSetReport(BluetoothDevice device, byte type, byte id, byte[] data)364 public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) { 365 clearCallingIdentity(); 366 mExecutor.execute(() -> mCallback.onSetReport(device, type, id, data)); 367 } 368 369 @Override onSetProtocol(BluetoothDevice device, byte protocol)370 public void onSetProtocol(BluetoothDevice device, byte protocol) { 371 clearCallingIdentity(); 372 mExecutor.execute(() -> mCallback.onSetProtocol(device, protocol)); 373 } 374 375 @Override onInterruptData(BluetoothDevice device, byte reportId, byte[] data)376 public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) { 377 clearCallingIdentity(); 378 mExecutor.execute(() -> mCallback.onInterruptData(device, reportId, data)); 379 } 380 381 @Override onVirtualCableUnplug(BluetoothDevice device)382 public void onVirtualCableUnplug(BluetoothDevice device) { 383 clearCallingIdentity(); 384 mExecutor.execute(() -> mCallback.onVirtualCableUnplug(device)); 385 } 386 } 387 388 private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = 389 new IBluetoothStateChangeCallback.Stub() { 390 391 public void onBluetoothStateChange(boolean up) { 392 Log.d(TAG, "onBluetoothStateChange: up=" + up); 393 synchronized (mConnection) { 394 if (up) { 395 try { 396 if (mService == null) { 397 Log.d(TAG, "Binding HID Device service..."); 398 doBind(); 399 } 400 } catch (IllegalStateException e) { 401 Log.e(TAG, "onBluetoothStateChange: could not bind to HID Dev " 402 + "service: ", e); 403 } catch (SecurityException e) { 404 Log.e(TAG, "onBluetoothStateChange: could not bind to HID Dev " 405 + "service: ", e); 406 } 407 } else { 408 Log.d(TAG, "Unbinding service..."); 409 doUnbind(); 410 } 411 } 412 } 413 }; 414 415 private final ServiceConnection mConnection = 416 new ServiceConnection() { 417 public void onServiceConnected(ComponentName className, IBinder service) { 418 Log.d(TAG, "onServiceConnected()"); 419 mService = IBluetoothHidDevice.Stub.asInterface(service); 420 if (mServiceListener != null) { 421 mServiceListener.onServiceConnected( 422 BluetoothProfile.HID_DEVICE, BluetoothHidDevice.this); 423 } 424 } 425 426 public void onServiceDisconnected(ComponentName className) { 427 Log.d(TAG, "onServiceDisconnected()"); 428 mService = null; 429 if (mServiceListener != null) { 430 mServiceListener.onServiceDisconnected(BluetoothProfile.HID_DEVICE); 431 } 432 } 433 }; 434 BluetoothHidDevice(Context context, ServiceListener listener)435 BluetoothHidDevice(Context context, ServiceListener listener) { 436 mContext = context; 437 mServiceListener = listener; 438 mAdapter = BluetoothAdapter.getDefaultAdapter(); 439 440 IBluetoothManager mgr = mAdapter.getBluetoothManager(); 441 if (mgr != null) { 442 try { 443 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); 444 } catch (RemoteException e) { 445 e.printStackTrace(); 446 } 447 } 448 449 doBind(); 450 } 451 doBind()452 boolean doBind() { 453 Intent intent = new Intent(IBluetoothHidDevice.class.getName()); 454 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); 455 intent.setComponent(comp); 456 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, 457 mContext.getUser())) { 458 Log.e(TAG, "Could not bind to Bluetooth HID Device Service with " + intent); 459 return false; 460 } 461 Log.d(TAG, "Bound to HID Device Service"); 462 return true; 463 } 464 doUnbind()465 void doUnbind() { 466 if (mService != null) { 467 mService = null; 468 try { 469 mContext.unbindService(mConnection); 470 } catch (IllegalArgumentException e) { 471 Log.e(TAG, "Unable to unbind HidDevService", e); 472 } 473 } 474 } 475 close()476 void close() { 477 IBluetoothManager mgr = mAdapter.getBluetoothManager(); 478 if (mgr != null) { 479 try { 480 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); 481 } catch (RemoteException e) { 482 e.printStackTrace(); 483 } 484 } 485 486 synchronized (mConnection) { 487 doUnbind(); 488 } 489 mServiceListener = null; 490 } 491 492 /** {@inheritDoc} */ 493 @Override getConnectedDevices()494 public List<BluetoothDevice> getConnectedDevices() { 495 final IBluetoothHidDevice service = mService; 496 if (service != null) { 497 try { 498 return service.getConnectedDevices(); 499 } catch (RemoteException e) { 500 Log.e(TAG, e.toString()); 501 } 502 } else { 503 Log.w(TAG, "Proxy not attached to service"); 504 } 505 506 return new ArrayList<>(); 507 } 508 509 /** {@inheritDoc} */ 510 @Override getDevicesMatchingConnectionStates(int[] states)511 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 512 final IBluetoothHidDevice service = mService; 513 if (service != null) { 514 try { 515 return service.getDevicesMatchingConnectionStates(states); 516 } catch (RemoteException e) { 517 Log.e(TAG, e.toString()); 518 } 519 } else { 520 Log.w(TAG, "Proxy not attached to service"); 521 } 522 523 return new ArrayList<>(); 524 } 525 526 /** {@inheritDoc} */ 527 @Override getConnectionState(BluetoothDevice device)528 public int getConnectionState(BluetoothDevice device) { 529 final IBluetoothHidDevice service = mService; 530 if (service != null) { 531 try { 532 return service.getConnectionState(device); 533 } catch (RemoteException e) { 534 Log.e(TAG, e.toString()); 535 } 536 } else { 537 Log.w(TAG, "Proxy not attached to service"); 538 } 539 540 return STATE_DISCONNECTED; 541 } 542 543 /** 544 * Registers application to be used for HID device. Connections to HID Device are only possible 545 * when application is registered. Only one application can be registered at one time. When an 546 * application is registered, the HID Host service will be disabled until it is unregistered. 547 * When no longer used, application should be unregistered using {@link #unregisterApp()}. The 548 * app will be automatically unregistered if it is not foreground. The registration status 549 * should be tracked by the application by handling callback from Callback#onAppStatusChanged. 550 * The app registration status is not related to the return value of this method. 551 * 552 * @param sdp {@link BluetoothHidDeviceAppSdpSettings} object of HID Device SDP record. The HID 553 * Device SDP record is required. 554 * @param inQos {@link BluetoothHidDeviceAppQosSettings} object of Incoming QoS Settings. The 555 * Incoming QoS Settings is not required. Use null or default 556 * BluetoothHidDeviceAppQosSettings.Builder for default values. 557 * @param outQos {@link BluetoothHidDeviceAppQosSettings} object of Outgoing QoS Settings. The 558 * Outgoing QoS Settings is not required. Use null or default 559 * BluetoothHidDeviceAppQosSettings.Builder for default values. 560 * @param executor {@link Executor} object on which callback will be executed. The Executor 561 * object is required. 562 * @param callback {@link Callback} object to which callback messages will be sent. The Callback 563 * object is required. 564 * @return true if the command is successfully sent; otherwise false. 565 */ registerApp( BluetoothHidDeviceAppSdpSettings sdp, BluetoothHidDeviceAppQosSettings inQos, BluetoothHidDeviceAppQosSettings outQos, Executor executor, Callback callback)566 public boolean registerApp( 567 BluetoothHidDeviceAppSdpSettings sdp, 568 BluetoothHidDeviceAppQosSettings inQos, 569 BluetoothHidDeviceAppQosSettings outQos, 570 Executor executor, 571 Callback callback) { 572 boolean result = false; 573 574 if (sdp == null) { 575 throw new IllegalArgumentException("sdp parameter cannot be null"); 576 } 577 578 if (executor == null) { 579 throw new IllegalArgumentException("executor parameter cannot be null"); 580 } 581 582 if (callback == null) { 583 throw new IllegalArgumentException("callback parameter cannot be null"); 584 } 585 586 final IBluetoothHidDevice service = mService; 587 if (service != null) { 588 try { 589 CallbackWrapper cbw = new CallbackWrapper(executor, callback); 590 result = service.registerApp(sdp, inQos, outQos, cbw); 591 } catch (RemoteException e) { 592 Log.e(TAG, e.toString()); 593 } 594 } else { 595 Log.w(TAG, "Proxy not attached to service"); 596 } 597 598 return result; 599 } 600 601 /** 602 * Unregisters application. Active connection will be disconnected and no new connections will 603 * be allowed until registered again using {@link #registerApp 604 * (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, 605 * BluetoothHidDeviceAppQosSettings, Executor, Callback)}. The registration status should be 606 * tracked by the application by handling callback from Callback#onAppStatusChanged. The app 607 * registration status is not related to the return value of this method. 608 * 609 * @return true if the command is successfully sent; otherwise false. 610 */ unregisterApp()611 public boolean unregisterApp() { 612 boolean result = false; 613 614 final IBluetoothHidDevice service = mService; 615 if (service != null) { 616 try { 617 result = service.unregisterApp(); 618 } catch (RemoteException e) { 619 Log.e(TAG, e.toString()); 620 } 621 } else { 622 Log.w(TAG, "Proxy not attached to service"); 623 } 624 625 return result; 626 } 627 628 /** 629 * Sends report to remote host using interrupt channel. 630 * 631 * @param id Report Id, as defined in descriptor. Can be 0 in case Report Id are not defined in 632 * descriptor. 633 * @param data Report data, not including Report Id. 634 * @return true if the command is successfully sent; otherwise false. 635 */ sendReport(BluetoothDevice device, int id, byte[] data)636 public boolean sendReport(BluetoothDevice device, int id, byte[] data) { 637 boolean result = false; 638 639 final IBluetoothHidDevice service = mService; 640 if (service != null) { 641 try { 642 result = service.sendReport(device, id, data); 643 } catch (RemoteException e) { 644 Log.e(TAG, e.toString()); 645 } 646 } else { 647 Log.w(TAG, "Proxy not attached to service"); 648 } 649 650 return result; 651 } 652 653 /** 654 * Sends report to remote host as reply for GET_REPORT request from {@link 655 * Callback#onGetReport(BluetoothDevice, byte, byte, int)}. 656 * 657 * @param type Report Type, as in request. 658 * @param id Report Id, as in request. 659 * @param data Report data, not including Report Id. 660 * @return true if the command is successfully sent; otherwise false. 661 */ replyReport(BluetoothDevice device, byte type, byte id, byte[] data)662 public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) { 663 boolean result = false; 664 665 final IBluetoothHidDevice service = mService; 666 if (service != null) { 667 try { 668 result = service.replyReport(device, type, id, data); 669 } catch (RemoteException e) { 670 Log.e(TAG, e.toString()); 671 } 672 } else { 673 Log.w(TAG, "Proxy not attached to service"); 674 } 675 676 return result; 677 } 678 679 /** 680 * Sends error handshake message as reply for invalid SET_REPORT request from {@link 681 * Callback#onSetReport(BluetoothDevice, byte, byte, byte[])}. 682 * 683 * @param error Error to be sent for SET_REPORT via HANDSHAKE. 684 * @return true if the command is successfully sent; otherwise false. 685 */ reportError(BluetoothDevice device, byte error)686 public boolean reportError(BluetoothDevice device, byte error) { 687 boolean result = false; 688 689 final IBluetoothHidDevice service = mService; 690 if (service != null) { 691 try { 692 result = service.reportError(device, error); 693 } catch (RemoteException e) { 694 Log.e(TAG, e.toString()); 695 } 696 } else { 697 Log.w(TAG, "Proxy not attached to service"); 698 } 699 700 return result; 701 } 702 703 /** 704 * Gets the application name of the current HidDeviceService user. 705 * 706 * @return the current user name, or empty string if cannot get the name 707 * {@hide} 708 */ getUserAppName()709 public String getUserAppName() { 710 final IBluetoothHidDevice service = mService; 711 712 if (service != null) { 713 try { 714 return service.getUserAppName(); 715 } catch (RemoteException e) { 716 Log.e(TAG, e.toString()); 717 } 718 } else { 719 Log.w(TAG, "Proxy not attached to service"); 720 } 721 722 return ""; 723 } 724 725 /** 726 * Initiates connection to host which is currently paired with this device. If the application 727 * is not registered, #connect(BluetoothDevice) will fail. The connection state should be 728 * tracked by the application by handling callback from Callback#onConnectionStateChanged. The 729 * connection state is not related to the return value of this method. 730 * 731 * @return true if the command is successfully sent; otherwise false. 732 */ connect(BluetoothDevice device)733 public boolean connect(BluetoothDevice device) { 734 boolean result = false; 735 736 final IBluetoothHidDevice service = mService; 737 if (service != null) { 738 try { 739 result = service.connect(device); 740 } catch (RemoteException e) { 741 Log.e(TAG, e.toString()); 742 } 743 } else { 744 Log.w(TAG, "Proxy not attached to service"); 745 } 746 747 return result; 748 } 749 750 /** 751 * Disconnects from currently connected host. The connection state should be tracked by the 752 * application by handling callback from Callback#onConnectionStateChanged. The connection state 753 * is not related to the return value of this method. 754 * 755 * @return true if the command is successfully sent; otherwise false. 756 */ disconnect(BluetoothDevice device)757 public boolean disconnect(BluetoothDevice device) { 758 boolean result = false; 759 760 final IBluetoothHidDevice service = mService; 761 if (service != null) { 762 try { 763 result = service.disconnect(device); 764 } catch (RemoteException e) { 765 Log.e(TAG, e.toString()); 766 } 767 } else { 768 Log.w(TAG, "Proxy not attached to service"); 769 } 770 771 return result; 772 } 773 } 774