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