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