RecentsAnimation.java revision ba40d3a107d57fb40af413d9118f8261dc362139
1/* 2 * Copyright (C) 2018 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.START_TASK_TO_FRONT; 20import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE; 21import static android.app.AppOpsManager.OP_NONE; 22import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; 23import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; 24import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; 25import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; 26import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION; 27import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; 28import static android.view.WindowManager.TRANSIT_NONE; 29import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS; 30import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; 31import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION; 32import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_TOP; 33 34import android.app.ActivityOptions; 35import android.app.AppOpsManager; 36import android.app.IAssistDataReceiver; 37import android.content.ComponentName; 38import android.content.Context; 39import android.content.Intent; 40import android.os.RemoteException; 41import android.os.Trace; 42import android.util.Slog; 43import android.view.IRecentsAnimationRunner; 44import com.android.server.wm.RecentsAnimationController; 45import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks; 46import com.android.server.wm.WindowManagerService; 47 48/** 49 * Manages the recents animation, including the reordering of the stacks for the transition and 50 * cleanup. See {@link com.android.server.wm.RecentsAnimationController}. 51 */ 52class RecentsAnimation implements RecentsAnimationCallbacks, 53 ActivityDisplay.OnStackOrderChangedListener { 54 private static final String TAG = RecentsAnimation.class.getSimpleName(); 55 // TODO (b/73188263): Reset debugging flags 56 private static final boolean DEBUG = true; 57 58 private final ActivityManagerService mService; 59 private final ActivityStackSupervisor mStackSupervisor; 60 private final ActivityStartController mActivityStartController; 61 private final WindowManagerService mWindowManager; 62 private final UserController mUserController; 63 private final ActivityDisplay mDefaultDisplay; 64 private final int mCallingPid; 65 66 private int mTargetActivityType; 67 private AssistDataRequester mAssistDataRequester; 68 69 // The stack to restore the target stack behind when the animation is finished 70 private ActivityStack mRestoreTargetBehindStack; 71 72 RecentsAnimation(ActivityManagerService am, ActivityStackSupervisor stackSupervisor, 73 ActivityStartController activityStartController, WindowManagerService wm, 74 UserController userController, int callingPid) { 75 mService = am; 76 mStackSupervisor = stackSupervisor; 77 mDefaultDisplay = stackSupervisor.getDefaultDisplay(); 78 mActivityStartController = activityStartController; 79 mWindowManager = wm; 80 mUserController = userController; 81 mCallingPid = callingPid; 82 } 83 84 void startRecentsActivity(Intent intent, IRecentsAnimationRunner recentsAnimationRunner, 85 ComponentName recentsComponent, int recentsUid, 86 IAssistDataReceiver assistDataReceiver) { 87 if (DEBUG) Slog.d(TAG, "startRecentsActivity(): intent=" + intent 88 + " assistDataReceiver=" + assistDataReceiver); 89 Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "RecentsAnimation#startRecentsActivity"); 90 91 if (!mWindowManager.canStartRecentsAnimation()) { 92 notifyAnimationCancelBeforeStart(recentsAnimationRunner); 93 if (DEBUG) Slog.d(TAG, "Can't start recents animation, nextAppTransition=" 94 + mWindowManager.getPendingAppTransition()); 95 return; 96 } 97 98 // If the activity is associated with the recents stack, then try and get that first 99 mTargetActivityType = intent.getComponent() != null 100 && recentsComponent.equals(intent.getComponent()) 101 ? ACTIVITY_TYPE_RECENTS 102 : ACTIVITY_TYPE_HOME; 103 final ActivityStack targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED, 104 mTargetActivityType); 105 ActivityRecord targetActivity = getTargetActivity(targetStack, intent.getComponent()); 106 final boolean hasExistingActivity = targetActivity != null; 107 if (hasExistingActivity) { 108 final ActivityDisplay display = targetActivity.getDisplay(); 109 mRestoreTargetBehindStack = display.getStackAbove(targetStack); 110 if (mRestoreTargetBehindStack == null) { 111 notifyAnimationCancelBeforeStart(recentsAnimationRunner); 112 if (DEBUG) Slog.d(TAG, "No stack above target stack=" + targetStack); 113 return; 114 } 115 } 116 117 // Send launch hint if we are actually launching the target. If it's already visible 118 // (shouldn't happen in general) we don't need to send it. 119 if (targetActivity == null || !targetActivity.visible) { 120 mStackSupervisor.sendPowerHintForLaunchStartIfNeeded(true /* forceSend */, 121 targetActivity); 122 } 123 124 mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching(); 125 126 mService.setRunningRemoteAnimation(mCallingPid, true); 127 128 mWindowManager.deferSurfaceLayout(); 129 try { 130 // Kick off the assist data request in the background before showing the target activity 131 if (assistDataReceiver != null) { 132 final AppOpsManager appOpsManager = (AppOpsManager) 133 mService.mContext.getSystemService(Context.APP_OPS_SERVICE); 134 final AssistDataReceiverProxy proxy = new AssistDataReceiverProxy( 135 assistDataReceiver, recentsComponent.getPackageName()); 136 mAssistDataRequester = new AssistDataRequester(mService.mContext, mService, 137 mWindowManager, appOpsManager, proxy, this, OP_ASSIST_STRUCTURE, OP_NONE); 138 mAssistDataRequester.requestAssistData(mStackSupervisor.getTopVisibleActivities(), 139 true /* fetchData */, false /* fetchScreenshots */, 140 true /* allowFetchData */, false /* allowFetchScreenshots */, 141 recentsUid, recentsComponent.getPackageName()); 142 } 143 144 if (hasExistingActivity) { 145 // Move the recents activity into place for the animation if it is not top most 146 mDefaultDisplay.moveStackBehindBottomMostVisibleStack(targetStack); 147 if (DEBUG) Slog.d(TAG, "Moved stack=" + targetStack + " behind stack=" 148 + mDefaultDisplay.getStackAbove(targetStack)); 149 150 // If there are multiple tasks in the target stack (ie. the home stack, with 3p 151 // and default launchers coexisting), then move the task to the top as a part of 152 // moving the stack to the front 153 if (targetStack.topTask() != targetActivity.getTask()) { 154 targetStack.addTask(targetActivity.getTask(), true /* toTop */, 155 "startRecentsActivity"); 156 } 157 } else { 158 // No recents activity 159 ActivityOptions options = ActivityOptions.makeBasic(); 160 options.setLaunchActivityType(mTargetActivityType); 161 options.setAvoidMoveToFront(); 162 intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION); 163 164 mActivityStartController 165 .obtainStarter(intent, "startRecentsActivity_noTargetActivity") 166 .setCallingUid(recentsUid) 167 .setCallingPackage(recentsComponent.getPackageName()) 168 .setActivityOptions(SafeActivityOptions.fromBundle(options.toBundle())) 169 .setMayWait(mUserController.getCurrentUserId()) 170 .execute(); 171 mWindowManager.prepareAppTransition(TRANSIT_NONE, false); 172 mWindowManager.executeAppTransition(); 173 174 targetActivity = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED, 175 mTargetActivityType).getTopActivity(); 176 177 // TODO: Maybe wait for app to draw in this particular case? 178 179 if (DEBUG) Slog.d(TAG, "Started intent=" + intent); 180 } 181 182 // Mark the target activity as launch-behind to bump its visibility for the 183 // duration of the gesture that is driven by the recents component 184 targetActivity.mLaunchTaskBehind = true; 185 186 // Fetch all the surface controls and pass them to the client to get the animation 187 // started. Cancel any existing recents animation running synchronously (do not hold the 188 // WM lock) 189 mWindowManager.cancelRecentsAnimationSynchronously(REORDER_MOVE_TO_ORIGINAL_POSITION, 190 "startRecentsActivity"); 191 mWindowManager.initializeRecentsAnimation(mTargetActivityType, recentsAnimationRunner, 192 this, mDefaultDisplay.mDisplayId, 193 mStackSupervisor.mRecentTasks.getRecentTaskIds()); 194 195 // If we updated the launch-behind state, update the visibility of the activities after 196 // we fetch the visible tasks to be controlled by the animation 197 mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS); 198 199 mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunched(START_TASK_TO_FRONT, 200 targetActivity); 201 202 // Register for stack order changes 203 mDefaultDisplay.registerStackOrderChangedListener(this); 204 } catch (Exception e) { 205 Slog.e(TAG, "Failed to start recents activity", e); 206 throw e; 207 } finally { 208 mWindowManager.continueSurfaceLayout(); 209 Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); 210 } 211 } 212 213 private void finishAnimation(@RecentsAnimationController.ReorderMode int reorderMode) { 214 synchronized (mService) { 215 if (DEBUG) Slog.d(TAG, "onAnimationFinished(): controller=" 216 + mWindowManager.getRecentsAnimationController() 217 + " reorderMode=" + reorderMode); 218 219 // Cancel the associated assistant data request 220 if (mAssistDataRequester != null) { 221 mAssistDataRequester.cancel(); 222 mAssistDataRequester = null; 223 } 224 225 // Unregister for stack order changes 226 mDefaultDisplay.unregisterStackOrderChangedListener(this); 227 228 if (mWindowManager.getRecentsAnimationController() == null) return; 229 230 // Just to be sure end the launch hint in case the target activity was never launched. 231 // However, if we're keeping the activity and making it visible, we can leave it on. 232 if (reorderMode != REORDER_KEEP_IN_PLACE) { 233 mStackSupervisor.sendPowerHintForLaunchEndIfNeeded(); 234 } 235 236 mService.setRunningRemoteAnimation(mCallingPid, false); 237 238 mWindowManager.inSurfaceTransaction(() -> { 239 Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, 240 "RecentsAnimation#onAnimationFinished_inSurfaceTransaction"); 241 mWindowManager.deferSurfaceLayout(); 242 try { 243 mWindowManager.cleanupRecentsAnimation(reorderMode); 244 245 final ActivityStack targetStack = mDefaultDisplay.getStack( 246 WINDOWING_MODE_UNDEFINED, mTargetActivityType); 247 final ActivityRecord targetActivity = targetStack != null 248 ? targetStack.getTopActivity() 249 : null; 250 if (DEBUG) Slog.d(TAG, "onAnimationFinished(): targetStack=" + targetStack 251 + " targetActivity=" + targetActivity 252 + " mRestoreTargetBehindStack=" + mRestoreTargetBehindStack); 253 if (targetActivity == null) { 254 return; 255 } 256 257 // Restore the launched-behind state 258 targetActivity.mLaunchTaskBehind = false; 259 260 if (reorderMode == REORDER_MOVE_TO_TOP) { 261 // Bring the target stack to the front 262 mStackSupervisor.mNoAnimActivities.add(targetActivity); 263 targetStack.moveToFront("RecentsAnimation.onAnimationFinished()"); 264 if (DEBUG) { 265 final ActivityStack topStack = getTopNonAlwaysOnTopStack(); 266 if (topStack != targetStack) { 267 Slog.w(TAG, "Expected target stack=" + targetStack 268 + " to be top most but found stack=" + topStack); 269 } 270 } 271 } else if (reorderMode == REORDER_MOVE_TO_ORIGINAL_POSITION){ 272 // Restore the target stack to its previous position 273 final ActivityDisplay display = targetActivity.getDisplay(); 274 display.moveStackBehindStack(targetStack, mRestoreTargetBehindStack); 275 if (DEBUG) { 276 final ActivityStack aboveTargetStack = 277 mDefaultDisplay.getStackAbove(targetStack); 278 if (mRestoreTargetBehindStack != null 279 && aboveTargetStack != mRestoreTargetBehindStack) { 280 Slog.w(TAG, "Expected target stack=" + targetStack 281 + " to restored behind stack=" + mRestoreTargetBehindStack 282 + " but it is behind stack=" + aboveTargetStack); 283 } 284 } 285 } else { 286 // Keep target stack in place, nothing changes, so ignore the transition 287 // logic below 288 return; 289 } 290 291 mWindowManager.prepareAppTransition(TRANSIT_NONE, false); 292 mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, false); 293 mStackSupervisor.resumeFocusedStackTopActivityLocked(); 294 295 // No reason to wait for the pausing activity in this case, as the hiding of 296 // surfaces needs to be done immediately. 297 mWindowManager.executeAppTransition(); 298 299 // After reordering the stacks, reset the minimized state. At this point, either 300 // the target activity is now top-most and we will stay minimized (if in 301 // split-screen), or we will have returned to the app, and the minimized state 302 // should be reset 303 mWindowManager.checkSplitScreenMinimizedChanged(true /* animate */); 304 } catch (Exception e) { 305 Slog.e(TAG, "Failed to clean up recents activity", e); 306 throw e; 307 } finally { 308 mWindowManager.continueSurfaceLayout(); 309 Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); 310 } 311 }); 312 } 313 } 314 315 @Override 316 public void onAnimationFinished(@RecentsAnimationController.ReorderMode int reorderMode, 317 boolean runSychronously) { 318 if (runSychronously) { 319 finishAnimation(reorderMode); 320 } else { 321 mService.mHandler.post(() -> finishAnimation(reorderMode)); 322 } 323 } 324 325 @Override 326 public void onStackOrderChanged() { 327 // If the activity display stack order changes, cancel any running recents animation in 328 // place 329 mWindowManager.cancelRecentsAnimationSynchronously(REORDER_KEEP_IN_PLACE, 330 "stackOrderChanged"); 331 } 332 333 /** 334 * Called only when the animation should be canceled prior to starting. 335 */ 336 private void notifyAnimationCancelBeforeStart(IRecentsAnimationRunner recentsAnimationRunner) { 337 try { 338 recentsAnimationRunner.onAnimationCanceled(); 339 } catch (RemoteException e) { 340 Slog.e(TAG, "Failed to cancel recents animation before start", e); 341 } 342 } 343 344 /** 345 * @return The top stack that is not always-on-top. 346 */ 347 private ActivityStack getTopNonAlwaysOnTopStack() { 348 for (int i = mDefaultDisplay.getChildCount() - 1; i >= 0; i--) { 349 final ActivityStack s = mDefaultDisplay.getChildAt(i); 350 if (s.getWindowConfiguration().isAlwaysOnTop()) { 351 continue; 352 } 353 return s; 354 } 355 return null; 356 } 357 358 /** 359 * @return the top activity in the {@param targetStack} matching the {@param component}, or just 360 * the top activity of the top task if no task matches the component. 361 */ 362 private ActivityRecord getTargetActivity(ActivityStack targetStack, ComponentName component) { 363 if (targetStack == null) { 364 return null; 365 } 366 367 for (int i = targetStack.getChildCount() - 1; i >= 0; i--) { 368 final TaskRecord task = (TaskRecord) targetStack.getChildAt(i); 369 if (task.getBaseIntent().getComponent().equals(component)) { 370 return task.getTopActivity(); 371 } 372 } 373 return targetStack.getTopActivity(); 374 } 375} 376