Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9956d89c authored by Andrii Kulian's avatar Andrii Kulian
Browse files

Use post-execution state for lifecycle callback sequences

onActivityResult callback should always be executed before onResume.
If an activity is in the process of starting or creation, it can be
executed after onStart. If an activity was already resumed, then we
should pause it first, execute onActivityResult, then resume again.
So there are two valid pre-execute states - onStart and onPause.

For cases like the one described above this CL uses post-execution
state to identify valid pre-execute states and will try to use the
one that is closer to the current activity state during execution.

It also moves activity result and new intent callbacks into the same
transaction as the resumed state request, so that all changes can be
handled appropriately on the client side.

Bug: 72547861
Bug: 73348613
Test: TransactionExecutorTests
Test: ActivityLifecycleTests
Change-Id: I0af457d305c73a640040b8b7aee46dbbdfa6038f
parent ddd4ca1e
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -3716,6 +3716,10 @@ public final class ActivityThread extends ClientTransactionHandler {
        if (localLOGV) Slog.v(TAG, "Performing resume of " + r
                + " finished=" + r.activity.mFinished);
        if (r != null && !r.activity.mFinished) {
            if (r.getLifecycleState() == ON_RESUME) {
                throw new IllegalStateException(
                        "Trying to resume activity which is already resumed");
            }
            if (clearHide) {
                r.hideForNow = false;
                r.activity.mStartedActivity = false;
+3 −3
Original line number Diff line number Diff line
@@ -16,7 +16,7 @@

package android.app.servertransaction;

import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;

import android.app.ClientTransactionHandler;
@@ -38,8 +38,8 @@ public class ActivityResultItem extends ClientTransactionItem {
    private List<ResultInfo> mResultInfoList;

    @Override
    public int getPreExecutionState() {
        return ON_PAUSE;
    public int getPostExecutionState() {
        return ON_RESUME;
    }

    @Override
+0 −6
Original line number Diff line number Diff line
@@ -32,12 +32,6 @@ import android.os.Parcelable;
 */
public abstract class ClientTransactionItem implements BaseClientRequest, Parcelable {

    /** Get the state in which this callback can be executed. */
    @LifecycleState
    public int getPreExecutionState() {
        return UNDEFINED;
    }

    /** Get the state that must follow this callback. */
    @LifecycleState
    public int getPostExecutionState() {
+0 −5
Original line number Diff line number Diff line
@@ -38,11 +38,6 @@ public class NewIntentItem extends ClientTransactionItem {

    // TODO(lifecycler): Switch new intent handling to this scheme.
    /*@Override
    public int getPreExecutionState() {
        return ON_PAUSE;
    }

    @Override
    public int getPostExecutionState() {
        return ON_RESUME;
    }*/
+28 −72
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
import static android.app.servertransaction.TransactionExecutorHelper.lastCallbackRequestingState;

import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
@@ -48,11 +49,7 @@ public class TransactionExecutor {

    private ClientTransactionHandler mTransactionHandler;
    private PendingTransactionActions mPendingActions = new PendingTransactionActions();

    // Temp holder for lifecycle path.
    // No direct transition between two states should take more than one complete cycle of 6 states.
    @ActivityLifecycleItem.LifecycleState
    private IntArray mLifecycleSequence = new IntArray(6);
    private TransactionExecutorHelper mHelper = new TransactionExecutorHelper();

    /** Initialize an instance with transaction handler, that will execute all requested actions. */
    public TransactionExecutor(ClientTransactionHandler clientTransactionHandler) {
@@ -89,13 +86,25 @@ public class TransactionExecutor {

        final IBinder token = transaction.getActivityToken();
        ActivityClientRecord r = mTransactionHandler.getActivityClient(token);

        // In case when post-execution state of the last callback matches the final state requested
        // for the activity in this transaction, we won't do the last transition here and do it when
        // moving to final state instead (because it may contain additional parameters from server).
        final ActivityLifecycleItem finalStateRequest = transaction.getLifecycleStateRequest();
        final int finalState = finalStateRequest != null ? finalStateRequest.getTargetState()
                : UNDEFINED;
        // Index of the last callback that requests some post-execution state.
        final int lastCallbackRequestingState = lastCallbackRequestingState(transaction);

        final int size = callbacks.size();
        for (int i = 0; i < size; ++i) {
            final ClientTransactionItem item = callbacks.get(i);
            log("Resolving callback: " + item);
            final int preExecutionState = item.getPreExecutionState();
            if (preExecutionState != UNDEFINED) {
                cycleToPath(r, preExecutionState);
            final int postExecutionState = item.getPostExecutionState();
            final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r,
                    item.getPostExecutionState());
            if (closestPreExecutionState != UNDEFINED) {
                cycleToPath(r, closestPreExecutionState);
            }

            item.execute(mTransactionHandler, token, mPendingActions);
@@ -105,9 +114,11 @@ public class TransactionExecutor {
                r = mTransactionHandler.getActivityClient(token);
            }

            final int postExecutionState = item.getPostExecutionState();
            if (postExecutionState != UNDEFINED) {
                cycleToPath(r, postExecutionState);
            if (postExecutionState != UNDEFINED && r != null) {
                // Skip the very last transition and perform it by explicit state request instead.
                final boolean shouldExcludeLastTransition =
                        i == lastCallbackRequestingState && finalState == postExecutionState;
                cycleToPath(r, postExecutionState, shouldExcludeLastTransition);
            }
        }
    }
@@ -162,15 +173,15 @@ public class TransactionExecutor {
            boolean excludeLastState) {
        final int start = r.getLifecycleState();
        log("Cycle from: " + start + " to: " + finish + " excludeLastState:" + excludeLastState);
        initLifecyclePath(start, finish, excludeLastState);
        performLifecycleSequence(r);
        final IntArray path = mHelper.getLifecyclePath(start, finish, excludeLastState);
        performLifecycleSequence(r, path);
    }

    /** Transition the client through previously initialized state sequence. */
    private void performLifecycleSequence(ActivityClientRecord r) {
        final int size = mLifecycleSequence.size();
    private void performLifecycleSequence(ActivityClientRecord r, IntArray path) {
        final int size = path.size();
        for (int i = 0, state; i < size; i++) {
            state = mLifecycleSequence.get(i);
            state = path.get(i);
            log("Transitioning to state: " + state);
            switch (state) {
                case ON_CREATE:
@@ -195,8 +206,7 @@ public class TransactionExecutor {
                case ON_DESTROY:
                    mTransactionHandler.handleDestroyActivity(r.token, false /* finishing */,
                            0 /* configChanges */, false /* getNonConfigInstance */,
                            "performLifecycleSequence. cycling to:"
                                    + mLifecycleSequence.get(size - 1));
                            "performLifecycleSequence. cycling to:" + path.get(size - 1));
                    break;
                case ON_RESTART:
                    mTransactionHandler.performRestartActivity(r.token, false /* start */);
@@ -207,60 +217,6 @@ public class TransactionExecutor {
        }
    }

    /**
     * Calculate the path through main lifecycle states for an activity and fill
     * @link #mLifecycleSequence} with values starting with the state that follows the initial
     * state.
     */
    public void initLifecyclePath(int start, int finish, boolean excludeLastState) {
        mLifecycleSequence.clear();
        if (finish >= start) {
            // just go there
            for (int i = start + 1; i <= finish; i++) {
                mLifecycleSequence.add(i);
            }
        } else { // finish < start, can't just cycle down
            if (start == ON_PAUSE && finish == ON_RESUME) {
                // Special case when we can just directly go to resumed state.
                mLifecycleSequence.add(ON_RESUME);
            } else if (start <= ON_STOP && finish >= ON_START) {
                // Restart and go to required state.

                // Go to stopped state first.
                for (int i = start + 1; i <= ON_STOP; i++) {
                    mLifecycleSequence.add(i);
                }
                // Restart
                mLifecycleSequence.add(ON_RESTART);
                // Go to required state
                for (int i = ON_START; i <= finish; i++) {
                    mLifecycleSequence.add(i);
                }
            } else {
                // Relaunch and go to required state

                // Go to destroyed state first.
                for (int i = start + 1; i <= ON_DESTROY; i++) {
                    mLifecycleSequence.add(i);
                }
                // Go to required state
                for (int i = ON_CREATE; i <= finish; i++) {
                    mLifecycleSequence.add(i);
                }
            }
        }

        // Remove last transition in case we want to perform it with some specific params.
        if (excludeLastState && mLifecycleSequence.size() != 0) {
            mLifecycleSequence.remove(mLifecycleSequence.size() - 1);
        }
    }

    @VisibleForTesting
    public int[] getLifecycleSequence() {
        return mLifecycleSequence.toArray();
    }

    private static void log(String message) {
        if (DEBUG_RESOLVER) Slog.d(TAG, message);
    }
Loading