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