Add device state Hal service
Forward device state change changes to all
registered Hal service listeners.
Flag: EXEMPT HAL interface change
Test: atest VtsHalDeviceStateServiceTargetTest
Test: atest FrameworksServicesTests:DeviceStateManagerServiceTest
Bug: 362601542
Change-Id: If7d8616b97b44cfc1974ceddc6b12a816a0d688e
diff --git a/services/core/Android.bp b/services/core/Android.bp
index aea16b0..d2b8bda 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -178,6 +178,7 @@
static_libs: [
"android.frameworks.vibrator-V1-java", // AIDL
+ "android.frameworks.devicestate-V1-java", // AIDL
"android.hardware.authsecret-V1.0-java",
"android.hardware.authsecret-V1-java",
"android.hardware.boot-V1.0-java", // HIDL
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 8b9c664..2513443 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -19,7 +19,16 @@
import static android.Manifest.permission.CONTROL_DEVICE_STATE;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FEATURE_DUAL_DISPLAY;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FEATURE_REAR_DISPLAY;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT;
import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY;
import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
import static android.hardware.devicestate.DeviceState.PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST;
import static android.hardware.devicestate.DeviceState.PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS;
@@ -44,6 +53,10 @@
import android.app.ActivityManagerInternal;
import android.app.IProcessObserver;
import android.content.Context;
+import android.frameworks.devicestate.DeviceStateConfiguration;
+import android.frameworks.devicestate.ErrorCode;
+import android.frameworks.devicestate.IDeviceStateListener;
+import android.frameworks.devicestate.IDeviceStateService;
import android.hardware.devicestate.DeviceState;
import android.hardware.devicestate.DeviceStateInfo;
import android.hardware.devicestate.DeviceStateManager;
@@ -56,9 +69,12 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
import android.os.ShellCallback;
import android.os.SystemProperties;
import android.os.Trace;
+import android.util.LongSparseLongArray;
import android.util.Slog;
import android.util.SparseArray;
@@ -82,6 +98,7 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.WeakHashMap;
@@ -130,6 +147,8 @@
@NonNull
private final BinderService mBinderService;
@NonNull
+ private final HalService mHalService;
+ @NonNull
private final OverrideRequestController mOverrideRequestController;
@NonNull
private final DeviceStateProviderListener mDeviceStateProviderListener;
@@ -139,7 +158,7 @@
// All supported device states keyed by identifier.
@GuardedBy("mLock")
- private SparseArray<DeviceState> mDeviceStates = new SparseArray<>();
+ private final SparseArray<DeviceState> mDeviceStates = new SparseArray<>();
// The current committed device state. Will be empty until the first device state provided by
// the DeviceStateProvider is committed.
@@ -177,7 +196,7 @@
@GuardedBy("mLock")
private final SparseArray<ProcessRecord> mProcessRecords = new SparseArray<>();
- private Set<Integer> mDeviceStatesAvailableForAppRequests = new HashSet<>();
+ private final Set<Integer> mDeviceStatesAvailableForAppRequests = new HashSet<>();
private Set<Integer> mFoldedDeviceStates = new HashSet<>();
@@ -259,6 +278,7 @@
mDeviceStateProviderListener = new DeviceStateProviderListener();
mDeviceStatePolicy.getDeviceStateProvider().setListener(mDeviceStateProviderListener);
mBinderService = new BinderService();
+ mHalService = new HalService();
mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
mDeviceStateNotificationController = new DeviceStateNotificationController(
context, mHandler,
@@ -272,6 +292,10 @@
@Override
public void onStart() {
publishBinderService(Context.DEVICE_STATE_SERVICE, mBinderService);
+ String halServiceName = IDeviceStateService.DESCRIPTOR + "/default";
+ if (ServiceManager.isDeclared(halServiceName)) {
+ publishBinderService(halServiceName, mHalService);
+ }
publishLocalService(DeviceStateManagerInternal.class, new LocalService());
if (!Flags.deviceStatePropertyMigration()) {
@@ -440,6 +464,11 @@
return mBinderService;
}
+ @VisibleForTesting
+ IDeviceStateService getHalBinderService() {
+ return mHalService;
+ }
+
private void updateSupportedStates(DeviceState[] supportedDeviceStates,
@DeviceStateProvider.SupportedStatesUpdatedReason int reason) {
synchronized (mLock) {
@@ -1282,6 +1311,124 @@
}
}
+ private final class HalService extends IDeviceStateService.Stub {
+ private final LongSparseLongArray mPublicProperties = new LongSparseLongArray();
+ public HalService() {
+ mPublicProperties.put(
+ DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED,
+ FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED);
+ mPublicProperties.put(
+ DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN,
+ FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN);
+ mPublicProperties.put(
+ DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN,
+ FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN);
+ mPublicProperties.put(
+ PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY,
+ FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY);
+ mPublicProperties.put(
+ PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY,
+ FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY);
+ mPublicProperties.put(
+ PROPERTY_FEATURE_REAR_DISPLAY,
+ FEATURE_REAR_DISPLAY);
+ mPublicProperties.put(
+ PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT,
+ FEATURE_DUAL_DISPLAY);
+ }
+
+ private final class HalBinderCallback implements IDeviceStateManagerCallback {
+ private final IDeviceStateListener mListener;
+
+ private HalBinderCallback(@NonNull IDeviceStateListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void onDeviceStateInfoChanged(DeviceStateInfo info) throws RemoteException {
+ DeviceStateConfiguration config = new DeviceStateConfiguration();
+ Set<Integer> systemProperties = new HashSet<>(
+ info.currentState.getConfiguration().getSystemProperties());
+ Set<Integer> physicalProperties = new HashSet<>(
+ info.currentState.getConfiguration().getPhysicalProperties());
+ config.deviceProperties = 0;
+ for (Integer prop : systemProperties) {
+ Long publicProperty = mPublicProperties.get(prop);
+ if (publicProperty != null) {
+ config.deviceProperties |= publicProperty.longValue();
+ }
+ }
+ for (Integer prop : physicalProperties) {
+ Long publicProperty = mPublicProperties.get(prop);
+ if (publicProperty != null) {
+ config.deviceProperties |= publicProperty.longValue();
+ }
+ }
+ mListener.onDeviceStateChanged(config);
+ }
+
+ @Override
+ public void onRequestActive(IBinder token) {
+ //No-op
+ }
+
+ @Override
+ public void onRequestCanceled(IBinder token) {
+ //No-op
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return mListener.asBinder();
+ }
+ }
+
+ @Override
+ public void registerListener(IDeviceStateListener listener) throws RemoteException {
+ if (listener == null) {
+ throw new ServiceSpecificException(ErrorCode.BAD_INPUT);
+ }
+
+ final int callingPid = Binder.getCallingPid();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ HalBinderCallback callback = new HalBinderCallback(listener);
+ DeviceStateInfo info = registerProcess(callingPid, callback);
+ if (info != null) {
+ callback.onDeviceStateInfoChanged(info);
+ }
+ } catch (SecurityException e) {
+ throw new ServiceSpecificException(ErrorCode.ALREADY_EXISTS);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void unregisterListener(IDeviceStateListener listener) throws RemoteException {
+ final int callingPid = Binder.getCallingPid();
+
+ synchronized (mLock) {
+ if (mProcessRecords.contains(callingPid)) {
+ mProcessRecords.remove(callingPid);
+ return;
+ }
+ }
+
+ throw new ServiceSpecificException(ErrorCode.BAD_INPUT);
+ }
+
+ @Override
+ public int getInterfaceVersion() throws RemoteException {
+ return IDeviceStateService.VERSION;
+ }
+
+ @Override
+ public String getInterfaceHash() throws RemoteException {
+ return IDeviceStateService.HASH;
+ }
+ }
+
/** Implementation of {@link IDeviceStateManager} published as a binder service. */
private final class BinderService extends IDeviceStateManager.Stub {
diff --git a/services/manifest_services.xml b/services/manifest_services.xml
index 114fe32..9457205 100644
--- a/services/manifest_services.xml
+++ b/services/manifest_services.xml
@@ -4,4 +4,9 @@
<version>2</version>
<fqname>IAltitudeService/default</fqname>
</hal>
+ <hal format="aidl">
+ <name>android.frameworks.devicestate</name>
+ <version>1</version>
+ <fqname>IDeviceStateService/default</fqname>
+ </hal>
</manifest>
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index ab5a5a9..5127b2d 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -24,9 +24,14 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertThrows;
import android.content.Context;
+import android.frameworks.devicestate.DeviceStateConfiguration;
+import android.frameworks.devicestate.ErrorCode;
+import android.frameworks.devicestate.IDeviceStateListener;
+import android.frameworks.devicestate.IDeviceStateService;
import android.hardware.devicestate.DeviceState;
import android.hardware.devicestate.DeviceStateInfo;
import android.hardware.devicestate.DeviceStateRequest;
@@ -34,6 +39,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.FlagsParameterization;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -336,6 +342,53 @@
}
@Test
+ public void halRegisterUnregisterCallback() throws RemoteException {
+ IDeviceStateService halService = mService.getHalBinderService();
+ IDeviceStateListener halListener = new IDeviceStateListener.Stub() {
+ @Override
+ public void onDeviceStateChanged(DeviceStateConfiguration deviceState) { }
+
+ @Override
+ public int getInterfaceVersion() {
+ return IDeviceStateListener.VERSION;
+ }
+
+ @Override
+ public String getInterfaceHash() {
+ return IDeviceStateListener.HASH;
+ }
+ };
+
+ int errorCode = ErrorCode.OK;
+ try {
+ halService.unregisterListener(halListener);
+ } catch(ServiceSpecificException e) {
+ errorCode = e.errorCode;
+ }
+ assertEquals(errorCode, ErrorCode.BAD_INPUT);
+
+ errorCode = ErrorCode.OK;
+ try {
+ halService.unregisterListener(null);
+ } catch(ServiceSpecificException e) {
+ errorCode = e.errorCode;
+ }
+ assertEquals(errorCode, ErrorCode.BAD_INPUT);
+
+ halService.registerListener(halListener);
+
+ errorCode = ErrorCode.OK;
+ try {
+ halService.registerListener(halListener);
+ } catch (ServiceSpecificException e) {
+ errorCode = e.errorCode;
+ }
+ assertEquals(errorCode, ErrorCode.ALREADY_EXISTS);
+
+ halService.unregisterListener(halListener);
+ }
+
+ @Test
public void registerCallback() throws RemoteException {
final TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
mService.getBinderService().registerCallback(callback);