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