Home | History | Annotate | Download | only in servertransaction
      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. */
     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      */
     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
     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. */
    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
    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      */
    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. */
    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 
    210     private static void log(String message) {
    211         if (DEBUG_RESOLVER) Slog.d(TAG, message);
    212     }
    213 }
    214