|
|
|
|
@@ -15,14 +15,22 @@
|
|
|
|
|
package com.android.internal.util;
|
|
|
|
|
|
|
|
|
|
import android.content.Context;
|
|
|
|
|
import android.database.ContentObserver;
|
|
|
|
|
import android.net.Uri;
|
|
|
|
|
import android.os.Build;
|
|
|
|
|
import android.os.SystemClock;
|
|
|
|
|
import android.os.Trace;
|
|
|
|
|
import android.os.UserHandle;
|
|
|
|
|
import android.provider.Settings;
|
|
|
|
|
import android.util.EventLog;
|
|
|
|
|
import android.util.KeyValueListParser;
|
|
|
|
|
import android.util.Log;
|
|
|
|
|
import android.util.SparseLongArray;
|
|
|
|
|
|
|
|
|
|
import com.android.internal.logging.EventLogTags;
|
|
|
|
|
import com.android.internal.os.BackgroundThread;
|
|
|
|
|
|
|
|
|
|
import java.util.concurrent.ThreadLocalRandom;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Class to track various latencies in SystemUI. It then writes the latency to statsd and also
|
|
|
|
|
@@ -34,6 +42,12 @@ import com.android.internal.logging.EventLogTags;
|
|
|
|
|
*/
|
|
|
|
|
public class LatencyTracker {
|
|
|
|
|
private static final String TAG = "LatencyTracker";
|
|
|
|
|
private static final String SETTINGS_ENABLED_KEY = "enabled";
|
|
|
|
|
private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
|
|
|
|
|
/** Default to being enabled on debug builds. */
|
|
|
|
|
private static final boolean DEFAULT_ENABLED = Build.IS_DEBUGGABLE;
|
|
|
|
|
/** Default to collecting data for 1/5 of all actions (randomly sampled). */
|
|
|
|
|
private static final int DEFAULT_SAMPLING_INTERVAL = 5;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Time it takes until the first frame of the notification panel to be displayed while expanding
|
|
|
|
|
@@ -76,7 +90,7 @@ public class LatencyTracker {
|
|
|
|
|
*/
|
|
|
|
|
public static final int ACTION_FACE_WAKE_AND_UNLOCK = 7;
|
|
|
|
|
|
|
|
|
|
private static final String[] NAMES = new String[] {
|
|
|
|
|
private static final String[] NAMES = new String[]{
|
|
|
|
|
"expand panel",
|
|
|
|
|
"toggle recents",
|
|
|
|
|
"fingerprint wake-and-unlock",
|
|
|
|
|
@@ -84,9 +98,9 @@ public class LatencyTracker {
|
|
|
|
|
"check credential unlocked",
|
|
|
|
|
"turn on screen",
|
|
|
|
|
"rotate the screen",
|
|
|
|
|
"face wake-and-unlock" };
|
|
|
|
|
"face wake-and-unlock"};
|
|
|
|
|
|
|
|
|
|
private static final int[] STATSD_ACTION = new int[] {
|
|
|
|
|
private static final int[] STATSD_ACTION = new int[]{
|
|
|
|
|
FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL,
|
|
|
|
|
FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS,
|
|
|
|
|
FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FINGERPRINT_WAKE_AND_UNLOCK,
|
|
|
|
|
@@ -100,20 +114,59 @@ public class LatencyTracker {
|
|
|
|
|
private static LatencyTracker sLatencyTracker;
|
|
|
|
|
|
|
|
|
|
private final SparseLongArray mStartRtc = new SparseLongArray();
|
|
|
|
|
private final Context mContext;
|
|
|
|
|
private volatile int mSamplingInterval;
|
|
|
|
|
private volatile boolean mEnabled;
|
|
|
|
|
|
|
|
|
|
public static LatencyTracker getInstance(Context context) {
|
|
|
|
|
if (sLatencyTracker == null) {
|
|
|
|
|
sLatencyTracker = new LatencyTracker();
|
|
|
|
|
synchronized (LatencyTracker.class) {
|
|
|
|
|
if (sLatencyTracker == null) {
|
|
|
|
|
sLatencyTracker = new LatencyTracker(context);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return sLatencyTracker;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public LatencyTracker(Context context) {
|
|
|
|
|
mContext = context;
|
|
|
|
|
mEnabled = DEFAULT_ENABLED;
|
|
|
|
|
mSamplingInterval = DEFAULT_SAMPLING_INTERVAL;
|
|
|
|
|
|
|
|
|
|
// Post initialization to the background in case we're running on the main thread.
|
|
|
|
|
BackgroundThread.getHandler().post(this::registerSettingsObserver);
|
|
|
|
|
BackgroundThread.getHandler().post(this::readSettings);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void registerSettingsObserver() {
|
|
|
|
|
Uri settingsUri = Settings.Global.getUriFor(Settings.Global.LATENCY_TRACKER);
|
|
|
|
|
mContext.getContentResolver().registerContentObserver(
|
|
|
|
|
settingsUri, false, new SettingsObserver(this), UserHandle.myUserId());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void readSettings() {
|
|
|
|
|
KeyValueListParser parser = new KeyValueListParser(',');
|
|
|
|
|
String settingsValue = Settings.Global.getString(mContext.getContentResolver(),
|
|
|
|
|
Settings.Global.LATENCY_TRACKER);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
parser.setString(settingsValue);
|
|
|
|
|
mSamplingInterval = parser.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
|
|
|
|
|
DEFAULT_SAMPLING_INTERVAL);
|
|
|
|
|
mEnabled = parser.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
|
|
|
|
|
} catch (IllegalArgumentException e) {
|
|
|
|
|
Log.e(TAG, "Incorrect settings format", e);
|
|
|
|
|
mEnabled = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static boolean isEnabled(Context ctx) {
|
|
|
|
|
return getInstance(ctx).isEnabled();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean isEnabled() {
|
|
|
|
|
return Build.IS_DEBUGGABLE;
|
|
|
|
|
return mEnabled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@@ -145,19 +198,48 @@ public class LatencyTracker {
|
|
|
|
|
}
|
|
|
|
|
mStartRtc.delete(action);
|
|
|
|
|
Trace.asyncTraceEnd(Trace.TRACE_TAG_APP, NAMES[action], 0);
|
|
|
|
|
logAction(action, (int)(endRtc - startRtc));
|
|
|
|
|
logAction(action, (int) (endRtc - startRtc));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
public static void logAction(int action, int duration) {
|
|
|
|
|
public void logAction(int action, int duration) {
|
|
|
|
|
boolean shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0;
|
|
|
|
|
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 writeToStatsLog Whether to write the measured latency to FrameworkStatsLog.
|
|
|
|
|
*/
|
|
|
|
|
public static void logActionDeprecated(int action, int duration, boolean writeToStatsLog) {
|
|
|
|
|
Log.i(TAG, "action=" + action + " latency=" + duration);
|
|
|
|
|
EventLog.writeEvent(EventLogTags.SYSUI_LATENCY, action, duration);
|
|
|
|
|
FrameworkStatsLog.write(
|
|
|
|
|
FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED, STATSD_ACTION[action], duration);
|
|
|
|
|
|
|
|
|
|
if (writeToStatsLog) {
|
|
|
|
|
FrameworkStatsLog.write(
|
|
|
|
|
FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED, STATSD_ACTION[action], duration);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static class SettingsObserver extends ContentObserver {
|
|
|
|
|
private final LatencyTracker mThisTracker;
|
|
|
|
|
|
|
|
|
|
SettingsObserver(LatencyTracker thisTracker) {
|
|
|
|
|
super(BackgroundThread.getHandler());
|
|
|
|
|
mThisTracker = thisTracker;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onChange(boolean selfChange, Uri uri, int userId) {
|
|
|
|
|
mThisTracker.readSettings();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|