1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package com.android.internal.util; 16 17 import android.content.BroadcastReceiver; 18 import android.content.Context; 19 import android.content.Intent; 20 import android.content.IntentFilter; 21 import android.os.Build; 22 import android.os.SystemClock; 23 import android.os.SystemProperties; 24 import android.os.Trace; 25 import android.util.EventLog; 26 import android.util.Log; 27 import android.util.SparseLongArray; 28 29 import com.android.internal.logging.EventLogTags; 30 31 /** 32 * Class to track various latencies in SystemUI. It then outputs the latency to logcat so these 33 * latencies can be captured by tests and then used for dashboards. 34 * <p> 35 * This is currently only in Keyguard so it can be shared between SystemUI and Keyguard, but 36 * eventually we'd want to merge these two packages together so Keyguard can use common classes 37 * that are shared with SystemUI. 38 */ 39 public class LatencyTracker { 40 41 private static final String ACTION_RELOAD_PROPERTY = 42 "com.android.systemui.RELOAD_LATENCY_TRACKER_PROPERTY"; 43 44 private static final String TAG = "LatencyTracker"; 45 46 /** 47 * Time it takes until the first frame of the notification panel to be displayed while expanding 48 */ 49 public static final int ACTION_EXPAND_PANEL = 0; 50 51 /** 52 * Time it takes until the first frame of recents is drawn after invoking it with the button. 53 */ 54 public static final int ACTION_TOGGLE_RECENTS = 1; 55 56 /** 57 * Time between we get a fingerprint acquired signal until we start with the unlock animation 58 */ 59 public static final int ACTION_FINGERPRINT_WAKE_AND_UNLOCK = 2; 60 61 /** 62 * Time it takes to check PIN/Pattern/Password. 63 */ 64 public static final int ACTION_CHECK_CREDENTIAL = 3; 65 66 /** 67 * Time it takes to check fully PIN/Pattern/Password, i.e. that's the time spent including the 68 * actions to unlock a user. 69 */ 70 public static final int ACTION_CHECK_CREDENTIAL_UNLOCKED = 4; 71 72 /** 73 * Time it takes to turn on the screen. 74 */ 75 public static final int ACTION_TURN_ON_SCREEN = 5; 76 77 /** 78 * Time it takes to rotate the screen. 79 */ 80 public static final int ACTION_ROTATE_SCREEN = 6; 81 82 /* 83 * Time between we get a face acquired signal until we start with the unlock animation 84 */ 85 public static final int ACTION_FACE_WAKE_AND_UNLOCK = 6; 86 87 private static final String[] NAMES = new String[] { 88 "expand panel", 89 "toggle recents", 90 "fingerprint wake-and-unlock", 91 "check credential", 92 "check credential unlocked", 93 "turn on screen", 94 "rotate the screen", 95 "face wake-and-unlock" }; 96 97 private static LatencyTracker sLatencyTracker; 98 99 private final SparseLongArray mStartRtc = new SparseLongArray(); 100 private boolean mEnabled; 101 getInstance(Context context)102 public static LatencyTracker getInstance(Context context) { 103 if (sLatencyTracker == null) { 104 sLatencyTracker = new LatencyTracker(context.getApplicationContext()); 105 } 106 return sLatencyTracker; 107 } 108 LatencyTracker(Context context)109 private LatencyTracker(Context context) { 110 context.registerReceiver(new BroadcastReceiver() { 111 @Override 112 public void onReceive(Context context, Intent intent) { 113 reloadProperty(); 114 } 115 }, new IntentFilter(ACTION_RELOAD_PROPERTY)); 116 reloadProperty(); 117 } 118 reloadProperty()119 private void reloadProperty() { 120 mEnabled = SystemProperties.getBoolean("debug.systemui.latency_tracking", false); 121 } 122 isEnabled(Context ctx)123 public static boolean isEnabled(Context ctx) { 124 return Build.IS_DEBUGGABLE && getInstance(ctx).mEnabled; 125 } 126 127 /** 128 * Notifies that an action is starting. This needs to be called from the main thread. 129 * 130 * @param action The action to start. One of the ACTION_* values. 131 */ onActionStart(int action)132 public void onActionStart(int action) { 133 if (!mEnabled) { 134 return; 135 } 136 Trace.asyncTraceBegin(Trace.TRACE_TAG_APP, NAMES[action], 0); 137 mStartRtc.put(action, SystemClock.elapsedRealtime()); 138 } 139 140 /** 141 * Notifies that an action has ended. This needs to be called from the main thread. 142 * 143 * @param action The action to end. One of the ACTION_* values. 144 */ onActionEnd(int action)145 public void onActionEnd(int action) { 146 if (!mEnabled) { 147 return; 148 } 149 long endRtc = SystemClock.elapsedRealtime(); 150 long startRtc = mStartRtc.get(action, -1); 151 if (startRtc == -1) { 152 return; 153 } 154 mStartRtc.delete(action); 155 Trace.asyncTraceEnd(Trace.TRACE_TAG_APP, NAMES[action], 0); 156 logAction(action, (int)(endRtc - startRtc)); 157 } 158 159 /** 160 * Logs an action that has started and ended. This needs to be called from the main thread. 161 * 162 * @param action The action to end. One of the ACTION_* values. 163 * @param duration The duration of the action in ms. 164 */ logAction(int action, int duration)165 public static void logAction(int action, int duration) { 166 Log.i(TAG, "action=" + action + " latency=" + duration); 167 EventLog.writeEvent(EventLogTags.SYSUI_LATENCY, action, duration); 168 } 169 } 170