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

Commit 6de2fa1a authored by Nicholas Ambur's avatar Nicholas Ambur Committed by Automerger Merge Worker
Browse files

Merge changes I4f8d21bc,I1560d62c into udc-dev am: 29805915

parents 53cb5a7c 29805915
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
# TODO(b/274465475): Migrate LatencyTracker testing to its own module
marcinoc@google.com
ilkos@google.com
+162 −46
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ import static com.android.internal.util.LatencyTracker.ActionProperties.SAMPLE_I
import static com.android.internal.util.LatencyTracker.ActionProperties.TRACE_THRESHOLD_SUFFIX;

import android.Manifest;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -55,7 +56,6 @@ import android.annotation.RequiresPermission;
import android.app.ActivityThread;
import android.content.Context;
import android.os.Build;
import android.os.ConditionVariable;
import android.os.SystemClock;
import android.os.Trace;
import android.provider.DeviceConfig;
@@ -79,7 +79,7 @@ import java.util.concurrent.TimeUnit;
 * Class to track various latencies in SystemUI. It then writes the latency to statsd and also
 * outputs it to logcat so these latencies can be captured by tests and then used for dashboards.
 * <p>
 * This is currently only in Keyguard so it can be shared between SystemUI and Keyguard, but
 * This is currently only in Keyguard. It can be shared between SystemUI and Keyguard, but
 * eventually we'd want to merge these two packages together so Keyguard can use common classes
 * that are shared with SystemUI.
 */
@@ -285,8 +285,6 @@ public class LatencyTracker {
            UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_HIDDEN,
    };

    private static LatencyTracker sLatencyTracker;

    private final Object mLock = new Object();
    @GuardedBy("mLock")
    private final SparseArray<Session> mSessions = new SparseArray<>();
@@ -294,20 +292,21 @@ public class LatencyTracker {
    private final SparseArray<ActionProperties> mActionPropertiesMap = new SparseArray<>();
    @GuardedBy("mLock")
    private boolean mEnabled;
    @VisibleForTesting
    public final ConditionVariable mDeviceConfigPropertiesUpdated = new ConditionVariable();

    public static LatencyTracker getInstance(Context context) {
        if (sLatencyTracker == null) {
            synchronized (LatencyTracker.class) {
                if (sLatencyTracker == null) {
                    sLatencyTracker = new LatencyTracker();
    // Wrapping this in a holder class achieves lazy loading behavior
    private static final class SLatencyTrackerHolder {
        private static final LatencyTracker sLatencyTracker = new LatencyTracker();
    }
            }
        }
        return sLatencyTracker;

    public static LatencyTracker getInstance(Context context) {
        return SLatencyTrackerHolder.sLatencyTracker;
    }

    /**
     * Constructor for LatencyTracker
     *
     * <p>This constructor is only visible for test classes to inject their own consumer callbacks
     */
    @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
    @VisibleForTesting
    public LatencyTracker() {
@@ -349,11 +348,8 @@ public class LatencyTracker {
                        properties.getInt(actionName + TRACE_THRESHOLD_SUFFIX,
                                legacyActionTraceThreshold)));
            }
            if (DEBUG) {
                Log.d(TAG, "updated action properties: " + mActionPropertiesMap);
            }
            onDeviceConfigPropertiesUpdated(mActionPropertiesMap);
        }
        mDeviceConfigPropertiesUpdated.open();
    }

    /**
@@ -477,7 +473,7 @@ public class LatencyTracker {
     */
    public void onActionStart(@Action int action, String tag) {
        synchronized (mLock) {
            if (!isEnabled()) {
            if (!isEnabled(action)) {
                return;
            }
            // skip if the action is already instrumenting.
@@ -501,7 +497,7 @@ public class LatencyTracker {
     */
    public void onActionEnd(@Action int action) {
        synchronized (mLock) {
            if (!isEnabled()) {
            if (!isEnabled(action)) {
                return;
            }
            Session session = mSessions.get(action);
@@ -539,6 +535,24 @@ public class LatencyTracker {
        }
    }

    /**
     * Testing API to get the time when a given action was started.
     *
     * @param action Action which to retrieve start time from
     * @return Elapsed realtime timestamp when the action started. -1 if the action is not active.
     * @hide
     */
    @VisibleForTesting
    @ElapsedRealtimeLong
    public long getActiveActionStartTime(@Action int action) {
        synchronized (mLock) {
            if (mSessions.contains(action)) {
                return mSessions.get(action).mStartRtc;
            }
            return -1;
        }
    }

    /**
     * Logs an action that has started and ended. This needs to be called from the main thread.
     *
@@ -549,6 +563,9 @@ public class LatencyTracker {
        boolean shouldSample;
        int traceThreshold;
        synchronized (mLock) {
            if (!isEnabled(action)) {
                return;
            }
            ActionProperties actionProperties = mActionPropertiesMap.get(action);
            if (actionProperties == null) {
                return;
@@ -559,28 +576,24 @@ public class LatencyTracker {
            traceThreshold = actionProperties.getTraceThreshold();
        }

        if (traceThreshold > 0 && duration >= traceThreshold) {
            PerfettoTrigger.trigger(getTraceTriggerNameForAction(action));
        }
        boolean shouldTriggerPerfettoTrace = traceThreshold > 0 && duration >= traceThreshold;

        logActionDeprecated(action, duration, shouldSample);
        if (DEBUG) {
            Log.i(TAG, "logAction: " + getNameOfAction(STATSD_ACTION[action])
                    + " duration=" + duration
                    + " shouldSample=" + shouldSample
                    + " shouldTriggerPerfettoTrace=" + shouldTriggerPerfettoTrace);
        }

    /**
     * Logs an action that has started and ended. This needs to be called from the main thread.
     *
     * @param action The action to end. One of the ACTION_* values.
     * @param duration The duration of the action in ms.
     * @param writeToStatsLog Whether to write the measured latency to FrameworkStatsLog.
     */
    public static void logActionDeprecated(
            @Action int action, int duration, boolean writeToStatsLog) {
        Log.i(TAG, getNameOfAction(STATSD_ACTION[action]) + " latency=" + duration);
        EventLog.writeEvent(EventLogTags.SYSUI_LATENCY, action, duration);

        if (writeToStatsLog) {
            FrameworkStatsLog.write(
                    FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED, STATSD_ACTION[action], duration);
        if (shouldTriggerPerfettoTrace) {
            onTriggerPerfetto(getTraceTriggerNameForAction(action));
        }
        if (shouldSample) {
            onLogToFrameworkStats(
                    new FrameworkStatsLogEvent(action, FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED,
                            STATSD_ACTION[action], duration)
            );
        }
    }

@@ -642,10 +655,10 @@ public class LatencyTracker {
    }

    @VisibleForTesting
    static class ActionProperties {
    public static class ActionProperties {
        static final String ENABLE_SUFFIX = "_enable";
        static final String SAMPLE_INTERVAL_SUFFIX = "_sample_interval";
        // TODO: migrate all usages of the legacy trace theshold property
        // TODO: migrate all usages of the legacy trace threshold property
        static final String LEGACY_TRACE_THRESHOLD_SUFFIX = "";
        static final String TRACE_THRESHOLD_SUFFIX = "_trace_threshold";

@@ -655,7 +668,8 @@ public class LatencyTracker {
        private final int mSamplingInterval;
        private final int mTraceThreshold;

        ActionProperties(
        @VisibleForTesting
        public ActionProperties(
                @Action int action,
                boolean enabled,
                int samplingInterval,
@@ -668,20 +682,24 @@ public class LatencyTracker {
            this.mTraceThreshold = traceThreshold;
        }

        @VisibleForTesting
        @Action
        int getAction() {
        public int getAction() {
            return mAction;
        }

        boolean isEnabled() {
        @VisibleForTesting
        public boolean isEnabled() {
            return mEnabled;
        }

        int getSamplingInterval() {
        @VisibleForTesting
        public int getSamplingInterval() {
            return mSamplingInterval;
        }

        int getTraceThreshold() {
        @VisibleForTesting
        public int getTraceThreshold() {
            return mTraceThreshold;
        }

@@ -694,5 +712,103 @@ public class LatencyTracker {
                    + ", mTraceThreshold=" + mTraceThreshold
                    + "}";
        }

        @Override
        public boolean equals(@Nullable Object o) {
            if (this == o) {
                return true;
            }
            if (o == null) {
                return false;
            }
            if (!(o instanceof ActionProperties)) {
                return false;
            }
            ActionProperties that = (ActionProperties) o;
            return mAction == that.mAction
                    && mEnabled == that.mEnabled
                    && mSamplingInterval == that.mSamplingInterval
                    && mTraceThreshold == that.mTraceThreshold;
        }

        @Override
        public int hashCode() {
            int _hash = 1;
            _hash = 31 * _hash + mAction;
            _hash = 31 * _hash + Boolean.hashCode(mEnabled);
            _hash = 31 * _hash + mSamplingInterval;
            _hash = 31 * _hash + mTraceThreshold;
            return _hash;
        }
    }

    /**
     * Testing method intended to be overridden to determine when the LatencyTracker's device
     * properties are updated.
     */
    @VisibleForTesting
    public void onDeviceConfigPropertiesUpdated(SparseArray<ActionProperties> actionProperties) {
        if (DEBUG) {
            Log.d(TAG, "onDeviceConfigPropertiesUpdated: " + actionProperties);
        }
    }

    /**
     * Testing class intended to be overridden to determine when LatencyTracker triggers perfetto.
     */
    @VisibleForTesting
    public void onTriggerPerfetto(String triggerName) {
        if (DEBUG) {
            Log.i(TAG, "onTriggerPerfetto: triggerName=" + triggerName);
        }
        PerfettoTrigger.trigger(triggerName);
    }

    /**
     * Testing method intended to be overridden to determine when LatencyTracker writes to
     * FrameworkStatsLog.
     */
    @VisibleForTesting
    public void onLogToFrameworkStats(FrameworkStatsLogEvent event) {
        if (DEBUG) {
            Log.i(TAG, "onLogToFrameworkStats: event=" + event);
        }
        FrameworkStatsLog.write(event.logCode, event.statsdAction, event.durationMillis);
    }

    /**
     * Testing class intended to reject what should be written to the {@link FrameworkStatsLog}
     *
     * <p>This class is used in {@link #onLogToFrameworkStats(FrameworkStatsLogEvent)} for test code
     * to observer when and what information is being logged by {@link LatencyTracker}
     */
    @VisibleForTesting
    public static class FrameworkStatsLogEvent {

        @VisibleForTesting
        public final int action;
        @VisibleForTesting
        public final int logCode;
        @VisibleForTesting
        public final int statsdAction;
        @VisibleForTesting
        public final int durationMillis;

        private FrameworkStatsLogEvent(int action, int logCode, int statsdAction,
                int durationMillis) {
            this.action = action;
            this.logCode = logCode;
            this.statsdAction = statsdAction;
            this.durationMillis = durationMillis;
        }

        @Override
        public String toString() {
            return "FrameworkStatsLogEvent{"
                    + " logCode=" + logCode
                    + ", statsdAction=" + statsdAction
                    + ", durationMillis=" + durationMillis
                    + "}";
        }
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -6,3 +6,4 @@ per-file Protocol* = etancohen@google.com, lorenzo@google.com
per-file State* = jchalard@google.com, lorenzo@google.com, satk@google.com
per-file *Dump* = file:/core/java/com/android/internal/util/dump/OWNERS
per-file *Screenshot* = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
per-file *LatencyTracker* = file:/core/java/com/android/internal/util/LATENCY_TRACKER_OWNERS
+1 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ android_test {
        "BinderProxyCountingTestService/src/**/*.java",
        "BinderDeathRecipientHelperApp/src/**/*.java",
        "aidl/**/I*.aidl",
        ":FrameworksCoreTestDoubles-sources",
    ],

    aidl: {
+67 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.util;

import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION;
import static com.android.internal.util.FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED;
import static com.android.internal.util.LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;

import static com.google.common.truth.Truth.assertThat;

import androidx.test.ext.junit.runners.AndroidJUnit4;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.List;

/**
 * This test class verifies the additional methods which {@link FakeLatencyTracker} exposes.
 *
 * <p>The typical {@link LatencyTracker} behavior test coverage is present in
 * {@link LatencyTrackerTest}
 */
@RunWith(AndroidJUnit4.class)
public class FakeLatencyTrackerTest {

    private FakeLatencyTracker mFakeLatencyTracker;

    @Before
    public void setUp() throws Exception {
        mFakeLatencyTracker = FakeLatencyTracker.create();
    }

    @Test
    public void testForceEnabled() throws Exception {
        mFakeLatencyTracker.logAction(ACTION_SHOW_VOICE_INTERACTION, 1234);

        assertThat(mFakeLatencyTracker.getEventsWrittenToFrameworkStats(
                ACTION_SHOW_VOICE_INTERACTION)).isEmpty();

        mFakeLatencyTracker.forceEnabled(ACTION_SHOW_VOICE_INTERACTION, 1000);
        mFakeLatencyTracker.logAction(ACTION_SHOW_VOICE_INTERACTION, 1234);
        List<LatencyTracker.FrameworkStatsLogEvent> events =
                mFakeLatencyTracker.getEventsWrittenToFrameworkStats(
                        ACTION_SHOW_VOICE_INTERACTION);
        assertThat(events).hasSize(1);
        assertThat(events.get(0).logCode).isEqualTo(UI_ACTION_LATENCY_REPORTED);
        assertThat(events.get(0).statsdAction).isEqualTo(
                UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION);
        assertThat(events.get(0).durationMillis).isEqualTo(1234);
    }
}
Loading