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 17package com.android.server.am; 18 19import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; 20import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; 21import static android.view.Display.DEFAULT_DISPLAY; 22import static android.view.Display.INVALID_DISPLAY; 23import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS; 24import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE; 25import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER; 26import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; 27import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; 28import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS; 29import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; 30import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; 31import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; 32import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_GOING_AWAY; 33import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_OCCLUDE; 34import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_UNOCCLUDE; 35import static com.android.server.wm.AppTransition.TRANSIT_UNSET; 36 37import android.app.ActivityManagerInternal.SleepToken; 38import android.os.IBinder; 39import android.os.RemoteException; 40import android.os.Trace; 41import android.util.Slog; 42 43import com.android.internal.policy.IKeyguardDismissCallback; 44import com.android.server.wm.WindowManagerService; 45 46import java.io.PrintWriter; 47import 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 */ 55class 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