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

Commit d3062cbf authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Skip execution of transactions on a destroyed activity

An Activity may not yet create on client side, there is another
launch request with flags to clear task, then a destroy transaction
is scheduled. If client side keeps blocking until destroy timeout,
the token on server side will be removed. When client begins to
handle the first creation, it will report its activity token to
server that causes IllegalArgumentException because there is no
matched ActivityRecord.

Bug: 32375307
Test: atest FrameworksCoreTests:TransactionExecutorTests
Change-Id: I1b7e0c2863b13091c3fd50df602ff31ae02ff38d
parent d9afa508
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ 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.ClientTransactionItem;
import android.app.servertransaction.PendingTransactionActions;
import android.app.servertransaction.PendingTransactionActions.StopInfo;
import android.app.servertransaction.TransactionExecutor;
@@ -176,6 +177,7 @@ import java.net.InetAddress;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -257,6 +259,8 @@ public final class ActivityThread extends ClientTransactionHandler {
    final H mH = new H();
    final Executor mExecutor = new HandlerExecutor(mH);
    final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
    final Map<IBinder, ClientTransactionItem> mActivitiesToBeDestroyed =
            Collections.synchronizedMap(new ArrayMap<IBinder, ClientTransactionItem>());
    // List of new activities (via ActivityRecord.nextIdle) that should
    // be reported when next we idle.
    ActivityClientRecord mNewActivities = null;
@@ -4473,6 +4477,11 @@ public final class ActivityThread extends ClientTransactionHandler {
        return component == null ? "[Unknown]" : component.toShortString();
    }

    @Override
    public Map<IBinder, ClientTransactionItem> getActivitiesToBeDestroyed() {
        return mActivitiesToBeDestroyed;
    }

    @Override
    public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
            boolean getNonConfigInstance, String reason) {
+5 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
package android.app;

import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.ClientTransactionItem;
import android.app.servertransaction.PendingTransactionActions;
import android.app.servertransaction.TransactionExecutor;
import android.content.Intent;
@@ -29,6 +30,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.ReferrerIntent;

import java.util.List;
import java.util.Map;

/**
 * Defines operations that a {@link android.app.servertransaction.ClientTransaction} or its items
@@ -78,6 +80,9 @@ public abstract class ClientTransactionHandler {
    // Execute phase related logic and handlers. Methods here execute actual lifecycle transactions
    // and deliver callbacks.

    /** Get activity and its corresponding transaction item which are going to destroy. */
    public abstract Map<IBinder, ClientTransactionItem> getActivitiesToBeDestroyed();

    /** Destroy the activity. */
    public abstract void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
            boolean getNonConfigInstance, String reason);
+25 −0
Original line number Diff line number Diff line
@@ -164,6 +164,31 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
        ObjectPool.recycle(this);
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder(64);
        sb.append("ClientTransaction{");
        if (mActivityToken != null) {
            sb.append(" a:").append(Integer.toHexString(System.identityHashCode(mActivityToken)));
        }
        if (mActivityCallbacks != null && !mActivityCallbacks.isEmpty()) {
            sb.append(" c:");
            final int size = mActivityCallbacks.size();
            for (int i = 0; i < size; i++) {
                sb.append(mActivityCallbacks.get(i).getClass().getSimpleName());
                if (i < size - 1) {
                    sb.append(",");
                }
            }
        }
        if (mLifecycleStateRequest != null) {
            sb.append(" s:");
            sb.append(mLifecycleStateRequest.getClass().getSimpleName());
        }
        sb.append(" }");
        return sb.toString();
    }


    // Parcelable implementation

+5 −0
Original line number Diff line number Diff line
@@ -32,6 +32,11 @@ public class DestroyActivityItem extends ActivityLifecycleItem {
    private boolean mFinished;
    private int mConfigChanges;

    @Override
    public void preExecute(ClientTransactionHandler client, IBinder token) {
        client.getActivitiesToBeDestroyed().put(token, this);
    }

    @Override
    public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
+20 −1
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;

import java.util.List;
import java.util.Map;

/**
 * Class that manages transaction execution in the correct order.
@@ -63,6 +64,24 @@ public class TransactionExecutor {
     */
    public void execute(ClientTransaction transaction) {
        final IBinder token = transaction.getActivityToken();
        if (token != null) {
            final Map<IBinder, ClientTransactionItem> activitiesToBeDestroyed =
                    mTransactionHandler.getActivitiesToBeDestroyed();
            final ClientTransactionItem destroyItem = activitiesToBeDestroyed.get(token);
            if (destroyItem != null) {
                if (transaction.getLifecycleStateRequest() == destroyItem) {
                    // It is going to execute the transaction that will destroy activity with the
                    // token, so the corresponding to-be-destroyed record can be removed.
                    activitiesToBeDestroyed.remove(token);
                }
                if (mTransactionHandler.getActivityClient(token) == null) {
                    // The activity has not been created but has been requested to destroy, so all
                    // transactions for the token are just like being cancelled.
                    Slog.w(TAG, "Skip pre-destroyed " + transaction);
                    return;
                }
            }
        }
        log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token);

        executeCallbacks(transaction);
@@ -76,7 +95,7 @@ public class TransactionExecutor {
    @VisibleForTesting
    public void executeCallbacks(ClientTransaction transaction) {
        final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
        if (callbacks == null) {
        if (callbacks == null || callbacks.isEmpty()) {
            // No callbacks to execute, return early.
            return;
        }
Loading