1 /*
2  * Copyright (C) 2014 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.systemui.doze;
18 
19 import android.content.Context;
20 import android.os.Build;
21 import android.util.Log;
22 import android.util.TimeUtils;
23 
24 import com.android.keyguard.KeyguardUpdateMonitor;
25 import com.android.keyguard.KeyguardUpdateMonitorCallback;
26 
27 import java.io.PrintWriter;
28 import java.text.SimpleDateFormat;
29 import java.util.Date;
30 
31 public class DozeLog {
32     private static final String TAG = "DozeLog";
33     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
34     private static final boolean ENABLED = true;
35     private static final int SIZE = Build.IS_DEBUGGABLE ? 400 : 50;
36     static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
37 
38     public static final int REASONS = 10;
39 
40     public static final int PULSE_REASON_NONE = -1;
41     public static final int PULSE_REASON_INTENT = 0;
42     public static final int PULSE_REASON_NOTIFICATION = 1;
43     public static final int PULSE_REASON_SENSOR_SIGMOTION = 2;
44     public static final int REASON_SENSOR_PICKUP = 3;
45     public static final int REASON_SENSOR_DOUBLE_TAP = 4;
46     public static final int PULSE_REASON_SENSOR_LONG_PRESS = 5;
47     public static final int PULSE_REASON_DOCKING = 6;
48     public static final int REASON_SENSOR_WAKE_UP = 7;
49     public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 8;
50     public static final int REASON_SENSOR_TAP = 9;
51 
52     private static boolean sRegisterKeyguardCallback = true;
53 
54     private static long[] sTimes;
55     private static String[] sMessages;
56     private static int sPosition;
57     private static int sCount;
58     private static boolean sPulsing;
59 
60     private static long sSince;
61     private static SummaryStats sPickupPulseNearVibrationStats;
62     private static SummaryStats sPickupPulseNotNearVibrationStats;
63     private static SummaryStats sNotificationPulseStats;
64     private static SummaryStats sScreenOnPulsingStats;
65     private static SummaryStats sScreenOnNotPulsingStats;
66     private static SummaryStats sEmergencyCallStats;
67     private static SummaryStats[][] sProxStats; // [reason][near/far]
68 
tracePickupWakeUp(Context context, boolean withinVibrationThreshold)69     public static void tracePickupWakeUp(Context context, boolean withinVibrationThreshold) {
70         if (!ENABLED) return;
71         init(context);
72         log("pickupWakeUp withinVibrationThreshold=" + withinVibrationThreshold);
73         (withinVibrationThreshold ? sPickupPulseNearVibrationStats
74                 : sPickupPulseNotNearVibrationStats).append();
75     }
76 
tracePulseStart(int reason)77     public static void tracePulseStart(int reason) {
78         if (!ENABLED) return;
79         sPulsing = true;
80         log("pulseStart reason=" + reasonToString(reason));
81     }
82 
tracePulseFinish()83     public static void tracePulseFinish() {
84         if (!ENABLED) return;
85         sPulsing = false;
86         log("pulseFinish");
87     }
88 
traceNotificationPulse(Context context)89     public static void traceNotificationPulse(Context context) {
90         if (!ENABLED) return;
91         init(context);
92         log("notificationPulse");
93         sNotificationPulseStats.append();
94     }
95 
init(Context context)96     private static void init(Context context) {
97         synchronized (DozeLog.class) {
98             if (sMessages == null) {
99                 sTimes = new long[SIZE];
100                 sMessages = new String[SIZE];
101                 sSince = System.currentTimeMillis();
102                 sPickupPulseNearVibrationStats = new SummaryStats();
103                 sPickupPulseNotNearVibrationStats = new SummaryStats();
104                 sNotificationPulseStats = new SummaryStats();
105                 sScreenOnPulsingStats = new SummaryStats();
106                 sScreenOnNotPulsingStats = new SummaryStats();
107                 sEmergencyCallStats = new SummaryStats();
108                 sProxStats = new SummaryStats[REASONS][2];
109                 for (int i = 0; i < REASONS; i++) {
110                     sProxStats[i][0] = new SummaryStats();
111                     sProxStats[i][1] = new SummaryStats();
112                 }
113                 log("init");
114                 if (sRegisterKeyguardCallback) {
115                     KeyguardUpdateMonitor.getInstance(context).registerCallback(sKeyguardCallback);
116                 }
117             }
118         }
119     }
120 
traceDozing(Context context, boolean dozing)121     public static void traceDozing(Context context, boolean dozing) {
122         if (!ENABLED) return;
123         sPulsing = false;
124         init(context);
125         log("dozing " + dozing);
126     }
127 
traceFling(boolean expand, boolean aboveThreshold, boolean thresholdNeeded, boolean screenOnFromTouch)128     public static void traceFling(boolean expand, boolean aboveThreshold, boolean thresholdNeeded,
129             boolean screenOnFromTouch) {
130         if (!ENABLED) return;
131         log("fling expand=" + expand + " aboveThreshold=" + aboveThreshold + " thresholdNeeded="
132                 + thresholdNeeded + " screenOnFromTouch=" + screenOnFromTouch);
133     }
134 
traceEmergencyCall()135     public static void traceEmergencyCall() {
136         if (!ENABLED) return;
137         log("emergencyCall");
138         sEmergencyCallStats.append();
139     }
140 
traceKeyguardBouncerChanged(boolean showing)141     public static void traceKeyguardBouncerChanged(boolean showing) {
142         if (!ENABLED) return;
143         log("bouncer " + showing);
144     }
145 
traceScreenOn()146     public static void traceScreenOn() {
147         if (!ENABLED) return;
148         log("screenOn pulsing=" + sPulsing);
149         (sPulsing ? sScreenOnPulsingStats : sScreenOnNotPulsingStats).append();
150         sPulsing = false;
151     }
152 
traceScreenOff(int why)153     public static void traceScreenOff(int why) {
154         if (!ENABLED) return;
155         log("screenOff why=" + why);
156     }
157 
traceMissedTick(String delay)158     public static void traceMissedTick(String delay) {
159         if (!ENABLED) return;
160         log("missedTick by=" + delay);
161     }
162 
traceTimeTickScheduled(long when, long triggerAt)163     public static void traceTimeTickScheduled(long when, long triggerAt) {
164         if (!ENABLED) return;
165         log("timeTickScheduled at=" + FORMAT.format(new Date(when)) + " triggerAt="
166                 + FORMAT.format(new Date(triggerAt)));
167     }
168 
traceKeyguard(boolean showing)169     public static void traceKeyguard(boolean showing) {
170         if (!ENABLED) return;
171         log("keyguard " + showing);
172         if (!showing) {
173             sPulsing = false;
174         }
175     }
176 
traceState(DozeMachine.State state)177     public static void traceState(DozeMachine.State state) {
178         if (!ENABLED) return;
179         log("state " + state);
180     }
181 
182     /**
183      * Appends wake-display event to the logs.
184      * @param wake if we're waking up or sleeping.
185      */
traceWakeDisplay(boolean wake)186     public static void traceWakeDisplay(boolean wake) {
187         if (!ENABLED) return;
188         log("wakeDisplay " + wake);
189     }
190 
traceProximityResult(Context context, boolean near, long millis, int reason)191     public static void traceProximityResult(Context context, boolean near, long millis,
192             int reason) {
193         if (!ENABLED) return;
194         init(context);
195         log("proximityResult reason=" + reasonToString(reason) + " near=" + near
196                 + " millis=" + millis);
197         sProxStats[reason][near ? 0 : 1].append();
198     }
199 
reasonToString(int pulseReason)200     public static String reasonToString(int pulseReason) {
201         switch (pulseReason) {
202             case PULSE_REASON_INTENT: return "intent";
203             case PULSE_REASON_NOTIFICATION: return "notification";
204             case PULSE_REASON_SENSOR_SIGMOTION: return "sigmotion";
205             case REASON_SENSOR_PICKUP: return "pickup";
206             case REASON_SENSOR_DOUBLE_TAP: return "doubletap";
207             case PULSE_REASON_SENSOR_LONG_PRESS: return "longpress";
208             case PULSE_REASON_DOCKING: return "docking";
209             case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "wakelockscreen";
210             case REASON_SENSOR_WAKE_UP: return "wakeup";
211             case REASON_SENSOR_TAP: return "tap";
212             default: throw new IllegalArgumentException("bad reason: " + pulseReason);
213         }
214     }
215 
dump(PrintWriter pw)216     public static void dump(PrintWriter pw) {
217         synchronized (DozeLog.class) {
218             if (sMessages == null) return;
219             pw.println("  Doze log:");
220             final int start = (sPosition - sCount + SIZE) % SIZE;
221             for (int i = 0; i < sCount; i++) {
222                 final int j = (start + i) % SIZE;
223                 pw.print("    ");
224                 pw.print(FORMAT.format(new Date(sTimes[j])));
225                 pw.print(' ');
226                 pw.println(sMessages[j]);
227             }
228             pw.print("  Doze summary stats (for ");
229             TimeUtils.formatDuration(System.currentTimeMillis() - sSince, pw);
230             pw.println("):");
231             sPickupPulseNearVibrationStats.dump(pw, "Pickup pulse (near vibration)");
232             sPickupPulseNotNearVibrationStats.dump(pw, "Pickup pulse (not near vibration)");
233             sNotificationPulseStats.dump(pw, "Notification pulse");
234             sScreenOnPulsingStats.dump(pw, "Screen on (pulsing)");
235             sScreenOnNotPulsingStats.dump(pw, "Screen on (not pulsing)");
236             sEmergencyCallStats.dump(pw, "Emergency call");
237             for (int i = 0; i < REASONS; i++) {
238                 final String reason = reasonToString(i);
239                 sProxStats[i][0].dump(pw, "Proximity near (" + reason + ")");
240                 sProxStats[i][1].dump(pw, "Proximity far (" + reason + ")");
241             }
242         }
243     }
244 
log(String msg)245     private static void log(String msg) {
246         synchronized (DozeLog.class) {
247             if (sMessages == null) return;
248             sTimes[sPosition] = System.currentTimeMillis();
249             sMessages[sPosition] = msg;
250             sPosition = (sPosition + 1) % SIZE;
251             sCount = Math.min(sCount + 1, SIZE);
252         }
253         if (DEBUG) Log.d(TAG, msg);
254     }
255 
tracePulseDropped(Context context, boolean pulsePending, DozeMachine.State state, boolean blocked)256     public static void tracePulseDropped(Context context, boolean pulsePending,
257             DozeMachine.State state, boolean blocked) {
258         if (!ENABLED) return;
259         init(context);
260         log("pulseDropped pulsePending=" + pulsePending + " state="
261                 + state + " blocked=" + blocked);
262     }
263 
tracePulseDropped(Context context, String why)264     public static void tracePulseDropped(Context context, String why) {
265         if (!ENABLED) return;
266         init(context);
267         log("pulseDropped why=" + why);
268     }
269 
tracePulseTouchDisabledByProx(Context context, boolean disabled)270     public static void tracePulseTouchDisabledByProx(Context context, boolean disabled) {
271         if (!ENABLED) return;
272         init(context);
273         log("pulseTouchDisabledByProx " + disabled);
274     }
275 
setRegisterKeyguardCallback(boolean registerKeyguardCallback)276     public static void setRegisterKeyguardCallback(boolean registerKeyguardCallback) {
277         if (!ENABLED) return;
278         synchronized (DozeLog.class) {
279             if (sRegisterKeyguardCallback != registerKeyguardCallback && sMessages != null) {
280                 throw new IllegalStateException("Cannot change setRegisterKeyguardCallback "
281                         + "after init()");
282             }
283             sRegisterKeyguardCallback = registerKeyguardCallback;
284         }
285     }
286 
traceSensor(Context context, int reason)287     public static void traceSensor(Context context, int reason) {
288         if (!ENABLED) return;
289         init(context);
290         log("sensor type=" + reasonToString(reason));
291     }
292 
293     private static class SummaryStats {
294         private int mCount;
295 
append()296         public void append() {
297             mCount++;
298         }
299 
dump(PrintWriter pw, String type)300         public void dump(PrintWriter pw, String type) {
301             if (mCount == 0) return;
302             pw.print("    ");
303             pw.print(type);
304             pw.print(": n=");
305             pw.print(mCount);
306             pw.print(" (");
307             final double perHr = (double) mCount / (System.currentTimeMillis() - sSince)
308                     * 1000 * 60 * 60;
309             pw.print(perHr);
310             pw.print("/hr)");
311             pw.println();
312         }
313     }
314 
315     private static final KeyguardUpdateMonitorCallback sKeyguardCallback =
316             new KeyguardUpdateMonitorCallback() {
317         @Override
318         public void onEmergencyCallAction() {
319             traceEmergencyCall();
320         }
321 
322         @Override
323         public void onKeyguardBouncerChanged(boolean bouncer) {
324             traceKeyguardBouncerChanged(bouncer);
325         }
326 
327         @Override
328         public void onStartedWakingUp() {
329             traceScreenOn();
330         }
331 
332         @Override
333         public void onFinishedGoingToSleep(int why) {
334             traceScreenOff(why);
335         }
336 
337         @Override
338         public void onKeyguardVisibilityChanged(boolean showing) {
339             traceKeyguard(showing);
340         }
341     };
342 }
343