Merge "Support for hotspot client visibility." am: cf84f31de4
am: e1e4b55d46
Change-Id: Iaf16f5400654bdf36ce5c49df2bac74c89733550
diff --git a/car-lib/src/android/car/Car.java b/car-lib/src/android/car/Car.java
index 67a00e3..cc18688 100644
--- a/car-lib/src/android/car/Car.java
+++ b/car-lib/src/android/car/Car.java
@@ -24,6 +24,8 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
+import android.app.Activity;
+import android.app.Service;
import android.car.cluster.CarInstrumentClusterManager;
import android.car.cluster.ClusterActivityState;
import android.car.content.pm.CarPackageManager;
@@ -725,11 +727,10 @@
mConnectionState = STATE_CONNECTED;
mService = newService;
}
- if (mServiceConnectionListenerClient != null) {
- mServiceConnectionListenerClient.onServiceConnected(name, service);
- }
if (mStatusChangeCallback != null) {
mStatusChangeCallback.onLifecycleChanged(Car.this, true);
+ } else if (mServiceConnectionListenerClient != null) {
+ mServiceConnectionListenerClient.onServiceConnected(name, service);
}
}
@@ -742,11 +743,13 @@
}
handleCarDisconnectLocked();
}
- if (mServiceConnectionListenerClient != null) {
- mServiceConnectionListenerClient.onServiceDisconnected(name);
- }
if (mStatusChangeCallback != null) {
mStatusChangeCallback.onLifecycleChanged(Car.this, false);
+ } else if (mServiceConnectionListenerClient != null) {
+ mServiceConnectionListenerClient.onServiceDisconnected(name);
+ } else {
+ // This client does not handle car service restart, so should be terminated.
+ finishClient();
}
}
};
@@ -1136,12 +1139,15 @@
@Nullable
public Object getCarManager(String serviceName) {
CarManagerBase manager;
- ICar service = getICarOrThrow();
synchronized (mLock) {
+ if (mService == null) {
+ Log.w(TAG_CAR, "getCarManager not working while car service not ready");
+ return null;
+ }
manager = mServiceMap.get(serviceName);
if (manager == null) {
try {
- IBinder binder = service.getCarService(serviceName);
+ IBinder binder = mService.getCarService(serviceName);
if (binder == null) {
Log.w(TAG_CAR, "getCarManager could not get binder for service:"
+ serviceName);
@@ -1155,7 +1161,7 @@
}
mServiceMap.put(serviceName, manager);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
}
@@ -1171,84 +1177,139 @@
return CONNECTION_TYPE_EMBEDDED;
}
+ /** @hide */
+ Context getContext() {
+ return mContext;
+ }
+
+ /** @hide */
+ Handler getEventHandler() {
+ return mEventHandler;
+ }
+
+ /** @hide */
+ <T> T handleRemoteExceptionFromCarService(RemoteException e, T returnValue) {
+ handleRemoteExceptionFromCarService(e);
+ return returnValue;
+ }
+
+ /** @hide */
+ void handleRemoteExceptionFromCarService(RemoteException e) {
+ Log.w(TAG_CAR, "Car service has crashed", e);
+ }
+
+
+ private void finishClient() {
+ if (mContext == null) {
+ throw new IllegalStateException("Car service has crashed, null Context");
+ }
+ if (mContext instanceof Activity) {
+ Activity activity = (Activity) mContext;
+ if (!activity.isFinishing()) {
+ Log.w(TAG_CAR, "Car service crashed, client not handling it, finish Activity");
+ activity.finish();
+ }
+ return;
+ } else if (mContext instanceof Service) {
+ Service service = (Service) mContext;
+ throw new RuntimeException("Car service has crashed, client not handle it:"
+ + service.getPackageName() + "," + service.getClass().getSimpleName());
+ }
+ throw new IllegalStateException("Car service crashed, client not handling it.");
+ }
+
+ /** @hide */
+ public static <T> T handleRemoteExceptionFromCarService(Service service, RemoteException e,
+ T returnValue) {
+ handleRemoteExceptionFromCarService(service, e);
+ return returnValue;
+ }
+
+ /** @hide */
+ public static void handleRemoteExceptionFromCarService(Service service, RemoteException e) {
+ Log.w(TAG_CAR,
+ "Car service has crashed, client:" + service.getPackageName() + ","
+ + service.getClass().getSimpleName(), e);
+ service.stopSelf();
+ }
+
@Nullable
private CarManagerBase createCarManager(String serviceName, IBinder binder) {
CarManagerBase manager = null;
switch (serviceName) {
case AUDIO_SERVICE:
- manager = new CarAudioManager(binder, mContext, mEventHandler);
+ manager = new CarAudioManager(this, binder);
break;
case SENSOR_SERVICE:
- manager = new CarSensorManager(binder, mContext, mEventHandler);
+ manager = new CarSensorManager(this, binder);
break;
case INFO_SERVICE:
- manager = new CarInfoManager(binder);
+ manager = new CarInfoManager(this, binder);
break;
case APP_FOCUS_SERVICE:
- manager = new CarAppFocusManager(binder, mEventHandler);
+ manager = new CarAppFocusManager(this, binder);
break;
case PACKAGE_SERVICE:
- manager = new CarPackageManager(binder, mContext);
+ manager = new CarPackageManager(this, binder);
break;
case CAR_NAVIGATION_SERVICE:
- manager = new CarNavigationStatusManager(binder);
+ manager = new CarNavigationStatusManager(this, binder);
break;
case CABIN_SERVICE:
- manager = new CarCabinManager(binder, mContext, mEventHandler);
+ manager = new CarCabinManager(this, binder);
break;
case DIAGNOSTIC_SERVICE:
- manager = new CarDiagnosticManager(binder, mContext, mEventHandler);
+ manager = new CarDiagnosticManager(this, binder);
break;
case HVAC_SERVICE:
- manager = new CarHvacManager(binder, mContext, mEventHandler);
+ manager = new CarHvacManager(this, binder);
break;
case POWER_SERVICE:
- manager = new CarPowerManager(binder, mContext, mEventHandler);
+ manager = new CarPowerManager(this, binder);
break;
case PROJECTION_SERVICE:
- manager = new CarProjectionManager(binder, mEventHandler);
+ manager = new CarProjectionManager(this, binder);
break;
case PROPERTY_SERVICE:
- manager = new CarPropertyManager(ICarProperty.Stub.asInterface(binder),
- mEventHandler);
+ manager = new CarPropertyManager(this, ICarProperty.Stub.asInterface(binder));
break;
case VENDOR_EXTENSION_SERVICE:
- manager = new CarVendorExtensionManager(binder, mEventHandler);
+ manager = new CarVendorExtensionManager(this, binder);
break;
case CAR_INSTRUMENT_CLUSTER_SERVICE:
- manager = new CarInstrumentClusterManager(binder, mEventHandler);
+ manager = new CarInstrumentClusterManager(this, binder);
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);
+ manager = new CarTestManagerBinderWrapper(this, binder);
break;
case VMS_SUBSCRIBER_SERVICE:
- manager = new VmsSubscriberManager(binder);
+ manager = new VmsSubscriberManager(this, binder);
break;
case BLUETOOTH_SERVICE:
- manager = new CarBluetoothManager(binder, mContext);
+ manager = new CarBluetoothManager(this, binder);
break;
case STORAGE_MONITORING_SERVICE:
- manager = new CarStorageMonitoringManager(binder, mEventHandler);
+ manager = new CarStorageMonitoringManager(this, binder);
break;
case CAR_DRIVING_STATE_SERVICE:
- manager = new CarDrivingStateManager(binder, mContext, mEventHandler);
+ manager = new CarDrivingStateManager(this, binder);
break;
case CAR_UX_RESTRICTION_SERVICE:
- manager = new CarUxRestrictionsManager(binder, mContext, mEventHandler);
+ manager = new CarUxRestrictionsManager(this, binder);
break;
case CAR_CONFIGURATION_SERVICE:
- manager = new CarConfigurationManager(binder);
+ manager = new CarConfigurationManager(this, binder);
break;
case CAR_TRUST_AGENT_ENROLLMENT_SERVICE:
- manager = new CarTrustAgentEnrollmentManager(binder, mContext, mEventHandler);
+ manager = new CarTrustAgentEnrollmentManager(this, binder);
break;
case CAR_MEDIA_SERVICE:
- manager = new CarMediaManager(binder);
+ manager = new CarMediaManager(this, binder);
break;
case CAR_BUGREPORT_SERVICE:
- manager = new CarBugreportManager(binder, mContext);
+ manager = new CarBugreportManager(this, binder);
break;
default:
break;
@@ -1281,15 +1342,6 @@
}
}
- private ICar getICarOrThrow() throws IllegalStateException {
- synchronized (mLock) {
- if (mService == null) {
- throw new IllegalStateException("not connected");
- }
- return mService;
- }
- }
-
private void tearDownCarManagersLocked() {
// All disconnected handling should be only doing its internal cleanup.
for (CarManagerBase manager: mServiceMap.values()) {
diff --git a/car-lib/src/android/car/CarAppFocusManager.java b/car-lib/src/android/car/CarAppFocusManager.java
index ff0c25d..b8936af 100644
--- a/car-lib/src/android/car/CarAppFocusManager.java
+++ b/car-lib/src/android/car/CarAppFocusManager.java
@@ -17,7 +17,6 @@
package android.car;
import android.annotation.IntDef;
-import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
@@ -35,7 +34,7 @@
* should run in the system, and other app setting the flag for the matching app should
* lead into other app to stop.
*/
-public final class CarAppFocusManager implements CarManagerBase {
+public final class CarAppFocusManager extends CarManagerBase {
/**
* Listener to get notification for app getting information on application type status changes.
*/
@@ -114,7 +113,6 @@
public @interface AppFocusRequestResult {}
private final IAppFocus mService;
- private final Handler mHandler;
private final Map<OnAppFocusChangedListener, IAppFocusListenerImpl> mChangeBinders =
new HashMap<>();
private final Map<OnAppFocusOwnershipCallback, IAppFocusOwnershipCallbackImpl>
@@ -123,9 +121,9 @@
/**
* @hide
*/
- CarAppFocusManager(IBinder service, Handler handler) {
+ CarAppFocusManager(Car car, IBinder service) {
+ super(car);
mService = IAppFocus.Stub.asInterface(service);
- mHandler = handler;
}
/**
@@ -149,7 +147,7 @@
try {
mService.registerFocusListener(binder, appType);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -169,7 +167,8 @@
try {
mService.unregisterFocusListener(binder, appType);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
+ // continue for local clean-up
}
synchronized (this) {
binder.removeAppType(appType);
@@ -197,7 +196,7 @@
mService.unregisterFocusListener(binder, appType);
}
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -209,7 +208,7 @@
try {
return mService.getActiveAppTypes();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, new int[0]);
}
}
@@ -229,7 +228,7 @@
try {
return mService.isOwningFocus(binder, appType);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, false);
}
}
@@ -261,7 +260,7 @@
try {
return mService.requestAppFocus(binder, appType);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, APP_FOCUS_REQUEST_FAILED);
}
}
@@ -286,7 +285,8 @@
try {
mService.abandonAppFocus(binder, appType);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
+ // continue for local clean-up
}
synchronized (this) {
binder.removeAppType(appType);
@@ -314,7 +314,7 @@
mService.abandonAppFocus(binder, appType);
}
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -359,11 +359,8 @@
if (manager == null || listener == null) {
return;
}
- manager.mHandler.post(new Runnable() {
- @Override
- public void run() {
- listener.onAppFocusChanged(appType, active);
- }
+ manager.getEventHandler().post(() -> {
+ listener.onAppFocusChanged(appType, active);
});
}
}
@@ -403,11 +400,8 @@
if (manager == null || callback == null) {
return;
}
- manager.mHandler.post(new Runnable() {
- @Override
- public void run() {
- callback.onAppFocusOwnershipLost(appType);
- }
+ manager.getEventHandler().post(() -> {
+ callback.onAppFocusOwnershipLost(appType);
});
}
@@ -418,11 +412,8 @@
if (manager == null || callback == null) {
return;
}
- manager.mHandler.post(new Runnable() {
- @Override
- public void run() {
- callback.onAppFocusOwnershipGranted(appType);
- }
+ manager.getEventHandler().post(() -> {
+ callback.onAppFocusOwnershipGranted(appType);
});
}
}
diff --git a/car-lib/src/android/car/CarBluetoothManager.java b/car-lib/src/android/car/CarBluetoothManager.java
index c85cec7..c8e46ca 100644
--- a/car-lib/src/android/car/CarBluetoothManager.java
+++ b/car-lib/src/android/car/CarBluetoothManager.java
@@ -17,7 +17,6 @@
import android.Manifest;
import android.annotation.RequiresPermission;
-import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
@@ -26,9 +25,8 @@
*
* @hide
*/
-public final class CarBluetoothManager implements CarManagerBase {
+public final class CarBluetoothManager extends CarManagerBase {
private static final String TAG = "CarBluetoothManager";
- private final Context mContext;
private final ICarBluetooth mService;
/**
@@ -50,7 +48,7 @@
try {
mService.connectDevices();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -59,8 +57,8 @@
*
* @hide
*/
- public CarBluetoothManager(IBinder service, Context context) {
- mContext = context;
+ public CarBluetoothManager(Car car, IBinder service) {
+ super(car);
mService = ICarBluetooth.Stub.asInterface(service);
}
diff --git a/car-lib/src/android/car/CarBugreportManager.java b/car-lib/src/android/car/CarBugreportManager.java
index f5d3b1d..99f2c7c 100644
--- a/car-lib/src/android/car/CarBugreportManager.java
+++ b/car-lib/src/android/car/CarBugreportManager.java
@@ -20,7 +20,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
-import android.content.Context;
import android.os.Handler;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
@@ -39,10 +38,9 @@
*
* @hide
*/
-public final class CarBugreportManager implements CarManagerBase {
+public final class CarBugreportManager extends CarManagerBase {
private final ICarBugreportService mService;
- private Handler mHandler;
/**
* Callback from carbugreport manager. Callback methods are always called on the main thread.
@@ -153,9 +151,9 @@
*
* Should not be obtained directly by clients, use {@link Car#getCarManager(String)} instead.
*/
- public CarBugreportManager(IBinder service, Context context) {
+ public CarBugreportManager(Car car, IBinder service) {
+ super(car);
mService = ICarBugreportService.Stub.asInterface(service);
- mHandler = new Handler(context.getMainLooper());
}
/**
@@ -185,10 +183,10 @@
Preconditions.checkNotNull(callback);
try {
CarBugreportManagerCallbackWrapper wrapper =
- new CarBugreportManagerCallbackWrapper(callback, mHandler);
+ new CarBugreportManagerCallbackWrapper(callback, getEventHandler());
mService.requestBugreport(output, extraOutput, wrapper);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
} finally {
IoUtils.closeQuietly(output);
IoUtils.closeQuietly(extraOutput);
diff --git a/car-lib/src/android/car/CarInfoManager.java b/car-lib/src/android/car/CarInfoManager.java
index bf09495..e9a170d 100644
--- a/car-lib/src/android/car/CarInfoManager.java
+++ b/car-lib/src/android/car/CarInfoManager.java
@@ -29,7 +29,7 @@
* Utility to retrieve various static information from car. Each data are grouped as {@link Bundle}
* and relevant data can be checked from {@link Bundle} using pre-specified keys.
*/
-public final class CarInfoManager implements CarManagerBase{
+public final class CarInfoManager extends CarManagerBase {
private final CarPropertyManager mCarPropertyMgr;
/**
@@ -261,9 +261,10 @@
}
/** @hide */
- CarInfoManager(IBinder service) {
+ CarInfoManager(Car car, IBinder service) {
+ super(car);
ICarProperty mCarPropertyService = ICarProperty.Stub.asInterface(service);
- mCarPropertyMgr = new CarPropertyManager(mCarPropertyService, null);
+ mCarPropertyMgr = new CarPropertyManager(car, mCarPropertyService);
}
/** @hide */
diff --git a/car-lib/src/android/car/CarManagerBase.java b/car-lib/src/android/car/CarManagerBase.java
index 737f356..8d30fdf 100644
--- a/car-lib/src/android/car/CarManagerBase.java
+++ b/car-lib/src/android/car/CarManagerBase.java
@@ -16,10 +16,44 @@
package android.car;
+import android.content.Context;
+import android.os.Handler;
+import android.os.RemoteException;
+
/**
- * Common interface for Car*Manager
+ * Common base class for Car*Manager
* @hide
*/
-public interface CarManagerBase {
- void onCarDisconnected();
+public abstract class CarManagerBase {
+
+ protected final Car mCar;
+
+ public CarManagerBase(Car car) {
+ mCar = car;
+ }
+
+ protected Context getContext() {
+ return mCar.getContext();
+ }
+
+ protected Handler getEventHandler() {
+ return mCar.getEventHandler();
+ }
+
+ protected <T> T handleRemoteExceptionFromCarService(RemoteException e, T returnValue) {
+ return mCar.handleRemoteExceptionFromCarService(e, returnValue);
+ }
+
+ protected void handleRemoteExceptionFromCarService(RemoteException e) {
+ mCar.handleRemoteExceptionFromCarService(e);
+ }
+
+ /**
+ * Handle disconnection of car service (=crash). As car service has crashed already, this call
+ * should only clean up any listeners / callbacks passed from client. Clearing object passed
+ * to car service is not necessary as car service has crashed. Note that Car*Manager will not
+ * work any more as all binders are invalid. Client should re-create all Car*Managers when
+ * car service is restarted.
+ */
+ protected abstract void onCarDisconnected();
}
diff --git a/car-lib/src/android/car/CarProjectionManager.java b/car-lib/src/android/car/CarProjectionManager.java
index 4c177f7..bc4107f 100644
--- a/car-lib/src/android/car/CarProjectionManager.java
+++ b/car-lib/src/android/car/CarProjectionManager.java
@@ -69,7 +69,7 @@
* @hide
*/
@SystemApi
-public final class CarProjectionManager implements CarManagerBase {
+public final class CarProjectionManager extends CarManagerBase {
private static final String TAG = CarProjectionManager.class.getSimpleName();
private final Binder mToken = new Binder();
@@ -194,7 +194,6 @@
public static final int PROJECTION_AP_FAILED = 2;
private final ICarProjection mService;
- private final Handler mHandler;
private final Executor mHandlerExecutor;
@GuardedBy("mLock")
@@ -241,9 +240,10 @@
/**
* @hide
*/
- public CarProjectionManager(IBinder service, Handler handler) {
+ public CarProjectionManager(Car car, IBinder service) {
+ super(car);
mService = ICarProjection.Stub.asInterface(service);
- mHandler = handler;
+ Handler handler = getEventHandler();
mHandlerExecutor = handler::post;
}
@@ -448,7 +448,8 @@
mService.unregisterKeyEventHandler(mBinderHandler);
}
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
+ return;
}
mHandledEvents = events;
@@ -466,7 +467,7 @@
try {
mService.registerProjectionRunner(serviceIntent);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
}
@@ -483,7 +484,7 @@
try {
mService.unregisterProjectionRunner(serviceIntent);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
}
@@ -507,14 +508,14 @@
public void startProjectionAccessPoint(@NonNull ProjectionAccessPointCallback callback) {
Preconditions.checkNotNull(callback, "callback cannot be null");
synchronized (mLock) {
- Looper looper = mHandler.getLooper();
+ Looper looper = getEventHandler().getLooper();
ProjectionAccessPointCallbackProxy proxy =
new ProjectionAccessPointCallbackProxy(this, looper, callback);
try {
mService.startProjectionAccessPoint(proxy.getMessenger(), mAccessPointProxyToken);
mProjectionAccessPointCallbackProxy = proxy;
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
}
@@ -535,7 +536,7 @@
}
return channelList;
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, Collections.emptyList());
}
}
@@ -556,7 +557,7 @@
try {
mService.stopProjectionAccessPoint(mAccessPointProxyToken);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -575,7 +576,7 @@
try {
return mService.requestBluetoothProfileInhibit(device, profile, mToken);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, false);
}
}
@@ -593,7 +594,7 @@
try {
return mService.releaseBluetoothProfileInhibit(device, profile, mToken);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, false);
}
}
@@ -611,7 +612,7 @@
try {
mService.updateProjectionStatus(status, mToken);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -634,12 +635,12 @@
try {
mService.registerProjectionStatusListener(mCarProjectionStatusListener);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
} else {
// Already subscribed to Car Service, immediately notify listener with the current
// projection status in the event handler thread.
- mHandler.post(() ->
+ getEventHandler().post(() ->
listener.onProjectionStatusChanged(
mCarProjectionStatusListener.mCurrentState,
mCarProjectionStatusListener.mCurrentPackageName,
@@ -671,7 +672,7 @@
mService.unregisterProjectionStatusListener(mCarProjectionStatusListener);
mCarProjectionStatusListener = null;
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -695,7 +696,7 @@
try {
return mService.getProjectionOptions();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, Bundle.EMPTY);
}
}
@@ -838,7 +839,7 @@
List<ProjectionStatus> details) {
CarProjectionManager mgr = mManagerRef.get();
if (mgr != null) {
- mgr.mHandler.post(() -> {
+ mgr.getEventHandler().post(() -> {
mCurrentState = projectionState;
mCurrentPackageName = packageName;
mDetails = Collections.unmodifiableList(details);
diff --git a/car-lib/src/android/car/VehiclePropertyIds.java b/car-lib/src/android/car/VehiclePropertyIds.java
index c335e58..ada2b2d 100644
--- a/car-lib/src/android/car/VehiclePropertyIds.java
+++ b/car-lib/src/android/car/VehiclePropertyIds.java
@@ -338,38 +338,44 @@
/**
* Distance units for display
* Requires permission {@link Car#PERMISSION_READ_DISPLAY_UNITS} to read the property.
- * Requires permission {@link Car#PERMISSION_CONTROL_DISPLAY_UNITS} to write the property.
+ * Requires permission {@link Car#PERMISSION_CONTROL_DISPLAY_UNITS} and
+ * {@link Car#PERMISSION_VENDOR_EXTENSION}to write the property.
*/
public static final int DISTANCE_DISPLAY_UNITS = 289408512;
/**
* Fuel volume units for display
* Requires permission {@link Car#PERMISSION_READ_DISPLAY_UNITS} to read the property.
- * Requires permission {@link Car#PERMISSION_CONTROL_DISPLAY_UNITS} to write the property.
+ * Requires permission {@link Car#PERMISSION_CONTROL_DISPLAY_UNITS} and
+ * {@link Car#PERMISSION_VENDOR_EXTENSION}to write the property.
*/
public static final int FUEL_VOLUME_DISPLAY_UNITS = 289408513;
/**
* Tire pressure units for display
* Requires permission {@link Car#PERMISSION_READ_DISPLAY_UNITS} to read the property.
- * Requires permission {@link Car#PERMISSION_CONTROL_DISPLAY_UNITS} to write the property.
+ * Requires permission {@link Car#PERMISSION_CONTROL_DISPLAY_UNITS} and
+ * {@link Car#PERMISSION_VENDOR_EXTENSION}to write the property.
*/
public static final int TIRE_PRESSURE_DISPLAY_UNITS = 289408514;
/**
* EV battery units for display
* Requires permission {@link Car#PERMISSION_READ_DISPLAY_UNITS} to read the property.
- * Requires permission {@link Car#PERMISSION_CONTROL_DISPLAY_UNITS} to write the property.
+ * Requires permission {@link Car#PERMISSION_CONTROL_DISPLAY_UNITS} and
+ * {@link Car#PERMISSION_VENDOR_EXTENSION}to write the property.
*/
public static final int EV_BATTERY_DISPLAY_UNITS = 289408515;
/**
* Speed Units for display
* Requires permission {@link Car#PERMISSION_READ_DISPLAY_UNITS} to read the property.
- * Requires permission {@link Car#PERMISSION_CONTROL_DISPLAY_UNITS} to write the property.
+ * Requires permission {@link Car#PERMISSION_CONTROL_DISPLAY_UNITS} and
+ * {@link Car#PERMISSION_VENDOR_EXTENSION}to write the property.
* @hide
*/
public static final int VEHICLE_SPEED_DISPLAY_UNITS = 289408516;
/**
* Fuel consumption units for display
* Requires permission {@link Car#PERMISSION_READ_DISPLAY_UNITS} to read the property.
- * Requires permission {@link Car#PERMISSION_CONTROL_DISPLAY_UNITS} to write the property.
+ * Requires permission {@link Car#PERMISSION_CONTROL_DISPLAY_UNITS} and
+ * {@link Car#PERMISSION_VENDOR_EXTENSION}to write the property.
*/
public static final int FUEL_CONSUMPTION_UNITS_DISTANCE_OVER_VOLUME = 287311364;
/**
diff --git a/car-lib/src/android/car/cluster/CarInstrumentClusterManager.java b/car-lib/src/android/car/cluster/CarInstrumentClusterManager.java
index 54d10e2..2b633a0 100644
--- a/car-lib/src/android/car/cluster/CarInstrumentClusterManager.java
+++ b/car-lib/src/android/car/cluster/CarInstrumentClusterManager.java
@@ -17,10 +17,10 @@
package android.car.cluster;
import android.annotation.SystemApi;
+import android.car.Car;
import android.car.CarManagerBase;
import android.content.Intent;
import android.os.Bundle;
-import android.os.Handler;
import android.os.IBinder;
/**
@@ -35,7 +35,7 @@
*/
@Deprecated
@SystemApi
-public class CarInstrumentClusterManager implements CarManagerBase {
+public class CarInstrumentClusterManager extends CarManagerBase {
/**
* @deprecated use {@link android.car.Car#CATEGORY_NAVIGATION} instead
*
@@ -101,7 +101,8 @@
}
/** @hide */
- public CarInstrumentClusterManager(IBinder service, Handler handler) {
+ public CarInstrumentClusterManager(Car car, IBinder service) {
+ super(car);
// No-op
}
diff --git a/car-lib/src/android/car/cluster/renderer/IInstrumentCluster.aidl b/car-lib/src/android/car/cluster/renderer/IInstrumentCluster.aidl
index 7deecc7..4f41796 100644
--- a/car-lib/src/android/car/cluster/renderer/IInstrumentCluster.aidl
+++ b/car-lib/src/android/car/cluster/renderer/IInstrumentCluster.aidl
@@ -28,6 +28,8 @@
/**
* Returns {@link IInstrumentClusterNavigation} that will be passed to the navigation
* application.
+ *
+ * TODO(b/141992448) : remove blocking call
*/
IInstrumentClusterNavigation getNavigationService();
diff --git a/car-lib/src/android/car/cluster/renderer/IInstrumentClusterHelper.aidl b/car-lib/src/android/car/cluster/renderer/IInstrumentClusterHelper.aidl
new file mode 100644
index 0000000..680e241
--- /dev/null
+++ b/car-lib/src/android/car/cluster/renderer/IInstrumentClusterHelper.aidl
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 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.cluster.renderer;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * Helper binder API for InstrumentClusterRenderingService. This contains binder calls to car
+ * service.
+ *
+ * @hide
+ */
+interface IInstrumentClusterHelper {
+ /**
+ * Start an activity to specified display / user. The activity is considered as
+ * in fixed mode for the display and will be re-launched if the activity crashes, the package
+ * is updated or goes to background for whatever reason.
+ * Only one activity can exist in fixed mode for the target display and calling this multiple
+ * times with different {@code Intent} will lead into making all previous activities into
+ * non-fixed normal state (= will not be re-launched.)
+ *
+ * Do not change binder transaction number.
+ */
+ boolean startFixedActivityModeForDisplayAndUser(in Intent intent,
+ in Bundle activityOptionsBundle, int userId) = 0;
+ /**
+ * The activity lauched on the display is no longer in fixed mode. Re-launching or finishing
+ * should not trigger re-launfhing any more. Note that Activity for non-current user will
+ * be auto-stopped and there is no need to call this for user swiching. Note that this does not
+ * stop the activity but it will not be re-launched any more.
+ *
+ * Do not change binder transaction number.
+ */
+ void stopFixedActivityMode(int displayId) = 1;
+}
diff --git a/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java b/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
index b692c02..3e463d1 100644
--- a/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
+++ b/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
@@ -22,6 +22,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.UserIdInt;
import android.app.ActivityOptions;
import android.app.Service;
import android.car.Car;
@@ -82,6 +83,15 @@
*/
@SystemApi
public abstract class InstrumentClusterRenderingService extends Service {
+ /**
+ * Key to pass IInstrumentClusterHelper binder in onBind call {@link Intent} through extra
+ * {@link Bundle). Both extra bundle and binder itself use this key.
+ *
+ * @hide
+ */
+ public static final String EXTRA_BUNDLE_KEY_FOR_INSTRUMENT_CLUSTER_HELPER =
+ "android.car.cluster.renderer.IInstrumentClusterHelper";
+
private static final String TAG = CarLibLog.TAG_CLUSTER;
/**
@@ -93,16 +103,22 @@
private static final int NAVIGATION_STATE_EVENT_ID = 1;
private static final String BITMAP_QUERY_WIDTH = "w";
private static final String BITMAP_QUERY_HEIGHT = "h";
+ private static final String BITMAP_QUERY_OFFLANESALPHA = "offLanesAlpha";
+
+ private final Handler mUiHandler = new Handler(Looper.getMainLooper());
private final Object mLock = new Object();
+ // Main thread only
private RendererBinder mRendererBinder;
- private Handler mUiHandler = new Handler(Looper.getMainLooper());
private ActivityOptions mActivityOptions;
private ClusterActivityState mActivityState;
private ComponentName mNavigationComponent;
@GuardedBy("mLock")
private ContextOwner mNavContextOwner;
+ @GuardedBy("mLock")
+ private IInstrumentClusterHelper mInstrumentClusterHelper;
+
private static final int IMAGE_CACHE_SIZE_BYTES = 4 * 1024 * 1024; /* 4 mb */
private final LruCache<String, Bitmap> mCache = new LruCache<String, Bitmap>(
IMAGE_CACHE_SIZE_BYTES) {
@@ -164,6 +180,18 @@
Log.d(TAG, "onBind, intent: " + intent);
}
+ Bundle bundle = intent.getBundleExtra(EXTRA_BUNDLE_KEY_FOR_INSTRUMENT_CLUSTER_HELPER);
+ IBinder binder = null;
+ if (bundle != null) {
+ binder = bundle.getBinder(EXTRA_BUNDLE_KEY_FOR_INSTRUMENT_CLUSTER_HELPER);
+ }
+ if (binder == null) {
+ Log.wtf(TAG, "IInstrumentClusterHelper not passed through binder");
+ } else {
+ synchronized (mLock) {
+ mInstrumentClusterHelper = IInstrumentClusterHelper.Stub.asInterface(binder);
+ }
+ }
if (mRendererBinder == null) {
mRendererBinder = new RendererBinder(getNavigationRenderer());
}
@@ -203,6 +231,76 @@
public void onNavigationComponentReleased() {
}
+ @Nullable
+ private IInstrumentClusterHelper getClusterHelper() {
+ synchronized (mLock) {
+ if (mInstrumentClusterHelper == null) {
+ Log.w("mInstrumentClusterHelper still null, should wait until onBind",
+ new RuntimeException());
+ }
+ return mInstrumentClusterHelper;
+ }
+ }
+
+ /**
+ * Start Activity in fixed mode.
+ *
+ * <p>Activity launched in this way will stay visible across crash, package updatge
+ * or other Activity launch. So this should be carefully used for case like apps running
+ * in instrument cluster.</p>
+ *
+ * <p> Only one Activity can stay in this mode for a display and launching other Activity
+ * with this call means old one get out of the mode. Alternatively
+ * {@link #stopFixedActivityMode(int)} can be called to get the top activitgy out of this
+ * mode.</p>
+ *
+ * @param intent Should include specific {@code ComponentName}.
+ * @param options Should include target display.
+ * @param userId Target user id
+ * @return {@code true} if succeeded. {@code false} may mean the target component is not ready
+ * or available. Note that failure can happen during early boot-up stage even if the
+ * target Activity is in normal state and client should retry when it fails. Once it is
+ * successfully launched, car service will guarantee that it is running across crash or
+ * other events.
+ *
+ * @hide
+ */
+ protected boolean startFixedActivityModeForDisplayAndUser(@NonNull Intent intent,
+ @NonNull ActivityOptions options, @UserIdInt int userId) {
+ IInstrumentClusterHelper helper = getClusterHelper();
+ if (helper == null) {
+ return false;
+ }
+ try {
+ return helper.startFixedActivityModeForDisplayAndUser(intent, options.toBundle(),
+ userId);
+ } catch (RemoteException e) {
+ Log.w("Remote exception from car service", e);
+ // Probably car service will restart and rebind. So do nothing.
+ }
+ return false;
+ }
+
+
+ /**
+ * Stop fixed mode for top Activity in the display. Crashing or launching other Activity
+ * will not re-launch the top Activity any more.
+ *
+ * @hide
+ */
+ protected void stopFixedActivityMode(int displayId) {
+ IInstrumentClusterHelper helper = getClusterHelper();
+ if (helper == null) {
+ return;
+ }
+ try {
+ helper.stopFixedActivityMode(displayId);
+ } catch (RemoteException e) {
+ Log.w("Remote exception from car service, displayId:" + displayId, e);
+ // Probably car service will restart and rebind. So do nothing.
+ }
+ }
+
/**
* Updates the cluster navigation activity by checking which activity to show (an activity of
* the {@link #mNavContextOwner}). If not yet launched, it will do so.
@@ -379,16 +477,19 @@
@CallSuper
@Override
protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
- writer.println("**" + getClass().getSimpleName() + "**");
- writer.println("renderer binder: " + mRendererBinder);
- if (mRendererBinder != null) {
- writer.println("navigation renderer: " + mRendererBinder.mNavigationRenderer);
+ synchronized (mLock) {
+ writer.println("**" + getClass().getSimpleName() + "**");
+ writer.println("renderer binder: " + mRendererBinder);
+ if (mRendererBinder != null) {
+ writer.println("navigation renderer: " + mRendererBinder.mNavigationRenderer);
+ }
+ writer.println("navigation focus owner: " + getNavigationContextOwner());
+ writer.println("activity options: " + mActivityOptions);
+ writer.println("activity state: " + mActivityState);
+ writer.println("current nav component: " + mNavigationComponent);
+ writer.println("current nav packages: " + getNavigationContextOwner().mPackageNames);
+ writer.println("mInstrumentClusterHelper" + mInstrumentClusterHelper);
}
- writer.println("navigation focus owner: " + getNavigationContextOwner());
- writer.println("activity options: " + mActivityOptions);
- writer.println("activity state: " + mActivityState);
- writer.println("current nav component: " + mNavigationComponent);
- writer.println("current nav packages: " + getNavigationContextOwner().mPackageNames);
}
private class RendererBinder extends IInstrumentCluster.Stub {
@@ -547,8 +648,18 @@
}
/**
+ * See {@link #getBitmap(Uri, int, int, float)}
+ *
+ * @hide
+ */
+ @Nullable
+ public Bitmap getBitmap(Uri uri, int width, int height) {
+ return getBitmap(uri, width, height, 1f);
+ }
+
+ /**
* Fetches a bitmap from the navigation context owner (application holding navigation focus)
- * of the given width and height. The fetched bitmaps are cached.
+ * of the given width and height and off lane opacity. The fetched bitmaps are cached.
* It returns null if:
* <ul>
* <li>there is no navigation context owner
@@ -557,12 +668,16 @@
* </ul>
* This is a costly operation. Returned bitmaps should be fetched on a secondary thread.
*
+ * @throws IllegalArgumentException if width, height <= 0, or 0 > offLanesAlpha > 1
* @hide
*/
@Nullable
- public Bitmap getBitmap(Uri uri, int width, int height) throws InvalidSizeException {
+ public Bitmap getBitmap(Uri uri, int width, int height, float offLanesAlpha) {
if (width <= 0 || height <= 0) {
- throw new InvalidSizeException("Width and height must be > 0");
+ throw new IllegalArgumentException("Width and height must be > 0");
+ }
+ if (offLanesAlpha < 0 || offLanesAlpha > 1) {
+ throw new IllegalArgumentException("offLanesAlpha must be between [0, 1]");
}
try {
@@ -575,6 +690,7 @@
uri = uri.buildUpon()
.appendQueryParameter(BITMAP_QUERY_WIDTH, String.valueOf(width))
.appendQueryParameter(BITMAP_QUERY_HEIGHT, String.valueOf(height))
+ .appendQueryParameter(BITMAP_QUERY_OFFLANESALPHA, String.valueOf(offLanesAlpha))
.build();
String host = uri.getHost();
diff --git a/car-lib/src/android/car/cluster/renderer/InvalidSizeException.java b/car-lib/src/android/car/cluster/renderer/InvalidSizeException.java
deleted file mode 100644
index b89cd9f..0000000
--- a/car-lib/src/android/car/cluster/renderer/InvalidSizeException.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2019 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.cluster.renderer;
-
-/**
- * Exception denoting invalid size of an object.
- * eg.) Minimum size of an image
- *
- * @hide
- */
-public class InvalidSizeException extends Exception {
- public InvalidSizeException(String message) {
- super(message);
- }
-
- public InvalidSizeException(String message, Throwable cause) {
- super(message, cause);
- }
-
- public InvalidSizeException(Throwable cause) {
- super(cause);
- }
-}
diff --git a/car-lib/src/android/car/content/pm/CarAppBlockingPolicyService.java b/car-lib/src/android/car/content/pm/CarAppBlockingPolicyService.java
index 5b0a6bd..f95063a 100644
--- a/car-lib/src/android/car/content/pm/CarAppBlockingPolicyService.java
+++ b/car-lib/src/android/car/content/pm/CarAppBlockingPolicyService.java
@@ -17,6 +17,7 @@
import android.annotation.SystemApi;
import android.app.Service;
+import android.car.Car;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
@@ -76,7 +77,7 @@
try {
setter.setAppBlockingPolicy(policy);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ Car.handleRemoteExceptionFromCarService(CarAppBlockingPolicyService.this, e);
}
}
}
diff --git a/car-lib/src/android/car/content/pm/CarPackageManager.java b/car-lib/src/android/car/content/pm/CarPackageManager.java
index d23633d..7498c65 100644
--- a/car-lib/src/android/car/content/pm/CarPackageManager.java
+++ b/car-lib/src/android/car/content/pm/CarPackageManager.java
@@ -19,9 +19,9 @@
import android.annotation.IntDef;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.car.Car;
import android.car.CarManagerBase;
import android.content.ComponentName;
-import android.content.Context;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
@@ -32,7 +32,7 @@
/**
* Provides car specific API related with package management.
*/
-public final class CarPackageManager implements CarManagerBase {
+public final class CarPackageManager extends CarManagerBase {
private static final String TAG = "CarPackageManager";
/**
@@ -70,12 +70,11 @@
public @interface SetPolicyFlags {}
private final ICarPackageManager mService;
- private final Context mContext;
/** @hide */
- public CarPackageManager(IBinder service, Context context) {
+ public CarPackageManager(Car car, IBinder service) {
+ super(car);
mService = ICarPackageManager.Stub.asInterface(service);
- mContext = context;
}
/** @hide */
@@ -115,7 +114,7 @@
try {
mService.setAppBlockingPolicy(packageName, policy, flags);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -128,7 +127,7 @@
try {
mService.restartTask(taskId);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -151,7 +150,7 @@
try {
return mService.isActivityBackedBySafeActivity(activityName);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, false);
}
}
@@ -165,7 +164,7 @@
try {
mService.setEnableActivityBlocking(enable);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -181,7 +180,7 @@
try {
return mService.isActivityDistractionOptimized(packageName, className);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, false);
}
}
@@ -197,7 +196,7 @@
try {
return mService.isServiceDistractionOptimized(packageName, className);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, false);
}
}
}
diff --git a/car-lib/src/android/car/diagnostic/CarDiagnosticManager.java b/car-lib/src/android/car/diagnostic/CarDiagnosticManager.java
index 1559dd4..c9c8b6f 100644
--- a/car-lib/src/android/car/diagnostic/CarDiagnosticManager.java
+++ b/car-lib/src/android/car/diagnostic/CarDiagnosticManager.java
@@ -23,8 +23,6 @@
import android.car.CarLibLog;
import android.car.CarManagerBase;
import android.car.diagnostic.ICarDiagnosticEventListener.Stub;
-import android.content.Context;
-import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
@@ -47,7 +45,7 @@
* @hide
*/
@SystemApi
-public final class CarDiagnosticManager implements CarManagerBase {
+public final class CarDiagnosticManager extends CarManagerBase {
public static final int FRAME_TYPE_LIVE = 0;
public static final int FRAME_TYPE_FREEZE = 1;
@@ -70,15 +68,16 @@
/** Handles call back into clients. */
private final SingleMessageHandler<CarDiagnosticEvent> mHandlerCallback;
- private CarDiagnosticEventListenerToService mListenerToService;
+ private final CarDiagnosticEventListenerToService mListenerToService;
private final CarPermission mVendorExtensionPermission;
/** @hide */
- public CarDiagnosticManager(IBinder service, Context context, Handler handler) {
+ public CarDiagnosticManager(Car car, IBinder service) {
+ super(car);
mService = ICarDiagnostic.Stub.asInterface(service);
- mHandlerCallback = new SingleMessageHandler<CarDiagnosticEvent>(handler.getLooper(),
- MSG_DIAGNOSTIC_EVENTS) {
+ mHandlerCallback = new SingleMessageHandler<CarDiagnosticEvent>(
+ getEventHandler().getLooper(), MSG_DIAGNOSTIC_EVENTS) {
@Override
protected void handleEvent(CarDiagnosticEvent event) {
CarDiagnosticListeners listeners;
@@ -90,7 +89,9 @@
}
}
};
- mVendorExtensionPermission = new CarPermission(context, Car.PERMISSION_VENDOR_EXTENSION);
+ mVendorExtensionPermission = new CarPermission(getContext(),
+ Car.PERMISSION_VENDOR_EXTENSION);
+ mListenerToService = new CarDiagnosticEventListenerToService(this);
}
@Override
@@ -98,7 +99,6 @@
public void onCarDisconnected() {
synchronized(mActiveListeners) {
mActiveListeners.clear();
- mListenerToService = null;
}
}
@@ -137,9 +137,6 @@
OnDiagnosticEventListener listener, @FrameType int frameType, int rate) {
assertFrameType(frameType);
synchronized(mActiveListeners) {
- if (null == mListenerToService) {
- mListenerToService = new CarDiagnosticEventListenerToService(this);
- }
boolean needsServerUpdate = false;
CarDiagnosticListeners listeners = mActiveListeners.get(frameType);
if (listeners == null) {
@@ -184,7 +181,8 @@
mService.unregisterDiagnosticListener(frameType,
mListenerToService);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
+ // continue for local clean-up
}
mActiveListeners.remove(frameType);
} else if (needsServerUpdate) {
@@ -197,7 +195,7 @@
try {
return mService.registerOrUpdateDiagnosticListener(frameType, rate, mListenerToService);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, false);
}
}
@@ -212,7 +210,7 @@
try {
return mService.getLatestLiveFrame();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, null);
}
}
@@ -229,7 +227,7 @@
try {
return mService.getFreezeFrameTimestamps();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, new long[0]);
}
}
@@ -246,7 +244,7 @@
try {
return mService.getFreezeFrame(timestamp);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, null);
}
}
@@ -264,7 +262,7 @@
try {
return mService.clearFreezeFrames(timestamps);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, false);
}
}
@@ -276,7 +274,7 @@
try {
return mService.isLiveFrameSupported();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, false);
}
}
@@ -288,7 +286,7 @@
try {
return mService.isFreezeFrameNotificationSupported();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, false);
}
}
@@ -300,7 +298,7 @@
try {
return mService.isGetFreezeFrameSupported();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, false);
}
}
@@ -318,7 +316,7 @@
try {
return mService.isClearFreezeFramesSupported();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, false);
}
}
@@ -336,7 +334,7 @@
try {
return mService.isSelectiveClearFreezeFramesSupported();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, false);
}
}
diff --git a/car-lib/src/android/car/drivingstate/CarDrivingStateManager.java b/car-lib/src/android/car/drivingstate/CarDrivingStateManager.java
index 9b0626f..4053c5c 100644
--- a/car-lib/src/android/car/drivingstate/CarDrivingStateManager.java
+++ b/car-lib/src/android/car/drivingstate/CarDrivingStateManager.java
@@ -22,7 +22,6 @@
import android.annotation.TestApi;
import android.car.Car;
import android.car.CarManagerBase;
-import android.content.Context;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -40,13 +39,12 @@
*/
@SystemApi
@TestApi
-public final class CarDrivingStateManager implements CarManagerBase {
+public final class CarDrivingStateManager extends CarManagerBase {
private static final String TAG = "CarDrivingStateMgr";
private static final boolean DBG = false;
private static final boolean VDBG = false;
private static final int MSG_HANDLE_DRIVING_STATE_CHANGE = 0;
- private final Context mContext;
private final ICarDrivingState mDrivingService;
private final EventCallbackHandler mEventCallbackHandler;
private CarDrivingStateEventListener mDrvStateEventListener;
@@ -54,10 +52,10 @@
/** @hide */
- public CarDrivingStateManager(IBinder service, Context context, Handler handler) {
- mContext = context;
+ public CarDrivingStateManager(Car car, IBinder service) {
+ super(car);
mDrivingService = ICarDrivingState.Stub.asInterface(service);
- mEventCallbackHandler = new EventCallbackHandler(this, handler.getLooper());
+ mEventCallbackHandler = new EventCallbackHandler(this, getEventHandler().getLooper());
}
/** @hide */
@@ -111,7 +109,7 @@
// register to the Service for getting notified
mDrivingService.registerDrivingStateChangeListener(mListenerToService);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -134,7 +132,7 @@
mDrvStateEventListener = null;
mListenerToService = null;
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -151,7 +149,7 @@
try {
return mDrivingService.getCurrentDrivingState();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, null);
}
}
@@ -172,7 +170,7 @@
try {
mDrivingService.injectDrivingState(event);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
diff --git a/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java b/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java
index 9a3d5cf..be194b8 100644
--- a/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java
+++ b/car-lib/src/android/car/drivingstate/CarUxRestrictionsManager.java
@@ -22,7 +22,6 @@
import android.annotation.RequiresPermission;
import android.car.Car;
import android.car.CarManagerBase;
-import android.content.Context;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -43,7 +42,7 @@
* API to register and get the User Experience restrictions imposed based on the car's driving
* state.
*/
-public final class CarUxRestrictionsManager implements CarManagerBase {
+public final class CarUxRestrictionsManager extends CarManagerBase {
private static final String TAG = "CarUxRManager";
private static final boolean DBG = false;
private static final boolean VDBG = false;
@@ -80,7 +79,6 @@
@Retention(RetentionPolicy.SOURCE)
public @interface UxRestrictionMode {}
- private final Context mContext;
private int mDisplayId = Display.INVALID_DISPLAY;
private final ICarUxRestrictionsManager mUxRService;
private final EventCallbackHandler mEventCallbackHandler;
@@ -89,10 +87,11 @@
private CarUxRestrictionsChangeListenerToService mListenerToService;
/** @hide */
- public CarUxRestrictionsManager(IBinder service, Context context, Handler handler) {
- mContext = context;
+ public CarUxRestrictionsManager(Car car, IBinder service) {
+ super(car);
mUxRService = ICarUxRestrictionsManager.Stub.asInterface(service);
- mEventCallbackHandler = new EventCallbackHandler(this, handler.getLooper());
+ mEventCallbackHandler = new EventCallbackHandler(this,
+ getEventHandler().getLooper());
}
/** @hide */
@@ -152,7 +151,7 @@
// register to the Service to listen for changes.
mUxRService.registerUxRestrictionsChangeListener(mListenerToService, displayId);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -172,7 +171,7 @@
try {
mUxRService.unregisterUxRestrictionsChangeListener(mListenerToService);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -197,7 +196,7 @@
try {
return mUxRService.saveUxRestrictionsConfigurationForNextBoot(configs);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, false);
}
}
@@ -219,7 +218,7 @@
try {
return mUxRService.getCurrentUxRestrictions(displayId);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, null);
}
}
@@ -233,7 +232,7 @@
try {
return mUxRService.setRestrictionMode(mode);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, false);
}
}
@@ -248,7 +247,7 @@
try {
return mUxRService.getRestrictionMode();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, null);
}
}
@@ -288,7 +287,7 @@
try {
return mUxRService.getStagedConfigs();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, null);
}
}
@@ -304,7 +303,7 @@
try {
return mUxRService.getConfigs();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, null);
}
}
@@ -399,7 +398,7 @@
return mDisplayId;
}
- mDisplayId = mContext.getDisplayId();
+ mDisplayId = getContext().getDisplayId();
Log.i(TAG, "Context returns display ID " + mDisplayId);
if (mDisplayId == Display.INVALID_DISPLAY) {
diff --git a/car-lib/src/android/car/hardware/CarSensorManager.java b/car-lib/src/android/car/hardware/CarSensorManager.java
index d61cb2e..082c8eb 100644
--- a/car-lib/src/android/car/hardware/CarSensorManager.java
+++ b/car-lib/src/android/car/hardware/CarSensorManager.java
@@ -26,9 +26,7 @@
import android.car.hardware.property.CarPropertyManager;
import android.car.hardware.property.CarPropertyManager.CarPropertyEventCallback;
import android.car.hardware.property.ICarProperty;
-import android.content.Context;
import android.os.Bundle;
-import android.os.Handler;
import android.os.IBinder;
import android.util.ArraySet;
import android.util.Log;
@@ -46,7 +44,7 @@
* API for monitoring car sensor data.
*/
@Deprecated
-public final class CarSensorManager implements CarManagerBase {
+public final class CarSensorManager extends CarManagerBase {
private static final String TAG = "CarSensorManager";
private final CarPropertyManager mCarPropertyMgr;
/** @hide */
@@ -304,9 +302,10 @@
}
/** @hide */
- public CarSensorManager(IBinder service, Context context, Handler handler) {
+ public CarSensorManager(Car car, IBinder service) {
+ super(car);
ICarProperty mCarPropertyService = ICarProperty.Stub.asInterface(service);
- mCarPropertyMgr = new CarPropertyManager(mCarPropertyService, handler);
+ mCarPropertyMgr = new CarPropertyManager(car, mCarPropertyService);
}
/** @hide */
diff --git a/car-lib/src/android/car/hardware/CarVendorExtensionManager.java b/car-lib/src/android/car/hardware/CarVendorExtensionManager.java
index e7df3b0..b796156 100644
--- a/car-lib/src/android/car/hardware/CarVendorExtensionManager.java
+++ b/car-lib/src/android/car/hardware/CarVendorExtensionManager.java
@@ -22,7 +22,6 @@
import android.car.hardware.property.CarPropertyManager;
import android.car.hardware.property.CarPropertyManager.CarPropertyEventCallback;
import android.car.hardware.property.ICarProperty;
-import android.os.Handler;
import android.os.IBinder;
import android.util.ArraySet;
@@ -44,7 +43,7 @@
*/
@Deprecated
@SystemApi
-public final class CarVendorExtensionManager implements CarManagerBase {
+public final class CarVendorExtensionManager extends CarManagerBase {
private final static boolean DBG = false;
private final static String TAG = CarVendorExtensionManager.class.getSimpleName();
@@ -84,9 +83,10 @@
* <p>Should not be obtained directly by clients, use {@link Car#getCarManager(String)} instead.
* @hide
*/
- public CarVendorExtensionManager(IBinder service, Handler handler) {
+ public CarVendorExtensionManager(Car car, IBinder service) {
+ super(car);
ICarProperty mCarPropertyService = ICarProperty.Stub.asInterface(service);
- mPropertyManager = new CarPropertyManager(mCarPropertyService, handler);
+ mPropertyManager = new CarPropertyManager(car, mCarPropertyService);
}
/**
@@ -206,6 +206,9 @@
/** @hide */
@Override
public void onCarDisconnected() {
+ synchronized (mLock) {
+ mCallbacks.clear();
+ }
mPropertyManager.onCarDisconnected();
}
private static class CarPropertyEventListenerToBase implements CarPropertyEventCallback {
diff --git a/car-lib/src/android/car/hardware/cabin/CarCabinManager.java b/car-lib/src/android/car/hardware/cabin/CarCabinManager.java
index 1c41a2b..7318176 100644
--- a/car-lib/src/android/car/hardware/cabin/CarCabinManager.java
+++ b/car-lib/src/android/car/hardware/cabin/CarCabinManager.java
@@ -25,8 +25,6 @@
import android.car.hardware.property.CarPropertyManager;
import android.car.hardware.property.CarPropertyManager.CarPropertyEventCallback;
import android.car.hardware.property.ICarProperty;
-import android.content.Context;
-import android.os.Handler;
import android.os.IBinder;
import android.util.ArraySet;
@@ -58,7 +56,7 @@
*/
@Deprecated
@SystemApi
-public final class CarCabinManager implements CarManagerBase {
+public final class CarCabinManager extends CarManagerBase {
private final static boolean DBG = false;
private final static String TAG = "CarCabinManager";
private final CarPropertyManager mCarPropertyMgr;
@@ -470,9 +468,10 @@
* @param handler
* @hide
*/
- public CarCabinManager(IBinder service, Context context, Handler handler) {
+ public CarCabinManager(Car car, IBinder service) {
+ super(car);
ICarProperty mCarPropertyService = ICarProperty.Stub.asInterface(service);
- mCarPropertyMgr = new CarPropertyManager(mCarPropertyService, handler);
+ mCarPropertyMgr = new CarPropertyManager(car, mCarPropertyService);
}
/**
@@ -594,6 +593,10 @@
/** @hide */
@Override
public void onCarDisconnected() {
+ // TODO(b/142730969) Fix synchronization to use separate mLock
+ synchronized (this) {
+ mCallbacks.clear();
+ }
mCarPropertyMgr.onCarDisconnected();
}
}
diff --git a/car-lib/src/android/car/hardware/hvac/CarHvacManager.java b/car-lib/src/android/car/hardware/hvac/CarHvacManager.java
index b2b8014..3ab7631 100644
--- a/car-lib/src/android/car/hardware/hvac/CarHvacManager.java
+++ b/car-lib/src/android/car/hardware/hvac/CarHvacManager.java
@@ -25,8 +25,6 @@
import android.car.hardware.property.CarPropertyManager;
import android.car.hardware.property.CarPropertyManager.CarPropertyEventCallback;
import android.car.hardware.property.ICarProperty;
-import android.content.Context;
-import android.os.Handler;
import android.os.IBinder;
import android.util.ArraySet;
import android.util.Log;
@@ -46,7 +44,7 @@
*/
@Deprecated
@SystemApi
-public final class CarHvacManager implements CarManagerBase {
+public final class CarHvacManager extends CarManagerBase {
private final static boolean DBG = false;
private final static String TAG = "CarHvacManager";
private final CarPropertyManager mCarPropertyMgr;
@@ -301,14 +299,15 @@
*
* Should not be obtained directly by clients, use {@link Car#getCarManager(String)} instead.
* @param service
- * @param context
- * @param handler
+ *
* @hide
*/
- public CarHvacManager(IBinder service, Context context, Handler handler) {
+ public CarHvacManager(Car car, IBinder service) {
+ super(car);
ICarProperty mCarPropertyService = ICarProperty.Stub.asInterface(service);
- mCarPropertyMgr = new CarPropertyManager(mCarPropertyService, handler);
+ mCarPropertyMgr = new CarPropertyManager(car, mCarPropertyService);
}
+
/**
* Implement wrappers for contained CarPropertyManager object
* @param callback
@@ -432,6 +431,10 @@
/** @hide */
public void onCarDisconnected() {
+ // TODO(b/142730482) Fix synchronization to use separate mLock
+ synchronized (this) {
+ mCallbacks.clear();
+ }
mCarPropertyMgr.onCarDisconnected();
}
}
diff --git a/car-lib/src/android/car/hardware/power/CarPowerManager.java b/car-lib/src/android/car/hardware/power/CarPowerManager.java
index 3d9a23a..4b0e8cf 100644
--- a/car-lib/src/android/car/hardware/power/CarPowerManager.java
+++ b/car-lib/src/android/car/hardware/power/CarPowerManager.java
@@ -19,8 +19,6 @@
import android.annotation.SystemApi;
import android.car.Car;
import android.car.CarManagerBase;
-import android.content.Context;
-import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
@@ -35,15 +33,18 @@
* @hide
*/
@SystemApi
-public class CarPowerManager implements CarManagerBase {
+public class CarPowerManager extends CarManagerBase {
private final static boolean DBG = false;
private final static String TAG = "CarPowerManager";
private final Object mLock = new Object();
private final ICarPower mService;
+ @GuardedBy("mLock")
private CarPowerStateListener mListener;
+ @GuardedBy("mLock")
private CarPowerStateListenerWithCompletion mListenerWithCompletion;
+ @GuardedBy("mLock")
private CompletableFuture<Void> mFuture;
@GuardedBy("mLock")
private ICarPowerStateListener mListenerToService;
@@ -131,7 +132,8 @@
* @param handler
* @hide
*/
- public CarPowerManager(IBinder service, Context context, Handler handler) {
+ public CarPowerManager(Car car, IBinder service) {
+ super(car);
mService = ICarPower.Stub.asInterface(service);
}
@@ -143,7 +145,7 @@
try {
mService.requestShutdownOnNextSuspend();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -155,7 +157,7 @@
try {
mService.scheduleNextWakeupTime(seconds);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -209,13 +211,23 @@
@Override
public void onStateChanged(int state) throws RemoteException {
if (useCompletion) {
- // Update CompletableFuture. This will recreate it or just clean it up.
- updateFuture(state);
+ CarPowerStateListenerWithCompletion listenerWithCompletion;
+ CompletableFuture<Void> future;
+ synchronized (mLock) {
+ // Update CompletableFuture. This will recreate it or just clean it up.
+ updateFutureLocked(state);
+ listenerWithCompletion = mListenerWithCompletion;
+ future = mFuture;
+ }
// Notify user that the state has changed and supply a future
- mListenerWithCompletion.onStateChanged(state, mFuture);
+ listenerWithCompletion.onStateChanged(state, future);
} else {
+ CarPowerStateListener listener;
+ synchronized (mLock) {
+ listener = mListener;
+ }
// Notify the user without supplying a future
- mListener.onStateChanged(state);
+ listener.onStateChanged(state);
}
}
};
@@ -227,7 +239,7 @@
}
mListenerToService = listenerToService;
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
}
@@ -243,7 +255,7 @@
mListenerToService = null;
mListener = null;
mListenerWithCompletion = null;
- cleanupFuture();
+ cleanupFutureLocked();
}
if (listenerToService == null) {
@@ -254,12 +266,12 @@
try {
mService.unregisterListener(listenerToService);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
- private void updateFuture(int state) {
- cleanupFuture();
+ private void updateFutureLocked(int state) {
+ cleanupFutureLocked();
if (state == CarPowerStateListener.SHUTDOWN_PREPARE) {
// Create a CompletableFuture and pass it to the listener.
// When the listener completes the future, tell
@@ -269,16 +281,20 @@
if (exception != null && !(exception instanceof CancellationException)) {
Log.e(TAG, "Exception occurred while waiting for future", exception);
}
+ ICarPowerStateListener listenerToService;
+ synchronized (mLock) {
+ listenerToService = mListenerToService;
+ }
try {
- mService.finished(mListenerToService);
+ mService.finished(listenerToService);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
});
}
}
- private void cleanupFuture() {
+ private void cleanupFutureLocked() {
if (mFuture != null) {
if (!mFuture.isDone()) {
mFuture.cancel(false);
@@ -290,13 +306,9 @@
/** @hide */
@Override
public void onCarDisconnected() {
- ICarPowerStateListener listenerToService;
synchronized (mLock) {
- listenerToService = mListenerToService;
- }
-
- if (listenerToService != null) {
- clearListener();
+ mListener = null;
+ mListenerWithCompletion = null;
}
}
}
diff --git a/car-lib/src/android/car/hardware/property/CarPropertyManager.java b/car-lib/src/android/car/hardware/property/CarPropertyManager.java
index 3f7da1d..e3651da 100644
--- a/car-lib/src/android/car/hardware/property/CarPropertyManager.java
+++ b/car-lib/src/android/car/hardware/property/CarPropertyManager.java
@@ -21,6 +21,7 @@
import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.car.Car;
import android.car.CarManagerBase;
import android.car.hardware.CarPropertyConfig;
import android.car.hardware.CarPropertyValue;
@@ -44,7 +45,7 @@
* For details about the individual properties, see the descriptions in
* hardware/interfaces/automotive/vehicle/types.hal
*/
-public class CarPropertyManager implements CarManagerBase {
+public class CarPropertyManager extends CarManagerBase {
private static final boolean DBG = false;
private static final String TAG = "CarPropertyManager";
private static final int MSG_GENERIC_EVENT = 0;
@@ -93,11 +94,12 @@
* Get an instance of the CarPropertyManager.
*
* Should not be obtained directly by clients, use {@link Car#getCarManager(String)} instead.
+ * @param car Car instance
* @param service ICarProperty instance
- * @param handler The handler to deal with CarPropertyEvent.
* @hide
*/
- public CarPropertyManager(@NonNull ICarProperty service, @Nullable Handler handler) {
+ public CarPropertyManager(Car car, @NonNull ICarProperty service) {
+ super(car);
mService = service;
try {
List<CarPropertyConfig> configs = mService.getPropertyList();
@@ -108,11 +110,12 @@
Log.e(TAG, "getPropertyList exception ", e);
throw new RuntimeException(e);
}
- if (handler == null) {
+ Handler eventHandler = getEventHandler();
+ if (eventHandler == null) {
mHandler = null;
return;
}
- mHandler = new SingleMessageHandler<CarPropertyEvent>(handler.getLooper(),
+ mHandler = new SingleMessageHandler<CarPropertyEvent>(eventHandler.getLooper(),
MSG_GENERIC_EVENT) {
@Override
protected void handleEvent(CarPropertyEvent event) {
@@ -206,7 +209,7 @@
try {
mService.registerListener(propertyId, rate, mCarPropertyEventToService);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, false);
}
return true;
}
@@ -274,7 +277,8 @@
try {
mService.unregisterListener(propertyId, mCarPropertyEventToService);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
+ // continue for local clean-up
}
mActivePropertyListener.remove(propertyId);
} else if (needsServerUpdate) {
@@ -327,7 +331,7 @@
try {
return mService.getReadPermission(propId);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, "");
}
}
@@ -346,7 +350,7 @@
try {
return mService.getWritePermission(propId);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, "");
}
}
@@ -363,7 +367,7 @@
return (propValue != null)
&& (propValue.getStatus() == CarPropertyValue.STATUS_AVAILABLE);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, false);
}
}
@@ -449,7 +453,7 @@
}
return propVal;
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, null);
}
}
@@ -466,7 +470,7 @@
CarPropertyValue<E> propVal = mService.getProperty(propId, areaId);
return propVal;
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, null);
}
}
@@ -488,7 +492,7 @@
try {
mService.setProperty(new CarPropertyValue<>(propId, areaId, val));
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
diff --git a/car-lib/src/android/car/input/CarInputHandlingService.java b/car-lib/src/android/car/input/CarInputHandlingService.java
index 518cee1..0ea990f 100644
--- a/car-lib/src/android/car/input/CarInputHandlingService.java
+++ b/car-lib/src/android/car/input/CarInputHandlingService.java
@@ -19,6 +19,7 @@
import android.annotation.MainThread;
import android.annotation.SystemApi;
import android.app.Service;
+import android.car.Car;
import android.car.CarLibLog;
import android.content.Intent;
import android.os.Bundle;
@@ -101,7 +102,7 @@
try {
callbackBinder.transact(INPUT_CALLBACK_BINDER_CODE, dataIn, null, IBinder.FLAG_ONEWAY);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ Car.handleRemoteExceptionFromCarService(this, e);
}
}
diff --git a/car-lib/src/android/car/media/CarAudioManager.java b/car-lib/src/android/car/media/CarAudioManager.java
index dcd4514..2bc8fd7 100644
--- a/car-lib/src/android/car/media/CarAudioManager.java
+++ b/car-lib/src/android/car/media/CarAudioManager.java
@@ -22,10 +22,8 @@
import android.car.Car;
import android.car.CarLibLog;
import android.car.CarManagerBase;
-import android.content.Context;
import android.media.AudioAttributes;
import android.os.Bundle;
-import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
@@ -52,7 +50,7 @@
* - There is exactly one audio zone, which is the primary zone
* - Each volume group represents a controllable STREAM_TYPE, same as AudioManager
*/
-public final class CarAudioManager implements CarManagerBase {
+public final class CarAudioManager extends CarManagerBase {
/**
* Zone id of the primary audio zone.
@@ -114,7 +112,7 @@
try {
return mService.isDynamicRoutingEnabled();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, false);
}
}
@@ -147,7 +145,7 @@
try {
mService.setGroupVolume(zoneId, groupId, index, flags);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -177,7 +175,7 @@
try {
return mService.getGroupMaxVolume(zoneId, groupId);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, 0);
}
}
@@ -207,7 +205,7 @@
try {
return mService.getGroupMinVolume(zoneId, groupId);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, 0);
}
}
@@ -240,7 +238,7 @@
try {
return mService.getGroupVolume(zoneId, groupId);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, 0);
}
}
@@ -259,7 +257,7 @@
try {
mService.setFadeTowardFront(value);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -278,7 +276,7 @@
try {
mService.setBalanceTowardRight(value);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -300,7 +298,8 @@
try {
return mService.getExternalSources();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
+ return new String[0];
}
}
@@ -330,7 +329,7 @@
try {
return mService.createAudioPatch(sourceAddress, usage, gainInMillibels);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, null);
}
}
@@ -350,7 +349,7 @@
try {
mService.releaseAudioPatch(patch);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -379,7 +378,7 @@
try {
return mService.getVolumeGroupCount(zoneId);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, 0);
}
}
@@ -409,7 +408,7 @@
try {
return mService.getVolumeGroupIdForUsage(zoneId, usage);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, 0);
}
}
@@ -436,7 +435,7 @@
try {
return mService.getAudioZoneIds();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, new int[0]);
}
}
@@ -453,7 +452,7 @@
try {
return mService.getZoneIdForUid(uid);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, 0);
}
}
@@ -470,7 +469,7 @@
try {
return mService.setZoneIdForUid(zoneId, uid);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, false);
}
}
@@ -486,7 +485,7 @@
try {
return mService.clearZoneIdForUid(uid);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, false);
}
}
@@ -523,7 +522,7 @@
try {
return mService.getZoneIdForDisplayPortId(displayPortId);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, 0);
}
}
@@ -541,7 +540,7 @@
try {
return mService.getUsagesForVolumeGroupId(zoneId, groupId);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, new int[0]);
}
}
@@ -552,16 +551,16 @@
try {
mService.unregisterVolumeCallback(mCarVolumeCallbackImpl.asBinder());
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
}
/** @hide */
- public CarAudioManager(IBinder service, Context context, Handler handler) {
+ public CarAudioManager(Car car, IBinder service) {
+ super(car);
mService = ICarAudio.Stub.asInterface(service);
mCarVolumeCallbacks = new ArrayList<>();
-
try {
mService.registerVolumeCallback(mCarVolumeCallbackImpl.asBinder());
} catch (RemoteException e) {
diff --git a/car-lib/src/android/car/media/CarMediaManager.java b/car-lib/src/android/car/media/CarMediaManager.java
index 12c2dc8..8537ed6 100644
--- a/car-lib/src/android/car/media/CarMediaManager.java
+++ b/car-lib/src/android/car/media/CarMediaManager.java
@@ -29,7 +29,7 @@
* API for updating and receiving updates to the primary media source in the car.
* @hide
*/
-public final class CarMediaManager implements CarManagerBase {
+public final class CarMediaManager extends CarManagerBase {
private final ICarMedia mService;
private Map<MediaSourceChangedListener, ICarMediaSourceListener> mCallbackMap = new HashMap();
@@ -40,7 +40,8 @@
* Should not be obtained directly by clients, use {@link Car#getCarManager(String)} instead.
* @hide
*/
- public CarMediaManager(IBinder service) {
+ public CarMediaManager(Car car, IBinder service) {
+ super(car);
mService = ICarMedia.Stub.asInterface(service);
}
@@ -67,7 +68,7 @@
try {
return mService.getMediaSource();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, null);
}
}
@@ -81,7 +82,7 @@
try {
mService.setMediaSource(componentName);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -102,7 +103,7 @@
mCallbackMap.put(callback, binderCallback);
mService.registerMediaSourceListener(binderCallback);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -117,12 +118,14 @@
ICarMediaSourceListener binderCallback = mCallbackMap.remove(callback);
mService.unregisterMediaSourceListener(binderCallback);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
/** @hide */
@Override
public synchronized void onCarDisconnected() {
+ // TODO(b/142733057) Fix synchronization to use separate mLock
+ mCallbackMap.clear();
}
}
diff --git a/car-lib/src/android/car/navigation/CarNavigationStatusManager.java b/car-lib/src/android/car/navigation/CarNavigationStatusManager.java
index a70e3c8..2aa2f10 100644
--- a/car-lib/src/android/car/navigation/CarNavigationStatusManager.java
+++ b/car-lib/src/android/car/navigation/CarNavigationStatusManager.java
@@ -24,14 +24,13 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
-import android.util.Log;
/**
* API for providing navigation status for instrument cluster.
* @hide
*/
@SystemApi
-public final class CarNavigationStatusManager implements CarManagerBase {
+public final class CarNavigationStatusManager extends CarManagerBase {
private static final String TAG = CarLibLog.TAG_NAV;
private final IInstrumentClusterNavigation mService;
@@ -40,7 +39,8 @@
* Only for CarServiceLoader
* @hide
*/
- public CarNavigationStatusManager(IBinder service) {
+ public CarNavigationStatusManager(Car car, IBinder service) {
+ super(car);
mService = IInstrumentClusterNavigation.Stub.asInterface(service);
}
@@ -67,14 +67,13 @@
try {
mService.onNavigationStateChanged(bundle);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
/** @hide */
@Override
public void onCarDisconnected() {
- Log.e(TAG, "Car service disconnected");
}
/** Returns navigation features of instrument cluster */
@@ -83,7 +82,7 @@
try {
return mService.getInstrumentClusterInfo();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, null);
}
}
}
diff --git a/car-lib/src/android/car/settings/CarConfigurationManager.java b/car-lib/src/android/car/settings/CarConfigurationManager.java
index 34d5f4a..626ad39 100644
--- a/car-lib/src/android/car/settings/CarConfigurationManager.java
+++ b/car-lib/src/android/car/settings/CarConfigurationManager.java
@@ -16,6 +16,7 @@
package android.car.settings;
+import android.car.Car;
import android.car.CarManagerBase;
import android.os.IBinder;
import android.os.RemoteException;
@@ -23,13 +24,14 @@
/**
* Manager that exposes car configuration values that are stored on the system.
*/
-public class CarConfigurationManager implements CarManagerBase {
+public class CarConfigurationManager extends CarManagerBase {
private static final String TAG = "CarConfigurationManager";
private final ICarConfigurationManager mConfigurationService;
/** @hide */
- public CarConfigurationManager(IBinder service) {
+ public CarConfigurationManager(Car car, IBinder service) {
+ super(car);
mConfigurationService = ICarConfigurationManager.Stub.asInterface(service);
}
@@ -42,7 +44,7 @@
try {
return mConfigurationService.getSpeedBumpConfiguration();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, null);
}
}
diff --git a/car-lib/src/android/car/storagemonitoring/CarStorageMonitoringManager.java b/car-lib/src/android/car/storagemonitoring/CarStorageMonitoringManager.java
index ff7b099..69c092b 100644
--- a/car-lib/src/android/car/storagemonitoring/CarStorageMonitoringManager.java
+++ b/car-lib/src/android/car/storagemonitoring/CarStorageMonitoringManager.java
@@ -19,7 +19,6 @@
import android.annotation.SystemApi;
import android.car.Car;
import android.car.CarManagerBase;
-import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
@@ -37,7 +36,7 @@
* @hide
*/
@SystemApi
-public final class CarStorageMonitoringManager implements CarManagerBase {
+public final class CarStorageMonitoringManager extends CarManagerBase {
private static final String TAG = CarStorageMonitoringManager.class.getSimpleName();
private static final int MSG_IO_STATS_EVENT = 0;
@@ -77,9 +76,10 @@
/**
* @hide
*/
- public CarStorageMonitoringManager(IBinder service, Handler handler) {
+ public CarStorageMonitoringManager(Car car, IBinder service) {
+ super(car);
mService = ICarStorageMonitoring.Stub.asInterface(service);
- mMessageHandler = new SingleMessageHandler<IoStats>(handler, MSG_IO_STATS_EVENT) {
+ mMessageHandler = new SingleMessageHandler<IoStats>(getEventHandler(), MSG_IO_STATS_EVENT) {
@Override
protected void handleEvent(IoStats event) {
for (IoStatsListener listener : mListeners) {
@@ -112,7 +112,7 @@
try {
return mService.getPreEolIndicatorStatus();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, PRE_EOL_INFO_UNKNOWN);
}
}
@@ -130,7 +130,7 @@
try {
return mService.getWearEstimate();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, null);
}
}
@@ -150,7 +150,7 @@
try {
return mService.getWearEstimateHistory();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, Collections.emptyList());
}
}
@@ -169,7 +169,7 @@
try {
return mService.getBootIoStats();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, Collections.emptyList());
}
}
@@ -199,7 +199,7 @@
try {
return mService.getShutdownDiskWriteAmount();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, 0);
}
}
@@ -216,7 +216,7 @@
try {
return mService.getAggregateIoStats();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, Collections.emptyList());
}
}
@@ -236,7 +236,7 @@
try {
return mService.getIoStatsDeltas();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, Collections.emptyList());
}
}
@@ -259,7 +259,7 @@
}
mListeners.add(listener);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -277,7 +277,7 @@
mListenerToService = null;
}
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
}
diff --git a/car-lib/src/android/car/test/CarTestManagerBinderWrapper.java b/car-lib/src/android/car/test/CarTestManagerBinderWrapper.java
index 5e39ea3..0a167cf 100644
--- a/car-lib/src/android/car/test/CarTestManagerBinderWrapper.java
+++ b/car-lib/src/android/car/test/CarTestManagerBinderWrapper.java
@@ -15,6 +15,7 @@
*/
package android.car.test;
+import android.car.Car;
import android.car.CarManagerBase;
import android.os.IBinder;
@@ -22,10 +23,17 @@
* Only for system testing
* @hide
*/
-public class CarTestManagerBinderWrapper implements CarManagerBase {
+public class CarTestManagerBinderWrapper extends CarManagerBase {
public final IBinder binder;
public CarTestManagerBinderWrapper(IBinder binder) {
+ super(null); // This will not work safely but is only for keeping API.
+ this.binder = binder;
+ }
+
+ /** @hide */
+ public CarTestManagerBinderWrapper(Car car, IBinder binder) {
+ super(car);
this.binder = binder;
}
diff --git a/car-lib/src/android/car/trust/CarTrustAgentEnrollmentManager.java b/car-lib/src/android/car/trust/CarTrustAgentEnrollmentManager.java
index c82d515..9881420 100644
--- a/car-lib/src/android/car/trust/CarTrustAgentEnrollmentManager.java
+++ b/car-lib/src/android/car/trust/CarTrustAgentEnrollmentManager.java
@@ -24,8 +24,8 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.bluetooth.BluetoothDevice;
+import android.car.Car;
import android.car.CarManagerBase;
-import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -39,6 +39,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
+import java.util.Collections;
import java.util.List;
@@ -67,7 +68,7 @@
* @hide
*/
@SystemApi
-public final class CarTrustAgentEnrollmentManager implements CarManagerBase {
+public final class CarTrustAgentEnrollmentManager extends CarManagerBase {
private static final String TAG = "CarTrustEnrollMgr";
private static final String KEY_HANDLE = "handle";
private static final String KEY_ACTIVE = "active";
@@ -81,7 +82,6 @@
private static final int MSG_ENROLL_TOKEN_STATE_CHANGED = 7;
private static final int MSG_ENROLL_TOKEN_REMOVED = 8;
- private final Context mContext;
private final ICarTrustAgentEnrollment mEnrollmentService;
private Object mListenerLock = new Object();
@GuardedBy("mListenerLock")
@@ -114,10 +114,10 @@
/** @hide */
- public CarTrustAgentEnrollmentManager(IBinder service, Context context, Handler handler) {
- mContext = context;
+ public CarTrustAgentEnrollmentManager(Car car, IBinder service) {
+ super(car);
mEnrollmentService = ICarTrustAgentEnrollment.Stub.asInterface(service);
- mEventCallbackHandler = new EventCallbackHandler(this, handler.getLooper());
+ mEventCallbackHandler = new EventCallbackHandler(this, getEventHandler().getLooper());
}
/** @hide */
@@ -134,7 +134,7 @@
try {
mEnrollmentService.startEnrollmentAdvertising();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -146,7 +146,7 @@
try {
mEnrollmentService.stopEnrollmentAdvertising();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -161,7 +161,7 @@
try {
mEnrollmentService.enrollmentHandshakeAccepted(device);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -173,7 +173,7 @@
try {
mEnrollmentService.terminateEnrollmentHandshake();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -194,7 +194,7 @@
try {
return mEnrollmentService.isEscrowTokenActive(handle, uid);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, false);
}
}
@@ -209,7 +209,7 @@
try {
mEnrollmentService.removeEscrowToken(handle, uid);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -223,7 +223,7 @@
try {
mEnrollmentService.removeAllTrustedDevices(uid);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -238,7 +238,7 @@
try {
mEnrollmentService.setTrustedDeviceEnrollmentEnabled(isEnabled);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -253,7 +253,7 @@
try {
mEnrollmentService.setTrustedDeviceUnlockEnabled(isEnabled);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -278,7 +278,7 @@
mEnrollmentService.registerEnrollmentCallback(mListenerToEnrollmentService);
mEnrollmentCallback = callback;
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
}
@@ -290,7 +290,7 @@
try {
mEnrollmentService.unregisterEnrollmentCallback(mListenerToEnrollmentService);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
mEnrollmentCallback = null;
}
@@ -318,7 +318,7 @@
mEnrollmentService.registerBleCallback(mListenerToBleService);
mBleCallback = callback;
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
}
@@ -330,7 +330,7 @@
try {
mEnrollmentService.unregisterBleCallback(mListenerToBleService);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
mBleCallback = null;
}
@@ -351,7 +351,7 @@
try {
return mEnrollmentService.getEnrolledDeviceInfosForUser(uid);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, Collections.emptyList());
}
}
diff --git a/car-lib/src/android/car/vms/VmsPublisherClientService.java b/car-lib/src/android/car/vms/VmsPublisherClientService.java
index 309d0ee..ea75707 100644
--- a/car-lib/src/android/car/vms/VmsPublisherClientService.java
+++ b/car-lib/src/android/car/vms/VmsPublisherClientService.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.app.Service;
+import android.car.Car;
import android.content.Intent;
import android.os.Binder;
import android.os.Build;
@@ -114,7 +115,7 @@
try {
mVmsPublisherService.publish(token, layer, publisherId, payload);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ Car.handleRemoteExceptionFromCarService(this, e);
}
}
@@ -134,7 +135,7 @@
mVmsPublisherService.setLayersOffering(token, offering);
VmsOperationRecorder.get().setLayersOffering(offering);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ Car.handleRemoteExceptionFromCarService(this, e);
}
}
@@ -172,6 +173,7 @@
publisherId = mVmsPublisherService.getPublisherId(publisherInfo);
Log.i(TAG, "Assigned publisher ID: " + publisherId);
} catch (RemoteException e) {
+ // This will crash. To prevent crash, safer invalid return value should be defined.
throw e.rethrowFromSystemServer();
}
VmsOperationRecorder.get().getPublisherId(publisherId);
@@ -191,7 +193,7 @@
try {
return mVmsPublisherService.getSubscriptions();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return Car.handleRemoteExceptionFromCarService(this, e, null);
}
}
diff --git a/car-lib/src/android/car/vms/VmsSubscriberManager.java b/car-lib/src/android/car/vms/VmsSubscriberManager.java
index c02d3a5..edde982 100644
--- a/car-lib/src/android/car/vms/VmsSubscriberManager.java
+++ b/car-lib/src/android/car/vms/VmsSubscriberManager.java
@@ -19,6 +19,7 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.car.Car;
import android.car.CarManagerBase;
import android.os.Binder;
import android.os.IBinder;
@@ -39,7 +40,7 @@
* @hide
*/
@SystemApi
-public final class VmsSubscriberManager implements CarManagerBase {
+public final class VmsSubscriberManager extends CarManagerBase {
private static final String TAG = "VmsSubscriberManager";
private final IVmsSubscriberService mVmsSubscriberService;
@@ -75,7 +76,8 @@
*
* @hide
*/
- public VmsSubscriberManager(IBinder service) {
+ public VmsSubscriberManager(Car car, IBinder service) {
+ super(car);
mVmsSubscriberService = IVmsSubscriberService.Stub.asInterface(service);
mSubscriberManagerClient = new IVmsSubscriberClient.Stub() {
@Override
@@ -133,7 +135,7 @@
try {
mVmsSubscriberService.addVmsSubscriberToNotifications(mSubscriberManagerClient);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -148,7 +150,7 @@
try {
mVmsSubscriberService.removeVmsSubscriberToNotifications(mSubscriberManagerClient);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
} finally {
synchronized (mClientCallbackLock) {
mClientCallback = null;
@@ -168,7 +170,7 @@
try {
return mVmsSubscriberService.getPublisherInfo(publisherId);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, null);
}
}
@@ -182,7 +184,7 @@
try {
return mVmsSubscriberService.getAvailableLayers();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ return handleRemoteExceptionFromCarService(e, null);
}
}
@@ -199,7 +201,7 @@
mVmsSubscriberService.addVmsSubscriber(mSubscriberManagerClient, layer);
VmsOperationRecorder.get().subscribe(layer);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -218,7 +220,7 @@
mSubscriberManagerClient, layer, publisherId);
VmsOperationRecorder.get().subscribe(layer, publisherId);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -231,7 +233,7 @@
mVmsSubscriberService.addVmsSubscriberPassive(mSubscriberManagerClient);
VmsOperationRecorder.get().startMonitoring();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -248,7 +250,7 @@
mVmsSubscriberService.removeVmsSubscriber(mSubscriberManagerClient, layer);
VmsOperationRecorder.get().unsubscribe(layer);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -266,7 +268,7 @@
mSubscriberManagerClient, layer, publisherId);
VmsOperationRecorder.get().unsubscribe(layer, publisherId);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -278,7 +280,7 @@
mVmsSubscriberService.removeVmsSubscriberPassive(mSubscriberManagerClient);
VmsOperationRecorder.get().stopMonitoring();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -326,5 +328,9 @@
*/
@Override
public void onCarDisconnected() {
+ synchronized (mClientCallbackLock) {
+ mClientCallback = null;
+ mExecutor = null;
+ }
}
}
diff --git a/car-systemtest-lib/src/android/car/test/CarTestManager.java b/car-systemtest-lib/src/android/car/test/CarTestManager.java
index 52b01a6..3ee1067 100644
--- a/car-systemtest-lib/src/android/car/test/CarTestManager.java
+++ b/car-systemtest-lib/src/android/car/test/CarTestManager.java
@@ -27,18 +27,19 @@
* @hide
*/
@SystemApi
-public final class CarTestManager implements CarManagerBase {
+public final class CarTestManager extends CarManagerBase {
private final ICarTest mService;
- public CarTestManager(IBinder carServiceBinder) {
+ public CarTestManager(Car car, IBinder carServiceBinder) {
+ super(car);
mService = ICarTest.Stub.asInterface(carServiceBinder);
}
@Override
public void onCarDisconnected() {
- // should not happen for embedded
+ // test will fail. nothing to do.
}
/**
@@ -52,7 +53,7 @@
try {
mService.stopCarService(token);
} catch (RemoteException e) {
- handleRemoteException(e);
+ handleRemoteExceptionFromCarService(e);
}
}
@@ -66,12 +67,7 @@
try {
mService.startCarService(token);
} catch (RemoteException e) {
- handleRemoteException(e);
+ handleRemoteExceptionFromCarService(e);
}
}
-
- private static void handleRemoteException(RemoteException e) {
- // let test fail
- throw new RuntimeException(e);
- }
}
diff --git a/car_product/sepolicy/private/bluetooth.te b/car_product/sepolicy/private/bluetooth.te
new file mode 100644
index 0000000..6ba74c2
--- /dev/null
+++ b/car_product/sepolicy/private/bluetooth.te
@@ -0,0 +1 @@
+allow bluetooth mediametrics_service:service_manager find;
diff --git a/car_product/sepolicy/public/property_contexts b/car_product/sepolicy/public/property_contexts
index 8dbe0bc..9646ac9 100644
--- a/car_product/sepolicy/public/property_contexts
+++ b/car_product/sepolicy/public/property_contexts
@@ -1 +1,3 @@
+android.car.number_pre_created_guests u:object_r:car_bootuser_prop:s0
+android.car.number_pre_created_users u:object_r:car_bootuser_prop:s0
android.car.systemuser.bootuseroverrideid u:object_r:car_bootuser_prop:s0
diff --git a/service/AndroidManifest.xml b/service/AndroidManifest.xml
index ebea889..e2db410 100644
--- a/service/AndroidManifest.xml
+++ b/service/AndroidManifest.xml
@@ -475,6 +475,16 @@
android:label="@string/car_permission_label_enroll_trust"
android:description="@string/car_permission_desc_enroll_trust" />
+ <!-- Allows a test application to control car service's testing mode.
+ This is only for platform level testing.
+ <p>Protection level: signature|privileged
+ -->
+ <permission
+ android:name="android.car.permission.CAR_TEST_SERVICE"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_car_test_service"
+ android:description="@string/car_permission_desc_car_test_service" />
+
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
<uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS" />
diff --git a/service/res/values/config.xml b/service/res/values/config.xml
index 3eb1007..81e0620 100644
--- a/service/res/values/config.xml
+++ b/service/res/values/config.xml
@@ -20,10 +20,6 @@
<!-- Resources to configure car service based on each OEM's preference. -->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Configuration to enable media center to autoplay when the media source is changed.
- If this is set to true, media will play automatically when a media source is selected and
- the selected media source supports media playback. -->
- <bool name="autoPlayOnMediaSourceChanged">false</bool>
<!-- Configuration to enable usage of dynamic audio routing. If this is set to false,
dynamic audio routing is disabled and audio works in legacy mode. It may be useful
@@ -236,4 +232,13 @@
resolve permission by itself to use any higher priority window type.
Setting this string to empty will disable the feature. -->
<string name="config_userNoticeUiService" translatable="false">com.google.android.car.kitchensink/.UserNoiticeDemoUiService</string>
+
+ <!-- Configuration to enable media center to autoplay when the media source is changed.
+ There are 3 supported configurations:
+ 0 - never play on change
+ 1 - always play
+ 2 - adaptive, play based on last remembered playback state -->
+ <integer name="config_mediaSourceChangedAutoplay">2</integer>
+ <!-- Configuration to enable media center to autoplay on boot -->
+ <integer name="config_mediaBootAutoplay">2</integer>
</resources>
diff --git a/service/res/values/strings.xml b/service/res/values/strings.xml
index ef731b8..1589180 100644
--- a/service/res/values/strings.xml
+++ b/service/res/values/strings.xml
@@ -256,6 +256,11 @@
<string name="car_permission_label_enroll_trust">Enroll Trusted Device</string>
<string name="car_permission_desc_enroll_trust">Allow Trusted Device Enrollment</string>
+ <!-- Permission text: Control car's test mode [CHAR LIMIT=NONE] -->
+ <string name="car_permission_label_car_test_service">Control car\u2019s test mode</string>
+ <!-- Permission text: Control car's test mode [CHAR LIMIT=NONE] -->
+ <string name="car_permission_desc_car_test_service">Control car\u2019s test mode</string>
+
<!-- The default name of device enrolled as trust device [CHAR LIMIT=NONE] -->
<string name="trust_device_default_name">My Device</string>
diff --git a/service/src/com/android/car/CarDrivingStateService.java b/service/src/com/android/car/CarDrivingStateService.java
index 584228d..66a2a7c 100644
--- a/service/src/com/android/car/CarDrivingStateService.java
+++ b/service/src/com/android/car/CarDrivingStateService.java
@@ -30,11 +30,15 @@
import android.content.Context;
import android.hardware.automotive.vehicle.V2_0.VehicleGear;
import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.List;
@@ -60,6 +64,8 @@
VehicleProperty.PERF_VEHICLE_SPEED,
VehicleProperty.GEAR_SELECTION,
VehicleProperty.PARKING_BRAKE_ON};
+ private final HandlerThread mClientDispatchThread;
+ private final Handler mClientDispatchHandler;
private CarDrivingStateEvent mCurrentDrivingState;
// For dumpsys logging
private final LinkedList<Utils.TransitionLog> mTransitionLogs = new LinkedList<>();
@@ -75,6 +81,9 @@
mContext = context;
mPropertyService = propertyService;
mCurrentDrivingState = createDrivingStateEvent(CarDrivingStateEvent.DRIVING_STATE_UNKNOWN);
+ mClientDispatchThread = new HandlerThread("ClientDispatchThread");
+ mClientDispatchThread.start();
+ mClientDispatchHandler = new Handler(mClientDispatchThread.getLooper());
}
@Override
@@ -316,7 +325,8 @@
* Handle events coming from {@link CarPropertyService}. Compute the driving state, map it to
* the corresponding UX Restrictions and dispatch the events to the registered clients.
*/
- private synchronized void handlePropertyEvent(CarPropertyEvent event) {
+ @VisibleForTesting
+ synchronized void handlePropertyEvent(CarPropertyEvent event) {
if (event.getEventType() != CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE) {
return;
}
@@ -387,9 +397,13 @@
if (DBG) {
Log.d(TAG, "dispatching to " + mDrivingStateClients.size() + " clients");
}
- for (DrivingStateClient client : mDrivingStateClients) {
- client.dispatchEventToClients(mCurrentDrivingState);
- }
+ // Dispatch to clients on a separate thread to prevent a deadlock
+ final CarDrivingStateEvent currentDrivingStateEvent = mCurrentDrivingState;
+ mClientDispatchHandler.post(() -> {
+ for (DrivingStateClient client : mDrivingStateClients) {
+ client.dispatchEventToClients(currentDrivingStateEvent);
+ }
+ });
}
}
diff --git a/service/src/com/android/car/CarLocalServices.java b/service/src/com/android/car/CarLocalServices.java
index 6c756b8..ea1b6f1 100644
--- a/service/src/com/android/car/CarLocalServices.java
+++ b/service/src/com/android/car/CarLocalServices.java
@@ -17,6 +17,7 @@
package com.android.car;
import android.annotation.Nullable;
+import android.car.Car;
import android.car.hardware.power.CarPowerManager;
import android.content.Context;
import android.util.ArrayMap;
@@ -90,10 +91,12 @@
*/
@Nullable
public static CarPowerManager createCarPowerManager(Context context) {
+ // This does not require connection as binder will be passed to CarPowerManager directly.
+ Car car = new Car(context, /* service= */null, /* handler= */ null);
CarPowerManagementService service = getService(CarPowerManagementService.class);
if (service == null) {
return null;
}
- return new CarPowerManager(service, context, null);
+ return new CarPowerManager(car, service);
}
}
diff --git a/service/src/com/android/car/CarMediaService.java b/service/src/com/android/car/CarMediaService.java
index 4841b0d..b69e666 100644
--- a/service/src/com/android/car/CarMediaService.java
+++ b/service/src/com/android/car/CarMediaService.java
@@ -77,6 +77,13 @@
private static final String SHARED_PREF = "com.android.car.media.car_media_service";
private static final String COMPONENT_NAME_SEPARATOR = ",";
private static final String MEDIA_CONNECTION_ACTION = "com.android.car.media.MEDIA_CONNECTION";
+ private static final String EXTRA_AUTOPLAY = "com.android.car.media.autoplay";
+
+ // XML configuration options for autoplay on media source change.
+ private static final int AUTOPLAY_CONFIG_NEVER = 0;
+ private static final int AUTOPLAY_CONFIG_ALWAYS = 1;
+ // This mode uses the last stored playback state to determine whether to resume playback
+ private static final int AUTOPLAY_CONFIG_ADAPTIVE = 2;
private final Context mContext;
private final UserManager mUserManager;
@@ -89,8 +96,8 @@
// null if playback has not been started yet.
private MediaController mActiveUserMediaController;
private SessionChangedListener mSessionsListener;
- private boolean mStartPlayback;
- private boolean mPlayOnMediaSourceChanged;
+ private int mPlayOnMediaSourceChangedConfig;
+ private int mPlayOnBootConfig;
private boolean mPendingInit;
private int mCurrentUser;
@@ -175,8 +182,9 @@
userSwitchFilter.addAction(Intent.ACTION_USER_SWITCHED);
mContext.registerReceiver(mUserSwitchReceiver, userSwitchFilter);
- mPlayOnMediaSourceChanged =
- mContext.getResources().getBoolean(R.bool.autoPlayOnMediaSourceChanged);
+ mPlayOnMediaSourceChangedConfig =
+ mContext.getResources().getInteger(R.integer.config_mediaSourceChangedAutoplay);
+ mPlayOnBootConfig = mContext.getResources().getInteger(R.integer.config_mediaBootAutoplay);
mCurrentUser = ActivityManager.getCurrentUser();
updateMediaSessionCallbackForCurrentUser();
}
@@ -200,22 +208,34 @@
mPackageUpdateFilter, null, null);
mIsPackageUpdateReceiverRegistered = true;
- mPrimaryMediaComponent = getLastMediaSource();
+ mPrimaryMediaComponent =
+ isCurrentUserEphemeral() ? getDefaultMediaSource() : getLastMediaSource();
mActiveUserMediaController = null;
- String key = PLAYBACK_STATE_KEY + mCurrentUser;
- mStartPlayback =
- mSharedPrefs.getInt(key, PlaybackState.STATE_NONE) == PlaybackState.STATE_PLAYING;
+
updateMediaSessionCallbackForCurrentUser();
notifyListeners();
- // Start a service on the current user that binds to the media browser of the current media
- // source. We start a new service because this one runs on user 0, and MediaBrowser doesn't
- // provide an API to connect on a specific user.
+ startMediaConnectorService(shouldStartPlayback(mPlayOnBootConfig), currentUser);
+ }
+
+ /**
+ * Starts a service on the current user that binds to the media browser of the current media
+ * source. We start a new service because this one runs on user 0, and MediaBrowser doesn't
+ * provide an API to connect on a specific user. Additionally, this service will attempt to
+ * resume playback using the MediaSession obtained via the media browser connection, which
+ * is more reliable than using active MediaSessions from MediaSessionManager.
+ */
+ private void startMediaConnectorService(boolean startPlayback, UserHandle currentUser) {
Intent serviceStart = new Intent(MEDIA_CONNECTION_ACTION);
serviceStart.setPackage(mContext.getResources().getString(R.string.serviceMediaConnection));
+ serviceStart.putExtra(EXTRA_AUTOPLAY, startPlayback);
mContext.startForegroundServiceAsUser(serviceStart, currentUser);
}
+ private boolean isCurrentUserEphemeral() {
+ return mUserManager.getUserInfo(mCurrentUser).isEphemeral();
+ }
+
@Override
public void release() {
mMediaSessionUpdater.unregisterCallbacks();
@@ -297,10 +317,9 @@
if (!unlocked) {
return;
}
- // No need to handle user0, non current foreground user, or ephemeral user.
+ // No need to handle user0, non current foreground user.
if (userHandle == UserHandle.USER_SYSTEM
- || userHandle != ActivityManager.getCurrentUser()
- || mUserManager.getUserInfo(userHandle).isEphemeral()) {
+ || userHandle != ActivityManager.getCurrentUser()) {
return;
}
if (mPendingInit) {
@@ -474,7 +493,6 @@
stop();
- mStartPlayback = mPlayOnMediaSourceChanged;
mPreviousMediaComponent = mPrimaryMediaComponent;
mPrimaryMediaComponent = componentName;
updateActiveMediaController(mMediaSessionManager
@@ -483,7 +501,9 @@
if (mSharedPrefs != null) {
if (mPrimaryMediaComponent != null && !TextUtils.isEmpty(
mPrimaryMediaComponent.flattenToString())) {
- saveLastMediaSource(mPrimaryMediaComponent);
+ if (!isCurrentUserEphemeral()) {
+ saveLastMediaSource(mPrimaryMediaComponent);
+ }
mRemovedMediaSourcePackage = null;
}
} else {
@@ -491,6 +511,9 @@
Log.e(CarLog.TAG_MEDIA, "Error trying to save last media source, prefs uninitialized");
}
notifyListeners();
+ if (shouldStartPlayback(mPlayOnMediaSourceChangedConfig)) {
+ startMediaConnectorService(true, new UserHandle(mCurrentUser));
+ }
}
private void notifyListeners() {
@@ -509,9 +532,9 @@
private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() {
@Override
public void onPlaybackStateChanged(PlaybackState state) {
- savePlaybackState(state);
- // Try to start playback if the new state allows the play action
- maybeRestartPlayback(state);
+ if (!isCurrentUserEphemeral()) {
+ savePlaybackState(state);
+ }
}
};
@@ -637,7 +660,10 @@
}
}
}
+ return getDefaultMediaSource();
+ }
+ private ComponentName getDefaultMediaSource() {
String defaultMediaSource = mContext.getString(R.string.default_media_source);
ComponentName defaultComponent = ComponentName.unflattenFromString(defaultMediaSource);
if (isMediaService(defaultComponent)) {
@@ -657,23 +683,17 @@
private void savePlaybackState(PlaybackState playbackState) {
int state = playbackState != null ? playbackState.getState() : PlaybackState.STATE_NONE;
- if (state == PlaybackState.STATE_PLAYING) {
- // No longer need to request play if audio was resumed already via some other means,
- // e.g. Assistant starts playback, user uses hardware button, etc.
- mStartPlayback = false;
- }
if (mSharedPrefs != null) {
- String key = PLAYBACK_STATE_KEY + mCurrentUser;
+ String key = getPlaybackStateKey();
mSharedPrefs.edit().putInt(key, state).apply();
}
}
- private void maybeRestartPlayback(PlaybackState state) {
- if (mStartPlayback && state != null
- && (state.getActions() & PlaybackState.ACTION_PLAY) != 0) {
- play();
- mStartPlayback = false;
- }
+ /**
+ * Builds a string key for saving the playback state for a specific media source (and user)
+ */
+ private String getPlaybackStateKey() {
+ return PLAYBACK_STATE_KEY + mCurrentUser + mPrimaryMediaComponent.flattenToString();
}
/**
@@ -691,19 +711,39 @@
for (MediaController controller : mediaControllers) {
if (matchPrimaryMediaSource(controller.getPackageName(), getClassName(controller))) {
mActiveUserMediaController = controller;
+ PlaybackState state = mActiveUserMediaController.getPlaybackState();
+ if (!isCurrentUserEphemeral()) {
+ savePlaybackState(state);
+ }
// Specify Handler to receive callbacks on, to avoid defaulting to the calling
// thread; this method can be called from the MediaSessionManager callback.
// Using the version of this method without passing a handler causes a
// RuntimeException for failing to create a Handler.
- PlaybackState state = mActiveUserMediaController.getPlaybackState();
- savePlaybackState(state);
mActiveUserMediaController.registerCallback(mMediaControllerCallback, mHandler);
- maybeRestartPlayback(state);
return;
}
}
}
+ /**
+ * Returns whether we should autoplay the current media source
+ */
+ private boolean shouldStartPlayback(int config) {
+ switch (config) {
+ case AUTOPLAY_CONFIG_NEVER:
+ return false;
+ case AUTOPLAY_CONFIG_ALWAYS:
+ return true;
+ case AUTOPLAY_CONFIG_ADAPTIVE:
+ return mSharedPrefs.getInt(getPlaybackStateKey(), PlaybackState.STATE_NONE)
+ == PlaybackState.STATE_PLAYING;
+ default:
+ Log.e(CarLog.TAG_MEDIA, "Unsupported playback configuration: " + config);
+ return false;
+ }
+
+ }
+
@NonNull
private static String getClassName(@NonNull MediaController controller) {
Bundle sessionExtras = controller.getExtras();
diff --git a/service/src/com/android/car/CarProjectionService.java b/service/src/com/android/car/CarProjectionService.java
index 057682f..02f5c3d 100644
--- a/service/src/com/android/car/CarProjectionService.java
+++ b/service/src/com/android/car/CarProjectionService.java
@@ -645,6 +645,11 @@
public void onStopped() {
Log.i(TAG, "Local-only hotspot stopped.");
synchronized (mLock) {
+ if (mLocalOnlyHotspotReservation != null) {
+ // We must explicitly released old reservation object, otherwise it may
+ // unexpectedly stop LOHS later because it overrode finalize() method.
+ mLocalOnlyHotspotReservation.close();
+ }
mLocalOnlyHotspotReservation = null;
}
sendApStopped();
diff --git a/service/src/com/android/car/CarPropertyService.java b/service/src/com/android/car/CarPropertyService.java
index 3b4e388..c9a14c9 100644
--- a/service/src/com/android/car/CarPropertyService.java
+++ b/service/src/com/android/car/CarPropertyService.java
@@ -18,6 +18,7 @@
import static java.lang.Integer.toHexString;
+import android.car.Car;
import android.car.hardware.CarPropertyConfig;
import android.car.hardware.CarPropertyValue;
import android.car.hardware.property.CarPropertyEvent;
@@ -348,6 +349,10 @@
return;
}
ICarImpl.assertPermission(mContext, mHal.getWritePermission(propId));
+ // need an extra permission for writing display units properties.
+ if (mHal.isDisplayUnitsProperty(propId)) {
+ ICarImpl.assertPermission(mContext, Car.PERMISSION_VENDOR_EXTENSION);
+ }
mHal.setProperty(prop);
}
diff --git a/service/src/com/android/car/CarService.java b/service/src/com/android/car/CarService.java
index 509ecdd..fdc4995 100644
--- a/service/src/com/android/car/CarService.java
+++ b/service/src/com/android/car/CarService.java
@@ -25,6 +25,7 @@
import android.os.Build;
import android.os.IBinder;
import android.os.IHwBinder.DeathRecipient;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -41,6 +42,8 @@
public class CarService extends Service {
+ private static final boolean RESTART_CAR_SERVICE_WHEN_VHAL_CRASH = true;
+
private static final long WAIT_FOR_VEHICLE_HAL_TIMEOUT_MS = 10_000;
private static final boolean IS_USER_BUILD = "user".equals(Build.TYPE);
@@ -178,7 +181,13 @@
@Override
public void serviceDied(long cookie) {
- Log.w(CarLog.TAG_SERVICE, "Vehicle HAL died.");
+ if (RESTART_CAR_SERVICE_WHEN_VHAL_CRASH) {
+ Log.wtf(CarLog.TAG_SERVICE, "***Vehicle HAL died. Car service will restart***");
+ Process.killProcess(Process.myPid());
+ return;
+ }
+
+ Log.wtf(CarLog.TAG_SERVICE, "***Vehicle HAL died.***");
try {
mVehicle.unlinkToDeath(this);
diff --git a/service/src/com/android/car/CarTestService.java b/service/src/com/android/car/CarTestService.java
index 8c3f64d..776fd53 100644
--- a/service/src/com/android/car/CarTestService.java
+++ b/service/src/com/android/car/CarTestService.java
@@ -22,6 +22,8 @@
import android.os.RemoteException;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashMap;
@@ -39,6 +41,9 @@
private final Context mContext;
private final ICarImpl mICarImpl;
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
private final Map<IBinder, TokenDeathRecipient> mTokens = new HashMap<>();
CarTestService(Context context, ICarImpl carImpl) {
@@ -69,7 +74,7 @@
Log.d(TAG, "stopCarService, token: " + token);
ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_TEST_SERVICE);
- synchronized (this) {
+ synchronized (mLock) {
if (mTokens.containsKey(token)) {
Log.w(TAG, "Calling stopCarService twice with the same token.");
return;
@@ -80,7 +85,7 @@
token.linkToDeath(deathRecipient, 0);
if (mTokens.size() == 1) {
- mICarImpl.release();
+ CarServiceUtils.runOnMainSync(mICarImpl::release);
}
}
}
@@ -92,15 +97,17 @@
releaseToken(token);
}
- private synchronized void releaseToken(IBinder token) {
+ private void releaseToken(IBinder token) {
Log.d(TAG, "releaseToken, token: " + token);
- DeathRecipient deathRecipient = mTokens.remove(token);
- if (deathRecipient != null) {
- token.unlinkToDeath(deathRecipient, 0);
- }
+ synchronized (mLock) {
+ DeathRecipient deathRecipient = mTokens.remove(token);
+ if (deathRecipient != null) {
+ token.unlinkToDeath(deathRecipient, 0);
+ }
- if (mTokens.size() == 0) {
- CarServiceUtils.runOnMain(mICarImpl::init);
+ if (mTokens.size() == 0) {
+ CarServiceUtils.runOnMainSync(mICarImpl::init);
+ }
}
}
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index fe7c4d6..3c6129e 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -37,6 +37,7 @@
import android.util.Slog;
import android.util.TimingsTraceLog;
+import com.android.car.am.FixedActivityService;
import com.android.car.audio.CarAudioService;
import com.android.car.cluster.InstrumentClusterService;
import com.android.car.garagemode.GarageModeService;
@@ -80,6 +81,7 @@
private final CarPropertyService mCarPropertyService;
private final CarNightService mCarNightService;
private final AppFocusService mAppFocusService;
+ private final FixedActivityService mFixedActivityService;
private final GarageModeService mGarageModeService;
private final InstrumentClusterService mInstrumentClusterService;
private final CarLocationService mCarLocationService;
@@ -149,6 +151,7 @@
mAppFocusService = new AppFocusService(serviceContext, mSystemActivityMonitoringService);
mCarAudioService = new CarAudioService(serviceContext);
mCarNightService = new CarNightService(serviceContext, mCarPropertyService);
+ mFixedActivityService = new FixedActivityService(serviceContext);
mInstrumentClusterService = new InstrumentClusterService(serviceContext,
mAppFocusService, mCarInputService);
mSystemStateControllerService = new SystemStateControllerService(
@@ -177,6 +180,7 @@
CarLocalServices.addService(SystemInterface.class, mSystemInterface);
CarLocalServices.addService(CarDrivingStateService.class, mCarDrivingStateService);
CarLocalServices.addService(PerUserCarServiceHelper.class, mPerUserCarServiceHelper);
+ CarLocalServices.addService(FixedActivityService.class, mFixedActivityService);
// Be careful with order. Service depending on other service should be inited later.
List<CarServiceBase> allServices = new ArrayList<>();
@@ -193,6 +197,7 @@
allServices.add(mAppFocusService);
allServices.add(mCarAudioService);
allServices.add(mCarNightService);
+ allServices.add(mFixedActivityService);
allServices.add(mInstrumentClusterService);
allServices.add(mSystemStateControllerService);
allServices.add(mPerUserCarServiceHelper);
diff --git a/service/src/com/android/car/am/FixedActivityService.java b/service/src/com/android/car/am/FixedActivityService.java
new file mode 100644
index 0000000..b8f8608
--- /dev/null
+++ b/service/src/com/android/car/am/FixedActivityService.java
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2019 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 com.android.car.am;
+
+import static com.android.car.CarLog.TAG_AM;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.ActivityManager.StackInfo;
+import android.app.ActivityOptions;
+import android.app.IActivityManager;
+import android.app.IProcessObserver;
+import android.app.TaskStackListener;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Display;
+
+import com.android.car.CarLocalServices;
+import com.android.car.CarServiceBase;
+import com.android.car.user.CarUserService;
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Monitors top activity for a display and guarantee activity in fixed mode is re-launched if it has
+ * crashed or gone to background for whatever reason.
+ *
+ * <p>This component also monitors the upddate of the target package and re-launch it once
+ * update is complete.</p>
+ */
+public final class FixedActivityService implements CarServiceBase {
+
+ private static final boolean DBG = false;
+
+ private static class RunningActivityInfo {
+ @NonNull
+ public final Intent intent;
+
+ @NonNull
+ public final ActivityOptions activityOptions;
+
+ @UserIdInt
+ public final int userId;
+
+ // Only used in a method for local book-keeping. So do not need a lock.
+ // This does not represent the current visibility.
+ public boolean isVisible;
+
+ RunningActivityInfo(@NonNull Intent intent, @NonNull ActivityOptions activityOptions,
+ @UserIdInt int userId) {
+ this.intent = intent;
+ this.activityOptions = activityOptions;
+ this.userId = userId;
+ }
+
+ @Override
+ public String toString() {
+ return "RunningActivityInfo{intent:" + intent + ",activityOptions:" + activityOptions
+ + ",userId:" + userId + "}";
+ }
+ }
+
+ private final Context mContext;
+
+ private final IActivityManager mAm;
+
+ private final UserManager mUm;
+
+ private final CarUserService.UserCallback mUserCallback = new CarUserService.UserCallback() {
+ @Override
+ public void onUserLockChanged(@UserIdInt int userId, boolean unlocked) {
+ // Nothing to do
+ }
+
+ @Override
+ public void onSwitchUser(@UserIdInt int userId) {
+ synchronized (mLock) {
+ mRunningActivities.clear();
+ }
+ }
+ };
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
+ || Intent.ACTION_PACKAGE_REPLACED.equals(
+ action)) {
+ launchIfNecessary();
+ }
+ }
+ };
+
+ // It says listener but is actually callback.
+ private final TaskStackListener mTaskStackListener = new TaskStackListener() {
+ @Override
+ public void onTaskStackChanged() {
+ launchIfNecessary();
+ }
+ };
+
+ private final IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
+ @Override
+ public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
+ launchIfNecessary();
+ }
+
+ @Override
+ public void onForegroundServicesChanged(int pid, int uid, int fgServiceTypes) {
+ // ignore
+ }
+
+ @Override
+ public void onProcessDied(int pid, int uid) {
+ launchIfNecessary();
+ }
+ };
+
+ private final Object mLock = new Object();
+
+ // key: displayId
+ @GuardedBy("mLock")
+ private final SparseArray<RunningActivityInfo> mRunningActivities =
+ new SparseArray<>(/* capacity= */ 1); // default to one cluster only case
+
+ @GuardedBy("mLock")
+ private boolean mEventMonitoringActive;
+
+ public FixedActivityService(Context context) {
+ mContext = context;
+ mAm = ActivityManager.getService();
+ mUm = context.getSystemService(UserManager.class);
+ }
+
+
+ @Override
+ public void init() {
+ // nothing to do
+ }
+
+ @Override
+ public void release() {
+ stopMonitoringEvents();
+ }
+
+ @Override
+ public void dump(PrintWriter writer) {
+ writer.println("*FixedActivityService*");
+ synchronized (mLock) {
+ writer.println("mRunningActivities:" + mRunningActivities
+ + " ,mEventMonitoringActive:" + mEventMonitoringActive);
+ }
+ }
+
+ private void startMonitoringEvents() {
+ synchronized (mLock) {
+ if (mEventMonitoringActive) {
+ return;
+ }
+ mEventMonitoringActive = true;
+ }
+ CarUserService userService = CarLocalServices.getService(CarUserService.class);
+ userService.addUserCallback(mUserCallback);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+ mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter,
+ /* broadcastPermission= */ null, /* scheduler= */ null);
+ try {
+ mAm.registerTaskStackListener(mTaskStackListener);
+ mAm.registerProcessObserver(mProcessObserver);
+ } catch (RemoteException e) {
+ Log.e(TAG_AM, "remote exception from AM", e);
+ }
+ }
+
+ private void stopMonitoringEvents() {
+ synchronized (mLock) {
+ if (!mEventMonitoringActive) {
+ return;
+ }
+ mEventMonitoringActive = false;
+ }
+ CarUserService userService = CarLocalServices.getService(CarUserService.class);
+ userService.removeUserCallback(mUserCallback);
+ try {
+ mAm.unregisterTaskStackListener(mTaskStackListener);
+ mAm.unregisterProcessObserver(mProcessObserver);
+ } catch (RemoteException e) {
+ Log.e(TAG_AM, "remote exception from AM", e);
+ }
+ }
+
+ @Nullable
+ private List<StackInfo> getStackInfos() {
+ try {
+ return mAm.getAllStackInfos();
+ } catch (RemoteException e) {
+ Log.e(TAG_AM, "remote exception from AM", e);
+ }
+ return null;
+ }
+
+ /**
+ * Launches all stored fixed mode activities if necessary.
+ * @param displayId Display id to check if it is visible. If check is not necessary, should pass
+ * {@link Display#INVALID_DISPLAY}.
+ * @return true if fixed Activity for given {@code displayId} is visible / successfully
+ * launched. It will return false for {@link Display#INVALID_DISPLAY} {@code displayId}.
+ */
+ private boolean launchIfNecessary(int displayId) {
+ List<StackInfo> infos = getStackInfos();
+ if (infos == null) {
+ Log.e(TAG_AM, "cannot get StackInfo from AM");
+ return false;
+ }
+ synchronized (mLock) {
+ if (mRunningActivities.size() == 0) {
+ // it must have been stopped.
+ if (DBG) {
+ Log.i(TAG_AM, "empty activity list", new RuntimeException());
+ }
+ return false;
+ }
+ for (int i = 0; i < mRunningActivities.size(); i++) {
+ mRunningActivities.valueAt(i).isVisible = false;
+ }
+ for (StackInfo stackInfo : infos) {
+ RunningActivityInfo activityInfo = mRunningActivities.get(stackInfo.displayId);
+ if (activityInfo == null) {
+ continue;
+ }
+ int topUserId = stackInfo.taskUserIds[stackInfo.taskUserIds.length - 1];
+ if (activityInfo.intent.getComponent().equals(stackInfo.topActivity)
+ && activityInfo.userId == topUserId && stackInfo.visible) {
+ // top one is matching.
+ activityInfo.isVisible = true;
+ continue;
+ }
+ if (DBG) {
+ Log.i(TAG_AM, "Unmatched top activity:" + stackInfo.topActivity
+ + " user:" + topUserId + " display:" + stackInfo.displayId);
+ }
+ }
+ for (int i = 0; i < mRunningActivities.size(); i++) {
+ RunningActivityInfo activityInfo = mRunningActivities.valueAt(i);
+ if (activityInfo.isVisible) {
+ continue;
+ }
+ if (!isComponentAvailable(activityInfo.intent.getComponent(),
+ activityInfo.userId) || !isUserAllowedToLaunchActivity(
+ activityInfo.userId)) {
+ continue;
+ }
+ Log.i(TAG_AM, "Launching Activity for fixed mode. Intent:" + activityInfo.intent
+ + ",userId:" + UserHandle.of(activityInfo.userId) + ",displayId:"
+ + mRunningActivities.keyAt(i));
+ try {
+ mContext.startActivityAsUser(activityInfo.intent,
+ activityInfo.activityOptions.toBundle(),
+ UserHandle.of(activityInfo.userId));
+ activityInfo.isVisible = true;
+ } catch (Exception e) { // Catch all for any app related issues.
+ Log.w(TAG_AM, "Cannot start activity:" + activityInfo.intent, e);
+ }
+ }
+ RunningActivityInfo activityInfo = mRunningActivities.get(displayId);
+ if (activityInfo == null) {
+ return false;
+ }
+ return activityInfo.isVisible;
+ }
+ }
+
+ private void launchIfNecessary() {
+ launchIfNecessary(Display.INVALID_DISPLAY);
+ }
+
+ private void logComponentNotFound(ComponentName component, @UserIdInt int userId,
+ Exception e) {
+ Log.e(TAG_AM, "Specified Component not found:" + component
+ + " for userid:" + userId, e);
+ }
+
+ private boolean isComponentAvailable(ComponentName component, @UserIdInt int userId) {
+ PackageInfo packageInfo;
+ try {
+ packageInfo = mContext.getPackageManager().getPackageInfoAsUser(
+ component.getPackageName(), PackageManager.GET_ACTIVITIES, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ logComponentNotFound(component, userId, e);
+ return false;
+ }
+ if (packageInfo == null || packageInfo.activities == null) {
+ // may not be necessary but additional safety check
+ logComponentNotFound(component, userId, new RuntimeException());
+ return false;
+ }
+ String fullName = component.getClassName();
+ String shortName = component.getShortClassName();
+ for (ActivityInfo info : packageInfo.activities) {
+ if (info.name.equals(fullName) || info.name.equals(shortName)) {
+ return true;
+ }
+ }
+ logComponentNotFound(component, userId, new RuntimeException());
+ return false;
+ }
+
+ private boolean isUserAllowedToLaunchActivity(@UserIdInt int userId) {
+ int currentUser = ActivityManager.getCurrentUser();
+ if (userId == currentUser) {
+ return true;
+ }
+ int[] profileIds = mUm.getEnabledProfileIds(currentUser);
+ for (int id : profileIds) {
+ if (id == userId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isDisplayAllowedForFixedMode(int displayId) {
+ if (displayId == Display.DEFAULT_DISPLAY || displayId == Display.INVALID_DISPLAY) {
+ Log.w(TAG_AM, "Target display cannot be used for fixed mode, displayId:" + displayId,
+ new RuntimeException());
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Checks {@link InstrumentClusterRenderingService#startFixedActivityModeForDisplayAndUser(
+ * Intent, ActivityOptions, int)}
+ */
+ public boolean startFixedActivityModeForDisplayAndUser(@NonNull Intent intent,
+ @NonNull ActivityOptions options, int displayId, @UserIdInt int userId) {
+ if (!isDisplayAllowedForFixedMode(displayId)) {
+ return false;
+ }
+ if (!isUserAllowedToLaunchActivity(userId)) {
+ Log.e(TAG_AM, "startFixedActivityModeForDisplayAndUser, requested user:" + userId
+ + " cannot launch activity, Intent:" + intent);
+ return false;
+ }
+ ComponentName component = intent.getComponent();
+ if (component == null) {
+ Log.e(TAG_AM,
+ "startFixedActivityModeForDisplayAndUser: No component specified for "
+ + "requested Intent"
+ + intent);
+ return false;
+ }
+ if (!isComponentAvailable(component, userId)) {
+ return false;
+ }
+ boolean startMonitoringEvents = false;
+ synchronized (mLock) {
+ if (mRunningActivities.size() == 0) {
+ startMonitoringEvents = true;
+ }
+ RunningActivityInfo activityInfo = mRunningActivities.get(displayId);
+ if (activityInfo == null) {
+ activityInfo = new RunningActivityInfo(intent, options, userId);
+ mRunningActivities.put(displayId, activityInfo);
+ }
+ }
+ boolean launched = launchIfNecessary(displayId);
+ if (!launched) {
+ synchronized (mLock) {
+ mRunningActivities.remove(displayId);
+ }
+ }
+ // If first trial fails, let client know and do not retry as it can be wrong setting.
+ if (startMonitoringEvents && launched) {
+ startMonitoringEvents();
+ }
+ return launched;
+ }
+
+ /** Check {@link InstrumentClusterRenderingService#stopFixedActivityMode(int)} */
+ public void stopFixedActivityMode(int displayId) {
+ if (!isDisplayAllowedForFixedMode(displayId)) {
+ return;
+ }
+ boolean stopMonitoringEvents = false;
+ synchronized (mLock) {
+ mRunningActivities.remove(displayId);
+ if (mRunningActivities.size() == 0) {
+ stopMonitoringEvents = true;
+ }
+ }
+ if (stopMonitoringEvents) {
+ stopMonitoringEvents();
+ }
+ }
+}
diff --git a/service/src/com/android/car/cluster/InstrumentClusterService.java b/service/src/com/android/car/cluster/InstrumentClusterService.java
index fd16da5..df6c476 100644
--- a/service/src/com/android/car/cluster/InstrumentClusterService.java
+++ b/service/src/com/android/car/cluster/InstrumentClusterService.java
@@ -15,17 +15,22 @@
*/
package com.android.car.cluster;
+import static android.car.cluster.renderer.InstrumentClusterRenderingService.EXTRA_BUNDLE_KEY_FOR_INSTRUMENT_CLUSTER_HELPER;
+
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.app.ActivityOptions;
import android.car.CarAppFocusManager;
import android.car.cluster.IInstrumentClusterManagerCallback;
import android.car.cluster.IInstrumentClusterManagerService;
import android.car.cluster.renderer.IInstrumentCluster;
+import android.car.cluster.renderer.IInstrumentClusterHelper;
import android.car.cluster.renderer.IInstrumentClusterNavigation;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -43,6 +48,7 @@
import com.android.car.CarLog;
import com.android.car.CarServiceBase;
import com.android.car.R;
+import com.android.car.am.FixedActivityService;
import com.android.car.user.CarUserService;
import com.android.internal.annotations.GuardedBy;
@@ -69,16 +75,18 @@
*/
@Deprecated
private final ClusterManagerService mClusterManagerService = new ClusterManagerService();
- private final Object mSync = new Object();
- @GuardedBy("mSync")
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
private ContextOwner mNavContextOwner = NO_OWNER;
- @GuardedBy("mSync")
+ @GuardedBy("mLock")
private IInstrumentCluster mRendererService;
// If renderer service crashed / stopped and this class fails to rebind with it immediately,
// we should wait some time before next attempt. This may happen during APK update for example.
+ @GuardedBy("mLock")
private DeferredRebinder mDeferredRebinder;
// Whether {@link android.car.cluster.renderer.InstrumentClusterRendererService} is bound
// (although not necessarily connected)
+ @GuardedBy("mLock")
private boolean mRendererBound = false;
/**
@@ -92,7 +100,7 @@
}
IInstrumentCluster service = IInstrumentCluster.Stub.asInterface(binder);
ContextOwner navContextOwner;
- synchronized (mSync) {
+ synchronized (mLock) {
mRendererService = service;
navContextOwner = mNavContextOwner;
}
@@ -107,19 +115,39 @@
Log.d(TAG, "onServiceDisconnected, name: " + name);
}
mContext.unbindService(this);
- mRendererBound = false;
-
- synchronized (mSync) {
+ DeferredRebinder rebinder;
+ synchronized (mLock) {
+ mRendererBound = false;
mRendererService = null;
+ if (mDeferredRebinder == null) {
+ mDeferredRebinder = new DeferredRebinder();
+ }
+ rebinder = mDeferredRebinder;
}
-
- if (mDeferredRebinder == null) {
- mDeferredRebinder = new DeferredRebinder();
- }
- mDeferredRebinder.rebind();
+ rebinder.rebind();
}
};
+ private final IInstrumentClusterHelper mInstrumentClusterHelper =
+ new IInstrumentClusterHelper.Stub() {
+ @Override
+ public boolean startFixedActivityModeForDisplayAndUser(Intent intent,
+ Bundle activityOptionsBundle, int userId) {
+ ActivityOptions options = new ActivityOptions(activityOptionsBundle);
+ FixedActivityService service = CarLocalServices.getService(
+ FixedActivityService.class);
+ return service.startFixedActivityModeForDisplayAndUser(intent, options,
+ options.getLaunchDisplayId(), userId);
+ }
+
+ @Override
+ public void stopFixedActivityMode(int displayId) {
+ FixedActivityService service = CarLocalServices.getService(
+ FixedActivityService.class);
+ service.stopFixedActivityMode(displayId);
+ }
+ };
+
public InstrumentClusterService(Context context, AppFocusService appFocusService,
CarInputService carInputService) {
mContext = context;
@@ -181,7 +209,7 @@
IInstrumentCluster service;
ContextOwner requester = new ContextOwner(uid, pid);
ContextOwner newOwner = acquire ? requester : NO_OWNER;
- synchronized (mSync) {
+ synchronized (mLock) {
if ((acquire && Objects.equals(mNavContextOwner, requester))
|| (!acquire && !Objects.equals(mNavContextOwner, requester))) {
// Nothing to do here. Either the same owner is acquiring twice, or someone is
@@ -221,6 +249,11 @@
Intent intent = new Intent();
intent.setComponent(ComponentName.unflattenFromString(rendererService));
+ // Litle bit inefficiency here as Intent.getIBinderExtra() is a hidden API.
+ Bundle bundle = new Bundle();
+ bundle.putBinder(EXTRA_BUNDLE_KEY_FOR_INSTRUMENT_CLUSTER_HELPER,
+ mInstrumentClusterHelper.asBinder());
+ intent.putExtra(EXTRA_BUNDLE_KEY_FOR_INSTRUMENT_CLUSTER_HELPER, bundle);
return mContext.bindServiceAsUser(intent, mRendererServiceConnection,
Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM);
}
@@ -262,7 +295,7 @@
private IInstrumentCluster getInstrumentClusterRendererService() {
IInstrumentCluster service;
- synchronized (mSync) {
+ synchronized (mLock) {
service = mRendererService;
}
return service;
diff --git a/service/src/com/android/car/hal/PropertyHalService.java b/service/src/com/android/car/hal/PropertyHalService.java
index 545fc2b..484e667 100644
--- a/service/src/com/android/car/hal/PropertyHalService.java
+++ b/service/src/com/android/car/hal/PropertyHalService.java
@@ -183,6 +183,14 @@
}
/**
+ * Return true if property is a display_units property
+ * @param propId
+ */
+ public boolean isDisplayUnitsProperty(int propId) {
+ return mPropIds.isPropertyToChangeUnits(propId);
+ }
+
+ /**
* Set the property value.
* @param prop
*/
diff --git a/service/src/com/android/car/hal/PropertyHalServiceIds.java b/service/src/com/android/car/hal/PropertyHalServiceIds.java
index 5409b4d..82a89d7 100644
--- a/service/src/com/android/car/hal/PropertyHalServiceIds.java
+++ b/service/src/com/android/car/hal/PropertyHalServiceIds.java
@@ -26,6 +26,8 @@
import android.util.Pair;
import android.util.SparseArray;
+import java.util.HashSet;
+
/**
* Helper class to define which property IDs are used by PropertyHalService. This class binds the
* read and write permissions to the property ID.
@@ -39,11 +41,12 @@
* properties.
*/
private final SparseArray<Pair<String, String>> mProps;
+ private final HashSet<Integer> mPropForUnits;
private static final String TAG = "PropertyHalServiceIds";
public PropertyHalServiceIds() {
mProps = new SparseArray<>();
-
+ mPropForUnits = new HashSet<>();
// Add propertyId and read/write permissions
// Cabin Properties
mProps.put(VehicleProperty.DOOR_POS, new Pair<>(
@@ -385,24 +388,31 @@
mProps.put(VehicleProperty.CABIN_LIGHTS_SWITCH, new Pair<>(
Car.PERMISSION_CONTROL_INTERIOR_LIGHTS,
Car.PERMISSION_CONTROL_INTERIOR_LIGHTS));
+ // Display_Units
mProps.put(VehicleProperty.DISTANCE_DISPLAY_UNITS, new Pair<>(
Car.PERMISSION_READ_DISPLAY_UNITS,
Car.PERMISSION_CONTROL_DISPLAY_UNITS));
+ mPropForUnits.add(VehicleProperty.DISTANCE_DISPLAY_UNITS);
mProps.put(VehicleProperty.FUEL_VOLUME_DISPLAY_UNITS, new Pair<>(
Car.PERMISSION_READ_DISPLAY_UNITS,
Car.PERMISSION_CONTROL_DISPLAY_UNITS));
+ mPropForUnits.add(VehicleProperty.FUEL_VOLUME_DISPLAY_UNITS);
mProps.put(VehicleProperty.TIRE_PRESSURE_DISPLAY_UNITS, new Pair<>(
Car.PERMISSION_READ_DISPLAY_UNITS,
Car.PERMISSION_CONTROL_DISPLAY_UNITS));
+ mPropForUnits.add(VehicleProperty.TIRE_PRESSURE_DISPLAY_UNITS);
mProps.put(VehicleProperty.EV_BATTERY_DISPLAY_UNITS, new Pair<>(
Car.PERMISSION_READ_DISPLAY_UNITS,
Car.PERMISSION_CONTROL_DISPLAY_UNITS));
+ mPropForUnits.add(VehicleProperty.EV_BATTERY_DISPLAY_UNITS);
mProps.put(VehicleProperty.FUEL_CONSUMPTION_UNITS_DISTANCE_OVER_VOLUME, new Pair<>(
Car.PERMISSION_READ_DISPLAY_UNITS,
Car.PERMISSION_CONTROL_DISPLAY_UNITS));
+ mPropForUnits.add(VehicleProperty.FUEL_CONSUMPTION_UNITS_DISTANCE_OVER_VOLUME);
mProps.put(VehicleProperty.VEHICLE_SPEED_DISPLAY_UNITS, new Pair<>(
Car.PERMISSION_READ_DISPLAY_UNITS,
Car.PERMISSION_CONTROL_DISPLAY_UNITS));
+ mPropForUnits.add(VehicleProperty.VEHICLE_SPEED_DISPLAY_UNITS);
}
/**
@@ -469,4 +479,11 @@
return insertVendorProperty(propId);
}
}
+
+ /**
+ * Check if the property is one of display units properties.
+ */
+ public boolean isPropertyToChangeUnits(int propertyId) {
+ return mPropForUnits.contains(propertyId);
+ }
}
diff --git a/service/src/com/android/car/pm/ActivityBlockingActivity.java b/service/src/com/android/car/pm/ActivityBlockingActivity.java
index 9dcb70a..9756523 100644
--- a/service/src/com/android/car/pm/ActivityBlockingActivity.java
+++ b/service/src/com/android/car/pm/ActivityBlockingActivity.java
@@ -79,14 +79,19 @@
// restrictions are lifted.
// This Activity should be launched only after car service is initialized. Currently this
// Activity is only launched from CPMS. So this is safe to do.
- mCar = Car.createCar(this);
- mUxRManager = (CarUxRestrictionsManager) mCar.getCarManager(
- Car.CAR_UX_RESTRICTION_SERVICE);
- // This activity would have been launched only in a restricted state.
- // But ensuring when the service connection is established, that we are still
- // in a restricted state.
- handleUxRChange(mUxRManager.getCurrentCarUxRestrictions());
- mUxRManager.registerListener(ActivityBlockingActivity.this::handleUxRChange);
+ mCar = Car.createCar(this, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER,
+ (car, ready) -> {
+ if (!ready) {
+ return;
+ }
+ mUxRManager = (CarUxRestrictionsManager) car.getCarManager(
+ Car.CAR_UX_RESTRICTION_SERVICE);
+ // This activity would have been launched only in a restricted state.
+ // But ensuring when the service connection is established, that we are still
+ // in a restricted state.
+ handleUxRChange(mUxRManager.getCurrentCarUxRestrictions());
+ mUxRManager.registerListener(ActivityBlockingActivity.this::handleUxRChange);
+ });
}
@Override
diff --git a/service/src/com/android/car/trust/CarTrustAgentEnrollmentService.java b/service/src/com/android/car/trust/CarTrustAgentEnrollmentService.java
index 0f54647..7f2923d 100644
--- a/service/src/com/android/car/trust/CarTrustAgentEnrollmentService.java
+++ b/service/src/com/android/car/trust/CarTrustAgentEnrollmentService.java
@@ -16,6 +16,7 @@
package com.android.car.trust;
+import static android.car.Car.PERMISSION_CAR_ENROLL_TRUST;
import static android.car.trust.CarTrustAgentEnrollmentManager.ENROLLMENT_HANDSHAKE_FAILURE;
import static android.car.trust.CarTrustAgentEnrollmentManager.ENROLLMENT_NOT_ALLOWED;
@@ -33,6 +34,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.app.ActivityManager;
import android.bluetooth.BluetoothDevice;
import android.car.encryptionrunner.EncryptionRunner;
@@ -52,6 +54,7 @@
import android.util.Log;
import com.android.car.BLEStreamProtos.BLEOperationProto.OperationType;
+import com.android.car.ICarImpl;
import com.android.car.R;
import com.android.car.Utils;
import com.android.internal.annotations.GuardedBy;
@@ -171,7 +174,9 @@
* the enrollment of the trusted device.
*/
@Override
+ @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
public void startEnrollmentAdvertising() {
+ ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
if (!mTrustedDeviceService.getSharedPrefs()
.getBoolean(TRUSTED_DEVICE_ENROLLMENT_ENABLED_KEY, true)) {
Log.e(TAG, "Trusted Device Enrollment disabled");
@@ -192,7 +197,9 @@
* Stop BLE advertisement for Enrollment
*/
@Override
+ @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
public void stopEnrollmentAdvertising() {
+ ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
logEnrollmentEvent(STOP_ENROLLMENT_ADVERTISING);
addEnrollmentServiceLog("stopEnrollmentAdvertising");
mCarTrustAgentBleManager.stopEnrollmentAdvertising();
@@ -205,7 +212,9 @@
* @param device the remote Bluetooth device that will receive the signal.
*/
@Override
+ @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
public void enrollmentHandshakeAccepted(BluetoothDevice device) {
+ ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
logEnrollmentEvent(ENROLLMENT_HANDSHAKE_ACCEPTED);
addEnrollmentServiceLog("enrollmentHandshakeAccepted");
if (device == null || !device.equals(mRemoteEnrollmentDevice)) {
@@ -226,7 +235,9 @@
* navigated away from the app before completing enrollment.
*/
@Override
+ @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
public void terminateEnrollmentHandshake() {
+ ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
addEnrollmentServiceLog("terminateEnrollmentHandshake");
// Disconnect from BLE
mCarTrustAgentBleManager.disconnectRemoteDevice();
@@ -252,7 +263,9 @@
* @return True if the escrow token is active, false if not
*/
@Override
+ @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
public boolean isEscrowTokenActive(long handle, int uid) {
+ ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
if (mTokenActiveStateMap.get(handle) != null) {
return mTokenActiveStateMap.get(handle);
}
@@ -266,7 +279,9 @@
* @param uid user id
*/
@Override
+ @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
public void removeEscrowToken(long handle, int uid) {
+ ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
mEnrollmentDelegate.removeEscrowToken(handle, uid);
addEnrollmentServiceLog("removeEscrowToken (handle:" + handle + " uid:" + uid + ")");
}
@@ -277,7 +292,9 @@
* @param uid user id
*/
@Override
+ @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
public void removeAllTrustedDevices(int uid) {
+ ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
for (TrustedDeviceInfo device : getEnrolledDeviceInfosForUser(uid)) {
removeEscrowToken(device.getHandle(), uid);
}
@@ -291,7 +308,9 @@
* @param isEnabled {@code true} to enable; {@code false} to disable the feature.
*/
@Override
+ @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
public void setTrustedDeviceEnrollmentEnabled(boolean isEnabled) {
+ ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
SharedPreferences.Editor editor = mTrustedDeviceService.getSharedPrefs().edit();
editor.putBoolean(TRUSTED_DEVICE_ENROLLMENT_ENABLED_KEY, isEnabled);
if (!editor.commit()) {
@@ -308,7 +327,9 @@
* back.
*/
@Override
+ @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
public void setTrustedDeviceUnlockEnabled(boolean isEnabled) {
+ ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
mTrustedDeviceService.getCarTrustAgentUnlockService()
.setTrustedDeviceUnlockEnabled(isEnabled);
}
@@ -322,7 +343,9 @@
*/
@NonNull
@Override
+ @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
public List<TrustedDeviceInfo> getEnrolledDeviceInfosForUser(int uid) {
+ ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
Set<String> enrolledDeviceInfos = mTrustedDeviceService.getSharedPrefs().getStringSet(
String.valueOf(uid), new HashSet<>());
List<TrustedDeviceInfo> trustedDeviceInfos = new ArrayList<>(enrolledDeviceInfos.size());
@@ -342,7 +365,9 @@
* @param listener {@link ICarTrustAgentEnrollmentCallback}
*/
@Override
+ @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
public synchronized void registerEnrollmentCallback(ICarTrustAgentEnrollmentCallback listener) {
+ ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
if (listener == null) {
throw new IllegalArgumentException("Listener is null");
}
@@ -835,8 +860,10 @@
* @param listener client to unregister
*/
@Override
+ @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
public synchronized void unregisterEnrollmentCallback(
ICarTrustAgentEnrollmentCallback listener) {
+ ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
if (listener == null) {
throw new IllegalArgumentException("Listener is null");
}
@@ -858,7 +885,9 @@
* @param listener {@link ICarTrustAgentBleCallback}
*/
@Override
+ @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
public synchronized void registerBleCallback(ICarTrustAgentBleCallback listener) {
+ ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
if (listener == null) {
throw new IllegalArgumentException("Listener is null");
}
@@ -903,7 +932,9 @@
* @param listener client to unregister
*/
@Override
+ @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
public synchronized void unregisterBleCallback(ICarTrustAgentBleCallback listener) {
+ ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
if (listener == null) {
throw new IllegalArgumentException("Listener is null");
}
diff --git a/service/src/com/android/car/vms/VmsClientManager.java b/service/src/com/android/car/vms/VmsClientManager.java
index 710793c..66a0fe4 100644
--- a/service/src/com/android/car/vms/VmsClientManager.java
+++ b/service/src/com/android/car/vms/VmsClientManager.java
@@ -146,16 +146,16 @@
CarUserService userService, CarUserManagerHelper userManagerHelper,
VmsHalService halService) {
this(context, brokerService, userService, userManagerHelper, halService,
- Binder::getCallingUid);
+ new Handler(Looper.getMainLooper()), Binder::getCallingUid);
}
@VisibleForTesting
VmsClientManager(Context context, VmsBrokerService brokerService,
CarUserService userService, CarUserManagerHelper userManagerHelper,
- VmsHalService halService, IntSupplier getCallingUid) {
+ VmsHalService halService, Handler handler, IntSupplier getCallingUid) {
mContext = context;
mPackageManager = context.getPackageManager();
- mHandler = new Handler(Looper.getMainLooper());
+ mHandler = handler;
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mUserService = userService;
mUserManagerHelper = userManagerHelper;
@@ -286,9 +286,11 @@
* Returns all active subscriber clients.
*/
public Collection<IVmsSubscriberClient> getAllSubscribers() {
- return mSubscribers.values().stream()
- .map(subscriber -> subscriber.mClient)
- .collect(Collectors.toList());
+ synchronized (mLock) {
+ return mSubscribers.values().stream()
+ .map(subscriber -> subscriber.mClient)
+ .collect(Collectors.toList());
+ }
}
/**
diff --git a/tests/CarCtsDummyLauncher/src/com/android/car/dummylauncher/LauncherActivity.java b/tests/CarCtsDummyLauncher/src/com/android/car/dummylauncher/LauncherActivity.java
index 20fcfc0..71b3c2b 100644
--- a/tests/CarCtsDummyLauncher/src/com/android/car/dummylauncher/LauncherActivity.java
+++ b/tests/CarCtsDummyLauncher/src/com/android/car/dummylauncher/LauncherActivity.java
@@ -33,6 +33,7 @@
View view = getLayoutInflater().inflate(R.layout.launcher_activity, null);
setContentView(view);
+ reportFullyDrawn();
}
}
diff --git a/tests/CarDeveloperOptions/AndroidManifest.xml b/tests/CarDeveloperOptions/AndroidManifest.xml
index 5975572..046b386 100644
--- a/tests/CarDeveloperOptions/AndroidManifest.xml
+++ b/tests/CarDeveloperOptions/AndroidManifest.xml
@@ -21,7 +21,6 @@
<original-package android:name="com.android.car.developeroptions" />
<uses-permission android:name="android.permission.REQUEST_NETWORK_SCORES" />
- <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
diff --git a/tests/CarDeveloperOptions/res/xml/development_settings.xml b/tests/CarDeveloperOptions/res/xml/development_settings.xml
index 9ed1285..a946526 100644
--- a/tests/CarDeveloperOptions/res/xml/development_settings.xml
+++ b/tests/CarDeveloperOptions/res/xml/development_settings.xml
@@ -30,11 +30,6 @@
android:summary="@string/summary_placeholder"
android:fragment="com.android.car.developeroptions.applications.ProcessStatsSummary" />
- <com.android.car.developeroptions.BugreportPreference
- android:key="bugreport"
- android:title="@*android:string/bugreport_title"
- android:dialogTitle="@*android:string/bugreport_title" />
-
<Preference
android:key="system_server_heap_dump"
android:title="@string/capture_system_heap_dump_title" />
@@ -151,11 +146,6 @@
android:summary="@string/enable_terminal_summary" />
<SwitchPreference
- android:key="bugreport_in_power"
- android:title="@string/bugreport_in_power"
- android:summary="@string/bugreport_in_power_summary" />
-
- <SwitchPreference
android:key="automatic_system_server_heap_dumps"
android:title="@string/automatic_system_heap_dump_title"
android:summary="@string/automatic_system_heap_dump_summary" />
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/BugreportPreference.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/BugreportPreference.java
deleted file mode 100644
index 6acef70..0000000
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/BugreportPreference.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2019 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 com.android.car.developeroptions;
-
-import android.app.ActivityManager;
-import android.app.settings.SettingsEnums;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.os.RemoteException;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.widget.CheckedTextView;
-import android.widget.TextView;
-
-import androidx.appcompat.app.AlertDialog.Builder;
-
-import com.android.car.developeroptions.overlay.FeatureFactory;
-import com.android.settingslib.CustomDialogPreferenceCompat;
-
-public class BugreportPreference extends CustomDialogPreferenceCompat {
-
- private static final String TAG = "BugreportPreference";
-
- private CheckedTextView mInteractiveTitle;
- private TextView mInteractiveSummary;
- private CheckedTextView mFullTitle;
- private TextView mFullSummary;
-
- public BugreportPreference(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onPrepareDialogBuilder(Builder builder, DialogInterface.OnClickListener listener) {
- super.onPrepareDialogBuilder(builder, listener);
-
- final View dialogView = View.inflate(getContext(), R.layout.bugreport_options_dialog, null);
- mInteractiveTitle = (CheckedTextView) dialogView.findViewById(R.id.bugreport_option_interactive_title);
- mInteractiveSummary = (TextView) dialogView.findViewById(R.id.bugreport_option_interactive_summary);
- mFullTitle = (CheckedTextView) dialogView.findViewById(R.id.bugreport_option_full_title);
- mFullSummary = (TextView) dialogView.findViewById(R.id.bugreport_option_full_summary);
- final View.OnClickListener l = new View.OnClickListener() {
-
- @Override
- public void onClick(View v) {
- if (v == mFullTitle || v == mFullSummary) {
- mInteractiveTitle.setChecked(false);
- mFullTitle.setChecked(true);
- }
- if (v == mInteractiveTitle || v == mInteractiveSummary) {
- mInteractiveTitle.setChecked(true);
- mFullTitle.setChecked(false);
- }
- }
- };
- mInteractiveTitle.setOnClickListener(l);
- mFullTitle.setOnClickListener(l);
- mInteractiveSummary.setOnClickListener(l);
- mFullSummary.setOnClickListener(l);
-
- builder.setPositiveButton(com.android.internal.R.string.report, listener);
- builder.setView(dialogView);
- }
-
- @Override
- protected void onClick(DialogInterface dialog, int which) {
- if (which == DialogInterface.BUTTON_POSITIVE) {
-
- final Context context = getContext();
- if (mFullTitle.isChecked()) {
- Log.v(TAG, "Taking full bugreport right away");
- FeatureFactory.getFactory(context).getMetricsFeatureProvider().action(context,
- SettingsEnums.ACTION_BUGREPORT_FROM_SETTINGS_FULL);
- takeBugreport(ActivityManager.BUGREPORT_OPTION_FULL);
- } else {
- Log.v(TAG, "Taking interactive bugreport right away");
- FeatureFactory.getFactory(context).getMetricsFeatureProvider().action(context,
- SettingsEnums.ACTION_BUGREPORT_FROM_SETTINGS_INTERACTIVE);
- takeBugreport(ActivityManager.BUGREPORT_OPTION_INTERACTIVE);
- }
- }
- }
-
- private void takeBugreport(int bugreportType) {
- try {
- ActivityManager.getService().requestBugReport(bugreportType);
- } catch (RemoteException e) {
- Log.e(TAG, "error taking bugreport (bugreportType=" + bugreportType + ")", e);
- }
- }
-}
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/BugReportInPowerPreferenceController.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/BugReportInPowerPreferenceController.java
deleted file mode 100644
index 1f22eb2..0000000
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/BugReportInPowerPreferenceController.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2019 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 com.android.car.developeroptions.development;
-
-import android.content.Context;
-import android.os.UserManager;
-import android.provider.Settings;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
-import androidx.preference.SwitchPreference;
-
-import com.android.car.developeroptions.core.PreferenceControllerMixin;
-import com.android.settingslib.development.DeveloperOptionsPreferenceController;
-
-public class BugReportInPowerPreferenceController extends
- DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener,
- PreferenceControllerMixin {
-
- private static final String KEY_BUGREPORT_IN_POWER = "bugreport_in_power";
-
- @VisibleForTesting
- static int SETTING_VALUE_ON = 1;
- @VisibleForTesting
- static int SETTING_VALUE_OFF = 0;
-
- private final UserManager mUserManager;
-
- public BugReportInPowerPreferenceController(Context context) {
- super(context);
- mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
- }
-
- @Override
- public boolean isAvailable() {
- return !mUserManager.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES);
- }
-
- @Override
- public String getPreferenceKey() {
- return KEY_BUGREPORT_IN_POWER;
- }
-
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- final boolean isEnabled = (Boolean) newValue;
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Global.BUGREPORT_IN_POWER_MENU,
- isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF);
- return true;
- }
-
- @Override
- public void updateState(Preference preference) {
- final int mode = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Global.BUGREPORT_IN_POWER_MENU, SETTING_VALUE_OFF);
- ((SwitchPreference) mPreference).setChecked(mode != SETTING_VALUE_OFF);
- }
-
- @Override
- protected void onDeveloperOptionsSwitchDisabled() {
- super.onDeveloperOptionsSwitchDisabled();
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Global.BUGREPORT_IN_POWER_MENU, SETTING_VALUE_OFF);
- ((SwitchPreference) mPreference).setChecked(false);
- }
-}
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/BugReportPreferenceController.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/BugReportPreferenceController.java
deleted file mode 100644
index 28fb9b5..0000000
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/BugReportPreferenceController.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2019 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 com.android.car.developeroptions.development;
-
-import android.content.Context;
-import android.os.UserManager;
-
-import com.android.car.developeroptions.core.PreferenceControllerMixin;
-import com.android.settingslib.development.DeveloperOptionsPreferenceController;
-
-public class BugReportPreferenceController extends DeveloperOptionsPreferenceController implements
- PreferenceControllerMixin {
-
- private static final String KEY_BUGREPORT = "bugreport";
-
- private final UserManager mUserManager;
-
- public BugReportPreferenceController(Context context) {
- super(context);
-
- mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
- }
-
- @Override
- public boolean isAvailable() {
- return !mUserManager.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES);
- }
-
- @Override
- public String getPreferenceKey() {
- return KEY_BUGREPORT;
- }
-}
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/DevelopmentSettingsDashboardFragment.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/DevelopmentSettingsDashboardFragment.java
index 95c8b6a..3253d21 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/DevelopmentSettingsDashboardFragment.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/development/DevelopmentSettingsDashboardFragment.java
@@ -403,7 +403,6 @@
BluetoothA2dpConfigStore bluetoothA2dpConfigStore) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new MemoryUsagePreferenceController(context));
- controllers.add(new BugReportPreferenceController(context));
controllers.add(new SystemServerHeapDumpPreferenceController(context));
controllers.add(new LocalBackupPasswordPreferenceController(context));
controllers.add(new StayAwakePreferenceController(context, lifecycle));
@@ -418,7 +417,6 @@
controllers.add(new AdbPreferenceController(context, fragment));
controllers.add(new ClearAdbKeysPreferenceController(context, fragment));
controllers.add(new LocalTerminalPreferenceController(context));
- controllers.add(new BugReportInPowerPreferenceController(context));
controllers.add(new AutomaticSystemServerHeapDumpPreferenceController(context));
controllers.add(new MockLocationAppPreferenceController(context, fragment));
controllers.add(new DebugViewAttributesPreferenceController(context));
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java
index b03e320..84b6bcf 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java
@@ -21,9 +21,7 @@
import android.car.CarAppFocusManager.OnAppFocusChangedListener;
import android.car.CarAppFocusManager.OnAppFocusOwnershipCallback;
import android.car.media.CarAudioManager;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.hardware.display.DisplayManager;
@@ -34,7 +32,6 @@
import android.media.HwAudioSource;
import android.os.Bundle;
import android.os.Handler;
-import android.os.IBinder;
import android.os.Looper;
import android.util.Log;
import android.view.Display;
@@ -124,41 +121,39 @@
private void connectCar() {
mContext = getContext();
mHandler = new Handler(Looper.getMainLooper());
- mCar = Car.createCar(mContext, new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- mAppFocusManager =
- (CarAppFocusManager) mCar.getCarManager(Car.APP_FOCUS_SERVICE);
- OnAppFocusChangedListener listener = new OnAppFocusChangedListener() {
- @Override
- public void onAppFocusChanged(int appType, boolean active) {
+ mCar = Car.createCar(mContext, /* handler= */ null,
+ Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, (car, ready) -> {
+ if (!ready) {
+ return;
}
- };
- mAppFocusManager.addFocusListener(listener,
- CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
- mAppFocusManager.addFocusListener(listener,
- CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ mAppFocusManager =
+ (CarAppFocusManager) car.getCarManager(Car.APP_FOCUS_SERVICE);
+ OnAppFocusChangedListener listener = new OnAppFocusChangedListener() {
+ @Override
+ public void onAppFocusChanged(int appType, boolean active) {
+ }
+ };
+ mAppFocusManager.addFocusListener(listener,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ mAppFocusManager.addFocusListener(listener,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
- mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
+ mCarAudioManager = (CarAudioManager) car.getCarManager(Car.AUDIO_SERVICE);
- //take care of zone selection
- int[] zoneList = mCarAudioManager.getAudioZoneIds();
- Integer[] zoneArray = Arrays.stream(zoneList).boxed().toArray(Integer[]::new);
- mZoneAdapter = new ArrayAdapter<>(mContext,
- android.R.layout.simple_spinner_item, zoneArray);
- mZoneAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mZoneSpinner.setAdapter(mZoneAdapter);
- mZoneSpinner.setEnabled(true);
+ //take care of zone selection
+ int[] zoneList = mCarAudioManager.getAudioZoneIds();
+ Integer[] zoneArray = Arrays.stream(zoneList).boxed().toArray(Integer[]::new);
+ mZoneAdapter = new ArrayAdapter<>(mContext,
+ android.R.layout.simple_spinner_item, zoneArray);
+ mZoneAdapter.setDropDownViewResource(
+ android.R.layout.simple_spinner_dropdown_item);
+ mZoneSpinner.setAdapter(mZoneAdapter);
+ mZoneSpinner.setEnabled(true);
- if (mCarAudioManager.isDynamicRoutingEnabled()) {
- setUpDisplayPlayer();
- }
- }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- }
- });
- mCar.connect();
+ if (mCarAudioManager.isDynamicRoutingEnabled()) {
+ setUpDisplayPlayer();
+ }
+ });
}
private void initializePlayers() {
@@ -217,9 +212,16 @@
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
Log.i(TAG, "onCreateView");
+ View view = inflater.inflate(R.layout.audio, container, false);
+ //Zone Spinner
+ setUpZoneSpinnerView(view);
+
+ //Display layout
+ setUpDisplayLayoutView(view);
+
connectCar();
initializePlayers();
- View view = inflater.inflate(R.layout.audio, container, false);
+
mAudioManager = (AudioManager) mContext.getSystemService(
Context.AUDIO_SERVICE);
mAudioFocusHandler = new FocusHandler(
@@ -331,35 +333,6 @@
}
});
- //Zone Spinner
- mZoneSpinner = view.findViewById(R.id.zone_spinner);
- mZoneSpinner.setEnabled(false);
- mZoneSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
- handleZoneSelection();
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- }
- });
-
-
- mDisplayLayout = view.findViewById(R.id.audio_display_layout);
-
- mDisplaySpinner = view.findViewById(R.id.display_spinner);
- mDisplaySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
- handleDisplaySelection();
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- }
- });
-
// Manage buttons for audio player for displays
view.findViewById(R.id.button_display_media_play_start).setOnClickListener(v -> {
startDisplayAudio();
@@ -375,6 +348,37 @@
return view;
}
+ private void setUpDisplayLayoutView(View view) {
+ mDisplayLayout = view.findViewById(R.id.audio_display_layout);
+
+ mDisplaySpinner = view.findViewById(R.id.display_spinner);
+ mDisplaySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+ handleDisplaySelection();
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+ });
+ }
+
+ private void setUpZoneSpinnerView(View view) {
+ mZoneSpinner = view.findViewById(R.id.zone_spinner);
+ mZoneSpinner.setEnabled(false);
+ mZoneSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+ handleZoneSelection();
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+ });
+ }
+
public void handleZoneSelection() {
int position = mZoneSpinner.getSelectedItemPosition();
int zone = mZoneAdapter.getItem(position);
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java
index 7657c38..8886913 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java
@@ -17,6 +17,7 @@
import android.annotation.Nullable;
import android.car.Car;
+import android.car.Car.CarServiceLifecycleListener;
import android.car.CarAppFocusManager;
import android.car.CarNotConnectedException;
import android.car.cluster.navigation.NavigationState;
@@ -33,11 +34,8 @@
import android.car.cluster.navigation.NavigationState.Step;
import android.car.cluster.navigation.NavigationState.Timestamp;
import android.car.navigation.CarNavigationStatusManager;
-import android.content.ComponentName;
-import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.os.Bundle;
-import android.os.IBinder;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -73,19 +71,19 @@
private NavigationStateProto[] mNavStateData;
private Button mTurnByTurnButton;
- private ServiceConnection mCarServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- Log.d(TAG, "Connected to Car Service");
- mCarNavigationStatusManager = (CarNavigationStatusManager) mCarApi
- .getCarManager(Car.CAR_NAVIGATION_SERVICE);
- mCarAppFocusManager = (CarAppFocusManager) mCarApi
- .getCarManager(Car.APP_FOCUS_SERVICE);
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
+ private CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> {
+ if (!ready) {
Log.d(TAG, "Disconnect from Car Service");
+ return;
+ }
+ Log.d(TAG, "Connected to Car Service");
+ try {
+ mCarNavigationStatusManager = (CarNavigationStatusManager) car.getCarManager(
+ Car.CAR_NAVIGATION_SERVICE);
+ mCarAppFocusManager = (CarAppFocusManager) car.getCarManager(
+ Car.APP_FOCUS_SERVICE);
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!", e);
}
};
@@ -117,13 +115,8 @@
private void initCarApi() {
- if (mCarApi != null && mCarApi.isConnected()) {
- mCarApi.disconnect();
- mCarApi = null;
- }
-
- mCarApi = Car.createCar(getContext(), mCarServiceConnection);
- mCarApi.connect();
+ mCarApi = Car.createCar(getContext(), /* handler= */ null,
+ Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, mCarServiceLifecycleListener);
}
@NonNull
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/volume/VolumeTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/volume/VolumeTestFragment.java
index 73e3798..df9aa7b 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/volume/VolumeTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/volume/VolumeTestFragment.java
@@ -16,14 +16,12 @@
package com.google.android.car.kitchensink.volume;
import android.car.Car;
+import android.car.Car.CarServiceLifecycleListener;
import android.car.media.CarAudioManager;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.ServiceConnection;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Handler;
-import android.os.IBinder;
import android.os.Message;
import android.util.Log;
import android.util.SparseIntArray;
@@ -112,20 +110,15 @@
public boolean mHasFocus;
}
- private final ServiceConnection mCarConnectionCallback =
- new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder binder) {
- Log.d(TAG, "Connected to Car Service");
- mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
- initVolumeInfo();
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- Log.d(TAG, "Disconnect from Car Service");
- }
- };
+ private CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> {
+ if (!ready) {
+ Log.d(TAG, "Disconnect from Car Service");
+ return;
+ }
+ Log.d(TAG, "Connected to Car Service");
+ mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
+ initVolumeInfo();
+ };
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@@ -161,8 +154,8 @@
mBalance = v.findViewById(R.id.balance_bar);
mBalance.setOnSeekBarChangeListener(seekListener);
- mCar = Car.createCar(getActivity(), mCarConnectionCallback);
- mCar.connect();
+ mCar = Car.createCar(getActivity(), /* handler= */ null,
+ Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, mCarServiceLifecycleListener);
return v;
}
@@ -210,12 +203,4 @@
}
mAdapter.refreshVolumes(mVolumeInfos);
}
-
- @Override
- public void onDestroy() {
- if (mCar != null) {
- mCar.disconnect();
- }
- super.onDestroy();
- }
}
diff --git a/tests/carservice_test/src/com/android/car/CarUxRestrictionsManagerServiceTest.java b/tests/carservice_test/src/com/android/car/CarUxRestrictionsManagerServiceTest.java
index aadb6f9..0aa89f9 100644
--- a/tests/carservice_test/src/com/android/car/CarUxRestrictionsManagerServiceTest.java
+++ b/tests/carservice_test/src/com/android/car/CarUxRestrictionsManagerServiceTest.java
@@ -27,14 +27,18 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
+import android.car.VehiclePropertyIds;
import android.car.drivingstate.CarDrivingStateEvent;
import android.car.drivingstate.CarUxRestrictions;
import android.car.drivingstate.CarUxRestrictionsConfiguration;
import android.car.drivingstate.CarUxRestrictionsConfiguration.Builder;
+import android.car.drivingstate.ICarDrivingStateChangeListener;
import android.car.hardware.CarPropertyValue;
+import android.car.hardware.property.CarPropertyEvent;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.util.JsonReader;
import android.util.JsonWriter;
@@ -46,6 +50,7 @@
import com.android.car.systeminterface.SystemInterface;
import org.junit.After;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -62,6 +67,7 @@
import java.nio.file.Files;
import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
@RunWith(AndroidJUnit4.class)
@MediumTest
@@ -220,6 +226,198 @@
assertTrue(restrictions.toString(), expected.isSameRestrictions(restrictions));
}
+ // This test only involves calling a few methods and should finish very quickly. If it doesn't
+ // finish in 20s, we probably encountered a deadlock.
+ @Test(timeout = 20000)
+ public void testInitService_NoDeadlockWithCarDrivingStateService()
+ throws Exception {
+
+ CarDrivingStateService drivingStateService = new CarDrivingStateService(mSpyContext,
+ mMockCarPropertyService);
+ CarUxRestrictionsManagerService uxRestrictionsService = new CarUxRestrictionsManagerService(
+ mSpyContext, drivingStateService, mMockCarPropertyService);
+
+ CountDownLatch dispatchingStartedSignal = new CountDownLatch(1);
+ CountDownLatch initCompleteSignal = new CountDownLatch(1);
+
+ // A deadlock can exist when the dispatching of a listener is synchronized. For instance,
+ // the CarUxRestrictionsManagerService#init() method registers a callback like this one. The
+ // deadlock risk occurs if:
+ // 1. CarUxRestrictionsManagerService has registered a listener with CarDrivingStateService
+ // 2. A synchronized method of CarUxRestrictionsManagerService starts to run
+ // 3. While the method from (2) is running, a property event occurs on a different thread
+ // that triggers a drive state event in CarDrivingStateService. If CarDrivingStateService
+ // handles the property event in a synchronized method, then CarDrivingStateService is
+ // locked. The listener from (1) will wait until the lock on
+ // CarUxRestrictionsManagerService is released.
+ // 4. The synchronized method from (2) attempts to access CarDrivingStateService. For
+ // example, the implementation below attempts to read the restriction mode.
+ //
+ // In the above steps, both CarUxRestrictionsManagerService and CarDrivingStateService are
+ // locked and waiting on each other, hence the deadlock.
+ drivingStateService.registerDrivingStateChangeListener(
+ new ICarDrivingStateChangeListener.Stub() {
+ @Override
+ public void onDrivingStateChanged(CarDrivingStateEvent event)
+ throws RemoteException {
+ // EVENT 2 [new thread]: this callback is called from within
+ // handlePropertyEvent(), which might (but shouldn't) lock
+ // CarDrivingStateService
+
+ // Notify that the dispatching process has started
+ dispatchingStartedSignal.countDown();
+
+ try {
+ // EVENT 3b [new thread]: Wait until init() has finished. If these
+ // threads don't have lock dependencies, there is no reason there
+ // would be an issue with waiting.
+ //
+ // In the real world, this wait could represent a long-running
+ // task, or hitting the below line that attempts to access the
+ // CarUxRestrictionsManagerService (which might be locked while init
+ // () is running).
+ //
+ // If there is a deadlock while waiting for init to complete, we will
+ // never progress past this line.
+ initCompleteSignal.await();
+ } catch (InterruptedException e) {
+ Assert.fail("onDrivingStateChanged thread interrupted");
+ }
+
+ // Attempt to access CarUxRestrictionsManagerService. If
+ // CarUxRestrictionsManagerService is locked because it is doing its own
+ // work, then this will wait.
+ //
+ // This line won't execute in the deadlock flow. However, it is an example
+ // of a real-world piece of code that would serve the same role as the above
+ uxRestrictionsService.getCurrentUxRestrictions();
+ }
+ });
+
+ // EVENT 1 [new thread]: handlePropertyEvent() is called, which locks CarDrivingStateService
+ // Ideally CarPropertyService would trigger the change event, but since that is mocked
+ // we manually trigger the event. This event is what eventually triggers the dispatch to
+ // ICarDrivingStateChangeListener that was defined above.
+ Runnable propertyChangeEventRunnable =
+ () -> drivingStateService.handlePropertyEvent(
+ new CarPropertyEvent(CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE,
+ new CarPropertyValue<>(
+ VehiclePropertyIds.PERF_VEHICLE_SPEED, 0, 100f)));
+ Thread thread = new Thread(propertyChangeEventRunnable);
+ thread.start();
+
+ // Wait until propertyChangeEventRunnable has triggered and the
+ // ICarDrivingStateChangeListener callback declared above started to run.
+ dispatchingStartedSignal.await();
+
+ // EVENT 3a [main thread]: init() is called, which locks CarUxRestrictionsManagerService
+ // If init() is synchronized, thereby locking CarUxRestrictionsManagerService, and it
+ // internally attempts to access CarDrivingStateService, and if CarDrivingStateService has
+ // been locked because of the above listener, then both classes are locked and waiting on
+ // each other, so we would encounter a deadlock.
+ uxRestrictionsService.init();
+
+ // If there is a deadlock in init(), then this will never be called
+ initCompleteSignal.countDown();
+
+ // wait for thread to join to leave in a deterministic state
+ try {
+ thread.join(5000);
+ } catch (InterruptedException e) {
+ Assert.fail("Thread failed to join");
+ }
+ }
+
+ // This test only involves calling a few methods and should finish very quickly. If it doesn't
+ // finish in 20s, we probably encountered a deadlock.
+ @Test(timeout = 20000)
+ public void testSetUxRChangeBroadcastEnabled_NoDeadlockWithCarDrivingStateService()
+ throws Exception {
+
+ CarDrivingStateService drivingStateService = new CarDrivingStateService(mSpyContext,
+ mMockCarPropertyService);
+ CarUxRestrictionsManagerService uxRestrictionService = new CarUxRestrictionsManagerService(
+ mSpyContext, drivingStateService, mMockCarPropertyService);
+
+ CountDownLatch dispatchingStartedSignal = new CountDownLatch(1);
+ CountDownLatch initCompleteSignal = new CountDownLatch(1);
+
+ // See testInitService_NoDeadlockWithCarDrivingStateService for details on why a deadlock
+ // may occur. This test could fail for the same reason, except the callback we register here
+ // is purely to introduce a delay, and the deadlock actually happens inside the callback
+ // that CarUxRestrictionsManagerService#init() registers internally.
+ drivingStateService.registerDrivingStateChangeListener(
+ new ICarDrivingStateChangeListener.Stub() {
+ @Override
+ public void onDrivingStateChanged(CarDrivingStateEvent event)
+ throws RemoteException {
+ // EVENT 2 [new thread]: this callback is called from within
+ // handlePropertyEvent(), which might (but shouldn't) lock
+ // CarDrivingStateService
+
+ // Notify that the dispatching process has started
+ dispatchingStartedSignal.countDown();
+
+ try {
+ // EVENT 3b [new thread]: Wait until init() has finished. If these
+ // threads don't have lock dependencies, there is no reason there
+ // would be an issue with waiting.
+ //
+ // In the real world, this wait could represent a long-running
+ // task, or hitting the line inside
+ // CarUxRestrictionsManagerService#init()'s internal registration
+ // that attempts to access the CarUxRestrictionsManagerService (which
+ // might be locked while init() is running).
+ //
+ // If there is a deadlock while waiting for init to complete, we will
+ // never progress past this line.
+ initCompleteSignal.await();
+ } catch (InterruptedException e) {
+ Assert.fail("onDrivingStateChanged thread interrupted");
+ }
+ }
+ });
+
+ // The init() method internally registers a callback to CarDrivingStateService
+ uxRestrictionService.init();
+
+ // EVENT 1 [new thread]: handlePropertyEvent() is called, which locks CarDrivingStateService
+ // Ideally CarPropertyService would trigger the change event, but since that is mocked
+ // we manually trigger the event. This event eventually triggers the dispatch to
+ // ICarDrivingStateChangeListener that was defined above and a dispatch to the registration
+ // that CarUxRestrictionsManagerService internally made to CarDrivingStateService in
+ // CarUxRestrictionsManagerService#init().
+ Runnable propertyChangeEventRunnable =
+ () -> drivingStateService.handlePropertyEvent(
+ new CarPropertyEvent(CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE,
+ new CarPropertyValue<>(
+ VehiclePropertyIds.PERF_VEHICLE_SPEED, 0, 100f)));
+ Thread thread = new Thread(propertyChangeEventRunnable);
+ thread.start();
+
+ // Wait until propertyChangeEventRunnable has triggered and the
+ // ICarDrivingStateChangeListener callback declared above started to run.
+ dispatchingStartedSignal.await();
+
+ // EVENT 3a [main thread]: a synchronized method is called, which locks
+ // CarUxRestrictionsManagerService
+ //
+ // Any synchronized method that internally accesses CarDrivingStateService could encounter a
+ // deadlock if the above setup locks CarDrivingStateService.
+ uxRestrictionService.setUxRChangeBroadcastEnabled(true);
+
+ // If there is a deadlock in init(), then this will never be called
+ initCompleteSignal.countDown();
+
+ // wait for thread to join to leave in a deterministic state
+ try {
+ thread.join(5000);
+ } catch (InterruptedException e) {
+ Assert.fail("Thread failed to join");
+ }
+ }
+
+
private CarUxRestrictionsConfiguration createEmptyConfig() {
return createEmptyConfig(null);
}
diff --git a/tests/carservice_test/src/com/android/car/MockedCarTestBase.java b/tests/carservice_test/src/com/android/car/MockedCarTestBase.java
index 894c402..5989000 100644
--- a/tests/carservice_test/src/com/android/car/MockedCarTestBase.java
+++ b/tests/carservice_test/src/com/android/car/MockedCarTestBase.java
@@ -261,11 +261,10 @@
if (mRealCarServiceReleased) {
return; // We just want to release it once.
}
-
mRealCarServiceReleased = true; // To make sure it was called once.
Object waitForConnection = new Object();
- android.car.Car car = android.car.Car.createCar(context, new ServiceConnection() {
+ Car car = android.car.Car.createCar(context, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (waitForConnection) {
@@ -287,10 +286,10 @@
if (car.isConnected()) {
Log.i(TAG, "Connected to real car service");
CarTestManagerBinderWrapper binderWrapper =
- (CarTestManagerBinderWrapper) car.getCarManager(android.car.Car.TEST_SERVICE);
+ (CarTestManagerBinderWrapper) car.getCarManager(Car.TEST_SERVICE);
assertNotNull(binderWrapper);
- CarTestManager mgr = new CarTestManager(binderWrapper.binder);
+ CarTestManager mgr = new CarTestManager(car, binderWrapper.binder);
mgr.stopCarService(mCarServiceToken);
}
}
diff --git a/tests/carservice_unit_test/AndroidManifest.xml b/tests/carservice_unit_test/AndroidManifest.xml
index e5e31bf..5ed59ef 100644
--- a/tests/carservice_unit_test/AndroidManifest.xml
+++ b/tests/carservice_unit_test/AndroidManifest.xml
@@ -21,6 +21,7 @@
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.car.carservice_unittest"
android:label="Unit Tests for Car APIs"/>
+ <uses-permission android:name="android.car.permission.CAR_ENROLL_TRUST" />
<application android:label="CarServiceUnitTest"
android:debuggable="true">
diff --git a/tests/carservice_unit_test/src/android/car/CarTest.java b/tests/carservice_unit_test/src/android/car/CarTest.java
index 9ac8d75..d12334d 100644
--- a/tests/carservice_unit_test/src/android/car/CarTest.java
+++ b/tests/carservice_unit_test/src/android/car/CarTest.java
@@ -22,6 +22,8 @@
import static com.google.common.truth.Truth.assertThat;
+import static junit.framework.Assert.fail;
+
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.times;
@@ -177,7 +179,13 @@
runOnMainSyncSafe(() -> {
car.getServiceConnectionListener().onServiceConnected(new ComponentName("", ""),
mService);
- car.getServiceConnectionListener().onServiceDisconnected(new ComponentName("", ""));
+ try {
+ car.getServiceConnectionListener().onServiceDisconnected(new ComponentName("", ""));
+ } catch (IllegalStateException e) {
+ // expected
+ return;
+ }
+ fail("onServiceDisconnected should have triggered exception");
});
}
diff --git a/tests/carservice_unit_test/src/com/android/car/hal/PropertyHalServiceTest.java b/tests/carservice_unit_test/src/com/android/car/hal/PropertyHalServiceTest.java
new file mode 100644
index 0000000..6f7fe18
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/hal/PropertyHalServiceTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2019 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 com.android.car.hal;
+
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@RunWith(AndroidJUnit4.class)
+public class PropertyHalServiceTest {
+ @Rule
+ public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+ @Mock
+ private VehicleHal mVehicleHal;
+
+ private PropertyHalService mPropertyHalService;
+ private static final int[] UNITS_PROPERTY_ID = {
+ VehicleProperty.DISTANCE_DISPLAY_UNITS,
+ VehicleProperty.FUEL_CONSUMPTION_UNITS_DISTANCE_OVER_VOLUME,
+ VehicleProperty.FUEL_VOLUME_DISPLAY_UNITS,
+ VehicleProperty.TIRE_PRESSURE_DISPLAY_UNITS,
+ VehicleProperty.EV_BATTERY_DISPLAY_UNITS,
+ VehicleProperty.VEHICLE_SPEED_DISPLAY_UNITS};
+
+ @Before
+ public void setUp() {
+ mPropertyHalService = new PropertyHalService(mVehicleHal);
+ mPropertyHalService.init();
+ }
+
+ @After
+ public void tearDown() {
+ mPropertyHalService.release();
+ mPropertyHalService = null;
+ }
+
+ @Test
+ public void checkDisplayUnitsProperty() {
+ for (int propId : UNITS_PROPERTY_ID) {
+ Assert.assertTrue(mPropertyHalService.isDisplayUnitsProperty(propId));
+ }
+ }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/vms/VmsClientManagerTest.java b/tests/carservice_unit_test/src/com/android/car/vms/VmsClientManagerTest.java
index b90dac4..73217e7 100644
--- a/tests/carservice_unit_test/src/com/android/car/vms/VmsClientManagerTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/vms/VmsClientManagerTest.java
@@ -97,6 +97,8 @@
private static final String HAL_CLIENT_NAME = "HalClient";
private static final String UNKNOWN_PACKAGE = "UnknownPackage";
+ private static final long MILLIS_BEFORE_REBIND = 100;
+
@Rule
public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Mock
@@ -120,6 +122,12 @@
private VmsHalService mHal;
@Mock
+ private Handler mHandler;
+
+ @Captor
+ private ArgumentCaptor<Runnable> mRebindCaptor;
+
+ @Mock
private VmsPublisherService mPublisherService;
@Mock
@@ -152,7 +160,7 @@
when(mResources.getInteger(
com.android.car.R.integer.millisecondsBeforeRebindToVmsPublisher)).thenReturn(
- 5);
+ (int) MILLIS_BEFORE_REBIND);
when(mResources.getStringArray(
com.android.car.R.array.vmsPublisherSystemClients)).thenReturn(
new String[]{ SYSTEM_CLIENT });
@@ -168,7 +176,7 @@
mCallingAppUid = UserHandle.getUid(USER_ID, 0);
mClientManager = new VmsClientManager(mContext, mBrokerService, mUserService,
- mUserManagerHelper, mHal, () -> mCallingAppUid);
+ mUserManagerHelper, mHal, mHandler, () -> mCallingAppUid);
verify(mHal).setClientManager(mClientManager);
mClientManager.setPublisherService(mPublisherService);
@@ -180,11 +188,10 @@
@After
public void tearDown() throws Exception {
- Thread.sleep(10); // Time to allow for delayed rebinds to settle
verify(mContext, atLeast(0)).getSystemService(eq(Context.USER_SERVICE));
verify(mContext, atLeast(0)).getResources();
verify(mContext, atLeast(0)).getPackageManager();
- verifyNoMoreInteractions(mContext, mBrokerService, mHal, mPublisherService);
+ verifyNoMoreInteractions(mContext, mBrokerService, mHal, mPublisherService, mHandler);
}
@Test
@@ -467,7 +474,7 @@
connection.onServiceDisconnected(null);
verify(mPublisherService).onClientDisconnected(eq(SYSTEM_CLIENT_NAME));
- Thread.sleep(10);
+ verifyAndRunRebindTask();
verify(mContext).unbindService(connection);
verifySystemBind(1);
}
@@ -490,6 +497,8 @@
binder = createPublisherBinder();
connection.onServiceConnected(null, binder);
verifyOnClientConnected(SYSTEM_CLIENT_NAME, binder);
+
+ verifyAndRunRebindTask();
// No more interactions (verified by tearDown)
}
@@ -507,7 +516,7 @@
connection.onBindingDied(null);
verify(mPublisherService).onClientDisconnected(eq(SYSTEM_CLIENT_NAME));
- Thread.sleep(10);
+ verifyAndRunRebindTask();
verify(mContext).unbindService(connection);
verifySystemBind(1);
}
@@ -523,7 +532,7 @@
verifyZeroInteractions(mPublisherService);
- Thread.sleep(10);
+ verifyAndRunRebindTask();
verify(mContext).unbindService(connection);
verifySystemBind(1);
}
@@ -541,7 +550,7 @@
connection.onServiceDisconnected(null);
verify(mPublisherService).onClientDisconnected(eq(USER_CLIENT_NAME));
- Thread.sleep(10);
+ verifyAndRunRebindTask();
verify(mContext).unbindService(connection);
verifyUserBind(1);
}
@@ -564,6 +573,8 @@
binder = createPublisherBinder();
connection.onServiceConnected(null, binder);
verifyOnClientConnected(USER_CLIENT_NAME, binder);
+
+ verifyAndRunRebindTask();
// No more interactions (verified by tearDown)
}
@@ -580,7 +591,7 @@
connection.onBindingDied(null);
verify(mPublisherService).onClientDisconnected(eq(USER_CLIENT_NAME));
- Thread.sleep(10);
+ verifyAndRunRebindTask();
verify(mContext).unbindService(connection);
verifyUserBind(1);
}
@@ -596,7 +607,7 @@
verifyZeroInteractions(mPublisherService);
- Thread.sleep(10);
+ verifyAndRunRebindTask();
verify(mContext).unbindService(connection);
verifyUserBind(1);
}
@@ -975,6 +986,11 @@
eq(Context.BIND_AUTO_CREATE), any(Handler.class), eq(user));
}
+ private void verifyAndRunRebindTask() {
+ verify(mHandler).postDelayed(mRebindCaptor.capture(), eq(MILLIS_BEFORE_REBIND));
+ mRebindCaptor.getValue().run();
+ }
+
private void verifyOnClientConnected(String publisherName, IBinder binder) {
ArgumentCaptor<IVmsPublisherClient> clientCaptor =
ArgumentCaptor.forClass(IVmsPublisherClient.class);
diff --git a/user/car-user-lib/src/android/car/userlib/CarUserManagerHelper.java b/user/car-user-lib/src/android/car/userlib/CarUserManagerHelper.java
index 0745945..72332d9 100644
--- a/user/car-user-lib/src/android/car/userlib/CarUserManagerHelper.java
+++ b/user/car-user-lib/src/android/car/userlib/CarUserManagerHelper.java
@@ -238,10 +238,8 @@
// If an override user is present and a real user, return it
if (bootUserOverride != BOOT_USER_NOT_FOUND
&& allUsers.contains(bootUserOverride)) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Boot user id override found for initial user, user id: "
- + bootUserOverride);
- }
+ Log.i(TAG, "Boot user id override found for initial user, user id: "
+ + bootUserOverride);
return bootUserOverride;
}
@@ -249,19 +247,15 @@
int lastActiveUser = getLastActiveUser();
if (lastActiveUser != UserHandle.USER_SYSTEM
&& allUsers.contains(lastActiveUser)) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Last active user loaded for initial user, user id: "
- + lastActiveUser);
- }
+ Log.i(TAG, "Last active user loaded for initial user, user id: "
+ + lastActiveUser);
return lastActiveUser;
}
// If all else fails, return the smallest user id
int returnId = Collections.min(allUsers);
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Saved ids were invalid. Returning smallest user id, user id: "
- + returnId);
- }
+ Log.i(TAG, "Saved ids were invalid. Returning smallest user id, user id: "
+ + returnId);
return returnId;
}