1 /* 2 * Copyright (C) 2022 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.doze; 18 19 import static android.content.res.Configuration.UI_MODE_TYPE_CAR; 20 21 import android.hardware.display.AmbientDisplayConfiguration; 22 import android.os.PowerManager; 23 import android.text.TextUtils; 24 25 import com.android.systemui.doze.dagger.DozeScope; 26 import com.android.systemui.settings.UserTracker; 27 import com.android.systemui.statusbar.phone.BiometricUnlockController; 28 29 import java.io.PrintWriter; 30 31 import javax.inject.Inject; 32 33 import dagger.Lazy; 34 35 /** 36 * Handles suppressing doze on: 37 * 1. INITIALIZED, don't allow dozing at all when: 38 * - in CAR_MODE, in this scenario the device is asleep and won't listen for any triggers 39 * to wake up. In this state, no UI shows. Unlike other conditions, this suppression is only 40 * temporary and stops when the device exits CAR_MODE 41 * - device is NOT provisioned 42 * - there's a pending authentication 43 * 2. PowerSaveMode active 44 * - no always-on-display (DOZE_AOD) 45 * - continues to allow doze triggers (DOZE, DOZE_REQUEST_PULSE) 46 * 3. Suppression changes from the PowerManager API. See {@link PowerManager#suppressAmbientDisplay} 47 * and {@link DozeHost#isAlwaysOnSuppressed()}. 48 * - no always-on-display (DOZE_AOD) 49 * - allow doze triggers (DOZE), but disallow notifications (handled by {@link DozeTriggers}) 50 * - See extra check in {@link DozeMachine} to guarantee device never enters always-on states 51 */ 52 @DozeScope 53 public class DozeSuppressor implements DozeMachine.Part { 54 55 private DozeMachine mMachine; 56 private final DozeHost mDozeHost; 57 private final AmbientDisplayConfiguration mConfig; 58 private final DozeLog mDozeLog; 59 private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy; 60 private final UserTracker mUserTracker; 61 62 private boolean mIsCarModeEnabled = false; 63 64 @Inject DozeSuppressor( DozeHost dozeHost, AmbientDisplayConfiguration config, DozeLog dozeLog, Lazy<BiometricUnlockController> biometricUnlockControllerLazy, UserTracker userTracker)65 public DozeSuppressor( 66 DozeHost dozeHost, 67 AmbientDisplayConfiguration config, 68 DozeLog dozeLog, 69 Lazy<BiometricUnlockController> biometricUnlockControllerLazy, 70 UserTracker userTracker) { 71 mDozeHost = dozeHost; 72 mConfig = config; 73 mDozeLog = dozeLog; 74 mBiometricUnlockControllerLazy = biometricUnlockControllerLazy; 75 mUserTracker = userTracker; 76 } 77 78 @Override onUiModeTypeChanged(int newUiModeType)79 public void onUiModeTypeChanged(int newUiModeType) { 80 boolean isCarModeEnabled = newUiModeType == UI_MODE_TYPE_CAR; 81 if (mIsCarModeEnabled == isCarModeEnabled) { 82 return; 83 } 84 mIsCarModeEnabled = isCarModeEnabled; 85 // Do not handle the event if doze machine is not initialized yet. 86 // It will be handled upon initialization. 87 if (mMachine.isUninitializedOrFinished()) { 88 return; 89 } 90 if (mIsCarModeEnabled) { 91 handleCarModeStarted(); 92 } else { 93 handleCarModeExited(); 94 } 95 } 96 97 @Override setDozeMachine(DozeMachine dozeMachine)98 public void setDozeMachine(DozeMachine dozeMachine) { 99 mMachine = dozeMachine; 100 } 101 102 @Override transitionTo(DozeMachine.State oldState, DozeMachine.State newState)103 public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { 104 switch (newState) { 105 case INITIALIZED: 106 mDozeHost.addCallback(mHostCallback); 107 checkShouldImmediatelyEndDoze(); 108 checkShouldImmediatelySuspendDoze(); 109 break; 110 case FINISH: 111 destroy(); 112 break; 113 default: 114 } 115 } 116 117 @Override destroy()118 public void destroy() { 119 mDozeHost.removeCallback(mHostCallback); 120 } 121 checkShouldImmediatelySuspendDoze()122 private void checkShouldImmediatelySuspendDoze() { 123 if (mIsCarModeEnabled) { 124 handleCarModeStarted(); 125 } 126 } 127 checkShouldImmediatelyEndDoze()128 private void checkShouldImmediatelyEndDoze() { 129 String reason = null; 130 if (!mDozeHost.isProvisioned()) { 131 reason = "device_unprovisioned"; 132 } else if (mBiometricUnlockControllerLazy.get().hasPendingAuthentication()) { 133 reason = "has_pending_auth"; 134 } 135 136 if (!TextUtils.isEmpty(reason)) { 137 mDozeLog.traceImmediatelyEndDoze(reason); 138 mMachine.requestState(DozeMachine.State.FINISH); 139 } 140 } 141 142 @Override dump(PrintWriter pw)143 public void dump(PrintWriter pw) { 144 pw.println(" isCarModeEnabled=" + mIsCarModeEnabled); 145 pw.println(" hasPendingAuth=" 146 + mBiometricUnlockControllerLazy.get().hasPendingAuthentication()); 147 pw.println(" isProvisioned=" + mDozeHost.isProvisioned()); 148 pw.println(" isAlwaysOnSuppressed=" + mDozeHost.isAlwaysOnSuppressed()); 149 pw.println(" aodPowerSaveActive=" + mDozeHost.isPowerSaveActive()); 150 } 151 handleCarModeExited()152 private void handleCarModeExited() { 153 mDozeLog.traceCarModeEnded(); 154 mMachine.requestState(mConfig.alwaysOnEnabled(mUserTracker.getUserId()) 155 ? DozeMachine.State.DOZE_AOD : DozeMachine.State.DOZE); 156 } 157 handleCarModeStarted()158 private void handleCarModeStarted() { 159 mDozeLog.traceCarModeStarted(); 160 mMachine.requestState(DozeMachine.State.DOZE_SUSPEND_TRIGGERS); 161 } 162 163 private final DozeHost.Callback mHostCallback = new DozeHost.Callback() { 164 @Override 165 public void onPowerSaveChanged(boolean active) { 166 // handles suppression changes, while DozeMachine#transitionPolicy handles gating 167 // transitions to DOZE_AOD 168 DozeMachine.State nextState = null; 169 if (mDozeHost.isPowerSaveActive()) { 170 nextState = DozeMachine.State.DOZE; 171 } else if (mMachine.getState() == DozeMachine.State.DOZE 172 && mConfig.alwaysOnEnabled(mUserTracker.getUserId())) { 173 nextState = DozeMachine.State.DOZE_AOD; 174 } 175 176 if (nextState != null) { 177 mDozeLog.tracePowerSaveChanged(mDozeHost.isPowerSaveActive(), nextState); 178 mMachine.requestState(nextState); 179 } 180 } 181 182 @Override 183 public void onAlwaysOnSuppressedChanged(boolean suppressed) { 184 // handles suppression changes, while DozeMachine#transitionPolicy handles gating 185 // transitions to DOZE_AOD 186 final DozeMachine.State nextState; 187 if (mConfig.alwaysOnEnabled(mUserTracker.getUserId()) && !suppressed) { 188 nextState = DozeMachine.State.DOZE_AOD; 189 } else { 190 nextState = DozeMachine.State.DOZE; 191 } 192 mDozeLog.traceAlwaysOnSuppressedChange(suppressed, nextState); 193 mMachine.requestState(nextState); 194 } 195 }; 196 } 197