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