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