| /* |
| * Copyright (C) 2021 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.hardware.devicestate.cts; |
| |
| import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE; |
| import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE; |
| import static android.hardware.devicestate.cts.DeviceStateUtils.assertValidState; |
| import static android.hardware.devicestate.cts.DeviceStateUtils.runWithControlDeviceStatePermission; |
| import static android.view.Display.DEFAULT_DISPLAY; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assume.assumeTrue; |
| import static org.mockito.Mockito.atLeastOnce; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.timeout; |
| import static org.mockito.Mockito.verify; |
| |
| import android.hardware.devicestate.DeviceStateManager; |
| import android.hardware.devicestate.DeviceStateRequest; |
| |
| import androidx.test.ext.junit.runners.AndroidJUnit4; |
| |
| import com.android.compatibility.common.util.PollingCheck; |
| |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.mockito.ArgumentCaptor; |
| |
| import java.util.concurrent.Executor; |
| |
| /** CTS tests for {@link DeviceStateManager} API(s). */ |
| @RunWith(AndroidJUnit4.class) |
| public class DeviceStateManagerTests extends DeviceStateManagerTestBase { |
| |
| public static final int TIMEOUT = 2000; |
| |
| /** |
| * Tests that {@link DeviceStateManager#getSupportedStates()} returns at least one state and |
| * that none of the returned states are in the range |
| * [{@link #MINIMUM_DEVICE_STATE}, {@link #MAXIMUM_DEVICE_STATE}]. |
| */ |
| @Test |
| public void testValidSupportedStates() throws Exception { |
| final int[] supportedStates = getDeviceStateManager().getSupportedStates(); |
| assertTrue(supportedStates.length > 0); |
| |
| for (int i = 0; i < supportedStates.length; i++) { |
| final int state = supportedStates[i]; |
| assertValidState(state); |
| } |
| } |
| |
| /** |
| * Tests that calling {@link DeviceStateManager#requestState(DeviceStateRequest, Executor, |
| * DeviceStateRequest.Callback)} is successful and results in a registered callback being |
| * triggered with a value equal to the requested state. |
| */ |
| @Test |
| public void testRequestAllSupportedStates() throws Throwable { |
| final ArgumentCaptor<Integer> intAgumentCaptor = ArgumentCaptor.forClass(Integer.class); |
| final DeviceStateManager.DeviceStateCallback callback |
| = mock(DeviceStateManager.DeviceStateCallback.class); |
| final DeviceStateManager manager = getDeviceStateManager(); |
| manager.registerCallback(Runnable::run, callback); |
| |
| final int[] supportedStates = manager.getSupportedStates(); |
| for (int i = 0; i < supportedStates.length; i++) { |
| final DeviceStateRequest request |
| = DeviceStateRequest.newBuilder(supportedStates[i]).build(); |
| |
| runWithRequestActive(request, () -> { |
| verify(callback, atLeastOnce()).onStateChanged(intAgumentCaptor.capture()); |
| assertEquals(intAgumentCaptor.getValue().intValue(), request.getState()); |
| }); |
| } |
| } |
| |
| /** |
| * Tests that calling {@link DeviceStateManager#requestState(DeviceStateRequest, Executor, |
| * DeviceStateRequest.Callback)} throws an {@link java.lang.IllegalArgumentException} if |
| * supplied with a state above {@link MAXIMUM_DEVICE_STATE}. |
| */ |
| @Test(expected = IllegalArgumentException.class) |
| public void testRequestStateTooLarge() throws Throwable { |
| final DeviceStateManager manager = getDeviceStateManager(); |
| final DeviceStateRequest request |
| = DeviceStateRequest.newBuilder(MAXIMUM_DEVICE_STATE + 1).build(); |
| runWithControlDeviceStatePermission(() -> manager.requestState(request, null, null)); |
| } |
| |
| /** |
| * Tests that calling {@link DeviceStateManager#requestState(DeviceStateRequest, Executor, |
| * DeviceStateRequest.Callback)} throws an {@link java.lang.IllegalArgumentException} if |
| * supplied with a state below {@link MINIMUM_DEVICE_STATE}. |
| */ |
| @Test(expected = IllegalArgumentException.class) |
| public void testRequestStateTooSmall() throws Throwable { |
| final DeviceStateManager manager = getDeviceStateManager(); |
| final DeviceStateRequest request |
| = DeviceStateRequest.newBuilder(MINIMUM_DEVICE_STATE - 1).build(); |
| runWithControlDeviceStatePermission(() -> manager.requestState(request, null, null)); |
| } |
| |
| /** |
| * Tests that calling {@link DeviceStateManager#requestState(DeviceStateRequest, Executor, |
| * DeviceStateRequest.Callback)} is successful and results in a registered callback being |
| * triggered with a value equal to the requested state. |
| */ |
| @Test |
| public void testRequestStateSucceedsAsTopApp() throws IllegalArgumentException { |
| final DeviceStateManager manager = getDeviceStateManager(); |
| final int[] supportedStates = manager.getSupportedStates(); |
| // We want to verify that the app can change device state |
| // So we only attempt if there are more than 1 possible state. |
| assumeTrue(supportedStates.length > 1); |
| |
| final StateTrackingCallback callback = new StateTrackingCallback(); |
| manager.registerCallback(Runnable::run, callback); |
| PollingCheck.waitFor(TIMEOUT, () -> callback.mCurrentState != -1); |
| final TestActivitySession<DeviceStateTestActivity> activitySession = |
| createManagedTestActivitySession(); |
| |
| activitySession.launchTestActivityOnDisplaySync( |
| DeviceStateTestActivity.class, |
| DEFAULT_DISPLAY |
| ); |
| |
| DeviceStateTestActivity activity = activitySession.getActivity(); |
| |
| int newState = determineNewState(callback.mCurrentState, supportedStates); |
| activity.requestDeviceStateChange(newState); |
| |
| PollingCheck.waitFor(TIMEOUT, () -> callback.mCurrentState == newState); |
| |
| assertEquals(newState, callback.mCurrentState); |
| assertFalse(activity.requestStateFailed); |
| } |
| |
| /** |
| * Tests that calling {@link DeviceStateManager#requestState} is unsuccessful and results in a |
| * failure to update the state of the device as expected since the activity is backgrounded. |
| */ |
| @Test |
| public void testRequestStateFailsAsBackgroundApp() throws IllegalArgumentException { |
| final DeviceStateManager manager = getDeviceStateManager(); |
| final int[] supportedStates = manager.getSupportedStates(); |
| // We want to verify that the app can change device state |
| // So we only attempt if there are more than 1 possible state. |
| assumeTrue(supportedStates.length > 1); |
| |
| final StateTrackingCallback callback = new StateTrackingCallback(); |
| manager.registerCallback(Runnable::run, callback); |
| PollingCheck.waitFor(TIMEOUT, () -> callback.mCurrentState != -1); |
| |
| final TestActivitySession<DeviceStateTestActivity> activitySession = |
| createManagedTestActivitySession(); |
| activitySession.launchTestActivityOnDisplaySync( |
| DeviceStateTestActivity.class, |
| DEFAULT_DISPLAY |
| ); |
| |
| DeviceStateTestActivity activity = activitySession.getActivity(); |
| assertFalse(activity.requestStateFailed); |
| |
| launchHomeActivity(); // places our test activity in the background |
| |
| int requestedState = determineNewState(callback.mCurrentState, supportedStates); |
| activity.requestDeviceStateChange(requestedState); |
| |
| assertTrue(activity.requestStateFailed); |
| } |
| |
| /** |
| * Tests that calling {@link DeviceStateManager#cancelStateRequest} is successful and results |
| * in a registered callback being triggered with a value equal to the base state. |
| */ |
| @Test |
| public void testCancelStateRequestFromNewActivity() throws IllegalArgumentException { |
| final DeviceStateManager manager = getDeviceStateManager(); |
| final int[] supportedStates = manager.getSupportedStates(); |
| // We want to verify that the app can change device state |
| // So we only attempt if there are more than 1 possible state. |
| assumeTrue(supportedStates.length > 1); |
| |
| final StateTrackingCallback callback = new StateTrackingCallback(); |
| manager.registerCallback(Runnable::run, callback); |
| PollingCheck.waitFor(TIMEOUT, () -> callback.mCurrentState != -1); |
| final TestActivitySession<DeviceStateTestActivity> activitySession = |
| createManagedTestActivitySession(); |
| |
| activitySession.launchTestActivityOnDisplaySync( |
| DeviceStateTestActivity.class, |
| DEFAULT_DISPLAY |
| ); |
| |
| DeviceStateTestActivity activity = activitySession.getActivity(); |
| |
| int originalState = callback.mCurrentState; |
| int newState = determineNewState(callback.mCurrentState, supportedStates); |
| activity.requestDeviceStateChange(newState); |
| |
| PollingCheck.waitFor(TIMEOUT, () -> callback.mCurrentState == newState); |
| |
| assertEquals(newState, callback.mCurrentState); |
| assertFalse(activity.requestStateFailed); |
| |
| activity.finish(); |
| |
| final TestActivitySession<DeviceStateTestActivity> secondActivitySession = |
| createManagedTestActivitySession(); |
| secondActivitySession.launchTestActivityOnDisplaySync( |
| DeviceStateTestActivity.class, |
| DEFAULT_DISPLAY |
| ); |
| // verify that the overridden state is still active after finishing |
| // and launching the second activity. |
| assertEquals(newState, callback.mCurrentState); |
| |
| activity = secondActivitySession.getActivity(); |
| activity.cancelOverriddenState(); |
| |
| PollingCheck.waitFor(TIMEOUT, () -> callback.mCurrentState == originalState); |
| |
| assertEquals(originalState, callback.mCurrentState); |
| } |
| |
| // determine what state we should request that isn't the current state |
| // throws an IllegalArgumentException if there is no unique states available |
| // in the list of supported states |
| private static int determineNewState(int currentState, int[] states) |
| throws IllegalArgumentException { |
| for (int state : states) { |
| if (state != currentState) { |
| return state; |
| } |
| } |
| throw new IllegalArgumentException( |
| "No unique supported states besides our current state were found"); |
| } |
| |
| /** |
| * Tests that calling {@link DeviceStateManager#requestState()} throws a |
| * {@link java.lang.SecurityException} without the |
| * {@link android.Manifest.permission.CONTROL_DEVICE_STATE} permission held. |
| */ |
| @Test(expected = SecurityException.class) |
| public void testRequestStateWithoutPermission() { |
| final DeviceStateManager manager = getDeviceStateManager(); |
| final int[] states = manager.getSupportedStates(); |
| final DeviceStateRequest request = DeviceStateRequest.newBuilder(states[0]).build(); |
| manager.requestState(request, null, null); |
| } |
| |
| /** |
| * Tests that calling {@link DeviceStateManager#cancelStateRequest} throws a |
| * {@link java.lang.SecurityException} without the |
| * {@link android.Manifest.permission.CONTROL_DEVICE_STATE} permission held. |
| */ |
| @Test(expected = SecurityException.class) |
| public void testCancelOverrideRequestWithoutPermission() throws Throwable { |
| final DeviceStateManager manager = getDeviceStateManager(); |
| final int[] states = manager.getSupportedStates(); |
| final DeviceStateRequest request = DeviceStateRequest.newBuilder(states[0]).build(); |
| runWithRequestActive(request, () -> { |
| manager.cancelStateRequest(); |
| }); |
| } |
| |
| /** |
| * Tests that callbacks added with {@link DeviceStateManager#registerDeviceStateCallback()} are |
| * supplied with an initial callback that contains the state at the time of registration. |
| */ |
| @Test |
| public void testRegisterCallbackSuppliesInitialValue() throws InterruptedException { |
| final ArgumentCaptor<int[]> intArrayAgumentCaptor = ArgumentCaptor.forClass(int[].class); |
| final ArgumentCaptor<Integer> intAgumentCaptor = ArgumentCaptor.forClass(Integer.class); |
| |
| final DeviceStateManager.DeviceStateCallback callback |
| = mock(DeviceStateManager.DeviceStateCallback.class); |
| final DeviceStateManager manager = getDeviceStateManager(); |
| manager.registerCallback(Runnable::run, callback); |
| |
| verify(callback, timeout(CALLBACK_TIMEOUT_MS)).onStateChanged(intAgumentCaptor.capture()); |
| assertValidState(intAgumentCaptor.getValue().intValue()); |
| |
| verify(callback, timeout(CALLBACK_TIMEOUT_MS)) |
| .onBaseStateChanged(intAgumentCaptor.capture()); |
| assertValidState(intAgumentCaptor.getValue().intValue()); |
| |
| verify(callback, timeout(CALLBACK_TIMEOUT_MS)) |
| .onSupportedStatesChanged(intArrayAgumentCaptor.capture()); |
| final int[] supportedStates = intArrayAgumentCaptor.getValue(); |
| assertTrue(supportedStates.length > 0); |
| for (int i = 0; i < supportedStates.length; i++) { |
| final int state = supportedStates[i]; |
| assertValidState(state); |
| } |
| } |
| |
| private class StateTrackingCallback implements DeviceStateManager.DeviceStateCallback { |
| private int mCurrentState = - 1; |
| |
| @Override |
| public void onStateChanged(int state) { |
| mCurrentState = state; |
| } |
| } |
| } |