947 lines
38 KiB
Java
947 lines
38 KiB
Java
![]() |
/*
|
||
|
* Copyright (C) 2017 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 android.Manifest.permission.READ_DEVICE_CONFIG;
|
||
|
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||
|
import static android.os.Trace.TRACE_TAG_APP;
|
||
|
import static android.provider.DeviceConfig.NAMESPACE_LATENCY_TRACKER;
|
||
|
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_BACK_SYSTEM_ANIMATION;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL_UNLOCKED;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FACE_WAKE_AND_UNLOCK;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FINGERPRINT_WAKE_AND_UNLOCK;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE_WITH_SHADE_OPEN;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOAD_SHARE_SHEET;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOCKSCREEN_UNLOCK;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATION_BIG_PICTURE_LOADED;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_HIDDEN;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_SHOWN;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_CAMERA_CHECK;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_SENSOR;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_BACK_ARROW;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_SELECTION_TOOLBAR;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SMARTSPACE_DOORBELL;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_START_RECENTS_ANIMATION;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SWITCH_DISPLAY_UNFOLD;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TURN_ON_SCREEN;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_UDFPS_ILLUMINATE;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_USER_SWITCH;
|
||
|
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__UNKNOWN_ACTION;
|
||
|
import static com.android.internal.util.LatencyTracker.ActionProperties.ENABLE_SUFFIX;
|
||
|
import static com.android.internal.util.LatencyTracker.ActionProperties.LEGACY_TRACE_THRESHOLD_SUFFIX;
|
||
|
import static com.android.internal.util.LatencyTracker.ActionProperties.SAMPLE_INTERVAL_SUFFIX;
|
||
|
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;
|
||
|
import android.annotation.RequiresPermission;
|
||
|
import android.app.ActivityThread;
|
||
|
import android.content.Context;
|
||
|
import android.os.Build;
|
||
|
import android.os.SystemClock;
|
||
|
import android.os.Trace;
|
||
|
import android.provider.DeviceConfig;
|
||
|
import android.text.TextUtils;
|
||
|
import android.util.EventLog;
|
||
|
import android.util.Log;
|
||
|
import android.util.SparseArray;
|
||
|
|
||
|
import com.android.internal.annotations.GuardedBy;
|
||
|
import com.android.internal.annotations.VisibleForTesting;
|
||
|
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.Locale;
|
||
|
import java.util.concurrent.ThreadLocalRandom;
|
||
|
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. 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.
|
||
|
*/
|
||
|
public class LatencyTracker {
|
||
|
private static final String TAG = "LatencyTracker";
|
||
|
public static final String SETTINGS_ENABLED_KEY = "enabled";
|
||
|
private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
|
||
|
private static final boolean DEBUG = false;
|
||
|
/** 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
|
||
|
*/
|
||
|
public static final int ACTION_EXPAND_PANEL = 0;
|
||
|
|
||
|
/**
|
||
|
* Time it takes until the first frame of recents is drawn after invoking it with the button.
|
||
|
*/
|
||
|
public static final int ACTION_TOGGLE_RECENTS = 1;
|
||
|
|
||
|
/**
|
||
|
* Time between we get a fingerprint acquired signal until we start with the unlock animation
|
||
|
*/
|
||
|
public static final int ACTION_FINGERPRINT_WAKE_AND_UNLOCK = 2;
|
||
|
|
||
|
/**
|
||
|
* Time it takes to check PIN/Pattern/Password.
|
||
|
*/
|
||
|
public static final int ACTION_CHECK_CREDENTIAL = 3;
|
||
|
|
||
|
/**
|
||
|
* Time it takes to check fully PIN/Pattern/Password, i.e. that's the time spent including the
|
||
|
* actions to unlock a user.
|
||
|
*/
|
||
|
public static final int ACTION_CHECK_CREDENTIAL_UNLOCKED = 4;
|
||
|
|
||
|
/**
|
||
|
* Time it takes to turn on the screen.
|
||
|
*/
|
||
|
public static final int ACTION_TURN_ON_SCREEN = 5;
|
||
|
|
||
|
/**
|
||
|
* Time it takes to rotate the screen.
|
||
|
*/
|
||
|
public static final int ACTION_ROTATE_SCREEN = 6;
|
||
|
|
||
|
/*
|
||
|
* Time between we get a face acquired signal until we start with the unlock animation
|
||
|
*/
|
||
|
public static final int ACTION_FACE_WAKE_AND_UNLOCK = 7;
|
||
|
|
||
|
/**
|
||
|
* Time between the swipe-up gesture and window drawn of recents activity.
|
||
|
*/
|
||
|
public static final int ACTION_START_RECENTS_ANIMATION = 8;
|
||
|
|
||
|
/**
|
||
|
* Time it takes to for the camera based algorithm to rotate the screen.
|
||
|
*/
|
||
|
public static final int ACTION_ROTATE_SCREEN_CAMERA_CHECK = 9;
|
||
|
|
||
|
/**
|
||
|
* Time it takes the sensor to detect rotation.
|
||
|
*/
|
||
|
public static final int ACTION_ROTATE_SCREEN_SENSOR = 10;
|
||
|
|
||
|
/**
|
||
|
* Time it takes to start unlock animation .
|
||
|
*/
|
||
|
public static final int ACTION_LOCKSCREEN_UNLOCK = 11;
|
||
|
|
||
|
/**
|
||
|
* Time it takes to switch users.
|
||
|
*/
|
||
|
public static final int ACTION_USER_SWITCH = 12;
|
||
|
|
||
|
/**
|
||
|
* Time it takes to turn on the inner screen for a foldable device.
|
||
|
*/
|
||
|
public static final int ACTION_SWITCH_DISPLAY_UNFOLD = 13;
|
||
|
|
||
|
/**
|
||
|
* Time it takes for a UDFPS sensor to appear ready after it is touched.
|
||
|
*/
|
||
|
public static final int ACTION_UDFPS_ILLUMINATE = 14;
|
||
|
|
||
|
/**
|
||
|
* Time it takes for the gesture back affordance arrow to show up.
|
||
|
*/
|
||
|
public static final int ACTION_SHOW_BACK_ARROW = 15;
|
||
|
|
||
|
/**
|
||
|
* Time it takes for loading share sheet.
|
||
|
*/
|
||
|
public static final int ACTION_LOAD_SHARE_SHEET = 16;
|
||
|
|
||
|
/**
|
||
|
* Time it takes for showing the selection toolbar.
|
||
|
*/
|
||
|
public static final int ACTION_SHOW_SELECTION_TOOLBAR = 17;
|
||
|
|
||
|
/**
|
||
|
* Time it takes to show AOD display after folding the device.
|
||
|
*/
|
||
|
public static final int ACTION_FOLD_TO_AOD = 18;
|
||
|
|
||
|
/**
|
||
|
* Time it takes to show the {@link android.service.voice.VoiceInteractionSession} system UI
|
||
|
* after a {@link android.hardware.soundtrigger3.ISoundTriggerHw} voice trigger.
|
||
|
*/
|
||
|
public static final int ACTION_SHOW_VOICE_INTERACTION = 19;
|
||
|
|
||
|
/**
|
||
|
* Time it takes to request IME shown animation.
|
||
|
*/
|
||
|
public static final int ACTION_REQUEST_IME_SHOWN = 20;
|
||
|
|
||
|
/**
|
||
|
* Time it takes to request IME hidden animation.
|
||
|
*/
|
||
|
public static final int ACTION_REQUEST_IME_HIDDEN = 21;
|
||
|
|
||
|
/**
|
||
|
* Time it takes to load the animation frames in smart space doorbell card.
|
||
|
* It measures the duration from the images uris are passed into the view
|
||
|
* to all the frames are loaded.
|
||
|
* <p/>
|
||
|
* A long latency makes the doorbell animation looks janky until all the frames are loaded.
|
||
|
*/
|
||
|
public static final int ACTION_SMARTSPACE_DOORBELL = 22;
|
||
|
|
||
|
/**
|
||
|
* Time it takes to lazy-load the image of a {@link android.app.Notification.BigPictureStyle}
|
||
|
* notification.
|
||
|
*/
|
||
|
public static final int ACTION_NOTIFICATION_BIG_PICTURE_LOADED = 23;
|
||
|
|
||
|
/**
|
||
|
* Time it takes to unlock the device via udfps, until the whole launcher appears.
|
||
|
*/
|
||
|
public static final int ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME = 24;
|
||
|
|
||
|
/**
|
||
|
* Time it takes to start back preview surface animation after a back gesture starts.
|
||
|
*/
|
||
|
public static final int ACTION_BACK_SYSTEM_ANIMATION = 25;
|
||
|
|
||
|
/**
|
||
|
* Time notifications spent in hidden state for performance reasons. We might temporary
|
||
|
* hide notifications after display size changes (e.g. fold/unfold of a foldable device)
|
||
|
* and measure them while they are hidden to unblock rendering of the rest of the UI.
|
||
|
*/
|
||
|
public static final int ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE = 26;
|
||
|
|
||
|
/**
|
||
|
* The same as {@link ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE} but tracks time only
|
||
|
* when the notifications are hidden and when the shade is open or keyguard is visible.
|
||
|
*/
|
||
|
public static final int ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE_WITH_SHADE_OPEN = 27;
|
||
|
|
||
|
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,
|
||
|
ACTION_ROTATE_SCREEN_CAMERA_CHECK,
|
||
|
ACTION_ROTATE_SCREEN_SENSOR,
|
||
|
ACTION_LOCKSCREEN_UNLOCK,
|
||
|
ACTION_USER_SWITCH,
|
||
|
ACTION_SWITCH_DISPLAY_UNFOLD,
|
||
|
ACTION_UDFPS_ILLUMINATE,
|
||
|
ACTION_SHOW_BACK_ARROW,
|
||
|
ACTION_LOAD_SHARE_SHEET,
|
||
|
ACTION_SHOW_SELECTION_TOOLBAR,
|
||
|
ACTION_FOLD_TO_AOD,
|
||
|
ACTION_SHOW_VOICE_INTERACTION,
|
||
|
ACTION_REQUEST_IME_SHOWN,
|
||
|
ACTION_REQUEST_IME_HIDDEN,
|
||
|
ACTION_SMARTSPACE_DOORBELL,
|
||
|
ACTION_NOTIFICATION_BIG_PICTURE_LOADED,
|
||
|
ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME,
|
||
|
ACTION_BACK_SYSTEM_ANIMATION,
|
||
|
ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE,
|
||
|
ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE_WITH_SHADE_OPEN,
|
||
|
};
|
||
|
|
||
|
/** @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,
|
||
|
ACTION_ROTATE_SCREEN_CAMERA_CHECK,
|
||
|
ACTION_ROTATE_SCREEN_SENSOR,
|
||
|
ACTION_LOCKSCREEN_UNLOCK,
|
||
|
ACTION_USER_SWITCH,
|
||
|
ACTION_SWITCH_DISPLAY_UNFOLD,
|
||
|
ACTION_UDFPS_ILLUMINATE,
|
||
|
ACTION_SHOW_BACK_ARROW,
|
||
|
ACTION_LOAD_SHARE_SHEET,
|
||
|
ACTION_SHOW_SELECTION_TOOLBAR,
|
||
|
ACTION_FOLD_TO_AOD,
|
||
|
ACTION_SHOW_VOICE_INTERACTION,
|
||
|
ACTION_REQUEST_IME_SHOWN,
|
||
|
ACTION_REQUEST_IME_HIDDEN,
|
||
|
ACTION_SMARTSPACE_DOORBELL,
|
||
|
ACTION_NOTIFICATION_BIG_PICTURE_LOADED,
|
||
|
ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME,
|
||
|
ACTION_BACK_SYSTEM_ANIMATION,
|
||
|
ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE,
|
||
|
ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE_WITH_SHADE_OPEN,
|
||
|
})
|
||
|
@Retention(RetentionPolicy.SOURCE)
|
||
|
public @interface Action {
|
||
|
}
|
||
|
|
||
|
@VisibleForTesting
|
||
|
public static final int[] STATSD_ACTION = new int[] {
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_FINGERPRINT_WAKE_AND_UNLOCK,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL_UNLOCKED,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_TURN_ON_SCREEN,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_FACE_WAKE_AND_UNLOCK,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_START_RECENTS_ANIMATION,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_CAMERA_CHECK,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_SENSOR,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOCKSCREEN_UNLOCK,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_USER_SWITCH,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_SWITCH_DISPLAY_UNFOLD,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_UDFPS_ILLUMINATE,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_BACK_ARROW,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOAD_SHARE_SHEET,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_SELECTION_TOOLBAR,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_SHOWN,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_HIDDEN,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_SMARTSPACE_DOORBELL,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATION_BIG_PICTURE_LOADED,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_BACK_SYSTEM_ANIMATION,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE,
|
||
|
UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE_WITH_SHADE_OPEN,
|
||
|
};
|
||
|
|
||
|
private final Object mLock = new Object();
|
||
|
@GuardedBy("mLock")
|
||
|
private final SparseArray<Session> mSessions = new SparseArray<>();
|
||
|
@GuardedBy("mLock")
|
||
|
private final SparseArray<ActionProperties> mActionPropertiesMap = new SparseArray<>();
|
||
|
@GuardedBy("mLock")
|
||
|
private boolean mEnabled;
|
||
|
private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
|
||
|
this::updateProperties;
|
||
|
|
||
|
// Wrapping this in a holder class achieves lazy loading behavior
|
||
|
private static final class SLatencyTrackerHolder {
|
||
|
private static final LatencyTracker sLatencyTracker;
|
||
|
|
||
|
static {
|
||
|
sLatencyTracker = new LatencyTracker();
|
||
|
sLatencyTracker.startListeningForLatencyTrackerConfigChanges();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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
|
||
|
*
|
||
|
* @param startListeningForPropertyChanges If set, constructor will register for device config
|
||
|
* property updates prior to returning. If not set,
|
||
|
* {@link #startListeningForLatencyTrackerConfigChanges} must be called
|
||
|
* to start listening.
|
||
|
*/
|
||
|
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
|
||
|
@VisibleForTesting
|
||
|
public LatencyTracker() {
|
||
|
mEnabled = DEFAULT_ENABLED;
|
||
|
}
|
||
|
|
||
|
private void updateProperties(DeviceConfig.Properties properties) {
|
||
|
synchronized (mLock) {
|
||
|
int samplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
|
||
|
DEFAULT_SAMPLING_INTERVAL);
|
||
|
boolean wasEnabled = mEnabled;
|
||
|
mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
|
||
|
if (wasEnabled != mEnabled) {
|
||
|
Log.d(TAG, "Latency tracker " + (mEnabled ? "enabled" : "disabled") + ".");
|
||
|
}
|
||
|
for (int action : ACTIONS_ALL) {
|
||
|
String actionName = getNameOfAction(STATSD_ACTION[action]).toLowerCase(Locale.ROOT);
|
||
|
int legacyActionTraceThreshold = properties.getInt(
|
||
|
actionName + LEGACY_TRACE_THRESHOLD_SUFFIX, -1);
|
||
|
mActionPropertiesMap.put(action, new ActionProperties(action,
|
||
|
properties.getBoolean(actionName + ENABLE_SUFFIX, mEnabled),
|
||
|
properties.getInt(actionName + SAMPLE_INTERVAL_SUFFIX, samplingInterval),
|
||
|
properties.getInt(actionName + TRACE_THRESHOLD_SUFFIX,
|
||
|
legacyActionTraceThreshold)));
|
||
|
}
|
||
|
onDeviceConfigPropertiesUpdated(mActionPropertiesMap);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Test method to start listening to {@link DeviceConfig} properties changes.
|
||
|
*
|
||
|
* <p>During testing, a {@link LatencyTracker} it is desired to stop and start listening for
|
||
|
* config updates.
|
||
|
*
|
||
|
* <p>This is not used for production usages of this class outside of testing as we are
|
||
|
* using a single static object.
|
||
|
*/
|
||
|
@VisibleForTesting
|
||
|
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
|
||
|
public void startListeningForLatencyTrackerConfigChanges() {
|
||
|
final Context context = ActivityThread.currentApplication();
|
||
|
if (context == null) {
|
||
|
if (DEBUG) {
|
||
|
Log.d(TAG, "No application for package: " + ActivityThread.currentPackageName());
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
if (context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG) != PERMISSION_GRANTED) {
|
||
|
if (DEBUG) {
|
||
|
synchronized (mLock) {
|
||
|
Log.d(TAG, "Initialized the LatencyTracker."
|
||
|
+ " (No READ_DEVICE_CONFIG permission to change configs)"
|
||
|
+ " enabled=" + mEnabled + ", package=" + context.getPackageName());
|
||
|
}
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Post initialization to the background in case we're running on the main thread.
|
||
|
BackgroundThread.getHandler().post(() -> {
|
||
|
try {
|
||
|
this.updateProperties(
|
||
|
DeviceConfig.getProperties(NAMESPACE_LATENCY_TRACKER));
|
||
|
DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_LATENCY_TRACKER,
|
||
|
BackgroundThread.getExecutor(), mOnPropertiesChangedListener);
|
||
|
} catch (SecurityException ex) {
|
||
|
// In case of running tests that the main thread passes the check,
|
||
|
// but the background thread doesn't have necessary permissions.
|
||
|
// Swallow it since it's ok to ignore device config changes in the tests.
|
||
|
Log.d(TAG, "Can't get properties: READ_DEVICE_CONFIG granted="
|
||
|
+ context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG)
|
||
|
+ ", package=" + context.getPackageName());
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Test method to stop listening to {@link DeviceConfig} properties changes.
|
||
|
*
|
||
|
* <p>During testing, a {@link LatencyTracker} it is desired to stop and start listening for
|
||
|
* config updates.
|
||
|
*
|
||
|
* <p>This is not used for production usages of this class outside of testing as we are
|
||
|
* using a single static object.
|
||
|
*/
|
||
|
@VisibleForTesting
|
||
|
public void stopListeningForLatencyTrackerConfigChanges() {
|
||
|
DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* A helper method to translate action type to name.
|
||
|
*
|
||
|
* @param atomsProtoAction the action type defined in AtomsProto.java
|
||
|
* @return the name of the action
|
||
|
*/
|
||
|
public static String getNameOfAction(int atomsProtoAction) {
|
||
|
// Defined in AtomsProto.java
|
||
|
switch (atomsProtoAction) {
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__UNKNOWN_ACTION:
|
||
|
return "UNKNOWN";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL:
|
||
|
return "ACTION_EXPAND_PANEL";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS:
|
||
|
return "ACTION_TOGGLE_RECENTS";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_FINGERPRINT_WAKE_AND_UNLOCK:
|
||
|
return "ACTION_FINGERPRINT_WAKE_AND_UNLOCK";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL:
|
||
|
return "ACTION_CHECK_CREDENTIAL";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL_UNLOCKED:
|
||
|
return "ACTION_CHECK_CREDENTIAL_UNLOCKED";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_TURN_ON_SCREEN:
|
||
|
return "ACTION_TURN_ON_SCREEN";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN:
|
||
|
return "ACTION_ROTATE_SCREEN";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_FACE_WAKE_AND_UNLOCK:
|
||
|
return "ACTION_FACE_WAKE_AND_UNLOCK";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_START_RECENTS_ANIMATION:
|
||
|
return "ACTION_START_RECENTS_ANIMATION";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_CAMERA_CHECK:
|
||
|
return "ACTION_ROTATE_SCREEN_CAMERA_CHECK";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_SENSOR:
|
||
|
return "ACTION_ROTATE_SCREEN_SENSOR";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOCKSCREEN_UNLOCK:
|
||
|
return "ACTION_LOCKSCREEN_UNLOCK";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_USER_SWITCH:
|
||
|
return "ACTION_USER_SWITCH";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SWITCH_DISPLAY_UNFOLD:
|
||
|
return "ACTION_SWITCH_DISPLAY_UNFOLD";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_UDFPS_ILLUMINATE:
|
||
|
return "ACTION_UDFPS_ILLUMINATE";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_BACK_ARROW:
|
||
|
return "ACTION_SHOW_BACK_ARROW";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOAD_SHARE_SHEET:
|
||
|
return "ACTION_LOAD_SHARE_SHEET";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_SELECTION_TOOLBAR:
|
||
|
return "ACTION_SHOW_SELECTION_TOOLBAR";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD:
|
||
|
return "ACTION_FOLD_TO_AOD";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION:
|
||
|
return "ACTION_SHOW_VOICE_INTERACTION";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_SHOWN:
|
||
|
return "ACTION_REQUEST_IME_SHOWN";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_HIDDEN:
|
||
|
return "ACTION_REQUEST_IME_HIDDEN";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SMARTSPACE_DOORBELL:
|
||
|
return "ACTION_SMARTSPACE_DOORBELL";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATION_BIG_PICTURE_LOADED:
|
||
|
return "ACTION_NOTIFICATION_BIG_PICTURE_LOADED";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME:
|
||
|
return "ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_BACK_SYSTEM_ANIMATION:
|
||
|
return "ACTION_BACK_SYSTEM_ANIMATION";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE:
|
||
|
return "ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE";
|
||
|
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE_WITH_SHADE_OPEN:
|
||
|
return "ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE_WITH_SHADE_OPEN";
|
||
|
default:
|
||
|
throw new IllegalArgumentException("Invalid action");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static String getTraceNameOfAction(@Action int action, String tag) {
|
||
|
if (TextUtils.isEmpty(tag)) {
|
||
|
return "L<" + getNameOfAction(STATSD_ACTION[action]) + ">";
|
||
|
} else {
|
||
|
return "L<" + getNameOfAction(STATSD_ACTION[action]) + "::" + tag + ">";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static String getTraceTriggerNameForAction(@Action int action) {
|
||
|
return "com.android.telemetry.latency-tracker-" + getNameOfAction(STATSD_ACTION[action]);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @deprecated Use {@link #isEnabled(Context, int)}
|
||
|
*/
|
||
|
@Deprecated
|
||
|
public static boolean isEnabled(Context ctx) {
|
||
|
return getInstance(ctx).isEnabled();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @deprecated Used {@link #isEnabled(int)}
|
||
|
*/
|
||
|
@Deprecated
|
||
|
public boolean isEnabled() {
|
||
|
synchronized (mLock) {
|
||
|
return mEnabled;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static boolean isEnabled(Context ctx, int action) {
|
||
|
return getInstance(ctx).isEnabled(action);
|
||
|
}
|
||
|
|
||
|
public boolean isEnabled(int action) {
|
||
|
synchronized (mLock) {
|
||
|
ActionProperties actionProperties = mActionPropertiesMap.get(action);
|
||
|
if (actionProperties != null) {
|
||
|
return actionProperties.isEnabled();
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Notifies that an action is starting. <s>This needs to be called from the main thread.</s>
|
||
|
*
|
||
|
* @param action The action to start. One of the ACTION_* values.
|
||
|
*/
|
||
|
public void onActionStart(@Action int action) {
|
||
|
onActionStart(action, null);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Notifies that an action is starting. <s>This needs to be called from the main thread.</s>
|
||
|
*
|
||
|
* @param action The action to start. One of the ACTION_* values.
|
||
|
* @param tag The brief description of the action.
|
||
|
*/
|
||
|
public void onActionStart(@Action int action, String tag) {
|
||
|
synchronized (mLock) {
|
||
|
if (!isEnabled(action)) {
|
||
|
return;
|
||
|
}
|
||
|
// skip if the action is already instrumenting.
|
||
|
if (mSessions.get(action) != null) {
|
||
|
return;
|
||
|
}
|
||
|
Session session = new Session(action, tag);
|
||
|
session.begin(() -> onActionCancel(action));
|
||
|
mSessions.put(action, session);
|
||
|
|
||
|
if (DEBUG) {
|
||
|
Log.d(TAG, "onActionStart: " + session.name() + ", start=" + session.mStartRtc);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Notifies that an action has ended. <s>This needs to be called from the main thread.</s>
|
||
|
*
|
||
|
* @param action The action to end. One of the ACTION_* values.
|
||
|
*/
|
||
|
public void onActionEnd(@Action int action) {
|
||
|
synchronized (mLock) {
|
||
|
if (!isEnabled(action)) {
|
||
|
return;
|
||
|
}
|
||
|
Session session = mSessions.get(action);
|
||
|
if (session == null) {
|
||
|
return;
|
||
|
}
|
||
|
session.end();
|
||
|
mSessions.delete(action);
|
||
|
logAction(action, session.duration());
|
||
|
|
||
|
if (DEBUG) {
|
||
|
Log.d(TAG, "onActionEnd:" + session.name() + ", duration=" + session.duration());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Notifies that an action has canceled. <s>This needs to be called from the main thread.</s>
|
||
|
*
|
||
|
* @param action The action to cancel. One of the ACTION_* values.
|
||
|
* @hide
|
||
|
*/
|
||
|
public void onActionCancel(@Action int action) {
|
||
|
synchronized (mLock) {
|
||
|
Session session = mSessions.get(action);
|
||
|
if (session == null) {
|
||
|
return;
|
||
|
}
|
||
|
session.cancel();
|
||
|
mSessions.delete(action);
|
||
|
|
||
|
if (DEBUG) {
|
||
|
Log.d(TAG, "onActionCancel: " + session.name());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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.
|
||
|
*
|
||
|
* @param action The action to end. One of the ACTION_* values.
|
||
|
* @param duration The duration of the action in ms.
|
||
|
*/
|
||
|
public void logAction(@Action int action, int duration) {
|
||
|
boolean shouldSample;
|
||
|
int traceThreshold;
|
||
|
synchronized (mLock) {
|
||
|
if (!isEnabled(action)) {
|
||
|
return;
|
||
|
}
|
||
|
ActionProperties actionProperties = mActionPropertiesMap.get(action);
|
||
|
if (actionProperties == null) {
|
||
|
return;
|
||
|
}
|
||
|
int nextRandNum = ThreadLocalRandom.current().nextInt(
|
||
|
actionProperties.getSamplingInterval());
|
||
|
shouldSample = nextRandNum == 0;
|
||
|
traceThreshold = actionProperties.getTraceThreshold();
|
||
|
}
|
||
|
|
||
|
boolean shouldTriggerPerfettoTrace = traceThreshold > 0 && duration >= traceThreshold;
|
||
|
|
||
|
if (DEBUG) {
|
||
|
Log.i(TAG, "logAction: " + getNameOfAction(STATSD_ACTION[action])
|
||
|
+ " duration=" + duration
|
||
|
+ " shouldSample=" + shouldSample
|
||
|
+ " shouldTriggerPerfettoTrace=" + shouldTriggerPerfettoTrace);
|
||
|
}
|
||
|
|
||
|
EventLog.writeEvent(EventLogTags.SYSUI_LATENCY, action, duration);
|
||
|
if (shouldTriggerPerfettoTrace) {
|
||
|
onTriggerPerfetto(getTraceTriggerNameForAction(action));
|
||
|
}
|
||
|
if (shouldSample) {
|
||
|
onLogToFrameworkStats(
|
||
|
new FrameworkStatsLogEvent(action, FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED,
|
||
|
STATSD_ACTION[action], duration)
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static class Session {
|
||
|
@Action
|
||
|
private final int mAction;
|
||
|
private final String mTag;
|
||
|
private final String mName;
|
||
|
private Runnable mTimeoutRunnable;
|
||
|
private long mStartRtc = -1;
|
||
|
private long mEndRtc = -1;
|
||
|
|
||
|
Session(@Action int action, @Nullable String tag) {
|
||
|
mAction = action;
|
||
|
mTag = tag;
|
||
|
mName = TextUtils.isEmpty(mTag)
|
||
|
? getNameOfAction(STATSD_ACTION[mAction])
|
||
|
: getNameOfAction(STATSD_ACTION[mAction]) + "::" + mTag;
|
||
|
}
|
||
|
|
||
|
String name() {
|
||
|
return mName;
|
||
|
}
|
||
|
|
||
|
String traceName() {
|
||
|
return getTraceNameOfAction(mAction, mTag);
|
||
|
}
|
||
|
|
||
|
void begin(@NonNull Runnable timeoutAction) {
|
||
|
mStartRtc = SystemClock.elapsedRealtime();
|
||
|
Trace.asyncTraceForTrackBegin(TRACE_TAG_APP, traceName(), traceName(), 0);
|
||
|
|
||
|
// start counting timeout.
|
||
|
mTimeoutRunnable = () -> {
|
||
|
Trace.instantForTrack(TRACE_TAG_APP, traceName(), "timeout");
|
||
|
timeoutAction.run();
|
||
|
};
|
||
|
BackgroundThread.getHandler()
|
||
|
.postDelayed(mTimeoutRunnable, TimeUnit.SECONDS.toMillis(15));
|
||
|
}
|
||
|
|
||
|
void end() {
|
||
|
mEndRtc = SystemClock.elapsedRealtime();
|
||
|
Trace.asyncTraceForTrackEnd(TRACE_TAG_APP, traceName(), 0);
|
||
|
BackgroundThread.getHandler().removeCallbacks(mTimeoutRunnable);
|
||
|
mTimeoutRunnable = null;
|
||
|
}
|
||
|
|
||
|
void cancel() {
|
||
|
Trace.instantForTrack(TRACE_TAG_APP, traceName(), "cancel");
|
||
|
Trace.asyncTraceForTrackEnd(TRACE_TAG_APP, traceName(), 0);
|
||
|
BackgroundThread.getHandler().removeCallbacks(mTimeoutRunnable);
|
||
|
mTimeoutRunnable = null;
|
||
|
}
|
||
|
|
||
|
int duration() {
|
||
|
return (int) (mEndRtc - mStartRtc);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@VisibleForTesting
|
||
|
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 threshold property
|
||
|
static final String LEGACY_TRACE_THRESHOLD_SUFFIX = "";
|
||
|
static final String TRACE_THRESHOLD_SUFFIX = "_trace_threshold";
|
||
|
|
||
|
@Action
|
||
|
private final int mAction;
|
||
|
private final boolean mEnabled;
|
||
|
private final int mSamplingInterval;
|
||
|
private final int mTraceThreshold;
|
||
|
|
||
|
@VisibleForTesting
|
||
|
public ActionProperties(
|
||
|
@Action int action,
|
||
|
boolean enabled,
|
||
|
int samplingInterval,
|
||
|
int traceThreshold) {
|
||
|
this.mAction = action;
|
||
|
com.android.internal.util.AnnotationValidations.validate(
|
||
|
Action.class, null, mAction);
|
||
|
this.mEnabled = enabled;
|
||
|
this.mSamplingInterval = samplingInterval;
|
||
|
this.mTraceThreshold = traceThreshold;
|
||
|
}
|
||
|
|
||
|
@VisibleForTesting
|
||
|
@Action
|
||
|
public int getAction() {
|
||
|
return mAction;
|
||
|
}
|
||
|
|
||
|
@VisibleForTesting
|
||
|
public boolean isEnabled() {
|
||
|
return mEnabled;
|
||
|
}
|
||
|
|
||
|
@VisibleForTesting
|
||
|
public int getSamplingInterval() {
|
||
|
return mSamplingInterval;
|
||
|
}
|
||
|
|
||
|
@VisibleForTesting
|
||
|
public int getTraceThreshold() {
|
||
|
return mTraceThreshold;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public String toString() {
|
||
|
return "ActionProperties{"
|
||
|
+ " mAction=" + mAction
|
||
|
+ ", mEnabled=" + mEnabled
|
||
|
+ ", mSamplingInterval=" + mSamplingInterval
|
||
|
+ ", 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
|
||
|
+ "}";
|
||
|
}
|
||
|
}
|
||
|
}
|