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