Car.java revision 905968cf95d4c8608d6d9351b5dd10fe994a1220
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.cluster.CarInstrumentClusterManager;
24import android.car.content.pm.CarPackageManager;
25import android.car.hardware.CarDiagnosticManager;
26import android.car.hardware.CarSensorManager;
27import android.car.hardware.CarVendorExtensionManager;
28import android.car.hardware.cabin.CarCabinManager;
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.internal.annotations.GuardedBy;
49
50import java.lang.annotation.Retention;
51import java.lang.annotation.RetentionPolicy;
52import 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 */
59public 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     * Service name for {@link CarNavigationStatusManager}
85     * @hide
86     */
87    public static final String CAR_NAVIGATION_SERVICE = "car_navigation_service";
88    /**
89     * Service name for {@link CarInstrumentClusterManager}
90     * @hide
91     */
92    public static final String CAR_INSTRUMENT_CLUSTER_SERVICE = "cluster_service";
93
94    /**
95     * @hide
96     */
97    @SystemApi
98    public static final String CABIN_SERVICE = "cabin";
99
100    /**
101     * @hide
102     */
103    @SystemApi
104    public static final String DIAGNOSTIC_SERVICE = "diagnostic";
105
106    /**
107     * @hide
108     */
109    @SystemApi
110    public static final String RADIO_SERVICE = "radio";
111
112    /**
113     * @hide
114     */
115    @SystemApi
116    public static final String HVAC_SERVICE = "hvac";
117
118    /**
119     * @hide
120     */
121    @SystemApi
122    public static final String PROJECTION_SERVICE = "projection";
123
124    /**
125     * @hide
126     */
127    @SystemApi
128    public static final String VENDOR_EXTENSION_SERVICE = "vendor_extension";
129
130    /**
131     * @FutureFeature Cannot drop due to usage in non-flag protected place.
132     * @hide
133     */
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    /** 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 access VMS publisher APIs.
258     *
259     * @hide
260     */
261    @FutureFeature
262    public static final String PERMISSION_VMS_PUBLISHER = "android.car.permission.VMS_PUBLISHER";
263
264    /**
265     * Permissions necessary to access VMS subscriber APIs.
266     *
267     * @hide
268     */
269    @FutureFeature
270    public static final String PERMISSION_VMS_SUBSCRIBER = "android.car.permission.VMS_SUBSCRIBER";
271
272    /**
273     * Permissions necessary to read diagnostic information, including vendor-specific bits.
274     *
275     * @hide
276     */
277    @SystemApi
278    public static final String PERMISSION_CAR_DIAGNOSTIC_READ_ALL =
279        "android.car.permission.DIAGNOSTIC_READ_ALL";
280
281    /**
282     * Permissions necessary to clear diagnostic information.
283     *
284     * @hide
285     */
286    @SystemApi
287    public static final String PERMISSION_CAR_DIAGNOSTIC_CLEAR = "android.car.permission.DIAGNOSTIC_CLEAR";
288
289    /** Type of car connection: platform runs directly in car. */
290    public static final int CONNECTION_TYPE_EMBEDDED = 5;
291
292
293    /** @hide */
294    @IntDef({CONNECTION_TYPE_EMBEDDED})
295    @Retention(RetentionPolicy.SOURCE)
296    public @interface ConnectionType {}
297
298    /**
299     * CarXyzService throws IllegalStateException with this message is re-thrown as
300     * {@link CarNotConnectedException}.
301     *
302     * @hide
303     */
304    public static final String CAR_NOT_CONNECTED_EXCEPTION_MSG = "CarNotConnected";
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 VMS_SUBSCRIBER_SERVICE:
635                if (FeatureConfiguration.ENABLE_VEHICLE_MAP_SERVICE) {
636                    manager = new VmsSubscriberManager(binder, mEventHandler);
637                }
638                break;
639        }
640        return manager;
641    }
642
643    private void startCarService() {
644        Intent intent = new Intent();
645        intent.setPackage(CAR_SERVICE_PACKAGE);
646        intent.setAction(Car.CAR_SERVICE_INTERFACE_NAME);
647        boolean bound = mContext.bindServiceAsUser(intent, mServiceConnectionListener,
648                Context.BIND_AUTO_CREATE, UserHandle.CURRENT_OR_SELF);
649        if (!bound) {
650            mConnectionRetryCount++;
651            if (mConnectionRetryCount > CAR_SERVICE_BIND_MAX_RETRY) {
652                Log.w(CarLibLog.TAG_CAR, "cannot bind to car service after max retry");
653                mMainThreadEventHandler.post(mConnectionRetryFailedRunnable);
654            } else {
655                mEventHandler.postDelayed(mConnectionRetryRunnable,
656                        CAR_SERVICE_BIND_RETRY_INTERVAL_MS);
657            }
658        } else {
659            mConnectionRetryCount = 0;
660        }
661    }
662
663    private synchronized ICar getICarOrThrow() throws IllegalStateException {
664        if (mService == null) {
665            throw new IllegalStateException("not connected");
666        }
667        return mService;
668    }
669
670    private void handleRemoteException(RemoteException e) {
671        Log.w(CarLibLog.TAG_CAR, "RemoteException", e);
672        disconnect();
673    }
674
675    private void tearDownCarManagers() {
676        synchronized (mCarManagerLock) {
677            for (CarManagerBase manager: mServiceMap.values()) {
678                manager.onCarDisconnected();
679            }
680            mServiceMap.clear();
681        }
682    }
683}
684