1 /* 2 * Copyright (C) 2021 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 package com.android.car.bluetooth; 17 18 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 19 20 import android.app.ActivityManager; 21 import android.bluetooth.BluetoothDevice; 22 import android.car.ICarBluetoothUserService; 23 import android.car.ICarPerUserService; 24 import android.car.builtin.os.UserManagerHelper; 25 import android.car.builtin.util.Slogf; 26 import android.content.Context; 27 import android.content.pm.PackageManager; 28 import android.os.IBinder; 29 import android.os.RemoteException; 30 import android.util.Log; 31 import android.util.proto.ProtoOutputStream; 32 33 import com.android.car.CarLog; 34 import com.android.car.CarPerUserServiceHelper; 35 import com.android.car.CarServiceBase; 36 import com.android.car.R; 37 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 38 import com.android.car.internal.util.IndentingPrintWriter; 39 import com.android.internal.annotations.GuardedBy; 40 41 import java.util.Objects; 42 43 /** 44 * CarBluetoothService - Maintains the current user's Bluetooth devices and profile connections. 45 * 46 * For each user, creates: 47 * 1) A {@link BluetoothDeviceManager} object, responsible for maintaining a list of 48 * the user's known devices. 49 * 2) A {@link BluetoothProfileInhibitManager} object that will maintain a set of inhibited 50 * profiles for each device, keeping a device from connecting on those profiles. This provides 51 * an interface to request and release inhibits. 52 * 3) A {@link BluetoothDeviceConnectionPolicy} object, representing a default implementation of 53 * a policy based method of determining and requesting times to auto-connect devices. This 54 * default is controllable through a resource overlay if one chooses to implement their own. 55 * 56 * Provides an interface for other programs to request auto connections. 57 */ 58 public class CarBluetoothService implements CarServiceBase { 59 private static final String TAG = CarLog.tagFor(CarBluetoothService.class); 60 private static final boolean DBG = Slogf.isLoggable(TAG, Log.DEBUG); 61 static final String THREAD_NAME = "CarBluetoothService"; 62 private final Context mContext; 63 64 // Each time CarPerUserService connects we need to get new Bluetooth profile proxies and refresh 65 // all our internal objects to use them. When it disconnects we're to assume our proxies are 66 // invalid. This lock protects all our internal objects. 67 private final Object mPerUserLock = new Object(); 68 69 // Default Bluetooth power policy, per user, enabled with an overlay 70 private final boolean mUseDefaultPowerPolicy; 71 @GuardedBy("mPerUserLock") 72 private BluetoothPowerPolicy mBluetoothPowerPolicy; 73 74 // The Bluetooth Device Manager, owns the priority connection list, updated on user 75 // switch 76 private BluetoothDeviceManager mDeviceManager; 77 78 // Profile-Inhibit Manager that will temporarily inhibit connections on profiles, per user 79 @GuardedBy("mPerUserLock") 80 private BluetoothProfileInhibitManager mInhibitManager; 81 82 // Default Bluetooth device connection policy, per user, enabled with an overlay 83 private final boolean mUseDefaultConnectionPolicy; 84 @GuardedBy("mPerUserLock") 85 private BluetoothDeviceConnectionPolicy mBluetoothDeviceConnectionPolicy; 86 87 // Bluetooth Connection Retry Manager, updated on user switch 88 @GuardedBy("mPerUserLock") 89 private BluetoothConnectionRetryManager mConnectionRetryManager; 90 91 // Listen for user switch events from the CarPerUserService 92 @GuardedBy("mPerUserLock") 93 private int mUserId; 94 @GuardedBy("mPerUserLock") 95 private ICarPerUserService mCarPerUserService; 96 @GuardedBy("mPerUserLock") 97 private ICarBluetoothUserService mCarBluetoothUserService; 98 // Whether this service is already released. We should not create new handler thread if service 99 // is already released. 100 @GuardedBy("mPerUserLock") 101 private boolean mReleased = false; 102 private final CarPerUserServiceHelper mUserServiceHelper; 103 private final CarPerUserServiceHelper.ServiceCallback mUserServiceCallback = 104 new CarPerUserServiceHelper.ServiceCallback() { 105 @Override 106 public void onServiceConnected(ICarPerUserService carPerUserService) { 107 if (DBG) { 108 Slogf.d(TAG, "Connected to CarPerUserService"); 109 } 110 synchronized (mPerUserLock) { 111 if (mReleased) { 112 // We create handlerThread in initializeUserLocked. We must make sure we do not 113 // create handler thread after release otherwise the newly created thread might 114 // not be cleaned up properly. 115 return; 116 } 117 118 // Explicitly clear out existing per-user objects since we can't rely on the 119 // onServiceDisconnected and onPreUnbind calls to always be called before this 120 destroyUserLocked(); 121 122 mCarPerUserService = carPerUserService; 123 124 // Create new objects with our new set of profile proxies 125 initializeUserLocked(); 126 } 127 } 128 129 @Override 130 public void onPreUnbind() { 131 if (DBG) { 132 Slogf.d(TAG, "Before Unbinding from CarPerUserService"); 133 } 134 synchronized (mPerUserLock) { 135 destroyUserLocked(); 136 } 137 } 138 139 @Override 140 public void onServiceDisconnected() { 141 if (DBG) { 142 Slogf.d(TAG, "Disconnected from CarPerUserService"); 143 } 144 synchronized (mPerUserLock) { 145 destroyUserLocked(); 146 } 147 } 148 }; 149 150 /** 151 * Create an instance of CarBluetoothService 152 * 153 * @param context - A Context object representing the context you want this service to run 154 * @param userSwitchService - An instance of CarPerUserServiceHelper that we can bind a listener 155 * to in order to receive user switch events 156 */ CarBluetoothService(Context context, CarPerUserServiceHelper userSwitchService)157 public CarBluetoothService(Context context, CarPerUserServiceHelper userSwitchService) { 158 mUserId = UserManagerHelper.USER_NULL; 159 mContext = Objects.requireNonNull(context); 160 mUserServiceHelper = userSwitchService; 161 mUseDefaultConnectionPolicy = mContext.getResources().getBoolean( 162 R.bool.useDefaultBluetoothConnectionPolicy); 163 mUseDefaultPowerPolicy = mContext.getResources().getBoolean( 164 R.bool.useDefaultBluetoothPowerPolicy); 165 } 166 167 /** 168 * Complete all necessary initialization keeping this service from being running. 169 * 170 * Wait for the user service helper to report a user before initializing a user. 171 */ 172 @Override init()173 public void init() { 174 if (DBG) { 175 Slogf.d(TAG, "init()"); 176 } 177 synchronized (mPerUserLock) { 178 mReleased = false; 179 } 180 mUserServiceHelper.registerServiceCallback(mUserServiceCallback); 181 } 182 183 /** 184 * Release all resources required to run this service and stop running. 185 * 186 * Clean up the user context once we've detached from the user service helper, if any. 187 */ 188 @Override release()189 public void release() { 190 if (DBG) { 191 Slogf.d(TAG, "release()"); 192 } 193 mUserServiceHelper.unregisterServiceCallback(mUserServiceCallback); 194 synchronized (mPerUserLock) { 195 destroyUserLocked(); 196 mReleased = true; 197 } 198 } 199 200 /** 201 * Initialize the user context using the current active user. 202 * 203 * Only call this following a known user switch once we've connected to the user service helper. 204 */ 205 @GuardedBy("mPerUserLock") initializeUserLocked()206 private void initializeUserLocked() { 207 if (DBG) { 208 Slogf.d(TAG, "Initializing new user"); 209 } 210 mUserId = ActivityManager.getCurrentUser(); 211 createBluetoothUserServiceLocked(); 212 createBluetoothProfileInhibitManagerLocked(); 213 214 // Determine if we need to begin the default power policy 215 mBluetoothPowerPolicy = null; 216 if (mUseDefaultPowerPolicy) { 217 createBluetoothPowerPolicyLocked(); 218 } 219 createBluetoothConnectionRetryManagerLocked(); 220 221 // Determine if we need to begin the default device connection policy and device manager 222 mDeviceManager = null; 223 mBluetoothDeviceConnectionPolicy = null; 224 if (mUseDefaultConnectionPolicy) { 225 createBluetoothDeviceManagerLocked(); 226 createBluetoothDeviceConnectionPolicyLocked(); 227 } 228 if (DBG) { 229 Slogf.d(TAG, "Switched to user %d", mUserId); 230 } 231 } 232 233 /** 234 * Destroy the current user context, defined by the set of profile proxies, profile device 235 * managers, inhibit manager and the policy. 236 */ 237 @GuardedBy("mPerUserLock") destroyUserLocked()238 private void destroyUserLocked() { 239 if (DBG) { 240 Slogf.d(TAG, "Destroying user %d", mUserId); 241 } 242 destroyBluetoothDeviceConnectionPolicyLocked(); 243 destroyBluetoothConnectionRetryManagerLocked(); 244 destroyBluetoothPowerPolicyLocked(); 245 destroyBluetoothProfileInhibitManagerLocked(); 246 destroyBluetoothDeviceManagerLocked(); 247 destroyBluetoothUserServiceLocked(); 248 mCarPerUserService = null; 249 mUserId = UserManagerHelper.USER_NULL; 250 } 251 252 /** 253 * Sets the Per User Car Bluetooth Service (ICarBluetoothService) from the CarPerUserService 254 * which acts as a top level Service running in the current user context. 255 * Also sets up the connection proxy objects required to communicate with the Bluetooth 256 * Profile Services. 257 */ 258 @GuardedBy("mPerUserLock") createBluetoothUserServiceLocked()259 private void createBluetoothUserServiceLocked() { 260 if (mCarPerUserService != null) { 261 try { 262 mCarBluetoothUserService = mCarPerUserService.getBluetoothUserService(); 263 mCarBluetoothUserService.setupBluetoothConnectionProxies(); 264 } catch (RemoteException e) { 265 Slogf.e(TAG, "Remote Service Exception on ServiceConnection Callback: %s", e); 266 } catch (java.lang.NullPointerException e) { 267 Slogf.e(TAG, "Initialization Failed: %s", e); 268 } 269 } else { 270 if (DBG) { 271 Slogf.d(TAG, 272 "CarPerUserService not connected. Cannot get bluetooth user proxy objects"); 273 } 274 } 275 } 276 277 /** 278 * Close out the Per User Car Bluetooth profile proxy connections and destroys the Car Bluetooth 279 * User Service object. 280 */ 281 @GuardedBy("mPerUserLock") destroyBluetoothUserServiceLocked()282 private void destroyBluetoothUserServiceLocked() { 283 if (mCarBluetoothUserService == null) { 284 return; 285 } 286 try { 287 mCarBluetoothUserService.closeBluetoothConnectionProxies(); 288 } catch (RemoteException e) { 289 Slogf.e(TAG, "Remote Service Exception on ServiceConnection Callback: %s", e); 290 } 291 mCarBluetoothUserService = null; 292 } 293 294 /** 295 * Clears out Profile Device Managers and re-creates them for the current user. 296 */ 297 @GuardedBy("mPerUserLock") createBluetoothDeviceManagerLocked()298 private void createBluetoothDeviceManagerLocked() { 299 if (DBG) { 300 Slogf.d(TAG, "Creating device manager"); 301 } 302 if (mUserId == UserManagerHelper.USER_NULL) { 303 if (DBG) { 304 Slogf.d(TAG, "No foreground user, cannot create profile device managers"); 305 } 306 return; 307 } 308 mDeviceManager = BluetoothDeviceManager.create(mContext); 309 mDeviceManager.start(); 310 } 311 312 /** 313 * Stops and clears the entire set of Profile Device Managers. 314 */ 315 @GuardedBy("mPerUserLock") destroyBluetoothDeviceManagerLocked()316 private void destroyBluetoothDeviceManagerLocked() { 317 if (DBG) { 318 Slogf.d(TAG, "Destroying device manager"); 319 } 320 if (mDeviceManager == null) return; 321 mDeviceManager.stop(); 322 mDeviceManager = null; 323 } 324 325 /** 326 * Creates an instance of a BluetoothProfileInhibitManager under the current user 327 */ 328 @GuardedBy("mPerUserLock") createBluetoothProfileInhibitManagerLocked()329 private void createBluetoothProfileInhibitManagerLocked() { 330 if (DBG) { 331 Slogf.d(TAG, "Creating inhibit manager"); 332 } 333 if (mUserId == UserManagerHelper.USER_NULL) { 334 if (DBG) { 335 Slogf.d(TAG, "No foreground user, cannot create profile inhibit manager"); 336 } 337 return; 338 } 339 mInhibitManager = new BluetoothProfileInhibitManager(mContext, mUserId, 340 mCarBluetoothUserService); 341 mInhibitManager.start(); 342 } 343 344 /** 345 * Destroys the current instance of a BluetoothProfileInhibitManager, if one exists 346 */ 347 @GuardedBy("mPerUserLock") destroyBluetoothProfileInhibitManagerLocked()348 private void destroyBluetoothProfileInhibitManagerLocked() { 349 if (DBG) { 350 Slogf.d(TAG, "Destroying inhibit manager"); 351 } 352 if (mInhibitManager == null) return; 353 mInhibitManager.stop(); 354 mInhibitManager = null; 355 } 356 357 /** 358 * Creates an instance of {@link BluetoothConnectionRetryManager} for the current user. 359 * Clears out any existing manager from previous user. 360 */ 361 @GuardedBy("mPerUserLock") createBluetoothConnectionRetryManagerLocked()362 private void createBluetoothConnectionRetryManagerLocked() { 363 if (DBG) { 364 Slogf.d(TAG, "Creating connection retry manager"); 365 } 366 if (mUserId == UserManagerHelper.USER_NULL) { 367 if (DBG) { 368 Slogf.d(TAG, "No foreground user, cannot create connection retry manager"); 369 } 370 return; 371 } 372 if (mConnectionRetryManager != null) { 373 if (DBG) { 374 Slogf.d(TAG, "Removing existing connection retry manager first"); 375 } 376 destroyBluetoothConnectionRetryManagerLocked(); 377 } 378 mConnectionRetryManager = BluetoothConnectionRetryManager.create(mContext); 379 if (mConnectionRetryManager == null) { 380 if (DBG) { 381 Slogf.d(TAG, "Failed to create connection retry manager"); 382 } 383 return; 384 } 385 mConnectionRetryManager.init(); 386 if (DBG) { 387 Slogf.d(TAG, "Created connection retry manager"); 388 } 389 } 390 391 /** 392 * Releases and clears {@link BluetoothConnectionRetryManager}. 393 */ 394 @GuardedBy("mPerUserLock") destroyBluetoothConnectionRetryManagerLocked()395 private void destroyBluetoothConnectionRetryManagerLocked() { 396 if (DBG) { 397 Slogf.d(TAG, "Destroying connection retry manager"); 398 } 399 if (mConnectionRetryManager == null) return; 400 mConnectionRetryManager.release(); 401 mConnectionRetryManager = null; 402 if (DBG) { 403 Slogf.d(TAG, "Connection retry manager removed"); 404 } 405 } 406 407 /** 408 * Creates an instance of a BluetoothDeviceConnectionPolicy under the current user 409 */ 410 @GuardedBy("mPerUserLock") createBluetoothDeviceConnectionPolicyLocked()411 private void createBluetoothDeviceConnectionPolicyLocked() { 412 if (DBG) { 413 Slogf.d(TAG, "Creating device connection policy"); 414 } 415 if (mUserId == UserManagerHelper.USER_NULL) { 416 if (DBG) { 417 Slogf.d(TAG, "No foreground user, cannot create device connection policy"); 418 } 419 return; 420 } 421 mBluetoothDeviceConnectionPolicy = BluetoothDeviceConnectionPolicy.create(mContext, 422 mUserId, this); 423 if (mBluetoothDeviceConnectionPolicy == null) { 424 if (DBG) { 425 Slogf.d(TAG, "Failed to create default Bluetooth device connection policy."); 426 } 427 return; 428 } 429 mBluetoothDeviceConnectionPolicy.init(); 430 } 431 432 /** 433 * Destroys the current instance of a BluetoothDeviceConnectionPolicy, if one exists 434 */ 435 @GuardedBy("mPerUserLock") destroyBluetoothDeviceConnectionPolicyLocked()436 private void destroyBluetoothDeviceConnectionPolicyLocked() { 437 if (DBG) { 438 Slogf.d(TAG, "Destroying device connection policy"); 439 } 440 if (mBluetoothDeviceConnectionPolicy != null) { 441 mBluetoothDeviceConnectionPolicy.release(); 442 mBluetoothDeviceConnectionPolicy = null; 443 } 444 } 445 446 /** 447 * Creates an instance of a BluetoothDeviceConnectionPolicy under the current user 448 */ 449 @GuardedBy("mPerUserLock") createBluetoothPowerPolicyLocked()450 private void createBluetoothPowerPolicyLocked() { 451 if (DBG) { 452 Slogf.d(TAG, "Creating power policy"); 453 } 454 if (mUserId == UserManagerHelper.USER_NULL) { 455 456 if (DBG) { 457 Slogf.d(TAG, "No foreground user, cannot create power policy"); 458 } 459 return; 460 } 461 mBluetoothPowerPolicy = BluetoothPowerPolicy.create(mContext, mUserId); 462 if (mBluetoothPowerPolicy == null) { 463 if (DBG) { 464 Slogf.d(TAG, "Failed to create Bluetooth power policy."); 465 } 466 return; 467 } 468 mBluetoothPowerPolicy.init(); 469 } 470 471 /** 472 * Destroys the current instance of a BluetoothDeviceConnectionPolicy, if one exists 473 */ 474 @GuardedBy("mPerUserLock") destroyBluetoothPowerPolicyLocked()475 private void destroyBluetoothPowerPolicyLocked() { 476 if (DBG) { 477 Slogf.d(TAG, "Destroying power policy"); 478 } 479 if (mBluetoothPowerPolicy != null) { 480 mBluetoothPowerPolicy.release(); 481 mBluetoothPowerPolicy = null; 482 } 483 } 484 485 /** 486 * Determine if we are using the default device connection policy or not 487 * 488 * @return true if the default policy is active, false otherwise 489 */ isUsingDefaultConnectionPolicy()490 public boolean isUsingDefaultConnectionPolicy() { 491 synchronized (mPerUserLock) { 492 return mBluetoothDeviceConnectionPolicy != null; 493 } 494 } 495 496 /** 497 * Determine if we are using the default power policy or not 498 * 499 * @return true if the default policy is active, false otherwise 500 */ isUsingDefaultPowerPolicy()501 public boolean isUsingDefaultPowerPolicy() { 502 synchronized (mPerUserLock) { 503 return mBluetoothPowerPolicy != null; 504 } 505 } 506 507 /** 508 * Initiate automatated connecting of devices based on the prioritized device lists for each 509 * profile. 510 */ connectDevices()511 public void connectDevices() { 512 enforceBluetoothConnectPermission(); 513 enforceBluetoothPrivilegedPermission(); 514 enforceModifyPhoneStatePermission(); 515 516 if (DBG) { 517 Slogf.d(TAG, "Connect devices for each profile"); 518 } 519 synchronized (mPerUserLock) { 520 if (mDeviceManager != null) { 521 mDeviceManager.beginAutoConnecting(); 522 } 523 } 524 } 525 526 /** 527 * Request to disconnect the given profile on the given device, and prevent it from reconnecting 528 * until either the request is released, or the process owning the given token dies. 529 * 530 * @param device The device on which to inhibit a profile. 531 * @param profile The {@link android.bluetooth.BluetoothProfile} to inhibit. 532 * @param token A {@link IBinder} to be used as an identity for the request. If the process 533 * owning the token dies, the request will automatically be released 534 * @return True if the profile was successfully inhibited, false if an error occurred. 535 */ requestProfileInhibit(BluetoothDevice device, int profile, IBinder token)536 public boolean requestProfileInhibit(BluetoothDevice device, int profile, IBinder token) { 537 enforceBluetoothConnectPermission(); 538 enforceBluetoothPrivilegedPermission(); 539 540 if (DBG) { 541 Slogf.d(TAG, "Request profile inhibit: profile %s, device %s", 542 BluetoothUtils.getProfileName(profile), device.getAddress()); 543 } 544 synchronized (mPerUserLock) { 545 if (mInhibitManager == null) return false; 546 return mInhibitManager.requestProfileInhibit(device, profile, token); 547 } 548 } 549 550 /** 551 * Undo a previous call to {@link #requestProfileInhibit} with the same parameters, 552 * and reconnect the profile if no other requests are active. 553 * 554 * @param device The device on which to release the inhibit request. 555 * @param profile The profile on which to release the inhibit request. 556 * @param token The token provided in the original call to 557 * {@link #requestBluetoothProfileInhibit}. 558 * @return True if the request was released, false if an error occurred. 559 */ releaseProfileInhibit(BluetoothDevice device, int profile, IBinder token)560 public boolean releaseProfileInhibit(BluetoothDevice device, int profile, IBinder token) { 561 enforceBluetoothConnectPermission(); 562 enforceBluetoothPrivilegedPermission(); 563 564 if (DBG) { 565 Slogf.d(TAG, "Release profile inhibit: profile %s, device %s", 566 BluetoothUtils.getProfileName(profile), device.getAddress()); 567 } 568 synchronized (mPerUserLock) { 569 if (mInhibitManager == null) return false; 570 return mInhibitManager.releaseProfileInhibit(device, profile, token); 571 } 572 } 573 574 575 /** 576 * Checks whether a request to disconnect the given profile on the given device has been made 577 * and if the inhibit request is still active. 578 * 579 * @param device The device on which to verify the inhibit request. 580 * @param profile The profile on which to verify the inhibit request. 581 * @param token The token provided in the original call to 582 * {@link #requestBluetoothProfileInhibit}. 583 * @return True if inhibit was requested and is still active, false if an error occurred or 584 * inactive. 585 */ isProfileInhibited(BluetoothDevice device, int profile, IBinder token)586 public boolean isProfileInhibited(BluetoothDevice device, int profile, IBinder token) { 587 enforceBluetoothConnectPermission(); 588 enforceBluetoothPrivilegedPermission(); 589 590 if (DBG) { 591 Slogf.d(TAG, "Check profile inhibit: profile %s, device %s", 592 BluetoothUtils.getProfileName(profile), device.getAddress()); 593 } 594 synchronized (mPerUserLock) { 595 if (mInhibitManager == null) return false; 596 return mInhibitManager.isProfileInhibited(device, profile, token); 597 } 598 } 599 600 /** 601 * Triggers Bluetooth to start a BVRA session with the default HFP Client device. 602 */ startBluetoothVoiceRecognition()603 public boolean startBluetoothVoiceRecognition() { 604 enforceBluetoothConnectPermission(); 605 606 synchronized (mPerUserLock) { 607 try { 608 return mCarBluetoothUserService.startBluetoothVoiceRecognition(); 609 } catch (RemoteException e) { 610 Slogf.e(TAG, "Remote Service Exception on BVRA", e); 611 } 612 } 613 return false; 614 } 615 616 /** 617 * Make sure the caller has the Bluetooth Connect permission 618 */ enforceBluetoothConnectPermission()619 private void enforceBluetoothConnectPermission() { 620 if (PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission( 621 android.Manifest.permission.BLUETOOTH_CONNECT)) { 622 return; 623 } 624 625 throw new SecurityException("requires permission " 626 + android.Manifest.permission.BLUETOOTH_CONNECT); 627 } 628 629 /** 630 * Make sure the caller has the Bluetooth Privileged permission 631 */ enforceBluetoothPrivilegedPermission()632 private void enforceBluetoothPrivilegedPermission() { 633 if (PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission( 634 android.Manifest.permission.BLUETOOTH_PRIVILEGED)) { 635 return; 636 } 637 throw new SecurityException("requires permission " 638 + android.Manifest.permission.BLUETOOTH_PRIVILEGED); 639 } 640 641 /** 642 * Make sure the caller has the Modify Phone State permission 643 */ enforceModifyPhoneStatePermission()644 private void enforceModifyPhoneStatePermission() { 645 if (PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission( 646 android.Manifest.permission.MODIFY_PHONE_STATE)) { 647 return; 648 } 649 throw new SecurityException("requires permission " 650 + android.Manifest.permission.MODIFY_PHONE_STATE); 651 } 652 653 /** 654 * Print out the verbose debug status of this object 655 */ 656 @Override 657 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(IndentingPrintWriter writer)658 public void dump(IndentingPrintWriter writer) { 659 writer.printf("* %s *\n", TAG); 660 mUserServiceHelper.dump(writer); 661 662 synchronized (mPerUserLock) { 663 writer.printf("User ID: %d\n", mUserId); 664 writer.printf("User Proxies: %s\n", mCarBluetoothUserService != null ? "Yes" : "No"); 665 writer.printf("Using default connection policy? %s\n", 666 mUseDefaultConnectionPolicy ? "Yes" : "No"); 667 writer.printf("Using default power policy? %s\n", 668 mUseDefaultPowerPolicy ? "Yes" : "No"); 669 670 // Device Connection Policy 671 if (mBluetoothDeviceConnectionPolicy != null) { 672 mBluetoothDeviceConnectionPolicy.dump(writer); 673 } else { 674 writer.printf("BluetoothDeviceConnectionPolicy: null\n"); 675 } 676 677 // Power Policy 678 if (mBluetoothPowerPolicy != null) { 679 mBluetoothPowerPolicy.dump(writer); 680 } else { 681 writer.printf("BluetoothPowerPolicy: null\n"); 682 } 683 684 // Device Manager status 685 if (mDeviceManager != null) { 686 mDeviceManager.dump(writer); 687 } else { 688 writer.printf("BluetoothDeviceManager: null\n"); 689 } 690 691 // Profile Inhibits 692 if (mInhibitManager != null) { 693 mInhibitManager.dump(writer); 694 } else { 695 writer.printf("BluetoothProfileInhibitManager: null\n"); 696 } 697 } 698 } 699 700 @Override 701 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dumpProto(ProtoOutputStream proto)702 public void dumpProto(ProtoOutputStream proto) {} 703 } 704