Loading core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java +14 −7 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.util.ArrayMap; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.window.flags.Flags; import java.util.ArrayList; import java.util.List; Loading @@ -47,6 +48,7 @@ import java.util.concurrent.Executor; @VisibleForTesting(visibility = Visibility.PACKAGE) public final class DeviceStateManagerGlobal { @Nullable @GuardedBy("DeviceStateManagerGlobal.class") private static DeviceStateManagerGlobal sInstance; private static final String TAG = "DeviceStateManagerGlobal"; private static final boolean DEBUG = Build.IS_DEBUGGABLE; Loading Loading @@ -84,10 +86,12 @@ public final class DeviceStateManagerGlobal { @GuardedBy("mLock") private DeviceStateInfo mLastReceivedInfo; // Constructor should be called while holding the lock. // @GuardedBy("DeviceStateManagerGlobal.class") can't be used on constructors. @VisibleForTesting public DeviceStateManagerGlobal(@NonNull IDeviceStateManager deviceStateManager) { mDeviceStateManager = deviceStateManager; registerCallbackIfNeededLocked(); registerCallbackLocked(); } /** Loading Loading @@ -279,14 +283,17 @@ public final class DeviceStateManagerGlobal { } } private void registerCallbackIfNeededLocked() { if (mCallback != null) { return; } @GuardedBy("DeviceStateManagerGlobal.class") private void registerCallbackLocked() { mCallback = new DeviceStateManagerCallback(); try { if (Flags.wlinfoOncreate()) { synchronized (mLock) { mLastReceivedInfo = mDeviceStateManager.registerCallback(mCallback); } } else { mDeviceStateManager.registerCallback(mCallback); } } catch (RemoteException ex) { mCallback = null; throw ex.rethrowFromSystemServer(); Loading core/java/android/hardware/devicestate/IDeviceStateManager.aidl +11 −3 Original line number Diff line number Diff line Loading @@ -32,16 +32,24 @@ interface IDeviceStateManager { DeviceStateInfo getDeviceStateInfo(); /** * Registers a callback to receive notifications from the device state manager. Only one * callback can be registered per-process. * Registers a callback to receive notifications from the device state manager and returns the * current {@link DeviceStateInfo}. Only one callback can be registered per-process. * <p> * As the callback mechanism is used to alert the caller of changes to request status a callback * <b>MUST</b> be registered before calling {@link #requestState(IBinder, int, int)} or * {@link #cancelRequest(IBinder)}, otherwise an exception will be thrown. * <p> * Upon successful registration, this method returns the committed {@link DeviceStateInfo} if * available, ensuring the availability of the device state after the callback is registered. * This guarantees that the client will have access to the latest device state immediately upon * completion of the two-way IPC call. * * @param callback the callback to register for device state notifications. * @return DeviceStateInfo the current device state information including the committed state * or null if no state has been committed by the {@link DeviceStateProvider} yet. * @throws SecurityException if a callback is already registered for the calling process. */ void registerCallback(in IDeviceStateManagerCallback callback); @nullable DeviceStateInfo registerCallback(in IDeviceStateManagerCallback callback); /** * Requests that the device enter the supplied {@code state}. A callback <b>MUST</b> have been Loading core/tests/devicestatetests/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -28,8 +28,10 @@ android_test { static_libs: [ "androidx.test.ext.junit", "androidx.test.rules", "flag-junit", "frameworks-base-testutils", "mockito-target-minus-junit4", "platform-parametric-runner-lib", "platform-test-annotations", "truth", ], Loading core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java +74 −7 Original line number Diff line number Diff line Loading @@ -18,8 +18,10 @@ package android.hardware.devicestate; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; Loading @@ -29,15 +31,20 @@ import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback; import android.os.IBinder; import android.os.RemoteException; import android.os.test.FakePermissionEnforcer; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.FlagsParameterization; import android.platform.test.flag.junit.SetFlagsRule; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.util.ConcurrentUtils; import com.android.window.flags.Flags; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; Loading @@ -46,6 +53,9 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import platform.test.runner.parameterized.ParameterizedAndroidJunit4; import platform.test.runner.parameterized.Parameters; /** * Unit tests for {@link DeviceStateManagerGlobal}. * Loading @@ -53,18 +63,30 @@ import java.util.Set; * atest FrameworksCoreDeviceStateManagerTests:DeviceStateManagerGlobalTest */ @SmallTest @RunWith(AndroidJUnit4.class) @RunWith(ParameterizedAndroidJunit4.class) public final class DeviceStateManagerGlobalTest { private static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState( new DeviceState.Configuration.Builder(0 /* identifier */, "" /* name */).build()); private static final DeviceState OTHER_DEVICE_STATE = new DeviceState( new DeviceState.Configuration.Builder(1 /* identifier */, "" /* name */).build()); @Rule public final SetFlagsRule mSetFlagsRule; @Parameters(name = "{0}") public static List<FlagsParameterization> getParams() { return FlagsParameterization.allCombinationsOf(Flags.FLAG_WLINFO_ONCREATE); } @NonNull private TestDeviceStateManagerService mService; @NonNull private DeviceStateManagerGlobal mDeviceStateManagerGlobal; public DeviceStateManagerGlobalTest(FlagsParameterization flags) { mSetFlagsRule = new SetFlagsRule(flags); } @Before public void setUp() { final FakePermissionEnforcer permissionEnforcer = new FakePermissionEnforcer(); Loading @@ -73,6 +95,36 @@ public final class DeviceStateManagerGlobalTest { assertThat(mService.mCallbacks).isNotEmpty(); } @Test @DisableFlags(Flags.FLAG_WLINFO_ONCREATE) public void create_whenWlinfoOncreateIsDisabled_receivesDeviceStateInfoFromCallback() { final FakePermissionEnforcer permissionEnforcer = new FakePermissionEnforcer(); final TestDeviceStateManagerService service = new TestDeviceStateManagerService( permissionEnforcer, true /* simulatePostCallback */); final DeviceStateManagerGlobal dsmGlobal = new DeviceStateManagerGlobal(service); final DeviceStateCallback callback = mock(DeviceStateCallback.class); dsmGlobal.registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); verify(callback, never()).onDeviceStateChanged(any()); // Simulate DeviceStateManagerService#registerProcess by notifying clients of current device // state via callback. service.notifyDeviceStateInfoChanged(); verify(callback).onDeviceStateChanged(eq(DEFAULT_DEVICE_STATE)); } @Test @EnableFlags(Flags.FLAG_WLINFO_ONCREATE) public void create_whenWlinfoOncreateIsEnabled_returnsDeviceStateInfoFromRegistration() { final FakePermissionEnforcer permissionEnforcer = new FakePermissionEnforcer(); final IDeviceStateManager service = new TestDeviceStateManagerService(permissionEnforcer); final DeviceStateManagerGlobal dsmGlobal = new DeviceStateManagerGlobal(service); final DeviceStateCallback callback = mock(DeviceStateCallback.class); dsmGlobal.registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); verify(callback).onDeviceStateChanged(eq(DEFAULT_DEVICE_STATE)); } @Test public void registerCallback() { final DeviceStateCallback callback1 = mock(DeviceStateCallback.class); Loading Loading @@ -267,10 +319,17 @@ public final class DeviceStateManagerGlobalTest { @Nullable private Request mBaseStateRequest; private final boolean mSimulatePostCallback; private final Set<IDeviceStateManagerCallback> mCallbacks = new HashSet<>(); TestDeviceStateManagerService(@NonNull FakePermissionEnforcer enforcer) { this(enforcer, false /* simulatePostCallback */); } TestDeviceStateManagerService(@NonNull FakePermissionEnforcer enforcer, boolean simulatePostCallback) { super(enforcer); mSimulatePostCallback = simulatePostCallback; } @NonNull Loading Loading @@ -304,19 +363,27 @@ public final class DeviceStateManagerGlobalTest { return getInfo(); } @Nullable @Override public void registerCallback(IDeviceStateManagerCallback callback) { public DeviceStateInfo registerCallback(IDeviceStateManagerCallback callback) { if (mCallbacks.contains(callback)) { throw new SecurityException("Callback is already registered."); } mCallbacks.add(callback); if (Flags.wlinfoOncreate()) { return getInfo(); } if (!mSimulatePostCallback) { try { callback.onDeviceStateInfoChanged(getInfo()); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } return null; } @Override public void requestState(@NonNull IBinder token, int state, int unusedFlags) { Loading services/core/java/com/android/server/devicestate/DeviceStateManagerService.java +17 −10 Original line number Diff line number Diff line Loading @@ -778,7 +778,8 @@ public final class DeviceStateManagerService extends SystemService { processRecord.notifyRequestActiveAsync(request.getToken()); } private void registerProcess(int pid, IDeviceStateManagerCallback callback) { @Nullable private DeviceStateInfo registerProcess(int pid, IDeviceStateManagerCallback callback) { synchronized (mLock) { if (mProcessRecords.contains(pid)) { throw new SecurityException("The calling process has already registered an" Loading @@ -794,16 +795,21 @@ public final class DeviceStateManagerService extends SystemService { } mProcessRecords.put(pid, record); // Callback clients should not be notified of invalid device states, so calls to // #getDeviceStateInfoLocked should be gated on checks if a committed state is present // before getting the device state info. final DeviceStateInfo currentInfo = mCommittedState.isPresent() ? getDeviceStateInfoLocked() : null; if (com.android.window.flags.Flags.wlinfoOncreate()) { return currentInfo; } else { // Callback clients should not be notified of invalid device states, so calls to // #getDeviceStateInfoLocked should be gated on checks if a committed state is // present before getting the device state info. if (currentInfo != null) { // If there is not a committed state we'll wait to notify the process of the initial // value. // If there is not a committed state we'll wait to notify the process of the // initial value. record.notifyDeviceStateInfoAsync(currentInfo); } return null; } } } Loading Loading @@ -1286,8 +1292,9 @@ public final class DeviceStateManagerService extends SystemService { } } @Nullable @Override // Binder call public void registerCallback(IDeviceStateManagerCallback callback) { public DeviceStateInfo registerCallback(IDeviceStateManagerCallback callback) { if (callback == null) { throw new IllegalArgumentException("Device state callback must not be null."); } Loading @@ -1295,7 +1302,7 @@ public final class DeviceStateManagerService extends SystemService { final int callingPid = Binder.getCallingPid(); final long token = Binder.clearCallingIdentity(); try { registerProcess(callingPid, callback); return registerProcess(callingPid, callback); } finally { Binder.restoreCallingIdentity(token); } Loading Loading
core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java +14 −7 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.util.ArrayMap; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.window.flags.Flags; import java.util.ArrayList; import java.util.List; Loading @@ -47,6 +48,7 @@ import java.util.concurrent.Executor; @VisibleForTesting(visibility = Visibility.PACKAGE) public final class DeviceStateManagerGlobal { @Nullable @GuardedBy("DeviceStateManagerGlobal.class") private static DeviceStateManagerGlobal sInstance; private static final String TAG = "DeviceStateManagerGlobal"; private static final boolean DEBUG = Build.IS_DEBUGGABLE; Loading Loading @@ -84,10 +86,12 @@ public final class DeviceStateManagerGlobal { @GuardedBy("mLock") private DeviceStateInfo mLastReceivedInfo; // Constructor should be called while holding the lock. // @GuardedBy("DeviceStateManagerGlobal.class") can't be used on constructors. @VisibleForTesting public DeviceStateManagerGlobal(@NonNull IDeviceStateManager deviceStateManager) { mDeviceStateManager = deviceStateManager; registerCallbackIfNeededLocked(); registerCallbackLocked(); } /** Loading Loading @@ -279,14 +283,17 @@ public final class DeviceStateManagerGlobal { } } private void registerCallbackIfNeededLocked() { if (mCallback != null) { return; } @GuardedBy("DeviceStateManagerGlobal.class") private void registerCallbackLocked() { mCallback = new DeviceStateManagerCallback(); try { if (Flags.wlinfoOncreate()) { synchronized (mLock) { mLastReceivedInfo = mDeviceStateManager.registerCallback(mCallback); } } else { mDeviceStateManager.registerCallback(mCallback); } } catch (RemoteException ex) { mCallback = null; throw ex.rethrowFromSystemServer(); Loading
core/java/android/hardware/devicestate/IDeviceStateManager.aidl +11 −3 Original line number Diff line number Diff line Loading @@ -32,16 +32,24 @@ interface IDeviceStateManager { DeviceStateInfo getDeviceStateInfo(); /** * Registers a callback to receive notifications from the device state manager. Only one * callback can be registered per-process. * Registers a callback to receive notifications from the device state manager and returns the * current {@link DeviceStateInfo}. Only one callback can be registered per-process. * <p> * As the callback mechanism is used to alert the caller of changes to request status a callback * <b>MUST</b> be registered before calling {@link #requestState(IBinder, int, int)} or * {@link #cancelRequest(IBinder)}, otherwise an exception will be thrown. * <p> * Upon successful registration, this method returns the committed {@link DeviceStateInfo} if * available, ensuring the availability of the device state after the callback is registered. * This guarantees that the client will have access to the latest device state immediately upon * completion of the two-way IPC call. * * @param callback the callback to register for device state notifications. * @return DeviceStateInfo the current device state information including the committed state * or null if no state has been committed by the {@link DeviceStateProvider} yet. * @throws SecurityException if a callback is already registered for the calling process. */ void registerCallback(in IDeviceStateManagerCallback callback); @nullable DeviceStateInfo registerCallback(in IDeviceStateManagerCallback callback); /** * Requests that the device enter the supplied {@code state}. A callback <b>MUST</b> have been Loading
core/tests/devicestatetests/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -28,8 +28,10 @@ android_test { static_libs: [ "androidx.test.ext.junit", "androidx.test.rules", "flag-junit", "frameworks-base-testutils", "mockito-target-minus-junit4", "platform-parametric-runner-lib", "platform-test-annotations", "truth", ], Loading
core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java +74 −7 Original line number Diff line number Diff line Loading @@ -18,8 +18,10 @@ package android.hardware.devicestate; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; Loading @@ -29,15 +31,20 @@ import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback; import android.os.IBinder; import android.os.RemoteException; import android.os.test.FakePermissionEnforcer; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.FlagsParameterization; import android.platform.test.flag.junit.SetFlagsRule; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.util.ConcurrentUtils; import com.android.window.flags.Flags; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; Loading @@ -46,6 +53,9 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import platform.test.runner.parameterized.ParameterizedAndroidJunit4; import platform.test.runner.parameterized.Parameters; /** * Unit tests for {@link DeviceStateManagerGlobal}. * Loading @@ -53,18 +63,30 @@ import java.util.Set; * atest FrameworksCoreDeviceStateManagerTests:DeviceStateManagerGlobalTest */ @SmallTest @RunWith(AndroidJUnit4.class) @RunWith(ParameterizedAndroidJunit4.class) public final class DeviceStateManagerGlobalTest { private static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState( new DeviceState.Configuration.Builder(0 /* identifier */, "" /* name */).build()); private static final DeviceState OTHER_DEVICE_STATE = new DeviceState( new DeviceState.Configuration.Builder(1 /* identifier */, "" /* name */).build()); @Rule public final SetFlagsRule mSetFlagsRule; @Parameters(name = "{0}") public static List<FlagsParameterization> getParams() { return FlagsParameterization.allCombinationsOf(Flags.FLAG_WLINFO_ONCREATE); } @NonNull private TestDeviceStateManagerService mService; @NonNull private DeviceStateManagerGlobal mDeviceStateManagerGlobal; public DeviceStateManagerGlobalTest(FlagsParameterization flags) { mSetFlagsRule = new SetFlagsRule(flags); } @Before public void setUp() { final FakePermissionEnforcer permissionEnforcer = new FakePermissionEnforcer(); Loading @@ -73,6 +95,36 @@ public final class DeviceStateManagerGlobalTest { assertThat(mService.mCallbacks).isNotEmpty(); } @Test @DisableFlags(Flags.FLAG_WLINFO_ONCREATE) public void create_whenWlinfoOncreateIsDisabled_receivesDeviceStateInfoFromCallback() { final FakePermissionEnforcer permissionEnforcer = new FakePermissionEnforcer(); final TestDeviceStateManagerService service = new TestDeviceStateManagerService( permissionEnforcer, true /* simulatePostCallback */); final DeviceStateManagerGlobal dsmGlobal = new DeviceStateManagerGlobal(service); final DeviceStateCallback callback = mock(DeviceStateCallback.class); dsmGlobal.registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); verify(callback, never()).onDeviceStateChanged(any()); // Simulate DeviceStateManagerService#registerProcess by notifying clients of current device // state via callback. service.notifyDeviceStateInfoChanged(); verify(callback).onDeviceStateChanged(eq(DEFAULT_DEVICE_STATE)); } @Test @EnableFlags(Flags.FLAG_WLINFO_ONCREATE) public void create_whenWlinfoOncreateIsEnabled_returnsDeviceStateInfoFromRegistration() { final FakePermissionEnforcer permissionEnforcer = new FakePermissionEnforcer(); final IDeviceStateManager service = new TestDeviceStateManagerService(permissionEnforcer); final DeviceStateManagerGlobal dsmGlobal = new DeviceStateManagerGlobal(service); final DeviceStateCallback callback = mock(DeviceStateCallback.class); dsmGlobal.registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); verify(callback).onDeviceStateChanged(eq(DEFAULT_DEVICE_STATE)); } @Test public void registerCallback() { final DeviceStateCallback callback1 = mock(DeviceStateCallback.class); Loading Loading @@ -267,10 +319,17 @@ public final class DeviceStateManagerGlobalTest { @Nullable private Request mBaseStateRequest; private final boolean mSimulatePostCallback; private final Set<IDeviceStateManagerCallback> mCallbacks = new HashSet<>(); TestDeviceStateManagerService(@NonNull FakePermissionEnforcer enforcer) { this(enforcer, false /* simulatePostCallback */); } TestDeviceStateManagerService(@NonNull FakePermissionEnforcer enforcer, boolean simulatePostCallback) { super(enforcer); mSimulatePostCallback = simulatePostCallback; } @NonNull Loading Loading @@ -304,19 +363,27 @@ public final class DeviceStateManagerGlobalTest { return getInfo(); } @Nullable @Override public void registerCallback(IDeviceStateManagerCallback callback) { public DeviceStateInfo registerCallback(IDeviceStateManagerCallback callback) { if (mCallbacks.contains(callback)) { throw new SecurityException("Callback is already registered."); } mCallbacks.add(callback); if (Flags.wlinfoOncreate()) { return getInfo(); } if (!mSimulatePostCallback) { try { callback.onDeviceStateInfoChanged(getInfo()); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } return null; } @Override public void requestState(@NonNull IBinder token, int state, int unusedFlags) { Loading
services/core/java/com/android/server/devicestate/DeviceStateManagerService.java +17 −10 Original line number Diff line number Diff line Loading @@ -778,7 +778,8 @@ public final class DeviceStateManagerService extends SystemService { processRecord.notifyRequestActiveAsync(request.getToken()); } private void registerProcess(int pid, IDeviceStateManagerCallback callback) { @Nullable private DeviceStateInfo registerProcess(int pid, IDeviceStateManagerCallback callback) { synchronized (mLock) { if (mProcessRecords.contains(pid)) { throw new SecurityException("The calling process has already registered an" Loading @@ -794,16 +795,21 @@ public final class DeviceStateManagerService extends SystemService { } mProcessRecords.put(pid, record); // Callback clients should not be notified of invalid device states, so calls to // #getDeviceStateInfoLocked should be gated on checks if a committed state is present // before getting the device state info. final DeviceStateInfo currentInfo = mCommittedState.isPresent() ? getDeviceStateInfoLocked() : null; if (com.android.window.flags.Flags.wlinfoOncreate()) { return currentInfo; } else { // Callback clients should not be notified of invalid device states, so calls to // #getDeviceStateInfoLocked should be gated on checks if a committed state is // present before getting the device state info. if (currentInfo != null) { // If there is not a committed state we'll wait to notify the process of the initial // value. // If there is not a committed state we'll wait to notify the process of the // initial value. record.notifyDeviceStateInfoAsync(currentInfo); } return null; } } } Loading Loading @@ -1286,8 +1292,9 @@ public final class DeviceStateManagerService extends SystemService { } } @Nullable @Override // Binder call public void registerCallback(IDeviceStateManagerCallback callback) { public DeviceStateInfo registerCallback(IDeviceStateManagerCallback callback) { if (callback == null) { throw new IllegalArgumentException("Device state callback must not be null."); } Loading @@ -1295,7 +1302,7 @@ public final class DeviceStateManagerService extends SystemService { final int callingPid = Binder.getCallingPid(); final long token = Binder.clearCallingIdentity(); try { registerProcess(callingPid, callback); return registerProcess(callingPid, callback); } finally { Binder.restoreCallingIdentity(token); } Loading