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