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

Commit 242b670b authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Avoid too long trace section name to crash FrameTracker" into tm-qpr-dev

parents e2823f9a 41998f86
Loading
Loading
Loading
Loading
+14 −1
Original line number Diff line number Diff line
@@ -66,6 +66,8 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
    private static final long INVALID_ID = -1;
    public static final int NANOS_IN_MILLISECOND = 1_000_000;

    private static final int MAX_LENGTH_EVENT_DESC = 20;

    static final int REASON_END_UNKNOWN = -1;
    static final int REASON_END_NORMAL = 0;
    static final int REASON_END_SURFACE_DESTROYED = 1;
@@ -394,7 +396,18 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
        return true;
    }

    private void markEvent(String desc) {
    /**
     * Mark the FrameTracker events in the trace.
     *
     * @param desc The description of the trace event,
     *            shouldn't exceed {@link #MAX_LENGTH_EVENT_DESC}.
     */
    private void markEvent(@NonNull String desc) {
        if (desc.length() > MAX_LENGTH_EVENT_DESC) {
            throw new IllegalArgumentException(TextUtils.formatSimple(
                    "The length of the trace event description <%s> exceeds %d",
                    desc, MAX_LENGTH_EVENT_DESC));
        }
        Trace.beginSection(TextUtils.formatSimple("%s#%s", mSession.getName(), desc));
        Trace.endSection();
    }
+28 −3
Original line number Diff line number Diff line
@@ -149,6 +149,10 @@ public class InteractionJankMonitor {
    private static final int DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES = 3;
    private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64;

    @VisibleForTesting
    public static final int MAX_LENGTH_OF_CUJ_NAME = 80;
    private static final int MAX_LENGTH_SESSION_NAME = 100;

    public static final String ACTION_SESSION_END = ACTION_PREFIX + ".ACTION_SESSION_END";
    public static final String ACTION_SESSION_CANCEL = ACTION_PREFIX + ".ACTION_SESSION_CANCEL";

@@ -732,6 +736,9 @@ public class InteractionJankMonitor {
     * @return the name of the cuj type
     */
    public static String getNameOfCuj(int cujType) {
        // Please note:
        // 1. The length of the returned string shouldn't exceed MAX_LENGTH_OF_CUJ_NAME.
        // 2. The returned string should be the same with the name defined in atoms.proto.
        switch (cujType) {
            case CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE:
                return "SHADE_EXPAND_COLLAPSE";
@@ -1106,9 +1113,27 @@ public class InteractionJankMonitor {
        public Session(@CujType int cujType, @NonNull String postfix) {
            mCujType = cujType;
            mTimeStamp = System.nanoTime();
            mName = TextUtils.isEmpty(postfix)
                    ? String.format("J<%s>", getNameOfCuj(mCujType))
                    : String.format("J<%s::%s>", getNameOfCuj(mCujType), postfix);
            mName = generateSessionName(getNameOfCuj(cujType), postfix);
        }

        private String generateSessionName(@NonNull String cujName, @NonNull String cujPostfix) {
            final boolean hasPostfix = !TextUtils.isEmpty(cujPostfix);
            // We assert that the cujName shouldn't exceed MAX_LENGTH_OF_CUJ_NAME.
            if (cujName.length() > MAX_LENGTH_OF_CUJ_NAME) {
                throw new IllegalArgumentException(TextUtils.formatSimple(
                        "The length of cuj name <%s> exceeds %d", cujName, MAX_LENGTH_OF_CUJ_NAME));
            }
            if (hasPostfix) {
                final int remaining = MAX_LENGTH_SESSION_NAME - cujName.length();
                if (cujPostfix.length() > remaining) {
                    cujPostfix = cujPostfix.substring(0, remaining - 3).concat("...");
                }
            }
            // The max length of the whole string should be:
            // 105 with postfix, 83 without postfix
            return hasPostfix
                    ? TextUtils.formatSimple("J<%s::%s>", cujName, cujPostfix)
                    : TextUtils.formatSimple("J<%s>", cujName);
        }

        @CujType
+158 −0
Original line number Diff line number Diff line
@@ -16,10 +16,26 @@

package com.android.internal.jank;

import static android.text.TextUtils.formatSimple;

import static com.android.internal.jank.FrameTracker.REASON_CANCEL_TIMEOUT;
import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_LAUNCH_CAMERA;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_ADD;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_APP_START;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_HEADS_UP_APPEAR;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_REMOVE;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_EXPAND;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_SWIPE;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLIT_SCREEN_RESIZE;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_TO_STATSD_INTERACTION_TYPE;
import static com.android.internal.jank.InteractionJankMonitor.MAX_LENGTH_OF_CUJ_NAME;
import static com.android.internal.jank.InteractionJankMonitor.getNameOfCuj;

import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -38,6 +54,7 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
import android.provider.DeviceConfig;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewAttachTestActivity;

@@ -52,8 +69,12 @@ import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
import com.android.internal.jank.FrameTracker.ViewRootWrapper;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
import com.android.internal.jank.InteractionJankMonitor.Session;
import com.android.internal.util.FrameworkStatsLog;

import com.google.common.truth.Expect;

import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@@ -64,11 +85,17 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@SmallTest
public class InteractionJankMonitorTest {
    private static final String CUJ_POSTFIX = "";
    private static final SparseArray<String> ENUM_NAME_EXCEPTION_MAP = new SparseArray<>();
    private static final SparseArray<String> CUJ_NAME_EXCEPTION_MAP = new SparseArray<>();
    private static final String ENUM_NAME_PREFIX =
            "UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__";

    private ViewAttachTestActivity mActivity;
    private View mView;
    private HandlerThread mWorker;
@@ -77,6 +104,53 @@ public class InteractionJankMonitorTest {
    public ActivityTestRule<ViewAttachTestActivity> mRule =
            new ActivityTestRule<>(ViewAttachTestActivity.class);

    @Rule
    public final Expect mExpect = Expect.create();

    @BeforeClass
    public static void initialize() {
        ENUM_NAME_EXCEPTION_MAP.put(CUJ_NOTIFICATION_ADD, getEnumName("SHADE_NOTIFICATION_ADD"));
        ENUM_NAME_EXCEPTION_MAP.put(CUJ_NOTIFICATION_APP_START, getEnumName("SHADE_APP_LAUNCH"));
        ENUM_NAME_EXCEPTION_MAP.put(
                CUJ_NOTIFICATION_HEADS_UP_APPEAR, getEnumName("SHADE_HEADS_UP_APPEAR"));
        ENUM_NAME_EXCEPTION_MAP.put(
                CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR, getEnumName("SHADE_HEADS_UP_DISAPPEAR"));
        ENUM_NAME_EXCEPTION_MAP.put(
                CUJ_NOTIFICATION_REMOVE, getEnumName("SHADE_NOTIFICATION_REMOVE"));
        ENUM_NAME_EXCEPTION_MAP.put(
                CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, getEnumName("NOTIFICATION_SHADE_SWIPE"));
        ENUM_NAME_EXCEPTION_MAP.put(
                CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE, getEnumName("SHADE_QS_EXPAND_COLLAPSE"));
        ENUM_NAME_EXCEPTION_MAP.put(
                CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE, getEnumName("SHADE_QS_SCROLL_SWIPE"));
        ENUM_NAME_EXCEPTION_MAP.put(
                CUJ_NOTIFICATION_SHADE_ROW_EXPAND, getEnumName("SHADE_ROW_EXPAND"));
        ENUM_NAME_EXCEPTION_MAP.put(
                CUJ_NOTIFICATION_SHADE_ROW_SWIPE, getEnumName("SHADE_ROW_SWIPE"));
        ENUM_NAME_EXCEPTION_MAP.put(
                CUJ_NOTIFICATION_SHADE_SCROLL_FLING, getEnumName("SHADE_SCROLL_FLING"));

        CUJ_NAME_EXCEPTION_MAP.put(
                CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, "CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE");
        CUJ_NAME_EXCEPTION_MAP.put(
                CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE,
                "CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE");
        CUJ_NAME_EXCEPTION_MAP.put(
                CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE, "CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE");
        CUJ_NAME_EXCEPTION_MAP.put(
                CUJ_NOTIFICATION_SHADE_ROW_EXPAND, "CUJ_NOTIFICATION_SHADE_ROW_EXPAND");
        CUJ_NAME_EXCEPTION_MAP.put(
                CUJ_NOTIFICATION_SHADE_ROW_SWIPE, "CUJ_NOTIFICATION_SHADE_ROW_SWIPE");
        CUJ_NAME_EXCEPTION_MAP.put(
                CUJ_NOTIFICATION_SHADE_SCROLL_FLING, "CUJ_NOTIFICATION_SHADE_SCROLL_FLING");
        CUJ_NAME_EXCEPTION_MAP.put(CUJ_LOCKSCREEN_LAUNCH_CAMERA, "CUJ_LOCKSCREEN_LAUNCH_CAMERA");
        CUJ_NAME_EXCEPTION_MAP.put(CUJ_SPLIT_SCREEN_RESIZE, "CUJ_SPLIT_SCREEN_RESIZE");
    }

    private static String getEnumName(String name) {
        return formatSimple("%s%s", ENUM_NAME_PREFIX, name);
    }

    @Before
    public void setup() {
        // Prepare an activity for getting ThreadedRenderer later.
@@ -174,6 +248,82 @@ public class InteractionJankMonitorTest {
        }
    }

    @Test
    public void testCujsMapToEnumsCorrectly() {
        List<Field> cujs = Arrays.stream(InteractionJankMonitor.class.getDeclaredFields())
                .filter(f -> f.getName().startsWith("CUJ_")
                        && Modifier.isStatic(f.getModifiers())
                        && f.getType() == int.class)
                .collect(Collectors.toList());

        Map<Integer, String> enumsMap = Arrays.stream(FrameworkStatsLog.class.getDeclaredFields())
                .filter(f -> f.getName().startsWith(ENUM_NAME_PREFIX)
                        && Modifier.isStatic(f.getModifiers())
                        && f.getType() == int.class)
                .collect(Collectors.toMap(this::getIntFieldChecked, Field::getName));

        cujs.forEach(f -> {
            final int cuj = getIntFieldChecked(f);
            final String cujName = f.getName();
            final String expectedEnumName = ENUM_NAME_EXCEPTION_MAP.contains(cuj)
                    ? ENUM_NAME_EXCEPTION_MAP.get(cuj)
                    : formatSimple("%s%s", ENUM_NAME_PREFIX, cujName.substring(4));
            final int enumKey = CUJ_TO_STATSD_INTERACTION_TYPE[cuj];
            final String enumName = enumsMap.get(enumKey);
            final String expectedNameOfCuj = CUJ_NAME_EXCEPTION_MAP.contains(cuj)
                    ? CUJ_NAME_EXCEPTION_MAP.get(cuj)
                    : formatSimple("CUJ_%s", getNameOfCuj(cuj));
            mExpect
                    .withMessage(formatSimple(
                            "%s (%d) not matches %s (%d)", cujName, cuj, enumName, enumKey))
                    .that(expectedEnumName.equals(enumName))
                    .isTrue();
            mExpect
                    .withMessage(formatSimple("getNameOfCuj(%d) not matches %s", cuj, cujName))
                    .that(cujName.equals(expectedNameOfCuj))
                    .isTrue();
        });
    }

    @Test
    public void testCujNameLimit() {
        Arrays.stream(InteractionJankMonitor.class.getDeclaredFields())
                .filter(f -> f.getName().startsWith("CUJ_")
                        && Modifier.isStatic(f.getModifiers())
                        && f.getType() == int.class)
                .forEach(f -> {
                    final int cuj = getIntFieldChecked(f);
                    mExpect
                            .withMessage(formatSimple(
                                    "Too long CUJ(%d) name: %s", cuj, getNameOfCuj(cuj)))
                            .that(getNameOfCuj(cuj).length())
                            .isAtMost(MAX_LENGTH_OF_CUJ_NAME);
                });
    }

    @Test
    public void testSessionNameLengthLimit() {
        final int cujType = CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
        final String cujName = getNameOfCuj(cujType);
        final String cujTag = "ThisIsTheCujTag";
        final String tooLongTag = cujTag.repeat(10);

        // Normal case, no postfix.
        Session noPostfix = new Session(cujType, "");
        assertThat(noPostfix.getName()).isEqualTo(formatSimple("J<%s>", cujName));

        // Normal case, with postfix.
        Session withPostfix = new Session(cujType, cujTag);
        assertThat(withPostfix.getName()).isEqualTo(formatSimple("J<%s::%s>", cujName, cujTag));

        // Since the length of the cuj name is tested in another test, no need to test it here.
        // Too long postfix case, should trim the postfix and keep the cuj name completed.
        final String expectedTrimmedName = formatSimple("J<%s::%s>", cujName,
                "ThisIsTheCujTagThisIsTheCujTagThisIsTheCujTagThisIsTheCujTagThisIsTheCujTagT...");
        Session longPostfix = new Session(cujType, tooLongTag);
        assertThat(longPostfix.getName()).isEqualTo(expectedTrimmedName);
    }

    private InteractionJankMonitor createMockedInteractionJankMonitor() {
        InteractionJankMonitor monitor = spy(new InteractionJankMonitor(mWorker));
        doReturn(true).when(monitor).shouldMonitor(anyInt());
@@ -217,4 +367,12 @@ public class InteractionJankMonitorTest {

        return tracker;
    }

    private int getIntFieldChecked(Field field) {
        try {
            return field.getInt(null);
        } catch (IllegalAccessException ex) {
            throw new RuntimeException(ex);
        }
    }
}