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