• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 android.car;
18 
19 import android.annotation.IntDef;
20 import android.annotation.Nullable;
21 import android.annotation.SystemApi;
22 import android.car.annotation.FutureFeature;
23 import android.car.cluster.CarInstrumentClusterManager;
24 import android.car.content.pm.CarPackageManager;
25 import android.car.diagnostic.CarDiagnosticManager;
26 import android.car.hardware.CarSensorManager;
27 import android.car.hardware.CarVendorExtensionManager;
28 import android.car.hardware.cabin.CarCabinManager;
29 import android.car.hardware.hvac.CarHvacManager;
30 import android.car.hardware.radio.CarRadioManager;
31 import android.car.media.CarAudioManager;
32 import android.car.navigation.CarNavigationStatusManager;
33 import android.car.CarBluetoothManager;
34 import android.car.test.CarTestManagerBinderWrapper;
35 import android.content.ComponentName;
36 import android.content.Context;
37 import android.content.Intent;
38 import android.content.ServiceConnection;
39 import android.content.pm.PackageManager;
40 import android.os.Handler;
41 import android.os.IBinder;
42 import android.os.Looper;
43 import android.os.RemoteException;
44 import android.os.UserHandle;
45 import android.util.Log;
46 
47 import com.android.car.internal.FeatureConfiguration;
48 import com.android.internal.annotations.GuardedBy;
49 
50 import java.lang.annotation.Retention;
51 import java.lang.annotation.RetentionPolicy;
52 import java.util.HashMap;
53 
54 /**
55  *   Top level car API for embedded Android Auto deployments.
56  *   This API works only for devices with {@link PackageManager#FEATURE_AUTOMOTIVE}
57  *   Calling this API on a device with no such feature will lead to an exception.
58  */
59 public final class Car {
60 
61     /**
62      * Represent the version of Car API. This is only updated when there is API change.
63      * 1 : N
64      * 2 : O
65      * 3 : O-MR1
66      */
67     public static final int VERSION = 3;
68 
69     /** Service name for {@link CarSensorManager}, to be used in {@link #getCarManager(String)}. */
70     public static final String SENSOR_SERVICE = "sensor";
71 
72     /** Service name for {@link CarInfoManager}, to be used in {@link #getCarManager(String)}. */
73     public static final String INFO_SERVICE = "info";
74 
75     /** Service name for {@link CarAppFocusManager}. */
76     public static final String APP_FOCUS_SERVICE = "app_focus";
77 
78     /** Service name for {@link CarPackageManager} */
79     public static final String PACKAGE_SERVICE = "package";
80 
81     /** Service name for {@link CarAudioManager} */
82     public static final String AUDIO_SERVICE = "audio";
83 
84     /**
85      * Service name for {@link CarNavigationStatusManager}
86      * @hide
87      */
88     public static final String CAR_NAVIGATION_SERVICE = "car_navigation_service";
89     /**
90      * Service name for {@link CarInstrumentClusterManager}
91      * @hide
92      */
93     public static final String CAR_INSTRUMENT_CLUSTER_SERVICE = "cluster_service";
94 
95     /**
96      * @hide
97      */
98     @SystemApi
99     public static final String CABIN_SERVICE = "cabin";
100 
101     /**
102      * @hide
103      */
104     @SystemApi
105     public static final String DIAGNOSTIC_SERVICE = "diagnostic";
106 
107     /**
108      * @hide
109      */
110     @SystemApi
111     public static final String RADIO_SERVICE = "radio";
112 
113     /**
114      * @hide
115      */
116     @SystemApi
117     public static final String HVAC_SERVICE = "hvac";
118 
119     /**
120      * @hide
121      */
122     @SystemApi
123     public static final String PROJECTION_SERVICE = "projection";
124 
125     /**
126      * @hide
127      */
128     @SystemApi
129     public static final String VENDOR_EXTENSION_SERVICE = "vendor_extension";
130 
131     /**
132      * @hide
133      */
134     public static final String BLUETOOTH_SERVICE = "car_bluetooth";
135 
136     /**
137      * Service for testing. This is system app only feature.
138      * Service name for {@link CarTestManager}, to be used in {@link #getCarManager(String)}.
139      * @hide
140      */
141     @SystemApi
142     public static final String TEST_SERVICE = "car-service-test";
143 
144     /** Permission necessary to access car's mileage information. */
145     public static final String PERMISSION_MILEAGE = "android.car.permission.CAR_MILEAGE";
146 
147     /** Permission necessary to access car's fuel level. */
148     public static final String PERMISSION_FUEL = "android.car.permission.CAR_FUEL";
149 
150     /** Permission necessary to access car's speed. */
151     public static final String PERMISSION_SPEED = "android.car.permission.CAR_SPEED";
152 
153     /** Permission necessary to access car's dynamics state. */
154     public static final String PERMISSION_VEHICLE_DYNAMICS_STATE =
155         "android.car.permission.VEHICLE_DYNAMICS_STATE";
156 
157     /**
158      * Permission necessary to change car audio volume through {@link CarAudioManager}.
159      */
160     public static final String PERMISSION_CAR_CONTROL_AUDIO_VOLUME =
161             "android.car.permission.CAR_CONTROL_AUDIO_VOLUME";
162 
163     /**
164      * Permission necessary to change car audio settings through {@link CarAudioManager}.
165      * @hide
166      */
167     public static final String PERMISSION_CAR_CONTROL_AUDIO_SETTINGS =
168             "android.car.permission.CAR_CONTROL_AUDIO_SETTINGS";
169 
170     /**
171      * Permission necessary to use {@link CarNavigationStatusManager}.
172      * @hide
173      */
174     public static final String PERMISSION_CAR_NAVIGATION_MANAGER =
175             "android.car.permission.CAR_NAVIGATION_MANAGER";
176 
177     /**
178      * Permission necessary to start activities in the instrument cluster through
179      * {@link CarInstrumentClusterManager}
180      *
181      * @hide
182      */
183     public static final String PERMISSION_CAR_INSTRUMENT_CLUSTER_CONTROL =
184             "android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL";
185 
186     /**
187      * Application must have this permission in order to be launched in the instrument cluster
188      * display.
189      *
190      * @hide
191      */
192     public static final String PERMISSION_CAR_DISPLAY_IN_CLUSTER =
193             "android.car.permission.CAR_DISPLAY_IN_CLUSTER";
194 
195     /**
196      * Permission necessary to access car specific communication channel.
197      * @hide
198      */
199     @SystemApi
200     public static final String PERMISSION_VENDOR_EXTENSION =
201             "android.car.permission.CAR_VENDOR_EXTENSION";
202 
203     /**
204      * @hide
205      */
206     @SystemApi
207     public static final String PERMISSION_CONTROL_APP_BLOCKING =
208             "android.car.permission.CONTROL_APP_BLOCKING";
209 
210     /**
211      * Permission necessary to access Car Cabin APIs.
212      * @hide
213      */
214     @SystemApi
215     public static final String PERMISSION_CAR_CABIN = "android.car.permission.CAR_CABIN";
216 
217     /**
218      * Permission necessary to access Car HVAC APIs.
219      * @hide
220      */
221     @SystemApi
222     public static final String PERMISSION_CAR_HVAC = "android.car.permission.CAR_HVAC";
223 
224     /**
225      * Permission necessary to access Car RADIO system APIs.
226      * @hide
227      */
228     @SystemApi
229     public static final String PERMISSION_CAR_RADIO = "android.car.permission.CAR_RADIO";
230 
231 
232     /**
233      * Permission necessary to access Car PROJECTION system APIs.
234      * @hide
235      */
236     @SystemApi
237     public static final String PERMISSION_CAR_PROJECTION = "android.car.permission.CAR_PROJECTION";
238 
239     /**
240      * Permission necessary to mock vehicle hal for testing.
241      * @hide
242      * @deprecated mocking vehicle HAL in car service is no longer supported.
243      */
244     @SystemApi
245     public static final String PERMISSION_MOCK_VEHICLE_HAL =
246             "android.car.permission.CAR_MOCK_VEHICLE_HAL";
247 
248     /**
249      * Permission necessary to access CarTestService.
250      * @hide
251      */
252     @SystemApi
253     public static final String PERMISSION_CAR_TEST_SERVICE =
254             "android.car.permission.CAR_TEST_SERVICE";
255 
256     /**
257      * Permissions necessary to read diagnostic information, including vendor-specific bits.
258      *
259      * @hide
260      */
261     @SystemApi
262     public static final String PERMISSION_CAR_DIAGNOSTIC_READ_ALL =
263         "android.car.permission.DIAGNOSTIC_READ_ALL";
264 
265     /**
266      * Permissions necessary to clear diagnostic information.
267      *
268      * @hide
269      */
270     @SystemApi
271     public static final String PERMISSION_CAR_DIAGNOSTIC_CLEAR = "android.car.permission.DIAGNOSTIC_CLEAR";
272 
273     /** Type of car connection: platform runs directly in car. */
274     public static final int CONNECTION_TYPE_EMBEDDED = 5;
275 
276 
277     /** @hide */
278     @IntDef({CONNECTION_TYPE_EMBEDDED})
279     @Retention(RetentionPolicy.SOURCE)
280     public @interface ConnectionType {}
281 
282     /**
283      * CarXyzService throws IllegalStateException with this message is re-thrown as
284      * {@link CarNotConnectedException}.
285      *
286      * @hide
287      */
288     public static final String CAR_NOT_CONNECTED_EXCEPTION_MSG = "CarNotConnected";
289 
290     /**
291      * Activity Action: Provide media playing through a media template app.
292      * <p>Input: String extra mapped by {@link android.app.SearchManager#QUERY} is the query
293      * used to start the media. String extra mapped by {@link #CAR_EXTRA_MEDIA_PACKAGE} is the
294      * package name of the media app which user wants to play media on.
295      * <p>Output: nothing.
296      */
297     public static final String CAR_INTENT_ACTION_MEDIA_TEMPLATE =
298             "android.car.intent.action.MEDIA_TEMPLATE";
299 
300     /**
301      * Used as a string extra field with {@link #CAR_INTENT_ACTION_MEDIA_TEMPLATE} to specify the
302      * media app that user wants to start the media on. Note: this is not the templated media app.
303      */
304     public static final String CAR_EXTRA_MEDIA_PACKAGE = "android.car.intent.extra.MEDIA_PACKAGE";
305 
306     /** @hide */
307     public static final String CAR_SERVICE_INTERFACE_NAME = "android.car.ICar";
308 
309     private static final String CAR_SERVICE_PACKAGE = "com.android.car";
310 
311     private static final String CAR_SERVICE_CLASS = "com.android.car.CarService";
312 
313     private static final long CAR_SERVICE_BIND_RETRY_INTERVAL_MS = 500;
314     private static final long CAR_SERVICE_BIND_MAX_RETRY = 20;
315 
316     private final Context mContext;
317     @GuardedBy("this")
318     private ICar mService;
319     private final boolean mOwnsService;
320     private static final int STATE_DISCONNECTED = 0;
321     private static final int STATE_CONNECTING = 1;
322     private static final int STATE_CONNECTED = 2;
323     @GuardedBy("this")
324     private int mConnectionState;
325     @GuardedBy("this")
326     private int mConnectionRetryCount;
327 
328     private final Runnable mConnectionRetryRunnable = new Runnable() {
329         @Override
330         public void run() {
331             startCarService();
332         }
333     };
334 
335     private final Runnable mConnectionRetryFailedRunnable = new Runnable() {
336         @Override
337         public void run() {
338             mServiceConnectionListener.onServiceDisconnected(new ComponentName(CAR_SERVICE_PACKAGE,
339                     CAR_SERVICE_CLASS));
340         }
341     };
342 
343     private final ServiceConnection mServiceConnectionListener =
344             new ServiceConnection () {
345         public void onServiceConnected(ComponentName name, IBinder service) {
346             synchronized (Car.this) {
347                 mService = ICar.Stub.asInterface(service);
348                 mConnectionState = STATE_CONNECTED;
349             }
350             mServiceConnectionListenerClient.onServiceConnected(name, service);
351         }
352 
353         public void onServiceDisconnected(ComponentName name) {
354             synchronized (Car.this) {
355                 mService = null;
356                 if (mConnectionState  == STATE_DISCONNECTED) {
357                     return;
358                 }
359                 mConnectionState = STATE_DISCONNECTED;
360             }
361             // unbind explicitly here.
362             disconnect();
363             mServiceConnectionListenerClient.onServiceDisconnected(name);
364         }
365     };
366 
367     private final ServiceConnection mServiceConnectionListenerClient;
368     private final Object mCarManagerLock = new Object();
369     @GuardedBy("mCarManagerLock")
370     private final HashMap<String, CarManagerBase> mServiceMap = new HashMap<>();
371 
372     /** Handler for generic event dispatching. */
373     private final Handler mEventHandler;
374 
375     private final Handler mMainThreadEventHandler;
376 
377     /**
378      * A factory method that creates Car instance for all Car API access.
379      * @param context
380      * @param serviceConnectionListener listener for monitoring service connection.
381      * @param handler the handler on which the callback should execute, or null to execute on the
382      * service's main thread. Note: the service connection listener will be always on the main
383      * thread regardless of the handler given.
384      * @return Car instance if system is in car environment and returns {@code null} otherwise.
385      */
createCar(Context context, ServiceConnection serviceConnectionListener, @Nullable Handler handler)386     public static Car createCar(Context context, ServiceConnection serviceConnectionListener,
387             @Nullable Handler handler) {
388         if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
389             Log.e(CarLibLog.TAG_CAR, "FEATURE_AUTOMOTIVE not declared while android.car is used");
390             return null;
391         }
392         try {
393           return new Car(context, serviceConnectionListener, handler);
394         } catch (IllegalArgumentException e) {
395           // Expected when car service loader is not available.
396         }
397         return null;
398     }
399 
400     /**
401      * A factory method that creates Car instance for all Car API access using main thread {@code
402      * Looper}.
403      *
404      * @see #createCar(Context, ServiceConnection, Handler)
405      */
createCar(Context context, ServiceConnection serviceConnectionListener)406     public static Car createCar(Context context, ServiceConnection serviceConnectionListener) {
407       return createCar(context, serviceConnectionListener, null);
408     }
409 
Car(Context context, ServiceConnection serviceConnectionListener, @Nullable Handler handler)410     private Car(Context context, ServiceConnection serviceConnectionListener,
411             @Nullable Handler handler) {
412         mContext = context;
413         mEventHandler = determineEventHandler(handler);
414         mMainThreadEventHandler = determineMainThreadEventHandler(mEventHandler);
415 
416         mService = null;
417         mOwnsService = true;
418         mServiceConnectionListenerClient = serviceConnectionListener;
419     }
420 
421 
422     /**
423      * Car constructor when ICar binder is already available.
424      * @hide
425      */
Car(Context context, ICar service, @Nullable Handler handler)426     public Car(Context context, ICar service, @Nullable Handler handler) {
427         mContext = context;
428         mEventHandler = determineEventHandler(handler);
429         mMainThreadEventHandler = determineMainThreadEventHandler(mEventHandler);
430 
431         mService = service;
432         mOwnsService = false;
433         mConnectionState = STATE_CONNECTED;
434         mServiceConnectionListenerClient = null;
435     }
436 
determineMainThreadEventHandler(Handler eventHandler)437     private static Handler determineMainThreadEventHandler(Handler eventHandler) {
438         Looper mainLooper = Looper.getMainLooper();
439         return (eventHandler.getLooper() == mainLooper) ? eventHandler : new Handler(mainLooper);
440     }
441 
determineEventHandler(@ullable Handler handler)442     private static Handler determineEventHandler(@Nullable Handler handler) {
443         if (handler == null) {
444             Looper looper = Looper.getMainLooper();
445             handler = new Handler(looper);
446         }
447         return handler;
448     }
449 
450     /**
451      * Connect to car service. This can be called while it is disconnected.
452      * @throws IllegalStateException If connection is still on-going from previous
453      *         connect call or it is already connected
454      */
connect()455     public void connect() throws IllegalStateException {
456         synchronized (this) {
457             if (mConnectionState != STATE_DISCONNECTED) {
458                 throw new IllegalStateException("already connected or connecting");
459             }
460             mConnectionState = STATE_CONNECTING;
461             startCarService();
462         }
463     }
464 
465     /**
466      * Disconnect from car service. This can be called while disconnected. Once disconnect is
467      * called, all Car*Managers from this instance becomes invalid, and
468      * {@link Car#getCarManager(String)} will return different instance if it is connected again.
469      */
disconnect()470     public void disconnect() {
471         synchronized (this) {
472             if (mConnectionState == STATE_DISCONNECTED) {
473                 return;
474             }
475             mEventHandler.removeCallbacks(mConnectionRetryRunnable);
476             mMainThreadEventHandler.removeCallbacks(mConnectionRetryFailedRunnable);
477             mConnectionRetryCount = 0;
478             tearDownCarManagers();
479             mService = null;
480             mConnectionState = STATE_DISCONNECTED;
481 
482             if (mOwnsService) {
483                 mContext.unbindService(mServiceConnectionListener);
484             }
485         }
486     }
487 
488     /**
489      * Tells if it is connected to the service or not. This will return false if it is still
490      * connecting.
491      * @return
492      */
isConnected()493     public boolean isConnected() {
494         synchronized (this) {
495             return mService != null;
496         }
497     }
498 
499     /**
500      * Tells if this instance is already connecting to car service or not.
501      * @return
502      */
isConnecting()503     public boolean isConnecting() {
504         synchronized (this) {
505             return mConnectionState == STATE_CONNECTING;
506         }
507     }
508 
509     /**
510      * Get car specific service as in {@link Context#getSystemService(String)}. Returned
511      * {@link Object} should be type-casted to the desired service.
512      * For example, to get sensor service,
513      * SensorManagerService sensorManagerService = car.getCarManager(Car.SENSOR_SERVICE);
514      * @param serviceName Name of service that should be created like {@link #SENSOR_SERVICE}.
515      * @return Matching service manager or null if there is no such service.
516      * @throws CarNotConnectedException if the connection to the car service has been lost.
517      */
getCarManager(String serviceName)518     public Object getCarManager(String serviceName) throws CarNotConnectedException {
519         CarManagerBase manager;
520         ICar service = getICarOrThrow();
521         synchronized (mCarManagerLock) {
522             manager = mServiceMap.get(serviceName);
523             if (manager == null) {
524                 try {
525                     IBinder binder = service.getCarService(serviceName);
526                     if (binder == null) {
527                         Log.w(CarLibLog.TAG_CAR, "getCarManager could not get binder for service:" +
528                                 serviceName);
529                         return null;
530                     }
531                     manager = createCarManager(serviceName, binder);
532                     if (manager == null) {
533                         Log.w(CarLibLog.TAG_CAR,
534                                 "getCarManager could not create manager for service:" +
535                                         serviceName);
536                         return null;
537                     }
538                     mServiceMap.put(serviceName, manager);
539                 } catch (RemoteException e) {
540                     handleRemoteException(e);
541                 }
542             }
543         }
544         return manager;
545     }
546 
547     /**
548      * Return the type of currently connected car.
549      * @return
550      */
551     @ConnectionType
getCarConnectionType()552     public int getCarConnectionType() {
553         return CONNECTION_TYPE_EMBEDDED;
554     }
555 
556     /**
557      * IllegalStateException from XyzCarService with special message is re-thrown as a different
558      * exception. If the IllegalStateException is not understood then this message will throw the
559      * original exception.
560      *
561      * @param e exception from XyzCarService.
562      * @throws CarNotConnectedException if the connection to the car service has been lost.
563      * @hide
564      */
checkCarNotConnectedExceptionFromCarService( IllegalStateException e)565     public static void checkCarNotConnectedExceptionFromCarService(
566             IllegalStateException e) throws CarNotConnectedException, IllegalStateException {
567         String message = e.getMessage();
568         if (CAR_NOT_CONNECTED_EXCEPTION_MSG.equals(message)) {
569             throw new CarNotConnectedException();
570         } else {
571             throw e;
572         }
573     }
574 
575     /** @hide */
hideCarNotConnectedExceptionFromCarService( IllegalStateException e)576     public static void hideCarNotConnectedExceptionFromCarService(
577             IllegalStateException e) throws IllegalStateException {
578         String message = e.getMessage();
579         if (CAR_NOT_CONNECTED_EXCEPTION_MSG.equals(message)) {
580             return; //ignore
581         } else {
582             throw e;
583         }
584     }
585 
createCarManager(String serviceName, IBinder binder)586     private CarManagerBase createCarManager(String serviceName, IBinder binder)
587             throws CarNotConnectedException {
588         CarManagerBase manager = null;
589         switch (serviceName) {
590             case AUDIO_SERVICE:
591                 manager = new CarAudioManager(binder, mContext, mEventHandler);
592                 break;
593             case SENSOR_SERVICE:
594                 manager = new CarSensorManager(binder, mContext, mEventHandler);
595                 break;
596             case INFO_SERVICE:
597                 manager = new CarInfoManager(binder);
598                 break;
599             case APP_FOCUS_SERVICE:
600                 manager = new CarAppFocusManager(binder, mEventHandler);
601                 break;
602             case PACKAGE_SERVICE:
603                 manager = new CarPackageManager(binder, mContext);
604                 break;
605             case CAR_NAVIGATION_SERVICE:
606                 manager = new CarNavigationStatusManager(binder);
607                 break;
608             case CABIN_SERVICE:
609                 manager = new CarCabinManager(binder, mContext, mEventHandler);
610                 break;
611             case DIAGNOSTIC_SERVICE:
612                 manager = new CarDiagnosticManager(binder, mContext, mEventHandler);
613                 break;
614             case HVAC_SERVICE:
615                 manager = new CarHvacManager(binder, mContext, mEventHandler);
616                 break;
617             case PROJECTION_SERVICE:
618                 manager = new CarProjectionManager(binder, mEventHandler);
619                 break;
620             case RADIO_SERVICE:
621                 manager = new CarRadioManager(binder, mEventHandler);
622                 break;
623             case VENDOR_EXTENSION_SERVICE:
624                 manager = new CarVendorExtensionManager(binder, mEventHandler);
625                 break;
626             case CAR_INSTRUMENT_CLUSTER_SERVICE:
627                 manager = new CarInstrumentClusterManager(binder, mEventHandler);
628                 break;
629             case TEST_SERVICE:
630                 /* CarTestManager exist in static library. So instead of constructing it here,
631                  * only pass binder wrapper so that CarTestManager can be constructed outside. */
632                 manager = new CarTestManagerBinderWrapper(binder);
633                 break;
634             case BLUETOOTH_SERVICE:
635                 manager = new CarBluetoothManager(binder, mContext);
636         }
637         return manager;
638     }
639 
startCarService()640     private void startCarService() {
641         Intent intent = new Intent();
642         intent.setPackage(CAR_SERVICE_PACKAGE);
643         intent.setAction(Car.CAR_SERVICE_INTERFACE_NAME);
644         boolean bound = mContext.bindServiceAsUser(intent, mServiceConnectionListener,
645                 Context.BIND_AUTO_CREATE, UserHandle.CURRENT_OR_SELF);
646         if (!bound) {
647             mConnectionRetryCount++;
648             if (mConnectionRetryCount > CAR_SERVICE_BIND_MAX_RETRY) {
649                 Log.w(CarLibLog.TAG_CAR, "cannot bind to car service after max retry");
650                 mMainThreadEventHandler.post(mConnectionRetryFailedRunnable);
651             } else {
652                 mEventHandler.postDelayed(mConnectionRetryRunnable,
653                         CAR_SERVICE_BIND_RETRY_INTERVAL_MS);
654             }
655         } else {
656             mConnectionRetryCount = 0;
657         }
658     }
659 
getICarOrThrow()660     private synchronized ICar getICarOrThrow() throws IllegalStateException {
661         if (mService == null) {
662             throw new IllegalStateException("not connected");
663         }
664         return mService;
665     }
666 
handleRemoteException(RemoteException e)667     private void handleRemoteException(RemoteException e) {
668         Log.w(CarLibLog.TAG_CAR, "RemoteException", e);
669         disconnect();
670     }
671 
tearDownCarManagers()672     private void tearDownCarManagers() {
673         synchronized (mCarManagerLock) {
674             for (CarManagerBase manager: mServiceMap.values()) {
675                 manager.onCarDisconnected();
676             }
677             mServiceMap.clear();
678         }
679     }
680 }
681