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