1 /* 2 * Copyright (C) 2012 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 com.android.bluetooth.hid; 18 19 import static android.Manifest.permission.BLUETOOTH_CONNECT; 20 21 import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission; 22 23 import static java.util.Objects.requireNonNull; 24 25 import android.annotation.RequiresPermission; 26 import android.bluetooth.BluetoothDevice; 27 import android.bluetooth.BluetoothHidHost; 28 import android.bluetooth.BluetoothProfile; 29 import android.bluetooth.BluetoothProtoEnums; 30 import android.bluetooth.BluetoothUuid; 31 import android.bluetooth.IBluetoothHidHost; 32 import android.content.AttributionSource; 33 import android.content.Intent; 34 import android.os.Bundle; 35 import android.os.Handler; 36 import android.os.Message; 37 import android.os.ParcelUuid; 38 import android.os.UserHandle; 39 import android.sysprop.BluetoothProperties; 40 import android.util.Log; 41 42 import androidx.annotation.VisibleForTesting; 43 44 import com.android.bluetooth.BluetoothMetricsProto; 45 import com.android.bluetooth.Utils; 46 import com.android.bluetooth.btservice.AdapterService; 47 import com.android.bluetooth.btservice.MetricsLogger; 48 import com.android.bluetooth.btservice.ProfileService; 49 import com.android.bluetooth.btservice.storage.DatabaseManager; 50 import com.android.bluetooth.flags.Flags; 51 52 import com.google.common.primitives.Ints; 53 54 import java.util.Collections; 55 import java.util.HashMap; 56 import java.util.List; 57 import java.util.Map; 58 import java.util.stream.Collectors; 59 60 /** Provides Bluetooth Hid Host profile, as a service in the Bluetooth application. */ 61 public class HidHostService extends ProfileService { 62 private static final String TAG = HidHostService.class.getSimpleName(); 63 64 public static final String ANDROID_HEADTRACKER_UUID_STR = 65 "109b862f-50e3-45cc-8ea1-ac62de4846d1"; 66 67 public static final ParcelUuid ANDROID_HEADTRACKER_UUID = 68 ParcelUuid.fromString(ANDROID_HEADTRACKER_UUID_STR); 69 70 private static class InputDevice { 71 int mSelectedTransport = BluetoothDevice.TRANSPORT_AUTO; 72 private int mHidState = BluetoothProfile.STATE_DISCONNECTED; 73 private int mHogpState = BluetoothProfile.STATE_DISCONNECTED; 74 getState(int transport)75 int getState(int transport) { 76 return (transport == BluetoothDevice.TRANSPORT_LE) ? mHogpState : mHidState; 77 } 78 getState()79 int getState() { 80 return getState(mSelectedTransport); 81 } 82 setState(int transport, int state)83 void setState(int transport, int state) { 84 if (transport == BluetoothDevice.TRANSPORT_LE) { 85 mHogpState = state; 86 } else { 87 mHidState = state; 88 } 89 } 90 setState(int state)91 void setState(int state) { 92 setState(mSelectedTransport, state); 93 } 94 95 @Override toString()96 public String toString() { 97 return ("Selected transport=" + mSelectedTransport) 98 + (" HID connection state=" + mHidState) 99 + (" HOGP connection state=" + mHogpState); 100 } 101 } 102 103 private static HidHostService sHidHostService; 104 105 private final Map<BluetoothDevice, InputDevice> mInputDevices = 106 Collections.synchronizedMap(new HashMap<>()); 107 108 private final AdapterService mAdapterService; 109 private final DatabaseManager mDatabaseManager; 110 private final HidHostNativeInterface mNativeInterface; 111 112 private boolean mNativeAvailable; 113 private BluetoothDevice mTargetDevice = null; 114 115 private static final int MESSAGE_CONNECT = 1; 116 private static final int MESSAGE_DISCONNECT = 2; 117 private static final int MESSAGE_CONNECT_STATE_CHANGED = 3; 118 private static final int MESSAGE_GET_PROTOCOL_MODE = 4; 119 private static final int MESSAGE_VIRTUAL_UNPLUG = 5; 120 private static final int MESSAGE_ON_GET_PROTOCOL_MODE = 6; 121 private static final int MESSAGE_SET_PROTOCOL_MODE = 7; 122 private static final int MESSAGE_GET_REPORT = 8; 123 private static final int MESSAGE_ON_GET_REPORT = 9; 124 private static final int MESSAGE_SET_REPORT = 10; 125 private static final int MESSAGE_ON_VIRTUAL_UNPLUG = 12; 126 private static final int MESSAGE_ON_HANDSHAKE = 13; 127 private static final int MESSAGE_GET_IDLE_TIME = 14; 128 private static final int MESSAGE_ON_GET_IDLE_TIME = 15; 129 private static final int MESSAGE_SET_IDLE_TIME = 16; 130 private static final int MESSAGE_SET_PREFERRED_TRANSPORT = 17; 131 private static final int MESSAGE_SEND_DATA = 18; 132 133 public static final int STATE_DISCONNECTED = BluetoothProfile.STATE_DISCONNECTED; 134 public static final int STATE_CONNECTING = BluetoothProfile.STATE_CONNECTING; 135 public static final int STATE_CONNECTED = BluetoothProfile.STATE_CONNECTED; 136 public static final int STATE_DISCONNECTING = BluetoothProfile.STATE_DISCONNECTING; 137 public static final int STATE_ACCEPTING = BluetoothProfile.STATE_DISCONNECTING + 1; 138 HidHostService(AdapterService adapterService)139 public HidHostService(AdapterService adapterService) { 140 super(adapterService); 141 142 mAdapterService = requireNonNull(adapterService); 143 mDatabaseManager = requireNonNull(mAdapterService.getDatabase()); 144 mNativeInterface = requireNonNull(HidHostNativeInterface.getInstance()); 145 } 146 isEnabled()147 public static boolean isEnabled() { 148 return BluetoothProperties.isProfileHidHostEnabled().orElse(false); 149 } 150 151 @Override initBinder()152 public IProfileServiceBinder initBinder() { 153 return new BluetoothHidHostBinder(this); 154 } 155 156 @Override start()157 public void start() { 158 mNativeInterface.init(this); 159 mNativeAvailable = true; 160 setHidHostService(this); 161 } 162 163 @Override stop()164 public void stop() { 165 Log.d(TAG, "Stop"); 166 } 167 168 @Override cleanup()169 public void cleanup() { 170 Log.d(TAG, "Cleanup"); 171 if (mNativeAvailable) { 172 mNativeInterface.cleanup(); 173 mNativeAvailable = false; 174 } 175 176 if (mInputDevices != null) { 177 for (BluetoothDevice device : mInputDevices.keySet()) { 178 // Set both HID and HOGP connection states to disconnected 179 updateConnectionState( 180 device, BluetoothDevice.TRANSPORT_LE, BluetoothProfile.STATE_DISCONNECTED); 181 updateConnectionState( 182 device, 183 BluetoothDevice.TRANSPORT_BREDR, 184 BluetoothProfile.STATE_DISCONNECTED); 185 } 186 mInputDevices.clear(); 187 } 188 // TODO(b/72948646): this should be moved to stop() 189 setHidHostService(null); 190 } 191 getIdentityAddress(BluetoothDevice device)192 private byte[] getIdentityAddress(BluetoothDevice device) { 193 if (Flags.identityAddressNullIfUnknown()) { 194 return Utils.getByteBrEdrAddress(mAdapterService, device); 195 } else { 196 return mAdapterService.getByteIdentityAddress(device); 197 } 198 } 199 getByteAddress(BluetoothDevice device, int transport)200 private byte[] getByteAddress(BluetoothDevice device, int transport) { 201 ParcelUuid[] uuids = mAdapterService.getRemoteUuids(device); 202 203 if (!Flags.allowSwitchingHidAndHogp()) { 204 boolean hogpSupported = Utils.arrayContains(uuids, BluetoothUuid.HOGP); 205 boolean headtrackerSupported = 206 Flags.androidHeadtrackerService() 207 && Utils.arrayContains(uuids, HidHostService.ANDROID_HEADTRACKER_UUID); 208 209 if (hogpSupported || headtrackerSupported) { 210 // Use pseudo address when HOGP is available 211 return Utils.getByteAddress(device); 212 } else { 213 // Otherwise use identity address 214 return getIdentityAddress(device); 215 } 216 } 217 218 if (transport == BluetoothDevice.TRANSPORT_LE) { 219 // Use pseudo address when HOGP is to be used 220 return Utils.getByteAddress(device); 221 } else if (transport == BluetoothDevice.TRANSPORT_BREDR) { 222 // Use identity address if HID is to be used 223 return getIdentityAddress(device); 224 } else { // BluetoothDevice.TRANSPORT_AUTO 225 boolean hidSupported = Utils.arrayContains(uuids, BluetoothUuid.HID); 226 // Prefer HID over HOGP 227 if (hidSupported) { 228 // Use identity address if HID is available 229 return getIdentityAddress(device); 230 } else { 231 // Otherwise use pseudo address 232 return Utils.getByteAddress(device); 233 } 234 } 235 } 236 getByteAddress(BluetoothDevice device)237 private byte[] getByteAddress(BluetoothDevice device) { 238 return getByteAddress(device, getTransport(device)); 239 } 240 241 /** 242 * Retrieves device address type 243 * 244 * @param device remote device 245 * @return address type 246 */ getAddressType(BluetoothDevice device)247 private int getAddressType(BluetoothDevice device) { 248 if (Flags.getAddressTypeApi()) { 249 return device.getAddressType(); 250 } 251 252 return BluetoothDevice.ADDRESS_TYPE_PUBLIC; 253 } 254 255 /** 256 * Retrieves preferred transport for the device 257 * 258 * @param device remote device 259 * @return transport 260 */ getTransport(BluetoothDevice device)261 private int getTransport(BluetoothDevice device) { 262 InputDevice inputDevice = mInputDevices.get(device); 263 if (inputDevice != null) { 264 return inputDevice.mSelectedTransport; 265 } 266 267 return BluetoothDevice.TRANSPORT_AUTO; 268 } 269 270 /** 271 * Saves the preferred transport for the input device. Adds an input device entry if not present 272 * 273 * @param device remote device 274 * @param transport preferred transport 275 */ setTransport(BluetoothDevice device, int transport)276 private void setTransport(BluetoothDevice device, int transport) { 277 InputDevice inputDevice = getOrCreateInputDevice(device); 278 if (inputDevice.mSelectedTransport != transport) { 279 inputDevice.mSelectedTransport = transport; 280 } 281 } 282 283 /** 284 * Retrieves the input device object. Creates a new one if it does not exist 285 * 286 * @param device remote device 287 * @return input device object 288 */ getOrCreateInputDevice(BluetoothDevice device)289 private InputDevice getOrCreateInputDevice(BluetoothDevice device) { 290 return mInputDevices.computeIfAbsent(device, k -> new InputDevice()); 291 } 292 293 /** 294 * Retrieves the connection state 295 * 296 * @param device remote device 297 * @param transport transport 298 * @return connection state 299 */ getState(BluetoothDevice device, int transport)300 private int getState(BluetoothDevice device, int transport) { 301 InputDevice inputDevice = mInputDevices.get(device); 302 if (inputDevice != null) { 303 return inputDevice.getState(transport); 304 } 305 306 return BluetoothProfile.STATE_DISCONNECTED; 307 } 308 getHidHostService()309 public static synchronized HidHostService getHidHostService() { 310 if (sHidHostService == null) { 311 Log.w(TAG, "getHidHostService(): service is null"); 312 return null; 313 } 314 if (!sHidHostService.isAvailable()) { 315 Log.w(TAG, "getHidHostService(): service is not available "); 316 return null; 317 } 318 return sHidHostService; 319 } 320 setHidHostService(HidHostService instance)321 private static synchronized void setHidHostService(HidHostService instance) { 322 Log.d(TAG, "setHidHostService(): set to: " + instance); 323 sHidHostService = instance; 324 } 325 326 /** 327 * Requests the native stack to start HID connection 328 * 329 * @param device remote device 330 * @param transport transport to be used 331 * @return true if successfully requested, else false 332 */ nativeConnect(BluetoothDevice device, int transport)333 private boolean nativeConnect(BluetoothDevice device, int transport) { 334 if (!mNativeInterface.connectHid( 335 getByteAddress(device, transport), getAddressType(device), transport)) { 336 Log.w( 337 TAG, 338 "nativeConnect: Connection attempt failed." 339 + (" device=" + device) 340 + (" transport=" + transport)); 341 342 if (!Flags.allowSwitchingHidAndHogp()) { 343 updateConnectionState(device, transport, BluetoothProfile.STATE_DISCONNECTING); 344 updateConnectionState(device, transport, BluetoothProfile.STATE_DISCONNECTED); 345 } 346 return false; 347 } 348 return true; 349 } 350 351 /** 352 * Requests the native stack to start HID disconnection 353 * 354 * @param device remote device 355 * @param transport transport 356 * @param reconnectAllowed true if remote device is allowed to initiate reconnections, else 357 * false 358 * @return true if successfully requested, else false 359 */ nativeDisconnect( BluetoothDevice device, int transport, boolean reconnectAllowed)360 private boolean nativeDisconnect( 361 BluetoothDevice device, int transport, boolean reconnectAllowed) { 362 if (!mNativeInterface.disconnectHid( 363 getByteAddress(device, transport), 364 getAddressType(device), 365 transport, 366 reconnectAllowed)) { 367 Log.w( 368 TAG, 369 "nativeDisconnect: Disconnection attempt failed." 370 + (" device=" + device) 371 + (" transport=" + transport)); 372 if (!Flags.allowSwitchingHidAndHogp()) { 373 updateConnectionState(device, transport, BluetoothProfile.STATE_DISCONNECTING); 374 updateConnectionState(device, transport, BluetoothProfile.STATE_DISCONNECTED); 375 } 376 return false; 377 } 378 return true; 379 } 380 381 private final Handler mHandler = 382 new Handler() { 383 @Override 384 public void handleMessage(Message msg) { 385 Log.v(TAG, "handleMessage(): msg.what=" + msg.what); 386 387 switch (msg.what) { 388 case MESSAGE_CONNECT: 389 handleMessageConnect(msg); 390 break; 391 case MESSAGE_DISCONNECT: 392 handleMessageDisconnect(msg); 393 break; 394 case MESSAGE_CONNECT_STATE_CHANGED: 395 handleMessageConnectStateChanged(msg); 396 break; 397 case MESSAGE_GET_PROTOCOL_MODE: 398 handleMessageGetProtocolMode(msg); 399 break; 400 case MESSAGE_ON_GET_PROTOCOL_MODE: 401 handleMessageOnGetProtocolMode(msg); 402 break; 403 case MESSAGE_VIRTUAL_UNPLUG: 404 handleMessageVirtualUnplug(msg); 405 break; 406 case MESSAGE_SET_PROTOCOL_MODE: 407 handleMessageSetProtocolMode(msg); 408 break; 409 case MESSAGE_GET_REPORT: 410 handleMessageGetReport(msg); 411 break; 412 case MESSAGE_ON_GET_REPORT: 413 handleMessageOnGetReport(msg); 414 break; 415 case MESSAGE_ON_HANDSHAKE: 416 handleMessageOnHandshake(msg); 417 break; 418 case MESSAGE_SET_REPORT: 419 handleMessageSetReport(msg); 420 break; 421 case MESSAGE_ON_VIRTUAL_UNPLUG: 422 handleMessageOnVirtualUnplug(msg); 423 break; 424 case MESSAGE_GET_IDLE_TIME: 425 handleMessageGetIdleTime(msg); 426 break; 427 case MESSAGE_ON_GET_IDLE_TIME: 428 handleMessageOnGetIdleTime(msg); 429 break; 430 case MESSAGE_SET_IDLE_TIME: 431 handleMessageSetIdleTime(msg); 432 break; 433 case MESSAGE_SET_PREFERRED_TRANSPORT: 434 handleMessageSetPreferredTransport(msg); 435 break; 436 case MESSAGE_SEND_DATA: 437 handleMessageSendData(msg); 438 break; 439 } 440 } 441 }; 442 handleMessageSendData(Message msg)443 private void handleMessageSendData(Message msg) { 444 if (!Flags.allowSwitchingHidAndHogp()) { 445 return; 446 } 447 BluetoothDevice device = mAdapterService.getDeviceFromByte((byte[]) msg.obj); 448 449 Bundle data = msg.getData(); 450 String report = data.getString(BluetoothHidHost.EXTRA_REPORT); 451 452 if (!mNativeInterface.sendData( 453 getByteAddress(device), getAddressType(device), getTransport(device), report)) { 454 Log.e(TAG, "handleMessageSendData: Failed to send data"); 455 } 456 } 457 handleMessageSetPreferredTransport(Message msg)458 private void handleMessageSetPreferredTransport(Message msg) { 459 BluetoothDevice device = (BluetoothDevice) msg.obj; 460 int transport = msg.arg1; 461 462 int prevTransport = getTransport(device); 463 Log.i( 464 TAG, 465 "handleMessageSetPreferredTransport: Transport changed" 466 + (" device=" + device) 467 + (" transport: prev=" + prevTransport + " -> new=" + transport)); 468 469 // Save the preferred transport 470 InputDevice inputDevice = getOrCreateInputDevice(device); 471 inputDevice.mSelectedTransport = transport; 472 473 /* If connections are allowed, ensure that the previous transport is disconnected and the 474 new transport is connected */ 475 if (getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 476 if (prevTransport != transport) { 477 Log.i( 478 TAG, 479 "handleMessageSetPreferredTransport: Connection switch" 480 + (" device=" + device) 481 + (" transport: prev=" + prevTransport + " -> new=" + transport)); 482 // Disconnect the other transport and disallow reconnections 483 nativeDisconnect(device, prevTransport, false); 484 485 // Request to connect the preferred transport 486 nativeConnect(device, transport); 487 } 488 } 489 } 490 handleMessageSetIdleTime(Message msg)491 private void handleMessageSetIdleTime(Message msg) { 492 BluetoothDevice device = (BluetoothDevice) msg.obj; 493 Bundle data = msg.getData(); 494 byte idleTime = data.getByte(BluetoothHidHost.EXTRA_IDLE_TIME); 495 if (!mNativeInterface.setIdleTime( 496 getByteAddress(device), getAddressType(device), getTransport(device), idleTime)) { 497 Log.e(TAG, "Error: get idle time native returns false"); 498 } 499 } 500 handleMessageOnGetIdleTime(Message msg)501 private void handleMessageOnGetIdleTime(Message msg) { 502 BluetoothDevice device = mAdapterService.getDeviceFromByte((byte[]) msg.obj); 503 int transport = msg.arg1; 504 505 if (!checkTransport(device, transport, msg.what)) { 506 return; 507 } 508 509 int idleTime = msg.arg2; 510 broadcastIdleTime(device, idleTime); 511 } 512 handleMessageGetIdleTime(Message msg)513 private void handleMessageGetIdleTime(Message msg) { 514 BluetoothDevice device = (BluetoothDevice) msg.obj; 515 if (!mNativeInterface.getIdleTime( 516 getByteAddress(device), getAddressType(device), getTransport(device))) { 517 Log.e(TAG, "Error: get idle time native returns false"); 518 } 519 } 520 handleMessageOnVirtualUnplug(Message msg)521 private void handleMessageOnVirtualUnplug(Message msg) { 522 BluetoothDevice device = mAdapterService.getDeviceFromByte((byte[]) msg.obj); 523 int transport = msg.arg1; 524 if (!checkTransport(device, transport, msg.what)) { 525 return; 526 } 527 int status = msg.arg2; 528 broadcastVirtualUnplugStatus(device, status); 529 } 530 handleMessageSetReport(Message msg)531 private void handleMessageSetReport(Message msg) { 532 BluetoothDevice device = (BluetoothDevice) msg.obj; 533 Bundle data = msg.getData(); 534 byte reportType = data.getByte(BluetoothHidHost.EXTRA_REPORT_TYPE); 535 String report = data.getString(BluetoothHidHost.EXTRA_REPORT); 536 if (!mNativeInterface.setReport( 537 getByteAddress(device), 538 getAddressType(device), 539 getTransport(device), 540 reportType, 541 report)) { 542 Log.e(TAG, "Error: set report native returns false"); 543 } 544 } 545 handleMessageOnHandshake(Message msg)546 private void handleMessageOnHandshake(Message msg) { 547 BluetoothDevice device = mAdapterService.getDeviceFromByte((byte[]) msg.obj); 548 int transport = msg.arg1; 549 if (!checkTransport(device, transport, msg.what)) { 550 return; 551 } 552 553 int status = msg.arg2; 554 broadcastHandshake(device, status); 555 } 556 handleMessageOnGetReport(Message msg)557 private void handleMessageOnGetReport(Message msg) { 558 BluetoothDevice device = mAdapterService.getDeviceFromByte((byte[]) msg.obj); 559 int transport = msg.arg1; 560 if (!checkTransport(device, transport, msg.what)) { 561 return; 562 } 563 564 Bundle data = msg.getData(); 565 byte[] report = data.getByteArray(BluetoothHidHost.EXTRA_REPORT); 566 int bufferSize = data.getInt(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE); 567 broadcastReport(device, report, bufferSize); 568 } 569 handleMessageGetReport(Message msg)570 private void handleMessageGetReport(Message msg) { 571 BluetoothDevice device = (BluetoothDevice) msg.obj; 572 Bundle data = msg.getData(); 573 byte reportType = data.getByte(BluetoothHidHost.EXTRA_REPORT_TYPE); 574 byte reportId = data.getByte(BluetoothHidHost.EXTRA_REPORT_ID); 575 int bufferSize = data.getInt(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE); 576 if (!mNativeInterface.getReport( 577 getByteAddress(device), 578 getAddressType(device), 579 getTransport(device), 580 reportType, 581 reportId, 582 bufferSize)) { 583 Log.e(TAG, "Error: get report native returns false"); 584 } 585 } 586 handleMessageSetProtocolMode(Message msg)587 private void handleMessageSetProtocolMode(Message msg) { 588 BluetoothDevice device = (BluetoothDevice) msg.obj; 589 byte protocolMode = (byte) msg.arg1; 590 Log.d(TAG, "sending set protocol mode(" + protocolMode + ")"); 591 if (!mNativeInterface.setProtocolMode( 592 getByteAddress(device), 593 getAddressType(device), 594 getTransport(device), 595 protocolMode)) { 596 Log.e(TAG, "Error: set protocol mode native returns false"); 597 } 598 } 599 handleMessageVirtualUnplug(Message msg)600 private void handleMessageVirtualUnplug(Message msg) { 601 BluetoothDevice device = (BluetoothDevice) msg.obj; 602 if (!mNativeInterface.virtualUnPlug( 603 getByteAddress(device), getAddressType(device), getTransport(device))) { 604 Log.e(TAG, "Error: virtual unplug native returns false"); 605 } 606 } 607 handleMessageOnGetProtocolMode(Message msg)608 private void handleMessageOnGetProtocolMode(Message msg) { 609 BluetoothDevice device = mAdapterService.getDeviceFromByte((byte[]) msg.obj); 610 int transport = msg.arg1; 611 int protocolMode = msg.arg2; 612 613 if (!checkTransport(device, transport, msg.what)) { 614 return; 615 } 616 617 broadcastProtocolMode(device, protocolMode); 618 } 619 handleMessageGetProtocolMode(Message msg)620 private void handleMessageGetProtocolMode(Message msg) { 621 BluetoothDevice device = (BluetoothDevice) msg.obj; 622 if (!mNativeInterface.getProtocolMode( 623 getByteAddress(device), getAddressType(device), getTransport(device))) { 624 Log.e(TAG, "Error: get protocol mode native returns false"); 625 } 626 } 627 handleMessageConnectStateChanged(Message msg)628 private void handleMessageConnectStateChanged(Message msg) { 629 BluetoothDevice device = mAdapterService.getDeviceFromByte((byte[]) msg.obj); 630 int transport = msg.arg1; 631 int state = msg.arg2; 632 int prevState = getState(device, transport); 633 634 if (Flags.allowSwitchingHidAndHogp()) { 635 InputDevice inputDevice = mInputDevices.get(device); 636 if (inputDevice != null) { 637 // Update transport if it was not resolved already 638 if (inputDevice.mSelectedTransport == BluetoothDevice.TRANSPORT_AUTO) { 639 inputDevice.mSelectedTransport = transport; 640 setTransport(device, transport); 641 } 642 } else { 643 // ACCEPTING state for unknown device indicates that this device 644 // was loaded from storage. Add it in the record. 645 if (state == STATE_ACCEPTING) { 646 setTransport(device, transport); 647 } else { 648 Log.e( 649 TAG, 650 "handleMessageConnectStateChanged: Disconnect and unknown inputDevice" 651 + (" device=" + device) 652 + (" state=" + state)); 653 nativeDisconnect(device, transport, false); 654 return; 655 } 656 } 657 658 if (transport != getTransport(device)) { 659 Log.w( 660 TAG, 661 "handleMessageConnectStateChanged: Not preferred transport in message" 662 + (" device=" + device) 663 + (" transport=" + transport) 664 + (" newState=" + state) 665 + (" prevState=" + prevState)); 666 } 667 } else { 668 // Only TRANSPORT_AUTO should be used when allowSwitchingHidAndHogp is disabled 669 transport = BluetoothDevice.TRANSPORT_AUTO; 670 setTransport(device, BluetoothDevice.TRANSPORT_AUTO); 671 } 672 673 Log.d( 674 TAG, 675 "handleMessageConnectStateChanged:" 676 + (" device=" + device) 677 + (" newState=" + state) 678 + (" prevState=" + prevState)); 679 680 boolean connectionAllowed = true; 681 // Process connection 682 if (prevState == BluetoothProfile.STATE_DISCONNECTED 683 && state == BluetoothProfile.STATE_CONNECTED) { 684 connectionAllowed = processConnection(device, transport); 685 } 686 687 // ACCEPTING state has to be treated as DISCONNECTED state 688 int reportedState = state; 689 if (state == STATE_ACCEPTING) { 690 reportedState = BluetoothProfile.STATE_DISCONNECTED; 691 } 692 693 if (Flags.allowSwitchingHidAndHogp() || connectionAllowed) { 694 updateConnectionState(device, transport, reportedState); 695 } 696 updateQuietMode(device, reportedState); 697 } 698 handleMessageDisconnect(Message msg)699 private void handleMessageDisconnect(Message msg) { 700 BluetoothDevice device = (BluetoothDevice) msg.obj; 701 int connectionPolicy = msg.arg1; 702 703 boolean reconnectAllowed = true; 704 if (Flags.allowSwitchingHidAndHogp()) { 705 if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 706 reconnectAllowed = false; 707 } 708 } 709 710 nativeDisconnect(device, getTransport(device), reconnectAllowed); 711 } 712 handleMessageConnect(Message msg)713 private void handleMessageConnect(Message msg) { 714 BluetoothDevice device = (BluetoothDevice) msg.obj; 715 InputDevice inputDevice = getOrCreateInputDevice(device); 716 717 if (Flags.allowSwitchingHidAndHogp()) { 718 int connectionPolicy = getConnectionPolicy(device); 719 if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 720 Log.e( 721 TAG, 722 "handleMessageConnect: Connection not allowed." 723 + (" device=" + device) 724 + (" connectionPolicy=" + connectionPolicy)); 725 726 return; 727 } 728 } 729 730 nativeConnect(device, inputDevice.mSelectedTransport); 731 } 732 733 /** 734 * Checks if the reported transport does not match the selected transport 735 * 736 * @param device remote device 737 * @param transport reported transport 738 * @param message message ID for logging purpose 739 * @return true if transport matches, otherwise false 740 */ checkTransport(BluetoothDevice device, int transport, int message)741 private boolean checkTransport(BluetoothDevice device, int transport, int message) { 742 if (Flags.allowSwitchingHidAndHogp() && getTransport(device) != transport) { 743 Log.w( 744 TAG, 745 "checkTransport:" 746 + (" message= " + message) 747 + (" reported transport(" + transport + ")") 748 + (" doesn't match selected transport(" + getTransport(device) + ")")); 749 return false; 750 } 751 return true; 752 } 753 754 /** 755 * Handles connection complete 756 * 757 * @param device remote device 758 * @return true if the connection is being retained, otherwise false 759 */ processConnection(BluetoothDevice device, int transport)760 private boolean processConnection(BluetoothDevice device, int transport) { 761 if (!okToConnect(device)) { 762 Log.w( 763 TAG, 764 "processConnection: Incoming HID connection rejected." 765 + (" device=" + device) 766 + (" connectionPolicy=" + getConnectionPolicy(device))); 767 768 if (Flags.allowSwitchingHidAndHogp()) { 769 nativeDisconnect(device, transport, false); 770 } else { 771 mNativeInterface.virtualUnPlug( 772 getByteAddress(device), getAddressType(device), getTransport(device)); 773 } 774 return false; 775 } 776 return true; 777 } 778 779 /** 780 * Disables the quiet mode if target device gets connected 781 * 782 * @param device remote device 783 * @param state connection state 784 */ updateQuietMode(BluetoothDevice device, int state)785 private void updateQuietMode(BluetoothDevice device, int state) { 786 if (state == BluetoothProfile.STATE_CONNECTED 787 && mTargetDevice != null 788 && mTargetDevice.equals(device)) { 789 // Locally initiated connection, move out of quiet mode 790 Log.i(TAG, "updateQuietMode: Move out of quiet mode. device=" + device); 791 mTargetDevice = null; 792 mAdapterService.enable(false); 793 } 794 } 795 796 @VisibleForTesting 797 static class BluetoothHidHostBinder extends IBluetoothHidHost.Stub 798 implements IProfileServiceBinder { 799 private HidHostService mService; 800 BluetoothHidHostBinder(HidHostService svc)801 BluetoothHidHostBinder(HidHostService svc) { 802 mService = svc; 803 } 804 805 @Override cleanup()806 public void cleanup() { 807 mService = null; 808 } 809 810 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getService(AttributionSource source)811 private HidHostService getService(AttributionSource source) { 812 if (Utils.isInstrumentationTestMode()) { 813 return mService; 814 } 815 if (!Utils.checkServiceAvailable(mService, TAG) 816 || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG) 817 || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) { 818 return null; 819 } 820 return mService; 821 } 822 823 @Override connect(BluetoothDevice device, AttributionSource source)824 public boolean connect(BluetoothDevice device, AttributionSource source) { 825 HidHostService service = getService(source); 826 if (service == null) { 827 return false; 828 } 829 enforceBluetoothPrivilegedPermission(service); 830 return service.connect(device); 831 } 832 833 @Override disconnect(BluetoothDevice device, AttributionSource source)834 public boolean disconnect(BluetoothDevice device, AttributionSource source) { 835 HidHostService service = getService(source); 836 if (service == null) { 837 return false; 838 } 839 enforceBluetoothPrivilegedPermission(service); 840 return service.disconnect(device); 841 } 842 843 @Override getConnectionState(BluetoothDevice device, AttributionSource source)844 public int getConnectionState(BluetoothDevice device, AttributionSource source) { 845 HidHostService service = getService(source); 846 if (service == null) { 847 return BluetoothProfile.STATE_DISCONNECTED; 848 } 849 return service.getConnectionState(device); 850 } 851 852 @Override getConnectedDevices(AttributionSource source)853 public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { 854 return getDevicesMatchingConnectionStates( 855 new int[] {BluetoothProfile.STATE_CONNECTED}, source); 856 } 857 858 @Override getDevicesMatchingConnectionStates( int[] states, AttributionSource source)859 public List<BluetoothDevice> getDevicesMatchingConnectionStates( 860 int[] states, AttributionSource source) { 861 HidHostService service = getService(source); 862 if (service == null) { 863 return Collections.emptyList(); 864 } 865 return service.getDevicesMatchingConnectionStates(states); 866 } 867 868 @Override setConnectionPolicy( BluetoothDevice device, int connectionPolicy, AttributionSource source)869 public boolean setConnectionPolicy( 870 BluetoothDevice device, int connectionPolicy, AttributionSource source) { 871 HidHostService service = getService(source); 872 if (service == null) { 873 return false; 874 } 875 enforceBluetoothPrivilegedPermission(service); 876 return service.setConnectionPolicy(device, connectionPolicy); 877 } 878 879 @Override getConnectionPolicy(BluetoothDevice device, AttributionSource source)880 public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { 881 HidHostService service = getService(source); 882 if (service == null) { 883 return BluetoothProfile.CONNECTION_POLICY_UNKNOWN; 884 } 885 enforceBluetoothPrivilegedPermission(service); 886 return service.getConnectionPolicy(device); 887 } 888 889 @Override setPreferredTransport( BluetoothDevice device, int transport, AttributionSource source)890 public boolean setPreferredTransport( 891 BluetoothDevice device, int transport, AttributionSource source) { 892 HidHostService service = getService(source); 893 if (service == null) { 894 return false; 895 } 896 enforceBluetoothPrivilegedPermission(service); 897 return service.setPreferredTransport(device, transport); 898 } 899 900 @Override getPreferredTransport(BluetoothDevice device, AttributionSource source)901 public int getPreferredTransport(BluetoothDevice device, AttributionSource source) { 902 HidHostService service = getService(source); 903 if (service == null) { 904 return BluetoothDevice.TRANSPORT_AUTO; 905 } 906 enforceBluetoothPrivilegedPermission(service); 907 return service.getPreferredTransport(device); 908 } 909 910 /* The following APIs regarding test app for compliance */ 911 @Override getProtocolMode(BluetoothDevice device, AttributionSource source)912 public boolean getProtocolMode(BluetoothDevice device, AttributionSource source) { 913 HidHostService service = getService(source); 914 if (service == null) { 915 return false; 916 } 917 return service.getProtocolMode(device); 918 } 919 920 @Override virtualUnplug(BluetoothDevice device, AttributionSource source)921 public boolean virtualUnplug(BluetoothDevice device, AttributionSource source) { 922 HidHostService service = getService(source); 923 if (service == null) { 924 return false; 925 } 926 return service.virtualUnplug(device); 927 } 928 929 @Override setProtocolMode( BluetoothDevice device, int protocolMode, AttributionSource source)930 public boolean setProtocolMode( 931 BluetoothDevice device, int protocolMode, AttributionSource source) { 932 HidHostService service = getService(source); 933 if (service == null) { 934 return false; 935 } 936 return service.setProtocolMode(device, protocolMode); 937 } 938 939 @Override getReport( BluetoothDevice device, byte reportType, byte reportId, int bufferSize, AttributionSource source)940 public boolean getReport( 941 BluetoothDevice device, 942 byte reportType, 943 byte reportId, 944 int bufferSize, 945 AttributionSource source) { 946 HidHostService service = getService(source); 947 if (service == null) { 948 return false; 949 } 950 return service.getReport(device, reportType, reportId, bufferSize); 951 } 952 953 @Override setReport( BluetoothDevice device, byte reportType, String report, AttributionSource source)954 public boolean setReport( 955 BluetoothDevice device, byte reportType, String report, AttributionSource source) { 956 HidHostService service = getService(source); 957 if (service == null) { 958 return false; 959 } 960 return service.setReport(device, reportType, report); 961 } 962 963 @Override sendData(BluetoothDevice device, String report, AttributionSource source)964 public boolean sendData(BluetoothDevice device, String report, AttributionSource source) { 965 HidHostService service = getService(source); 966 if (service == null) { 967 return false; 968 } 969 return service.sendData(device, report); 970 } 971 972 @Override setIdleTime( BluetoothDevice device, byte idleTime, AttributionSource source)973 public boolean setIdleTime( 974 BluetoothDevice device, byte idleTime, AttributionSource source) { 975 HidHostService service = getService(source); 976 if (service == null) { 977 return false; 978 } 979 return service.setIdleTime(device, idleTime); 980 } 981 982 @Override getIdleTime(BluetoothDevice device, AttributionSource source)983 public boolean getIdleTime(BluetoothDevice device, AttributionSource source) { 984 HidHostService service = getService(source); 985 if (service == null) { 986 return false; 987 } 988 return service.getIdleTime(device); 989 } 990 } 991 ; 992 993 // APIs 994 995 /** 996 * Connects the hid host profile for the passed in device 997 * 998 * @param device is the device with which to connect the hid host profile 999 * @return true if connection request is passed down to mHandler. 1000 */ connect(BluetoothDevice device)1001 public boolean connect(BluetoothDevice device) { 1002 Log.d(TAG, "connect: device=" + device); 1003 int state = getConnectionState(device); 1004 if (state != BluetoothProfile.STATE_DISCONNECTED) { 1005 Log.e(TAG, "Device " + device + " not disconnected. state=" + state); 1006 return false; 1007 } 1008 if (getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { 1009 Log.e(TAG, "Device " + device + " CONNECTION_POLICY_FORBIDDEN"); 1010 return false; 1011 } 1012 1013 Message msg = mHandler.obtainMessage(MESSAGE_CONNECT, device); 1014 mHandler.sendMessage(msg); 1015 return true; 1016 } 1017 1018 /** 1019 * Disconnects the hid host profile from the passed in device 1020 * 1021 * @param device is the device with which to disconnect the hid host profile 1022 * @return true 1023 */ disconnect(BluetoothDevice device, int connectionPolicy)1024 private boolean disconnect(BluetoothDevice device, int connectionPolicy) { 1025 Log.d(TAG, "disconnect: device=" + device + " connectionPolicy=" + connectionPolicy); 1026 Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT, device); 1027 msg.arg1 = connectionPolicy; 1028 mHandler.sendMessage(msg); 1029 return true; 1030 } 1031 1032 /** 1033 * Disconnects the hid host profile from the passed in device 1034 * 1035 * @param device is the device with which to disconnect the hid host profile 1036 * @return true 1037 */ disconnect(BluetoothDevice device)1038 public boolean disconnect(BluetoothDevice device) { 1039 disconnect(device, getConnectionPolicy(device)); 1040 return true; 1041 } 1042 1043 /** 1044 * Get the current connection state of the profile 1045 * 1046 * @param device is the remote bluetooth device 1047 * @return {@link BluetoothProfile#STATE_DISCONNECTED} if this profile is disconnected, {@link 1048 * BluetoothProfile#STATE_CONNECTING} if this profile is being connected, {@link 1049 * BluetoothProfile#STATE_CONNECTED} if this profile is connected, or {@link 1050 * BluetoothProfile#STATE_DISCONNECTING} if this profile is being disconnected 1051 */ getConnectionState(BluetoothDevice device)1052 public int getConnectionState(BluetoothDevice device) { 1053 Log.d(TAG, "getConnectionState: device=" + device); 1054 InputDevice inputDevice = mInputDevices.get(device); 1055 if (inputDevice != null) { 1056 return inputDevice.getState(); 1057 } 1058 return BluetoothProfile.STATE_DISCONNECTED; 1059 } 1060 getDevicesMatchingConnectionStates(int[] states)1061 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 1062 Log.d(TAG, "getDevicesMatchingConnectionStates()"); 1063 return mInputDevices.entrySet().stream() 1064 .filter(entry -> Ints.asList(states).contains(entry.getValue().getState())) 1065 .map(Map.Entry::getKey) 1066 .collect(Collectors.toList()); 1067 } 1068 1069 /** 1070 * Set connection policy of the profile and connects it if connectionPolicy is {@link 1071 * BluetoothProfile#CONNECTION_POLICY_ALLOWED} or disconnects if connectionPolicy is {@link 1072 * BluetoothProfile#CONNECTION_POLICY_FORBIDDEN} 1073 * 1074 * <p>The device should already be paired. Connection policy can be one of: {@link 1075 * BluetoothProfile#CONNECTION_POLICY_ALLOWED}, {@link 1076 * BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, {@link 1077 * BluetoothProfile#CONNECTION_POLICY_UNKNOWN} 1078 * 1079 * @param device Paired bluetooth device 1080 * @param connectionPolicy is the connection policy to set to for this profile 1081 * @return true if connectionPolicy is set, false on error 1082 */ setConnectionPolicy(BluetoothDevice device, int connectionPolicy)1083 public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) { 1084 Log.d(TAG, "setConnectionPolicy: device=" + device); 1085 1086 if (!mDatabaseManager.setProfileConnectionPolicy( 1087 device, BluetoothProfile.HID_HOST, connectionPolicy)) { 1088 return false; 1089 } 1090 Log.d(TAG, "Saved connectionPolicy=" + connectionPolicy + " for device=" + device); 1091 if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 1092 connect(device); 1093 } else if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { 1094 disconnect(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 1095 MetricsLogger.getInstance() 1096 .count(BluetoothProtoEnums.HIDH_COUNT_CONNECTION_POLICY_DISABLED, 1); 1097 } 1098 return true; 1099 } 1100 1101 /** 1102 * @see BluetoothHidHost#setPreferredTransport 1103 */ setPreferredTransport(BluetoothDevice device, int transport)1104 boolean setPreferredTransport(BluetoothDevice device, int transport) { 1105 Log.i(TAG, "setPreferredTransport: device=" + device + " transport=" + transport); 1106 1107 if (mAdapterService.getBondState(device) != BluetoothDevice.BOND_BONDED) { 1108 Log.w(TAG, "Device " + device + " not bonded"); 1109 return false; 1110 } 1111 1112 ParcelUuid[] uuids = mAdapterService.getRemoteUuids(device); 1113 boolean hidSupported = Utils.arrayContains(uuids, BluetoothUuid.HID); 1114 boolean hogpSupported = Utils.arrayContains(uuids, BluetoothUuid.HOGP); 1115 boolean headtrackerSupported = 1116 Flags.androidHeadtrackerService() 1117 && Utils.arrayContains(uuids, HidHostService.ANDROID_HEADTRACKER_UUID); 1118 if (transport == BluetoothDevice.TRANSPORT_BREDR && !hidSupported) { 1119 Log.w(TAG, "device " + device + " does not support HID"); 1120 return false; 1121 } else if (transport == BluetoothDevice.TRANSPORT_LE 1122 && !(hogpSupported || headtrackerSupported)) { 1123 Log.w(TAG, "device " + device + " does not support HOGP"); 1124 return false; 1125 } 1126 1127 Message msg = mHandler.obtainMessage(MESSAGE_SET_PREFERRED_TRANSPORT, device); 1128 msg.arg1 = transport; 1129 mHandler.sendMessage(msg); 1130 1131 return true; 1132 } 1133 1134 /** 1135 * Get the connection policy of the profile. 1136 * 1137 * <p>The connection policy can be any of: {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, 1138 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, {@link 1139 * BluetoothProfile#CONNECTION_POLICY_UNKNOWN} 1140 * 1141 * @param device Bluetooth device 1142 * @return connection policy of the device 1143 */ getConnectionPolicy(BluetoothDevice device)1144 public int getConnectionPolicy(BluetoothDevice device) { 1145 Log.d(TAG, "getConnectionPolicy: device=" + device); 1146 return mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.HID_HOST); 1147 } 1148 1149 /** 1150 * @see BluetoothHidHost#getPreferredTransport 1151 */ getPreferredTransport(BluetoothDevice device)1152 int getPreferredTransport(BluetoothDevice device) { 1153 Log.d(TAG, "getPreferredTransport: device=" + device); 1154 1155 // TODO: Access to mInputDevices should be protected in binder thread 1156 return getTransport(device); 1157 } 1158 1159 /* The following APIs regarding test app for compliance */ getProtocolMode(BluetoothDevice device)1160 boolean getProtocolMode(BluetoothDevice device) { 1161 Log.d(TAG, "getProtocolMode: device=" + device); 1162 int state = this.getConnectionState(device); 1163 if (state != BluetoothProfile.STATE_CONNECTED) { 1164 return false; 1165 } 1166 Message msg = mHandler.obtainMessage(MESSAGE_GET_PROTOCOL_MODE, device); 1167 mHandler.sendMessage(msg); 1168 return true; 1169 } 1170 virtualUnplug(BluetoothDevice device)1171 boolean virtualUnplug(BluetoothDevice device) { 1172 Log.d(TAG, "virtualUnplug: device=" + device); 1173 int state = this.getConnectionState(device); 1174 if (state != BluetoothProfile.STATE_CONNECTED) { 1175 return false; 1176 } 1177 Message msg = mHandler.obtainMessage(MESSAGE_VIRTUAL_UNPLUG, device); 1178 mHandler.sendMessage(msg); 1179 return true; 1180 } 1181 setProtocolMode(BluetoothDevice device, int protocolMode)1182 boolean setProtocolMode(BluetoothDevice device, int protocolMode) { 1183 Log.d(TAG, "setProtocolMode: device=" + device); 1184 int state = this.getConnectionState(device); 1185 if (state != BluetoothProfile.STATE_CONNECTED) { 1186 return false; 1187 } 1188 Message msg = mHandler.obtainMessage(MESSAGE_SET_PROTOCOL_MODE); 1189 msg.obj = device; 1190 msg.arg1 = protocolMode; 1191 mHandler.sendMessage(msg); 1192 return true; 1193 } 1194 getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize)1195 boolean getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize) { 1196 Log.d(TAG, "getReport: device=" + device); 1197 int state = this.getConnectionState(device); 1198 if (state != BluetoothProfile.STATE_CONNECTED) { 1199 return false; 1200 } 1201 Message msg = mHandler.obtainMessage(MESSAGE_GET_REPORT); 1202 msg.obj = device; 1203 Bundle data = new Bundle(); 1204 data.putByte(BluetoothHidHost.EXTRA_REPORT_TYPE, reportType); 1205 data.putByte(BluetoothHidHost.EXTRA_REPORT_ID, reportId); 1206 data.putInt(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE, bufferSize); 1207 msg.setData(data); 1208 mHandler.sendMessage(msg); 1209 return true; 1210 } 1211 setReport(BluetoothDevice device, byte reportType, String report)1212 boolean setReport(BluetoothDevice device, byte reportType, String report) { 1213 Log.d(TAG, "setReport: device=" + device); 1214 int state = this.getConnectionState(device); 1215 if (state != BluetoothProfile.STATE_CONNECTED) { 1216 return false; 1217 } 1218 Message msg = mHandler.obtainMessage(MESSAGE_SET_REPORT); 1219 msg.obj = device; 1220 Bundle data = new Bundle(); 1221 data.putByte(BluetoothHidHost.EXTRA_REPORT_TYPE, reportType); 1222 data.putString(BluetoothHidHost.EXTRA_REPORT, report); 1223 msg.setData(data); 1224 mHandler.sendMessage(msg); 1225 return true; 1226 } 1227 sendData(BluetoothDevice device, String report)1228 boolean sendData(BluetoothDevice device, String report) { 1229 Log.d(TAG, "sendData: device=" + device); 1230 int state = this.getConnectionState(device); 1231 if (state != BluetoothProfile.STATE_CONNECTED) { 1232 return false; 1233 } 1234 1235 if (!Flags.allowSwitchingHidAndHogp()) { 1236 return mNativeInterface.sendData( 1237 getByteAddress(device), getAddressType(device), getTransport(device), report); 1238 } 1239 1240 Message msg = mHandler.obtainMessage(MESSAGE_SEND_DATA, device); 1241 Bundle data = new Bundle(); 1242 data.putString(BluetoothHidHost.EXTRA_REPORT, report); 1243 msg.setData(data); 1244 mHandler.sendMessage(msg); 1245 return true; 1246 } 1247 getIdleTime(BluetoothDevice device)1248 boolean getIdleTime(BluetoothDevice device) { 1249 Log.d(TAG, "getIdleTime: device=" + device); 1250 int state = this.getConnectionState(device); 1251 if (state != BluetoothProfile.STATE_CONNECTED) { 1252 return false; 1253 } 1254 Message msg = mHandler.obtainMessage(MESSAGE_GET_IDLE_TIME, device); 1255 mHandler.sendMessage(msg); 1256 return true; 1257 } 1258 setIdleTime(BluetoothDevice device, byte idleTime)1259 boolean setIdleTime(BluetoothDevice device, byte idleTime) { 1260 Log.d(TAG, "setIdleTime: device=" + device); 1261 int state = this.getConnectionState(device); 1262 if (state != BluetoothProfile.STATE_CONNECTED) { 1263 return false; 1264 } 1265 Message msg = mHandler.obtainMessage(MESSAGE_SET_IDLE_TIME); 1266 msg.obj = device; 1267 Bundle data = new Bundle(); 1268 data.putByte(BluetoothHidHost.EXTRA_IDLE_TIME, idleTime); 1269 msg.setData(data); 1270 mHandler.sendMessage(msg); 1271 return true; 1272 } 1273 onGetProtocolMode(byte[] address, int addressType, int transport, int mode)1274 void onGetProtocolMode(byte[] address, int addressType, int transport, int mode) { 1275 Log.d(TAG, "onGetProtocolMode()"); 1276 Message msg = mHandler.obtainMessage(MESSAGE_ON_GET_PROTOCOL_MODE); 1277 msg.obj = address; 1278 msg.arg1 = transport; 1279 msg.arg2 = mode; 1280 mHandler.sendMessage(msg); 1281 } 1282 onGetIdleTime(byte[] address, int addressType, int transport, int idleTime)1283 void onGetIdleTime(byte[] address, int addressType, int transport, int idleTime) { 1284 Log.d(TAG, "onGetIdleTime()"); 1285 Message msg = mHandler.obtainMessage(MESSAGE_ON_GET_IDLE_TIME); 1286 msg.obj = address; 1287 msg.arg1 = transport; 1288 msg.arg2 = idleTime; 1289 mHandler.sendMessage(msg); 1290 } 1291 onGetReport(byte[] address, int addressType, int transport, byte[] report, int rptSize)1292 void onGetReport(byte[] address, int addressType, int transport, byte[] report, int rptSize) { 1293 Log.d(TAG, "onGetReport()"); 1294 Message msg = mHandler.obtainMessage(MESSAGE_ON_GET_REPORT); 1295 msg.obj = address; 1296 msg.arg1 = transport; 1297 Bundle data = new Bundle(); 1298 data.putByteArray(BluetoothHidHost.EXTRA_REPORT, report); 1299 data.putInt(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE, rptSize); 1300 msg.setData(data); 1301 mHandler.sendMessage(msg); 1302 } 1303 onHandshake(byte[] address, int addressType, int transport, int status)1304 void onHandshake(byte[] address, int addressType, int transport, int status) { 1305 Log.d(TAG, "onHandshake: status=" + status); 1306 Message msg = mHandler.obtainMessage(MESSAGE_ON_HANDSHAKE); 1307 msg.obj = address; 1308 msg.arg1 = transport; 1309 msg.arg2 = status; 1310 mHandler.sendMessage(msg); 1311 } 1312 onVirtualUnplug(byte[] address, int addressType, int transport, int status)1313 void onVirtualUnplug(byte[] address, int addressType, int transport, int status) { 1314 Log.d(TAG, "onVirtualUnplug: status=" + status); 1315 Message msg = mHandler.obtainMessage(MESSAGE_ON_VIRTUAL_UNPLUG); 1316 msg.obj = address; 1317 msg.arg1 = transport; 1318 msg.arg2 = status; 1319 mHandler.sendMessage(msg); 1320 } 1321 onConnectStateChanged(byte[] address, int addressType, int transport, int state)1322 void onConnectStateChanged(byte[] address, int addressType, int transport, int state) { 1323 Log.d(TAG, "onConnectStateChanged: state=" + state); 1324 Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED, address); 1325 msg.arg1 = transport; 1326 msg.arg2 = state; 1327 mHandler.sendMessage(msg); 1328 } 1329 1330 /** 1331 * Saves new connection state. Broadcasts any change from previous state 1332 * 1333 * @param device remote device 1334 * @param transport transport 1335 * @param newState new connection state 1336 */ updateConnectionState(BluetoothDevice device, int transport, int newState)1337 private void updateConnectionState(BluetoothDevice device, int transport, int newState) { 1338 InputDevice inputDevice = mInputDevices.get(device); 1339 int prevState = BluetoothProfile.STATE_DISCONNECTED; 1340 1341 if (Flags.allowSwitchingHidAndHogp()) { 1342 if (inputDevice == null) { 1343 Log.w( 1344 TAG, 1345 "updateConnectionState: requested on unknown inputDevice" 1346 + (" device=" + device) 1347 + (" newState=" + newState) 1348 + (" transport=" + transport)); 1349 return; 1350 } 1351 1352 if (transport == BluetoothDevice.TRANSPORT_AUTO) { 1353 Log.w( 1354 TAG, 1355 "updateConnectionState: requested with AUTO transport" 1356 + (" device=" + device) 1357 + (" newState=" + newState)); 1358 return; 1359 } 1360 1361 prevState = inputDevice.getState(transport); 1362 inputDevice.setState(transport, newState); 1363 } else { 1364 if (inputDevice == null) { 1365 inputDevice = getOrCreateInputDevice(device); 1366 } 1367 prevState = inputDevice.getState(); 1368 setTransport(device, transport); 1369 inputDevice.setState(newState); 1370 } 1371 1372 if (prevState == newState) { 1373 Log.d( 1374 TAG, 1375 "updateConnectionState: No state change for" 1376 + (" device=" + device) 1377 + (" newState=" + newState) 1378 + (" transport=" + transport)); 1379 return; 1380 } 1381 1382 if (newState == BluetoothProfile.STATE_CONNECTED) { 1383 MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.HID_HOST); 1384 } 1385 1386 mInputDevices.put(device, inputDevice); 1387 1388 broadcastConnectionState(device, transport, prevState, newState); 1389 } 1390 1391 // This method does not check for error condition (newState == prevState) broadcastConnectionState( BluetoothDevice device, int transport, int prevState, int newState)1392 private void broadcastConnectionState( 1393 BluetoothDevice device, int transport, int prevState, int newState) { 1394 // Notifying the connection state change of the profile before sending the intent for 1395 // connection state change, as it was causing a race condition, with the UI not being 1396 // updated with the correct connection state. 1397 Log.i( 1398 TAG, 1399 "broadcastConnectionState:" 1400 + (" device= " + device) 1401 + (" transport= " + transport) 1402 + (" prevState=" + prevState + " -> newState=" + newState)); 1403 1404 mAdapterService.updateProfileConnectionAdapterProperties( 1405 device, BluetoothProfile.HID_HOST, newState, prevState); 1406 1407 Intent intent = new Intent(BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED); 1408 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 1409 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 1410 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1411 if (Flags.allowSwitchingHidAndHogp()) { 1412 intent.putExtra(BluetoothDevice.EXTRA_TRANSPORT, transport); 1413 } 1414 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 1415 sendBroadcastAsUser( 1416 intent, 1417 UserHandle.ALL, 1418 BLUETOOTH_CONNECT, 1419 Utils.getTempBroadcastOptions().toBundle()); 1420 } 1421 broadcastHandshake(BluetoothDevice device, int status)1422 private void broadcastHandshake(BluetoothDevice device, int status) { 1423 Intent intent = new Intent(BluetoothHidHost.ACTION_HANDSHAKE); 1424 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1425 intent.putExtra(BluetoothHidHost.EXTRA_STATUS, status); 1426 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 1427 sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle()); 1428 } 1429 broadcastProtocolMode(BluetoothDevice device, int protocolMode)1430 private void broadcastProtocolMode(BluetoothDevice device, int protocolMode) { 1431 Intent intent = new Intent(BluetoothHidHost.ACTION_PROTOCOL_MODE_CHANGED); 1432 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1433 intent.putExtra(BluetoothHidHost.EXTRA_PROTOCOL_MODE, protocolMode); 1434 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 1435 sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle()); 1436 Log.d(TAG, "broadcastProtocolMode: device=" + device + " protocolMode=" + protocolMode); 1437 } 1438 broadcastReport(BluetoothDevice device, byte[] report, int rptSize)1439 private void broadcastReport(BluetoothDevice device, byte[] report, int rptSize) { 1440 Intent intent = new Intent(BluetoothHidHost.ACTION_REPORT); 1441 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1442 intent.putExtra(BluetoothHidHost.EXTRA_REPORT, report); 1443 intent.putExtra(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE, rptSize); 1444 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 1445 sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle()); 1446 } 1447 broadcastVirtualUnplugStatus(BluetoothDevice device, int status)1448 private void broadcastVirtualUnplugStatus(BluetoothDevice device, int status) { 1449 Intent intent = new Intent(BluetoothHidHost.ACTION_VIRTUAL_UNPLUG_STATUS); 1450 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1451 intent.putExtra(BluetoothHidHost.EXTRA_VIRTUAL_UNPLUG_STATUS, status); 1452 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 1453 sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle()); 1454 } 1455 broadcastIdleTime(BluetoothDevice device, int idleTime)1456 private void broadcastIdleTime(BluetoothDevice device, int idleTime) { 1457 Intent intent = new Intent(BluetoothHidHost.ACTION_IDLE_TIME_CHANGED); 1458 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1459 intent.putExtra(BluetoothHidHost.EXTRA_IDLE_TIME, idleTime); 1460 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 1461 sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle()); 1462 Log.d(TAG, "broadcastIdleTime: device=" + device + " idleTime=" + idleTime); 1463 } 1464 1465 /** 1466 * Check whether can connect to a peer device. The check considers a number of factors during 1467 * the evaluation. 1468 * 1469 * @param device the peer device to connect to 1470 * @return true if connection is allowed, otherwise false 1471 */ 1472 @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) okToConnect(BluetoothDevice device)1473 public boolean okToConnect(BluetoothDevice device) { 1474 // Check if this is an incoming connection in Quiet mode. 1475 if (mAdapterService.isQuietModeEnabled() && mTargetDevice == null) { 1476 Log.w(TAG, "okToConnect: return false because of quiet mode enabled. device=" + device); 1477 return false; 1478 } 1479 // Check connection policy and accept or reject the connection. 1480 int connectionPolicy = getConnectionPolicy(device); 1481 int bondState = mAdapterService.getBondState(device); 1482 // Allow this connection only if the device is bonded. Any attempt to connect while 1483 // bonding would potentially lead to an unauthorized connection. 1484 if (bondState != BluetoothDevice.BOND_BONDED) { 1485 Log.w(TAG, "okToConnect: return false, device=" + device + " bondState=" + bondState); 1486 return false; 1487 } else if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_UNKNOWN 1488 && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 1489 // Otherwise, reject the connection if connectionPolicy is not valid. 1490 Log.w( 1491 TAG, 1492 "okToConnect: return false," 1493 + (" device=" + device) 1494 + (" connectionPolicy=" + connectionPolicy)); 1495 return false; 1496 } 1497 return true; 1498 } 1499 1500 @Override dump(StringBuilder sb)1501 public void dump(StringBuilder sb) { 1502 super.dump(sb); 1503 println(sb, "mTargetDevice: " + mTargetDevice); 1504 println(sb, "mInputDevices:"); 1505 mInputDevices.forEach( 1506 (k, v) -> sb.append(" " + k.getAddressForLogging() + " : " + v + "\n")); 1507 } 1508 } 1509