blob: de0bdd344a72b47cca9d26a9954ff3f34683b109 [file] [log] [blame]
/*
* Copyright (C) 2022 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.doze;
import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.PowerManager;
import android.text.TextUtils;
import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import java.io.PrintWriter;
import javax.inject.Inject;
import dagger.Lazy;
/**
* Handles suppressing doze on:
* 1. INITIALIZED, don't allow dozing at all when:
* - in CAR_MODE, in this scenario the device is asleep and won't listen for any triggers
* to wake up. In this state, no UI shows. Unlike other conditions, this suppression is only
* temporary and stops when the device exits CAR_MODE
* - device is NOT provisioned
* - there's a pending authentication
* 2. PowerSaveMode active
* - no always-on-display (DOZE_AOD)
* - continues to allow doze triggers (DOZE, DOZE_REQUEST_PULSE)
* 3. Suppression changes from the PowerManager API. See {@link PowerManager#suppressAmbientDisplay}
* and {@link DozeHost#isAlwaysOnSuppressed()}.
* - no always-on-display (DOZE_AOD)
* - allow doze triggers (DOZE), but disallow notifications (handled by {@link DozeTriggers})
* - See extra check in {@link DozeMachine} to guarantee device never enters always-on states
*/
@DozeScope
public class DozeSuppressor implements DozeMachine.Part {
private DozeMachine mMachine;
private final DozeHost mDozeHost;
private final AmbientDisplayConfiguration mConfig;
private final DozeLog mDozeLog;
private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
private final UserTracker mUserTracker;
private boolean mIsCarModeEnabled = false;
@Inject
public DozeSuppressor(
DozeHost dozeHost,
AmbientDisplayConfiguration config,
DozeLog dozeLog,
Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
UserTracker userTracker) {
mDozeHost = dozeHost;
mConfig = config;
mDozeLog = dozeLog;
mBiometricUnlockControllerLazy = biometricUnlockControllerLazy;
mUserTracker = userTracker;
}
@Override
public void onUiModeTypeChanged(int newUiModeType) {
boolean isCarModeEnabled = newUiModeType == UI_MODE_TYPE_CAR;
if (mIsCarModeEnabled == isCarModeEnabled) {
return;
}
mIsCarModeEnabled = isCarModeEnabled;
// Do not handle the event if doze machine is not initialized yet.
// It will be handled upon initialization.
if (mMachine.isUninitializedOrFinished()) {
return;
}
if (mIsCarModeEnabled) {
handleCarModeStarted();
} else {
handleCarModeExited();
}
}
@Override
public void setDozeMachine(DozeMachine dozeMachine) {
mMachine = dozeMachine;
}
@Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
switch (newState) {
case INITIALIZED:
mDozeHost.addCallback(mHostCallback);
checkShouldImmediatelyEndDoze();
checkShouldImmediatelySuspendDoze();
break;
case FINISH:
destroy();
break;
default:
}
}
@Override
public void destroy() {
mDozeHost.removeCallback(mHostCallback);
}
private void checkShouldImmediatelySuspendDoze() {
if (mIsCarModeEnabled) {
handleCarModeStarted();
}
}
private void checkShouldImmediatelyEndDoze() {
String reason = null;
if (!mDozeHost.isProvisioned()) {
reason = "device_unprovisioned";
} else if (mBiometricUnlockControllerLazy.get().hasPendingAuthentication()) {
reason = "has_pending_auth";
}
if (!TextUtils.isEmpty(reason)) {
mDozeLog.traceImmediatelyEndDoze(reason);
mMachine.requestState(DozeMachine.State.FINISH);
}
}
@Override
public void dump(PrintWriter pw) {
pw.println(" isCarModeEnabled=" + mIsCarModeEnabled);
pw.println(" hasPendingAuth="
+ mBiometricUnlockControllerLazy.get().hasPendingAuthentication());
pw.println(" isProvisioned=" + mDozeHost.isProvisioned());
pw.println(" isAlwaysOnSuppressed=" + mDozeHost.isAlwaysOnSuppressed());
pw.println(" aodPowerSaveActive=" + mDozeHost.isPowerSaveActive());
}
private void handleCarModeExited() {
mDozeLog.traceCarModeEnded();
mMachine.requestState(mConfig.alwaysOnEnabled(mUserTracker.getUserId())
? DozeMachine.State.DOZE_AOD : DozeMachine.State.DOZE);
}
private void handleCarModeStarted() {
mDozeLog.traceCarModeStarted();
mMachine.requestState(DozeMachine.State.DOZE_SUSPEND_TRIGGERS);
}
private final DozeHost.Callback mHostCallback = new DozeHost.Callback() {
@Override
public void onPowerSaveChanged(boolean active) {
// handles suppression changes, while DozeMachine#transitionPolicy handles gating
// transitions to DOZE_AOD
DozeMachine.State nextState = null;
if (mDozeHost.isPowerSaveActive()) {
nextState = DozeMachine.State.DOZE;
} else if (mMachine.getState() == DozeMachine.State.DOZE
&& mConfig.alwaysOnEnabled(mUserTracker.getUserId())) {
nextState = DozeMachine.State.DOZE_AOD;
}
if (nextState != null) {
mDozeLog.tracePowerSaveChanged(mDozeHost.isPowerSaveActive(), nextState);
mMachine.requestState(nextState);
}
}
@Override
public void onAlwaysOnSuppressedChanged(boolean suppressed) {
// handles suppression changes, while DozeMachine#transitionPolicy handles gating
// transitions to DOZE_AOD
final DozeMachine.State nextState;
if (mConfig.alwaysOnEnabled(mUserTracker.getUserId()) && !suppressed) {
nextState = DozeMachine.State.DOZE_AOD;
} else {
nextState = DozeMachine.State.DOZE;
}
mDozeLog.traceAlwaysOnSuppressedChange(suppressed, nextState);
mMachine.requestState(nextState);
}
};
}