blob: fbc92c1f99c440ec62b574b7b74ea6d8757f9a8a [file] [log] [blame]
/*
* 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.jank;
import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY;
import static com.android.internal.jank.FrameTracker.ChoreographerWrapper;
import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_ICON;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_APPEAR;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_DISAPPEAR;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_APPEAR;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_DISAPPEAR;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_APPEAR;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_DISAPPEAR;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_FROM_AOD;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_TO_AOD;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_COLLAPSE_LOCK;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_APPEAR;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_DISAPPEAR;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_ADD;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_REMOVE;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_EXPAND_COLLAPSE;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_SCROLL_SWIPE;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.SystemProperties;
import android.provider.DeviceConfig;
import android.util.Log;
import android.util.SparseArray;
import android.view.Choreographer;
import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
import com.android.internal.jank.FrameTracker.FrameTrackerListener;
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;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
/**
* This class let users to begin and end the always on tracing mechanism.
* @hide
*/
public class InteractionJankMonitor {
private static final String TAG = InteractionJankMonitor.class.getSimpleName();
private static final String ACTION_PREFIX = InteractionJankMonitor.class.getCanonicalName();
private static final String DEFAULT_WORKER_NAME = TAG + "-Worker";
private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5L);
private static final String SETTINGS_ENABLED_KEY = "enabled";
private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
private static final String SETTINGS_THRESHOLD_MISSED_FRAMES_KEY =
"trace_threshold_missed_frames";
private static final String SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY =
"trace_threshold_frame_time_millis";
/** Default to being enabled on debug builds. */
private static final boolean DEFAULT_ENABLED = Build.IS_DEBUGGABLE;
/** Default to collecting data for all CUJs. */
private static final int DEFAULT_SAMPLING_INTERVAL = 1;
/** Default to triggering trace if 3 frames are missed OR a frame takes at least 64ms */
private static final int DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES = 3;
private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64;
public static final String ACTION_SESSION_BEGIN = ACTION_PREFIX + ".ACTION_SESSION_BEGIN";
public static final String ACTION_SESSION_CANCEL = ACTION_PREFIX + ".ACTION_SESSION_CANCEL";
public static final String ACTION_METRICS_LOGGED = ACTION_PREFIX + ".ACTION_METRICS_LOGGED";
public static final String BUNDLE_KEY_CUJ_NAME = ACTION_PREFIX + ".CUJ_NAME";
public static final String BUNDLE_KEY_TIMESTAMP = ACTION_PREFIX + ".TIMESTAMP";
@VisibleForTesting
public static final String PROP_NOTIFY_CUJ_EVENT = "debug.notify_cuj_events";
// Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE.
public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0;
public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK = 1;
public static final int CUJ_NOTIFICATION_SHADE_SCROLL_FLING = 2;
public static final int CUJ_NOTIFICATION_SHADE_ROW_EXPAND = 3;
public static final int CUJ_NOTIFICATION_SHADE_ROW_SWIPE = 4;
public static final int CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE = 5;
public static final int CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE = 6;
public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS = 7;
public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON = 8;
public static final int CUJ_LAUNCHER_APP_CLOSE_TO_HOME = 9;
public static final int CUJ_LAUNCHER_APP_CLOSE_TO_PIP = 10;
public static final int CUJ_LAUNCHER_QUICK_SWITCH = 11;
public static final int CUJ_NOTIFICATION_HEADS_UP_APPEAR = 12;
public static final int CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR = 13;
public static final int CUJ_NOTIFICATION_ADD = 14;
public static final int CUJ_NOTIFICATION_REMOVE = 15;
public static final int CUJ_NOTIFICATION_APP_START = 16;
public static final int CUJ_LOCKSCREEN_PASSWORD_APPEAR = 17;
public static final int CUJ_LOCKSCREEN_PATTERN_APPEAR = 18;
public static final int CUJ_LOCKSCREEN_PIN_APPEAR = 19;
public static final int CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR = 20;
public static final int CUJ_LOCKSCREEN_PATTERN_DISAPPEAR = 21;
public static final int CUJ_LOCKSCREEN_PIN_DISAPPEAR = 22;
public static final int CUJ_LOCKSCREEN_TRANSITION_FROM_AOD = 23;
public static final int CUJ_LOCKSCREEN_TRANSITION_TO_AOD = 24;
public static final int CUJ_LAUNCHER_OPEN_ALL_APPS = 25;
public static final int CUJ_LAUNCHER_ALL_APPS_SCROLL = 26;
private static final int NO_STATSD_LOGGING = -1;
// Used to convert CujType to InteractionType enum value for statsd logging.
// Use NO_STATSD_LOGGING in case the measurement for a given CUJ should not be logged to statsd.
@VisibleForTesting
public static final int[] CUJ_TO_STATSD_INTERACTION_TYPE = {
// This should be mapping to CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE.
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_COLLAPSE_LOCK,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_EXPAND_COLLAPSE,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_SCROLL_SWIPE,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_ICON,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_APPEAR,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_DISAPPEAR,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_ADD,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_REMOVE,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_APPEAR,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_APPEAR,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_APPEAR,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_DISAPPEAR,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_DISAPPEAR,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_DISAPPEAR,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_FROM_AOD,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_TO_AOD,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL,
};
private static volatile InteractionJankMonitor sInstance;
private final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener =
this::updateProperties;
private FrameMetricsWrapper mMetrics;
private SparseArray<FrameTracker> mRunningTrackers;
private SparseArray<Runnable> mTimeoutActions;
private HandlerThread mWorker;
private boolean mEnabled = DEFAULT_ENABLED;
private int mSamplingInterval = DEFAULT_SAMPLING_INTERVAL;
private int mTraceThresholdMissedFrames = DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES;
private int mTraceThresholdFrameTimeMillis = DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS;
/** @hide */
@IntDef({
CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE,
CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK,
CUJ_NOTIFICATION_SHADE_SCROLL_FLING,
CUJ_NOTIFICATION_SHADE_ROW_EXPAND,
CUJ_NOTIFICATION_SHADE_ROW_SWIPE,
CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE,
CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE,
CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS,
CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON,
CUJ_LAUNCHER_APP_CLOSE_TO_HOME,
CUJ_LAUNCHER_APP_CLOSE_TO_PIP,
CUJ_LAUNCHER_QUICK_SWITCH,
CUJ_NOTIFICATION_HEADS_UP_APPEAR,
CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR,
CUJ_NOTIFICATION_ADD,
CUJ_NOTIFICATION_REMOVE,
CUJ_NOTIFICATION_APP_START,
CUJ_LOCKSCREEN_PASSWORD_APPEAR,
CUJ_LOCKSCREEN_PATTERN_APPEAR,
CUJ_LOCKSCREEN_PIN_APPEAR,
CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR,
CUJ_LOCKSCREEN_PATTERN_DISAPPEAR,
CUJ_LOCKSCREEN_PIN_DISAPPEAR,
CUJ_LOCKSCREEN_TRANSITION_FROM_AOD,
CUJ_LOCKSCREEN_TRANSITION_TO_AOD,
CUJ_LAUNCHER_OPEN_ALL_APPS,
CUJ_LAUNCHER_ALL_APPS_SCROLL,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
}
/**
* Get the singleton of InteractionJankMonitor.
*
* @return instance of InteractionJankMonitor
*/
public static InteractionJankMonitor getInstance() {
// Use DCL here since this method might be invoked very often.
if (sInstance == null) {
synchronized (InteractionJankMonitor.class) {
if (sInstance == null) {
sInstance = new InteractionJankMonitor(new HandlerThread(DEFAULT_WORKER_NAME));
}
}
}
return sInstance;
}
/**
* This constructor should be only public to tests.
*
* @param worker the worker thread for the callbacks
*/
@VisibleForTesting
public InteractionJankMonitor(@NonNull HandlerThread worker) {
mRunningTrackers = new SparseArray<>();
mTimeoutActions = new SparseArray<>();
mWorker = worker;
mMetrics = new FrameMetricsWrapper();
mWorker.start();
mEnabled = DEFAULT_ENABLED;
mSamplingInterval = DEFAULT_SAMPLING_INTERVAL;
// Post initialization to the background in case we're running on the main
// thread.
mWorker.getThreadHandler().post(
() -> mPropertiesChangedListener.onPropertiesChanged(
DeviceConfig.getProperties(
DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR)));
DeviceConfig.addOnPropertiesChangedListener(
DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR,
new HandlerExecutor(mWorker.getThreadHandler()),
mPropertiesChangedListener);
}
/**
* Create a {@link FrameTracker} instance.
*
* @param session the session associates with this tracker
* @return instance of the FrameTracker
*/
@VisibleForTesting
public FrameTracker createFrameTracker(View v, Session session) {
final Context c = v.getContext().getApplicationContext();
synchronized (this) {
boolean needListener = SystemProperties.getBoolean(PROP_NOTIFY_CUJ_EVENT, false);
FrameTrackerListener eventsListener =
!needListener ? null : (s, act) -> notifyEvents(c, act, s);
return new FrameTracker(session, mWorker.getThreadHandler(),
new ThreadedRendererWrapper(v.getThreadedRenderer()),
new ViewRootWrapper(v.getViewRootImpl()), new SurfaceControlWrapper(),
new ChoreographerWrapper(Choreographer.getInstance()), mMetrics,
mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis, eventsListener);
}
}
private void notifyEvents(Context context, String action, Session session) {
Intent intent = new Intent(action);
intent.putExtra(BUNDLE_KEY_CUJ_NAME, getNameOfCuj(session.getCuj()));
intent.putExtra(BUNDLE_KEY_TIMESTAMP, session.getTimeStamp());
intent.addFlags(FLAG_RECEIVER_REGISTERED_ONLY);
context.sendBroadcast(intent);
}
/**
* Begin a trace session.
*
* @param cujType the specific {@link InteractionJankMonitor.CujType}.
* @return boolean true if the tracker is started successfully, false otherwise.
*/
public boolean begin(View v, @CujType int cujType) {
synchronized (this) {
return begin(v, cujType, DEFAULT_TIMEOUT_MS);
}
}
/**
* Begin a trace session.
*
* @param cujType the specific {@link InteractionJankMonitor.CujType}.
* @param timeout the elapsed time in ms until firing the timeout action.
* @return boolean true if the tracker is started successfully, false otherwise.
*/
public boolean begin(View v, @CujType int cujType, long timeout) {
synchronized (this) {
if (!v.isAttachedToWindow()) {
Log.d(TAG, "View not attached!", new Throwable());
return false;
}
boolean shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0;
if (!mEnabled || !shouldSample) {
return false;
}
FrameTracker tracker = getTracker(cujType);
// Skip subsequent calls if we already have an ongoing tracing.
if (tracker != null) return false;
// begin a new trace session.
tracker = createFrameTracker(v, new Session(cujType));
mRunningTrackers.put(cujType, tracker);
tracker.begin();
// Cancel the trace if we don't get an end() call in specified duration.
Runnable timeoutAction = () -> cancel(cujType);
mTimeoutActions.put(cujType, timeoutAction);
mWorker.getThreadHandler().postDelayed(timeoutAction, timeout);
return true;
}
}
/**
* End a trace session.
*
* @param cujType the specific {@link InteractionJankMonitor.CujType}.
* @return boolean true if the tracker is ended successfully, false otherwise.
*/
public boolean end(@CujType int cujType) {
//TODO (163505250): This should be no-op if not in droid food rom.
synchronized (this) {
// remove the timeout action first.
Runnable timeout = mTimeoutActions.get(cujType);
if (timeout != null) {
mWorker.getThreadHandler().removeCallbacks(timeout);
mTimeoutActions.remove(cujType);
}
FrameTracker tracker = getTracker(cujType);
// Skip this call since we haven't started a trace yet.
if (tracker == null) return false;
tracker.end();
mRunningTrackers.remove(cujType);
return true;
}
}
/**
* Cancel the trace session.
*
* @return boolean true if the tracker is cancelled successfully, false otherwise.
*/
public boolean cancel(@CujType int cujType) {
//TODO (163505250): This should be no-op if not in droid food rom.
synchronized (this) {
// remove the timeout action first.
Runnable timeout = mTimeoutActions.get(cujType);
if (timeout != null) {
mWorker.getThreadHandler().removeCallbacks(timeout);
mTimeoutActions.remove(cujType);
}
FrameTracker tracker = getTracker(cujType);
// Skip this call since we haven't started a trace yet.
if (tracker == null) return false;
tracker.cancel();
mRunningTrackers.remove(cujType);
return true;
}
}
private FrameTracker getTracker(@CujType int cuj) {
synchronized (this) {
return mRunningTrackers.get(cuj);
}
}
private void updateProperties(DeviceConfig.Properties properties) {
synchronized (this) {
mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
DEFAULT_SAMPLING_INTERVAL);
mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
mTraceThresholdMissedFrames = properties.getInt(SETTINGS_THRESHOLD_MISSED_FRAMES_KEY,
DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES);
mTraceThresholdFrameTimeMillis = properties.getInt(
SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY,
DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS);
}
}
@VisibleForTesting
public DeviceConfig.OnPropertiesChangedListener getPropertiesChangedListener() {
return mPropertiesChangedListener;
}
/**
* Trigger the perfetto daemon to collect and upload data.
*/
@VisibleForTesting
public void trigger(Session session) {
synchronized (this) {
mWorker.getThreadHandler().post(
() -> PerfettoTrigger.trigger(session.getPerfettoTrigger()));
}
}
/**
* A helper method to translate interaction type to CUJ name.
*
* @param interactionType the interaction type defined in AtomsProto.java
* @return the name of the interaction type
*/
public static String getNameOfInteraction(int interactionType) {
// There is an offset amount of 1 between cujType and interactionType.
return getNameOfCuj(interactionType - 1);
}
/**
* A helper method to translate CUJ type to CUJ name.
*
* @param cujType the cuj type defined in this file
* @return the name of the cuj type
*/
public static String getNameOfCuj(int cujType) {
switch (cujType) {
case CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE:
return "SHADE_EXPAND_COLLAPSE";
case CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK:
return "SHADE_EXPAND_COLLAPSE_LOCK";
case CUJ_NOTIFICATION_SHADE_SCROLL_FLING:
return "SHADE_SCROLL_FLING";
case CUJ_NOTIFICATION_SHADE_ROW_EXPAND:
return "SHADE_ROW_EXPAND";
case CUJ_NOTIFICATION_SHADE_ROW_SWIPE:
return "SHADE_ROW_SWIPE";
case CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE:
return "SHADE_QS_EXPAND_COLLAPSE";
case CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE:
return "SHADE_QS_SCROLL_SWIPE";
case CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS:
return "LAUNCHER_APP_LAUNCH_FROM_RECENTS";
case CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON:
return "LAUNCHER_APP_LAUNCH_FROM_ICON";
case CUJ_LAUNCHER_APP_CLOSE_TO_HOME:
return "LAUNCHER_APP_CLOSE_TO_HOME";
case CUJ_LAUNCHER_APP_CLOSE_TO_PIP:
return "LAUNCHER_APP_CLOSE_TO_PIP";
case CUJ_LAUNCHER_QUICK_SWITCH:
return "LAUNCHER_QUICK_SWITCH";
case CUJ_NOTIFICATION_HEADS_UP_APPEAR:
return "NOTIFICATION_HEADS_UP_APPEAR";
case CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR:
return "NOTIFICATION_HEADS_UP_DISAPPEAR";
case CUJ_NOTIFICATION_ADD:
return "NOTIFICATION_ADD";
case CUJ_NOTIFICATION_REMOVE:
return "NOTIFICATION_REMOVE";
case CUJ_NOTIFICATION_APP_START:
return "NOTIFICATION_APP_START";
case CUJ_LOCKSCREEN_PASSWORD_APPEAR:
return "CUJ_LOCKSCREEN_PASSWORD_APPEAR";
case CUJ_LOCKSCREEN_PATTERN_APPEAR:
return "CUJ_LOCKSCREEN_PATTERN_APPEAR";
case CUJ_LOCKSCREEN_PIN_APPEAR:
return "CUJ_LOCKSCREEN_PIN_APPEAR";
case CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR:
return "CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR";
case CUJ_LOCKSCREEN_PATTERN_DISAPPEAR:
return "CUJ_LOCKSCREEN_PATTERN_DISAPPEAR";
case CUJ_LOCKSCREEN_PIN_DISAPPEAR:
return "CUJ_LOCKSCREEN_PIN_DISAPPEAR";
case CUJ_LOCKSCREEN_TRANSITION_FROM_AOD:
return "CUJ_LOCKSCREEN_TRANSITION_FROM_AOD";
case CUJ_LOCKSCREEN_TRANSITION_TO_AOD:
return "CUJ_LOCKSCREEN_TRANSITION_TO_AOD";
case CUJ_LAUNCHER_OPEN_ALL_APPS :
return "CUJ_LAUNCHER_OPEN_ALL_APPS";
case CUJ_LAUNCHER_ALL_APPS_SCROLL:
return "CUJ_LAUNCHER_ALL_APPS_SCROLL";
}
return "UNKNOWN";
}
/**
* A class to represent a session.
*/
public static class Session {
@CujType
private int mCujType;
private long mTimeStamp;
public Session(@CujType int cujType) {
mCujType = cujType;
}
@CujType
public int getCuj() {
return mCujType;
}
public int getStatsdInteractionType() {
return CUJ_TO_STATSD_INTERACTION_TYPE[mCujType];
}
/** Describes whether the measurement from this session should be written to statsd. */
public boolean logToStatsd() {
return getStatsdInteractionType() != NO_STATSD_LOGGING;
}
public String getPerfettoTrigger() {
return String.format("com.android.telemetry.interaction-jank-monitor-%d", mCujType);
}
public String getName() {
return "J<" + getNameOfCuj(mCujType) + ">";
}
public void setTimeStamp(long timeStamp) {
mTimeStamp = timeStamp;
}
public long getTimeStamp() {
return mTimeStamp;
}
}
}