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