blob: 738a4238adf1e0744a9c76c3c4bf1663eb48c3b6 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.car;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.car.cluster.CarInstrumentClusterManager;
import android.car.content.pm.CarPackageManager;
import android.car.diagnostic.CarDiagnosticManager;
import android.car.drivingstate.CarDrivingStateManager;
import android.car.drivingstate.CarUxRestrictionsManager;
import android.car.hardware.CarSensorManager;
import android.car.hardware.CarVendorExtensionManager;
import android.car.hardware.cabin.CarCabinManager;
import android.car.hardware.hvac.CarHvacManager;
import android.car.hardware.power.CarPowerManager;
import android.car.hardware.property.CarPropertyManager;
import android.car.media.CarAudioManager;
import android.car.navigation.CarNavigationStatusManager;
import android.car.settings.CarConfigurationManager;
import android.car.storagemonitoring.CarStorageMonitoringManager;
import android.car.test.CarTestManagerBinderWrapper;
import android.car.vms.VmsSubscriberManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
/**
* Top level car API for embedded Android Auto deployments.
* This API works only for devices with {@link PackageManager#FEATURE_AUTOMOTIVE}
* Calling this API on a device with no such feature will lead to an exception.
*/
public final class Car {
/**
* Represent the version of Car API. This is only updated when there is API change.
* 1 : N
* 2 : O
* 3 : O-MR1
*/
public static final int VERSION = 3;
/** Service name for {@link CarSensorManager}, to be used in {@link #getCarManager(String)}. */
public static final String SENSOR_SERVICE = "sensor";
/** Service name for {@link CarInfoManager}, to be used in {@link #getCarManager(String)}. */
public static final String INFO_SERVICE = "info";
/** Service name for {@link CarAppFocusManager}. */
public static final String APP_FOCUS_SERVICE = "app_focus";
/** Service name for {@link CarPackageManager} */
public static final String PACKAGE_SERVICE = "package";
/** Service name for {@link CarAudioManager} */
public static final String AUDIO_SERVICE = "audio";
/** Service name for {@link CarNavigationStatusManager} */
public static final String CAR_NAVIGATION_SERVICE = "car_navigation_service";
/**
* Service name for {@link CarInstrumentClusterManager}
* @hide
*/
public static final String CAR_INSTRUMENT_CLUSTER_SERVICE = "cluster_service";
/**
* @hide
*/
@SystemApi
public static final String CABIN_SERVICE = "cabin";
/**
* @hide
*/
@SystemApi
public static final String DIAGNOSTIC_SERVICE = "diagnostic";
/**
* @hide
*/
@SystemApi
public static final String HVAC_SERVICE = "hvac";
/**
* @hide
*/
@SystemApi
public static final String POWER_SERVICE = "power";
/**
* @hide
*/
@SystemApi
public static final String PROJECTION_SERVICE = "projection";
/**
* @hide
*/
@SystemApi
public static final String PROPERTY_SERVICE = "property";
/**
* @hide
*/
@SystemApi
public static final String VENDOR_EXTENSION_SERVICE = "vendor_extension";
/**
* @hide
*/
public static final String BLUETOOTH_SERVICE = "car_bluetooth";
/**
* @hide
*/
@SystemApi
public static final String VMS_SUBSCRIBER_SERVICE = "vehicle_map_subscriber_service";
/**
* Service name for {@link CarDrivingStateManager}
* @hide
*/
@SystemApi
public static final String CAR_DRIVING_STATE_SERVICE = "drivingstate";
/**
* Service name for {@link CarUxRestrictionsManager}
*/
public static final String CAR_UX_RESTRICTION_SERVICE = "uxrestriction";
/**
* Service name for {@link android.car.settings.CarConfigurationManager}
*/
public static final String CAR_CONFIGURATION_SERVICE = "configuration";
/**
* @hide
*/
@SystemApi
public static final String STORAGE_MONITORING_SERVICE = "storage_monitoring";
/**
* Service for testing. This is system app only feature.
* Service name for {@link CarTestManager}, to be used in {@link #getCarManager(String)}.
* @hide
*/
@SystemApi
public static final String TEST_SERVICE = "car-service-test";
/** Permission necessary to access car's mileage information.
* @hide
*/
@SystemApi
public static final String PERMISSION_MILEAGE = "android.car.permission.CAR_MILEAGE";
/** Permission necessary to access car's energy information. */
public static final String PERMISSION_ENERGY = "android.car.permission.CAR_ENERGY";
/** Permission necessary to access car's VIN information */
public static final String PERMISSION_IDENTIFICATION =
"android.car.permission.CAR_IDENTIFICATION";
/** Permission necessary to access car's speed. */
public static final String PERMISSION_SPEED = "android.car.permission.CAR_SPEED";
/** Permission necessary to access car's dynamics state.
* @hide
*/
@SystemApi
public static final String PERMISSION_CAR_DYNAMICS_STATE =
"android.car.permission.CAR_DYNAMICS_STATE";
/** Permission necessary to access car's fuel door and ev charge port. */
public static final String PERMISSION_ENERGY_PORTS = "android.car.permission.CAR_ENERGY_PORTS";
/** Permission necessary to read car's lights information.
* @hide
*/
@SystemApi
public static final String PERMISSION_EXTERIOR_LIGHTS =
"android.car.permission.CAR_EXTERIOR_LIGHTS";
/** Permission necessary to control car's exterior lights.
* @hide
*/
@SystemApi
public static final String PERMISSION_CONTROL_EXTERIOR_LIGHTS =
"android.car.permission.CONTROL_CAR_EXTERIOR_LIGHTS";
/** Permission necessary to access car's powertrain information.*/
public static final String PERMISSION_POWERTRAIN = "android.car.permission.CAR_POWERTRAIN";
/**
* Permission necessary to change car audio volume through {@link CarAudioManager}.
*/
public static final String PERMISSION_CAR_CONTROL_AUDIO_VOLUME =
"android.car.permission.CAR_CONTROL_AUDIO_VOLUME";
/**
* Permission necessary to change car audio settings through {@link CarAudioManager}.
* @hide
*/
public static final String PERMISSION_CAR_CONTROL_AUDIO_SETTINGS =
"android.car.permission.CAR_CONTROL_AUDIO_SETTINGS";
/**
* Permission necessary to use {@link CarNavigationStatusManager}.
*/
public static final String PERMISSION_CAR_NAVIGATION_MANAGER =
"android.car.permission.CAR_NAVIGATION_MANAGER";
/**
* Permission necessary to start activities in the instrument cluster through
* {@link CarInstrumentClusterManager}
*
* @hide
*/
@SystemApi
public static final String PERMISSION_CAR_INSTRUMENT_CLUSTER_CONTROL =
"android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL";
/**
* Application must have this permission in order to be launched in the instrument cluster
* display.
*
* @hide
*/
public static final String PERMISSION_CAR_DISPLAY_IN_CLUSTER =
"android.car.permission.CAR_DISPLAY_IN_CLUSTER";
/** Permission necessary to use {@link CarInfoManager}. */
public static final String PERMISSION_CAR_INFO = "android.car.permission.CAR_INFO";
/** Permission necessary to read temperature of car's exterior environment. */
public static final String PERMISSION_EXTERIOR_ENVIRONMENT =
"android.car.permission.CAR_EXTERIOR_ENVIRONMENT";
/**
* Permission necessary to access car specific communication channel.
* @hide
*/
@SystemApi
public static final String PERMISSION_VENDOR_EXTENSION =
"android.car.permission.CAR_VENDOR_EXTENSION";
/**
* @hide
*/
@SystemApi
public static final String PERMISSION_CONTROL_APP_BLOCKING =
"android.car.permission.CONTROL_APP_BLOCKING";
/**
* Permission necessary to access Car Cabin APIs.
* @hide
*/
@SystemApi
public static final String PERMISSION_ADJUST_CAR_CABIN =
"android.car.permission.ADJUST_CAR_CABIN";
/**
* Permission necessary to access car's engine information.
* @hide
*/
@SystemApi
public static final String PERMISSION_CAR_ENGINE_DETAILED =
"android.car.permission.CAR_ENGINE_DETAILED";
/**
* Permission necessary to access car's tire pressure information.
* @hide
*/
@SystemApi
public static final String PERMISSION_TIRES = "android.car.permission.CAR_TIRES";
/**
* Permission necessary to control car's door.
* @hide
*/
@SystemApi
public static final String PERMISSION_CONTROL_CAR_DOORS =
"android.car.permission.CONTROL_CAR_DOORS";
/**
* Permission necessary to control car's windows.
* @hide
*/
@SystemApi
public static final String PERMISSION_CONTROL_CAR_WINDOWS =
"android.car.permission.CONTROL_CAR_WINDOWS";
/**
* Permission necessary to control car's seats.
* @hide
*/
@SystemApi
public static final String PERMISSION_CONTROL_CAR_SEATS =
"android.car.permission.CONTROL_CAR_SEATS";
/**
* Permission necessary to control car's mirrors.
* @hide
*/
@SystemApi
public static final String PERMISSION_CONTROL_CAR_MIRRORS =
"android.car.permission.CONTROL_CAR_MIRRORS";
/**
* Permission necessary to access Car HVAC APIs.
* @hide
*/
@SystemApi
public static final String PERMISSION_CONTROL_CAR_CLIMATE =
"android.car.permission.CONTROL_CAR_CLIMATE";
/**
* Permission necessary to access Car POWER APIs.
* @hide
*/
@SystemApi
public static final String PERMISSION_CAR_POWER = "android.car.permission.CAR_POWER";
/**
* Permission necessary to access Car PROJECTION system APIs.
* @hide
*/
@SystemApi
public static final String PERMISSION_CAR_PROJECTION = "android.car.permission.CAR_PROJECTION";
/**
* Permission necessary to mock vehicle hal for testing.
* @hide
* @deprecated mocking vehicle HAL in car service is no longer supported.
*/
@SystemApi
public static final String PERMISSION_MOCK_VEHICLE_HAL =
"android.car.permission.CAR_MOCK_VEHICLE_HAL";
/**
* Permission necessary to access CarTestService.
* @hide
*/
@SystemApi
public static final String PERMISSION_CAR_TEST_SERVICE =
"android.car.permission.CAR_TEST_SERVICE";
/**
* Permission necessary to access CarDrivingStateService to get a Car's driving state.
* @hide
*/
@SystemApi
public static final String PERMISSION_CAR_DRIVING_STATE =
"android.car.permission.CAR_DRIVING_STATE";
/**
* Permissions necessary to access VMS publisher APIs.
*
* @hide
*/
@SystemApi
public static final String PERMISSION_VMS_PUBLISHER = "android.car.permission.VMS_PUBLISHER";
/**
* Permissions necessary to access VMS subscriber APIs.
*
* @hide
*/
@SystemApi
public static final String PERMISSION_VMS_SUBSCRIBER = "android.car.permission.VMS_SUBSCRIBER";
/**
* Permissions necessary to read diagnostic information, including vendor-specific bits.
*
* @hide
*/
@SystemApi
public static final String PERMISSION_CAR_DIAGNOSTIC_READ_ALL =
"android.car.permission.CAR_DIAGNOSTICS";
/**
* Permissions necessary to clear diagnostic information.
*
* @hide
*/
@SystemApi
public static final String PERMISSION_CAR_DIAGNOSTIC_CLEAR = "android.car.permission.CLEAR_CAR_DIAGNOSTICS";
/**
* Permissions necessary to clear diagnostic information.
*
* @hide
*/
@SystemApi
public static final String PERMISSION_STORAGE_MONITORING = "android.car.permission.STORAGE_MONITORING";
/** Type of car connection: platform runs directly in car. */
public static final int CONNECTION_TYPE_EMBEDDED = 5;
/** @hide */
@IntDef({CONNECTION_TYPE_EMBEDDED})
@Retention(RetentionPolicy.SOURCE)
public @interface ConnectionType {}
/**
* CarXyzService throws IllegalStateException with this message is re-thrown as
* {@link CarNotConnectedException}.
*
* @hide
*/
public static final String CAR_NOT_CONNECTED_EXCEPTION_MSG = "CarNotConnected";
/**
* Activity Action: Provide media playing through a media template app.
* <p>Input: String extra mapped by {@link android.app.SearchManager#QUERY} is the query
* used to start the media. String extra mapped by {@link #CAR_EXTRA_MEDIA_PACKAGE} is the
* package name of the media app which user wants to play media on.
* <p>Output: nothing.
*/
public static final String CAR_INTENT_ACTION_MEDIA_TEMPLATE =
"android.car.intent.action.MEDIA_TEMPLATE";
/**
* Used as a string extra field with {@link #CAR_INTENT_ACTION_MEDIA_TEMPLATE} to specify the
* media app that user wants to start the media on. Note: this is not the templated media app.
*/
public static final String CAR_EXTRA_MEDIA_PACKAGE = "android.car.intent.extra.MEDIA_PACKAGE";
/** @hide */
public static final String CAR_SERVICE_INTERFACE_NAME = "android.car.ICar";
private static final String CAR_SERVICE_PACKAGE = "com.android.car";
private static final String CAR_SERVICE_CLASS = "com.android.car.CarService";
private static final long CAR_SERVICE_BIND_RETRY_INTERVAL_MS = 500;
private static final long CAR_SERVICE_BIND_MAX_RETRY = 20;
private final Context mContext;
@GuardedBy("this")
private ICar mService;
private final boolean mOwnsService;
private static final int STATE_DISCONNECTED = 0;
private static final int STATE_CONNECTING = 1;
private static final int STATE_CONNECTED = 2;
@GuardedBy("this")
private int mConnectionState;
@GuardedBy("this")
private int mConnectionRetryCount;
private final Runnable mConnectionRetryRunnable = new Runnable() {
@Override
public void run() {
startCarService();
}
};
private final Runnable mConnectionRetryFailedRunnable = new Runnable() {
@Override
public void run() {
mServiceConnectionListener.onServiceDisconnected(new ComponentName(CAR_SERVICE_PACKAGE,
CAR_SERVICE_CLASS));
}
};
private final ServiceConnection mServiceConnectionListener =
new ServiceConnection () {
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (Car.this) {
mService = ICar.Stub.asInterface(service);
mConnectionState = STATE_CONNECTED;
}
mServiceConnectionListenerClient.onServiceConnected(name, service);
}
public void onServiceDisconnected(ComponentName name) {
synchronized (Car.this) {
mService = null;
if (mConnectionState == STATE_DISCONNECTED) {
return;
}
mConnectionState = STATE_DISCONNECTED;
}
// unbind explicitly here.
disconnect();
mServiceConnectionListenerClient.onServiceDisconnected(name);
}
};
private final ServiceConnection mServiceConnectionListenerClient;
private final Object mCarManagerLock = new Object();
@GuardedBy("mCarManagerLock")
private final HashMap<String, CarManagerBase> mServiceMap = new HashMap<>();
/** Handler for generic event dispatching. */
private final Handler mEventHandler;
private final Handler mMainThreadEventHandler;
/**
* A factory method that creates Car instance for all Car API access.
* @param context
* @param serviceConnectionListener listener for monitoring service connection.
* @param handler the handler on which the callback should execute, or null to execute on the
* service's main thread. Note: the service connection listener will be always on the main
* thread regardless of the handler given.
* @return Car instance if system is in car environment and returns {@code null} otherwise.
*/
public static Car createCar(Context context, ServiceConnection serviceConnectionListener,
@Nullable Handler handler) {
if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
Log.e(CarLibLog.TAG_CAR, "FEATURE_AUTOMOTIVE not declared while android.car is used");
return null;
}
try {
return new Car(context, serviceConnectionListener, handler);
} catch (IllegalArgumentException e) {
// Expected when car service loader is not available.
}
return null;
}
/**
* A factory method that creates Car instance for all Car API access using main thread {@code
* Looper}.
*
* @see #createCar(Context, ServiceConnection, Handler)
*/
public static Car createCar(Context context, ServiceConnection serviceConnectionListener) {
return createCar(context, serviceConnectionListener, null);
}
private Car(Context context, ServiceConnection serviceConnectionListener,
@Nullable Handler handler) {
mContext = context;
mEventHandler = determineEventHandler(handler);
mMainThreadEventHandler = determineMainThreadEventHandler(mEventHandler);
mService = null;
mOwnsService = true;
mServiceConnectionListenerClient = serviceConnectionListener;
}
/**
* Car constructor when ICar binder is already available.
* @hide
*/
public Car(Context context, ICar service, @Nullable Handler handler) {
mContext = context;
mEventHandler = determineEventHandler(handler);
mMainThreadEventHandler = determineMainThreadEventHandler(mEventHandler);
mService = service;
mOwnsService = false;
mConnectionState = STATE_CONNECTED;
mServiceConnectionListenerClient = null;
}
private static Handler determineMainThreadEventHandler(Handler eventHandler) {
Looper mainLooper = Looper.getMainLooper();
return (eventHandler.getLooper() == mainLooper) ? eventHandler : new Handler(mainLooper);
}
private static Handler determineEventHandler(@Nullable Handler handler) {
if (handler == null) {
Looper looper = Looper.getMainLooper();
handler = new Handler(looper);
}
return handler;
}
/**
* Connect to car service. This can be called while it is disconnected.
* @throws IllegalStateException If connection is still on-going from previous
* connect call or it is already connected
*/
public void connect() throws IllegalStateException {
synchronized (this) {
if (mConnectionState != STATE_DISCONNECTED) {
throw new IllegalStateException("already connected or connecting");
}
mConnectionState = STATE_CONNECTING;
startCarService();
}
}
/**
* Disconnect from car service. This can be called while disconnected. Once disconnect is
* called, all Car*Managers from this instance becomes invalid, and
* {@link Car#getCarManager(String)} will return different instance if it is connected again.
*/
public void disconnect() {
synchronized (this) {
if (mConnectionState == STATE_DISCONNECTED) {
return;
}
mEventHandler.removeCallbacks(mConnectionRetryRunnable);
mMainThreadEventHandler.removeCallbacks(mConnectionRetryFailedRunnable);
mConnectionRetryCount = 0;
tearDownCarManagers();
mService = null;
mConnectionState = STATE_DISCONNECTED;
if (mOwnsService) {
mContext.unbindService(mServiceConnectionListener);
}
}
}
/**
* Tells if it is connected to the service or not. This will return false if it is still
* connecting.
* @return
*/
public boolean isConnected() {
synchronized (this) {
return mService != null;
}
}
/**
* Tells if this instance is already connecting to car service or not.
* @return
*/
public boolean isConnecting() {
synchronized (this) {
return mConnectionState == STATE_CONNECTING;
}
}
/**
* Get car specific service as in {@link Context#getSystemService(String)}. Returned
* {@link Object} should be type-casted to the desired service.
* For example, to get sensor service,
* SensorManagerService sensorManagerService = car.getCarManager(Car.SENSOR_SERVICE);
* @param serviceName Name of service that should be created like {@link #SENSOR_SERVICE}.
* @return Matching service manager or null if there is no such service.
* @throws CarNotConnectedException if the connection to the car service has been lost.
*/
public Object getCarManager(String serviceName) throws CarNotConnectedException {
CarManagerBase manager;
ICar service = getICarOrThrow();
synchronized (mCarManagerLock) {
manager = mServiceMap.get(serviceName);
if (manager == null) {
try {
IBinder binder = service.getCarService(serviceName);
if (binder == null) {
Log.w(CarLibLog.TAG_CAR, "getCarManager could not get binder for service:" +
serviceName);
return null;
}
manager = createCarManager(serviceName, binder);
if (manager == null) {
Log.w(CarLibLog.TAG_CAR,
"getCarManager could not create manager for service:" +
serviceName);
return null;
}
mServiceMap.put(serviceName, manager);
} catch (RemoteException e) {
handleRemoteException(e);
}
}
}
return manager;
}
/**
* Return the type of currently connected car.
* @return
*/
@ConnectionType
public int getCarConnectionType() {
return CONNECTION_TYPE_EMBEDDED;
}
/**
* IllegalStateException from XyzCarService with special message is re-thrown as a different
* exception. If the IllegalStateException is not understood then this message will throw the
* original exception.
*
* @param e exception from XyzCarService.
* @throws CarNotConnectedException if the connection to the car service has been lost.
* @hide
*/
public static void checkCarNotConnectedExceptionFromCarService(
IllegalStateException e) throws CarNotConnectedException, IllegalStateException {
String message = e.getMessage();
if (CAR_NOT_CONNECTED_EXCEPTION_MSG.equals(message)) {
throw new CarNotConnectedException();
} else {
throw e;
}
}
/** @hide */
public static void hideCarNotConnectedExceptionFromCarService(
IllegalStateException e) throws IllegalStateException {
String message = e.getMessage();
if (CAR_NOT_CONNECTED_EXCEPTION_MSG.equals(message)) {
return; //ignore
} else {
throw e;
}
}
private CarManagerBase createCarManager(String serviceName, IBinder binder)
throws CarNotConnectedException {
CarManagerBase manager = null;
switch (serviceName) {
case AUDIO_SERVICE:
manager = new CarAudioManager(binder, mContext, mEventHandler);
break;
case SENSOR_SERVICE:
manager = new CarSensorManager(binder, mContext, mEventHandler);
break;
case INFO_SERVICE:
manager = new CarInfoManager(binder);
break;
case APP_FOCUS_SERVICE:
manager = new CarAppFocusManager(binder, mEventHandler);
break;
case PACKAGE_SERVICE:
manager = new CarPackageManager(binder, mContext);
break;
case CAR_NAVIGATION_SERVICE:
manager = new CarNavigationStatusManager(binder);
break;
case CABIN_SERVICE:
manager = new CarCabinManager(binder, mContext, mEventHandler);
break;
case DIAGNOSTIC_SERVICE:
manager = new CarDiagnosticManager(binder, mContext, mEventHandler);
break;
case HVAC_SERVICE:
manager = new CarHvacManager(binder, mContext, mEventHandler);
break;
case POWER_SERVICE:
manager = new CarPowerManager(binder, mContext, mEventHandler);
break;
case PROJECTION_SERVICE:
manager = new CarProjectionManager(binder, mEventHandler);
break;
case PROPERTY_SERVICE:
manager = new CarPropertyManager(binder, mEventHandler, false,
"CarPropertyManager");
break;
case VENDOR_EXTENSION_SERVICE:
manager = new CarVendorExtensionManager(binder, mEventHandler);
break;
case CAR_INSTRUMENT_CLUSTER_SERVICE:
manager = new CarInstrumentClusterManager(binder, mEventHandler);
break;
case TEST_SERVICE:
/* CarTestManager exist in static library. So instead of constructing it here,
* only pass binder wrapper so that CarTestManager can be constructed outside. */
manager = new CarTestManagerBinderWrapper(binder);
break;
case VMS_SUBSCRIBER_SERVICE:
manager = new VmsSubscriberManager(binder);
break;
case BLUETOOTH_SERVICE:
manager = new CarBluetoothManager(binder, mContext);
break;
case STORAGE_MONITORING_SERVICE:
manager = new CarStorageMonitoringManager(binder, mEventHandler);
break;
case CAR_DRIVING_STATE_SERVICE:
manager = new CarDrivingStateManager(binder, mContext, mEventHandler);
break;
case CAR_UX_RESTRICTION_SERVICE:
manager = new CarUxRestrictionsManager(binder, mContext, mEventHandler);
break;
case CAR_CONFIGURATION_SERVICE:
manager = new CarConfigurationManager(binder);
break;
default:
break;
}
return manager;
}
private void startCarService() {
Intent intent = new Intent();
intent.setPackage(CAR_SERVICE_PACKAGE);
intent.setAction(Car.CAR_SERVICE_INTERFACE_NAME);
boolean bound = mContext.bindServiceAsUser(intent, mServiceConnectionListener,
Context.BIND_AUTO_CREATE, UserHandle.CURRENT_OR_SELF);
if (!bound) {
mConnectionRetryCount++;
if (mConnectionRetryCount > CAR_SERVICE_BIND_MAX_RETRY) {
Log.w(CarLibLog.TAG_CAR, "cannot bind to car service after max retry");
mMainThreadEventHandler.post(mConnectionRetryFailedRunnable);
} else {
mEventHandler.postDelayed(mConnectionRetryRunnable,
CAR_SERVICE_BIND_RETRY_INTERVAL_MS);
}
} else {
mConnectionRetryCount = 0;
}
}
private synchronized ICar getICarOrThrow() throws IllegalStateException {
if (mService == null) {
throw new IllegalStateException("not connected");
}
return mService;
}
private void handleRemoteException(RemoteException e) {
Log.w(CarLibLog.TAG_CAR, "RemoteException", e);
disconnect();
}
private void tearDownCarManagers() {
synchronized (mCarManagerLock) {
for (CarManagerBase manager: mServiceMap.values()) {
manager.onCarDisconnected();
}
mServiceMap.clear();
}
}
}