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