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 android.annotation.RequiresPermission; 24 import android.bluetooth.BluetoothDevice; 25 import android.bluetooth.BluetoothHidHost; 26 import android.bluetooth.BluetoothProfile; 27 import android.bluetooth.IBluetoothHidHost; 28 import android.content.AttributionSource; 29 import android.content.Intent; 30 import android.os.Bundle; 31 import android.os.Handler; 32 import android.os.Message; 33 import android.os.UserHandle; 34 import android.sysprop.BluetoothProperties; 35 import android.util.Log; 36 37 import androidx.annotation.VisibleForTesting; 38 39 import com.android.bluetooth.BluetoothMetricsProto; 40 import com.android.bluetooth.Utils; 41 import com.android.bluetooth.btservice.AdapterService; 42 import com.android.bluetooth.btservice.MetricsLogger; 43 import com.android.bluetooth.btservice.ProfileService; 44 import com.android.bluetooth.btservice.storage.DatabaseManager; 45 import com.android.modules.utils.SynchronousResultReceiver; 46 47 import java.util.ArrayList; 48 import java.util.Collections; 49 import java.util.HashMap; 50 import java.util.List; 51 import java.util.Map; 52 import java.util.Objects; 53 54 /** 55 * Provides Bluetooth Hid Host profile, as a service in 56 * the Bluetooth application. 57 * @hide 58 */ 59 public class HidHostService extends ProfileService { 60 private static final boolean DBG = false; 61 private static final String TAG = "BluetoothHidHostService"; 62 63 private Map<BluetoothDevice, Integer> mInputDevices; 64 private boolean mNativeAvailable; 65 private static HidHostService sHidHostService; 66 private BluetoothDevice mTargetDevice = null; 67 68 private DatabaseManager mDatabaseManager; 69 private AdapterService mAdapterService; 70 71 private static final int MESSAGE_CONNECT = 1; 72 private static final int MESSAGE_DISCONNECT = 2; 73 private static final int MESSAGE_CONNECT_STATE_CHANGED = 3; 74 private static final int MESSAGE_GET_PROTOCOL_MODE = 4; 75 private static final int MESSAGE_VIRTUAL_UNPLUG = 5; 76 private static final int MESSAGE_ON_GET_PROTOCOL_MODE = 6; 77 private static final int MESSAGE_SET_PROTOCOL_MODE = 7; 78 private static final int MESSAGE_GET_REPORT = 8; 79 private static final int MESSAGE_ON_GET_REPORT = 9; 80 private static final int MESSAGE_SET_REPORT = 10; 81 private static final int MESSAGE_ON_VIRTUAL_UNPLUG = 12; 82 private static final int MESSAGE_ON_HANDSHAKE = 13; 83 private static final int MESSAGE_GET_IDLE_TIME = 14; 84 private static final int MESSAGE_ON_GET_IDLE_TIME = 15; 85 private static final int MESSAGE_SET_IDLE_TIME = 16; 86 87 static { classInitNative()88 classInitNative(); 89 } 90 isEnabled()91 public static boolean isEnabled() { 92 return BluetoothProperties.isProfileHidHostEnabled().orElse(false); 93 } 94 95 @Override initBinder()96 public IProfileServiceBinder initBinder() { 97 return new BluetoothHidHostBinder(this); 98 } 99 100 @Override start()101 protected boolean start() { 102 mDatabaseManager = Objects.requireNonNull(AdapterService.getAdapterService().getDatabase(), 103 "DatabaseManager cannot be null when HidHostService starts"); 104 mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(), 105 "AdapterService cannot be null when HidHostService starts"); 106 107 mInputDevices = Collections.synchronizedMap(new HashMap<BluetoothDevice, Integer>()); 108 initializeNative(); 109 mNativeAvailable = true; 110 setHidHostService(this); 111 return true; 112 } 113 114 @Override stop()115 protected boolean stop() { 116 if (DBG) { 117 Log.d(TAG, "Stopping Bluetooth HidHostService"); 118 } 119 return true; 120 } 121 122 @Override cleanup()123 protected void cleanup() { 124 if (DBG) Log.d(TAG, "Stopping Bluetooth HidHostService"); 125 if (mNativeAvailable) { 126 cleanupNative(); 127 mNativeAvailable = false; 128 } 129 130 if (mInputDevices != null) { 131 for (BluetoothDevice device : mInputDevices.keySet()) { 132 int inputDeviceState = getConnectionState(device); 133 if (inputDeviceState != BluetoothProfile.STATE_DISCONNECTED) { 134 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED); 135 } 136 } 137 mInputDevices.clear(); 138 } 139 // TODO(b/72948646): this should be moved to stop() 140 setHidHostService(null); 141 } 142 getDevice(byte[] address)143 private BluetoothDevice getDevice(byte[] address) { 144 return mAdapterService.getDeviceFromByte(address); 145 } 146 getByteAddress(BluetoothDevice device)147 private byte[] getByteAddress(BluetoothDevice device) { 148 return mAdapterService.getByteIdentityAddress(device); 149 } 150 getHidHostService()151 public static synchronized HidHostService getHidHostService() { 152 if (sHidHostService == null) { 153 Log.w(TAG, "getHidHostService(): service is null"); 154 return null; 155 } 156 if (!sHidHostService.isAvailable()) { 157 Log.w(TAG, "getHidHostService(): service is not available "); 158 return null; 159 } 160 return sHidHostService; 161 } 162 setHidHostService(HidHostService instance)163 private static synchronized void setHidHostService(HidHostService instance) { 164 if (DBG) { 165 Log.d(TAG, "setHidHostService(): set to: " + instance); 166 } 167 sHidHostService = instance; 168 } 169 170 private final Handler mHandler = new Handler() { 171 172 @Override 173 public void handleMessage(Message msg) { 174 if (DBG) Log.v(TAG, "handleMessage(): msg.what=" + msg.what); 175 176 switch (msg.what) { 177 case MESSAGE_CONNECT: { 178 BluetoothDevice device = (BluetoothDevice) msg.obj; 179 if (!connectHidNative(getByteAddress(device))) { 180 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING); 181 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED); 182 break; 183 } 184 mTargetDevice = device; 185 } 186 break; 187 case MESSAGE_DISCONNECT: { 188 BluetoothDevice device = (BluetoothDevice) msg.obj; 189 if (!disconnectHidNative(getByteAddress(device))) { 190 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING); 191 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED); 192 break; 193 } 194 } 195 break; 196 case MESSAGE_CONNECT_STATE_CHANGED: { 197 BluetoothDevice device = mAdapterService.getDeviceFromByte((byte[]) msg.obj); 198 int halState = msg.arg1; 199 Integer prevStateInteger = mInputDevices.get(device); 200 int prevState = 201 (prevStateInteger == null) ? BluetoothHidHost.STATE_DISCONNECTED 202 : prevStateInteger; 203 if (DBG) { 204 Log.d(TAG, "MESSAGE_CONNECT_STATE_CHANGED newState:" + convertHalState( 205 halState) + ", prevState:" + prevState); 206 } 207 if (halState == CONN_STATE_CONNECTED 208 && prevState == BluetoothHidHost.STATE_DISCONNECTED 209 && (!okToConnect(device))) { 210 if (DBG) { 211 Log.d(TAG, "Incoming HID connection rejected"); 212 } 213 virtualUnPlugNative(getByteAddress(device)); 214 } else { 215 broadcastConnectionState(device, convertHalState(halState)); 216 } 217 if (halState == CONN_STATE_CONNECTED && (mTargetDevice != null 218 && mTargetDevice.equals(device))) { 219 mTargetDevice = null; 220 // local device originated connection to hid device, move out 221 // of quiet mode 222 AdapterService adapterService = AdapterService.getAdapterService(); 223 adapterService.enable(false); 224 } 225 } 226 break; 227 case MESSAGE_GET_PROTOCOL_MODE: { 228 BluetoothDevice device = (BluetoothDevice) msg.obj; 229 if (!getProtocolModeNative(getByteAddress(device))) { 230 Log.e(TAG, "Error: get protocol mode native returns false"); 231 } 232 } 233 break; 234 235 case MESSAGE_ON_GET_PROTOCOL_MODE: { 236 BluetoothDevice device = mAdapterService.getDeviceFromByte((byte[]) msg.obj); 237 int protocolMode = msg.arg1; 238 broadcastProtocolMode(device, protocolMode); 239 } 240 break; 241 case MESSAGE_VIRTUAL_UNPLUG: { 242 BluetoothDevice device = (BluetoothDevice) msg.obj; 243 if (!virtualUnPlugNative(getByteAddress(device))) { 244 Log.e(TAG, "Error: virtual unplug native returns false"); 245 } 246 } 247 break; 248 case MESSAGE_SET_PROTOCOL_MODE: { 249 BluetoothDevice device = (BluetoothDevice) msg.obj; 250 byte protocolMode = (byte) msg.arg1; 251 Log.d(TAG, "sending set protocol mode(" + protocolMode + ")"); 252 if (!setProtocolModeNative(getByteAddress(device), protocolMode)) { 253 Log.e(TAG, "Error: set protocol mode native returns false"); 254 } 255 } 256 break; 257 case MESSAGE_GET_REPORT: { 258 BluetoothDevice device = (BluetoothDevice) msg.obj; 259 Bundle data = msg.getData(); 260 byte reportType = data.getByte(BluetoothHidHost.EXTRA_REPORT_TYPE); 261 byte reportId = data.getByte(BluetoothHidHost.EXTRA_REPORT_ID); 262 int bufferSize = data.getInt(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE); 263 if (!getReportNative(getByteAddress(device), reportType, reportId, 264 bufferSize)) { 265 Log.e(TAG, "Error: get report native returns false"); 266 } 267 } 268 break; 269 case MESSAGE_ON_GET_REPORT: { 270 BluetoothDevice device = mAdapterService.getDeviceFromByte((byte[]) msg.obj); 271 Bundle data = msg.getData(); 272 byte[] report = data.getByteArray(BluetoothHidHost.EXTRA_REPORT); 273 int bufferSize = data.getInt(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE); 274 broadcastReport(device, report, bufferSize); 275 } 276 break; 277 case MESSAGE_ON_HANDSHAKE: { 278 BluetoothDevice device = mAdapterService.getDeviceFromByte((byte[]) msg.obj); 279 int status = msg.arg1; 280 broadcastHandshake(device, status); 281 } 282 break; 283 case MESSAGE_SET_REPORT: { 284 BluetoothDevice device = (BluetoothDevice) msg.obj; 285 Bundle data = msg.getData(); 286 byte reportType = data.getByte(BluetoothHidHost.EXTRA_REPORT_TYPE); 287 String report = data.getString(BluetoothHidHost.EXTRA_REPORT); 288 if (!setReportNative(getByteAddress(device), reportType, report)) { 289 Log.e(TAG, "Error: set report native returns false"); 290 } 291 } 292 break; 293 case MESSAGE_ON_VIRTUAL_UNPLUG: { 294 BluetoothDevice device = mAdapterService.getDeviceFromByte((byte[]) msg.obj); 295 int status = msg.arg1; 296 broadcastVirtualUnplugStatus(device, status); 297 } 298 break; 299 case MESSAGE_GET_IDLE_TIME: { 300 BluetoothDevice device = (BluetoothDevice) msg.obj; 301 if (!getIdleTimeNative(getByteAddress(device))) { 302 Log.e(TAG, "Error: get idle time native returns false"); 303 } 304 } 305 break; 306 case MESSAGE_ON_GET_IDLE_TIME: { 307 BluetoothDevice device = mAdapterService.getDeviceFromByte((byte[]) msg.obj); 308 int idleTime = msg.arg1; 309 broadcastIdleTime(device, idleTime); 310 } 311 break; 312 case MESSAGE_SET_IDLE_TIME: { 313 BluetoothDevice device = (BluetoothDevice) msg.obj; 314 Bundle data = msg.getData(); 315 byte idleTime = data.getByte(BluetoothHidHost.EXTRA_IDLE_TIME); 316 if (!setIdleTimeNative(getByteAddress(device), idleTime)) { 317 Log.e(TAG, "Error: get idle time native returns false"); 318 } 319 } 320 break; 321 } 322 } 323 }; 324 325 /** 326 * Handlers for incoming service calls 327 */ 328 @VisibleForTesting 329 static class BluetoothHidHostBinder extends IBluetoothHidHost.Stub 330 implements IProfileServiceBinder { 331 private HidHostService mService; 332 BluetoothHidHostBinder(HidHostService svc)333 BluetoothHidHostBinder(HidHostService svc) { 334 mService = svc; 335 } 336 337 @Override cleanup()338 public void cleanup() { 339 mService = null; 340 } 341 342 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getService(AttributionSource source)343 private HidHostService getService(AttributionSource source) { 344 if (Utils.isInstrumentationTestMode()) { 345 return mService; 346 } 347 if (!Utils.checkServiceAvailable(mService, TAG) 348 || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG) 349 || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) { 350 return null; 351 } 352 return mService; 353 } 354 355 @Override connect(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)356 public void connect(BluetoothDevice device, AttributionSource source, 357 SynchronousResultReceiver receiver) { 358 try { 359 HidHostService service = getService(source); 360 boolean defaultValue = false; 361 if (service != null) { 362 enforceBluetoothPrivilegedPermission(service); 363 defaultValue = service.connect(device); 364 } 365 receiver.send(defaultValue); 366 } catch (RuntimeException e) { 367 receiver.propagateException(e); 368 } 369 } 370 371 @Override disconnect(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)372 public void disconnect(BluetoothDevice device, AttributionSource source, 373 SynchronousResultReceiver receiver) { 374 try { 375 HidHostService service = getService(source); 376 boolean defaultValue = false; 377 if (service != null) { 378 enforceBluetoothPrivilegedPermission(service); 379 defaultValue = service.disconnect(device); 380 } 381 receiver.send(defaultValue); 382 } catch (RuntimeException e) { 383 receiver.propagateException(e); 384 } 385 } 386 387 @Override getConnectionState(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)388 public void getConnectionState(BluetoothDevice device, AttributionSource source, 389 SynchronousResultReceiver receiver) { 390 try { 391 HidHostService service = getService(source); 392 int defaultValue = BluetoothHidHost.STATE_DISCONNECTED; 393 if (service != null) { 394 defaultValue = service.getConnectionState(device); 395 } 396 receiver.send(defaultValue); 397 } catch (RuntimeException e) { 398 receiver.propagateException(e); 399 } 400 } 401 402 @Override getConnectedDevices(AttributionSource source, SynchronousResultReceiver receiver)403 public void getConnectedDevices(AttributionSource source, 404 SynchronousResultReceiver receiver) { 405 getDevicesMatchingConnectionStates(new int[] { BluetoothProfile.STATE_CONNECTED }, 406 source, receiver); 407 } 408 409 @Override getDevicesMatchingConnectionStates(int[] states, AttributionSource source, SynchronousResultReceiver receiver)410 public void getDevicesMatchingConnectionStates(int[] states, 411 AttributionSource source, SynchronousResultReceiver receiver) { 412 try { 413 HidHostService service = getService(source); 414 List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(0); 415 if (service != null) { 416 defaultValue = service.getDevicesMatchingConnectionStates(states); 417 } 418 receiver.send(defaultValue); 419 } catch (RuntimeException e) { 420 receiver.propagateException(e); 421 } 422 } 423 424 @Override setConnectionPolicy(BluetoothDevice device, int connectionPolicy, AttributionSource source, SynchronousResultReceiver receiver)425 public void setConnectionPolicy(BluetoothDevice device, int connectionPolicy, 426 AttributionSource source, SynchronousResultReceiver receiver) { 427 try { 428 HidHostService service = getService(source); 429 boolean defaultValue = false; 430 if (service != null) { 431 enforceBluetoothPrivilegedPermission(service); 432 defaultValue = service.setConnectionPolicy(device, connectionPolicy); 433 } 434 receiver.send(defaultValue); 435 } catch (RuntimeException e) { 436 receiver.propagateException(e); 437 } 438 } 439 440 @Override getConnectionPolicy(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)441 public void getConnectionPolicy(BluetoothDevice device, AttributionSource source, 442 SynchronousResultReceiver receiver) { 443 try { 444 HidHostService service = getService(source); 445 int defaultValue = BluetoothProfile.CONNECTION_POLICY_UNKNOWN; 446 if (service != null) { 447 enforceBluetoothPrivilegedPermission(service); 448 defaultValue = service.getConnectionPolicy(device); 449 } 450 receiver.send(defaultValue); 451 } catch (RuntimeException e) { 452 receiver.propagateException(e); 453 } 454 } 455 456 /* The following APIs regarding test app for compliance */ 457 @Override getProtocolMode(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)458 public void getProtocolMode(BluetoothDevice device, AttributionSource source, 459 SynchronousResultReceiver receiver) { 460 try { 461 HidHostService service = getService(source); 462 boolean defaultValue = false; 463 if (service != null) { 464 defaultValue = service.getProtocolMode(device); 465 } 466 receiver.send(defaultValue); 467 } catch (RuntimeException e) { 468 receiver.propagateException(e); 469 } 470 } 471 472 @Override virtualUnplug(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)473 public void virtualUnplug(BluetoothDevice device, AttributionSource source, 474 SynchronousResultReceiver receiver) { 475 try { 476 HidHostService service = getService(source); 477 boolean defaultValue = false; 478 if (service != null) { 479 defaultValue = service.virtualUnplug(device); 480 } 481 receiver.send(defaultValue); 482 } catch (RuntimeException e) { 483 receiver.propagateException(e); 484 } 485 } 486 487 @Override setProtocolMode(BluetoothDevice device, int protocolMode, AttributionSource source, SynchronousResultReceiver receiver)488 public void setProtocolMode(BluetoothDevice device, int protocolMode, 489 AttributionSource source, SynchronousResultReceiver receiver) { 490 try { 491 HidHostService service = getService(source); 492 boolean defaultValue = false; 493 if (service != null) { 494 defaultValue = service.setProtocolMode(device, protocolMode); 495 } 496 receiver.send(defaultValue); 497 } catch (RuntimeException e) { 498 receiver.propagateException(e); 499 } 500 } 501 502 @Override getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize, AttributionSource source, SynchronousResultReceiver receiver)503 public void getReport(BluetoothDevice device, byte reportType, byte reportId, 504 int bufferSize, AttributionSource source, SynchronousResultReceiver receiver) { 505 try { 506 HidHostService service = getService(source); 507 boolean defaultValue = false; 508 if (service != null) { 509 defaultValue = service.getReport(device, reportType, reportId, bufferSize); 510 } 511 receiver.send(defaultValue); 512 } catch (RuntimeException e) { 513 receiver.propagateException(e); 514 } 515 } 516 517 @Override setReport(BluetoothDevice device, byte reportType, String report, AttributionSource source, SynchronousResultReceiver receiver)518 public void setReport(BluetoothDevice device, byte reportType, String report, 519 AttributionSource source, SynchronousResultReceiver receiver) { 520 try { 521 HidHostService service = getService(source); 522 boolean defaultValue = false; 523 if (service != null) { 524 defaultValue = service.setReport(device, reportType, report); 525 } 526 receiver.send(defaultValue); 527 } catch (RuntimeException e) { 528 receiver.propagateException(e); 529 } 530 } 531 532 @Override sendData(BluetoothDevice device, String report, AttributionSource source, SynchronousResultReceiver receiver)533 public void sendData(BluetoothDevice device, String report, AttributionSource source, 534 SynchronousResultReceiver receiver) { 535 try { 536 HidHostService service = getService(source); 537 boolean defaultValue = false; 538 if (service != null) { 539 defaultValue = service.sendData(device, report); 540 } 541 receiver.send(defaultValue); 542 } catch (RuntimeException e) { 543 receiver.propagateException(e); 544 } 545 } 546 547 @Override setIdleTime(BluetoothDevice device, byte idleTime, AttributionSource source, SynchronousResultReceiver receiver)548 public void setIdleTime(BluetoothDevice device, byte idleTime, 549 AttributionSource source, SynchronousResultReceiver receiver) { 550 try { 551 HidHostService service = getService(source); 552 boolean defaultValue = false; 553 if (service != null) { 554 defaultValue = service.setIdleTime(device, idleTime); 555 } 556 receiver.send(defaultValue); 557 } catch (RuntimeException e) { 558 receiver.propagateException(e); 559 } 560 } 561 562 @Override getIdleTime(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)563 public void getIdleTime(BluetoothDevice device, AttributionSource source, 564 SynchronousResultReceiver receiver) { 565 try { 566 HidHostService service = getService(source); 567 boolean defaultValue = false; 568 if (service != null) { 569 defaultValue = service.getIdleTime(device); 570 } 571 receiver.send(defaultValue); 572 } catch (RuntimeException e) { 573 receiver.propagateException(e); 574 } 575 } 576 } 577 578 ; 579 580 //APIs 581 582 /** 583 * Connects the hid host profile for the passed in device 584 * 585 * @param device is the device with which to connect the hid host profile 586 * @return true if connection request is passed down to mHandler. 587 */ connect(BluetoothDevice device)588 public boolean connect(BluetoothDevice device) { 589 if (DBG) Log.d(TAG, "connect: " + device.getAddress()); 590 if (getConnectionState(device) != BluetoothHidHost.STATE_DISCONNECTED) { 591 Log.e(TAG, "Hid Device not disconnected: " + device); 592 return false; 593 } 594 if (getConnectionPolicy(device) == BluetoothHidHost.CONNECTION_POLICY_FORBIDDEN) { 595 Log.e(TAG, "Hid Device CONNECTION_POLICY_FORBIDDEN: " + device); 596 return false; 597 } 598 599 Message msg = mHandler.obtainMessage(MESSAGE_CONNECT, device); 600 mHandler.sendMessage(msg); 601 return true; 602 } 603 604 /** 605 * Disconnects the hid host profile from the passed in device 606 * 607 * @param device is the device with which to disconnect the hid host profile 608 * @return true 609 */ disconnect(BluetoothDevice device)610 public boolean disconnect(BluetoothDevice device) { 611 if (DBG) Log.d(TAG, "disconnect: " + device.getAddress()); 612 Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT, device); 613 mHandler.sendMessage(msg); 614 return true; 615 } 616 617 /** 618 * Get the current connection state of the profile 619 * 620 * @param device is the remote bluetooth device 621 * @return {@link BluetoothProfile#STATE_DISCONNECTED} if this profile is disconnected, 622 * {@link BluetoothProfile#STATE_CONNECTING} if this profile is being connected, 623 * {@link BluetoothProfile#STATE_CONNECTED} if this profile is connected, or 624 * {@link BluetoothProfile#STATE_DISCONNECTING} if this profile is being disconnected 625 */ getConnectionState(BluetoothDevice device)626 public int getConnectionState(BluetoothDevice device) { 627 if (DBG) Log.d(TAG, "getConnectionState: " + device.getAddress()); 628 if (mInputDevices.get(device) == null) { 629 return BluetoothHidHost.STATE_DISCONNECTED; 630 } 631 return mInputDevices.get(device); 632 } 633 getDevicesMatchingConnectionStates(int[] states)634 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 635 if (DBG) Log.d(TAG, "getDevicesMatchingConnectionStates()"); 636 List<BluetoothDevice> inputDevices = new ArrayList<BluetoothDevice>(); 637 638 for (BluetoothDevice device : mInputDevices.keySet()) { 639 int inputDeviceState = getConnectionState(device); 640 for (int state : states) { 641 if (state == inputDeviceState) { 642 inputDevices.add(device); 643 break; 644 } 645 } 646 } 647 return inputDevices; 648 } 649 650 /** 651 * Set connection policy of the profile and connects it if connectionPolicy is 652 * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED} or disconnects if connectionPolicy is 653 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN} 654 * 655 * <p> The device should already be paired. 656 * Connection policy can be one of: 657 * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, 658 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, 659 * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN} 660 * 661 * @param device Paired bluetooth device 662 * @param connectionPolicy is the connection policy to set to for this profile 663 * @return true if connectionPolicy is set, false on error 664 */ setConnectionPolicy(BluetoothDevice device, int connectionPolicy)665 public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) { 666 if (DBG) { 667 Log.d(TAG, "setConnectionPolicy: " + device.getAddress()); 668 } 669 670 if (!mDatabaseManager.setProfileConnectionPolicy(device, BluetoothProfile.HID_HOST, 671 connectionPolicy)) { 672 return false; 673 } 674 if (DBG) { 675 Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy); 676 } 677 if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 678 connect(device); 679 } else if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { 680 disconnect(device); 681 } 682 return true; 683 } 684 685 /** 686 * Get the connection policy of the profile. 687 * 688 * <p> The connection policy can be any of: 689 * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, 690 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, 691 * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN} 692 * 693 * @param device Bluetooth device 694 * @return connection policy of the device 695 * @hide 696 */ getConnectionPolicy(BluetoothDevice device)697 public int getConnectionPolicy(BluetoothDevice device) { 698 if (DBG) { 699 Log.d(TAG, "getConnectionPolicy: " + device.getAddress()); 700 } 701 return mDatabaseManager 702 .getProfileConnectionPolicy(device, BluetoothProfile.HID_HOST); 703 } 704 705 /* The following APIs regarding test app for compliance */ getProtocolMode(BluetoothDevice device)706 boolean getProtocolMode(BluetoothDevice device) { 707 if (DBG) { 708 Log.d(TAG, "getProtocolMode: " + device.getAddress()); 709 } 710 int state = this.getConnectionState(device); 711 if (state != BluetoothHidHost.STATE_CONNECTED) { 712 return false; 713 } 714 Message msg = mHandler.obtainMessage(MESSAGE_GET_PROTOCOL_MODE, device); 715 mHandler.sendMessage(msg); 716 return true; 717 } 718 virtualUnplug(BluetoothDevice device)719 boolean virtualUnplug(BluetoothDevice device) { 720 if (DBG) { 721 Log.d(TAG, "virtualUnplug: " + device.getAddress()); 722 } 723 int state = this.getConnectionState(device); 724 if (state != BluetoothHidHost.STATE_CONNECTED) { 725 return false; 726 } 727 Message msg = mHandler.obtainMessage(MESSAGE_VIRTUAL_UNPLUG, device); 728 mHandler.sendMessage(msg); 729 return true; 730 } 731 setProtocolMode(BluetoothDevice device, int protocolMode)732 boolean setProtocolMode(BluetoothDevice device, int protocolMode) { 733 if (DBG) { 734 Log.d(TAG, "setProtocolMode: " + device.getAddress()); 735 } 736 int state = this.getConnectionState(device); 737 if (state != BluetoothHidHost.STATE_CONNECTED) { 738 return false; 739 } 740 Message msg = mHandler.obtainMessage(MESSAGE_SET_PROTOCOL_MODE); 741 msg.obj = device; 742 msg.arg1 = protocolMode; 743 mHandler.sendMessage(msg); 744 return true; 745 } 746 getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize)747 boolean getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize) { 748 if (DBG) { 749 Log.d(TAG, "getReport: " + device.getAddress()); 750 } 751 int state = this.getConnectionState(device); 752 if (state != BluetoothHidHost.STATE_CONNECTED) { 753 return false; 754 } 755 Message msg = mHandler.obtainMessage(MESSAGE_GET_REPORT); 756 msg.obj = device; 757 Bundle data = new Bundle(); 758 data.putByte(BluetoothHidHost.EXTRA_REPORT_TYPE, reportType); 759 data.putByte(BluetoothHidHost.EXTRA_REPORT_ID, reportId); 760 data.putInt(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE, bufferSize); 761 msg.setData(data); 762 mHandler.sendMessage(msg); 763 return true; 764 } 765 setReport(BluetoothDevice device, byte reportType, String report)766 boolean setReport(BluetoothDevice device, byte reportType, String report) { 767 if (DBG) { 768 Log.d(TAG, "setReport: " + device.getAddress()); 769 } 770 int state = this.getConnectionState(device); 771 if (state != BluetoothHidHost.STATE_CONNECTED) { 772 return false; 773 } 774 Message msg = mHandler.obtainMessage(MESSAGE_SET_REPORT); 775 msg.obj = device; 776 Bundle data = new Bundle(); 777 data.putByte(BluetoothHidHost.EXTRA_REPORT_TYPE, reportType); 778 data.putString(BluetoothHidHost.EXTRA_REPORT, report); 779 msg.setData(data); 780 mHandler.sendMessage(msg); 781 return true; 782 783 } 784 sendData(BluetoothDevice device, String report)785 boolean sendData(BluetoothDevice device, String report) { 786 if (DBG) { 787 Log.d(TAG, "sendData: " + device.getAddress()); 788 } 789 int state = this.getConnectionState(device); 790 if (state != BluetoothHidHost.STATE_CONNECTED) { 791 return false; 792 } 793 794 return sendDataNative(getByteAddress(device), report); 795 } 796 getIdleTime(BluetoothDevice device)797 boolean getIdleTime(BluetoothDevice device) { 798 if (DBG) Log.d(TAG, "getIdleTime: " + device.getAddress()); 799 int state = this.getConnectionState(device); 800 if (state != BluetoothHidHost.STATE_CONNECTED) { 801 return false; 802 } 803 Message msg = mHandler.obtainMessage(MESSAGE_GET_IDLE_TIME, device); 804 mHandler.sendMessage(msg); 805 return true; 806 } 807 setIdleTime(BluetoothDevice device, byte idleTime)808 boolean setIdleTime(BluetoothDevice device, byte idleTime) { 809 if (DBG) Log.d(TAG, "setIdleTime: " + device.getAddress()); 810 int state = this.getConnectionState(device); 811 if (state != BluetoothHidHost.STATE_CONNECTED) { 812 return false; 813 } 814 Message msg = mHandler.obtainMessage(MESSAGE_SET_IDLE_TIME); 815 msg.obj = device; 816 Bundle data = new Bundle(); 817 data.putByte(BluetoothHidHost.EXTRA_IDLE_TIME, idleTime); 818 msg.setData(data); 819 mHandler.sendMessage(msg); 820 return true; 821 } 822 onGetProtocolMode(byte[] address, int mode)823 private void onGetProtocolMode(byte[] address, int mode) { 824 if (DBG) Log.d(TAG, "onGetProtocolMode()"); 825 Message msg = mHandler.obtainMessage(MESSAGE_ON_GET_PROTOCOL_MODE); 826 msg.obj = address; 827 msg.arg1 = mode; 828 mHandler.sendMessage(msg); 829 } 830 onGetIdleTime(byte[] address, int idleTime)831 private void onGetIdleTime(byte[] address, int idleTime) { 832 if (DBG) Log.d(TAG, "onGetIdleTime()"); 833 Message msg = mHandler.obtainMessage(MESSAGE_ON_GET_IDLE_TIME); 834 msg.obj = address; 835 msg.arg1 = idleTime; 836 mHandler.sendMessage(msg); 837 } 838 onGetReport(byte[] address, byte[] report, int rptSize)839 private void onGetReport(byte[] address, byte[] report, int rptSize) { 840 if (DBG) Log.d(TAG, "onGetReport()"); 841 Message msg = mHandler.obtainMessage(MESSAGE_ON_GET_REPORT); 842 msg.obj = address; 843 Bundle data = new Bundle(); 844 data.putByteArray(BluetoothHidHost.EXTRA_REPORT, report); 845 data.putInt(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE, rptSize); 846 msg.setData(data); 847 mHandler.sendMessage(msg); 848 } 849 onHandshake(byte[] address, int status)850 private void onHandshake(byte[] address, int status) { 851 if (DBG) Log.d(TAG, "onHandshake: status=" + status); 852 Message msg = mHandler.obtainMessage(MESSAGE_ON_HANDSHAKE); 853 msg.obj = address; 854 msg.arg1 = status; 855 mHandler.sendMessage(msg); 856 } 857 onVirtualUnplug(byte[] address, int status)858 private void onVirtualUnplug(byte[] address, int status) { 859 if (DBG) Log.d(TAG, "onVirtualUnplug: status=" + status); 860 Message msg = mHandler.obtainMessage(MESSAGE_ON_VIRTUAL_UNPLUG); 861 msg.obj = address; 862 msg.arg1 = status; 863 mHandler.sendMessage(msg); 864 } 865 onConnectStateChanged(byte[] address, int state)866 private void onConnectStateChanged(byte[] address, int state) { 867 if (DBG) Log.d(TAG, "onConnectStateChanged: state=" + state); 868 Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED); 869 msg.obj = address; 870 msg.arg1 = state; 871 mHandler.sendMessage(msg); 872 } 873 874 // This method does not check for error conditon (newState == prevState) broadcastConnectionState(BluetoothDevice device, int newState)875 private void broadcastConnectionState(BluetoothDevice device, int newState) { 876 Integer prevStateInteger = mInputDevices.get(device); 877 int prevState = (prevStateInteger == null) ? BluetoothHidHost.STATE_DISCONNECTED 878 : prevStateInteger; 879 if (prevState == newState) { 880 Log.w(TAG, "no state change: " + newState); 881 return; 882 } 883 if (newState == BluetoothProfile.STATE_CONNECTED) { 884 MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.HID_HOST); 885 } 886 mInputDevices.put(device, newState); 887 888 /* Notifying the connection state change of the profile before sending the intent for 889 connection state change, as it was causing a race condition, with the UI not being 890 updated with the correct connection state. */ 891 Log.d(TAG, "Connection state " + device + ": " + prevState + "->" + newState); 892 Intent intent = new Intent(BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED); 893 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 894 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 895 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 896 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 897 sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_CONNECT, 898 Utils.getTempAllowlistBroadcastOptions()); 899 } 900 broadcastHandshake(BluetoothDevice device, int status)901 private void broadcastHandshake(BluetoothDevice device, int status) { 902 Intent intent = new Intent(BluetoothHidHost.ACTION_HANDSHAKE); 903 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 904 intent.putExtra(BluetoothHidHost.EXTRA_STATUS, status); 905 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 906 sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions()); 907 } 908 broadcastProtocolMode(BluetoothDevice device, int protocolMode)909 private void broadcastProtocolMode(BluetoothDevice device, int protocolMode) { 910 Intent intent = new Intent(BluetoothHidHost.ACTION_PROTOCOL_MODE_CHANGED); 911 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 912 intent.putExtra(BluetoothHidHost.EXTRA_PROTOCOL_MODE, protocolMode); 913 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 914 sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions()); 915 if (DBG) { 916 Log.d(TAG, "Protocol Mode (" + device + "): " + protocolMode); 917 } 918 } 919 broadcastReport(BluetoothDevice device, byte[] report, int rptSize)920 private void broadcastReport(BluetoothDevice device, byte[] report, int rptSize) { 921 Intent intent = new Intent(BluetoothHidHost.ACTION_REPORT); 922 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 923 intent.putExtra(BluetoothHidHost.EXTRA_REPORT, report); 924 intent.putExtra(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE, rptSize); 925 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 926 sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions()); 927 } 928 broadcastVirtualUnplugStatus(BluetoothDevice device, int status)929 private void broadcastVirtualUnplugStatus(BluetoothDevice device, int status) { 930 Intent intent = new Intent(BluetoothHidHost.ACTION_VIRTUAL_UNPLUG_STATUS); 931 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 932 intent.putExtra(BluetoothHidHost.EXTRA_VIRTUAL_UNPLUG_STATUS, status); 933 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 934 sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions()); 935 } 936 broadcastIdleTime(BluetoothDevice device, int idleTime)937 private void broadcastIdleTime(BluetoothDevice device, int idleTime) { 938 Intent intent = new Intent(BluetoothHidHost.ACTION_IDLE_TIME_CHANGED); 939 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 940 intent.putExtra(BluetoothHidHost.EXTRA_IDLE_TIME, idleTime); 941 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 942 sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions()); 943 if (DBG) { 944 Log.d(TAG, "Idle time (" + device + "): " + idleTime); 945 } 946 } 947 948 /** 949 * Check whether can connect to a peer device. 950 * The check considers a number of factors during the evaluation. 951 * 952 * @param device the peer device to connect to 953 * @return true if connection is allowed, otherwise false 954 */ 955 @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) okToConnect(BluetoothDevice device)956 public boolean okToConnect(BluetoothDevice device) { 957 AdapterService adapterService = AdapterService.getAdapterService(); 958 // Check if adapter service is null. 959 if (adapterService == null) { 960 Log.w(TAG, "okToConnect: adapter service is null"); 961 return false; 962 } 963 // Check if this is an incoming connection in Quiet mode. 964 if (adapterService.isQuietModeEnabled() && mTargetDevice == null) { 965 Log.w(TAG, "okToConnect: return false as quiet mode enabled"); 966 return false; 967 } 968 // Check connection policy and accept or reject the connection. 969 int connectionPolicy = getConnectionPolicy(device); 970 int bondState = adapterService.getBondState(device); 971 // Allow this connection only if the device is bonded. Any attempt to connect while 972 // bonding would potentially lead to an unauthorized connection. 973 if (bondState != BluetoothDevice.BOND_BONDED) { 974 Log.w(TAG, "okToConnect: return false, bondState=" + bondState); 975 return false; 976 } else if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_UNKNOWN 977 && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 978 // Otherwise, reject the connection if connectionPolicy is not valid. 979 Log.w(TAG, "okToConnect: return false, connectionPolicy=" + connectionPolicy); 980 return false; 981 } 982 return true; 983 } 984 convertHalState(int halState)985 private static int convertHalState(int halState) { 986 switch (halState) { 987 case CONN_STATE_CONNECTED: 988 return BluetoothProfile.STATE_CONNECTED; 989 case CONN_STATE_CONNECTING: 990 return BluetoothProfile.STATE_CONNECTING; 991 case CONN_STATE_DISCONNECTED: 992 return BluetoothProfile.STATE_DISCONNECTED; 993 case CONN_STATE_DISCONNECTING: 994 return BluetoothProfile.STATE_DISCONNECTING; 995 default: 996 Log.e(TAG, "bad hid connection state: " + halState); 997 return BluetoothProfile.STATE_DISCONNECTED; 998 } 999 } 1000 1001 @Override dump(StringBuilder sb)1002 public void dump(StringBuilder sb) { 1003 super.dump(sb); 1004 println(sb, "mTargetDevice: " + mTargetDevice); 1005 println(sb, "mInputDevices:"); 1006 for (BluetoothDevice device : mInputDevices.keySet()) { 1007 println(sb, " " + device + " : " + mInputDevices.get(device)); 1008 } 1009 } 1010 1011 // Constants matching Hal header file bt_hh.h 1012 // bthh_connection_state_t 1013 private static final int CONN_STATE_CONNECTED = 0; 1014 private static final int CONN_STATE_CONNECTING = 1; 1015 private static final int CONN_STATE_DISCONNECTED = 2; 1016 private static final int CONN_STATE_DISCONNECTING = 3; 1017 classInitNative()1018 private static native void classInitNative(); 1019 initializeNative()1020 private native void initializeNative(); 1021 cleanupNative()1022 private native void cleanupNative(); 1023 connectHidNative(byte[] btAddress)1024 private native boolean connectHidNative(byte[] btAddress); 1025 disconnectHidNative(byte[] btAddress)1026 private native boolean disconnectHidNative(byte[] btAddress); 1027 getProtocolModeNative(byte[] btAddress)1028 private native boolean getProtocolModeNative(byte[] btAddress); 1029 virtualUnPlugNative(byte[] btAddress)1030 private native boolean virtualUnPlugNative(byte[] btAddress); 1031 setProtocolModeNative(byte[] btAddress, byte protocolMode)1032 private native boolean setProtocolModeNative(byte[] btAddress, byte protocolMode); 1033 getReportNative(byte[] btAddress, byte reportType, byte reportId, int bufferSize)1034 private native boolean getReportNative(byte[] btAddress, byte reportType, byte reportId, 1035 int bufferSize); 1036 setReportNative(byte[] btAddress, byte reportType, String report)1037 private native boolean setReportNative(byte[] btAddress, byte reportType, String report); 1038 sendDataNative(byte[] btAddress, String report)1039 private native boolean sendDataNative(byte[] btAddress, String report); 1040 setIdleTimeNative(byte[] btAddress, byte idleTime)1041 private native boolean setIdleTimeNative(byte[] btAddress, byte idleTime); 1042 getIdleTimeNative(byte[] btAddress)1043 private native boolean getIdleTimeNative(byte[] btAddress); 1044 } 1045