diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index b133bcd142189..989efba13649e 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -14591,6 +14591,19 @@ public final class Settings { */ public static final String MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH = "maximum_obscuring_opacity_for_touch"; + + /** + * LatencyTracker settings. + * + * The following strings are supported as keys: + *
+ * enabled (boolean) + * sampling_interval (int) + *+ * + * @hide + */ + public static final String LATENCY_TRACKER = "latency_tracker"; } /** diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java index 555f62c533872..c7ac1895855b9 100644 --- a/core/java/com/android/internal/util/LatencyTracker.java +++ b/core/java/com/android/internal/util/LatencyTracker.java @@ -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(); + } } } diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 69be14413583e..8bdde8be94988 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -314,6 +314,7 @@ public class SettingsBackupTest { Settings.Global.KERNEL_CPU_THREAD_READER, Settings.Global.LANG_ID_UPDATE_CONTENT_URL, Settings.Global.LANG_ID_UPDATE_METADATA_URL, + Settings.Global.LATENCY_TRACKER, Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS, Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS, Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST, diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java index 0d5933e5f5990..bf4fb0b6517e5 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java @@ -28,7 +28,18 @@ public class LatencyTrackerCompat { return LatencyTracker.isEnabled(context); } + /** + * @see LatencyTracker + * @deprecated Please use {@link LatencyTrackerCompat#logToggleRecents(Context, int)} instead. + */ + @Deprecated public static void logToggleRecents(int duration) { - LatencyTracker.logAction(LatencyTracker.ACTION_TOGGLE_RECENTS, duration); + LatencyTracker.logActionDeprecated(LatencyTracker.ACTION_TOGGLE_RECENTS, duration, false); + } + + /** @see LatencyTracker */ + public static void logToggleRecents(Context context, int duration) { + LatencyTracker.getInstance(context).logAction(LatencyTracker.ACTION_TOGGLE_RECENTS, + duration); } } \ No newline at end of file