diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 030c78b7f4b65..a32632736ee8f 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -589,6 +589,13 @@ public interface WindowManagerPolicy { * Notifies window manager that {@link #isKeyguardTrustedLw} has changed. */ void notifyKeyguardTrustedChanged(); + + /** + * Notifies the window manager that screen is being turned off. + * + * @param listener callback to call when display can be turned off + */ + void screenTurningOff(ScreenOffListener listener); } public interface PointerEventListener { @@ -1266,6 +1273,15 @@ public interface WindowManagerPolicy { */ public void screenTurnedOn(); + /** + * Called when the display would like to be turned off. This gives policy a chance to do some + * things before the display power state is actually changed to off. + * + * @param screenOffListener Must be called to tell that the display power state can actually be + * changed now after policy has done its work. + */ + public void screenTurningOff(ScreenOffListener screenOffListener); + /** * Called when the device has turned the screen off. */ @@ -1275,6 +1291,13 @@ public interface WindowManagerPolicy { void onScreenOn(); } + /** + * See {@link #screenTurnedOff} + */ + public interface ScreenOffListener { + void onScreenOff(); + } + /** * Return whether the default display is on and not blocked by a black surface. */ diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 0ef0561fe14fb..9dc317ad38ac3 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -71,6 +71,7 @@ import java.io.PrintWriter; final class DisplayPowerController implements AutomaticBrightnessController.Callbacks { private static final String TAG = "DisplayPowerController"; private static final String SCREEN_ON_BLOCKED_TRACE_NAME = "Screen on blocked"; + private static final String SCREEN_OFF_BLOCKED_TRACE_NAME = "Screen off blocked"; private static final boolean DEBUG = false; private static final boolean DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT = false; @@ -90,6 +91,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private static final int MSG_UPDATE_POWER_STATE = 1; private static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 2; private static final int MSG_SCREEN_ON_UNBLOCKED = 3; + private static final int MSG_SCREEN_OFF_UNBLOCKED = 4; private static final int PROXIMITY_UNKNOWN = -1; private static final int PROXIMITY_NEGATIVE = 0; @@ -105,6 +107,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private static final int REPORTED_TO_POLICY_SCREEN_OFF = 0; private static final int REPORTED_TO_POLICY_SCREEN_TURNING_ON = 1; private static final int REPORTED_TO_POLICY_SCREEN_ON = 2; + private static final int REPORTED_TO_POLICY_SCREEN_TURNING_OFF = 3; private final Object mLock = new Object(); @@ -219,6 +222,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // The currently active screen on unblocker. This field is non-null whenever // we are waiting for a callback to release it and unblock the screen. private ScreenOnUnblocker mPendingScreenOnUnblocker; + private ScreenOffUnblocker mPendingScreenOffUnblocker; // True if we were in the process of turning off the screen. // This allows us to recover more gracefully from situations where we abort @@ -230,6 +234,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // The elapsed real time when the screen on was blocked. private long mScreenOnBlockStartRealTime; + private long mScreenOffBlockStartRealTime; // Screen state we reported to policy. Must be one of REPORTED_TO_POLICY_SCREEN_* fields. private int mReportedScreenStateToPolicy; @@ -810,9 +815,43 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } + private void blockScreenOff() { + if (mPendingScreenOffUnblocker == null) { + Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_OFF_BLOCKED_TRACE_NAME, 0); + mPendingScreenOffUnblocker = new ScreenOffUnblocker(); + mScreenOffBlockStartRealTime = SystemClock.elapsedRealtime(); + Slog.i(TAG, "Blocking screen off"); + } + } + + private void unblockScreenOff() { + if (mPendingScreenOffUnblocker != null) { + mPendingScreenOffUnblocker = null; + long delay = SystemClock.elapsedRealtime() - mScreenOffBlockStartRealTime; + Slog.i(TAG, "Unblocked screen off after " + delay + " ms"); + Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, SCREEN_OFF_BLOCKED_TRACE_NAME, 0); + } + } + private boolean setScreenState(int state) { + final boolean isOff = (state == Display.STATE_OFF); if (mPowerState.getScreenState() != state) { - final boolean wasOn = (mPowerState.getScreenState() != Display.STATE_OFF); + + // If we are trying to turn screen off, give policy a chance to do something before we + // actually turn the screen off. + if (isOff && !mScreenOffBecauseOfProximity) { + if (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_ON) { + mReportedScreenStateToPolicy = REPORTED_TO_POLICY_SCREEN_TURNING_OFF; + blockScreenOff(); + mWindowManagerPolicy.screenTurningOff(mPendingScreenOffUnblocker); + return false; + } else if (mPendingScreenOffUnblocker != null) { + + // Abort doing the state change until screen off is unblocked. + return false; + } + } + mPowerState.setScreenState(state); // Tell battery stats about the transition. @@ -829,13 +868,21 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // This surface is essentially the final state of the color fade animation and // it is only removed once the window manager tells us that the activity has // finished drawing underneath. - final boolean isOff = (state == Display.STATE_OFF); if (isOff && mReportedScreenStateToPolicy != REPORTED_TO_POLICY_SCREEN_OFF && !mScreenOffBecauseOfProximity) { mReportedScreenStateToPolicy = REPORTED_TO_POLICY_SCREEN_OFF; unblockScreenOn(); mWindowManagerPolicy.screenTurnedOff(); - } else if (!isOff && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_OFF) { + } else if (!isOff + && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_TURNING_OFF) { + + // We told policy already that screen was turning off, but now we changed our minds. + // Complete the full state transition on -> turningOff -> off. + unblockScreenOff(); + mWindowManagerPolicy.screenTurnedOff(); + mReportedScreenStateToPolicy = REPORTED_TO_POLICY_SCREEN_OFF; + } + if (!isOff && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_OFF) { mReportedScreenStateToPolicy = REPORTED_TO_POLICY_SCREEN_TURNING_ON; if (mPowerState.getColorFadeLevel() == 0.0f) { blockScreenOn(); @@ -1282,6 +1329,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call updatePowerState(); } break; + case MSG_SCREEN_OFF_UNBLOCKED: + if (mPendingScreenOffUnblocker == msg.obj) { + unblockScreenOff(); + updatePowerState(); + } + break; } } } @@ -1311,4 +1364,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mHandler.sendMessage(msg); } } + + private final class ScreenOffUnblocker implements WindowManagerPolicy.ScreenOffListener { + + @Override + public void onScreenOff() { + Message msg = mHandler.obtainMessage(MSG_SCREEN_OFF_UNBLOCKED, this); + msg.setAsynchronous(true); + mHandler.sendMessage(msg); + } + } } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 6f5c070cf7300..d93934599dc88 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -6667,6 +6667,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { reportScreenStateToVrManager(true); } + @Override + public void screenTurningOff(ScreenOffListener screenOffListener) { + mWindowManagerFuncs.screenTurningOff(screenOffListener); + } + private void reportScreenStateToVrManager(boolean isScreenOn) { if (mVrManagerInternal == null) { return; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 49416109ebd1b..221e7957ef352 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -2903,6 +2903,11 @@ class DisplayContent extends WindowContainer { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 27661e2ef0450..818df018d2fdd 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -41,6 +41,7 @@ import android.view.Surface; import com.android.internal.annotations.VisibleForTesting; import java.io.PrintWriter; +import java.util.function.Consumer; class Task extends WindowContainer implements DimLayer.DimLayerUser { static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_WM; @@ -683,6 +684,11 @@ class Task extends WindowContainer implements DimLayer.DimLayerU return (TaskWindowContainerController) super.getController(); } + @Override + void forAllTasks(Consumer callback) { + callback.accept(this); + } + @Override public String toString() { return "{taskId=" + mTaskId + " appTokens=" + mChildren + " mdr=" + mDeferRemoval + "}"; diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index 1bbe1d0c9a93c..24cb464b59934 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -30,8 +30,10 @@ import android.graphics.Canvas; import android.graphics.GraphicBuffer; import android.graphics.Rect; import android.os.Environment; +import android.os.Handler; import android.util.ArraySet; import android.view.WindowManager.LayoutParams; +import android.view.WindowManagerPolicy.ScreenOffListener; import android.view.WindowManagerPolicy.StartingSurface; import com.google.android.collect.Sets; @@ -40,6 +42,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.server.wm.TaskSnapshotSurface.SystemBarBackgroundPainter; import java.io.PrintWriter; +import java.util.function.Consumer; /** * When an app token becomes invisible, we take a snapshot (bitmap) of the corresponding task and @@ -83,6 +86,7 @@ class TaskSnapshotController { Environment::getDataSystemCeDirectory); private final TaskSnapshotLoader mLoader = new TaskSnapshotLoader(mPersister); private final ArraySet mTmpTasks = new ArraySet<>(); + private final Handler mHandler = new Handler(); TaskSnapshotController(WindowManagerService service) { mService = service; @@ -114,8 +118,13 @@ class TaskSnapshotController { // We need to take a snapshot of the task if and only if all activities of the task are // either closing or hidden. getClosingTasks(closingApps, mTmpTasks); - for (int i = mTmpTasks.size() - 1; i >= 0; i--) { - final Task task = mTmpTasks.valueAt(i); + snapshotTasks(mTmpTasks); + + } + + private void snapshotTasks(ArraySet tasks) { + for (int i = tasks.size() - 1; i >= 0; i--) { + final Task task = tasks.valueAt(i); final int mode = getSnapshotMode(task); final TaskSnapshot snapshot; switch (mode) { @@ -284,6 +293,33 @@ class TaskSnapshotController { mPersister.setPaused(paused); } + /** + * Called when screen is being turned off. + */ + void screenTurningOff(ScreenOffListener listener) { + if (!ENABLE_TASK_SNAPSHOTS || ActivityManager.isLowRamDeviceStatic()) { + listener.onScreenOff(); + return; + } + + // We can't take a snapshot when screen is off, so take a snapshot now! + mHandler.post(() -> { + try { + synchronized (mService.mWindowMap) { + mTmpTasks.clear(); + mService.mRoot.forAllTasks(task -> { + if (task.isVisible()) { + mTmpTasks.add(task); + } + }); + snapshotTasks(mTmpTasks); + } + } finally { + listener.onScreenOff(); + } + }); + } + void dump(PrintWriter pw, String prefix) { mCache.dump(pw, prefix); } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 6d3e0ed18818a..600bc5c47062d 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -627,6 +627,17 @@ class WindowContainer implements Comparable callback) { + for (int i = mChildren.size() - 1; i >= 0; --i) { + mChildren.get(i).forAllTasks(callback); + } + } + WindowState getWindow(Predicate callback) { for (int i = mChildren.size() - 1; i >= 0; --i) { final WindowState w = mChildren.get(i).getWindow(callback); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index c8eb98c90da20..a2ae430bacecc 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -203,6 +203,7 @@ import android.view.WindowManagerGlobal; import android.view.WindowManagerInternal; import android.view.WindowManagerPolicy; import android.view.WindowManagerPolicy.PointerEventListener; +import android.view.WindowManagerPolicy.ScreenOffListener; import android.view.animation.Animation; import android.view.inputmethod.InputMethodManagerInternal; @@ -2810,6 +2811,11 @@ public class WindowManagerService extends IWindowManager.Stub mH.sendEmptyMessage(H.NOTIFY_KEYGUARD_TRUSTED_CHANGED); } + @Override + public void screenTurningOff(ScreenOffListener listener) { + mTaskSnapshotController.screenTurningOff(listener); + } + /** * Starts deferring layout passes. Useful when doing multiple changes but to optimize * performance, only one layout pass should be done. This can be called multiple times, and diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java index be53667084d74..623d77ba57ed5 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -365,6 +365,11 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } + @Override + public void screenTurningOff(ScreenOffListener screenOffListener) { + + } + @Override public void screenTurnedOff() {