1 /* 2 * Copyright (C) 2019 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.wm; 18 19 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; 20 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS; 21 22 import android.app.ActivityOptions; 23 import android.content.Intent; 24 import android.content.pm.ActivityInfo; 25 import android.os.Debug; 26 27 import com.android.internal.protolog.common.ProtoLog; 28 import com.android.internal.util.function.pooled.PooledConsumer; 29 import com.android.internal.util.function.pooled.PooledFunction; 30 import com.android.internal.util.function.pooled.PooledLambda; 31 32 import java.util.ArrayList; 33 34 /** Helper class for processing the reset of a task. */ 35 class ResetTargetTaskHelper { 36 private Task mTask; 37 private Task mTargetTask; 38 private Task mTargetRootTask; 39 private ActivityRecord mRoot; 40 private boolean mForceReset; 41 private boolean mCanMoveOptions; 42 private boolean mTargetTaskFound; 43 private int mActivityReparentPosition; 44 private ActivityOptions mTopOptions; 45 private ArrayList<ActivityRecord> mResultActivities = new ArrayList<>(); 46 private ArrayList<ActivityRecord> mAllActivities = new ArrayList<>(); 47 private ArrayList<ActivityRecord> mPendingReparentActivities = new ArrayList<>(); 48 reset(Task task)49 private void reset(Task task) { 50 mTask = task; 51 mRoot = null; 52 mCanMoveOptions = true; 53 mTopOptions = null; 54 mResultActivities.clear(); 55 mAllActivities.clear(); 56 } 57 process(Task targetTask, boolean forceReset)58 ActivityOptions process(Task targetTask, boolean forceReset) { 59 mForceReset = forceReset; 60 mTargetTask = targetTask; 61 mTargetTaskFound = false; 62 mTargetRootTask = targetTask.getRootTask(); 63 mActivityReparentPosition = -1; 64 65 final PooledConsumer c = PooledLambda.obtainConsumer( 66 ResetTargetTaskHelper::processTask, this, PooledLambda.__(Task.class)); 67 targetTask.mWmService.mRoot.forAllLeafTasks(c, true /*traverseTopToBottom*/); 68 c.recycle(); 69 70 processPendingReparentActivities(); 71 reset(null); 72 return mTopOptions; 73 } 74 processTask(Task task)75 private void processTask(Task task) { 76 reset(task); 77 mRoot = task.getRootActivity(true); 78 if (mRoot == null) return; 79 80 final boolean isTargetTask = task == mTargetTask; 81 if (isTargetTask) mTargetTaskFound = true; 82 83 final PooledFunction f = PooledLambda.obtainFunction( 84 ResetTargetTaskHelper::processActivity, this, 85 PooledLambda.__(ActivityRecord.class), isTargetTask); 86 task.forAllActivities(f); 87 f.recycle(); 88 } 89 processActivity(ActivityRecord r, boolean isTargetTask)90 private boolean processActivity(ActivityRecord r, boolean isTargetTask) { 91 // End processing if we have reached the root. 92 if (r == mRoot) return true; 93 94 mAllActivities.add(r); 95 final int flags = r.info.flags; 96 final boolean finishOnTaskLaunch = 97 (flags & ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH) != 0; 98 final boolean allowTaskReparenting = 99 (flags & ActivityInfo.FLAG_ALLOW_TASK_REPARENTING) != 0; 100 final boolean clearWhenTaskReset = 101 (r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0; 102 103 if (isTargetTask) { 104 if (!finishOnTaskLaunch && !clearWhenTaskReset) { 105 if (r.resultTo != null) { 106 // If this activity is sending a reply to a previous activity, we can't do 107 // anything with it now until we reach the start of the reply chain. 108 // NOTE: that we are assuming the result is always to the previous activity, 109 // which is almost always the case but we really shouldn't count on. 110 mResultActivities.add(r); 111 return false; 112 } 113 if (allowTaskReparenting && r.taskAffinity != null 114 && !r.taskAffinity.equals(mTask.affinity)) { 115 // If this activity has an affinity for another task, then we need to move 116 // it out of here. We will move it as far out of the way as possible, to the 117 // bottom of the activity root task. This also keeps it correctly ordered with 118 // any activities we previously moved. 119 120 // Handle this activity after we have done traversing the hierarchy. 121 mPendingReparentActivities.add(r); 122 return false; 123 } 124 } 125 if (mForceReset || finishOnTaskLaunch || clearWhenTaskReset) { 126 // If the activity should just be removed either because it asks for it, or the 127 // task should be cleared, then finish it and anything that is part of its reply 128 // chain. 129 if (clearWhenTaskReset) { 130 // In this case, we want to finish this activity and everything above it, 131 // so be sneaky and pretend like these are all in the reply chain. 132 finishActivities(mAllActivities, "clearWhenTaskReset"); 133 } else { 134 mResultActivities.add(r); 135 finishActivities(mResultActivities, "reset-task"); 136 } 137 138 mResultActivities.clear(); 139 return false; 140 } else { 141 // If we were in the middle of a chain, well the activity that started it all 142 // doesn't want anything special, so leave it all as-is. 143 mResultActivities.clear(); 144 } 145 146 return false; 147 148 } else { 149 mResultActivities.add(r); 150 if (r.resultTo != null) { 151 // If this activity is sending a reply to a previous activity, we can't do 152 // anything with it now until we reach the start of the reply chain. 153 // NOTE: that we are assuming the result is always to the previous activity, 154 // which is almost always the case but we really shouldn't count on. 155 return false; 156 } else if (mTargetTaskFound && allowTaskReparenting && mTargetTask.affinity != null 157 && mTargetTask.affinity.equals(r.taskAffinity)) { 158 // This activity has an affinity for our task. Either remove it if we are 159 // clearing or move it over to our task. Note that we currently punt on the case 160 // where we are resetting a task that is not at the top but who has activities 161 // above with an affinity to it... this is really not a normal case, and we will 162 // need to later pull that task to the front and usually at that point we will 163 // do the reset and pick up those remaining activities. (This only happens if 164 // someone starts an activity in a new task from an activity in a task that is 165 // not currently on top.) 166 if (mForceReset || finishOnTaskLaunch) { 167 finishActivities(mResultActivities, "move-affinity"); 168 return false; 169 } 170 if (mActivityReparentPosition == -1) { 171 mActivityReparentPosition = mTargetTask.getChildCount(); 172 } 173 174 processResultActivities( 175 r, mTargetTask, mActivityReparentPosition, false, false); 176 177 // Now we've moved it in to place...but what if this is a singleTop activity and 178 // we have put it on top of another instance of the same activity? Then we drop 179 // the instance below so it remains singleTop. 180 if (r.info.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP) { 181 final ActivityRecord p = mTargetTask.getActivityBelow(r); 182 if (p != null) { 183 if (p.intent.getComponent().equals(r.intent.getComponent())) { 184 p.finishIfPossible("replace", false /* oomAdj */); 185 } 186 } 187 } 188 } 189 return false; 190 } 191 } 192 finishActivities(ArrayList<ActivityRecord> activities, String reason)193 private void finishActivities(ArrayList<ActivityRecord> activities, String reason) { 194 boolean noOptions = mCanMoveOptions; 195 196 while (!activities.isEmpty()) { 197 final ActivityRecord p = activities.remove(0); 198 if (p.finishing) continue; 199 200 noOptions = takeOption(p, noOptions); 201 202 ProtoLog.w(WM_DEBUG_TASKS, "resetTaskIntendedTask: calling finishActivity " 203 + "on %s", p); 204 p.finishIfPossible(reason, false /* oomAdj */); 205 } 206 } 207 processResultActivities(ActivityRecord target, Task targetTask, int position, boolean ignoreFinishing, boolean takeOptions)208 private void processResultActivities(ActivityRecord target, Task targetTask, int position, 209 boolean ignoreFinishing, boolean takeOptions) { 210 boolean noOptions = mCanMoveOptions; 211 212 while (!mResultActivities.isEmpty()) { 213 final ActivityRecord p = mResultActivities.remove(0); 214 if (ignoreFinishing && p.finishing) continue; 215 216 if (takeOptions) { 217 noOptions = takeOption(p, noOptions); 218 } 219 ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Removing activity %s from task=%s " 220 + "adding to task=%s Callers=%s", p, mTask, targetTask, Debug.getCallers(4)); 221 ProtoLog.v(WM_DEBUG_TASKS, "Pushing next activity %s out to target's task %s", p, 222 target); 223 p.reparent(targetTask, position, "resetTargetTaskIfNeeded"); 224 } 225 } 226 processPendingReparentActivities()227 private void processPendingReparentActivities() { 228 if (mPendingReparentActivities.isEmpty()) { 229 return; 230 } 231 232 final ActivityTaskManagerService atmService = mTargetRootTask.mAtmService; 233 TaskDisplayArea taskDisplayArea = mTargetRootTask.getDisplayArea(); 234 235 final int windowingMode = mTargetRootTask.getWindowingMode(); 236 final int activityType = mTargetRootTask.getActivityType(); 237 238 while (!mPendingReparentActivities.isEmpty()) { 239 final ActivityRecord r = mPendingReparentActivities.remove(0); 240 final boolean alwaysCreateTask = DisplayContent.alwaysCreateRootTask(windowingMode, 241 activityType); 242 final Task task = alwaysCreateTask 243 ? taskDisplayArea.getBottomMostTask() : mTargetRootTask.getBottomMostTask(); 244 Task targetTask = null; 245 if (task != null && r.taskAffinity.equals(task.affinity)) { 246 // If the activity currently at the bottom has the same task affinity as 247 // the one we are moving, then merge it into the same task. 248 targetTask = task; 249 ProtoLog.v(WM_DEBUG_TASKS, "Start pushing activity %s out to bottom task %s", r, 250 targetTask); 251 } 252 if (targetTask == null) { 253 if (alwaysCreateTask) { 254 targetTask = taskDisplayArea.getOrCreateRootTask(windowingMode, 255 activityType, false /* onTop */); 256 } else { 257 targetTask = mTargetRootTask.reuseOrCreateTask(r.info, null /*intent*/, 258 false /*toTop*/); 259 } 260 targetTask.affinityIntent = r.intent; 261 } 262 r.reparent(targetTask, 0 /* position */, "resetTargetTaskIfNeeded"); 263 atmService.mTaskSupervisor.mRecentTasks.add(targetTask); 264 } 265 } 266 takeOption(ActivityRecord p, boolean noOptions)267 private boolean takeOption(ActivityRecord p, boolean noOptions) { 268 mCanMoveOptions = false; 269 if (noOptions && mTopOptions == null) { 270 mTopOptions = p.getOptions(); 271 if (mTopOptions != null) { 272 p.clearOptionsAnimation(); 273 noOptions = false; 274 } 275 } 276 return noOptions; 277 } 278 } 279