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 130 if (item.shouldHaveDefinedPreExecutionState()) { 131 final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r, 132 item.getPostExecutionState()); 133 if (closestPreExecutionState != UNDEFINED) { 134 cycleToPath(r, closestPreExecutionState, transaction); 135 } 136 } 137 138 item.execute(mTransactionHandler, token, mPendingActions); 139 item.postExecute(mTransactionHandler, token, mPendingActions); 140 if (r == null) { 141 // Launch activity request will create an activity record. 142 r = mTransactionHandler.getActivityClient(token); 143 } 144 145 if (postExecutionState != UNDEFINED && r != null) { 146 // Skip the very last transition and perform it by explicit state request instead. 147 final boolean shouldExcludeLastTransition = 148 i == lastCallbackRequestingState && finalState == postExecutionState; 149 cycleToPath(r, postExecutionState, shouldExcludeLastTransition, transaction); 150 } 151 } 152 } 153 154 /** Transition to the final state if requested by the transaction. */ executeLifecycleState(ClientTransaction transaction)155 private void executeLifecycleState(ClientTransaction transaction) { 156 final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest(); 157 if (lifecycleItem == null) { 158 // No lifecycle request, return early. 159 return; 160 } 161 162 final IBinder token = transaction.getActivityToken(); 163 final ActivityClientRecord r = mTransactionHandler.getActivityClient(token); 164 if (DEBUG_RESOLVER) { 165 Slog.d(TAG, tId(transaction) + "Resolving lifecycle state: " 166 + lifecycleItem + " for activity: " 167 + getShortActivityName(token, mTransactionHandler)); 168 } 169 170 if (r == null) { 171 // Ignore requests for non-existent client records for now. 172 return; 173 } 174 175 // Cycle to the state right before the final requested state. 176 cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */, transaction); 177 178 // Execute the final transition with proper parameters. 179 lifecycleItem.execute(mTransactionHandler, token, mPendingActions); 180 lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions); 181 } 182 183 /** Transition the client between states. */ 184 @VisibleForTesting cycleToPath(ActivityClientRecord r, int finish, ClientTransaction transaction)185 public void cycleToPath(ActivityClientRecord r, int finish, ClientTransaction transaction) { 186 cycleToPath(r, finish, false /* excludeLastState */, transaction); 187 } 188 189 /** 190 * Transition the client between states with an option not to perform the last hop in the 191 * sequence. This is used when resolving lifecycle state request, when the last transition must 192 * be performed with some specific parameters. 193 */ cycleToPath(ActivityClientRecord r, int finish, boolean excludeLastState, ClientTransaction transaction)194 private void cycleToPath(ActivityClientRecord r, int finish, boolean excludeLastState, 195 ClientTransaction transaction) { 196 final int start = r.getLifecycleState(); 197 if (DEBUG_RESOLVER) { 198 Slog.d(TAG, tId(transaction) + "Cycle activity: " 199 + getShortActivityName(r.token, mTransactionHandler) 200 + " from: " + getStateName(start) + " to: " + getStateName(finish) 201 + " excludeLastState: " + excludeLastState); 202 } 203 final IntArray path = mHelper.getLifecyclePath(start, finish, excludeLastState); 204 performLifecycleSequence(r, path, transaction); 205 } 206 207 /** Transition the client through previously initialized state sequence. */ performLifecycleSequence(ActivityClientRecord r, IntArray path, ClientTransaction transaction)208 private void performLifecycleSequence(ActivityClientRecord r, IntArray path, 209 ClientTransaction transaction) { 210 final int size = path.size(); 211 for (int i = 0, state; i < size; i++) { 212 state = path.get(i); 213 if (DEBUG_RESOLVER) { 214 Slog.d(TAG, tId(transaction) + "Transitioning activity: " 215 + getShortActivityName(r.token, mTransactionHandler) 216 + " to state: " + getStateName(state)); 217 } 218 switch (state) { 219 case ON_CREATE: 220 mTransactionHandler.handleLaunchActivity(r, mPendingActions, 221 null /* customIntent */); 222 break; 223 case ON_START: 224 mTransactionHandler.handleStartActivity(r, mPendingActions, 225 null /* activityOptions */); 226 break; 227 case ON_RESUME: 228 mTransactionHandler.handleResumeActivity(r, false /* finalStateRequest */, 229 r.isForward, false /* shouldSendCompatFakeFocus */, 230 "LIFECYCLER_RESUME_ACTIVITY"); 231 break; 232 case ON_PAUSE: 233 mTransactionHandler.handlePauseActivity(r, false /* finished */, 234 false /* userLeaving */, 0 /* configChanges */, 235 false /* autoEnteringPip */, mPendingActions, 236 "LIFECYCLER_PAUSE_ACTIVITY"); 237 break; 238 case ON_STOP: 239 mTransactionHandler.handleStopActivity(r, 0 /* configChanges */, 240 mPendingActions, false /* finalStateRequest */, 241 "LIFECYCLER_STOP_ACTIVITY"); 242 break; 243 case ON_DESTROY: 244 mTransactionHandler.handleDestroyActivity(r, false /* finishing */, 245 0 /* configChanges */, false /* getNonConfigInstance */, 246 "performLifecycleSequence. cycling to:" + path.get(size - 1)); 247 break; 248 case ON_RESTART: 249 mTransactionHandler.performRestartActivity(r, false /* start */); 250 break; 251 default: 252 throw new IllegalArgumentException("Unexpected lifecycle state: " + state); 253 } 254 } 255 } 256 } 257