1 /* 2 * Copyright (C) 2024 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 android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.util.Slog; 23 24 import com.android.window.flags.Flags; 25 26 /** 27 * Represents a chain of WM actions where each action is "caused by" the prior action (except the 28 * first one of course). A whole chain is associated with one Transition (in fact, the purpose 29 * of this object is to communicate, to all callees, which transition they are part of). 30 * 31 * A single action is defined as "one logical thing requested of WM". This usually corresponds to 32 * each ingress-point into the process. For example, when starting an activity: 33 * * the first action is to pause the current/top activity. 34 * At this point, control leaves the process while the activity pauses. 35 * * Then WM receives completePause (a new ingress). This is a new action that gets linked 36 * to the prior action. This action involves resuming the next activity, at which point, 37 * control leaves the process again. 38 * * Eventually, when everything is done, we will have formed a chain of actions. 39 * 40 * We don't technically need to hold onto each prior action in the chain once a new action has 41 * been linked to the same transition; however, keeping the whole chain enables improved 42 * debugging and the ability to detect anomalies. 43 */ 44 public class ActionChain { 45 private static final String TAG = "TransitionChain"; 46 47 /** 48 * Normal link type. This means the action was expected and is properly linked to the 49 * current chain. 50 */ 51 static final int TYPE_NORMAL = 0; 52 53 /** 54 * This is the "default" link. It means we haven't done anything to properly track this case 55 * so it may or may not be correct. It represents the behavior as if there was no tracking. 56 * 57 * Any type that has "default" behavior uses the global "collecting transition" if it exists, 58 * otherwise it doesn't use any transition. 59 */ 60 static final int TYPE_DEFAULT = 1; 61 62 /** 63 * This means the action was performed via a legacy code-path. These should be removed 64 * eventually. This will have the "default" behavior. 65 */ 66 static final int TYPE_LEGACY = 2; 67 68 /** This is for a test. */ 69 static final int TYPE_TEST = 3; 70 71 /** This is finishing a transition. Collection isn't supported during this. */ 72 static final int TYPE_FINISH = 4; 73 74 /** 75 * Something unexpected happened so this action was started to recover from the unexpected 76 * state. This means that a "real" chain-link couldn't be determined. For now, the behavior of 77 * this is the same as "default". 78 */ 79 static final int TYPE_FAILSAFE = 5; 80 81 /** 82 * Types of chain links (ie. how is this action associated with the chain it is linked to). 83 * @hide 84 */ 85 @IntDef(prefix = { "TYPE_" }, value = { 86 TYPE_NORMAL, 87 TYPE_DEFAULT, 88 TYPE_LEGACY, 89 TYPE_TEST, 90 TYPE_FINISH, 91 TYPE_FAILSAFE 92 }) 93 public @interface LinkType {} 94 95 /** Identifies the entry-point of this action. */ 96 @NonNull 97 final String mSource; 98 99 /** Reference to ATMS. TEMPORARY! ONLY USE THIS WHEN tracker_plumbing flag is DISABLED! */ 100 @Nullable 101 ActivityTaskManagerService mTmpAtm; 102 103 /** The transition that this chain's changes belong to. */ 104 @Nullable 105 Transition mTransition; 106 107 /** The previous action in the chain. */ 108 @Nullable 109 ActionChain mPrevious = null; 110 111 /** Classification of how this action is connected to the chain. */ 112 @LinkType int mType = TYPE_NORMAL; 113 114 /** When this Action started. */ 115 long mCreateTimeMs; 116 ActionChain(String source, @LinkType int type, Transition transit)117 private ActionChain(String source, @LinkType int type, Transition transit) { 118 mSource = source; 119 mCreateTimeMs = System.currentTimeMillis(); 120 mType = type; 121 mTransition = transit; 122 if (mTransition != null) { 123 mTransition.recordChain(this); 124 } 125 } 126 getTransition()127 private Transition getTransition() { 128 if (!Flags.transitTrackerPlumbing()) { 129 return mTmpAtm.getTransitionController().getCollectingTransition(); 130 } 131 return mTransition; 132 } 133 isFinishing()134 boolean isFinishing() { 135 return mType == TYPE_FINISH; 136 } 137 138 /** 139 * Some common checks to determine (and report) whether this chain has a collecting transition. 140 */ expectCollecting()141 private boolean expectCollecting() { 142 final Transition transition = getTransition(); 143 if (transition == null) { 144 Slog.e(TAG, "Can't collect into a chain with no transition"); 145 return false; 146 } 147 if (isFinishing()) { 148 Slog.e(TAG, "Trying to collect into a finished transition"); 149 return false; 150 } 151 if (transition.mController.getCollectingTransition() != mTransition) { 152 Slog.e(TAG, "Mismatch between current collecting (" 153 + transition.mController.getCollectingTransition() + ") and chain (" 154 + transition + ")"); 155 return false; 156 } 157 return true; 158 } 159 160 /** 161 * Helper to collect a container into the associated transition. This will automatically do 162 * nothing if the chain isn't associated with a collecting transition. 163 */ collect(@onNull WindowContainer wc)164 void collect(@NonNull WindowContainer wc) { 165 if (!wc.mTransitionController.isShellTransitionsEnabled()) return; 166 if (!expectCollecting()) return; 167 getTransition().collect(wc); 168 } 169 170 /** 171 * An interface for creating and tracking action chains. 172 */ 173 static class Tracker { 174 private final ActivityTaskManagerService mAtm; 175 Tracker(ActivityTaskManagerService atm)176 Tracker(ActivityTaskManagerService atm) { 177 mAtm = atm; 178 } 179 makeChain(String source, @LinkType int type, Transition transit)180 private ActionChain makeChain(String source, @LinkType int type, Transition transit) { 181 final ActionChain out = new ActionChain(source, type, transit); 182 if (!Flags.transitTrackerPlumbing()) { 183 out.mTmpAtm = mAtm; 184 } 185 return out; 186 } 187 makeChain(String source, @LinkType int type)188 private ActionChain makeChain(String source, @LinkType int type) { 189 return makeChain(source, type, 190 mAtm.getTransitionController().getCollectingTransition()); 191 } 192 193 /** 194 * Starts tracking a normal action. 195 * @see #TYPE_NORMAL 196 */ 197 @NonNull start(String source, Transition transit)198 ActionChain start(String source, Transition transit) { 199 return makeChain(source, TYPE_NORMAL, transit); 200 } 201 202 /** @see #TYPE_DEFAULT */ 203 @NonNull startDefault(String source)204 ActionChain startDefault(String source) { 205 return makeChain(source, TYPE_DEFAULT); 206 } 207 208 /** 209 * Starts tracking an action that finishes a transition. 210 * @see #TYPE_NORMAL 211 */ 212 @NonNull startFinish(String source, Transition finishTransit)213 ActionChain startFinish(String source, Transition finishTransit) { 214 return makeChain(source, TYPE_FINISH, finishTransit); 215 } 216 217 /** @see #TYPE_LEGACY */ 218 @NonNull startLegacy(String source)219 ActionChain startLegacy(String source) { 220 return makeChain(source, TYPE_LEGACY, null); 221 } 222 223 /** @see #TYPE_FAILSAFE */ 224 @NonNull startFailsafe(String source)225 ActionChain startFailsafe(String source) { 226 return makeChain(source, TYPE_FAILSAFE); 227 } 228 } 229 230 /** Helpers for usage in tests. */ 231 @NonNull test()232 static ActionChain test() { 233 return new ActionChain("test", TYPE_TEST, null /* transition */); 234 } 235 236 @NonNull testFinish(Transition toFinish)237 static ActionChain testFinish(Transition toFinish) { 238 return new ActionChain("test", TYPE_FINISH, toFinish); 239 } 240 } 241