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

Commit 4e0e3b70 authored by Christian Göllner's avatar Christian Göllner Committed by Android (Google) Code Review
Browse files

Merge "Device state rotation: fallback to defaults for invalid ignored state" into tm-qpr-dev

parents e1766512 88bc34f7
Loading
Loading
Loading
Loading
+43 −3
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import android.os.Looper;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.SparseIntArray;

@@ -36,6 +37,7 @@ import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@@ -57,6 +59,7 @@ public final class DeviceStateRotationLockSettingsManager {
    private final SecureSettings mSecureSettings;
    private String[] mDeviceStateRotationLockDefaults;
    private SparseIntArray mDeviceStateRotationLockSettings;
    private SparseIntArray mDeviceStateDefaultRotationLockSettings;
    private SparseIntArray mDeviceStateRotationLockFallbackSettings;
    private String mLastSettingValue;
    private List<SettableDeviceState> mSettableDeviceStates;
@@ -93,9 +96,7 @@ public final class DeviceStateRotationLockSettingsManager {
    /** Returns true if device-state based rotation lock settings are enabled. */
    public static boolean isDeviceStateRotationLockEnabled(Context context) {
        return context.getResources()
                        .getStringArray(R.array.config_perDeviceStateRotationLockDefaults)
                        .length
                > 0;
                .getStringArray(R.array.config_perDeviceStateRotationLockDefaults).length > 0;
    }

    private void listenForSettingsChange() {
@@ -228,6 +229,15 @@ public final class DeviceStateRotationLockSettingsManager {
            try {
                key = Integer.parseInt(values[i++]);
                value = Integer.parseInt(values[i++]);
                boolean isPersistedValueIgnored = value == DEVICE_STATE_ROTATION_LOCK_IGNORED;
                boolean isDefaultValueIgnored = mDeviceStateDefaultRotationLockSettings.get(key)
                        == DEVICE_STATE_ROTATION_LOCK_IGNORED;
                if (isPersistedValueIgnored != isDefaultValueIgnored) {
                    Log.w(TAG, "Conflict for ignored device state " + key
                            + ". Falling back on defaults");
                    fallbackOnDefaults();
                    return;
                }
                mDeviceStateRotationLockSettings.put(key, value);
            } catch (NumberFormatException e) {
                Log.wtf(TAG, "Error deserializing one of the saved settings", e);
@@ -276,6 +286,9 @@ public final class DeviceStateRotationLockSettingsManager {
    }

    private void persistSettingIfChanged(String newSettingValue) {
        Log.v(TAG, "persistSettingIfChanged: "
                + "last=" + mLastSettingValue + ", "
                + "new=" + newSettingValue);
        if (TextUtils.equals(mLastSettingValue, newSettingValue)) {
            return;
        }
@@ -288,6 +301,8 @@ public final class DeviceStateRotationLockSettingsManager {

    private void loadDefaults() {
        mSettableDeviceStates = new ArrayList<>(mDeviceStateRotationLockDefaults.length);
        mDeviceStateDefaultRotationLockSettings = new SparseIntArray(
                mDeviceStateRotationLockDefaults.length);
        mDeviceStateRotationLockSettings = new SparseIntArray(
                mDeviceStateRotationLockDefaults.length);
        mDeviceStateRotationLockFallbackSettings = new SparseIntArray(1);
@@ -311,6 +326,7 @@ public final class DeviceStateRotationLockSettingsManager {
                boolean isSettable = rotationLockSetting != DEVICE_STATE_ROTATION_LOCK_IGNORED;
                mSettableDeviceStates.add(new SettableDeviceState(deviceState, isSettable));
                mDeviceStateRotationLockSettings.put(deviceState, rotationLockSetting);
                mDeviceStateDefaultRotationLockSettings.put(deviceState, rotationLockSetting);
            } catch (NumberFormatException e) {
                Log.wtf(TAG, "Error parsing settings entry. Entry was: " + entry, e);
                return;
@@ -318,6 +334,22 @@ public final class DeviceStateRotationLockSettingsManager {
        }
    }

    /** Dumps internal state. */
    public void dump(IndentingPrintWriter pw) {
        pw.println("DeviceStateRotationLockSettingsManager");
        pw.increaseIndent();
        pw.println("mDeviceStateRotationLockDefaults: " + Arrays.toString(
                mDeviceStateRotationLockDefaults));
        pw.println("mDeviceStateDefaultRotationLockSettings: "
                + mDeviceStateDefaultRotationLockSettings);
        pw.println("mDeviceStateRotationLockSettings: " + mDeviceStateRotationLockSettings);
        pw.println("mDeviceStateRotationLockFallbackSettings: "
                + mDeviceStateRotationLockFallbackSettings);
        pw.println("mSettableDeviceStates: " + mSettableDeviceStates);
        pw.println("mLastSettingValue: " + mLastSettingValue);
        pw.decreaseIndent();
    }

    /**
     * Called when the persisted settings have changed, requiring a reinitialization of the
     * in-memory map.
@@ -372,5 +404,13 @@ public final class DeviceStateRotationLockSettingsManager {
        public int hashCode() {
            return Objects.hash(mDeviceState, mIsSettable);
        }

        @Override
        public String toString() {
            return "SettableDeviceState{"
                    + "mDeviceState=" + mDeviceState
                    + ", mIsSettable=" + mIsSettable
                    + '}';
        }
    }
}
+41 −0
Original line number Diff line number Diff line
@@ -33,7 +33,10 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager.SettableDeviceState;

import com.google.common.truth.Expect;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -45,6 +48,8 @@ import java.util.List;
@RunWith(AndroidJUnit4.class)
public class DeviceStateRotationLockSettingsManagerTest {

    @Rule public Expect mExpect = Expect.create();

    @Mock private Context mMockContext;
    @Mock private Resources mMockResources;

@@ -117,4 +122,40 @@ public class DeviceStateRotationLockSettingsManagerTest {
                new SettableDeviceState(/* deviceState= */ 0, /* isSettable= */ false)
        ).inOrder();
    }

    @Test
    public void persistedInvalidIgnoredState_returnsDefaults() {
        when(mMockResources.getStringArray(
                R.array.config_perDeviceStateRotationLockDefaults)).thenReturn(
                new String[]{"0:1", "1:0:2", "2:2"});
        // Here 2 has IGNORED, and in the defaults 1 has IGNORED.
        persistSettings("0:2:2:0:1:2");
        DeviceStateRotationLockSettingsManager manager =
                new DeviceStateRotationLockSettingsManager(mMockContext, mFakeSecureSettings);

        mExpect.that(manager.getRotationLockSetting(0)).isEqualTo(1);
        mExpect.that(manager.getRotationLockSetting(1)).isEqualTo(2);
        mExpect.that(manager.getRotationLockSetting(2)).isEqualTo(2);
    }

    @Test
    public void persistedValidValues_returnsPersistedValues() {
        when(mMockResources.getStringArray(
                R.array.config_perDeviceStateRotationLockDefaults)).thenReturn(
                new String[]{"0:1", "1:0:2", "2:2"});
        persistSettings("0:2:1:0:2:1");
        DeviceStateRotationLockSettingsManager manager =
                new DeviceStateRotationLockSettingsManager(mMockContext, mFakeSecureSettings);

        mExpect.that(manager.getRotationLockSetting(0)).isEqualTo(2);
        mExpect.that(manager.getRotationLockSetting(1)).isEqualTo(1);
        mExpect.that(manager.getRotationLockSetting(2)).isEqualTo(1);
    }

    private void persistSettings(String value) {
        mFakeSecureSettings.putStringForUser(
                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
                value,
                UserHandle.USER_CURRENT);
    }
}
+30 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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 com.android.systemui.log.dagger;

import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;

import javax.inject.Qualifier;

@Qualifier
@Documented
@Retention(RUNTIME)
public @interface DeviceStateAutoRotationLog {
}
+10 −0
Original line number Diff line number Diff line
@@ -369,6 +369,16 @@ public class LogModule {
        return factory.create("KeyguardFaceAuthManagerLog", 300);
    }

    /**
     * Provides a {@link LogBuffer} for Device State Auto-Rotation logs.
     */
    @Provides
    @SysUISingleton
    @DeviceStateAutoRotationLog
    public static LogBuffer provideDeviceStateAutoRotationLogBuffer(LogBufferFactory factory) {
        return factory.create("DeviceStateAutoRotationLog", 100);
    }

    /**
     * Provides a {@link LogBuffer} for bluetooth-related logs.
     */
+49 −32
Original line number Diff line number Diff line
@@ -22,13 +22,18 @@ import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED
import android.annotation.Nullable;
import android.hardware.devicestate.DeviceStateManager;
import android.os.Trace;
import android.util.Log;
import android.util.IndentingPrintWriter;

import androidx.annotation.NonNull;

import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.util.wrapper.RotationPolicyWrapper;

import java.io.PrintWriter;
import java.util.concurrent.Executor;

import javax.inject.Inject;
@@ -39,19 +44,19 @@ import javax.inject.Inject;
 */
@SysUISingleton
public final class DeviceStateRotationLockSettingController
        implements Listenable, RotationLockController.RotationLockControllerCallback {

    private static final String TAG = "DSRotateLockSettingCon";
        implements Listenable, RotationLockController.RotationLockControllerCallback, Dumpable {

    private final RotationPolicyWrapper mRotationPolicyWrapper;
    private final DeviceStateManager mDeviceStateManager;
    private final Executor mMainExecutor;
    private final DeviceStateRotationLockSettingsManager mDeviceStateRotationLockSettingsManager;
    private final DeviceStateRotationLockSettingControllerLogger mLogger;

    // On registration for DeviceStateCallback, we will receive a callback with the current state
    // and this will be initialized.
    private int mDeviceState = -1;
    @Nullable private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
    @Nullable
    private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
    private DeviceStateRotationLockSettingsManager.DeviceStateRotationLockSettingsListener
            mDeviceStateRotationLockSettingsListener;

@@ -60,21 +65,27 @@ public final class DeviceStateRotationLockSettingController
            RotationPolicyWrapper rotationPolicyWrapper,
            DeviceStateManager deviceStateManager,
            @Main Executor executor,
            DeviceStateRotationLockSettingsManager deviceStateRotationLockSettingsManager) {
            DeviceStateRotationLockSettingsManager deviceStateRotationLockSettingsManager,
            DeviceStateRotationLockSettingControllerLogger logger,
            DumpManager dumpManager) {
        mRotationPolicyWrapper = rotationPolicyWrapper;
        mDeviceStateManager = deviceStateManager;
        mMainExecutor = executor;
        mDeviceStateRotationLockSettingsManager = deviceStateRotationLockSettingsManager;
        mLogger = logger;
        dumpManager.registerDumpable(this);
    }

    @Override
    public void setListening(boolean listening) {
        mLogger.logListeningChange(listening);
        if (listening) {
            // Note that this is called once with the initial state of the device, even if there
            // is no user action.
            mDeviceStateCallback = this::updateDeviceState;
            mDeviceStateManager.registerCallback(mMainExecutor, mDeviceStateCallback);
            mDeviceStateRotationLockSettingsListener = () -> readPersistedSetting(mDeviceState);
            mDeviceStateRotationLockSettingsListener = () ->
                    readPersistedSetting("deviceStateRotationLockChange", mDeviceState);
            mDeviceStateRotationLockSettingsManager.registerListener(
                    mDeviceStateRotationLockSettingsListener);
        } else {
@@ -89,35 +100,28 @@ public final class DeviceStateRotationLockSettingController
    }

    @Override
    public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) {
        if (mDeviceState == -1) {
            Log.wtf(TAG, "Device state was not initialized.");
    public void onRotationLockStateChanged(boolean newRotationLocked, boolean affordanceVisible) {
        int deviceState = mDeviceState;
        boolean currentRotationLocked = mDeviceStateRotationLockSettingsManager
                .isRotationLocked(deviceState);
        mLogger.logRotationLockStateChanged(deviceState, newRotationLocked, currentRotationLocked);
        if (deviceState == -1) {
            return;
        }

        if (rotationLocked
                == mDeviceStateRotationLockSettingsManager.isRotationLocked(mDeviceState)) {
            Log.v(TAG, "Rotation lock same as the current setting, no need to update.");
        if (newRotationLocked == currentRotationLocked) {
            return;
        }

        saveNewRotationLockSetting(rotationLocked);
        saveNewRotationLockSetting(newRotationLocked);
    }

    private void saveNewRotationLockSetting(boolean isRotationLocked) {
        Log.v(
                TAG,
                "saveNewRotationLockSetting [state="
                        + mDeviceState
                        + "] [isRotationLocked="
                        + isRotationLocked
                        + "]");

        mDeviceStateRotationLockSettingsManager.updateSetting(mDeviceState, isRotationLocked);
        int deviceState = mDeviceState;
        mLogger.logSaveNewRotationLockSetting(isRotationLocked, deviceState);
        mDeviceStateRotationLockSettingsManager.updateSetting(deviceState, isRotationLocked);
    }

    private void updateDeviceState(int state) {
        Log.v(TAG, "updateDeviceState [state=" + state + "]");
        mLogger.logUpdateDeviceState(mDeviceState, state);
        if (Trace.isEnabled()) {
            Trace.traceBegin(
                    Trace.TRACE_TAG_APP, "updateDeviceState [state=" + state + "]");
@@ -127,22 +131,26 @@ public final class DeviceStateRotationLockSettingController
                return;
            }

            readPersistedSetting(state);
            readPersistedSetting("updateDeviceState", state);
        } finally {
            Trace.endSection();
        }
    }

    private void readPersistedSetting(int state) {
    private void readPersistedSetting(String caller, int state) {
        int rotationLockSetting =
                mDeviceStateRotationLockSettingsManager.getRotationLockSetting(state);
        boolean shouldBeLocked = rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_LOCKED;
        boolean isLocked = mRotationPolicyWrapper.isRotationLocked();

        mLogger.readPersistedSetting(caller, state, rotationLockSetting, shouldBeLocked, isLocked);

        if (rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
            // This should not happen. Device states that have an ignored setting, should also
            // specify a fallback device state which is not ignored.
            // We won't handle this device state. The same rotation lock setting as before should
            // apply and any changes to the rotation lock setting will be written for the previous
            // valid device state.
            Log.w(TAG, "Missing fallback. Ignoring new device state: " + state);
            return;
        }

@@ -150,9 +158,18 @@ public final class DeviceStateRotationLockSettingController
        mDeviceState = state;

        // Update the rotation policy, if needed, for this new device state
        boolean newRotationLockSetting = rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_LOCKED;
        if (newRotationLockSetting != mRotationPolicyWrapper.isRotationLocked()) {
            mRotationPolicyWrapper.setRotationLock(newRotationLockSetting);
        if (shouldBeLocked != isLocked) {
            mRotationPolicyWrapper.setRotationLock(shouldBeLocked);
        }
    }

    @Override
    public void dump(@NonNull PrintWriter printWriter, @NonNull String[] args) {
        IndentingPrintWriter pw = new IndentingPrintWriter(printWriter);
        mDeviceStateRotationLockSettingsManager.dump(pw);
        pw.println("DeviceStateRotationLockSettingController");
        pw.increaseIndent();
        pw.println("mDeviceState: " + mDeviceState);
        pw.decreaseIndent();
    }
}
Loading