Home | History | Annotate | Download | only in car
      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      */
    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      */
    406     public static Car createCar(Context context, ServiceConnection serviceConnectionListener) {
    407       return createCar(context, serviceConnectionListener, null);
    408     }
    409 
    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      */
    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 
    437     private static Handler determineMainThreadEventHandler(Handler eventHandler) {
    438         Looper mainLooper = Looper.getMainLooper();
    439         return (eventHandler.getLooper() == mainLooper) ? eventHandler : new Handler(mainLooper);
    440     }
    441 
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    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      */
    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 */
    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 
    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 
    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 
    660     private synchronized ICar getICarOrThrow() throws IllegalStateException {
    661         if (mService == null) {
    662             throw new IllegalStateException("not connected");
    663         }
    664         return mService;
    665     }
    666 
    667     private void handleRemoteException(RemoteException e) {
    668         Log.w(CarLibLog.TAG_CAR, "RemoteException", e);
    669         disconnect();
    670     }
    671 
    672     private void tearDownCarManagers() {
    673         synchronized (mCarManagerLock) {
    674             for (CarManagerBase manager: mServiceMap.values()) {
    675                 manager.onCarDisconnected();
    676             }
    677             mServiceMap.clear();
    678         }
    679     }
    680 }
    681