1 /* 2 * Copyright 2017 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 android.app.servertransaction; 18 19 import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE; 20 import static android.app.servertransaction.ActivityLifecycleItem.ON_DESTROY; 21 import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE; 22 import static android.app.servertransaction.ActivityLifecycleItem.ON_RESTART; 23 import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME; 24 import static android.app.servertransaction.ActivityLifecycleItem.ON_START; 25 import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP; 26 import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED; 27 import static android.app.servertransaction.TransactionExecutorHelper.getShortActivityName; 28 import static android.app.servertransaction.TransactionExecutorHelper.getStateName; 29 import static android.app.servertransaction.TransactionExecutorHelper.shouldExcludeLastLifecycleState; 30 import static android.app.servertransaction.TransactionExecutorHelper.tId; 31 import static android.app.servertransaction.TransactionExecutorHelper.transactionToString; 32 33 import android.annotation.NonNull; 34 import android.app.ActivityThread.ActivityClientRecord; 35 import android.app.ClientTransactionHandler; 36 import android.content.Context; 37 import android.os.IBinder; 38 import android.os.Trace; 39 import android.util.IntArray; 40 import android.util.Slog; 41 42 import com.android.internal.annotations.VisibleForTesting; 43 44 import java.util.List; 45 46 /** 47 * Class that manages transaction execution in the correct order. 48 * 49 * @hide 50 */ 51 public class TransactionExecutor { 52 53 private static final boolean DEBUG_RESOLVER = false; 54 private static final String TAG = "TransactionExecutor"; 55 56 private final ClientTransactionHandler mTransactionHandler; 57 private final PendingTransactionActions mPendingActions = new PendingTransactionActions(); 58 private final TransactionExecutorHelper mHelper = new TransactionExecutorHelper(); 59 60 /** Initialize an instance with transaction handler, that will execute all requested actions. */ TransactionExecutor(@onNull ClientTransactionHandler clientTransactionHandler)61 public TransactionExecutor(@NonNull ClientTransactionHandler clientTransactionHandler) { 62 mTransactionHandler = clientTransactionHandler; 63 } 64 65 /** 66 * Resolve transaction. 67 * First all callbacks will be executed in the order they appear in the list. If a callback 68 * requires a certain pre- or post-execution state, the client will be transitioned accordingly. 69 * Then the client will cycle to the final lifecycle state if provided. Otherwise, it will 70 * either remain in the initial state, or last state needed by a callback. 71 */ execute(@onNull ClientTransaction transaction)72 public void execute(@NonNull ClientTransaction transaction) { 73 if (DEBUG_RESOLVER) { 74 Slog.d(TAG, tId(transaction) + "Start resolving transaction"); 75 Slog.d(TAG, transactionToString(transaction, mTransactionHandler)); 76 } 77 78 Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "clientTransactionExecuted"); 79 try { 80 executeTransactionItems(transaction); 81 } catch (Exception e) { 82 Slog.e(TAG, "Failed to execute the transaction: " 83 + transactionToString(transaction, mTransactionHandler)); 84 throw e; 85 } finally { 86 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); 87 } 88 89 mPendingActions.clear(); 90 if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "End resolving transaction"); 91 } 92 93 /** Cycles through all transaction items and execute them at proper times. */ 94 @VisibleForTesting executeTransactionItems(@onNull ClientTransaction transaction)95 public void executeTransactionItems(@NonNull ClientTransaction transaction) { 96 final List<ClientTransactionItem> items = transaction.getTransactionItems(); 97 final int size = items.size(); 98 for (int i = 0; i < size; i++) { 99 final ClientTransactionItem item = items.get(i); 100 if (item.isActivityLifecycleItem()) { 101 executeLifecycleItem(transaction, (ActivityLifecycleItem) item); 102 } else { 103 executeNonLifecycleItem(transaction, item, 104 shouldExcludeLastLifecycleState(items, i)); 105 } 106 } 107 } 108 executeNonLifecycleItem(@onNull ClientTransaction transaction, @NonNull ClientTransactionItem item, boolean shouldExcludeLastLifecycleState)109 private void executeNonLifecycleItem(@NonNull ClientTransaction transaction, 110 @NonNull ClientTransactionItem item, boolean shouldExcludeLastLifecycleState) { 111 final IBinder token = item.getActivityToken(); 112 ActivityClientRecord r = mTransactionHandler.getActivityClient(token); 113 114 if (token != null && r == null 115 && mTransactionHandler.getActivitiesToBeDestroyed().containsKey(token)) { 116 // The activity has not been created but has been requested to destroy, so all 117 // transactions for the token are just like being cancelled. 118 Slog.w(TAG, "Skip pre-destroyed transaction item:\n" + item); 119 return; 120 } 121 122 if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callback: " + item); 123 final int postExecutionState = item.getPostExecutionState(); 124 125 if (item.shouldHaveDefinedPreExecutionState()) { 126 final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r, 127 postExecutionState); 128 if (closestPreExecutionState != UNDEFINED) { 129 cycleToPath(r, closestPreExecutionState, transaction); 130 } 131 } 132 133 item.execute(mTransactionHandler, mPendingActions); 134 135 item.postExecute(mTransactionHandler, mPendingActions); 136 if (r == null) { 137 // Launch activity request will create an activity record. 138 r = mTransactionHandler.getActivityClient(token); 139 } 140 141 if (postExecutionState != UNDEFINED && r != null) { 142 cycleToPath(r, postExecutionState, shouldExcludeLastLifecycleState, transaction); 143 } 144 } 145 executeLifecycleItem(@onNull ClientTransaction transaction, @NonNull ActivityLifecycleItem lifecycleItem)146 private void executeLifecycleItem(@NonNull ClientTransaction transaction, 147 @NonNull ActivityLifecycleItem lifecycleItem) { 148 final IBinder token = lifecycleItem.getActivityToken(); 149 final ActivityClientRecord r = mTransactionHandler.getActivityClient(token); 150 if (DEBUG_RESOLVER) { 151 Slog.d(TAG, tId(transaction) + "Resolving lifecycle state: " 152 + lifecycleItem + " for activity: " 153 + getShortActivityName(token, mTransactionHandler)); 154 } 155 156 if (r == null) { 157 if (mTransactionHandler.getActivitiesToBeDestroyed().get(token) == lifecycleItem) { 158 // Always cleanup for destroy item. 159 lifecycleItem.postExecute(mTransactionHandler, mPendingActions); 160 } 161 // Ignore requests for non-existent client records for now. 162 return; 163 } 164 165 // Cycle to the state right before the final requested state. 166 cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */, transaction); 167 168 // Execute the final transition with proper parameters. 169 lifecycleItem.execute(mTransactionHandler, mPendingActions); 170 lifecycleItem.postExecute(mTransactionHandler, mPendingActions); 171 } 172 173 /** Transition the client between states. */ 174 @VisibleForTesting cycleToPath(ActivityClientRecord r, int finish, ClientTransaction transaction)175 public void cycleToPath(ActivityClientRecord r, int finish, ClientTransaction transaction) { 176 cycleToPath(r, finish, false /* excludeLastState */, transaction); 177 } 178 179 /** 180 * Transition the client between states with an option not to perform the last hop in the 181 * sequence. This is used when resolving lifecycle state request, when the last transition must 182 * be performed with some specific parameters. 183 */ cycleToPath(ActivityClientRecord r, int finish, boolean excludeLastState, ClientTransaction transaction)184 private void cycleToPath(ActivityClientRecord r, int finish, boolean excludeLastState, 185 ClientTransaction transaction) { 186 final int start = r.getLifecycleState(); 187 if (DEBUG_RESOLVER) { 188 Slog.d(TAG, tId(transaction) + "Cycle activity: " 189 + getShortActivityName(r.token, mTransactionHandler) 190 + " from: " + getStateName(start) + " to: " + getStateName(finish) 191 + " excludeLastState: " + excludeLastState); 192 } 193 final IntArray path = mHelper.getLifecyclePath(start, finish, excludeLastState); 194 performLifecycleSequence(r, path, transaction); 195 } 196 197 /** Transition the client through previously initialized state sequence. */ performLifecycleSequence(ActivityClientRecord r, IntArray path, ClientTransaction transaction)198 private void performLifecycleSequence(ActivityClientRecord r, IntArray path, 199 ClientTransaction transaction) { 200 final int size = path.size(); 201 for (int i = 0, state; i < size; i++) { 202 state = path.get(i); 203 if (DEBUG_RESOLVER) { 204 Slog.d(TAG, tId(transaction) + "Transitioning activity: " 205 + getShortActivityName(r.token, mTransactionHandler) 206 + " to state: " + getStateName(state)); 207 } 208 switch (state) { 209 case ON_CREATE: 210 mTransactionHandler.handleLaunchActivity(r, mPendingActions, 211 Context.DEVICE_ID_INVALID, null /* customIntent */); 212 break; 213 case ON_START: 214 mTransactionHandler.handleStartActivity(r, mPendingActions, 215 null /* sceneTransitionInfo */); 216 break; 217 case ON_RESUME: 218 mTransactionHandler.handleResumeActivity(r, false /* finalStateRequest */, 219 r.isForward, false /* shouldSendCompatFakeFocus */, 220 "LIFECYCLER_RESUME_ACTIVITY"); 221 break; 222 case ON_PAUSE: 223 mTransactionHandler.handlePauseActivity(r, false /* finished */, 224 false /* userLeaving */, 225 false /* autoEnteringPip */, mPendingActions, 226 "LIFECYCLER_PAUSE_ACTIVITY"); 227 break; 228 case ON_STOP: 229 mTransactionHandler.handleStopActivity(r, 230 mPendingActions, false /* finalStateRequest */, 231 "LIFECYCLER_STOP_ACTIVITY"); 232 break; 233 case ON_DESTROY: 234 mTransactionHandler.handleDestroyActivity(r, false /* finishing */, 235 false /* getNonConfigInstance */, 236 "performLifecycleSequence. cycling to:" + path.get(size - 1)); 237 break; 238 case ON_RESTART: 239 mTransactionHandler.performRestartActivity(r, false /* start */); 240 break; 241 default: 242 throw new IllegalArgumentException("Unexpected lifecycle state: " + state); 243 } 244 } 245 } 246 } 247