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

Commit c5e18c89 authored by dshivangi's avatar dshivangi
Browse files

Handle auto-rotate setting is being set while device-state is unavailable

After the device is booted, it may be possible for device state update to come in after a delay.
To be able to honour incoming requests while device state stays missing, we should queue them.
Later, when we finally receive current device state, we can process all requests in the queue(realistically in worst case a couple of requests).

Fixes: 413639166
Flag: com.android.window.flags.enable_device_state_auto_rotate_setting_refactor
Test: atest DeviceStateAutoRotateSettingControllerTests
Change-Id: Ied82fd784ff150f2ad60af46bc1a8dbd408090f1
parent 2e071263
Loading
Loading
Loading
Loading
+31 −8
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
import android.util.Slog;
import android.util.SparseIntArray;

@@ -44,6 +45,8 @@ import com.android.server.wm.DeviceStateAutoRotateSettingController.Event.Update
import com.android.server.wm.DeviceStateController.DeviceStateEnum;
import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@@ -68,6 +71,7 @@ public class DeviceStateAutoRotateSettingController {
    private static final String SEPARATOR_REGEX = ":";
    private static final int ACCELEROMETER_ROTATION_OFF = 0;
    private static final int ACCELEROMETER_ROTATION_ON = 1;
    private static final int INVALID_DEVICE_STATE = -1;
    // TODO(b/413598268): Disable debugging after the
    //  com.android.window.flags.enable_device_state_auto_rotate_setting_refactor flag is rolled-out
    private static final boolean DEBUG = true;
@@ -78,10 +82,9 @@ public class DeviceStateAutoRotateSettingController {
    private final DeviceStateAutoRotateSettingManager mDeviceStateAutoRotateSettingManager;
    private final ContentResolver mContentResolver;
    private final DeviceStateController mDeviceStateController;
    private final List<Event> mPendingEvents = new ArrayList<>();

    // TODO(b/413639166): Handle device state being missing until we receive first device state
    //  update
    private int mDeviceState = -1;
    private int mDeviceState = INVALID_DEVICE_STATE;
    private boolean mAccelerometerSetting;
    private SparseIntArray mDeviceStateAutoRotateSetting;

@@ -103,10 +106,23 @@ public class DeviceStateAutoRotateSettingController {
            @Override
            public void handleMessage(@NonNull Message msg) {
                final Event event = (Event) msg.obj;
                final boolean inMemoryStateUpdated = updateInMemoryState(event);
                if (mDeviceState == INVALID_DEVICE_STATE && !(event instanceof UpdateDeviceState)) {
                    mPendingEvents.add(event);
                    Log.w(TAG, "Trying to write into auto-rotate settings, while "
                            + "device-state is unavailable.\n" + "Could not process the event="
                            + event.getClass().getSimpleName() + ".\n"
                            + "This event will be queued and processed later once we receive "
                            + "device-state update.");
                    return;
                }

                if (inMemoryStateUpdated) {
                    writeInMemoryStateIntoPersistedSetting();
                handleEvent(event);

                if (!mPendingEvents.isEmpty()) {
                    for (int i = 0; i < mPendingEvents.size(); i++) {
                        handleEvent(mPendingEvents.get(i));
                    }
                    mPendingEvents.clear();
                }
            }
        };
@@ -116,6 +132,14 @@ public class DeviceStateAutoRotateSettingController {
        registerDeviceStateObserver();
    }

    private void handleEvent(@NonNull Event event) {
        final boolean inMemoryStateUpdated = updateInMemoryState(event);

        if (inMemoryStateUpdated) {
            writeInMemoryStateIntoPersistedSetting();
        }
    }

    /** Request to change {@link DEVICE_STATE_ROTATION_LOCK} persisted setting. */
    public void requestDeviceStateAutoRotateSettingChange(int deviceState, boolean autoRotate) {
        postUpdate(new UpdateDeviceStateAutoRotateSetting(deviceState, autoRotate));
@@ -128,8 +152,7 @@ public class DeviceStateAutoRotateSettingController {

    private void registerDeviceStateAutoRotateSettingObserver() {
        mDeviceStateAutoRotateSettingManager.registerListener(
                () -> postUpdate(
                        PersistedSettingUpdate.INSTANCE));
                () -> postUpdate(PersistedSettingUpdate.INSTANCE));
    }

    private void registerAccelerometerRotationSettingObserver() {
+118 −32
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
package com.android.server.wm;

import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK;
import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED;
import static android.provider.Settings.System.ACCELEROMETER_ROTATION;
import static android.provider.Settings.System.getUriFor;
@@ -28,11 +27,13 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.devicestate.DeviceState;
import android.os.Handler;
@@ -45,11 +46,14 @@ import android.util.SparseIntArray;

import androidx.test.filters.SmallTest;

import com.android.internal.R;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.wm.utils.DeviceStateTestUtils;
import com.android.settingslib.devicestate.AndroidSecureSettings;
import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener;
import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManagerImpl;
import com.android.settingslib.devicestate.PostureDeviceStateConverter;

import org.junit.Before;
import org.junit.Rule;
@@ -59,6 +63,8 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.concurrent.Executor;

/**
 * Test class for {@link DeviceStateAutoRotateSettingController}.
 *
@@ -66,8 +72,6 @@ import org.mockito.MockitoAnnotations;
 */
@SmallTest
@Presubmit
// TODO(b/413639166): Add testcase to check if class under test behaves expectedly when
//  deviceState = -1
public class DeviceStateAutoRotateSettingControllerTests {
    private static final int ACCELEROMETER_ROTATION_OFF = 0;
    private static final int ACCELEROMETER_ROTATION_ON = 1;
@@ -79,8 +83,6 @@ public class DeviceStateAutoRotateSettingControllerTests {
    @Rule
    public final FakeSettingsProviderRule rule = FakeSettingsProvider.rule();

    @Mock
    private DeviceStateAutoRotateSettingManagerImpl mMockAutoRotateSettingManager;
    @Mock
    private DeviceStateController mMockDeviceStateController;
    @Mock
@@ -95,41 +97,48 @@ public class DeviceStateAutoRotateSettingControllerTests {

    private final TestLooper mTestLooper = new TestLooper();
    private DeviceStateAutoRotateSettingController mDeviceStateAutoRotateSettingController;
    private DeviceStateAutoRotateSettingManagerImpl mSpyAutoRotateSettingManager;

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
        final Context mockContext = mock(Context.class);
        when(mockContext.getContentResolver()).thenReturn(mMockResolver);
        final Resources mockResources = mock(Resources.class);
        final FakeSettingsProvider fakeSettingsProvider = new FakeSettingsProvider();
        when(mMockResolver.acquireProvider(Settings.AUTHORITY))
                .thenReturn(fakeSettingsProvider.getIContentProvider());
        final Looper looper = mTestLooper.getLooper();
        final Handler handler = new Handler(looper);

        when(mockContext.getContentResolver()).thenReturn(mMockResolver);
        when(mockContext.getResources()).thenReturn(mockResources);
        when(mockResources.getStringArray(
                R.array.config_perDeviceStateRotationLockDefaults)).thenReturn(new String[]{});
        when(mMockResolver.acquireProvider(Settings.AUTHORITY)).thenReturn(
                fakeSettingsProvider.getIContentProvider());
        mSpyAutoRotateSettingManager = spy(
                new DeviceStateAutoRotateSettingManagerImpl(mockContext, mock(Executor.class),
                        new AndroidSecureSettings(mMockResolver), handler,
                        mock(PostureDeviceStateConverter.class)));

        setAccelerometerRotationSetting(ACCELEROMETER_ROTATION_OFF);
        when(mMockAutoRotateSettingManager.getRotationLockSetting()).thenAnswer(invocation ->
                createDeviceStateAutoRotateSettingMap(DEVICE_STATE_ROTATION_LOCK_LOCKED,
                        DEVICE_STATE_ROTATION_LOCK_LOCKED));
        setDeviceStateAutoRotateSetting(FOLDED_LOCKED_OPEN_LOCKED_SETTING);

        mDeviceStateAutoRotateSettingController = new DeviceStateAutoRotateSettingController(
                mockContext, looper, handler, mMockDeviceStateController,
                mMockAutoRotateSettingManager);
                mSpyAutoRotateSettingManager);
        mTestLooper.dispatchAll();

        verify(mMockAutoRotateSettingManager).registerListener(
        verify(mSpyAutoRotateSettingManager).registerListener(
                mSettingListenerArgumentCaptor.capture());
        verify(mMockResolver).registerContentObserver(
                eq(getUriFor(ACCELEROMETER_ROTATION)), anyBoolean(),
                mAccelerometerRotationSettingObserver.capture(), eq(UserHandle.USER_CURRENT));
        verify(mMockDeviceStateController).registerDeviceStateCallback(
                mDeviceStateListenerCaptor.capture(), any());

        setDeviceState(DeviceStateTestUtils.FOLDED);
    }

    @Test
    public void requestDSAutoRotateSettingChange_updatesDeviceStateAutoRotateSetting() {
        setDeviceState(DeviceStateTestUtils.FOLDED);
        mDeviceStateAutoRotateSettingController.requestDeviceStateAutoRotateSettingChange(
                DeviceStateTestUtils.FOLDED.getIdentifier(), true);
        mTestLooper.dispatchAll();
@@ -140,6 +149,7 @@ public class DeviceStateAutoRotateSettingControllerTests {

    @Test
    public void requestAccelerometerRotationChange_updatesAccelerometerRotation() {
        setDeviceState(DeviceStateTestUtils.FOLDED);
        mDeviceStateAutoRotateSettingController.requestAccelerometerRotationSettingChange(true);
        mTestLooper.dispatchAll();

@@ -148,6 +158,7 @@ public class DeviceStateAutoRotateSettingControllerTests {

    @Test
    public void requestDSAutoRotateSettingChange_curDeviceState_updatesAccelerometerRotation() {
        setDeviceState(DeviceStateTestUtils.FOLDED);
        mDeviceStateAutoRotateSettingController.requestDeviceStateAutoRotateSettingChange(
                DeviceStateTestUtils.FOLDED.getIdentifier(), true);
        mTestLooper.dispatchAll();
@@ -157,6 +168,7 @@ public class DeviceStateAutoRotateSettingControllerTests {

    @Test
    public void requestAccelerometerRotationChange_updatesDSAutoRotateSetting() {
        setDeviceState(DeviceStateTestUtils.FOLDED);
        mDeviceStateAutoRotateSettingController.requestAccelerometerRotationSettingChange(true);
        mTestLooper.dispatchAll();

@@ -165,6 +177,7 @@ public class DeviceStateAutoRotateSettingControllerTests {

    @Test
    public void accelerometerRotationSettingChanged_updatesDSAutoRotateSetting() {
        setDeviceState(DeviceStateTestUtils.FOLDED);
        setAccelerometerRotationSetting(ACCELEROMETER_ROTATION_ON);

        mAccelerometerRotationSettingObserver.getValue().onChange(false);
@@ -175,9 +188,8 @@ public class DeviceStateAutoRotateSettingControllerTests {

    @Test
    public void dSAutoRotateSettingChanged_updatesAccelerometerRotation() {
        when(mMockAutoRotateSettingManager.getRotationLockSetting()).thenAnswer(invocation ->
                createDeviceStateAutoRotateSettingMap(DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
                        DEVICE_STATE_ROTATION_LOCK_LOCKED));
        setDeviceState(DeviceStateTestUtils.FOLDED);
        setDeviceStateAutoRotateSetting(FOLDED_UNLOCKED_OPEN_LOCKED_SETTING);

        mSettingListenerArgumentCaptor.getValue().onSettingsChanged();
        mTestLooper.dispatchAll();
@@ -187,9 +199,8 @@ public class DeviceStateAutoRotateSettingControllerTests {

    @Test
    public void onDeviceStateChange_updatesAccelerometerRotation() {
        when(mMockAutoRotateSettingManager.getRotationLockSetting()).thenAnswer(invocation ->
                createDeviceStateAutoRotateSettingMap(DEVICE_STATE_ROTATION_LOCK_LOCKED,
                        DEVICE_STATE_ROTATION_LOCK_UNLOCKED));
        setDeviceState(DeviceStateTestUtils.FOLDED);
        setDeviceStateAutoRotateSetting(FOLDED_LOCKED_OPEN_UNLOCKED_SETTING);
        mSettingListenerArgumentCaptor.getValue().onSettingsChanged();
        mTestLooper.dispatchAll();

@@ -200,6 +211,7 @@ public class DeviceStateAutoRotateSettingControllerTests {

    @Test
    public void requestDSAutoRotateSettingChange_nonCurDeviceState_noUpdateAccelerometerRotation() {
        setDeviceState(DeviceStateTestUtils.FOLDED);
        mDeviceStateAutoRotateSettingController.requestDeviceStateAutoRotateSettingChange(
                DeviceStateTestUtils.OPEN.getIdentifier(), true);
        mTestLooper.dispatchAll();
@@ -209,8 +221,9 @@ public class DeviceStateAutoRotateSettingControllerTests {

    @Test
    public void dSAutoRotateCorrupted_writesDefaultSettingWhileRespectingAccelerometerRotation() {
        when(mMockAutoRotateSettingManager.getRotationLockSetting()).thenAnswer(invocation -> null);
        when(mMockAutoRotateSettingManager.getDefaultRotationLockSetting()).thenReturn(
        setDeviceState(DeviceStateTestUtils.FOLDED);
        setDeviceStateAutoRotateSetting("invalid");
        when(mSpyAutoRotateSettingManager.getDefaultRotationLockSetting()).thenReturn(
                createDeviceStateAutoRotateSettingMap(DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
                        DEVICE_STATE_ROTATION_LOCK_UNLOCKED));

@@ -222,9 +235,8 @@ public class DeviceStateAutoRotateSettingControllerTests {

    @Test
    public void multipleSettingChanges_accelerometerRotationSettingTakesPrecedenceWhenConflict() {
        when(mMockAutoRotateSettingManager.getRotationLockSetting()).thenAnswer(invocation ->
                createDeviceStateAutoRotateSettingMap(DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
                        DEVICE_STATE_ROTATION_LOCK_LOCKED));
        setDeviceState(DeviceStateTestUtils.FOLDED);
        setDeviceStateAutoRotateSetting(FOLDED_UNLOCKED_OPEN_LOCKED_SETTING);
        setAccelerometerRotationSetting(ACCELEROMETER_ROTATION_ON);
        mAccelerometerRotationSettingObserver.getValue().onChange(false);
        mTestLooper.dispatchAll();
@@ -237,9 +249,7 @@ public class DeviceStateAutoRotateSettingControllerTests {
        verifyDeviceStateAutoRotateSettingSet(FOLDED_LOCKED_OPEN_LOCKED_SETTING);

        // Change device state auto rotate setting to unlocked for both states
        when(mMockAutoRotateSettingManager.getRotationLockSetting()).thenAnswer(invocation ->
                createDeviceStateAutoRotateSettingMap(DEVICE_STATE_ROTATION_LOCK_LOCKED,
                        DEVICE_STATE_ROTATION_LOCK_UNLOCKED));
        setDeviceStateAutoRotateSetting(FOLDED_LOCKED_OPEN_UNLOCKED_SETTING);
        setAccelerometerRotationSetting(ACCELEROMETER_ROTATION_ON);
        mSettingListenerArgumentCaptor.getValue().onSettingsChanged();
        mTestLooper.dispatchAll();
@@ -249,9 +259,8 @@ public class DeviceStateAutoRotateSettingControllerTests {

    @Test
    public void multipleDeviceStateChanges_updatesAccelerometerRotationForRespectiveDeviceState() {
        when(mMockAutoRotateSettingManager.getRotationLockSetting()).thenAnswer(invocation ->
                createDeviceStateAutoRotateSettingMap(DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
                        DEVICE_STATE_ROTATION_LOCK_LOCKED));
        setDeviceState(DeviceStateTestUtils.FOLDED);
        setDeviceStateAutoRotateSetting(FOLDED_UNLOCKED_OPEN_LOCKED_SETTING);
        setAccelerometerRotationSetting(ACCELEROMETER_ROTATION_ON);
        mSettingListenerArgumentCaptor.getValue().onSettingsChanged();
        mTestLooper.dispatchAll();
@@ -266,6 +275,83 @@ public class DeviceStateAutoRotateSettingControllerTests {
        verifyDeviceStateAutoRotateSettingSet(FOLDED_UNLOCKED_OPEN_LOCKED_SETTING);
    }

    @Test
    public void requestAccelerometerRotationChange_dSUnavailable_noSettingUpdate() {
        mDeviceStateAutoRotateSettingController.requestAccelerometerRotationSettingChange(true);
        mTestLooper.dispatchAll();

        verifyAccelerometerRotationSettingSet(ACCELEROMETER_ROTATION_OFF);
        verifyDeviceStateAutoRotateSettingSet(FOLDED_LOCKED_OPEN_LOCKED_SETTING);
    }

    @Test
    public void requestDSAutoRotateSettingChange_dSUnavailable_noSettingUpdate() {
        mDeviceStateAutoRotateSettingController.requestDeviceStateAutoRotateSettingChange(
                DeviceStateTestUtils.FOLDED.getIdentifier(), true);
        mTestLooper.dispatchAll();

        verifyAccelerometerRotationSettingSet(ACCELEROMETER_ROTATION_OFF);
        verifyDeviceStateAutoRotateSettingSet(FOLDED_LOCKED_OPEN_LOCKED_SETTING);
    }

    @Test
    public void requestAccelerometerRotationChange_dSUnavailable_writeAfterReceivingDSUpdate() {
        mDeviceStateAutoRotateSettingController.requestAccelerometerRotationSettingChange(true);
        mTestLooper.dispatchAll();

        setDeviceState(DeviceStateTestUtils.FOLDED);

        verifyAccelerometerRotationSettingSet(ACCELEROMETER_ROTATION_ON);
        verifyDeviceStateAutoRotateSettingSet(FOLDED_UNLOCKED_OPEN_LOCKED_SETTING);
    }

    @Test
    public void requestDSAutoRotateSettingChange_dSUnavailable_writeAfterReceivingDSUpdate() {
        mDeviceStateAutoRotateSettingController.requestDeviceStateAutoRotateSettingChange(
                DeviceStateTestUtils.FOLDED.getIdentifier(), true);
        mTestLooper.dispatchAll();

        setDeviceState(DeviceStateTestUtils.FOLDED);

        verifyAccelerometerRotationSettingSet(ACCELEROMETER_ROTATION_ON);
        verifyDeviceStateAutoRotateSettingSet(FOLDED_UNLOCKED_OPEN_LOCKED_SETTING);
    }

    @Test
    public void dSUnavailable_sendMultipleRequests_accelerometerPrecedesAfterReceivingDSUpdate() {
        mDeviceStateAutoRotateSettingController.requestDeviceStateAutoRotateSettingChange(
                DeviceStateTestUtils.FOLDED.getIdentifier(), true);
        mDeviceStateAutoRotateSettingController.requestAccelerometerRotationSettingChange(false);
        mDeviceStateAutoRotateSettingController.requestDeviceStateAutoRotateSettingChange(
                DeviceStateTestUtils.OPEN.getIdentifier(), true);
        mTestLooper.dispatchAll();

        setDeviceState(DeviceStateTestUtils.FOLDED);

        verifyAccelerometerRotationSettingSet(ACCELEROMETER_ROTATION_OFF);
        verifyDeviceStateAutoRotateSettingSet(FOLDED_LOCKED_OPEN_UNLOCKED_SETTING);
    }

    @Test
    public void dSUnavailable_sendMultipleRequests_dSAutoRotatePrecedesAfterReceivingDSUpdate() {
        mDeviceStateAutoRotateSettingController.requestAccelerometerRotationSettingChange(false);
        mDeviceStateAutoRotateSettingController.requestDeviceStateAutoRotateSettingChange(
                DeviceStateTestUtils.OPEN.getIdentifier(), true);
        mDeviceStateAutoRotateSettingController.requestDeviceStateAutoRotateSettingChange(
                DeviceStateTestUtils.FOLDED.getIdentifier(), true);
        mTestLooper.dispatchAll();

        setDeviceState(DeviceStateTestUtils.FOLDED);

        verifyAccelerometerRotationSettingSet(ACCELEROMETER_ROTATION_ON);
        verifyDeviceStateAutoRotateSettingSet(FOLDED_UNLOCKED_OPEN_UNLOCKED_SETTING);
    }

    private void setDeviceStateAutoRotateSetting(String deviceStateAutoRotateSetting) {
        Settings.Secure.putStringForUser(mMockResolver, DEVICE_STATE_ROTATION_LOCK,
                deviceStateAutoRotateSetting, UserHandle.USER_CURRENT);
    }

    private void setAccelerometerRotationSetting(int accelerometerRotationSetting) {
        Settings.System.putIntForUser(mMockResolver, ACCELEROMETER_ROTATION,
                accelerometerRotationSetting, UserHandle.USER_CURRENT);