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     private static final int PULSE_REASONS = 5;
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 PULSE_REASON_SENSOR_PICKUP = 3;
45     public static final int PULSE_REASON_SENSOR_DOUBLE_TAP = 4;
46 
47     private static boolean sRegisterKeyguardCallback = true;
48 
49     private static long[] sTimes;
50     private static String[] sMessages;
51     private static int sPosition;
52     private static int sCount;
53     private static boolean sPulsing;
54 
55     private static long sSince;
56     private static SummaryStats sPickupPulseNearVibrationStats;
57     private static SummaryStats sPickupPulseNotNearVibrationStats;
58     private static SummaryStats sNotificationPulseStats;
59     private static SummaryStats sScreenOnPulsingStats;
60     private static SummaryStats sScreenOnNotPulsingStats;
61     private static SummaryStats sEmergencyCallStats;
62     private static SummaryStats[][] sProxStats; // [reason][near/far]
63 
tracePickupPulse(Context context, boolean withinVibrationThreshold)64     public static void tracePickupPulse(Context context, boolean withinVibrationThreshold) {
65         if (!ENABLED) return;
66         init(context);
67         log("pickupPulse withinVibrationThreshold=" + withinVibrationThreshold);
68         (withinVibrationThreshold ? sPickupPulseNearVibrationStats
69                 : sPickupPulseNotNearVibrationStats).append();
70     }
71 
tracePulseStart(int reason)72     public static void tracePulseStart(int reason) {
73         if (!ENABLED) return;
74         sPulsing = true;
75         log("pulseStart reason=" + pulseReasonToString(reason));
76     }
77 
tracePulseFinish()78     public static void tracePulseFinish() {
79         if (!ENABLED) return;
80         sPulsing = false;
81         log("pulseFinish");
82     }
83 
traceNotificationPulse(Context context)84     public static void traceNotificationPulse(Context context) {
85         if (!ENABLED) return;
86         init(context);
87         log("notificationPulse");
88         sNotificationPulseStats.append();
89     }
90 
init(Context context)91     private static void init(Context context) {
92         synchronized (DozeLog.class) {
93             if (sMessages == null) {
94                 sTimes = new long[SIZE];
95                 sMessages = new String[SIZE];
96                 sSince = System.currentTimeMillis();
97                 sPickupPulseNearVibrationStats = new SummaryStats();
98                 sPickupPulseNotNearVibrationStats = new SummaryStats();
99                 sNotificationPulseStats = new SummaryStats();
100                 sScreenOnPulsingStats = new SummaryStats();
101                 sScreenOnNotPulsingStats = new SummaryStats();
102                 sEmergencyCallStats = new SummaryStats();
103                 sProxStats = new SummaryStats[PULSE_REASONS][2];
104                 for (int i = 0; i < PULSE_REASONS; i++) {
105                     sProxStats[i][0] = new SummaryStats();
106                     sProxStats[i][1] = new SummaryStats();
107                 }
108                 log("init");
109                 if (sRegisterKeyguardCallback) {
110                     KeyguardUpdateMonitor.getInstance(context).registerCallback(sKeyguardCallback);
111                 }
112             }
113         }
114     }
115 
traceDozing(Context context, boolean dozing)116     public static void traceDozing(Context context, boolean dozing) {
117         if (!ENABLED) return;
118         sPulsing = false;
119         init(context);
120         log("dozing " + dozing);
121     }
122 
traceFling(boolean expand, boolean aboveThreshold, boolean thresholdNeeded, boolean screenOnFromTouch)123     public static void traceFling(boolean expand, boolean aboveThreshold, boolean thresholdNeeded,
124             boolean screenOnFromTouch) {
125         if (!ENABLED) return;
126         log("fling expand=" + expand + " aboveThreshold=" + aboveThreshold + " thresholdNeeded="
127                 + thresholdNeeded + " screenOnFromTouch=" + screenOnFromTouch);
128     }
129 
traceEmergencyCall()130     public static void traceEmergencyCall() {
131         if (!ENABLED) return;
132         log("emergencyCall");
133         sEmergencyCallStats.append();
134     }
135 
traceKeyguardBouncerChanged(boolean showing)136     public static void traceKeyguardBouncerChanged(boolean showing) {
137         if (!ENABLED) return;
138         log("bouncer " + showing);
139     }
140 
traceScreenOn()141     public static void traceScreenOn() {
142         if (!ENABLED) return;
143         log("screenOn pulsing=" + sPulsing);
144         (sPulsing ? sScreenOnPulsingStats : sScreenOnNotPulsingStats).append();
145         sPulsing = false;
146     }
147 
traceScreenOff(int why)148     public static void traceScreenOff(int why) {
149         if (!ENABLED) return;
150         log("screenOff why=" + why);
151     }
152 
traceMissedTick(String delay)153     public static void traceMissedTick(String delay) {
154         if (!ENABLED) return;
155         log("missedTick by=" + delay);
156     }
157 
traceKeyguard(boolean showing)158     public static void traceKeyguard(boolean showing) {
159         if (!ENABLED) return;
160         log("keyguard " + showing);
161         if (!showing) {
162             sPulsing = false;
163         }
164     }
165 
traceProximityResult(Context context, boolean near, long millis, int pulseReason)166     public static void traceProximityResult(Context context, boolean near, long millis,
167             int pulseReason) {
168         if (!ENABLED) return;
169         init(context);
170         log("proximityResult reason=" + pulseReasonToString(pulseReason) + " near=" + near
171                 + " millis=" + millis);
172         sProxStats[pulseReason][near ? 0 : 1].append();
173     }
174 
pulseReasonToString(int pulseReason)175     public static String pulseReasonToString(int pulseReason) {
176         switch (pulseReason) {
177             case PULSE_REASON_INTENT: return "intent";
178             case PULSE_REASON_NOTIFICATION: return "notification";
179             case PULSE_REASON_SENSOR_SIGMOTION: return "sigmotion";
180             case PULSE_REASON_SENSOR_PICKUP: return "pickup";
181             case PULSE_REASON_SENSOR_DOUBLE_TAP: return "doubletap";
182             default: throw new IllegalArgumentException("bad reason: " + pulseReason);
183         }
184     }
185 
dump(PrintWriter pw)186     public static void dump(PrintWriter pw) {
187         synchronized (DozeLog.class) {
188             if (sMessages == null) return;
189             pw.println("  Doze log:");
190             final int start = (sPosition - sCount + SIZE) % SIZE;
191             for (int i = 0; i < sCount; i++) {
192                 final int j = (start + i) % SIZE;
193                 pw.print("    ");
194                 pw.print(FORMAT.format(new Date(sTimes[j])));
195                 pw.print(' ');
196                 pw.println(sMessages[j]);
197             }
198             pw.print("  Doze summary stats (for ");
199             TimeUtils.formatDuration(System.currentTimeMillis() - sSince, pw);
200             pw.println("):");
201             sPickupPulseNearVibrationStats.dump(pw, "Pickup pulse (near vibration)");
202             sPickupPulseNotNearVibrationStats.dump(pw, "Pickup pulse (not near vibration)");
203             sNotificationPulseStats.dump(pw, "Notification pulse");
204             sScreenOnPulsingStats.dump(pw, "Screen on (pulsing)");
205             sScreenOnNotPulsingStats.dump(pw, "Screen on (not pulsing)");
206             sEmergencyCallStats.dump(pw, "Emergency call");
207             for (int i = 0; i < PULSE_REASONS; i++) {
208                 final String reason = pulseReasonToString(i);
209                 sProxStats[i][0].dump(pw, "Proximity near (" + reason + ")");
210                 sProxStats[i][1].dump(pw, "Proximity far (" + reason + ")");
211             }
212         }
213     }
214 
log(String msg)215     private static void log(String msg) {
216         synchronized (DozeLog.class) {
217             if (sMessages == null) return;
218             sTimes[sPosition] = System.currentTimeMillis();
219             sMessages[sPosition] = msg;
220             sPosition = (sPosition + 1) % SIZE;
221             sCount = Math.min(sCount + 1, SIZE);
222         }
223         if (DEBUG) Log.d(TAG, msg);
224     }
225 
tracePulseDropped(Context context, boolean pulsePending, DozeMachine.State state, boolean blocked)226     public static void tracePulseDropped(Context context, boolean pulsePending,
227             DozeMachine.State state, boolean blocked) {
228         if (!ENABLED) return;
229         init(context);
230         log("pulseDropped pulsePending=" + pulsePending + " state="
231                 + state + " blocked=" + blocked);
232     }
233 
tracePulseCanceledByProx(Context context)234     public static void tracePulseCanceledByProx(Context context) {
235         if (!ENABLED) return;
236         init(context);
237         log("pulseCanceledByProx");
238     }
239 
setRegisterKeyguardCallback(boolean registerKeyguardCallback)240     public static void setRegisterKeyguardCallback(boolean registerKeyguardCallback) {
241         if (!ENABLED) return;
242         synchronized (DozeLog.class) {
243             if (sRegisterKeyguardCallback != registerKeyguardCallback && sMessages != null) {
244                 throw new IllegalStateException("Cannot change setRegisterKeyguardCallback "
245                         + "after init()");
246             }
247             sRegisterKeyguardCallback = registerKeyguardCallback;
248         }
249     }
250 
251     private static class SummaryStats {
252         private int mCount;
253 
append()254         public void append() {
255             mCount++;
256         }
257 
dump(PrintWriter pw, String type)258         public void dump(PrintWriter pw, String type) {
259             if (mCount == 0) return;
260             pw.print("    ");
261             pw.print(type);
262             pw.print(": n=");
263             pw.print(mCount);
264             pw.print(" (");
265             final double perHr = (double) mCount / (System.currentTimeMillis() - sSince)
266                     * 1000 * 60 * 60;
267             pw.print(perHr);
268             pw.print("/hr)");
269             pw.println();
270         }
271     }
272 
273     private static final KeyguardUpdateMonitorCallback sKeyguardCallback =
274             new KeyguardUpdateMonitorCallback() {
275         @Override
276         public void onEmergencyCallAction() {
277             traceEmergencyCall();
278         }
279 
280         @Override
281         public void onKeyguardBouncerChanged(boolean bouncer) {
282             traceKeyguardBouncerChanged(bouncer);
283         }
284 
285         @Override
286         public void onStartedWakingUp() {
287             traceScreenOn();
288         }
289 
290         @Override
291         public void onFinishedGoingToSleep(int why) {
292             traceScreenOff(why);
293         }
294 
295         @Override
296         public void onKeyguardVisibilityChanged(boolean showing) {
297             traceKeyguard(showing);
298         }
299     };
300 }
301