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