HidDeviceService.java revision 5fed27a96f263473e8f4c2d2f2a0b29e5e54aa79
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 17package com.android.bluetooth.hid; 18 19import android.bluetooth.BluetoothDevice; 20import android.bluetooth.BluetoothHidDevice; 21import android.bluetooth.BluetoothHidDeviceAppConfiguration; 22import android.bluetooth.BluetoothHidDeviceAppQosSettings; 23import android.bluetooth.BluetoothHidDeviceAppSdpSettings; 24import android.bluetooth.BluetoothProfile; 25import android.bluetooth.IBluetoothHidDevice; 26import android.bluetooth.IBluetoothHidDeviceCallback; 27import android.content.Intent; 28import android.os.Binder; 29import android.os.Handler; 30import android.os.IBinder; 31import android.os.Message; 32import android.os.Process; 33import android.os.RemoteException; 34import android.util.Log; 35 36import com.android.bluetooth.Utils; 37import com.android.bluetooth.btservice.ProfileService; 38import com.android.internal.annotations.VisibleForTesting; 39 40import java.nio.ByteBuffer; 41import java.util.ArrayList; 42import java.util.Arrays; 43import java.util.List; 44import java.util.NoSuchElementException; 45 46/** @hide */ 47public class HidDeviceService extends ProfileService { 48 private static final boolean DBG = false; 49 private static final String TAG = HidDeviceService.class.getSimpleName(); 50 51 private static final int MESSAGE_APPLICATION_STATE_CHANGED = 1; 52 private static final int MESSAGE_CONNECT_STATE_CHANGED = 2; 53 private static final int MESSAGE_GET_REPORT = 3; 54 private static final int MESSAGE_SET_REPORT = 4; 55 private static final int MESSAGE_SET_PROTOCOL = 5; 56 private static final int MESSAGE_INTR_DATA = 6; 57 private static final int MESSAGE_VC_UNPLUG = 7; 58 59 private static HidDeviceService sHidDeviceService; 60 61 private HidDeviceNativeInterface mHidDeviceNativeInterface; 62 63 private boolean mNativeAvailable = false; 64 private BluetoothDevice mHidDevice; 65 private int mHidDeviceState = BluetoothHidDevice.STATE_DISCONNECTED; 66 private int mUserUid = 0; 67 private IBluetoothHidDeviceCallback mCallback; 68 private BluetoothHidDeviceDeathRecipient mDeathRcpt; 69 70 private HidDeviceServiceHandler mHandler; 71 72 public HidDeviceService() { 73 mHidDeviceNativeInterface = HidDeviceNativeInterface.getInstance(); 74 } 75 76 private class HidDeviceServiceHandler extends Handler { 77 @Override 78 public void handleMessage(Message msg) { 79 if (DBG) { 80 Log.d(TAG, "handleMessage(): msg.what=" + msg.what); 81 } 82 83 switch (msg.what) { 84 case MESSAGE_APPLICATION_STATE_CHANGED: { 85 BluetoothDevice device = msg.obj != null ? (BluetoothDevice) msg.obj : null; 86 boolean success = (msg.arg1 != 0); 87 88 if (success) { 89 Log.d(TAG, "App registered, set device to: " + device); 90 mHidDevice = device; 91 } else { 92 mHidDevice = null; 93 } 94 95 try { 96 if (mCallback != null) { 97 // TODO(hsz) remove AppConfig in frameworks/base in the follow-up CL 98 mCallback.onAppStatusChanged(device, null, success); 99 } else { 100 break; 101 } 102 } catch (RemoteException e) { 103 Log.e(TAG, "e=" + e.toString()); 104 e.printStackTrace(); 105 } 106 107 if (success) { 108 mDeathRcpt = new BluetoothHidDeviceDeathRecipient(HidDeviceService.this); 109 if (mCallback != null) { 110 IBinder binder = mCallback.asBinder(); 111 try { 112 binder.linkToDeath(mDeathRcpt, 0); 113 Log.i(TAG, "IBinder.linkToDeath() ok"); 114 } catch (RemoteException e) { 115 e.printStackTrace(); 116 } 117 } 118 } else if (mDeathRcpt != null) { 119 if (mCallback != null) { 120 IBinder binder = mCallback.asBinder(); 121 try { 122 binder.unlinkToDeath(mDeathRcpt, 0); 123 Log.i(TAG, "IBinder.unlinkToDeath() ok"); 124 } catch (NoSuchElementException e) { 125 e.printStackTrace(); 126 } 127 mDeathRcpt.cleanup(); 128 mDeathRcpt = null; 129 } 130 } 131 132 if (!success) { 133 mCallback = null; 134 } 135 136 break; 137 } 138 139 case MESSAGE_CONNECT_STATE_CHANGED: { 140 BluetoothDevice device = (BluetoothDevice) msg.obj; 141 int halState = msg.arg1; 142 int state = convertHalState(halState); 143 144 if (state != BluetoothHidDevice.STATE_DISCONNECTED) { 145 mHidDevice = device; 146 } 147 148 setAndBroadcastConnectionState(device, state); 149 150 try { 151 if (mCallback != null) { 152 mCallback.onConnectionStateChanged(device, state); 153 } 154 } catch (RemoteException e) { 155 e.printStackTrace(); 156 } 157 break; 158 } 159 160 case MESSAGE_GET_REPORT: 161 byte type = (byte) msg.arg1; 162 byte id = (byte) msg.arg2; 163 int bufferSize = msg.obj == null ? 0 : ((Integer) msg.obj).intValue(); 164 165 try { 166 if (mCallback != null) { 167 mCallback.onGetReport(mHidDevice, type, id, bufferSize); 168 } 169 } catch (RemoteException e) { 170 e.printStackTrace(); 171 } 172 break; 173 174 case MESSAGE_SET_REPORT: { 175 byte reportType = (byte) msg.arg1; 176 byte reportId = (byte) msg.arg2; 177 byte[] data = ((ByteBuffer) msg.obj).array(); 178 179 try { 180 if (mCallback != null) { 181 mCallback.onSetReport(mHidDevice, reportType, reportId, data); 182 } 183 } catch (RemoteException e) { 184 e.printStackTrace(); 185 } 186 break; 187 } 188 189 case MESSAGE_SET_PROTOCOL: 190 byte protocol = (byte) msg.arg1; 191 192 try { 193 if (mCallback != null) { 194 mCallback.onSetProtocol(mHidDevice, protocol); 195 } 196 } catch (RemoteException e) { 197 e.printStackTrace(); 198 } 199 break; 200 201 case MESSAGE_INTR_DATA: 202 byte reportId = (byte) msg.arg1; 203 byte[] data = ((ByteBuffer) msg.obj).array(); 204 205 try { 206 if (mCallback != null) { 207 mCallback.onIntrData(mHidDevice, reportId, data); 208 } 209 } catch (RemoteException e) { 210 e.printStackTrace(); 211 } 212 break; 213 214 case MESSAGE_VC_UNPLUG: 215 try { 216 if (mCallback != null) { 217 mCallback.onVirtualCableUnplug(mHidDevice); 218 } 219 } catch (RemoteException e) { 220 e.printStackTrace(); 221 } 222 mHidDevice = null; 223 break; 224 } 225 } 226 }; 227 228 private static class BluetoothHidDeviceDeathRecipient implements IBinder.DeathRecipient { 229 private HidDeviceService mService; 230 231 BluetoothHidDeviceDeathRecipient(HidDeviceService service) { 232 mService = service; 233 } 234 235 @Override 236 public void binderDied() { 237 Log.w(TAG, "Binder died, need to unregister app :("); 238 mService.unregisterApp(); 239 } 240 241 public void cleanup() { 242 mService = null; 243 } 244 } 245 246 @VisibleForTesting 247 static class BluetoothHidDeviceBinder extends IBluetoothHidDevice.Stub 248 implements IProfileServiceBinder { 249 250 private static final String TAG = BluetoothHidDeviceBinder.class.getSimpleName(); 251 252 private HidDeviceService mService; 253 254 BluetoothHidDeviceBinder(HidDeviceService service) { 255 mService = service; 256 } 257 258 @VisibleForTesting 259 HidDeviceService getServiceForTesting() { 260 if (mService != null && mService.isAvailable()) { 261 return mService; 262 } 263 return null; 264 } 265 266 @Override 267 public boolean cleanup() { 268 mService = null; 269 return true; 270 } 271 272 private HidDeviceService getService() { 273 if (!Utils.checkCaller()) { 274 Log.w(TAG, "HidDevice call not allowed for non-active user"); 275 return null; 276 } 277 278 if (mService != null && mService.isAvailable()) { 279 return mService; 280 } 281 282 return null; 283 } 284 285 @Override 286 public boolean registerApp(BluetoothHidDeviceAppConfiguration config, 287 BluetoothHidDeviceAppSdpSettings sdp, BluetoothHidDeviceAppQosSettings inQos, 288 BluetoothHidDeviceAppQosSettings outQos, IBluetoothHidDeviceCallback callback) { 289 if (DBG) { 290 Log.d(TAG, "registerApp()"); 291 } 292 293 HidDeviceService service = getService(); 294 if (service == null) { 295 return false; 296 } 297 298 return service.registerApp(sdp, inQos, outQos, callback); 299 } 300 301 @Override 302 public boolean unregisterApp(BluetoothHidDeviceAppConfiguration config) { 303 if (DBG) { 304 Log.d(TAG, "unregisterApp()"); 305 } 306 307 HidDeviceService service = getService(); 308 if (service == null) { 309 return false; 310 } 311 312 return service.unregisterApp(); 313 } 314 315 @Override 316 public boolean sendReport(BluetoothDevice device, int id, byte[] data) { 317 if (DBG) { 318 Log.d(TAG, "sendReport(): device=" + device + " id=" + id); 319 } 320 321 HidDeviceService service = getService(); 322 if (service == null) { 323 return false; 324 } 325 326 return service.sendReport(device, id, data); 327 } 328 329 @Override 330 public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) { 331 if (DBG) { 332 Log.d(TAG, "replyReport(): device=" + device + " type=" + type + " id=" + id); 333 } 334 335 HidDeviceService service = getService(); 336 if (service == null) { 337 return false; 338 } 339 340 return service.replyReport(device, type, id, data); 341 } 342 343 @Override 344 public boolean unplug(BluetoothDevice device) { 345 if (DBG) { 346 Log.d(TAG, "unplug(): device=" + device); 347 } 348 349 HidDeviceService service = getService(); 350 if (service == null) { 351 return false; 352 } 353 354 return service.unplug(device); 355 } 356 357 @Override 358 public boolean connect(BluetoothDevice device) { 359 if (DBG) { 360 Log.d(TAG, "connect(): device=" + device); 361 } 362 363 HidDeviceService service = getService(); 364 if (service == null) { 365 return false; 366 } 367 368 return service.connect(device); 369 } 370 371 @Override 372 public boolean disconnect(BluetoothDevice device) { 373 if (DBG) { 374 Log.d(TAG, "disconnect(): device=" + device); 375 } 376 377 HidDeviceService service = getService(); 378 if (service == null) { 379 return false; 380 } 381 382 return service.disconnect(device); 383 } 384 385 @Override 386 public boolean reportError(BluetoothDevice device, byte error) { 387 if (DBG) { 388 Log.d(TAG, "reportError(): device=" + device + " error=" + error); 389 } 390 391 HidDeviceService service = getService(); 392 if (service == null) { 393 return false; 394 } 395 396 return service.reportError(device, error); 397 } 398 399 @Override 400 public int getConnectionState(BluetoothDevice device) { 401 if (DBG) { 402 Log.d(TAG, "getConnectionState(): device=" + device); 403 } 404 405 HidDeviceService service = getService(); 406 if (service == null) { 407 return BluetoothHidDevice.STATE_DISCONNECTED; 408 } 409 410 return service.getConnectionState(device); 411 } 412 413 @Override 414 public List<BluetoothDevice> getConnectedDevices() { 415 if (DBG) { 416 Log.d(TAG, "getConnectedDevices()"); 417 } 418 419 return getDevicesMatchingConnectionStates(new int[]{BluetoothProfile.STATE_CONNECTED}); 420 } 421 422 @Override 423 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 424 if (DBG) { 425 Log.d(TAG, 426 "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states)); 427 } 428 429 HidDeviceService service = getService(); 430 if (service == null) { 431 return new ArrayList<BluetoothDevice>(0); 432 } 433 434 return service.getDevicesMatchingConnectionStates(states); 435 } 436 } 437 438 @Override 439 protected IProfileServiceBinder initBinder() { 440 return new BluetoothHidDeviceBinder(this); 441 } 442 443 private boolean checkDevice(BluetoothDevice device) { 444 if (mHidDevice == null || !mHidDevice.equals(device)) { 445 Log.w(TAG, "Unknown device: " + device); 446 return false; 447 } 448 return true; 449 } 450 451 private boolean checkCallingUid() { 452 int callingUid = Binder.getCallingUid(); 453 if (callingUid != mUserUid) { 454 Log.w(TAG, "checkCallingUid(): caller UID doesn't match registered user UID"); 455 return false; 456 } 457 return true; 458 } 459 460 synchronized boolean registerApp(BluetoothHidDeviceAppSdpSettings sdp, 461 BluetoothHidDeviceAppQosSettings inQos, BluetoothHidDeviceAppQosSettings outQos, 462 IBluetoothHidDeviceCallback callback) { 463 if (mUserUid != 0) { 464 Log.w(TAG, "registerApp(): failed because another app is registered"); 465 return false; 466 } 467 468 mUserUid = Binder.getCallingUid(); 469 if (DBG) { 470 Log.d(TAG, "registerApp(): calling uid=" + mUserUid); 471 } 472 mCallback = callback; 473 474 return mHidDeviceNativeInterface.registerApp(sdp.name, sdp.description, sdp.provider, 475 sdp.subclass, sdp.descriptors, inQos == null ? null : inQos.toArray(), 476 outQos == null ? null : outQos.toArray()); 477 } 478 479 synchronized boolean unregisterApp() { 480 if (DBG) { 481 Log.d(TAG, "unregisterApp()"); 482 } 483 484 int callingUid = Binder.getCallingUid(); 485 if (callingUid == mUserUid || callingUid < Process.FIRST_APPLICATION_UID) { 486 mUserUid = 0; 487 return mHidDeviceNativeInterface.unregisterApp(); 488 } 489 Log.w(TAG, "unregisterApp(): caller UID doesn't match user UID"); 490 return false; 491 } 492 493 synchronized boolean sendReport(BluetoothDevice device, int id, byte[] data) { 494 if (DBG) { 495 Log.d(TAG, "sendReport(): device=" + device + " id=" + id); 496 } 497 498 return checkDevice(device) && checkCallingUid() 499 && mHidDeviceNativeInterface.sendReport(id, data); 500 } 501 502 synchronized boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) { 503 if (DBG) { 504 Log.d(TAG, "replyReport(): device=" + device + " type=" + type + " id=" + id); 505 } 506 507 return checkDevice(device) && checkCallingUid() 508 && mHidDeviceNativeInterface.replyReport(type, id, data); 509 } 510 511 synchronized boolean unplug(BluetoothDevice device) { 512 if (DBG) { 513 Log.d(TAG, "unplug(): device=" + device); 514 } 515 516 return checkDevice(device) && checkCallingUid() 517 && mHidDeviceNativeInterface.unplug(); 518 } 519 520 synchronized boolean connect(BluetoothDevice device) { 521 if (DBG) { 522 Log.d(TAG, "connect(): device=" + device); 523 } 524 525 return checkCallingUid() && mHidDeviceNativeInterface.connect(device); 526 } 527 528 synchronized boolean disconnect(BluetoothDevice device) { 529 if (DBG) { 530 Log.d(TAG, "disconnect(): device=" + device); 531 } 532 533 int callingUid = Binder.getCallingUid(); 534 if (callingUid != mUserUid && callingUid >= Process.FIRST_APPLICATION_UID) { 535 Log.w(TAG, "disconnect(): caller UID doesn't match user UID"); 536 return false; 537 } 538 return checkDevice(device) && mHidDeviceNativeInterface.disconnect(); 539 } 540 541 synchronized boolean reportError(BluetoothDevice device, byte error) { 542 if (DBG) { 543 Log.d(TAG, "reportError(): device=" + device + " error=" + error); 544 } 545 546 return checkDevice(device) && checkCallingUid() 547 && mHidDeviceNativeInterface.reportError(error); 548 } 549 550 @Override 551 protected boolean start() { 552 if (DBG) { 553 Log.d(TAG, "start()"); 554 } 555 556 mHandler = new HidDeviceServiceHandler(); 557 setHidDeviceService(this); 558 mHidDeviceNativeInterface.init(); 559 mNativeAvailable = true; 560 return true; 561 } 562 563 @Override 564 protected boolean stop() { 565 if (DBG) { 566 Log.d(TAG, "stop()"); 567 } 568 569 return true; 570 } 571 572 @Override 573 protected boolean cleanup() { 574 if (DBG) { 575 Log.d(TAG, "cleanup()"); 576 } 577 578 if (mNativeAvailable) { 579 mHidDeviceNativeInterface.cleanup(); 580 mNativeAvailable = false; 581 } 582 583 return true; 584 } 585 586 @Override 587 public boolean onUnbind(Intent intent) { 588 Log.d(TAG, "Need to unregister app"); 589 unregisterApp(); 590 return super.onUnbind(intent); 591 } 592 593 /** 594 * Get the HID Device Service instance 595 * @return HID Device Service instance 596 */ 597 public static synchronized HidDeviceService getHidDeviceService() { 598 if (sHidDeviceService == null) { 599 Log.d(TAG, "getHidDeviceService(): service is NULL"); 600 return null; 601 } 602 if (!sHidDeviceService.isAvailable()) { 603 Log.d(TAG, "getHidDeviceService(): service is not available"); 604 return null; 605 } 606 return sHidDeviceService; 607 } 608 609 private static synchronized void setHidDeviceService(HidDeviceService instance) { 610 sHidDeviceService = instance; 611 } 612 613 int getConnectionState(BluetoothDevice device) { 614 if (mHidDevice != null && mHidDevice.equals(device)) { 615 return mHidDeviceState; 616 } 617 return BluetoothHidDevice.STATE_DISCONNECTED; 618 } 619 620 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 621 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 622 List<BluetoothDevice> inputDevices = new ArrayList<BluetoothDevice>(); 623 624 if (mHidDevice != null) { 625 for (int state : states) { 626 if (state == mHidDeviceState) { 627 inputDevices.add(mHidDevice); 628 break; 629 } 630 } 631 } 632 return inputDevices; 633 } 634 635 synchronized void onApplicationStateChangedFromNative(BluetoothDevice device, 636 boolean registered) { 637 if (DBG) { 638 Log.d(TAG, "onApplicationStateChanged(): registered=" + registered); 639 } 640 641 Message msg = mHandler.obtainMessage(MESSAGE_APPLICATION_STATE_CHANGED); 642 msg.obj = device; 643 msg.arg1 = registered ? 1 : 0; 644 mHandler.sendMessage(msg); 645 } 646 647 synchronized void onConnectStateChangedFromNative(BluetoothDevice device, int state) { 648 if (DBG) { 649 Log.d(TAG, "onConnectStateChanged(): device=" 650 + device + " state=" + state); 651 } 652 653 Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED); 654 msg.obj = device; 655 msg.arg1 = state; 656 mHandler.sendMessage(msg); 657 } 658 659 synchronized void onGetReportFromNative(byte type, byte id, short bufferSize) { 660 if (DBG) { 661 Log.d(TAG, "onGetReport(): type=" + type + " id=" + id + " bufferSize=" + bufferSize); 662 } 663 664 Message msg = mHandler.obtainMessage(MESSAGE_GET_REPORT); 665 msg.obj = bufferSize > 0 ? new Integer(bufferSize) : null; 666 msg.arg1 = type; 667 msg.arg2 = id; 668 mHandler.sendMessage(msg); 669 } 670 671 synchronized void onSetReportFromNative(byte reportType, byte reportId, byte[] data) { 672 if (DBG) { 673 Log.d(TAG, "onSetReport(): reportType=" + reportType + " reportId=" + reportId); 674 } 675 676 ByteBuffer bb = ByteBuffer.wrap(data); 677 678 Message msg = mHandler.obtainMessage(MESSAGE_SET_REPORT); 679 msg.arg1 = reportType; 680 msg.arg2 = reportId; 681 msg.obj = bb; 682 mHandler.sendMessage(msg); 683 } 684 685 synchronized void onSetProtocolFromNative(byte protocol) { 686 if (DBG) { 687 Log.d(TAG, "onSetProtocol(): protocol=" + protocol); 688 } 689 690 Message msg = mHandler.obtainMessage(MESSAGE_SET_PROTOCOL); 691 msg.arg1 = protocol; 692 mHandler.sendMessage(msg); 693 } 694 695 synchronized void onIntrDataFromNative(byte reportId, byte[] data) { 696 if (DBG) { 697 Log.d(TAG, "onIntrData(): reportId=" + reportId); 698 } 699 700 ByteBuffer bb = ByteBuffer.wrap(data); 701 702 Message msg = mHandler.obtainMessage(MESSAGE_INTR_DATA); 703 msg.arg1 = reportId; 704 msg.obj = bb; 705 mHandler.sendMessage(msg); 706 } 707 708 synchronized void onVirtualCableUnplugFromNative() { 709 if (DBG) { 710 Log.d(TAG, "onVirtualCableUnplug()"); 711 } 712 713 Message msg = mHandler.obtainMessage(MESSAGE_VC_UNPLUG); 714 mHandler.sendMessage(msg); 715 } 716 717 private void setAndBroadcastConnectionState(BluetoothDevice device, int newState) { 718 if (DBG) { 719 Log.d(TAG, "setAndBroadcastConnectionState(): device=" + device.getAddress() 720 + " oldState=" + mHidDeviceState + " newState=" + newState); 721 } 722 723 if (mHidDevice != null && !mHidDevice.equals(device)) { 724 Log.w(TAG, "Connection state changed for unknown device, ignoring"); 725 return; 726 } 727 728 int prevState = mHidDeviceState; 729 mHidDeviceState = newState; 730 731 if (prevState == newState) { 732 Log.w(TAG, "Connection state is unchanged, ignoring"); 733 return; 734 } 735 736 Intent intent = new Intent(BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED); 737 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 738 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 739 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 740 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 741 sendBroadcast(intent, BLUETOOTH_PERM); 742 } 743 744 private static int convertHalState(int halState) { 745 switch (halState) { 746 case CONN_STATE_CONNECTED: 747 return BluetoothProfile.STATE_CONNECTED; 748 case CONN_STATE_CONNECTING: 749 return BluetoothProfile.STATE_CONNECTING; 750 case CONN_STATE_DISCONNECTED: 751 return BluetoothProfile.STATE_DISCONNECTED; 752 case CONN_STATE_DISCONNECTING: 753 return BluetoothProfile.STATE_DISCONNECTING; 754 default: 755 return BluetoothProfile.STATE_DISCONNECTED; 756 } 757 } 758 759 private static final int CONN_STATE_CONNECTED = 0; 760 private static final int CONN_STATE_CONNECTING = 1; 761 private static final int CONN_STATE_DISCONNECTED = 2; 762 private static final int CONN_STATE_DISCONNECTING = 3; 763} 764