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