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