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 tracePulseTouchDisabledByProx(Context context, boolean disabled)264 public static void tracePulseTouchDisabledByProx(Context context, boolean disabled) { 265 if (!ENABLED) return; 266 init(context); 267 log("pulseTouchDisabledByProx " + disabled); 268 } 269 setRegisterKeyguardCallback(boolean registerKeyguardCallback)270 public static void setRegisterKeyguardCallback(boolean registerKeyguardCallback) { 271 if (!ENABLED) return; 272 synchronized (DozeLog.class) { 273 if (sRegisterKeyguardCallback != registerKeyguardCallback && sMessages != null) { 274 throw new IllegalStateException("Cannot change setRegisterKeyguardCallback " 275 + "after init()"); 276 } 277 sRegisterKeyguardCallback = registerKeyguardCallback; 278 } 279 } 280 traceSensor(Context context, int reason)281 public static void traceSensor(Context context, int reason) { 282 if (!ENABLED) return; 283 init(context); 284 log("sensor type=" + reasonToString(reason)); 285 } 286 287 private static class SummaryStats { 288 private int mCount; 289 append()290 public void append() { 291 mCount++; 292 } 293 dump(PrintWriter pw, String type)294 public void dump(PrintWriter pw, String type) { 295 if (mCount == 0) return; 296 pw.print(" "); 297 pw.print(type); 298 pw.print(": n="); 299 pw.print(mCount); 300 pw.print(" ("); 301 final double perHr = (double) mCount / (System.currentTimeMillis() - sSince) 302 * 1000 * 60 * 60; 303 pw.print(perHr); 304 pw.print("/hr)"); 305 pw.println(); 306 } 307 } 308 309 private static final KeyguardUpdateMonitorCallback sKeyguardCallback = 310 new KeyguardUpdateMonitorCallback() { 311 @Override 312 public void onEmergencyCallAction() { 313 traceEmergencyCall(); 314 } 315 316 @Override 317 public void onKeyguardBouncerChanged(boolean bouncer) { 318 traceKeyguardBouncerChanged(bouncer); 319 } 320 321 @Override 322 public void onStartedWakingUp() { 323 traceScreenOn(); 324 } 325 326 @Override 327 public void onFinishedGoingToSleep(int why) { 328 traceScreenOff(why); 329 } 330 331 @Override 332 public void onKeyguardVisibilityChanged(boolean showing) { 333 traceKeyguard(showing); 334 } 335 }; 336 } 337