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