Home | History | Annotate | Download | only in am
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 
     17 package com.android.server.am;
     18 
     19 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
     20 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
     21 import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
     22 import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
     23 import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
     24 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
     25 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
     26 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
     27 import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
     28 import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
     29 import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
     30 import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_GOING_AWAY;
     31 import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_OCCLUDE;
     32 import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_UNOCCLUDE;
     33 import static com.android.server.wm.AppTransition.TRANSIT_NONE;
     34 import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
     35 
     36 import android.os.IBinder;
     37 import android.os.RemoteException;
     38 import android.os.Trace;
     39 import android.util.Slog;
     40 
     41 import com.android.internal.policy.IKeyguardDismissCallback;
     42 import com.android.server.wm.WindowManagerService;
     43 
     44 import java.io.PrintWriter;
     45 import java.util.ArrayList;
     46 
     47 /**
     48  * Controls Keyguard occluding, dismissing and transitions depending on what kind of activities are
     49  * currently visible.
     50  * <p>
     51  * Note that everything in this class should only be accessed with the AM lock being held.
     52  */
     53 class KeyguardController {
     54 
     55     private static final String TAG = TAG_WITH_CLASS_NAME ? "KeyguardController" : TAG_AM;
     56 
     57     private final ActivityManagerService mService;
     58     private final ActivityStackSupervisor mStackSupervisor;
     59     private WindowManagerService mWindowManager;
     60     private boolean mKeyguardShowing;
     61     private boolean mKeyguardGoingAway;
     62     private boolean mOccluded;
     63     private boolean mDismissalRequested;
     64     private ActivityRecord mDismissingKeyguardActivity;
     65     private int mBeforeUnoccludeTransit;
     66     private int mVisibilityTransactionDepth;
     67 
     68     KeyguardController(ActivityManagerService service,
     69             ActivityStackSupervisor stackSupervisor) {
     70         mService = service;
     71         mStackSupervisor = stackSupervisor;
     72     }
     73 
     74     void setWindowManager(WindowManagerService windowManager) {
     75         mWindowManager = windowManager;
     76     }
     77 
     78     /**
     79      * @return true if Keyguard is showing, not going away, and not being occluded, false otherwise
     80      */
     81     boolean isKeyguardShowing() {
     82         return mKeyguardShowing && !mKeyguardGoingAway && !mOccluded;
     83     }
     84 
     85     /**
     86      * @return true if Keyguard is either showing or occluded, but not going away
     87      */
     88     boolean isKeyguardLocked() {
     89         return mKeyguardShowing && !mKeyguardGoingAway;
     90     }
     91 
     92     /**
     93      * Update the Keyguard showing state.
     94      */
     95     void setKeyguardShown(boolean showing) {
     96         if (showing == mKeyguardShowing) {
     97             return;
     98         }
     99         mKeyguardShowing = showing;
    100         dismissDockedStackIfNeeded();
    101         if (showing) {
    102             setKeyguardGoingAway(false);
    103             mDismissalRequested = false;
    104         }
    105         mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
    106         mService.updateSleepIfNeededLocked();
    107     }
    108 
    109     /**
    110      * Called when Keyguard is going away.
    111      *
    112      * @param flags See {@link android.view.WindowManagerPolicy#KEYGUARD_GOING_AWAY_FLAG_TO_SHADE}
    113      *              etc.
    114      */
    115     void keyguardGoingAway(int flags) {
    116         if (!mKeyguardShowing) {
    117             return;
    118         }
    119         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway");
    120         mWindowManager.deferSurfaceLayout();
    121         try {
    122             setKeyguardGoingAway(true);
    123             mWindowManager.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY,
    124                     false /* alwaysKeepCurrent */, convertTransitFlags(flags),
    125                     false /* forceOverride */);
    126             mService.updateSleepIfNeededLocked();
    127 
    128             // Some stack visibility might change (e.g. docked stack)
    129             mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
    130             mStackSupervisor.addStartingWindowsForVisibleActivities(true /* taskSwitch */);
    131             mWindowManager.executeAppTransition();
    132         } finally {
    133             Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway: surfaceLayout");
    134             mWindowManager.continueSurfaceLayout();
    135             Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
    136 
    137             Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
    138         }
    139     }
    140 
    141     void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback) {
    142         final ActivityRecord activityRecord = ActivityRecord.forTokenLocked(token);
    143         if (activityRecord == null || !activityRecord.visibleIgnoringKeyguard) {
    144             failCallback(callback);
    145             return;
    146         }
    147         mWindowManager.dismissKeyguard(callback);
    148     }
    149 
    150     private void setKeyguardGoingAway(boolean keyguardGoingAway) {
    151         mKeyguardGoingAway = keyguardGoingAway;
    152         mWindowManager.setKeyguardGoingAway(keyguardGoingAway);
    153     }
    154 
    155     private void failCallback(IKeyguardDismissCallback callback) {
    156         try {
    157             callback.onDismissError();
    158         } catch (RemoteException e) {
    159             Slog.w(TAG, "Failed to call callback", e);
    160         }
    161     }
    162 
    163     private int convertTransitFlags(int keyguardGoingAwayFlags) {
    164         int result = 0;
    165         if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_TO_SHADE) != 0) {
    166             result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
    167         }
    168         if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS) != 0) {
    169             result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
    170         }
    171         if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER) != 0) {
    172             result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
    173         }
    174         return result;
    175     }
    176 
    177     /**
    178      * Starts a batch of visibility updates.
    179      */
    180     void beginActivityVisibilityUpdate() {
    181         mVisibilityTransactionDepth++;
    182     }
    183 
    184     /**
    185      * Ends a batch of visibility updates. After all batches are done, this method makes sure to
    186      * update lockscreen occluded/dismiss state if needed.
    187      */
    188     void endActivityVisibilityUpdate() {
    189         mVisibilityTransactionDepth--;
    190         if (mVisibilityTransactionDepth == 0) {
    191             visibilitiesUpdated();
    192         }
    193     }
    194 
    195     /**
    196      * @return True if we may show an activity while Keyguard is showing because we are in the
    197      *         process of dismissing it anyways, false otherwise.
    198      */
    199     boolean canShowActivityWhileKeyguardShowing(ActivityRecord r, boolean dismissKeyguard) {
    200 
    201         // Allow to show it when we are about to dismiss Keyguard. This isn't allowed if r is
    202         // already the dismissing activity, in which case we don't allow it to repeatedly dismiss
    203         // Keyguard.
    204         return dismissKeyguard && canDismissKeyguard() &&
    205                 (mDismissalRequested || r != mDismissingKeyguardActivity);
    206     }
    207 
    208     /**
    209      * @return True if we may show an activity while Keyguard is occluded, false otherwise.
    210      */
    211     boolean canShowWhileOccluded(boolean dismissKeyguard, boolean showWhenLocked) {
    212         return showWhenLocked || dismissKeyguard && !mWindowManager.isKeyguardSecure();
    213     }
    214 
    215     private void visibilitiesUpdated() {
    216         final boolean lastOccluded = mOccluded;
    217         final ActivityRecord lastDismissingKeyguardActivity = mDismissingKeyguardActivity;
    218         mOccluded = false;
    219         mDismissingKeyguardActivity = null;
    220         final ArrayList<ActivityStack> stacks = mStackSupervisor.getStacksOnDefaultDisplay();
    221         for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
    222             final ActivityStack stack = stacks.get(stackNdx);
    223 
    224             // Only the focused stack top activity may control occluded state
    225             if (mStackSupervisor.isFocusedStack(stack)) {
    226 
    227                 // A dismissing activity occludes Keyguard in the insecure case for legacy reasons.
    228                 final ActivityRecord topDismissing = stack.getTopDismissingKeyguardActivity();
    229                 mOccluded = stack.topActivityOccludesKeyguard()
    230                         || (topDismissing != null
    231                                 && stack.topRunningActivityLocked() == topDismissing
    232                                 && canShowWhileOccluded(true /* dismissKeyguard */,
    233                                         false /* showWhenLocked */));
    234             }
    235             if (mDismissingKeyguardActivity == null
    236                     && stack.getTopDismissingKeyguardActivity() != null) {
    237                 mDismissingKeyguardActivity = stack.getTopDismissingKeyguardActivity();
    238             }
    239         }
    240         mOccluded |= mWindowManager.isShowingDream();
    241         if (mOccluded != lastOccluded) {
    242             handleOccludedChanged();
    243         }
    244         if (mDismissingKeyguardActivity != lastDismissingKeyguardActivity) {
    245             handleDismissKeyguard();
    246         }
    247     }
    248 
    249     /**
    250      * Called when occluded state changed.
    251      */
    252     private void handleOccludedChanged() {
    253         mWindowManager.onKeyguardOccludedChanged(mOccluded);
    254         if (isKeyguardLocked()) {
    255             mWindowManager.deferSurfaceLayout();
    256             try {
    257                 mWindowManager.prepareAppTransition(resolveOccludeTransit(),
    258                         false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */);
    259                 mService.updateSleepIfNeededLocked();
    260                 mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
    261                 mWindowManager.executeAppTransition();
    262             } finally {
    263                 mWindowManager.continueSurfaceLayout();
    264             }
    265         }
    266         dismissDockedStackIfNeeded();
    267     }
    268 
    269     /**
    270      * Called when somebody might want to dismiss the Keyguard.
    271      */
    272     private void handleDismissKeyguard() {
    273         // We only allow dismissing Keyguard via the flag when Keyguard is secure for legacy
    274         // reasons, because that's how apps used to dismiss Keyguard in the secure case. In the
    275         // insecure case, we actually show it on top of the lockscreen. See #canShowWhileOccluded.
    276         if (!mOccluded && mDismissingKeyguardActivity != null
    277                 && mWindowManager.isKeyguardSecure()) {
    278             mWindowManager.dismissKeyguard(null /* callback */);
    279             mDismissalRequested = true;
    280 
    281             // If we are about to unocclude the Keyguard, but we can dismiss it without security,
    282             // we immediately dismiss the Keyguard so the activity gets shown without a flicker.
    283             if (mKeyguardShowing && canDismissKeyguard()
    284                     && mWindowManager.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) {
    285                 mWindowManager.prepareAppTransition(mBeforeUnoccludeTransit,
    286                         false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */);
    287                 mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
    288                 mWindowManager.executeAppTransition();
    289             }
    290         }
    291     }
    292 
    293     /**
    294      * @return true if Keyguard can be currently dismissed without entering credentials.
    295      */
    296     boolean canDismissKeyguard() {
    297         return mWindowManager.isKeyguardTrusted() || !mWindowManager.isKeyguardSecure();
    298     }
    299 
    300     private int resolveOccludeTransit() {
    301         if (mBeforeUnoccludeTransit != TRANSIT_UNSET
    302                 && mWindowManager.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE
    303                 && mOccluded) {
    304 
    305             // Reuse old transit in case we are occluding Keyguard again, meaning that we never
    306             // actually occclude/unocclude Keyguard, but just run a normal transition.
    307             return mBeforeUnoccludeTransit;
    308         } else if (!mOccluded) {
    309 
    310             // Save transit in case we dismiss/occlude Keyguard shortly after.
    311             mBeforeUnoccludeTransit = mWindowManager.getPendingAppTransition();
    312             return TRANSIT_KEYGUARD_UNOCCLUDE;
    313         } else {
    314             return TRANSIT_KEYGUARD_OCCLUDE;
    315         }
    316     }
    317 
    318     private void dismissDockedStackIfNeeded() {
    319         if (mKeyguardShowing && mOccluded) {
    320             // The lock screen is currently showing, but is occluded by a window that can
    321             // show on top of the lock screen. In this can we want to dismiss the docked
    322             // stack since it will be complicated/risky to try to put the activity on top
    323             // of the lock screen in the right fullscreen configuration.
    324             mStackSupervisor.moveTasksToFullscreenStackLocked(DOCKED_STACK_ID,
    325                     mStackSupervisor.mFocusedStack.getStackId() == DOCKED_STACK_ID);
    326         }
    327     }
    328 
    329     void dump(PrintWriter pw, String prefix) {
    330         pw.println(prefix + "KeyguardController:");
    331         pw.println(prefix + "  mKeyguardShowing=" + mKeyguardShowing);
    332         pw.println(prefix + "  mKeyguardGoingAway=" + mKeyguardGoingAway);
    333         pw.println(prefix + "  mOccluded=" + mOccluded);
    334         pw.println(prefix + "  mDismissingKeyguardActivity=" + mDismissingKeyguardActivity);
    335         pw.println(prefix + "  mDismissalRequested=" + mDismissalRequested);
    336         pw.println(prefix + "  mVisibilityTransactionDepth=" + mVisibilityTransactionDepth);
    337     }
    338 }
    339