Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7f8d5fd4 authored by Kenneth Ford's avatar Kenneth Ford
Browse files

Introduces requestBaseStateOverride test API

Adds new requestBaseStateOverride on DeviceStateManager
to be used for testing, to provide a similar behavior
to changing the physical configuration of the device.

This is necessary now that the requestState API is being
used for features now, we need a test API that will
be a closer simulation to physical device changes.

Bug: 234336979
Test: DeviceStateManagerGlobalTest
Change-Id: I6ad9e799329f16521aa3c13b248eed762fe0b121
parent d5fe5cc1
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -1155,9 +1155,11 @@ package android.hardware.camera2 {
package android.hardware.devicestate {

  public final class DeviceStateManager {
    method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void cancelBaseStateOverride();
    method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void cancelStateRequest();
    method @NonNull public int[] getSupportedStates();
    method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
    method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void requestBaseStateOverride(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
    method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
    method public void unregisterCallback(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
    field public static final int MAXIMUM_DEVICE_STATE = 255; // 0xff
+47 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.hardware.devicestate;

import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -114,6 +115,52 @@ public final class DeviceStateManager {
        mGlobal.cancelStateRequest();
    }

    /**
     * Submits a {@link DeviceStateRequest request} to override the base state of the device. This
     * should only be used for testing, where you want to simulate the physical change to the
     * device state.
     * <p>
     * By default, the request is kept active until one of the following occurs:
     * <ul>
     *     <li>The physical state of the device changes</li>
     *     <li>The system deems the request can no longer be honored, for example if the requested
     *     state becomes unsupported.
     *     <li>A call to {@link #cancelBaseStateOverride}.
     *     <li>Another processes submits a request succeeding this request in which case the request
     *     will be canceled.
     * </ul>
     *
     * Submitting a base state override request may not cause any change in the presentation
     * of the system if there is an emulated request made through {@link #requestState}, as the
     * emulated override requests take priority.
     *
     * @throws IllegalArgumentException if the requested state is unsupported.
     * @throws SecurityException if the caller does not hold the
     * {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission.
     *
     * @see DeviceStateRequest
     */
    @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
    public void requestBaseStateOverride(@NonNull DeviceStateRequest request,
            @Nullable @CallbackExecutor Executor executor,
            @Nullable DeviceStateRequest.Callback callback) {
        mGlobal.requestBaseStateOverride(request, executor, callback);
    }

    /**
     * Cancels the active {@link DeviceStateRequest} previously submitted with a call to
     * {@link #requestBaseStateOverride(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
     * <p>
     * This method is noop if there is no base state request currently active.
     *
     * @throws SecurityException if the caller does not hold the
     * {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission.
     */
    @RequiresPermission(Manifest.permission.CONTROL_DEVICE_STATE)
    public void cancelBaseStateOverride() {
        mGlobal.cancelBaseStateOverride();
    }

    /**
     * Registers a callback to receive notifications about changes in device state.
     *
+70 −15
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.hardware.devicestate;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.content.Context;
import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
import android.os.Binder;
@@ -81,6 +82,7 @@ public final class DeviceStateManagerGlobal {
    @VisibleForTesting
    public DeviceStateManagerGlobal(@NonNull IDeviceStateManager deviceStateManager) {
        mDeviceStateManager = deviceStateManager;
        registerCallbackIfNeededLocked();
    }

    /**
@@ -116,27 +118,22 @@ public final class DeviceStateManagerGlobal {
     * DeviceStateRequest.Callback)
     * @see DeviceStateRequest
     */
    @RequiresPermission(value = android.Manifest.permission.CONTROL_DEVICE_STATE,
            conditional = true)
    public void requestState(@NonNull DeviceStateRequest request,
            @Nullable Executor executor, @Nullable DeviceStateRequest.Callback callback) {
        if (callback == null && executor != null) {
            throw new IllegalArgumentException("Callback must be supplied with executor.");
        } else if (executor == null && callback != null) {
            throw new IllegalArgumentException("Executor must be supplied with callback.");
        }

        DeviceStateRequestWrapper requestWrapper = new DeviceStateRequestWrapper(request, callback,
                executor);
        synchronized (mLock) {
            registerCallbackIfNeededLocked();

            if (findRequestTokenLocked(request) != null) {
                // This request has already been submitted.
                return;
            }

            // Add the request wrapper to the mRequests array before requesting the state as the
            // callback could be triggered immediately if the mDeviceStateManager IBinder is in the
            // same process as this instance.
            IBinder token = new Binder();
            mRequests.put(token, new DeviceStateRequestWrapper(request, callback, executor));
            mRequests.put(token, requestWrapper);

            try {
                mDeviceStateManager.requestState(token, request.getState(), request.getFlags());
@@ -153,10 +150,10 @@ public final class DeviceStateManagerGlobal {
     *
     * @see DeviceStateManager#cancelStateRequest
     */
    @RequiresPermission(value = android.Manifest.permission.CONTROL_DEVICE_STATE,
            conditional = true)
    public void cancelStateRequest() {
        synchronized (mLock) {
            registerCallbackIfNeededLocked();

            try {
                mDeviceStateManager.cancelStateRequest();
            } catch (RemoteException ex) {
@@ -165,6 +162,56 @@ public final class DeviceStateManagerGlobal {
        }
    }

    /**
     * Submits a {@link DeviceStateRequest request} to modify the base state of the device.
     *
     * @see DeviceStateManager#requestBaseStateOverride(DeviceStateRequest, Executor,
     * DeviceStateRequest.Callback)
     * @see DeviceStateRequest
     */
    @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
    public void requestBaseStateOverride(@NonNull DeviceStateRequest request,
            @Nullable Executor executor, @Nullable DeviceStateRequest.Callback callback) {
        DeviceStateRequestWrapper requestWrapper = new DeviceStateRequestWrapper(request, callback,
                executor);
        synchronized (mLock) {
            if (findRequestTokenLocked(request) != null) {
                // This request has already been submitted.
                return;
            }
            // Add the request wrapper to the mRequests array before requesting the state as the
            // callback could be triggered immediately if the mDeviceStateManager IBinder is in the
            // same process as this instance.
            IBinder token = new Binder();
            mRequests.put(token, requestWrapper);

            try {
                mDeviceStateManager.requestBaseStateOverride(token, request.getState(),
                        request.getFlags());
            } catch (RemoteException ex) {
                mRequests.remove(token);
                throw ex.rethrowFromSystemServer();
            }
        }
    }

    /**
     * Cancels a {@link DeviceStateRequest request} previously submitted with a call to
     * {@link #requestBaseStateOverride(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
     *
     * @see DeviceStateManager#cancelBaseStateOverride
     */
    @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
    public void cancelBaseStateOverride() {
        synchronized (mLock) {
            try {
                mDeviceStateManager.cancelBaseStateOverride();
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        }
    }

    /**
     * Registers a callback to receive notifications about changes in device state.
     *
@@ -179,9 +226,6 @@ public final class DeviceStateManagerGlobal {
                // This callback is already registered.
                return;
            }

            registerCallbackIfNeededLocked();

            // Add the callback wrapper to the mCallbacks array after registering the callback as
            // the callback could be triggered immediately if the mDeviceStateManager IBinder is in
            // the same process as this instance.
@@ -357,6 +401,8 @@ public final class DeviceStateManagerGlobal {

        DeviceStateRequestWrapper(@NonNull DeviceStateRequest request,
                @Nullable DeviceStateRequest.Callback callback, @Nullable Executor executor) {
            validateRequestWrapperParameters(callback, executor);

            mRequest = request;
            mCallback = callback;
            mExecutor = executor;
@@ -377,5 +423,14 @@ public final class DeviceStateManagerGlobal {

            mExecutor.execute(() -> mCallback.onRequestCanceled(mRequest));
        }

        private void validateRequestWrapperParameters(
                @Nullable DeviceStateRequest.Callback callback, @Nullable Executor executor) {
            if (callback == null && executor != null) {
                throw new IllegalArgumentException("Callback must be supplied with executor.");
            } else if (executor == null && callback != null) {
                throw new IllegalArgumentException("Executor must be supplied with callback.");
            }
        }
    }
}
+122 −26
Original line number Diff line number Diff line
@@ -16,11 +16,12 @@

package android.hardware.devicestate;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;

import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;

@@ -36,7 +37,6 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mockito;

import java.util.HashSet;
import java.util.Set;
@@ -59,6 +59,7 @@ public final class DeviceStateManagerGlobalTest {
    public void setUp() {
        mService = new TestDeviceStateManagerService();
        mDeviceStateManagerGlobal = new DeviceStateManagerGlobal(mService);
        assertFalse(mService.mCallbacks.isEmpty());
    }

    @Test
@@ -79,8 +80,8 @@ public final class DeviceStateManagerGlobalTest {
        verify(callback2).onBaseStateChanged(eq(mService.getBaseState()));
        verify(callback2).onStateChanged(eq(mService.getMergedState()));

        Mockito.reset(callback1);
        Mockito.reset(callback2);
        reset(callback1);
        reset(callback2);

        // Change the supported states and verify callback
        mService.setSupportedStates(new int[]{ DEFAULT_DEVICE_STATE });
@@ -88,8 +89,8 @@ public final class DeviceStateManagerGlobalTest {
        verify(callback2).onSupportedStatesChanged(eq(mService.getSupportedStates()));
        mService.setSupportedStates(new int[]{ DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE });

        Mockito.reset(callback1);
        Mockito.reset(callback2);
        reset(callback1);
        reset(callback2);

        // Change the base state and verify callback
        mService.setBaseState(OTHER_DEVICE_STATE);
@@ -98,8 +99,8 @@ public final class DeviceStateManagerGlobalTest {
        verify(callback2).onBaseStateChanged(eq(mService.getBaseState()));
        verify(callback2).onStateChanged(eq(mService.getMergedState()));

        Mockito.reset(callback1);
        Mockito.reset(callback2);
        reset(callback1);
        reset(callback2);

        // Change the requested state and verify callback
        DeviceStateRequest request = DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE).build();
@@ -120,7 +121,7 @@ public final class DeviceStateManagerGlobalTest {
        verify(callback).onSupportedStatesChanged(eq(mService.getSupportedStates()));
        verify(callback).onBaseStateChanged(eq(mService.getBaseState()));
        verify(callback).onStateChanged(eq(mService.getMergedState()));
        Mockito.reset(callback);
        reset(callback);

        mDeviceStateManagerGlobal.unregisterDeviceStateCallback(callback);

@@ -130,33 +131,86 @@ public final class DeviceStateManagerGlobalTest {
    }

    @Test
    public void submittingRequestRegistersCallback() {
        assertTrue(mService.mCallbacks.isEmpty());
    public void submitRequest() {
        DeviceStateCallback callback = mock(DeviceStateCallback.class);
        mDeviceStateManagerGlobal.registerDeviceStateCallback(callback,
                ConcurrentUtils.DIRECT_EXECUTOR);

        DeviceStateRequest request = DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE).build();
        verify(callback).onStateChanged(eq(mService.getBaseState()));
        reset(callback);

        DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build();
        mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */);

        assertFalse(mService.mCallbacks.isEmpty());
        verify(callback).onStateChanged(eq(OTHER_DEVICE_STATE));
        reset(callback);

        mDeviceStateManagerGlobal.cancelStateRequest();

        verify(callback).onStateChanged(eq(mService.getBaseState()));
    }

    @Test
    public void submitRequest() {
    public void submitBaseStateOverrideRequest() {
        DeviceStateCallback callback = mock(DeviceStateCallback.class);
        mDeviceStateManagerGlobal.registerDeviceStateCallback(callback,
                ConcurrentUtils.DIRECT_EXECUTOR);

        verify(callback).onBaseStateChanged(eq(mService.getBaseState()));
        verify(callback).onStateChanged(eq(mService.getBaseState()));
        Mockito.reset(callback);
        reset(callback);

        DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build();
        mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */);
        mDeviceStateManagerGlobal.requestBaseStateOverride(request, null /* executor */,
                null /* callback */);

        verify(callback).onBaseStateChanged(eq(OTHER_DEVICE_STATE));
        verify(callback).onStateChanged(eq(OTHER_DEVICE_STATE));
        Mockito.reset(callback);
        reset(callback);

        mDeviceStateManagerGlobal.cancelStateRequest();
        mDeviceStateManagerGlobal.cancelBaseStateOverride();

        verify(callback).onBaseStateChanged(eq(mService.getBaseState()));
        verify(callback).onStateChanged(eq(mService.getBaseState()));
    }

    @Test
    public void submitBaseAndEmulatedStateOverride() {
        DeviceStateCallback callback = mock(DeviceStateCallback.class);
        mDeviceStateManagerGlobal.registerDeviceStateCallback(callback,
                ConcurrentUtils.DIRECT_EXECUTOR);

        verify(callback).onBaseStateChanged(eq(mService.getBaseState()));
        verify(callback).onStateChanged(eq(mService.getBaseState()));
        reset(callback);

        DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build();
        mDeviceStateManagerGlobal.requestBaseStateOverride(request, null /* executor */,
                null /* callback */);

        verify(callback).onBaseStateChanged(eq(OTHER_DEVICE_STATE));
        verify(callback).onStateChanged(eq(OTHER_DEVICE_STATE));
        assertEquals(OTHER_DEVICE_STATE, mService.getBaseState());
        reset(callback);

        DeviceStateRequest secondRequest = DeviceStateRequest.newBuilder(
                DEFAULT_DEVICE_STATE).build();

        mDeviceStateManagerGlobal.requestState(secondRequest, null, null);

        assertEquals(OTHER_DEVICE_STATE, mService.getBaseState());
        verify(callback).onStateChanged(eq(DEFAULT_DEVICE_STATE));
        reset(callback);

        mDeviceStateManagerGlobal.cancelStateRequest();

        verify(callback).onStateChanged(OTHER_DEVICE_STATE);
        reset(callback);

        mDeviceStateManagerGlobal.cancelBaseStateOverride();

        verify(callback).onBaseStateChanged(DEFAULT_DEVICE_STATE);
        verify(callback).onStateChanged(DEFAULT_DEVICE_STATE);
    }

    @Test
@@ -169,7 +223,7 @@ public final class DeviceStateManagerGlobalTest {
                callback /* callback */);

        verify(callback).onRequestActivated(eq(request));
        Mockito.reset(callback);
        reset(callback);

        mDeviceStateManagerGlobal.cancelStateRequest();

@@ -203,13 +257,16 @@ public final class DeviceStateManagerGlobalTest {
        private int[] mSupportedStates = new int[] { DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE };
        private int mBaseState = DEFAULT_DEVICE_STATE;
        private Request mRequest;
        private Request mBaseStateRequest;

        private Set<IDeviceStateManagerCallback> mCallbacks = new HashSet<>();

        private DeviceStateInfo getInfo() {
            final int mergedBaseState = mBaseStateRequest == null
                    ? mBaseState : mBaseStateRequest.state;
            final int mergedState = mRequest == null
                    ? mBaseState : mRequest.state;
            return new DeviceStateInfo(mSupportedStates, mBaseState, mergedState);
                    ? mergedBaseState : mRequest.state;
            return new DeviceStateInfo(mSupportedStates, mergedBaseState, mergedState);
        }

        private void notifyDeviceStateInfoChanged() {
@@ -238,7 +295,7 @@ public final class DeviceStateManagerGlobalTest {
            try {
                callback.onDeviceStateInfoChanged(getInfo());
            } catch (RemoteException e) {
                // Do nothing. Should never happen.
                e.rethrowFromSystemServer();
            }
        }

@@ -249,7 +306,7 @@ public final class DeviceStateManagerGlobalTest {
                    try {
                        callback.onRequestCanceled(mRequest.token);
                    } catch (RemoteException e) {
                        // Do nothing. Should never happen.
                        e.rethrowFromSystemServer();
                    }
                }
            }
@@ -262,7 +319,7 @@ public final class DeviceStateManagerGlobalTest {
                try {
                    callback.onRequestActive(token);
                } catch (RemoteException e) {
                    // Do nothing. Should never happen.
                    e.rethrowFromSystemServer();
                }
            }
        }
@@ -275,7 +332,46 @@ public final class DeviceStateManagerGlobalTest {
                try {
                    callback.onRequestCanceled(token);
                } catch (RemoteException e) {
                    // Do nothing. Should never happen.
                    e.rethrowFromSystemServer();
                }
            }
            notifyDeviceStateInfoChanged();
        }

        @Override
        public void requestBaseStateOverride(IBinder token, int state, int flags) {
            if (mBaseStateRequest != null) {
                for (IDeviceStateManagerCallback callback : mCallbacks) {
                    try {
                        callback.onRequestCanceled(mBaseStateRequest.token);
                    } catch (RemoteException e) {
                        e.rethrowFromSystemServer();
                    }
                }
            }

            final Request request = new Request(token, state, flags);
            mBaseStateRequest = request;
            notifyDeviceStateInfoChanged();

            for (IDeviceStateManagerCallback callback : mCallbacks) {
                try {
                    callback.onRequestActive(token);
                } catch (RemoteException e) {
                    e.rethrowFromSystemServer();
                }
            }
        }

        @Override
        public void cancelBaseStateOverride() throws RemoteException {
            IBinder token = mBaseStateRequest.token;
            mBaseStateRequest = null;
            for (IDeviceStateManagerCallback callback : mCallbacks) {
                try {
                    callback.onRequestCanceled(token);
                } catch (RemoteException e) {
                    e.rethrowFromSystemServer();
                }
            }
            notifyDeviceStateInfoChanged();
@@ -296,7 +392,7 @@ public final class DeviceStateManagerGlobalTest {
        }

        public int getBaseState() {
            return mBaseState;
            return getInfo().baseState;
        }

        public int getMergedState() {