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      */
createCar(Context context, ServiceConnection serviceConnectionListener, @Nullable Handler handler)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      */
createCar(Context context, ServiceConnection serviceConnectionListener)379     public static Car createCar(Context context, ServiceConnection serviceConnectionListener) {
380       return createCar(context, serviceConnectionListener, null);
381     }
382 
Car(Context context, ServiceConnection serviceConnectionListener, @Nullable Handler handler)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      */
Car(Context context, ICar service, @Nullable Handler handler)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 
determineMainThreadEventHandler(Handler eventHandler)410     private static Handler determineMainThreadEventHandler(Handler eventHandler) {
411         Looper mainLooper = Looper.getMainLooper();
412         return (eventHandler.getLooper() == mainLooper) ? eventHandler : new Handler(mainLooper);
413     }
414 
determineEventHandler(@ullable Handler handler)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      */
connect()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      */
disconnect()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      */
isConnected()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      */
isConnecting()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      */
getCarManager(String serviceName)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
getCarConnectionType()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      */
checkCarNotConnectedExceptionFromCarService( IllegalStateException e)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 */
hideCarNotConnectedExceptionFromCarService( IllegalStateException e)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 
createCarManager(String serviceName, IBinder binder)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 
startCarService()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 
getICarOrThrow()635     private synchronized ICar getICarOrThrow() throws IllegalStateException {
636         if (mService == null) {
637             throw new IllegalStateException("not connected");
638         }
639         return mService;
640     }
641 
handleRemoteException(RemoteException e)642     private void handleRemoteException(RemoteException e) {
643         Log.w(CarLibLog.TAG_CAR, "RemoteException", e);
644         disconnect();
645     }
646 
tearDownCarManagers()647     private void tearDownCarManagers() {
648         synchronized (mCarManagerLock) {
649             for (CarManagerBase manager: mServiceMap.values()) {
650                 manager.onCarDisconnected();
651             }
652             mServiceMap.clear();
653         }
654     }
655 }
656