All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.android.server.wm.KeyguardController Maven / Gradle / Ivy

/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package com.android.server.wm;

import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
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_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_UNSET;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
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.am.KeyguardControllerProto.AOD_SHOWING;
import static com.android.server.am.KeyguardControllerProto.KEYGUARD_OCCLUDED_STATES;
import static com.android.server.am.KeyguardControllerProto.KEYGUARD_SHOWING;
import static com.android.server.am.KeyguardOccludedProto.DISPLAY_ID;
import static com.android.server.am.KeyguardOccludedProto.KEYGUARD_OCCLUDED;
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;

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 com.android.internal.policy.IKeyguardDismissCallback;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;

import java.io.PrintWriter;

/**
 * Controls Keyguard occluding, dismissing and transitions depending on what kind of activities are
 * currently visible.
 * 

* 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 final ActivityStackSupervisor mStackSupervisor; private WindowManagerService mWindowManager; private boolean mKeyguardShowing; private boolean mAodShowing; private boolean mKeyguardGoingAway; private boolean mDismissalRequested; private int[] mSecondaryDisplayIdsShowing; private int mBeforeUnoccludeTransit; private int mVisibilityTransactionDepth; private final SparseArray mDisplayStates = new SparseArray<>(); private final ActivityTaskManagerService mService; private RootActivityContainer mRootActivityContainer; KeyguardController(ActivityTaskManagerService service, ActivityStackSupervisor stackSupervisor) { mService = service; mStackSupervisor = stackSupervisor; } void setWindowManager(WindowManagerService windowManager) { mWindowManager = windowManager; mRootActivityContainer = mService.mRootActivityContainer; } /** * @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 (mKeyguardShowing || mAodShowing) && !mKeyguardGoingAway && !isDisplayOccluded(displayId); } /** * @return {@code true} for default display when AOD is showing. Otherwise, same as * {@link #isKeyguardOrAodShowing(int)} * TODO(b/125198167): Replace isKeyguardOrAodShowing() by this logic. */ boolean isKeyguardUnoccludedOrAodShowing(int displayId) { if (displayId == DEFAULT_DISPLAY && mAodShowing) { return true; } return isKeyguardOrAodShowing(displayId); } /** * @return true if Keyguard is showing, not going away, and not being occluded on the given * display, false otherwise */ boolean isKeyguardShowing(int displayId) { return mKeyguardShowing && !mKeyguardGoingAway && !isDisplayOccluded(displayId); } /** * @return true if Keyguard is either showing or occluded, but not going away */ boolean isKeyguardLocked() { return mKeyguardShowing && !mKeyguardGoingAway; } /** * @return {@code true} if the keyguard is going away, {@code false} otherwise. */ boolean isKeyguardGoingAway() { // Also check keyguard showing in case value is stale. return mKeyguardGoingAway && mKeyguardShowing; } /** * Update the Keyguard showing state. */ void setKeyguardShown(boolean keyguardShowing, boolean aodShowing) { // If keyguard is going away, but SystemUI aborted the transition, need to reset state. final boolean keyguardChanged = keyguardShowing != mKeyguardShowing || mKeyguardGoingAway && keyguardShowing; final boolean aodChanged = aodShowing != mAodShowing; if (!keyguardChanged && !aodChanged) { return; } mKeyguardShowing = keyguardShowing; mAodShowing = aodShowing; mWindowManager.setAodShowing(aodShowing); if (keyguardChanged) { // Irrelevant to AOD. dismissDockedStackIfNeeded(); setKeyguardGoingAway(false); if (keyguardShowing) { mDismissalRequested = false; } } // TODO(b/113840485): Check usage for non-default display mWindowManager.setKeyguardOrAodShowingOnDefaultDisplay( isKeyguardOrAodShowing(DEFAULT_DISPLAY)); // Update the sleep token first such that ensureActivitiesVisible has correct sleep token // state when evaluating visibilities. updateKeyguardSleepToken(); mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); } /** * Called when Keyguard is going away. * * @param flags See {@link WindowManagerPolicy#KEYGUARD_GOING_AWAY_FLAG_TO_SHADE} * etc. */ void keyguardGoingAway(int flags) { if (!mKeyguardShowing) { return; } Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway"); mWindowManager.deferSurfaceLayout(); try { setKeyguardGoingAway(true); mRootActivityContainer.getDefaultDisplay().mDisplayContent .prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */, convertTransitFlags(flags), false /* forceOverride */); updateKeyguardSleepToken(); // Some stack visibility might change (e.g. docked stack) mRootActivityContainer.resumeFocusedStacksTopActivities(); mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); mRootActivityContainer.addStartingWindowsForVisibleActivities( true /* taskSwitch */); mWindowManager.executeAppTransition(); } finally { Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway: surfaceLayout"); mWindowManager.continueSurfaceLayout(); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } } void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback, CharSequence 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 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()) { mStackSupervisor.wakeUp("dismissKeyguard"); } mWindowManager.dismissKeyguard(callback, message); } private void setKeyguardGoingAway(boolean keyguardGoingAway) { mKeyguardGoingAway = keyguardGoingAway; mWindowManager.setKeyguardGoingAway(keyguardGoingAway); } private void failCallback(IKeyguardDismissCallback callback) { try { callback.onDismissError(); } catch (RemoteException e) { Slog.w(TAG, "Failed to call callback", e); } } private int convertTransitFlags(int keyguardGoingAwayFlags) { int result = 0; if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_TO_SHADE) != 0) { result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; } if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS) != 0) { result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; } if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER) != 0) { result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; } return result; } /** * Starts a batch of visibility updates. */ void beginActivityVisibilityUpdate() { mVisibilityTransactionDepth++; } /** * Ends a batch of visibility updates. After all batches are done, this method makes sure to * update lockscreen occluded/dismiss state if needed. */ void endActivityVisibilityUpdate() { mVisibilityTransactionDepth--; if (mVisibilityTransactionDepth == 0) { visibilitiesUpdated(); } } /** * @return True if we may show an activity while Keyguard is showing because we are in the * process of dismissing it anyways, false otherwise. */ boolean canShowActivityWhileKeyguardShowing(ActivityRecord r, boolean dismissKeyguard) { // 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. return dismissKeyguard && canDismissKeyguard() && !mAodShowing && (mDismissalRequested || (r.canShowWhenLocked() && getDisplay(r.getDisplayId()).mDismissingKeyguardActivity != r)); } /** * @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()); } private void visibilitiesUpdated() { boolean requestDismissKeyguard = false; for (int displayNdx = mRootActivityContainer.getChildCount() - 1; displayNdx >= 0; displayNdx--) { final ActivityDisplay display = mRootActivityContainer.getChildAt(displayNdx); final KeyguardDisplayState state = getDisplay(display.mDisplayId); state.visibilitiesUpdated(this, display); requestDismissKeyguard |= state.mRequestDismissKeyguard; } // Dismissing Keyguard happens globally using the information from all displays. if (requestDismissKeyguard) { handleDismissKeyguard(); } } /** * Called when occluded state changed. */ private void handleOccludedChanged(int displayId) { // 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 keygaurd sleep token on that display. if (displayId != DEFAULT_DISPLAY) { updateKeyguardSleepToken(displayId); return; } mWindowManager.onKeyguardOccludedChanged(isDisplayOccluded(DEFAULT_DISPLAY)); if (isKeyguardLocked()) { mWindowManager.deferSurfaceLayout(); try { mRootActivityContainer.getDefaultDisplay().mDisplayContent .prepareAppTransition(resolveOccludeTransit(), false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */); updateKeyguardSleepToken(DEFAULT_DISPLAY); mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); mWindowManager.executeAppTransition(); } finally { mWindowManager.continueSurfaceLayout(); } } dismissDockedStackIfNeeded(); } /** * Called when somebody wants to dismiss the Keyguard via the flag. */ private void handleDismissKeyguard() { // 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; } mWindowManager.dismissKeyguard(null /* callback */, null /* message */); mDismissalRequested = true; // 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 = mRootActivityContainer.getDefaultDisplay().mDisplayContent; if (mKeyguardShowing && canDismissKeyguard() && dc.mAppTransition.getAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) { dc.prepareAppTransition(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */); mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); mWindowManager.executeAppTransition(); } } private boolean isDisplayOccluded(int displayId) { return getDisplay(displayId).mOccluded; } /** * @return true if Keyguard can be currently dismissed without entering credentials. */ boolean canDismissKeyguard() { return mWindowManager.isKeyguardTrusted() || !mWindowManager.isKeyguardSecure(mService.getCurrentUserId()); } private int resolveOccludeTransit() { final DisplayContent dc = mService.mRootActivityContainer.getDefaultDisplay().mDisplayContent; if (mBeforeUnoccludeTransit != TRANSIT_UNSET && dc.mAppTransition.getAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE // TODO(b/113840485): Handle app transition for individual display. && isDisplayOccluded(DEFAULT_DISPLAY)) { // Reuse old transit in case we are occluding Keyguard again, meaning that we never // actually occclude/unocclude Keyguard, but just run a normal transition. return mBeforeUnoccludeTransit; // TODO(b/113840485): Handle app transition for individual display. } else if (!isDisplayOccluded(DEFAULT_DISPLAY)) { // Save transit in case we dismiss/occlude Keyguard shortly after. mBeforeUnoccludeTransit = dc.mAppTransition.getAppTransition(); return TRANSIT_KEYGUARD_UNOCCLUDE; } else { return TRANSIT_KEYGUARD_OCCLUDE; } } private void dismissDockedStackIfNeeded() { // TODO(b/113840485): Handle docked stack for individual display. if (mKeyguardShowing && isDisplayOccluded(DEFAULT_DISPLAY)) { // The lock screen is currently showing, but is occluded by a window that can // show on top of the lock screen. In this can we want to dismiss the docked // stack since it will be complicated/risky to try to put the activity on top // of the lock screen in the right fullscreen configuration. final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().getSplitScreenPrimaryStack(); if (stack == null) { return; } mStackSupervisor.moveTasksToFullscreenStackLocked(stack, stack.isFocusedStackOnDisplay()); } } private void updateKeyguardSleepToken() { for (int displayNdx = mRootActivityContainer.getChildCount() - 1; displayNdx >= 0; displayNdx--) { final ActivityDisplay display = mRootActivityContainer.getChildAt(displayNdx); updateKeyguardSleepToken(display.mDisplayId); } } private void updateKeyguardSleepToken(int displayId) { final KeyguardDisplayState state = getDisplay(displayId); if (isKeyguardUnoccludedOrAodShowing(displayId) && state.mSleepToken == null) { state.acquiredSleepToken(); } else if (!isKeyguardUnoccludedOrAodShowing(displayId) && state.mSleepToken != null) { state.releaseSleepToken(); } } private KeyguardDisplayState getDisplay(int displayId) { KeyguardDisplayState state = mDisplayStates.get(displayId); if (state == null) { state = new KeyguardDisplayState(mService, displayId); mDisplayStates.append(displayId, state); } return state; } void onDisplayRemoved(int displayId) { final KeyguardDisplayState state = mDisplayStates.get(displayId); if (state != null) { state.onRemoved(); mDisplayStates.remove(displayId); } } /** Represents Keyguard state per individual display. */ private static class KeyguardDisplayState { private final int mDisplayId; private boolean mOccluded; private ActivityRecord mDismissingKeyguardActivity; private boolean mRequestDismissKeyguard; private final ActivityTaskManagerService mService; private SleepToken mSleepToken; KeyguardDisplayState(ActivityTaskManagerService service, int displayId) { mService = service; mDisplayId = displayId; } void onRemoved() { mDismissingKeyguardActivity = null; releaseSleepToken(); } void acquiredSleepToken() { if (mSleepToken == null) { mSleepToken = mService.acquireSleepToken("keyguard", mDisplayId); } } void releaseSleepToken() { if (mSleepToken != null) { mSleepToken.release(); mSleepToken = null; } } void visibilitiesUpdated(KeyguardController controller, ActivityDisplay display) { final boolean lastOccluded = mOccluded; final ActivityRecord lastDismissActivity = mDismissingKeyguardActivity; mRequestDismissKeyguard = false; mOccluded = false; mDismissingKeyguardActivity = null; final ActivityStack stack = getStackForControllingOccluding(display); if (stack != null) { final ActivityRecord topDismissing = stack.getTopDismissingKeyguardActivity(); mOccluded = stack.topActivityOccludesKeyguard() || (topDismissing != null && stack.topRunningActivityLocked() == topDismissing && controller.canShowWhileOccluded( true /* dismissKeyguard */, false /* showWhenLocked */)); if (stack.getTopDismissingKeyguardActivity() != null) { mDismissingKeyguardActivity = stack.getTopDismissingKeyguardActivity(); } // FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD only apply for secondary display. if (mDisplayId != DEFAULT_DISPLAY) { mOccluded |= stack.canShowWithInsecureKeyguard() && controller.canDismissKeyguard(); } } // TODO(b/123372519): isShowingDream can only works on default display. if (mDisplayId == DEFAULT_DISPLAY) { mOccluded |= controller.mWindowManager.isShowingDream(); } if (lastOccluded != mOccluded) { controller.handleOccludedChanged(mDisplayId); } if (lastDismissActivity != mDismissingKeyguardActivity && !mOccluded && mDismissingKeyguardActivity != null && controller.mWindowManager.isKeyguardSecure( controller.mService.getCurrentUserId())) { mRequestDismissKeyguard = true; } } /** * Gets the stack used to check the occluded state. *

* Only the top non-pinned activity of the focusable stack on each display can control its * occlusion state. */ private ActivityStack getStackForControllingOccluding(ActivityDisplay display) { for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { final ActivityStack stack = display.getChildAt(stackNdx); if (stack != null && stack.isFocusableAndVisible() && !stack.inPinnedWindowingMode()) { return stack; } } return null; } void dumpStatus(PrintWriter pw, String prefix) { final StringBuilder sb = new StringBuilder(); sb.append(prefix); sb.append(" Occluded=").append(mOccluded) .append(" DismissingKeyguardActivity=") .append(mDismissingKeyguardActivity) .append(" at display=") .append(mDisplayId); pw.println(sb.toString()); } void writeToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); proto.write(DISPLAY_ID, mDisplayId); proto.write(KEYGUARD_OCCLUDED, mOccluded); proto.end(token); } } void dump(PrintWriter pw, String prefix) { pw.println(prefix + "KeyguardController:"); pw.println(prefix + " mKeyguardShowing=" + mKeyguardShowing); pw.println(prefix + " mAodShowing=" + mAodShowing); pw.println(prefix + " mKeyguardGoingAway=" + mKeyguardGoingAway); dumpDisplayStates(pw, prefix); pw.println(prefix + " mDismissalRequested=" + mDismissalRequested); pw.println(prefix + " mVisibilityTransactionDepth=" + mVisibilityTransactionDepth); } void writeToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); proto.write(AOD_SHOWING, mAodShowing); proto.write(KEYGUARD_SHOWING, mKeyguardShowing); writeDisplayStatesToProto(proto, KEYGUARD_OCCLUDED_STATES); 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).writeToProto(proto, fieldId); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy