diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java index e0f95544d5882..3d896c866c8b3 100644 --- a/core/java/com/android/internal/jank/InteractionJankMonitor.java +++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java @@ -59,6 +59,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.FrameTracker.FrameMetricsWrapper; import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper; import com.android.internal.jank.FrameTracker.ViewRootWrapper; +import com.android.internal.util.PerfettoTrigger; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/core/java/com/android/internal/jank/PerfettoTrigger.java b/core/java/com/android/internal/jank/PerfettoTrigger.java deleted file mode 100644 index 643d24a51b58c..0000000000000 --- a/core/java/com/android/internal/jank/PerfettoTrigger.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2020 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. - */ - -//TODO (165884885): Make PerfettoTrigger more generic and move it to another package. -package com.android.internal.jank; - -import android.annotation.NonNull; -import android.util.Log; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; - -/** - * A trigger implementation with perfetto backend. - * @hide - */ -public class PerfettoTrigger { - private static final String TAG = PerfettoTrigger.class.getSimpleName(); - private static final boolean DEBUG = false; - private static final String TRIGGER_COMMAND = "/system/bin/trigger_perfetto"; - - /** - * @param triggerName The name of the trigger. Must match the value defined in the AOT - * Perfetto config. - */ - public static void trigger(String triggerName) { - try { - ProcessBuilder pb = new ProcessBuilder(TRIGGER_COMMAND, triggerName); - if (DEBUG) { - StringBuilder sb = new StringBuilder(); - for (String arg : pb.command()) { - sb.append(arg).append(" "); - } - Log.d(TAG, "Triggering " + sb.toString()); - } - Process process = pb.start(); - if (DEBUG) { - readConsoleOutput(process); - } - } catch (IOException | InterruptedException e) { - Log.w(TAG, "Failed to trigger " + triggerName, e); - } - } - - private static void readConsoleOutput(@NonNull Process process) - throws IOException, InterruptedException { - process.waitFor(); - try (BufferedReader errReader = - new BufferedReader(new InputStreamReader(process.getErrorStream()))) { - StringBuilder errLine = new StringBuilder(); - String line; - while ((line = errReader.readLine()) != null) { - errLine.append(line).append("\n"); - } - errLine.append(", code=").append(process.exitValue()); - Log.d(TAG, "err message=" + errLine.toString()); - } - } -} diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java index 30cd94c21169c..f42f468aefa08 100644 --- a/core/java/com/android/internal/util/LatencyTracker.java +++ b/core/java/com/android/internal/util/LatencyTracker.java @@ -14,6 +14,7 @@ package com.android.internal.util; +import android.annotation.IntDef; import android.content.Context; import android.os.Build; import android.os.SystemClock; @@ -23,9 +24,12 @@ import android.util.EventLog; import android.util.Log; import android.util.SparseLongArray; +import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.EventLogTags; import com.android.internal.os.BackgroundThread; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.concurrent.ThreadLocalRandom; /** @@ -91,6 +95,34 @@ public class LatencyTracker { */ public static final int ACTION_START_RECENTS_ANIMATION = 8; + private static final int[] ACTIONS_ALL = { + ACTION_EXPAND_PANEL, + ACTION_TOGGLE_RECENTS, + ACTION_FINGERPRINT_WAKE_AND_UNLOCK, + ACTION_CHECK_CREDENTIAL, + ACTION_CHECK_CREDENTIAL_UNLOCKED, + ACTION_TURN_ON_SCREEN, + ACTION_ROTATE_SCREEN, + ACTION_FACE_WAKE_AND_UNLOCK, + ACTION_START_RECENTS_ANIMATION + }; + + /** @hide */ + @IntDef({ + ACTION_EXPAND_PANEL, + ACTION_TOGGLE_RECENTS, + ACTION_FINGERPRINT_WAKE_AND_UNLOCK, + ACTION_CHECK_CREDENTIAL, + ACTION_CHECK_CREDENTIAL_UNLOCKED, + ACTION_TURN_ON_SCREEN, + ACTION_ROTATE_SCREEN, + ACTION_FACE_WAKE_AND_UNLOCK, + ACTION_START_RECENTS_ANIMATION + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Action { + } + private static final int[] STATSD_ACTION = new int[]{ FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL, FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS, @@ -105,9 +137,14 @@ public class LatencyTracker { private static LatencyTracker sLatencyTracker; + private final Object mLock = new Object(); private final SparseLongArray mStartRtc = new SparseLongArray(); - private volatile int mSamplingInterval; - private volatile boolean mEnabled; + @GuardedBy("mLock") + private final int[] mTraceThresholdPerAction = new int[ACTIONS_ALL.length]; + @GuardedBy("mLock") + private int mSamplingInterval; + @GuardedBy("mLock") + private boolean mEnabled; public static LatencyTracker getInstance(Context context) { if (sLatencyTracker == null) { @@ -132,20 +169,26 @@ public class LatencyTracker { } private void updateProperties(DeviceConfig.Properties properties) { - mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY, - DEFAULT_SAMPLING_INTERVAL); - mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED); + synchronized (mLock) { + mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY, + DEFAULT_SAMPLING_INTERVAL); + mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED); + for (int action : ACTIONS_ALL) { + mTraceThresholdPerAction[action] = + properties.getInt(getTraceTriggerNameForAction(action), -1); + } + } } /** * A helper method to translate action type to name. * - * @param action the action type defined in AtomsProto.java + * @param atomsProtoAction the action type defined in AtomsProto.java * @return the name of the action */ - public static String getNameOfAction(int action) { + public static String getNameOfAction(int atomsProtoAction) { // Defined in AtomsProto.java - switch (action) { + switch (atomsProtoAction) { case 0: return "UNKNOWN"; case 1: @@ -171,16 +214,22 @@ public class LatencyTracker { } } - private static String getTraceNameOfAction(int action) { + private static String getTraceNameOfAction(@Action int action) { return "L<" + getNameOfAction(STATSD_ACTION[action]) + ">"; } + private static String getTraceTriggerNameForAction(@Action int action) { + return "latency-tracker-" + getNameOfAction(STATSD_ACTION[action]); + } + public static boolean isEnabled(Context ctx) { return getInstance(ctx).isEnabled(); } public boolean isEnabled() { - return mEnabled; + synchronized (mLock) { + return mEnabled; + } } /** @@ -188,7 +237,7 @@ public class LatencyTracker { * * @param action The action to start. One of the ACTION_* values. */ - public void onActionStart(int action) { + public void onActionStart(@Action int action) { if (!isEnabled()) { return; } @@ -201,7 +250,7 @@ public class LatencyTracker { * * @param action The action to end. One of the ACTION_* values. */ - public void onActionEnd(int action) { + public void onActionEnd(@Action int action) { if (!isEnabled()) { return; } @@ -221,19 +270,30 @@ public class LatencyTracker { * @param action The action to end. One of the ACTION_* values. * @param duration The duration of the action in ms. */ - public void logAction(int action, int duration) { - boolean shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0; + public void logAction(@Action int action, int duration) { + boolean shouldSample; + int traceThreshold; + synchronized (mLock) { + shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0; + traceThreshold = mTraceThresholdPerAction[action]; + } + + if (traceThreshold > 0 && duration >= traceThreshold) { + PerfettoTrigger.trigger(getTraceTriggerNameForAction(action)); + } + logActionDeprecated(action, duration, shouldSample); } /** * 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 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(int action, int duration, boolean writeToStatsLog) { + 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); diff --git a/core/java/com/android/internal/util/PerfettoTrigger.java b/core/java/com/android/internal/util/PerfettoTrigger.java new file mode 100644 index 0000000000000..9c87c697a7376 --- /dev/null +++ b/core/java/com/android/internal/util/PerfettoTrigger.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2020 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 android.util.Log; + +import java.io.IOException; + +/** + * A trigger implementation with perfetto backend. + * @hide + */ +public class PerfettoTrigger { + private static final String TAG = "PerfettoTrigger"; + private static final String TRIGGER_COMMAND = "/system/bin/trigger_perfetto"; + + /** + * @param triggerName The name of the trigger. Must match the value defined in the AOT + * Perfetto config. + */ + public static void trigger(String triggerName) { + try { + ProcessBuilder pb = new ProcessBuilder(TRIGGER_COMMAND, triggerName); + Log.v(TAG, "Triggering " + String.join(" ", pb.command())); + Process process = pb.start(); + } catch (IOException e) { + Log.w(TAG, "Failed to trigger " + triggerName, e); + } + } +}