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