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