diff options
9 files changed, 547 insertions, 1295 deletions
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 40898d02b97a..9bec409ea8a1 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -629,114 +629,6 @@ public interface WindowManager extends ViewManager { @interface DisplayImePolicy {} /** - * The root state of all the state. This is an abstract state, and the keyguard should be in - * one of this sub state. - * - * @hide - */ - int KEYGUARD_STATE_ROOT = 0x0; - - /** - * Keyguard is off, so activity can be shown on the screen. - * - * @hide - */ - int KEYGUARD_STATE_OFF = 0x1; - - /** - * The keyguard is off, but lock screen is still rendered on the screen. Waiting for - * starting unlock animation. - * - * @hide - */ - int KEYGUARD_STATE_GOING_AWAY = 0x11; - - /** - * They keyguard is on, so normal activities cannot be shown on the screen. This is an abstract - * state, and the keyguard should be in one of ths sub state. - * - * @hide - */ - int KEYGUARD_STATE_ON = 0x2; - - /** - * The keyguard is on and not occluded. - * @hide - */ - int KEYGUARD_STATE_KEYGUARD_TOP = 0x21; - - /** - * The keyguard is on, and the lock screen is shown. - * - * @hide - */ - int KEYGUARD_STATE_LOCKSCREEN_SHOWN = 0x211; - - /** - * The keyguard is on, and the AOD is shown. - * - * @hide - */ - int KEYGUARD_STATE_AOD_SHOWN = 0x212; - - /** - * The keyguard is on but it's occluded by a normal SHOW_WHEN_LOCKED activity (i.e. non - * occluded by Dream activity). - * - * @hide - */ - int KEYGUARD_STATE_OCCLUDED = 0x22; - - /** - * The keyguard is on but it's occluded by a Dream activity. - * - * @hide - */ - int KEYGUARD_STATE_DREAMING = 0x221; - - /** @hide */ - @IntDef(prefix = { "KEYGUARD_STATE_" }, value = { - KEYGUARD_STATE_ROOT, - KEYGUARD_STATE_OFF, - KEYGUARD_STATE_GOING_AWAY, - KEYGUARD_STATE_ON, - KEYGUARD_STATE_KEYGUARD_TOP, - KEYGUARD_STATE_LOCKSCREEN_SHOWN, - KEYGUARD_STATE_AOD_SHOWN, - KEYGUARD_STATE_OCCLUDED, - KEYGUARD_STATE_DREAMING, - }) - @interface KeyguardState {} - - /** - * @hide - */ - static String keyguardStateToString(@KeyguardState int type) { - switch (type) { - case KEYGUARD_STATE_ROOT: - return "ROOT"; - case KEYGUARD_STATE_OFF: - return "KEYGUARD_OFF"; - case KEYGUARD_STATE_GOING_AWAY: - return "KEYGUARD_GOING_AWAY"; - case KEYGUARD_STATE_ON: - return "KEYGUARD_ON"; - case KEYGUARD_STATE_KEYGUARD_TOP: - return "KEYGUARD_TOP"; - case KEYGUARD_STATE_LOCKSCREEN_SHOWN: - return "KEYGUARD_LOCKSCREEN_SHOWN"; - case KEYGUARD_STATE_AOD_SHOWN: - return "KEYGUARD_AOD_SHOWN"; - case KEYGUARD_STATE_OCCLUDED: - return "KEYGUARD_OCCLUDED"; - case KEYGUARD_STATE_DREAMING: - return "KEYGUARD_DREAMING"; - default: - return "KEYGUARD_STATE_UNKNOWN(" + Integer.toHexString(type) + ")"; - } - } - - /** * Exception that is thrown when trying to add view whose * {@link LayoutParams} {@link LayoutParams#token} * is invalid. diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index b112e7a366bf..82e1777ab7cb 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -117,7 +117,7 @@ message KeyguardControllerProto { repeated KeyguardOccludedProto keyguard_occluded_states = 2 [deprecated=true]; optional bool aod_showing = 3; repeated KeyguardPerDisplayProto keyguard_per_display = 4; - optional bool keyguard_going_away = 5 [deprecated=true]; + optional bool keyguard_going_away = 5; } message KeyguardOccludedProto { @@ -134,7 +134,7 @@ message KeyguardPerDisplayProto { optional bool keyguard_showing = 2; optional bool aod_showing = 3; optional bool keyguard_occluded = 4; - optional bool keyguard_going_away = 5 [deprecated=true]; + optional bool keyguard_going_away = 5; } /* represents PhoneWindowManager */ diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index e0bcc0e3e883..4e9cd2e6337d 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -3770,6 +3770,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { private boolean setKeyguardOccludedLw(boolean isOccluded, boolean notify) { if (DEBUG_KEYGUARD) Slog.d(TAG, "setKeyguardOccluded occluded=" + isOccluded); mKeyguardOccludedChanged = false; + if (isKeyguardOccluded() == isOccluded) { + return false; + } mKeyguardDelegate.setOccluded(isOccluded, notify); return mKeyguardDelegate.isShowing(); } diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index 718a9e3f0ffd..e9badefe1bb1 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -20,63 +20,47 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; -import static android.view.WindowManager.KEYGUARD_STATE_AOD_SHOWN; -import static android.view.WindowManager.KEYGUARD_STATE_DREAMING; -import static android.view.WindowManager.KEYGUARD_STATE_GOING_AWAY; -import static android.view.WindowManager.KEYGUARD_STATE_KEYGUARD_TOP; -import static android.view.WindowManager.KEYGUARD_STATE_LOCKSCREEN_SHOWN; -import static android.view.WindowManager.KEYGUARD_STATE_OCCLUDED; -import static android.view.WindowManager.KEYGUARD_STATE_OFF; -import static android.view.WindowManager.KEYGUARD_STATE_ON; -import static android.view.WindowManager.KEYGUARD_STATE_ROOT; -import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; -import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED; import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE; import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; -import static android.view.WindowManager.TRANSIT_TO_FRONT; -import static android.view.WindowManager.TRANSIT_WAKE; -import static android.view.WindowManager.keyguardStateToString; -import static android.view.WindowManager.transitTypeToString; -import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD; - +import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS; +import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS; +import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT; +import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE; +import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.KeyguardControllerProto.AOD_SHOWING; +import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_GOING_AWAY; import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_PER_DISPLAY; import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_SHOWING; -import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.ActivityManager; -import android.app.ActivityOptions; -import android.os.Debug; import android.os.IBinder; import android.os.RemoteException; import android.os.Trace; import android.util.Slog; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; +import android.view.Display; import android.view.WindowManager; -import android.view.WindowManager.KeyguardState; -import android.window.TransitionInfo; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.policy.WindowManagerPolicy; -import com.android.server.wm.utils.StateMachine; import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; /** * Controls Keyguard occluding, dismissing and transitions depending on what kind of activities are @@ -85,285 +69,171 @@ import java.util.Optional; * Note that everything in this class should only be accessed with the AM lock being held. */ class KeyguardController { - private static final String TAG = TAG_WITH_CLASS_NAME ? "KeyguardController" : TAG_ATM; - private static final boolean DEBUG = true; + private static final String TAG = TAG_WITH_CLASS_NAME ? "KeyguardController" : TAG_ATM; static final String KEYGUARD_SLEEP_TOKEN_TAG = "keyguard"; private static final int DEFER_WAKE_TRANSITION_TIMEOUT_MS = 5000; - private final ActivityTaskManagerService mService; private final ActivityTaskSupervisor mTaskSupervisor; - private final ActivityTaskManagerInternal.SleepTokenAcquirer mSleepTokenAcquirer; - private boolean mWaitingForWakeTransition; private WindowManagerService mWindowManager; + private final SparseArray<KeyguardDisplayState> mDisplayStates = new SparseArray<>(); + private final ActivityTaskManagerService mService; private RootWindowContainer mRootWindowContainer; - - private final SparseArray<DisplayState> mDisplayStates = new SparseArray<>(); - - @NonNull private final ServiceDelegate mServiceDelegate = new ServiceDelegate(); + private final ActivityTaskManagerInternal.SleepTokenAcquirer mSleepTokenAcquirer; + private boolean mWaitingForWakeTransition; KeyguardController(ActivityTaskManagerService service, ActivityTaskSupervisor taskSupervisor) { mService = service; mTaskSupervisor = taskSupervisor; - mSleepTokenAcquirer = service.new SleepTokenAcquirerImpl(KEYGUARD_SLEEP_TOKEN_TAG); + mSleepTokenAcquirer = mService.new SleepTokenAcquirerImpl(KEYGUARD_SLEEP_TOKEN_TAG); } void setWindowManager(WindowManagerService windowManager) { mWindowManager = windowManager; mRootWindowContainer = mService.mRootWindowContainer; - mService.getTransitionController().registerLegacyListener( - new WindowManagerInternal.AppTransitionListener() { - @Override - public int onAppTransitionStartingLocked(TransitionInfo info) { - final List<TransitionInfo.Change> changes = info.getChanges(); - if (changes.size() == 0) { - Slog.e(TAG, "TransitionInfo doesn't contain change: " + info); - return 0; - } - final ActivityManager.RunningTaskInfo taskInfo = - changes.get(0).getTaskInfo(); - if (taskInfo == null) { - Slog.e(TAG, "No RunningTaskInfo: " + info); - return 0; - } - - // TODO(b/242856311): Filtering condition is defined here and in SysUI - // Keyguard service, which need to be in sync. For a long term, we should - // define a new API for notifying occlude status from WMS to SysUI, and - // the filtering logic should only exist in WM Shell. - if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) == 0) { - return 0; - } - - boolean occludeOpeningApp = false; - boolean occludeClosingApp = false; - for (int i = 0; i < changes.size(); ++i) { - final TransitionInfo.Change change = changes.get(i); - if (change.hasFlags(FLAG_OCCLUDES_KEYGUARD)) { - if (change.getMode() == TRANSIT_OPEN - || change.getMode() == TRANSIT_TO_FRONT) { - occludeOpeningApp = true; - } - if (change.getMode() == TRANSIT_CLOSE - || change.getMode() == TRANSIT_TO_BACK) { - occludeClosingApp = true; - } - } - } - final DisplayState state = getDisplayState(taskInfo.displayId); - if (occludeOpeningApp && !occludeClosingApp) { - state.commitOccludedStatus(true /* occluded */); - } else if (!occludeOpeningApp && occludeClosingApp) { - state.commitOccludedStatus(false /* occluded */); - } - return 0; - } - }); } boolean isAodShowing(int displayId) { - return getDisplayState(displayId).isIn(KEYGUARD_STATE_AOD_SHOWN); + return getDisplayState(displayId).mAodShowing; } /** - * @return {@code true} if either Keyguard or AOD are showing. + * @return true if either Keyguard or AOD are showing, not going away, and not being occluded + * on the given display, false otherwise. */ boolean isKeyguardOrAodShowing(int displayId) { - return getDisplayState(displayId).isIn(KEYGUARD_STATE_KEYGUARD_TOP); + final KeyguardDisplayState state = getDisplayState(displayId); + return (state.mKeyguardShowing || state.mAodShowing) + && !state.mKeyguardGoingAway + && !isDisplayOccluded(displayId); } /** - * @return {@codd true} if lock screen is showing. + * @return {@code true} for default display when AOD is showing, not going away. Otherwise, same + * as {@link #isKeyguardOrAodShowing(int)} + * TODO(b/125198167): Replace isKeyguardOrAodShowing() by this logic. */ - boolean isLocksScreenShowing(int displayId) { - // NOTE: This is only used by WindowManagerService#notifyKeyguardTrustedChanged - return getDisplayState(displayId).isIn(KEYGUARD_STATE_LOCKSCREEN_SHOWN); + boolean isKeyguardUnoccludedOrAodShowing(int displayId) { + final KeyguardDisplayState state = getDisplayState(displayId); + if (displayId == DEFAULT_DISPLAY && state.mAodShowing) { + return !state.mKeyguardGoingAway; + } + return isKeyguardOrAodShowing(displayId); } /** - * @return {@code true} if Keyguard is either showing or occluded. + * @return true if Keyguard is showing, not going away, and not being occluded on the given + * display, false otherwise + */ + boolean isKeyguardShowing(int displayId) { + final KeyguardDisplayState state = getDisplayState(displayId); + return state.mKeyguardShowing && !state.mKeyguardGoingAway + && !isDisplayOccluded(displayId); + } + + /** + * @return true if Keyguard is either showing or occluded, but not going away */ boolean isKeyguardLocked(int displayId) { - return getDisplayState(displayId).isIn(KEYGUARD_STATE_ON); + final KeyguardDisplayState state = getDisplayState(displayId); + return state.mKeyguardShowing && !state.mKeyguardGoingAway; } /** + * * @return true if the activity is controlling keyguard state. */ boolean topActivityOccludesKeyguard(ActivityRecord r) { - return getDisplayState(r.getDisplayId()).topActivityOccludesKeyguard(r); + return getDisplayState(r.getDisplayId()).mTopOccludesActivity == r; } /** * @return {@code true} if the keyguard is going away, {@code false} otherwise. */ boolean isKeyguardGoingAway(int displayId) { - return getDisplayState(displayId).isIn(KEYGUARD_STATE_GOING_AWAY); + final KeyguardDisplayState state = getDisplayState(displayId); + // Also check keyguard showing in case value is stale. + return state.mKeyguardGoingAway && state.mKeyguardShowing; } /** - * Checks whether the top activity occludes the keyguard. - */ - boolean isDisplayOccluded(int displayId) { - return getDisplayState(displayId).isIn(KEYGUARD_STATE_OCCLUDED); - } - - /** - * @return Whether the dream activity is on top of default display. - */ - boolean isShowingDream() { - return getDisplayState(DEFAULT_DISPLAY).isIn(KEYGUARD_STATE_DREAMING); - } - - /** - * Checks whether {@param r} should be visible depending on Keyguard state. - * - * @return true if {@param r} is visible taken Keyguard state into account, false otherwise + * Update the Keyguard showing state. */ - boolean checkKeyguardVisibility(@NonNull ActivityRecord r) { - if (r.mDisplayContent.canShowWithInsecureKeyguard() && canDismissKeyguard()) { - return true; - } - return getDisplayState(r.mDisplayContent.getDisplayId()).checkKeyguardVisibility(r); - } - - void onDisplayRemoved(int displayId) { - final DisplayState state = mDisplayStates.get(displayId); - if (state != null) { - state.onRemoved(); - mDisplayStates.remove(displayId); + void setKeyguardShown(int displayId, boolean keyguardShowing, boolean aodShowing) { + if (mRootWindowContainer.getDisplayContent(displayId).isKeyguardAlwaysUnlocked()) { + Slog.i(TAG, "setKeyguardShown ignoring always unlocked display " + displayId); + return; } - } - private final Runnable mResetWaitTransition = () -> { - synchronized (mWindowManager.mGlobalLock) { + final KeyguardDisplayState state = getDisplayState(displayId); + final boolean aodChanged = aodShowing != state.mAodShowing; + final boolean aodRemoved = state.mAodShowing && !aodShowing; + // If keyguard is going away, but SystemUI aborted the transition, need to reset state. + // Do not reset keyguardChanged status when only AOD is removed. + final boolean keyguardChanged = (keyguardShowing != state.mKeyguardShowing) + || (state.mKeyguardGoingAway && keyguardShowing && !aodRemoved); + if (aodRemoved) { updateDeferTransitionForAod(false /* waiting */); } - }; - - /** - * Update if app transition should be deferred until AOD state changes. - * - * <p>Note: This is used for defer app transition before the device fully wakes up, since during - * wake up process, activities life cycle can be messed up due to a display sleep token. - * - * @param waiting {@code true} to defer an app transition, {@code false} to continue an app - * transition. - */ - void updateDeferTransitionForAod(boolean waiting) { - if (waiting == mWaitingForWakeTransition) { - return; - } - if (!mService.getTransitionController().isCollecting()) { + if (!keyguardChanged && !aodChanged) { + setWakeTransitionReady(); return; } - // if AOD is showing, defer the wake transition until AOD state changed. - if (waiting && isAodShowing(DEFAULT_DISPLAY)) { - mWaitingForWakeTransition = true; - mService.getTransitionController().deferTransitionReady(); - mWindowManager.mH.postDelayed(mResetWaitTransition, DEFER_WAKE_TRANSITION_TIMEOUT_MS); - } else if (!waiting) { - // dismiss the deferring if the AOD state change or cancel awake. - mWaitingForWakeTransition = false; - mService.getTransitionController().continueTransitionReady(); - mWindowManager.mH.removeCallbacks(mResetWaitTransition); - } - } - - /** - * TODO(b/242851358): Remove this function once SysUI migrate to the new API. - */ - @KeyguardState private static int convertToState(int displayId, boolean keyguardShowing, - boolean aodShowing) { - if (displayId == DEFAULT_DISPLAY) { - if (aodShowing) { - return KEYGUARD_STATE_AOD_SHOWN; - } else if (keyguardShowing) { - return KEYGUARD_STATE_LOCKSCREEN_SHOWN; - } else { - return KEYGUARD_STATE_OFF; - } - } else { - if (keyguardShowing || aodShowing) { - return KEYGUARD_STATE_LOCKSCREEN_SHOWN; - } else { - return KEYGUARD_STATE_OFF; - } - } - } - - /** - * Update the Keyguard showing state. - * - * @deprecated Use {@link #setKeyguardState(int, int)} instead. See b/242851358 - */ - @Deprecated - void setKeyguardShown(int displayId, boolean keyguardShowing, boolean aodShowing) { - final DisplayState state = getDisplayState(displayId); EventLogTags.writeWmSetKeyguardShown( displayId, keyguardShowing ? 1 : 0, aodShowing ? 1 : 0, - state.isIn(KEYGUARD_STATE_GOING_AWAY) ? 1 : 0, + state.mKeyguardGoingAway ? 1 : 0, "setKeyguardShown"); - setKeyguardState(displayId, convertToState(displayId, keyguardShowing, aodShowing)); - } - /** - * Set keyguard state. - */ - private void setKeyguardState(int displayId, @KeyguardState int newState) { - if (mRootWindowContainer.getDisplayContent(displayId).isKeyguardAlwaysUnlocked()) { - Slog.i(TAG, "setKeyguardShown ignoring always unlocked display " + displayId); - return; - } - if (newState != KEYGUARD_STATE_LOCKSCREEN_SHOWN - && newState != KEYGUARD_STATE_AOD_SHOWN - && newState != KEYGUARD_STATE_OFF - && newState != KEYGUARD_STATE_GOING_AWAY) { - Slog.i(TAG, "Invalid state is requested: displayId=" + displayId - + ", state=" + keyguardStateToString(newState) - + ", stack=" + Debug.getCallers(30)); - return; - } - if (isKeyguardLocked(displayId) && newState == KEYGUARD_STATE_OFF) { - newState = KEYGUARD_STATE_GOING_AWAY; + // Update the task snapshot if the screen will not be turned off. To make sure that the + // unlocking animation can animate consistent content. The conditions are: + // - Either AOD or keyguard changes to be showing. So if the states change individually, + // the later one can be skipped to avoid taking snapshot again. While it still accepts + // if both of them change to show at the same time. + // - Keyguard was not going away. Because if it was, the closing transition is able to + // handle the snapshot. + // - The display state is ON. Because if AOD is not on or pulsing, the display state will + // be OFF or DOZE (the path of screen off may have handled it). + if (((aodShowing ^ keyguardShowing) || (aodShowing && aodChanged && keyguardChanged)) + && !state.mKeyguardGoingAway && Display.isOnState( + mRootWindowContainer.getDefaultDisplay().getDisplayInfo().state)) { + mWindowManager.mTaskSnapshotController.snapshotForSleeping(DEFAULT_DISPLAY); + } + + state.mKeyguardShowing = keyguardShowing; + state.mAodShowing = aodShowing; + + if (keyguardChanged) { + // Irrelevant to AOD. + state.mKeyguardGoingAway = false; + if (keyguardShowing) { + state.mDismissalRequested = false; + } } - final DisplayState state = getDisplayState(displayId); - // SysUI requests to show LOCKSCREEN, but the keyguard is already occluded. Ignore the - // requests. - if (state.isIn(KEYGUARD_STATE_OCCLUDED) - && StateMachine.isIn(newState, KEYGUARD_STATE_LOCKSCREEN_SHOWN)) { - Slog.w(TAG, "Ignore setKeyguardState request: OCCLUDE -> LOCK_SCREEN_SHOWN"); - return; - } - // SysUI requests to show AOD_SHOWN again. This can happen when SysUI still uses the old - // API and enables AOD first, then lock screen, i.e. #setLockScreenShown(false, true), then - // #setLockScreenShown(true, true) - if (state.isIn(KEYGUARD_STATE_AOD_SHOWN) - && StateMachine.isIn(newState, KEYGUARD_STATE_AOD_SHOWN)) { - Slog.w(TAG, "Ignore setKeyguardState request: AOD_SHOWN -> AOD_SHOWN"); - return; + // Update the sleep token first such that ensureActivitiesVisible has correct sleep token + // state when evaluating visibilities. + updateKeyguardSleepToken(); + mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); + InputMethodManagerInternal.get().updateImeWindowStatus(false /* disableImeIcon */); + setWakeTransitionReady(); + if (aodChanged) { + // Ensure the new state takes effect. + mWindowManager.mWindowPlacerLocked.performSurfacePlacement(); } - if (state.isIn(KEYGUARD_STATE_OFF) - && StateMachine.isIn(newState, KEYGUARD_STATE_GOING_AWAY)) { - Slog.w(TAG, "Ignore setKeyguardState request: OFF -> GOING_AWAY"); - return; - } - if (state.isIn(KEYGUARD_STATE_AOD_SHOWN) - && StateMachine.isIn(newState, KEYGUARD_STATE_LOCKSCREEN_SHOWN)) { - ActivityRecord top = getTopNonFinishingActivity(displayId); - if (canOcclude(top)) { - newState = isTopActivityDreaming(displayId) ? KEYGUARD_STATE_DREAMING - : KEYGUARD_STATE_OCCLUDED; - } + } + + private void setWakeTransitionReady() { + if (mWindowManager.mAtmService.getTransitionController().getCollectingTransitionType() + == WindowManager.TRANSIT_WAKE) { + mWindowManager.mAtmService.getTransitionController().setReady( + mRootWindowContainer.getDefaultDisplay()); } - state.setKeyguardState(newState); } /** @@ -371,1008 +241,520 @@ class KeyguardController { * * @param flags See {@link WindowManagerPolicy#KEYGUARD_GOING_AWAY_FLAG_TO_SHADE} * etc. - * - * @deprecated Use {@link #setKeyguardState(int, int)} */ void keyguardGoingAway(int displayId, int flags) { - // TODO(b/242851358): Remove IActivityTaskManagerService#keyguardGoingAway and SysUI should - // request the state change via #setKeyguardState. - final DisplayState state = getDisplayState(displayId); - EventLogTags.writeWmSetKeyguardShown( - displayId, - state.isIn(KEYGUARD_STATE_LOCKSCREEN_SHOWN) ? 1 : 0, - state.isIn(KEYGUARD_STATE_AOD_SHOWN) ? 1 : 0, - 1 /* keyguardGoingAway */, - "keyguardGoingAway"); - setKeyguardState(displayId, KEYGUARD_STATE_GOING_AWAY); - } - - /** - * Makes sure to update lockscreen state if needed before completing set all visibility - * ({@link ActivityTaskSupervisor#beginActivityVisibilityUpdate}). - */ - void updateVisibility() { - for (int displayNdx = mRootWindowContainer.getChildCount() - 1; - displayNdx >= 0; displayNdx--) { - final DisplayContent display = mRootWindowContainer.getChildAt(displayNdx); - if (display.isRemoving() || display.isRemoved()) continue; - final DisplayState state = getDisplayState(display.mDisplayId); - state.updateVisibility(); + final KeyguardDisplayState state = getDisplayState(displayId); + if (!state.mKeyguardShowing || state.mKeyguardGoingAway) { + return; + } + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "keyguardGoingAway"); + mService.deferWindowLayout(); + state.mKeyguardGoingAway = true; + try { + EventLogTags.writeWmSetKeyguardShown( + displayId, + 1 /* keyguardShowing */, + state.mAodShowing ? 1 : 0, + 1 /* keyguardGoingAway */, + "keyguardGoingAway"); + final int transitFlags = convertTransitFlags(flags); + final DisplayContent dc = mRootWindowContainer.getDefaultDisplay(); + dc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, transitFlags); + // We are deprecating TRANSIT_KEYGUARD_GOING_AWAY for Shell transition and use + // TRANSIT_FLAG_KEYGUARD_GOING_AWAY to indicate that it should animate keyguard going + // away. + dc.mAtmService.getTransitionController().requestTransitionIfNeeded( + TRANSIT_TO_BACK, transitFlags, null /* trigger */, dc); + updateKeyguardSleepToken(); + + // Some stack visibility might change (e.g. docked stack) + mRootWindowContainer.resumeFocusedTasksTopActivities(); + mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); + mRootWindowContainer.addStartingWindowsForVisibleActivities(); + mWindowManager.executeAppTransition(); + } finally { + mService.continueWindowLayout(); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } } void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback, CharSequence message) { - boolean ok; - final ActivityRecord r = ActivityRecord.forTokenLocked(token); - if (r == null) { - ok = false; - } else { - final DisplayState state = getDisplayState(r.getDisplayId()); - ok = state.dismissKeyguard(r, callback, message); + final ActivityRecord activityRecord = ActivityRecord.forTokenLocked(token); + if (activityRecord == null || !activityRecord.visibleIgnoringKeyguard) { + failCallback(callback); + return; } + Slog.i(TAG, "Activity requesting to dismiss Keyguard: " + activityRecord); - if (!ok) { - try { - callback.onDismissError(); - } catch (RemoteException e) { - Slog.w(TAG, "Failed to call callback", e); - } + // If the client has requested to dismiss the keyguard and the Activity has the flag to + // turn the screen on, wakeup the screen if it's the top Activity. + if (activityRecord.getTurnScreenOnFlag() && activityRecord.isTopRunningActivity()) { + mTaskSupervisor.wakeUp("dismissKeyguard"); } - } - private boolean isKeyguardSecure() { - return mWindowManager.isKeyguardSecure(mService.getCurrentUserId()); + mWindowManager.dismissKeyguard(callback, message); } - /** - * @return true if Keyguard can be currently dismissed without entering credentials. - */ - private boolean canDismissKeyguard() { - return mWindowManager.mPolicy.isKeyguardTrustedLw() || !isKeyguardSecure(); + private void failCallback(IKeyguardDismissCallback callback) { + try { + callback.onDismissError(); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to call callback", e); + } } - private boolean canOcclude(@Nullable ActivityRecord r) { - if (r == null) { - return false; + private int convertTransitFlags(int keyguardGoingAwayFlags) { + int result = TRANSIT_FLAG_KEYGUARD_GOING_AWAY; + if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_TO_SHADE) != 0) { + result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; } - // FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD only apply for secondary display. - if (r.getDisplayId() != DEFAULT_DISPLAY) { - final DisplayContent dc = r.getDisplayContent(); - if (dc != null && dc.canShowWithInsecureKeyguard() && canDismissKeyguard()) { - return true; - } + if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS) != 0) { + result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; } - // FLAG_DISMISS_KEYGUARD activity - // Insecure: Treat as FLAG_SHOW_WHEN_LOCKED - // Trusted: Actually dismiss Keyguard. - // Secure: Show bouncer. - return r.canShowWhenLocked() || (r.containsDismissKeyguardWindow() && !isKeyguardSecure()); - } - - private boolean isTopActivityDreaming(int displayId) { - final DisplayContent dc = mRootWindowContainer.getDisplayContent(displayId); - final ActivityRecord top = getTopNonFinishingActivity(displayId); - return dc.getDisplayPolicy().isShowingDreamLw() - && top != null && top.getActivityType() == ACTIVITY_TYPE_DREAM; - } - - @Nullable private ActivityRecord getTopNonFinishingActivity(int displayId) { - final DisplayContent dc = mRootWindowContainer.getDisplayContent(displayId); - final Task rootTask = dc == null ? null : dc.getRootTask(t -> - t != null && t.isFocusableAndVisible() && !t.inPinnedWindowingMode()); - return rootTask != null ? rootTask.getTopNonFinishingActivity() : null; - } - - private DisplayState getDisplayState(int displayId) { - DisplayState state = mDisplayStates.get(displayId); - if (state == null) { - state = new DisplayState(displayId, mServiceDelegate); - mDisplayStates.append(displayId, state); + if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER) != 0) { + result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; } - return state; - } - - void dumpDebug(ProtoOutputStream proto, long fieldId) { - final DisplayState default_state = getDisplayState(DEFAULT_DISPLAY); - final long token = proto.start(fieldId); - proto.write(AOD_SHOWING, default_state.isIn(KEYGUARD_STATE_AOD_SHOWN)); - proto.write(KEYGUARD_SHOWING, default_state.isIn(KEYGUARD_STATE_LOCKSCREEN_SHOWN)); - writeDisplayStatesToProto(proto, KEYGUARD_PER_DISPLAY); - proto.end(token); - } - - private void writeDisplayStatesToProto(ProtoOutputStream proto, long fieldId) { - for (int i = 0; i < mDisplayStates.size(); i++) { - mDisplayStates.valueAt(i).dumpDebug(proto, fieldId); + if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS) != 0) { + result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION; } - } - - void dump(PrintWriter pw, String prefix) { - pw.print(prefix); - pw.println("KeyguardController:"); - for (int i = 0; i < mDisplayStates.size(); i++) { - mDisplayStates.valueAt(i).dump(pw, prefix); + if ((keyguardGoingAwayFlags + & KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT) != 0) { + result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT; } - pw.println(); + return result; } /** - * Interface for {@link DisplayState} to access non-local information. - * <p> - * Keep this interface as small as possible, and don't let {@link DisplayState} access arbitrary - * large classes such as ActivityTaskSupervisor, which makes managing dependency complicated. + * @return True if we may show an activity while Keyguard is showing because we are in the + * process of dismissing it anyways, false otherwise. */ - private final class ServiceDelegate { - boolean isKeyguardSecure() { - return KeyguardController.this.isKeyguardSecure(); - } - - boolean canOcclude(@Nullable ActivityRecord r) { - return KeyguardController.this.canOcclude(r); - } - - boolean canDismissKeyguard() { - return KeyguardController.this.canDismissKeyguard(); - } - - boolean isDeviceInteractive() { - return mService.mWindowManager.mPowerManager.isInteractive(); - } - - void dismissKeyguard(IKeyguardDismissCallback callback, CharSequence message) { - mWindowManager.dismissKeyguard(callback, message); - } - - @Nullable - ActivityRecord getTopNonFinishingActivity(int displayId) { - return KeyguardController.this.getTopNonFinishingActivity(displayId); - } - - boolean isTopActivityDreaming(int displayId) { - return KeyguardController.this.isTopActivityDreaming(displayId); - } - - void wakeUp(String reason) { - mTaskSupervisor.wakeUp(reason); - } - - void forceSyncOccludedStatus(boolean occluded) { - if (DEBUG) { - Slog.d(TAG, "forceSyncOccludedStatus: occluded=" + occluded); - } - mWindowManager.mPolicy.onKeyguardOccludedChangedLw(occluded); - mWindowManager.mPolicy.applyKeyguardOcclusionChange(true /* notify */); - } - - void snapshotForSleeping(int displayId) { - if (displayId == DEFAULT_DISPLAY) { - mWindowManager.mTaskSnapshotController.snapshotForSleeping(displayId); - } - } + boolean canShowActivityWhileKeyguardShowing(ActivityRecord r) { + // Allow to show it when we are about to dismiss Keyguard. This isn't allowed if r is + // already the dismissing activity, in which case we don't allow it to repeatedly dismiss + // Keyguard. + final KeyguardDisplayState state = getDisplayState(r.getDisplayId()); + return r.containsDismissKeyguardWindow() && canDismissKeyguard() && !state.mAodShowing + && (state.mDismissalRequested + || (r.canShowWhenLocked() && state.mDismissingKeyguardActivity != r)); + } - void notifyKeyguardOccludeChanged(boolean occluded) { - // TODO: This updates status of KeyguardDelegate. Once we delete occlude status from - // KeyguardDelegate, we should remove WindowManagerPolicy#onKeyguardOccludedChangedLw. - mWindowManager.mPolicy.onKeyguardOccludedChangedLw(occluded); - } + /** + * @return True if we may show an activity while Keyguard is occluded, false otherwise. + */ + boolean canShowWhileOccluded(boolean dismissKeyguard, boolean showWhenLocked) { + return showWhenLocked || dismissKeyguard + && !mWindowManager.isKeyguardSecure(mService.getCurrentUserId()); + } - void collect(@NonNull WindowContainer wc) { - mService.getTransitionController().collect(wc); + /** + * Checks whether {@param r} should be visible depending on Keyguard state. + * + * @return true if {@param r} is visible taken Keyguard state into account, false otherwise + */ + boolean checkKeyguardVisibility(ActivityRecord r) { + if (r.mDisplayContent.canShowWithInsecureKeyguard() && canDismissKeyguard()) { + return true; } - void requestTransitionIfNeeded(int displayId, @WindowManager.TransitionType int transit, - @WindowManager.TransitionFlags int flags) { - if (displayId != DEFAULT_DISPLAY) { - return; - } - if (DEBUG) { - Slog.d(TAG, "requestTransitionIfNeeded: display=" + displayId + ", transit=" - + transitTypeToString(transit)); - } - - final DisplayContent dc = mRootWindowContainer.getDisplayContent(displayId); - if (dc == null) { - Slog.e(TAG, "No DisplayContent exists: displayId=" + displayId); - return; - } - - if (transit == TRANSIT_KEYGUARD_GOING_AWAY) { - dc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, flags); - // We are deprecating TRANSIT_KEYGUARD_GOING_AWAY for Shell transition and use - // TRANSIT_FLAG_KEYGUARD_GOING_AWAY to indicate that it should animate keyguard - // going away. - mService.getTransitionController().requestTransitionIfNeeded( - TRANSIT_TO_BACK, flags, null /* trigger */, dc); - } else { - dc.requestTransitionAndLegacyPrepare(transit, flags); - } + if (isKeyguardOrAodShowing(r.mDisplayContent.getDisplayId())) { + // If keyguard is showing, nothing is visible, except if we are able to dismiss Keyguard + // right away and AOD isn't visible. + return canShowActivityWhileKeyguardShowing(r); + } else if (isKeyguardLocked(r.getDisplayId())) { + return canShowWhileOccluded(r.containsDismissKeyguardWindow(), r.canShowWhenLocked()); + } else { + return true; } + } - void acquireSleepToken(int displayId, boolean ensureActivitiesVisible) { - mSleepTokenAcquirer.acquire(displayId); - if (ensureActivitiesVisible) { - mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); + /** + * Makes sure to update lockscreen occluded/dismiss/turnScreenOn state if needed before + * completing set all visibility + * ({@link ActivityTaskSupervisor#beginActivityVisibilityUpdate}). + */ + void updateVisibility() { + for (int displayNdx = mRootWindowContainer.getChildCount() - 1; + displayNdx >= 0; displayNdx--) { + final DisplayContent display = mRootWindowContainer.getChildAt(displayNdx); + if (display.isRemoving() || display.isRemoved()) continue; + final KeyguardDisplayState state = getDisplayState(display.mDisplayId); + state.updateVisibility(this, display); + if (state.mRequestDismissKeyguard) { + handleDismissKeyguard(display.getDisplayId()); } } + } - void releaseSleepToken(int displayId, boolean resumeTopActivities) { - mSleepTokenAcquirer.release(displayId); - if (resumeTopActivities) { - mRootWindowContainer.resumeFocusedTasksTopActivities(); - mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); - mRootWindowContainer.addStartingWindowsForVisibleActivities(); - - } + /** + * Called when occluded state changed. + * + * @param topActivity the activity that controls the state whether keyguard should + * be occluded. That is the activity to be shown on top of keyguard if it requests so. + */ + private void handleOccludedChanged(int displayId, @Nullable ActivityRecord topActivity) { + // TODO(b/113840485): Handle app transition for individual display, and apply occluded + // state change to secondary displays. + // For now, only default display fully supports occluded change. Other displays only + // updates keyguard sleep token on that display. + if (displayId != DEFAULT_DISPLAY) { + updateKeyguardSleepToken(displayId); + return; } - void deferWindowLayout() { + mWindowManager.mPolicy.onKeyguardOccludedChangedLw(isDisplayOccluded(DEFAULT_DISPLAY)); + if (isKeyguardLocked(displayId)) { mService.deferWindowLayout(); - } - - void continueWindowLayout() { - mService.continueWindowLayout(); - } - - void executeAppTransition() { - mWindowManager.executeAppTransition(); - } - - private void updateDeferTransitionForAod(boolean waiting) { - KeyguardController.this.updateDeferTransitionForAod(waiting); - } - - private void setWakeTransitionReady() { - if (mService.getTransitionController().getCollectingTransitionType() == TRANSIT_WAKE) { - mService.getTransitionController().setReady( - mRootWindowContainer.getDefaultDisplay()); - } - } - - void requestLayoutRedoWallpaper(int displayId) { - final DisplayContent dc = mRootWindowContainer.getDisplayContent(displayId); - if (!dc.isSleeping() && dc.mWallpaperController.getWallpaperTarget() != null) { - dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; + try { + mRootWindowContainer.getDefaultDisplay() + .requestTransitionAndLegacyPrepare( + isDisplayOccluded(DEFAULT_DISPLAY) + ? TRANSIT_KEYGUARD_OCCLUDE + : TRANSIT_KEYGUARD_UNOCCLUDE, 0 /* flags */); + updateKeyguardSleepToken(DEFAULT_DISPLAY); + mWindowManager.executeAppTransition(); + } finally { + mService.continueWindowLayout(); } } - }; + dismissMultiWindowModeForTaskIfNeeded(displayId, topActivity != null + ? topActivity.getRootTask() : null); + } - private static class KeyguardDisplayStateMachine extends StateMachine { - static final int EVENT_DISMISS_KEYGUARD_ACTIVITY = 1; - static final int EVENT_SHOW_WHEN_LOCKED_ACTIVITY = 2; - static final int EVENT_CHECK_KEYGUARD_VISIBILITY = 3; - static final int EVENT_TOP_ACTIVITY_OCCLUDES_KEYGUARD = 4; - static final int EVENT_LAYOUT_CHANGES = 5; - static final int EVENT_DUMP = 6; - static final int EVENT_DISMISS_KEYGUARD_API = 7; - static final int EVENT_TURN_SCREEN_ON_ACTIVITY = 8; - final int mDisplayId; - - static final class CheckKeyguardVisibilityParam { - boolean mRet; - @NonNull final ActivityRecord mActivity; - - CheckKeyguardVisibilityParam(@NonNull ActivityRecord activity) { - mActivity = activity; - } + /** + * Called when keyguard going away state changed. + */ + private void handleKeyguardGoingAwayChanged(DisplayContent dc) { + mService.deferWindowLayout(); + try { + dc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, 0 /* transitFlags */); + // We are deprecating TRANSIT_KEYGUARD_GOING_AWAY for Shell transition and use + // TRANSIT_FLAG_KEYGUARD_GOING_AWAY to indicate that it should animate keyguard going + // away. + dc.mAtmService.getTransitionController().requestTransitionIfNeeded( + TRANSIT_OPEN, TRANSIT_FLAG_KEYGUARD_GOING_AWAY, null /* trigger */, dc); + updateKeyguardSleepToken(); + mWindowManager.executeAppTransition(); + } finally { + mService.continueWindowLayout(); } + } - static final class DismissKeyguardParam { - boolean mRet; - @NonNull final ActivityRecord mActivity; - @Nullable final IKeyguardDismissCallback mCallback; - @Nullable final CharSequence mMessage; - - DismissKeyguardParam(@NonNull ActivityRecord activity, - @Nullable IKeyguardDismissCallback callback, @Nullable CharSequence message) { - mActivity = activity; - mCallback = callback; - mMessage = message; - } + /** + * Called when somebody wants to dismiss the Keyguard via the flag. + */ + private void handleDismissKeyguard(int displayId) { + // We only allow dismissing Keyguard via the flag when Keyguard is secure for legacy + // reasons, because that's how apps used to dismiss Keyguard in the secure case. In the + // insecure case, we actually show it on top of the lockscreen. See #canShowWhileOccluded. + if (!mWindowManager.isKeyguardSecure(mService.getCurrentUserId())) { + return; } - static final class TopActivityOccludesKeyguardParam { - boolean mRet; - @NonNull final ActivityRecord mActivity; + mWindowManager.dismissKeyguard(null /* callback */, null /* message */); + final KeyguardDisplayState state = getDisplayState(displayId); + state.mDismissalRequested = true; - TopActivityOccludesKeyguardParam(@NonNull ActivityRecord activity) { - mActivity = activity; - } + // If we are about to unocclude the Keyguard, but we can dismiss it without security, + // we immediately dismiss the Keyguard so the activity gets shown without a flicker. + final DisplayContent dc = mRootWindowContainer.getDefaultDisplay(); + if (state.mKeyguardShowing && canDismissKeyguard() + && dc.mAppTransition.containsTransitRequest(TRANSIT_KEYGUARD_UNOCCLUDE)) { + mWindowManager.executeAppTransition(); } + } - static final class DumpParam { - ArrayList<String> mRet = new ArrayList<>(); - String mPrefix; - - DumpParam(@NonNull String prefix) { - mPrefix = prefix; - } - } + boolean isDisplayOccluded(int displayId) { + return getDisplayState(displayId).mOccluded; + } - KeyguardDisplayStateMachine(int displayId, @KeyguardState int initialState) { - super(initialState); - mDisplayId = displayId; - } + /** + * @return true if Keyguard can be currently dismissed without entering credentials. + */ + boolean canDismissKeyguard() { + return mWindowManager.mPolicy.isKeyguardTrustedLw() + || !mWindowManager.isKeyguardSecure(mService.getCurrentUserId()); + } - @Nullable - @Override - public Handler addStateHandler(int state, Handler handler) { - Handler prevHandler = super.addStateHandler(state, handler); - if (prevHandler != null) { - throw new IllegalStateException( - "Duplicate state handler registration: display=" + mDisplayId - + ", state=" + state); - } - return null; - } + /** + * @return Whether the dream activity is on top of default display. + */ + boolean isShowingDream() { + return getDisplayState(DEFAULT_DISPLAY).mShowingDream; + } - @Override - public void transit(@KeyguardState int newState) { - if (DEBUG) { - StringBuilder sb = new StringBuilder(); - sb.append("[ "); - for (Command cmd : getCommands()) { - sb.append(cmd); - sb.append(' '); - } - sb.append(" ]"); - Slog.d(TAG, "State change: display=" + mDisplayId - + ", current=" + keyguardStateToString(getCurrentState()) - + ", lastRequested=" + keyguardStateToString(getState()) - + ", newState=" + keyguardStateToString(newState) - + ", command=" + sb - + ", stack=" + Debug.getCallers(30)); - } - super.transit(newState); + private void dismissMultiWindowModeForTaskIfNeeded(int displayId, + @Nullable Task currentTaskControllingOcclusion) { + // TODO(b/113840485): Handle docked stack for individual display. + if (!getDisplayState(displayId).mKeyguardShowing || !isDisplayOccluded(DEFAULT_DISPLAY)) { + return; } - @Override - public void enter(@KeyguardState int state) { - if (DEBUG) { - Slog.d(TAG, "enter: display=" + mDisplayId + ", state=" - + keyguardStateToString(state)); - } - super.enter(state); + // Dismiss freeform windowing mode + if (currentTaskControllingOcclusion == null) { + return; } - - @Override - public void exit(@KeyguardState int state) { - if (DEBUG) { - Slog.d(TAG, "exit: display=" + mDisplayId + ", state=" - + keyguardStateToString(state)); - } - super.exit(state); + if (currentTaskControllingOcclusion.inFreeformWindowingMode()) { + currentTaskControllingOcclusion.setWindowingMode(WINDOWING_MODE_FULLSCREEN); } + } - void handleDismissKeyguardActivity() { - handle(EVENT_DISMISS_KEYGUARD_ACTIVITY, null /* param */); + private void updateKeyguardSleepToken() { + for (int displayNdx = mRootWindowContainer.getChildCount() - 1; + displayNdx >= 0; displayNdx--) { + final DisplayContent display = mRootWindowContainer.getChildAt(displayNdx); + updateKeyguardSleepToken(display.mDisplayId); } + } - void handleTurnScreenOnActivity() { - handle(EVENT_TURN_SCREEN_ON_ACTIVITY, null /* param */); + private void updateKeyguardSleepToken(int displayId) { + final KeyguardDisplayState state = getDisplayState(displayId); + if (isKeyguardUnoccludedOrAodShowing(displayId)) { + state.mSleepTokenAcquirer.acquire(displayId); + } else { + state.mSleepTokenAcquirer.release(displayId); } + } - boolean handleDismissKeyguard(@NonNull ActivityRecord r, - @Nullable IKeyguardDismissCallback callback, @Nullable CharSequence message) { - DismissKeyguardParam param = new DismissKeyguardParam(r, callback, message); - handle(EVENT_DISMISS_KEYGUARD_API, param); - return param.mRet; + private KeyguardDisplayState getDisplayState(int displayId) { + KeyguardDisplayState state = mDisplayStates.get(displayId); + if (state == null) { + state = new KeyguardDisplayState(mService, displayId, mSleepTokenAcquirer); + mDisplayStates.append(displayId, state); } + return state; + } - void handleShowWhenLockedActivity() { - handle(EVENT_SHOW_WHEN_LOCKED_ACTIVITY, null /* param */); + void onDisplayRemoved(int displayId) { + final KeyguardDisplayState state = mDisplayStates.get(displayId); + if (state != null) { + state.onRemoved(); + mDisplayStates.remove(displayId); } + } - void handleLayoutChanges() { - handle(EVENT_LAYOUT_CHANGES, null /* param */); + private final Runnable mResetWaitTransition = () -> { + synchronized (mWindowManager.mGlobalLock) { + updateDeferTransitionForAod(false /* waiting */); } + }; - boolean checkKeyguardVisibility(@NonNull ActivityRecord r) { - final CheckKeyguardVisibilityParam param = new CheckKeyguardVisibilityParam(r); - handle(EVENT_CHECK_KEYGUARD_VISIBILITY, param); - return param.mRet; + // Defer transition until AOD dismissed. + void updateDeferTransitionForAod(boolean waiting) { + if (waiting == mWaitingForWakeTransition) { + return; } - - boolean topActivityOccludesKeyguard(@NonNull ActivityRecord r) { - final TopActivityOccludesKeyguardParam param = new TopActivityOccludesKeyguardParam(r); - handle(EVENT_TOP_ACTIVITY_OCCLUDES_KEYGUARD, param); - return param.mRet; + if (!mService.getTransitionController().isCollecting()) { + return; } - - ArrayList<String> handleDump(String prefix) { - final DumpParam param = new DumpParam(prefix); - handle(EVENT_DUMP, param); - return param.mRet; + // if AOD is showing, defer the wake transition until AOD state changed. + if (waiting && isAodShowing(DEFAULT_DISPLAY)) { + mWaitingForWakeTransition = true; + mWindowManager.mAtmService.getTransitionController().deferTransitionReady(); + mWindowManager.mH.postDelayed(mResetWaitTransition, DEFER_WAKE_TRANSITION_TIMEOUT_MS); + } else if (!waiting) { + // dismiss the deferring if the AOD state change or cancel awake. + mWaitingForWakeTransition = false; + mWindowManager.mAtmService.getTransitionController().continueTransitionReady(); + mWindowManager.mH.removeCallbacks(mResetWaitTransition); } } - /** - * Helper class for implementing handler in type-safe way. - */ - private abstract static class Handler implements StateMachine.Handler { - @Override - public final boolean handle(int event, @Nullable Object param) { - switch (event) { - case KeyguardDisplayStateMachine.EVENT_DISMISS_KEYGUARD_ACTIVITY: - return handleDismissKeyguardActivity(); - case KeyguardDisplayStateMachine.EVENT_TURN_SCREEN_ON_ACTIVITY: - return handleTurnScreenOnActivity(); - case KeyguardDisplayStateMachine.EVENT_DISMISS_KEYGUARD_API: { - final KeyguardDisplayStateMachine.DismissKeyguardParam typedParam = - (KeyguardDisplayStateMachine.DismissKeyguardParam) param; - Optional<Boolean> ret = handleDismissKeyguard(typedParam.mActivity, - typedParam.mCallback, typedParam.mMessage); - if (ret.isPresent()) { - typedParam.mRet = ret.get(); - return true; - } - return false; - } - case KeyguardDisplayStateMachine.EVENT_SHOW_WHEN_LOCKED_ACTIVITY: - return handleShowWhenLockedActivity(); - case KeyguardDisplayStateMachine.EVENT_CHECK_KEYGUARD_VISIBILITY: { - final KeyguardDisplayStateMachine.CheckKeyguardVisibilityParam typedParam = - (KeyguardDisplayStateMachine.CheckKeyguardVisibilityParam) param; - Optional<Boolean> ret = checkKeyguardVisibility(typedParam.mActivity); - if (ret.isPresent()) { - typedParam.mRet = ret.get(); - return true; - } - return false; - } - case KeyguardDisplayStateMachine.EVENT_TOP_ACTIVITY_OCCLUDES_KEYGUARD: { - final KeyguardDisplayStateMachine.TopActivityOccludesKeyguardParam typedParam = - (KeyguardDisplayStateMachine.TopActivityOccludesKeyguardParam) param; - Optional<Boolean> ret = topActivityOccludesKeyguardParam(typedParam.mActivity); - if (ret.isPresent()) { - typedParam.mRet = ret.get(); - return true; - } - return false; - } - case KeyguardDisplayStateMachine.EVENT_LAYOUT_CHANGES: - return handleLayoutChanges(); - case KeyguardDisplayStateMachine.EVENT_DUMP: - final KeyguardDisplayStateMachine.DumpParam typedParam = - (KeyguardDisplayStateMachine.DumpParam) param; - String dumpInfo = handleDump(typedParam.mPrefix); - if (dumpInfo != null) { - typedParam.mRet.add(dumpInfo); - } - // keep collecting information for dump up to top status. - return false; - default: - Slog.e(TAG, "Handler.handle(): Unknown event(" + event + ")"); - return false; - } - } - - Optional<Boolean> checkKeyguardVisibility(@NonNull ActivityRecord activity) { - return Optional.empty(); - } - Optional<Boolean> topActivityOccludesKeyguardParam(@NonNull ActivityRecord r) { - return Optional.empty(); - } - - /** - * Handle flags in the activity which request to dismiss the keyguard. - * - * @see ActivityOptions#setDismissKeyguard() - * @see WindowManager.LayoutParams#FLAG_DISMISS_KEYGUARD - */ - boolean handleDismissKeyguardActivity() { - return false; - } - - /** - * Handle flags in the activity which request to turn the screen on. This must be called - * after dismiss keyguard flag is handled. - */ - boolean handleTurnScreenOnActivity() { - return false; - } - /** - * Handle flags in the activity which decides if the activity can be shown on top of the - * keyguard. - * - * @see android.app.Activity#setShowWhenLocked(boolean) - * @see android.app.Activity#setInheritShowWhenLocked(boolean) - */ - boolean handleShowWhenLockedActivity() { - return false; + /** Represents Keyguard state per individual display. */ + private static class KeyguardDisplayState { + private final int mDisplayId; + private boolean mKeyguardShowing; + private boolean mAodShowing; + private boolean mKeyguardGoingAway; + private boolean mDismissalRequested; + private boolean mOccluded; + private boolean mShowingDream; + + private ActivityRecord mTopOccludesActivity; + private ActivityRecord mDismissingKeyguardActivity; + private ActivityRecord mTopTurnScreenOnActivity; + + private boolean mRequestDismissKeyguard; + private final ActivityTaskManagerService mService; + private final ActivityTaskManagerInternal.SleepTokenAcquirer mSleepTokenAcquirer; + + KeyguardDisplayState(ActivityTaskManagerService service, int displayId, + ActivityTaskManagerInternal.SleepTokenAcquirer acquirer) { + mService = service; + mDisplayId = displayId; + mSleepTokenAcquirer = acquirer; } - /** - * Request relayout if necessary. - */ - boolean handleLayoutChanges() { - return false; + void onRemoved() { + mTopOccludesActivity = null; + mDismissingKeyguardActivity = null; + mTopTurnScreenOnActivity = null; + mSleepTokenAcquirer.release(mDisplayId); } /** - * Called when the activity requests to dismiss the keyguard via KeyguardManager APIs. - * - * @param r The activity which requested to dismiss the keyguard. - * @return Present if the state handles, delegate to its parent state otherwise. When the - * value is present, the value is {@code true} if the keyguard dismiss request is - * processed, {@code false} otherwise. + * Updates keyguard status if the top task could be visible. The top task may occlude + * keyguard, request to dismiss keyguard or make insecure keyguard go away based on its + * properties. */ - Optional<Boolean> handleDismissKeyguard(@NonNull ActivityRecord r, - @Nullable IKeyguardDismissCallback callback, @Nullable CharSequence message) { - return Optional.empty(); - } - - @Nullable String handleDump(@NonNull String prefix) { - return null; - } - } - - private static class DisplayState { - private final int mDisplayId; - @NonNull private final ServiceDelegate mServiceDelegate; - private final KeyguardDisplayStateMachine mStateMachine; - - // TODO: Set watchdog timer to sync mLastNotifiedOccludedState == isIn(OCCLUDED) - private boolean mLastNotifiedOccludedState = false; - - // Top activity which has a window with FLAG_DISMISS_KEYGUARD flag. Valid only when the - // current state is KEYGUARD_STATE_ON or one of its sub states. - @Nullable private ActivityRecord mDismissingKeyguardActivity; - - // KeyguardController has requested to dismiss keyguard via IWindowManager#dismissKeyguard. - // Reset this to false again, once the KeyguardController status is updated. - private boolean mDismissalRequested = false; - - DisplayState(int displayId, @NonNull ServiceDelegate serviceDelegate) { - mDisplayId = displayId; - mServiceDelegate = serviceDelegate; - mStateMachine = new KeyguardDisplayStateMachine(displayId, KEYGUARD_STATE_OFF); - - mStateMachine.addStateHandler(KEYGUARD_STATE_ROOT, new Handler() { - @Override - Optional<Boolean> checkKeyguardVisibility(@NonNull ActivityRecord activity) { - return Optional.of(false); + void updateVisibility(KeyguardController controller, DisplayContent display) { + final boolean lastOccluded = mOccluded; + final boolean lastKeyguardGoingAway = mKeyguardGoingAway; + + final ActivityRecord lastDismissKeyguardActivity = mDismissingKeyguardActivity; + final ActivityRecord lastTurnScreenOnActivity = mTopTurnScreenOnActivity; + + mRequestDismissKeyguard = false; + mOccluded = false; + mShowingDream = false; + + mTopOccludesActivity = null; + mDismissingKeyguardActivity = null; + mTopTurnScreenOnActivity = null; + + boolean occludedByActivity = false; + final Task task = getRootTaskForControllingOccluding(display); + final ActivityRecord top = task != null ? task.getTopNonFinishingActivity() : null; + if (top != null) { + if (top.containsDismissKeyguardWindow()) { + mDismissingKeyguardActivity = top; } - - @Override - Optional<Boolean> topActivityOccludesKeyguardParam(@NonNull ActivityRecord r) { - return Optional.of(false); + if (top.getTurnScreenOnFlag() && top.currentLaunchCanTurnScreenOn()) { + mTopTurnScreenOnActivity = top; } - }); - mStateMachine.addStateHandler(KEYGUARD_STATE_OFF, new Handler() { - @Override - public Optional<Boolean> checkKeyguardVisibility(@NonNull ActivityRecord r) { - return Optional.of(true); + if (top.mDismissKeyguard && mKeyguardShowing) { + mKeyguardGoingAway = true; + } else if (top.canShowWhenLocked()) { + mTopOccludesActivity = top; } - - @Override - Optional<Boolean> handleDismissKeyguard( - @NonNull ActivityRecord r, @Nullable IKeyguardDismissCallback callback, - @Nullable CharSequence message) { - // Keyguard is not shown, so we don't handle the request to dismiss the - // keyguard. - return Optional.of(false); - } - }); - - mStateMachine.addStateHandler(KEYGUARD_STATE_GOING_AWAY, new Handler() { - @Override - public void enter() { - mServiceDelegate.deferWindowLayout(); - try { - mServiceDelegate.requestTransitionIfNeeded(mDisplayId, - TRANSIT_KEYGUARD_GOING_AWAY, - TRANSIT_FLAG_KEYGUARD_GOING_AWAY - | TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER); - // Some stack visibility might change (e.g. docked stack) - mServiceDelegate.releaseSleepToken(mDisplayId, - true /* resumeTopActivities */); - mServiceDelegate.executeAppTransition(); - } finally { - mServiceDelegate.continueWindowLayout(); - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - } - } - }); - - mStateMachine.addStateHandler(KEYGUARD_STATE_ON, new Handler() { - public boolean handleDismissKeyguardActivity() { - final ActivityRecord lastDismissingKeyguardActivity = - mDismissingKeyguardActivity; - final ActivityRecord top = mServiceDelegate.getTopNonFinishingActivity( - mDisplayId); - mDismissingKeyguardActivity = - (top != null && top.containsDismissKeyguardWindow()) ? top : null; - if (lastDismissingKeyguardActivity != mDismissingKeyguardActivity - && mDismissingKeyguardActivity != null - && mServiceDelegate.isKeyguardSecure()) { - // We only allow dismissing Keyguard via the flag when Keyguard is secure - // for legacy reasons, because that's how apps used to dismiss Keyguard in - // the secure case. In the insecure case, we actually show it on top of the - // lockscreen. See #canShowWhileOccluded. - mDismissalRequested = true; - mServiceDelegate.dismissKeyguard(null, null); - } - return true; - } - - @Override - Optional<Boolean> handleDismissKeyguard(@NonNull ActivityRecord r, - @Nullable IKeyguardDismissCallback callback, - @Nullable CharSequence message) { - if (!r.visibleIgnoringKeyguard) { - return Optional.of(false); - } - if (DEBUG) { - Slog.d(TAG, "Activity requesting to dismiss Keyguard: " + r); - } - // If the client has requested to dismiss the keyguard and the Activity has the - // flag to turn the screen on, wakeup the screen if it's the top Activity. - // Note that it's possible that the client requests to dismiss the keyguard - // before the activity adds a window. In this case the flag set on the window - // is not yet visible from ActivityRecord, so we need to check the flag again - // when the activity adds a window later. See #handleTurnScreenOnActivity(). - if (r.getTurnScreenOnFlag() && r.isTopRunningActivity()) { - mServiceDelegate.wakeUp("ON/handleDismissKeyguard"); - r.setCurrentLaunchCanTurnScreenOn(false); - } - mDismissalRequested = true; - mServiceDelegate.dismissKeyguard(callback, message); - return Optional.of(true); - } - - @Override - public void enter() { - // Update the task snapshot if the screen will not be turned off. To make sure - // that the unlocking animation can animate consistent content. - mServiceDelegate.snapshotForSleeping(mDisplayId); + top.mDismissKeyguard = false; + + // Only the top activity may control occluded, as we can't occlude the Keyguard + // if the top app doesn't want to occlude it. + occludedByActivity = mTopOccludesActivity != null + || (mDismissingKeyguardActivity != null + && task.topRunningActivity() == mDismissingKeyguardActivity + && controller.canShowWhileOccluded( + true /* dismissKeyguard */, false /* showWhenLocked */)); + // FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD only apply for secondary display. + if (mDisplayId != DEFAULT_DISPLAY) { + occludedByActivity |= display.canShowWithInsecureKeyguard() + && controller.canDismissKeyguard(); } + } - @Nullable - @Override - String handleDump(@NonNull String prefix) { - StringBuffer sb = new StringBuffer(); - sb.append(prefix) - .append(" mDismissingKeyguardActivity=") - .append(mDismissingKeyguardActivity) - .append("\n"); - return sb.toString(); - } - }); - - mStateMachine.addStateHandler(KEYGUARD_STATE_OCCLUDED, new Handler() { - ActivityRecord mTopOccludesActivity; - - @Override - Optional<Boolean> checkKeyguardVisibility(@NonNull ActivityRecord activity) { - return Optional.of(mServiceDelegate.canOcclude(activity)); - } - - @Override - public boolean handleShowWhenLockedActivity() { - final ActivityRecord top = mServiceDelegate.getTopNonFinishingActivity( - mDisplayId); - final ActivityRecord topOccludesActivity = mServiceDelegate.canOcclude(top) - ? top : null; - if (mTopOccludesActivity == topOccludesActivity) { - return true; - } - // Launch SHOW_WHEN_LOCKED or INHERIT_SHOW_WHEN_LOCKED activity on top of an - // occluding activity. - mTopOccludesActivity = topOccludesActivity; - if (mServiceDelegate.isTopActivityDreaming(mDisplayId)) { - // Dream activity is launched on top of the previous SHOW_WHEN_LOCKED - // activity. - setKeyguardState(KEYGUARD_STATE_DREAMING); - } else if (topOccludesActivity == null) { - // SHOW_WHEN_LOCKED activity finishes. - setKeyguardState(KEYGUARD_STATE_LOCKSCREEN_SHOWN); - } - return true; - } - - @Override - boolean handleLayoutChanges() { - // The occluding activity may be translucent or not fill screen. Then let - // wallpaper to check whether it should set itself as target to avoid blank - // background. - if (!mTopOccludesActivity.fillsParent()) { - mServiceDelegate.requestLayoutRedoWallpaper(mDisplayId); - } - return true; - } - - @Override - Optional<Boolean> topActivityOccludesKeyguardParam(@NonNull ActivityRecord r) { - return Optional.of(mTopOccludesActivity == r); - } - - @Override - public void enter() { - mTopOccludesActivity = mServiceDelegate.getTopNonFinishingActivity(mDisplayId); - if (!mServiceDelegate.canOcclude(mTopOccludesActivity)) { - Slog.e(TAG, "enter(OCCLUDE): no occluding activity"); - setKeyguardState(KEYGUARD_STATE_LOCKSCREEN_SHOWN); - return; - } - - if (DEBUG) { - Slog.d(TAG, "handleOccludedChanged: display=" + mDisplayId - + ", topActivity=" + mTopOccludesActivity); - } - // Collect the participates for shell transition, so that transition won't - // happen too early since the transition was set ready. - mServiceDelegate.collect(mTopOccludesActivity); - // TODO(b/113840485): Handle app transition for individual display, and apply - // occluded state change to secondary displays. For now, only default display - // fully supports occluded change. Other displays only updates keyguard sleep - // token on that display. - if (mDisplayId != DEFAULT_DISPLAY) { - mServiceDelegate.releaseSleepToken(mDisplayId, - false /* resumeTopActivities */); - return; - } - - if (mTopOccludesActivity.getTurnScreenOnFlag() - && mTopOccludesActivity.currentLaunchCanTurnScreenOn() - && !mServiceDelegate.isDeviceInteractive()) { - mServiceDelegate.wakeUp("OCCLUDE/enter"); - mTopOccludesActivity.setCurrentLaunchCanTurnScreenOn(false); - } - - mServiceDelegate.notifyKeyguardOccludeChanged(true /* occluded */); - mServiceDelegate.deferWindowLayout(); - try { - mServiceDelegate.requestTransitionIfNeeded(mDisplayId, - TRANSIT_KEYGUARD_OCCLUDE, 0 /* flags */); - mServiceDelegate.releaseSleepToken(mDisplayId, - false /* resumeTopActivities */); - mServiceDelegate.executeAppTransition(); - } finally { - mServiceDelegate.continueWindowLayout(); - } - // Dismiss freeform windowing mode - final Task currentTaskControllingOcclusion = mTopOccludesActivity.getRootTask(); - if (currentTaskControllingOcclusion != null - && currentTaskControllingOcclusion.inFreeformWindowingMode()) { - currentTaskControllingOcclusion.setWindowingMode(WINDOWING_MODE_FULLSCREEN); - } - } - - @Override - public void exit() { - mTopOccludesActivity = null; - if (DEBUG) { - Slog.d(TAG, "handleOccludedChanged: topActivity=" + null); - } - // TODO(b/113840485): Handle app transition for individual display, and apply - // occluded state change to secondary displays. - // For now, only default display fully supports occluded change. Other displays - // only updates keyguard sleep token on that display. - if (mDisplayId != DEFAULT_DISPLAY) { - mServiceDelegate.acquireSleepToken( - mDisplayId, false /* ensureActivitiesVisible */); - return; - } - - mServiceDelegate.notifyKeyguardOccludeChanged(false /* occluded */); - mServiceDelegate.deferWindowLayout(); - try { - mServiceDelegate.requestTransitionIfNeeded(mDisplayId, - TRANSIT_KEYGUARD_UNOCCLUDE, 0 /* flags */); - mServiceDelegate.acquireSleepToken( - mDisplayId, false /* ensureActivitiesVisible */); - mServiceDelegate.executeAppTransition(); - } finally { - mServiceDelegate.continueWindowLayout(); - } - } - - @Nullable - @Override - String handleDump(@NonNull String prefix) { - StringBuffer sb = new StringBuffer(); - sb.append(prefix) - .append(" mTopOccludesActivity=") - .append(mTopOccludesActivity) - .append("\n"); - return sb.toString(); - } - }); - - mStateMachine.addStateHandler(KEYGUARD_STATE_KEYGUARD_TOP, new Handler() { - @Override - public boolean handleDismissKeyguardActivity() { - final ActivityRecord top = mServiceDelegate.getTopNonFinishingActivity( - mDisplayId); - if (top != null && top.mDismissKeyguard) { - // Top activity has been launched with ActivityOptions#setDismissKeyguard. - // Authentication has already been passed, so we can turn off the keyguard - // immediately. - top.mDismissKeyguard = false; - setKeyguardState(KEYGUARD_STATE_GOING_AWAY); - // Collect the participates for shell transition, so that transition won't - // happen too early since the transition was set ready. - mServiceDelegate.collect(top); - return true; - } - return false; - } - - @Override - public void enter() { - mServiceDelegate.acquireSleepToken(mDisplayId, - true /* ensureActivitiesVisible */); - InputMethodManagerInternal.get().updateImeWindowStatus( - false /* disableImeIcon */); - mServiceDelegate.setWakeTransitionReady(); - } - - @Override - public void exit() { - // Sleep token is released in enter() action in other states, since we need - // to call requestTransition() before updating visibility of the activities. - } - }); - - mStateMachine.addStateHandler(KEYGUARD_STATE_LOCKSCREEN_SHOWN, new Handler() { - @Override - public Optional<Boolean> checkKeyguardVisibility(@NonNull ActivityRecord r) { - // If lock screen is showing, nothing is visible, except if we are able to - // dismiss Keyguard right away. This isn't allowed if r is already the - // dismissing activity, in which case we don't allow it to repeatedly - // dismiss Keyguard. - return Optional.of(r.containsDismissKeyguardWindow() - && mServiceDelegate.canDismissKeyguard() - && (mDismissalRequested - || (r.canShowWhenLocked() && mDismissingKeyguardActivity != r))); - } - - @Override - public boolean handleShowWhenLockedActivity() { - final ActivityRecord top = mServiceDelegate.getTopNonFinishingActivity( - mDisplayId); - final ActivityRecord topOccludesActivity = mServiceDelegate.canOcclude(top) - ? top : null; - if (topOccludesActivity != null) { - setKeyguardState(mServiceDelegate.isTopActivityDreaming(mDisplayId) - ? KEYGUARD_STATE_DREAMING : KEYGUARD_STATE_OCCLUDED); - } - return true; - } - }); - - mStateMachine.addStateHandler(KEYGUARD_STATE_AOD_SHOWN, new Handler() { - // Top activity which has FLAG_TURN_SCREEN_ON flag. - @Nullable private ActivityRecord mTopTurnScreenOnActivity; - - @Override - public Optional<Boolean> checkKeyguardVisibility(@NonNull ActivityRecord r) { - return Optional.of(false); - } - - @Override - boolean handleTurnScreenOnActivity() { - final ActivityRecord lastTopTurnScreenOnActivity = mTopTurnScreenOnActivity; - final ActivityRecord top = mServiceDelegate.getTopNonFinishingActivity( - mDisplayId); - mTopTurnScreenOnActivity = (top != null && top.getTurnScreenOnFlag() - && top.currentLaunchCanTurnScreenOn()) ? top : null; - if (mTopTurnScreenOnActivity != lastTopTurnScreenOnActivity - && mTopTurnScreenOnActivity != null - && !mServiceDelegate.isDeviceInteractive() - && mDismissalRequested) { - mServiceDelegate.wakeUp("AOD_SHOWN/handleTurnScreenOnActivity"); - mTopTurnScreenOnActivity.setCurrentLaunchCanTurnScreenOn(false); - } - return true; - } - - @Override - public void enter() { - if (mLastNotifiedOccludedState) { - if (mDisplayId == DEFAULT_DISPLAY) { - mServiceDelegate.forceSyncOccludedStatus(false); - } - commitOccludedStatus(false); - } - } - - @Override - public void exit() { - mServiceDelegate.updateDeferTransitionForAod(false /* waiting */); - } - - @Nullable - @Override - String handleDump(@NonNull String prefix) { - StringBuffer sb = new StringBuffer(); - sb.append(prefix) - .append(" mTopTurnScreenOnActivity=") - .append(mTopTurnScreenOnActivity) - .append("\n"); - return sb.toString(); - } - }); - } - - void onRemoved() { - mServiceDelegate.releaseSleepToken(mDisplayId, false /* resumeTopActivities */); - } - - void updateVisibility() { - mStateMachine.handleDismissKeyguardActivity(); - mStateMachine.handleTurnScreenOnActivity(); - mStateMachine.handleShowWhenLockedActivity(); - mStateMachine.handleLayoutChanges(); - } - - boolean dismissKeyguard(@NonNull ActivityRecord r, - @Nullable IKeyguardDismissCallback callback, - @Nullable CharSequence message) { - return mStateMachine.handleDismissKeyguard(r, callback, message); - } - - void commitOccludedStatus(boolean occluded) { - mLastNotifiedOccludedState = occluded; - } - - void setKeyguardState(@KeyguardState int newState) { - mDismissalRequested = false; - mStateMachine.transit(newState); - } - - boolean isKeyguardTop() { - return mStateMachine.isIn(KEYGUARD_STATE_KEYGUARD_TOP); - } + mShowingDream = display.getDisplayPolicy().isShowingDreamLw() && (top != null + && top.getActivityType() == ACTIVITY_TYPE_DREAM); + mOccluded = mShowingDream || occludedByActivity; + mRequestDismissKeyguard = lastDismissKeyguardActivity != mDismissingKeyguardActivity + && !mOccluded && !mKeyguardGoingAway + && mDismissingKeyguardActivity != null; + if (mOccluded && mKeyguardShowing && !display.isSleeping() && !top.fillsParent() + && display.mWallpaperController.getWallpaperTarget() == null) { + // The occluding activity may be translucent or not fill screen. Then let wallpaper + // to check whether it should set itself as target to avoid blank background. + display.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; + } - boolean isIn(@KeyguardState int category) { - return mStateMachine.isIn(category); - } + if (mTopTurnScreenOnActivity != lastTurnScreenOnActivity + && mTopTurnScreenOnActivity != null + && !mService.mWindowManager.mPowerManager.isInteractive() + && (mRequestDismissKeyguard || occludedByActivity)) { + controller.mTaskSupervisor.wakeUp("handleTurnScreenOn"); + mTopTurnScreenOnActivity.setCurrentLaunchCanTurnScreenOn(false); + } - boolean topActivityOccludesKeyguard(@NonNull ActivityRecord r) { - return mStateMachine.topActivityOccludesKeyguard(r); + boolean hasChange = false; + if (lastOccluded != mOccluded) { + controller.handleOccludedChanged(mDisplayId, mTopOccludesActivity); + hasChange = true; + } else if (!lastKeyguardGoingAway && mKeyguardGoingAway) { + controller.handleKeyguardGoingAwayChanged(display); + hasChange = true; + } + // Collect the participates for shell transition, so that transition won't happen too + // early since the transition was set ready. + if (hasChange && top != null && (mOccluded || mKeyguardGoingAway)) { + display.mTransitionController.collect(top); + } } - boolean checkKeyguardVisibility(@NonNull ActivityRecord r) { - return mStateMachine.checkKeyguardVisibility(r); + /** + * Gets the stack used to check the occluded state. + * <p> + * Only the top non-pinned activity of the focusable stack on each display can control its + * occlusion state. + */ + @Nullable + private Task getRootTaskForControllingOccluding(DisplayContent display) { + return display.getRootTask(task -> + task != null && task.isFocusableAndVisible() && !task.inPinnedWindowingMode()); + } + + void dumpStatus(PrintWriter pw, String prefix) { + final StringBuilder sb = new StringBuilder(); + sb.append(prefix); + sb.append(" KeyguardShowing=") + .append(mKeyguardShowing) + .append(" AodShowing=") + .append(mAodShowing) + .append(" KeyguardGoingAway=") + .append(mKeyguardGoingAway) + .append(" DismissalRequested=") + .append(mDismissalRequested) + .append(" Occluded=") + .append(mOccluded) + .append(" DismissingKeyguardActivity=") + .append(mDismissingKeyguardActivity) + .append(" TurnScreenOnActivity=") + .append(mTopTurnScreenOnActivity) + .append(" at display=") + .append(mDisplayId); + pw.println(sb.toString()); } void dumpDebug(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); proto.write(KeyguardPerDisplayProto.DISPLAY_ID, mDisplayId); - proto.write(KeyguardPerDisplayProto.KEYGUARD_SHOWING, - isIn(KEYGUARD_STATE_LOCKSCREEN_SHOWN)); - proto.write(KeyguardPerDisplayProto.AOD_SHOWING, isIn(KEYGUARD_STATE_AOD_SHOWN)); - proto.write(KeyguardPerDisplayProto.KEYGUARD_OCCLUDED, isIn(KEYGUARD_STATE_OCCLUDED)); + proto.write(KeyguardPerDisplayProto.KEYGUARD_SHOWING, mKeyguardShowing); + proto.write(KeyguardPerDisplayProto.AOD_SHOWING, mAodShowing); + proto.write(KeyguardPerDisplayProto.KEYGUARD_OCCLUDED, mOccluded); + proto.write(KeyguardPerDisplayProto.KEYGUARD_GOING_AWAY, mKeyguardGoingAway); proto.end(token); } + } - void dump(PrintWriter pw, String prefix) { - StringBuffer sb = new StringBuffer(); - sb.append(prefix) - .append("* display=") - .append(mDisplayId) - .append("\n"); - sb.append(prefix) - .append(" state=") - .append(keyguardStateToString(mStateMachine.getState())) - .append("\n"); - sb.append(prefix) - .append(" mLastNotifiedOccludedState=") - .append(mLastNotifiedOccludedState) - .append("\n"); - sb.append(prefix) - .append(" mDismissalRequested=") - .append(mDismissalRequested) - .append("\n"); - pw.print(sb.toString()); + void dump(PrintWriter pw, String prefix) { + final KeyguardDisplayState default_state = getDisplayState(DEFAULT_DISPLAY); + pw.println(prefix + "KeyguardController:"); + pw.println(prefix + " mKeyguardShowing=" + default_state.mKeyguardShowing); + pw.println(prefix + " mAodShowing=" + default_state.mAodShowing); + pw.println(prefix + " mKeyguardGoingAway=" + default_state.mKeyguardGoingAway); + dumpDisplayStates(pw, prefix); + pw.println(prefix + " mDismissalRequested=" + default_state.mDismissalRequested); + pw.println(); + } - ArrayList<String> dumpInfo = mStateMachine.handleDump(prefix); - for (int i = dumpInfo.size() - 1; i >= 0; --i) { - pw.print(dumpInfo.get(i)); - } + void dumpDebug(ProtoOutputStream proto, long fieldId) { + final KeyguardDisplayState default_state = getDisplayState(DEFAULT_DISPLAY); + final long token = proto.start(fieldId); + proto.write(AOD_SHOWING, default_state.mAodShowing); + proto.write(KEYGUARD_SHOWING, default_state.mKeyguardShowing); + proto.write(KEYGUARD_GOING_AWAY, default_state.mKeyguardGoingAway); + writeDisplayStatesToProto(proto, KEYGUARD_PER_DISPLAY); + proto.end(token); + } + + private void dumpDisplayStates(PrintWriter pw, String prefix) { + for (int i = 0; i < mDisplayStates.size(); i++) { + mDisplayStates.valueAt(i).dumpStatus(pw, prefix); + } + } + + private void writeDisplayStatesToProto(ProtoOutputStream proto, long fieldId) { + for (int i = 0; i < mDisplayStates.size(); i++) { + mDisplayStates.valueAt(i).dumpDebug(proto, fieldId); } } } diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 80965a78bc9b..5e116baa3fae 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -730,7 +730,7 @@ class TransitionController { void dispatchLegacyAppTransitionStarting(TransitionInfo info, long statusBarTransitionDelay) { for (int i = 0; i < mLegacyListeners.size(); ++i) { - mLegacyListeners.get(i).onAppTransitionStartingLocked(info); + // TODO(shell-transitions): handle (un)occlude transition. mLegacyListeners.get(i).onAppTransitionStartingLocked( SystemClock.uptimeMillis() + statusBarTransitionDelay, AnimationAdapter.STATUS_BAR_TRANSITION_DURATION); diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index ad6bd3c6ccf6..1282acbc9e5a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -44,7 +44,6 @@ import android.view.SurfaceControlViewHost; import android.view.WindowInfo; import android.view.WindowManager.DisplayImePolicy; import android.view.inputmethod.ImeTracker; -import android.window.TransitionInfo; import com.android.internal.policy.KeyInterceptionInfo; import com.android.server.input.InputManagerService; @@ -213,7 +212,7 @@ public abstract class WindowManagerInternal { * Abstract class to be notified about {@link com.android.server.wm.AppTransition} events. Held * as an abstract class so a listener only needs to implement the methods of its interest. */ - public abstract static class AppTransitionListener { + public static abstract class AppTransitionListener { /** * Called when an app transition is being setup and about to be executed. @@ -252,20 +251,6 @@ public abstract class WindowManagerInternal { } /** - * Called when an app transition gets started when WM shell is enabled. - * - * @param info Information about what is changing during a transition. - * - * @return Return any bit set of {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_LAYOUT}, - * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_CONFIG}, - * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_WALLPAPER}, - * or {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_ANIM}. - */ - public int onAppTransitionStartingLocked(TransitionInfo info) { - return 0; - } - - /** * Called when an app transition is finished running. * * @param token the token for app whose transition has finished diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index f6cb06866cf3..a596eed2a500 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -3132,7 +3132,7 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void notifyKeyguardTrustedChanged() { synchronized (mGlobalLock) { - if (mAtmService.mKeyguardController.isLocksScreenShowing(DEFAULT_DISPLAY)) { + if (mAtmService.mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)) { mRoot.ensureActivitiesVisible(null, 0, false /* preserveWindows */); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 9fda2043c5d2..e663245157cf 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -1475,6 +1475,7 @@ public class ActivityRecordTests extends WindowTestsBase { // Make keyguard locked and set the top activity show-when-locked. KeyguardController keyguardController = activity.mTaskSupervisor.getKeyguardController(); int displayId = activity.getDisplayId(); + doReturn(true).when(keyguardController).isKeyguardLocked(eq(displayId)); final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build(); topActivity.setVisibleRequested(true); topActivity.nowVisible = true; @@ -1484,24 +1485,18 @@ public class ActivityRecordTests extends WindowTestsBase { anyBoolean() /* preserveWindows */, anyBoolean() /* notifyClients */); topActivity.setShowWhenLocked(true); - try { - keyguardController.setKeyguardShown(displayId, true, false); - - // Verify the stack-top activity is occluded keyguard. - assertEquals(topActivity, task.topRunningActivity()); - assertTrue(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY)); + // Verify the stack-top activity is occluded keyguard. + assertEquals(topActivity, task.topRunningActivity()); + assertTrue(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY)); - // Finish the top activity - topActivity.setState(PAUSED, "true"); - topActivity.finishing = true; - topActivity.completeFinishing("test"); + // Finish the top activity + topActivity.setState(PAUSED, "true"); + topActivity.finishing = true; + topActivity.completeFinishing("test"); - // Verify new top activity does not occlude keyguard. - assertEquals(activity, task.topRunningActivity()); - assertFalse(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY)); - } finally { - keyguardController.setKeyguardShown(displayId, false, false); - } + // Verify new top activity does not occlude keyguard. + assertEquals(activity, task.topRunningActivity()); + assertFalse(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY)); } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java index 305260b23d12..3dcae91f5c89 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java @@ -252,12 +252,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { mAtm.setLockScreenShown(true, true); mRootWindowContainer.forAllDisplays(displayContent -> { assertTrue(displayContent.isKeyguardLocked()); - // Only default display supports AOD. - if (displayContent.isDefaultDisplay) { - assertTrue(displayContent.isAodShowing()); - } else { - assertFalse(displayContent.isAodShowing()); - } + assertTrue(displayContent.isAodShowing()); }); // Check setLockScreenShown unlocking both displays |