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

Commit 914aa7da authored by Andrii Kulian's avatar Andrii Kulian
Browse files

Use transaction for local activity relaunch

To know that onPostCreate callback should be executed we should use
TransactionExecutor for the entire transaction. It will fill
PendingActions object during the launch and the callback will be
triggered after onStart.
This CL changes local activity relaunch to use Lifecycler
infrastructure. We should immediately execute local recreate
requests, because if we wait until the scheduled message to be
handled, we may already be in a different state and final state
request in the scheduled relaunch transaction will already be
obsolete.

Bug: 72029061
Bug: 64610483
Bug: 76088057
Bug: 73747058
Test: ActivityLifecycleTests
Change-Id: Ia53ecd199c83d030932c4493064e58568805f2a5
parent 461a71a5
Loading
Loading
Loading
Loading
+28 −8
Original line number Diff line number Diff line
@@ -30,12 +30,15 @@ import android.annotation.Nullable;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
import android.app.backup.BackupAgent;
import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ActivityLifecycleItem.LifecycleState;
import android.app.servertransaction.ActivityRelaunchItem;
import android.app.servertransaction.ActivityResultItem;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.PendingTransactionActions;
import android.app.servertransaction.PendingTransactionActions.StopInfo;
import android.app.servertransaction.TransactionExecutor;
import android.app.servertransaction.TransactionExecutorHelper;
import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks2;
import android.content.ComponentName;
@@ -520,6 +523,10 @@ public final class ActivityThread extends ClientTransactionHandler {
            return activityInfo.persistableMode == ActivityInfo.PERSIST_ACROSS_REBOOTS;
        }

        public boolean isVisibleFromServer() {
            return activity != null && activity.mVisibleFromServer;
        }

        public String toString() {
            ComponentName componentName = intent != null ? intent.getComponent() : null;
            return "ActivityRecord{"
@@ -1797,6 +1804,7 @@ public final class ActivityThread extends ClientTransactionHandler {
                        // message is handled.
                        transaction.recycle();
                    }
                    // TODO(lifecycler): Recycle locally scheduled transactions.
                    break;
            }
            Object obj = msg.obj;
@@ -2755,6 +2763,11 @@ public final class ActivityThread extends ClientTransactionHandler {
        }
    }

    @Override
    TransactionExecutor getTransactionExecutor() {
        return mTransactionExecutor;
    }

    void sendMessage(int what, Object obj) {
        sendMessage(what, obj, 0, 0, false);
    }
@@ -4723,15 +4736,22 @@ public final class ActivityThread extends ClientTransactionHandler {
            return;
        }

        // TODO(b/73747058): Investigate converting this to use transaction to relaunch.
        handleRelaunchActivityInner(r, 0 /* configChanges */, null /* pendingResults */,
                null /* pendingIntents */, null /* pendingActions */, prevState != ON_RESUME,
                r.overrideConfig, "handleRelaunchActivityLocally");

        // Restore back to the previous state before relaunch if needed.
        if (prevState != r.getLifecycleState()) {
            mTransactionExecutor.cycleToPath(r, prevState);
        }
        // Initialize a relaunch request.
        final MergedConfiguration mergedConfiguration = new MergedConfiguration(
                r.createdConfig != null ? r.createdConfig : mConfiguration,
                r.overrideConfig);
        final ActivityRelaunchItem activityRelaunchItem = ActivityRelaunchItem.obtain(
                null /* pendingResults */, null /* pendingIntents */, 0 /* configChanges */,
                mergedConfiguration, r.mPreserveWindow);
        // Make sure to match the existing lifecycle state in the end of the transaction.
        final ActivityLifecycleItem lifecycleRequest =
                TransactionExecutorHelper.getLifecycleRequestForCurrentState(r);
        // Schedule the transaction.
        final ClientTransaction transaction = ClientTransaction.obtain(this.mAppThread, r.token);
        transaction.addCallback(activityRelaunchItem);
        transaction.setLifecycleStateRequest(lifecycleRequest);
        executeTransaction(transaction);
    }

    private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
+17 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ package android.app;

import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.PendingTransactionActions;
import android.app.servertransaction.TransactionExecutor;
import android.content.pm.ApplicationInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -43,6 +44,22 @@ public abstract class ClientTransactionHandler {
        sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
    }

    /**
     * Execute transaction immediately without scheduling it. This is used for local requests, so
     * it will also recycle the transaction.
     */
    void executeTransaction(ClientTransaction transaction) {
        transaction.preExecute(this);
        getTransactionExecutor().execute(transaction);
        transaction.recycle();
    }

    /**
     * Get the {@link TransactionExecutor} that will be performing lifecycle transitions and
     * callbacks for activities.
     */
    abstract TransactionExecutor getTransactionExecutor();

    abstract void sendMessage(int what, Object obj);


+24 −3
Original line number Diff line number Diff line
@@ -26,7 +26,7 @@ import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;

import android.app.ActivityThread;
import android.app.ActivityThread.ActivityClientRecord;
import android.util.IntArray;

import com.android.internal.annotations.VisibleForTesting;
@@ -124,7 +124,7 @@ public class TransactionExecutorHelper {
     *         {@link ActivityLifecycleItem#UNDEFINED} if there is not path.
     */
    @VisibleForTesting
    public int getClosestPreExecutionState(ActivityThread.ActivityClientRecord r,
    public int getClosestPreExecutionState(ActivityClientRecord r,
            int postExecutionState) {
        switch (postExecutionState) {
            case UNDEFINED:
@@ -147,7 +147,7 @@ public class TransactionExecutorHelper {
     *         were provided or there is not path.
     */
    @VisibleForTesting
    public int getClosestOfStates(ActivityThread.ActivityClientRecord r, int[] finalStates) {
    public int getClosestOfStates(ActivityClientRecord r, int[] finalStates) {
        if (finalStates == null || finalStates.length == 0) {
            return UNDEFINED;
        }
@@ -168,6 +168,27 @@ public class TransactionExecutorHelper {
        return closestState;
    }

    /** Get the lifecycle state request to match the current state in the end of a transaction. */
    public static ActivityLifecycleItem getLifecycleRequestForCurrentState(ActivityClientRecord r) {
        final int prevState = r.getLifecycleState();
        final ActivityLifecycleItem lifecycleItem;
        switch (prevState) {
            // TODO(lifecycler): Extend to support all possible states.
            case ON_PAUSE:
                lifecycleItem = PauseActivityItem.obtain();
                break;
            case ON_STOP:
                lifecycleItem = StopActivityItem.obtain(r.isVisibleFromServer(),
                        0 /* configChanges */);
                break;
            default:
                lifecycleItem = ResumeActivityItem.obtain(false /* isForward */);
                break;
        }

        return lifecycleItem;
    }

    /**
     * Check if there is a destruction involved in the path. We want to avoid a lifecycle sequence
     * that involves destruction and recreation if there is another path.