• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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