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