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

Commit d56ed0cd authored by Andrii Kulian's avatar Andrii Kulian
Browse files

Improve transaction logging

Add more detailed conditional logs to transaction executor on the
client side for easier debugging. This will print out transaction
callbacks and final state requests, as well as target activity
names.

Test: Manual
Change-Id: I14ab1a34985975bfd8c1f90b9c5945e354f79446
parent bd2a8811
Loading
Loading
Loading
Loading
+2 −1
Original line number Original line Diff line number Diff line
@@ -2731,7 +2731,8 @@ public final class ActivityThread extends ClientTransactionHandler {
    }
    }


    public final Activity getActivity(IBinder token) {
    public final Activity getActivity(IBinder token) {
        return mActivities.get(token).activity;
        final ActivityClientRecord activityRecord = mActivities.get(token);
        return activityRecord != null ? activityRecord.activity : null;
    }
    }


    @Override
    @Override
+2 −0
Original line number Original line Diff line number Diff line
@@ -66,6 +66,8 @@ public abstract class ClientTransactionHandler {


    abstract void sendMessage(int what, Object obj);
    abstract void sendMessage(int what, Object obj);


    /** Get activity instance for the token. */
    public abstract Activity getActivity(IBinder token);


    // Prepare phase related logic and handlers. Methods that inform about about pending changes or
    // Prepare phase related logic and handlers. Methods that inform about about pending changes or
    // do other internal bookkeeping.
    // do other internal bookkeeping.
+20 −26
Original line number Original line Diff line number Diff line
@@ -26,6 +26,7 @@ import android.os.RemoteException;


import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;


import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.List;
import java.util.List;
import java.util.Objects;
import java.util.Objects;
@@ -164,32 +165,6 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
        ObjectPool.recycle(this);
        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
    // Parcelable implementation


    /** Write to Parcel. */
    /** Write to Parcel. */
@@ -262,4 +237,23 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
        result = 31 * result + Objects.hashCode(mLifecycleStateRequest);
        result = 31 * result + Objects.hashCode(mLifecycleStateRequest);
        return result;
        return result;
    }
    }

    /** Dump transaction items callback items and final lifecycle state request. */
    public void dump(String prefix, PrintWriter pw) {
        pw.append(prefix).println("ClientTransaction{");
        pw.append(prefix).print("  callbacks=[");
        final int size = mActivityCallbacks != null ? mActivityCallbacks.size() : 0;
        if (size > 0) {
            pw.println();
            for (int i = 0; i < size; i++) {
                pw.append(prefix).append("    ").println(mActivityCallbacks.get(i).toString());
            }
            pw.append(prefix).println("  ]");
        } else {
            pw.println("]");
        }
        pw.append(prefix).append("  stateRequest=").println(mLifecycleStateRequest != null
                ? mLifecycleStateRequest.toString() : null);
        pw.append(prefix).println("}");
    }
}
}
+39 −21
Original line number Original line Diff line number Diff line
@@ -24,7 +24,11 @@ import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
import static android.app.servertransaction.TransactionExecutorHelper.getShortActivityName;
import static android.app.servertransaction.TransactionExecutorHelper.getStateName;
import static android.app.servertransaction.TransactionExecutorHelper.lastCallbackRequestingState;
import static android.app.servertransaction.TransactionExecutorHelper.lastCallbackRequestingState;
import static android.app.servertransaction.TransactionExecutorHelper.tId;
import static android.app.servertransaction.TransactionExecutorHelper.transactionToString;


import android.app.ActivityThread.ActivityClientRecord;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.app.ClientTransactionHandler;
@@ -63,6 +67,8 @@ public class TransactionExecutor {
     * either remain in the initial state, or last state needed by a callback.
     * either remain in the initial state, or last state needed by a callback.
     */
     */
    public void execute(ClientTransaction transaction) {
    public void execute(ClientTransaction transaction) {
        if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Start resolving transaction");

        final IBinder token = transaction.getActivityToken();
        final IBinder token = transaction.getActivityToken();
        if (token != null) {
        if (token != null) {
            final Map<IBinder, ClientTransactionItem> activitiesToBeDestroyed =
            final Map<IBinder, ClientTransactionItem> activitiesToBeDestroyed =
@@ -77,18 +83,20 @@ public class TransactionExecutor {
                if (mTransactionHandler.getActivityClient(token) == null) {
                if (mTransactionHandler.getActivityClient(token) == null) {
                    // The activity has not been created but has been requested to destroy, so all
                    // The activity has not been created but has been requested to destroy, so all
                    // transactions for the token are just like being cancelled.
                    // transactions for the token are just like being cancelled.
                    Slog.w(TAG, "Skip pre-destroyed " + transaction);
                    Slog.w(TAG, tId(transaction) + "Skip pre-destroyed transaction:\n"
                            + transactionToString(transaction, mTransactionHandler));
                    return;
                    return;
                }
                }
            }
            }
        }
        }
        log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token);

        if (DEBUG_RESOLVER) Slog.d(TAG, transactionToString(transaction, mTransactionHandler));


        executeCallbacks(transaction);
        executeCallbacks(transaction);


        executeLifecycleState(transaction);
        executeLifecycleState(transaction);
        mPendingActions.clear();
        mPendingActions.clear();
        log("End resolving transaction");
        if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "End resolving transaction");
    }
    }


    /** Cycle through all states requested by callbacks and execute them at proper times. */
    /** Cycle through all states requested by callbacks and execute them at proper times. */
@@ -99,7 +107,7 @@ public class TransactionExecutor {
            // No callbacks to execute, return early.
            // No callbacks to execute, return early.
            return;
            return;
        }
        }
        log("Resolving callbacks");
        if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callbacks in transaction");


        final IBinder token = transaction.getActivityToken();
        final IBinder token = transaction.getActivityToken();
        ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
        ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
@@ -116,12 +124,12 @@ public class TransactionExecutor {
        final int size = callbacks.size();
        final int size = callbacks.size();
        for (int i = 0; i < size; ++i) {
        for (int i = 0; i < size; ++i) {
            final ClientTransactionItem item = callbacks.get(i);
            final ClientTransactionItem item = callbacks.get(i);
            log("Resolving callback: " + item);
            if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callback: " + item);
            final int postExecutionState = item.getPostExecutionState();
            final int postExecutionState = item.getPostExecutionState();
            final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r,
            final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r,
                    item.getPostExecutionState());
                    item.getPostExecutionState());
            if (closestPreExecutionState != UNDEFINED) {
            if (closestPreExecutionState != UNDEFINED) {
                cycleToPath(r, closestPreExecutionState);
                cycleToPath(r, closestPreExecutionState, transaction);
            }
            }


            item.execute(mTransactionHandler, token, mPendingActions);
            item.execute(mTransactionHandler, token, mPendingActions);
@@ -135,7 +143,7 @@ public class TransactionExecutor {
                // Skip the very last transition and perform it by explicit state request instead.
                // Skip the very last transition and perform it by explicit state request instead.
                final boolean shouldExcludeLastTransition =
                final boolean shouldExcludeLastTransition =
                        i == lastCallbackRequestingState && finalState == postExecutionState;
                        i == lastCallbackRequestingState && finalState == postExecutionState;
                cycleToPath(r, postExecutionState, shouldExcludeLastTransition);
                cycleToPath(r, postExecutionState, shouldExcludeLastTransition, transaction);
            }
            }
        }
        }
    }
    }
@@ -147,10 +155,14 @@ public class TransactionExecutor {
            // No lifecycle request, return early.
            // No lifecycle request, return early.
            return;
            return;
        }
        }
        log("Resolving lifecycle state: " + lifecycleItem);


        final IBinder token = transaction.getActivityToken();
        final IBinder token = transaction.getActivityToken();
        final ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
        final ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
        if (DEBUG_RESOLVER) {
            Slog.d(TAG, tId(transaction) + "Resolving lifecycle state: "
                    + lifecycleItem + " for activity: "
                    + getShortActivityName(token, mTransactionHandler));
        }


        if (r == null) {
        if (r == null) {
            // Ignore requests for non-existent client records for now.
            // Ignore requests for non-existent client records for now.
@@ -158,7 +170,7 @@ public class TransactionExecutor {
        }
        }


        // Cycle to the state right before the final requested state.
        // Cycle to the state right before the final requested state.
        cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */);
        cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */, transaction);


        // Execute the final transition with proper parameters.
        // Execute the final transition with proper parameters.
        lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
        lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
@@ -167,8 +179,8 @@ public class TransactionExecutor {


    /** Transition the client between states. */
    /** Transition the client between states. */
    @VisibleForTesting
    @VisibleForTesting
    public void cycleToPath(ActivityClientRecord r, int finish) {
    public void cycleToPath(ActivityClientRecord r, int finish, ClientTransaction transaction) {
        cycleToPath(r, finish, false /* excludeLastState */);
        cycleToPath(r, finish, false /* excludeLastState */, transaction);
    }
    }


    /**
    /**
@@ -176,20 +188,30 @@ public class TransactionExecutor {
     * sequence. This is used when resolving lifecycle state request, when the last transition must
     * sequence. This is used when resolving lifecycle state request, when the last transition must
     * be performed with some specific parameters.
     * be performed with some specific parameters.
     */
     */
    private void cycleToPath(ActivityClientRecord r, int finish,
    private void cycleToPath(ActivityClientRecord r, int finish, boolean excludeLastState,
            boolean excludeLastState) {
            ClientTransaction transaction) {
        final int start = r.getLifecycleState();
        final int start = r.getLifecycleState();
        log("Cycle from: " + start + " to: " + finish + " excludeLastState:" + excludeLastState);
        if (DEBUG_RESOLVER) {
            Slog.d(TAG, tId(transaction) + "Cycle activity: "
                    + getShortActivityName(r.token, mTransactionHandler)
                    + " from: " + getStateName(start) + " to: " + getStateName(finish)
                    + " excludeLastState: " + excludeLastState);
        }
        final IntArray path = mHelper.getLifecyclePath(start, finish, excludeLastState);
        final IntArray path = mHelper.getLifecyclePath(start, finish, excludeLastState);
        performLifecycleSequence(r, path);
        performLifecycleSequence(r, path, transaction);
    }
    }


    /** Transition the client through previously initialized state sequence. */
    /** Transition the client through previously initialized state sequence. */
    private void performLifecycleSequence(ActivityClientRecord r, IntArray path) {
    private void performLifecycleSequence(ActivityClientRecord r, IntArray path,
            ClientTransaction transaction) {
        final int size = path.size();
        final int size = path.size();
        for (int i = 0, state; i < size; i++) {
        for (int i = 0, state; i < size; i++) {
            state = path.get(i);
            state = path.get(i);
            log("Transitioning to state: " + state);
            if (DEBUG_RESOLVER) {
                Slog.d(TAG, tId(transaction) + "Transitioning activity: "
                        + getShortActivityName(r.token, mTransactionHandler)
                        + " to state: " + getStateName(state));
            }
            switch (state) {
            switch (state) {
                case ON_CREATE:
                case ON_CREATE:
                    mTransactionHandler.handleLaunchActivity(r, mPendingActions,
                    mTransactionHandler.handleLaunchActivity(r, mPendingActions,
@@ -225,8 +247,4 @@ public class TransactionExecutor {
            }
            }
        }
        }
    }
    }

    private static void log(String message) {
        if (DEBUG_RESOLVER) Slog.d(TAG, message);
    }
}
}
+74 −0
Original line number Original line Diff line number Diff line
@@ -26,11 +26,16 @@ import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;


import android.app.Activity;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.os.IBinder;
import android.util.IntArray;
import android.util.IntArray;


import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;


import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
import java.util.List;


/**
/**
@@ -243,4 +248,73 @@ public class TransactionExecutorHelper {


        return lastRequestingCallback;
        return lastRequestingCallback;
    }
    }

    /** Dump transaction to string. */
    static String transactionToString(ClientTransaction transaction,
            ClientTransactionHandler transactionHandler) {
        final StringWriter stringWriter = new StringWriter();
        final PrintWriter pw = new PrintWriter(stringWriter);
        final String prefix = tId(transaction);
        transaction.dump(prefix, pw);
        pw.append(prefix + "Target activity: ")
                .println(getActivityName(transaction.getActivityToken(), transactionHandler));
        return stringWriter.toString();
    }

    /** @return A string in format "tId:<transaction hashcode> ". */
    static String tId(ClientTransaction transaction) {
        return "tId:" + transaction.hashCode() + " ";
    }

    /** Get activity string name for provided token. */
    static String getActivityName(IBinder token, ClientTransactionHandler transactionHandler) {
        final Activity activity = getActivityForToken(token, transactionHandler);
        if (activity != null) {
            return activity.getComponentName().getClassName();
        }
        return "Not found for token: " + token;
    }

    /** Get short activity class name for provided token. */
    static String getShortActivityName(IBinder token, ClientTransactionHandler transactionHandler) {
        final Activity activity = getActivityForToken(token, transactionHandler);
        if (activity != null) {
            return activity.getComponentName().getShortClassName();
        }
        return "Not found for token: " + token;
    }

    private static Activity getActivityForToken(IBinder token,
            ClientTransactionHandler transactionHandler) {
        if (token == null) {
            return null;
        }
        return transactionHandler.getActivity(token);
    }

    /** Get lifecycle state string name. */
    static String getStateName(int state) {
        switch (state) {
            case UNDEFINED:
                return "UNDEFINED";
            case PRE_ON_CREATE:
                return "PRE_ON_CREATE";
            case ON_CREATE:
                return "ON_CREATE";
            case ON_START:
                return "ON_START";
            case ON_RESUME:
                return "ON_RESUME";
            case ON_PAUSE:
                return "ON_PAUSE";
            case ON_STOP:
                return "ON_STOP";
            case ON_DESTROY:
                return "ON_DESTROY";
            case ON_RESTART:
                return "ON_RESTART";
            default:
                throw new IllegalArgumentException("Unexpected lifecycle state: " + state);
        }
    }
}
}
Loading