1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.statusbar.policy;
18 
19 import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED;
20 import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
21 
22 import android.annotation.Nullable;
23 import android.hardware.devicestate.DeviceStateManager;
24 import android.os.Trace;
25 import android.util.IndentingPrintWriter;
26 
27 import androidx.annotation.NonNull;
28 
29 import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
30 import com.android.systemui.Dumpable;
31 import com.android.systemui.dagger.SysUISingleton;
32 import com.android.systemui.dagger.qualifiers.Main;
33 import com.android.systemui.dump.DumpManager;
34 import com.android.systemui.util.wrapper.RotationPolicyWrapper;
35 
36 import java.io.PrintWriter;
37 import java.util.concurrent.Executor;
38 
39 import javax.inject.Inject;
40 
41 /**
42  * Handles reading and writing of rotation lock settings per device state, as well as setting the
43  * rotation lock when device state changes.
44  */
45 @SysUISingleton
46 public final class DeviceStateRotationLockSettingController
47         implements Listenable, RotationLockController.RotationLockControllerCallback, Dumpable {
48 
49     private final RotationPolicyWrapper mRotationPolicyWrapper;
50     private final DeviceStateManager mDeviceStateManager;
51     private final Executor mMainExecutor;
52     private final DeviceStateRotationLockSettingsManager mDeviceStateRotationLockSettingsManager;
53     private final DeviceStateRotationLockSettingControllerLogger mLogger;
54 
55     // On registration for DeviceStateCallback, we will receive a callback with the current state
56     // and this will be initialized.
57     private int mDeviceState = -1;
58     @Nullable
59     private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
60     private DeviceStateRotationLockSettingsManager.DeviceStateRotationLockSettingsListener
61             mDeviceStateRotationLockSettingsListener;
62 
63     @Inject
DeviceStateRotationLockSettingController( RotationPolicyWrapper rotationPolicyWrapper, DeviceStateManager deviceStateManager, @Main Executor executor, DeviceStateRotationLockSettingsManager deviceStateRotationLockSettingsManager, DeviceStateRotationLockSettingControllerLogger logger, DumpManager dumpManager)64     public DeviceStateRotationLockSettingController(
65             RotationPolicyWrapper rotationPolicyWrapper,
66             DeviceStateManager deviceStateManager,
67             @Main Executor executor,
68             DeviceStateRotationLockSettingsManager deviceStateRotationLockSettingsManager,
69             DeviceStateRotationLockSettingControllerLogger logger,
70             DumpManager dumpManager) {
71         mRotationPolicyWrapper = rotationPolicyWrapper;
72         mDeviceStateManager = deviceStateManager;
73         mMainExecutor = executor;
74         mDeviceStateRotationLockSettingsManager = deviceStateRotationLockSettingsManager;
75         mLogger = logger;
76         dumpManager.registerDumpable(this);
77     }
78 
79     @Override
setListening(boolean listening)80     public void setListening(boolean listening) {
81         mLogger.logListeningChange(listening);
82         if (listening) {
83             // Note that this is called once with the initial state of the device, even if there
84             // is no user action.
85             mDeviceStateCallback = this::updateDeviceState;
86             mDeviceStateManager.registerCallback(mMainExecutor, mDeviceStateCallback);
87             mDeviceStateRotationLockSettingsListener = () ->
88                     readPersistedSetting("deviceStateRotationLockChange", mDeviceState);
89             mDeviceStateRotationLockSettingsManager.registerListener(
90                     mDeviceStateRotationLockSettingsListener);
91         } else {
92             if (mDeviceStateCallback != null) {
93                 mDeviceStateManager.unregisterCallback(mDeviceStateCallback);
94             }
95             if (mDeviceStateRotationLockSettingsListener != null) {
96                 mDeviceStateRotationLockSettingsManager.unregisterListener(
97                         mDeviceStateRotationLockSettingsListener);
98             }
99         }
100     }
101 
102     @Override
onRotationLockStateChanged(boolean newRotationLocked, boolean affordanceVisible)103     public void onRotationLockStateChanged(boolean newRotationLocked, boolean affordanceVisible) {
104         int deviceState = mDeviceState;
105         boolean currentRotationLocked = mDeviceStateRotationLockSettingsManager
106                 .isRotationLocked(deviceState);
107         mLogger.logRotationLockStateChanged(deviceState, newRotationLocked, currentRotationLocked);
108         if (deviceState == -1) {
109             return;
110         }
111         if (newRotationLocked == currentRotationLocked) {
112             return;
113         }
114         saveNewRotationLockSetting(newRotationLocked);
115     }
116 
saveNewRotationLockSetting(boolean isRotationLocked)117     private void saveNewRotationLockSetting(boolean isRotationLocked) {
118         int deviceState = mDeviceState;
119         mLogger.logSaveNewRotationLockSetting(isRotationLocked, deviceState);
120         mDeviceStateRotationLockSettingsManager.updateSetting(deviceState, isRotationLocked);
121     }
122 
updateDeviceState(int state)123     private void updateDeviceState(int state) {
124         mLogger.logUpdateDeviceState(mDeviceState, state);
125         if (Trace.isEnabled()) {
126             Trace.traceBegin(
127                     Trace.TRACE_TAG_APP, "updateDeviceState [state=" + state + "]");
128         }
129         try {
130             if (mDeviceState == state) {
131                 return;
132             }
133 
134             readPersistedSetting("updateDeviceState", state);
135         } finally {
136             Trace.endSection();
137         }
138     }
139 
readPersistedSetting(String caller, int state)140     private void readPersistedSetting(String caller, int state) {
141         int rotationLockSetting =
142                 mDeviceStateRotationLockSettingsManager.getRotationLockSetting(state);
143         boolean shouldBeLocked = rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_LOCKED;
144         boolean isLocked = mRotationPolicyWrapper.isRotationLocked();
145 
146         mLogger.readPersistedSetting(caller, state, rotationLockSetting, shouldBeLocked, isLocked);
147 
148         if (rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
149             // This should not happen. Device states that have an ignored setting, should also
150             // specify a fallback device state which is not ignored.
151             // We won't handle this device state. The same rotation lock setting as before should
152             // apply and any changes to the rotation lock setting will be written for the previous
153             // valid device state.
154             return;
155         }
156 
157         // Accept the new state
158         mDeviceState = state;
159 
160         // Update the rotation policy, if needed, for this new device state
161         if (shouldBeLocked != isLocked) {
162             mRotationPolicyWrapper.setRotationLock(shouldBeLocked);
163         }
164     }
165 
166     @Override
dump(@onNull PrintWriter printWriter, @NonNull String[] args)167     public void dump(@NonNull PrintWriter printWriter, @NonNull String[] args) {
168         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter);
169         mDeviceStateRotationLockSettingsManager.dump(pw);
170         pw.println("DeviceStateRotationLockSettingController");
171         pw.increaseIndent();
172         pw.println("mDeviceState: " + mDeviceState);
173         pw.decreaseIndent();
174     }
175 }
176