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 KeyguardController(ActivityManagerService service, ActivityStackSupervisor stackSupervisor)68 KeyguardController(ActivityManagerService service, 69 ActivityStackSupervisor stackSupervisor) { 70 mService = service; 71 mStackSupervisor = stackSupervisor; 72 } 73 setWindowManager(WindowManagerService windowManager)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 */ isKeyguardShowing()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 */ isKeyguardLocked()88 boolean isKeyguardLocked() { 89 return mKeyguardShowing && !mKeyguardGoingAway; 90 } 91 92 /** 93 * Update the Keyguard showing state. 94 */ setKeyguardShown(boolean showing)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 */ keyguardGoingAway(int flags)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 dismissKeyguard(IBinder token, IKeyguardDismissCallback callback)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 setKeyguardGoingAway(boolean keyguardGoingAway)150 private void setKeyguardGoingAway(boolean keyguardGoingAway) { 151 mKeyguardGoingAway = keyguardGoingAway; 152 mWindowManager.setKeyguardGoingAway(keyguardGoingAway); 153 } 154 failCallback(IKeyguardDismissCallback callback)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 convertTransitFlags(int keyguardGoingAwayFlags)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 */ beginActivityVisibilityUpdate()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 */ endActivityVisibilityUpdate()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 */ canShowActivityWhileKeyguardShowing(ActivityRecord r, boolean dismissKeyguard)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 */ canShowWhileOccluded(boolean dismissKeyguard, boolean showWhenLocked)211 boolean canShowWhileOccluded(boolean dismissKeyguard, boolean showWhenLocked) { 212 return showWhenLocked || dismissKeyguard && !mWindowManager.isKeyguardSecure(); 213 } 214 visibilitiesUpdated()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 */ handleOccludedChanged()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 */ handleDismissKeyguard()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 */ canDismissKeyguard()296 boolean canDismissKeyguard() { 297 return mWindowManager.isKeyguardTrusted() || !mWindowManager.isKeyguardSecure(); 298 } 299 resolveOccludeTransit()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 dismissDockedStackIfNeeded()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 dump(PrintWriter pw, String prefix)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