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