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

Commit 29a5abd4 authored by Thomas Stuart's avatar Thomas Stuart
Browse files

Correct tree printing logic to prevent crash

The previous iterative implementation for printing the session tree in
`printSessionTreeLegacy` contained flawed logic for calculating indentation
depth. This led to two issues:

1. A `java.lang.IllegalArgumentException` was thrown when the depth counter
   became negative.
2. For most tree structures, the logged output was incorrectly indented,
   making it difficult to read.

This change replaces the buggy method with a standard, recursive
implementation (`printSessionTree`) that properly handles indentation.
This resolves both the crash and the visual bug in the logs.

Flag: com.android.server.telecom.flags.fix_session_tree_logging
Fixes: 427861789
Test: new unit test
Change-Id: Icfd1cae9be02c5fec8387053dc78f26bc1d5ff08
parent b1640c13
Loading
Loading
Loading
Loading
+49 −2
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.telecom.Log;
import android.text.TextUtils;

import com.android.internal.annotations.VisibleForTesting;
import com.android.server.telecom.flags.Flags;

import java.util.ArrayDeque;
import java.util.ArrayList;
@@ -308,10 +309,14 @@ public class Session {

    // Print out the full Session tree from any subsession node
    public String printFullSessionTree() {
        if (Flags.fixSessionTreeLogging()) {
            return getRootSession("printFullSessionTree").printSessionTree();
        } else {
            return getRootSession("printFullSessionTree").printSessionTreeLegacy();
        }
    }

    private String printSessionTree() {
    private String printSessionTreeLegacy() {
        StringBuilder sb = new StringBuilder();
        int depth = 0;
        ArrayDeque<Session> deque = new ArrayDeque<>();
@@ -339,6 +344,48 @@ public class Session {
        return sb.toString();
    }

    private String printSessionTree() {
        final StringBuilder sb = new StringBuilder();
        // Use an ArrayDeque as a stack for nodes to visit.
        final ArrayDeque<Session> sessionStack = new ArrayDeque<>();
        // Use a parallel stack to track the depth of each node.
        final ArrayDeque<Integer> depthStack = new ArrayDeque<>();

        // Start with the current session at depth 0.
        sessionStack.push(this);
        depthStack.push(0);

        while (!sessionStack.isEmpty()) {
            // Pop the next session and its corresponding depth.
            final Session session = sessionStack.pop();
            final int depth = depthStack.pop();

            // Append indented session information.
            sb.append("\t".repeat(depth));
            sb.append(session.toString());
            sb.append("\n");

            // If the depth limit is reached, print a truncation marker and stop descending.
            if (depth >= SESSION_RECURSION_LIMIT) {
                sb.append("\t".repeat(depth + 1));
                sb.append(TRUNCATE_STRING);
                sb.append("\n");
                continue;
            }

            final List<Session> childSessions = session.getChildSessions();
            // Push children onto the stack in reverse order. This ensures they are
            // processed in their natural (first-to-last) order due to the LIFO
            // nature of the stack.
            for (int i = childSessions.size() - 1; i >= 0; i--) {
                sessionStack.push(childSessions.get(i));
                depthStack.push(depth + 1);
            }
        }

        return sb.toString();
    }

    /**
     * Concatenate the short method name with the parent Sessions to create full method path.
     * @param truncatePath if truncatePath is set to true, all other external sessions (except for