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.DeviceState;
24 import android.hardware.devicestate.DeviceStateManager;
25 import android.os.Trace;
26 import android.util.IndentingPrintWriter;
27 
28 import androidx.annotation.NonNull;
29 
30 import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
31 import com.android.systemui.Dumpable;
32 import com.android.systemui.dagger.SysUISingleton;
33 import com.android.systemui.dagger.qualifiers.Main;
34 import com.android.systemui.dump.DumpManager;
35 import com.android.systemui.util.wrapper.RotationPolicyWrapper;
36 
37 import java.io.PrintWriter;
38 import java.util.concurrent.Executor;
39 
40 import javax.inject.Inject;
41 
42 /**
43  * Handles reading and writing of rotation lock settings per device state, as well as setting the
44  * rotation lock when device state changes.
45  */
46 @SysUISingleton
47 public final class DeviceStateRotationLockSettingController
48         implements Listenable, RotationLockController.RotationLockControllerCallback, Dumpable {
49 
50     private final RotationPolicyWrapper mRotationPolicyWrapper;
51     private final DeviceStateManager mDeviceStateManager;
52     private final Executor mMainExecutor;
53     private final DeviceStateRotationLockSettingsManager mDeviceStateRotationLockSettingsManager;
54     private final DeviceStateRotationLockSettingControllerLogger mLogger;
55 
56     // On registration for DeviceStateCallback, we will receive a callback with the current state
57     // and this will be initialized.
58     private int mDeviceState = -1;
59     @Nullable
60     private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
61     private DeviceStateRotationLockSettingsManager.DeviceStateRotationLockSettingsListener
62             mDeviceStateRotationLockSettingsListener;
63 
64     @Inject
DeviceStateRotationLockSettingController( RotationPolicyWrapper rotationPolicyWrapper, DeviceStateManager deviceStateManager, @Main Executor executor, DeviceStateRotationLockSettingsManager deviceStateRotationLockSettingsManager, DeviceStateRotationLockSettingControllerLogger logger, DumpManager dumpManager)65     public DeviceStateRotationLockSettingController(
66             RotationPolicyWrapper rotationPolicyWrapper,
67             DeviceStateManager deviceStateManager,
68             @Main Executor executor,
69             DeviceStateRotationLockSettingsManager deviceStateRotationLockSettingsManager,
70             DeviceStateRotationLockSettingControllerLogger logger,
71             DumpManager dumpManager) {
72         mRotationPolicyWrapper = rotationPolicyWrapper;
73         mDeviceStateManager = deviceStateManager;
74         mMainExecutor = executor;
75         mDeviceStateRotationLockSettingsManager = deviceStateRotationLockSettingsManager;
76         mLogger = logger;
77         dumpManager.registerDumpable(this);
78     }
79 
80     @Override
setListening(boolean listening)81     public void setListening(boolean listening) {
82         mLogger.logListeningChange(listening);
83         if (listening) {
84             // Note that this is called once with the initial state of the device, even if there
85             // is no user action.
86             mDeviceStateCallback = this::updateDeviceState;
87             mDeviceStateManager.registerCallback(mMainExecutor, mDeviceStateCallback);
88             mDeviceStateRotationLockSettingsListener = () ->
89                     readPersistedSetting("deviceStateRotationLockChange", mDeviceState);
90             mDeviceStateRotationLockSettingsManager.registerListener(
91                     mDeviceStateRotationLockSettingsListener);
92         } else {
93             if (mDeviceStateCallback != null) {
94                 mDeviceStateManager.unregisterCallback(mDeviceStateCallback);
95             }
96             if (mDeviceStateRotationLockSettingsListener != null) {
97                 mDeviceStateRotationLockSettingsManager.unregisterListener(
98                         mDeviceStateRotationLockSettingsListener);
99             }
100         }
101     }
102 
103     @Override
onRotationLockStateChanged(boolean newRotationLocked, boolean affordanceVisible)104     public void onRotationLockStateChanged(boolean newRotationLocked, boolean affordanceVisible) {
105         int deviceState = mDeviceState;
106         boolean currentRotationLocked = mDeviceStateRotationLockSettingsManager
107                 .isRotationLocked(deviceState);
108         mLogger.logRotationLockStateChanged(deviceState, newRotationLocked, currentRotationLocked);
109         if (deviceState == -1) {
110             return;
111         }
112         if (newRotationLocked == currentRotationLocked) {
113             return;
114         }
115         saveNewRotationLockSetting(newRotationLocked);
116     }
117 
saveNewRotationLockSetting(boolean isRotationLocked)118     private void saveNewRotationLockSetting(boolean isRotationLocked) {
119         int deviceState = mDeviceState;
120         mLogger.logSaveNewRotationLockSetting(isRotationLocked, deviceState);
121         mDeviceStateRotationLockSettingsManager.updateSetting(deviceState, isRotationLocked);
122     }
123 
updateDeviceState(@onNull DeviceState state)124     private void updateDeviceState(@NonNull DeviceState state) {
125         mLogger.logUpdateDeviceState(mDeviceState, state.getIdentifier());
126         try {
127             if (Trace.isEnabled()) {
128                 Trace.traceBegin(Trace.TRACE_TAG_APP,
129                         "updateDeviceState [state=" + state.getIdentifier() + "]");
130             }
131             if (mDeviceState == state.getIdentifier()) {
132                 return;
133             }
134 
135             readPersistedSetting("updateDeviceState", state.getIdentifier());
136         } finally {
137             Trace.endSection();
138         }
139     }
140 
readPersistedSetting(String caller, int state)141     private void readPersistedSetting(String caller, int state) {
142         int rotationLockSetting =
143                 mDeviceStateRotationLockSettingsManager.getRotationLockSetting(state);
144         boolean shouldBeLocked = rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_LOCKED;
145         boolean isLocked = mRotationPolicyWrapper.isRotationLocked();
146 
147         mLogger.readPersistedSetting(caller, state, rotationLockSetting, shouldBeLocked, isLocked);
148 
149         if (rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
150             // This should not happen. Device states that have an ignored setting, should also
151             // specify a fallback device state which is not ignored.
152             // We won't handle this device state. The same rotation lock setting as before should
153             // apply and any changes to the rotation lock setting will be written for the previous
154             // valid device state.
155             return;
156         }
157 
158         // Accept the new state
159         mDeviceState = state;
160 
161         // Update the rotation policy, if needed, for this new device state
162         if (shouldBeLocked != isLocked) {
163             mRotationPolicyWrapper.setRotationLock(shouldBeLocked,
164                     /* caller= */"DeviceStateRotationLockSettingController#readPersistedSetting");
165         }
166     }
167 
168     @Override
dump(@onNull PrintWriter printWriter, @NonNull String[] args)169     public void dump(@NonNull PrintWriter printWriter, @NonNull String[] args) {
170         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter);
171         mDeviceStateRotationLockSettingsManager.dump(pw);
172         pw.println("DeviceStateRotationLockSettingController");
173         pw.increaseIndent();
174         pw.println("mDeviceState: " + mDeviceState);
175         pw.decreaseIndent();
176     }
177 }
178