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