| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License |
| */ |
| |
| package com.android.systemui.doze; |
| |
| import android.annotation.IntDef; |
| import android.util.TimeUtils; |
| |
| import androidx.annotation.NonNull; |
| |
| import com.android.keyguard.KeyguardUpdateMonitor; |
| import com.android.keyguard.KeyguardUpdateMonitorCallback; |
| import com.android.systemui.DumpController; |
| import com.android.systemui.Dumpable; |
| |
| import java.io.FileDescriptor; |
| import java.io.PrintWriter; |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| |
| import javax.inject.Inject; |
| import javax.inject.Singleton; |
| |
| /** |
| * Logs doze events for debugging and triaging purposes. Logs are dumped in bugreports or on demand: |
| * adb shell dumpsys activity service com.android.systemui/.SystemUIService \ |
| * dependency DumpController DozeLog,DozeStats |
| */ |
| @Singleton |
| public class DozeLog implements Dumpable { |
| private final DozeLogger mLogger; |
| |
| private boolean mPulsing; |
| private long mSince; |
| private SummaryStats mPickupPulseNearVibrationStats; |
| private SummaryStats mPickupPulseNotNearVibrationStats; |
| private SummaryStats mNotificationPulseStats; |
| private SummaryStats mScreenOnPulsingStats; |
| private SummaryStats mScreenOnNotPulsingStats; |
| private SummaryStats mEmergencyCallStats; |
| private SummaryStats[][] mProxStats; // [reason][near/far] |
| |
| @Inject |
| public DozeLog( |
| KeyguardUpdateMonitor keyguardUpdateMonitor, |
| DumpController dumpController, |
| DozeLogger logger) { |
| mLogger = logger; |
| mSince = System.currentTimeMillis(); |
| mPickupPulseNearVibrationStats = new SummaryStats(); |
| mPickupPulseNotNearVibrationStats = new SummaryStats(); |
| mNotificationPulseStats = new SummaryStats(); |
| mScreenOnPulsingStats = new SummaryStats(); |
| mScreenOnNotPulsingStats = new SummaryStats(); |
| mEmergencyCallStats = new SummaryStats(); |
| mProxStats = new SummaryStats[TOTAL_REASONS][2]; |
| for (int i = 0; i < TOTAL_REASONS; i++) { |
| mProxStats[i][0] = new SummaryStats(); |
| mProxStats[i][1] = new SummaryStats(); |
| } |
| |
| if (keyguardUpdateMonitor != null) { |
| keyguardUpdateMonitor.registerCallback(mKeyguardCallback); |
| } |
| |
| dumpController.registerDumpable("DumpStats", this); |
| } |
| |
| /** |
| * Appends pickup wakeup event to the logs |
| */ |
| public void tracePickupWakeUp(boolean withinVibrationThreshold) { |
| mLogger.logPickupWakeup(withinVibrationThreshold); |
| (withinVibrationThreshold ? mPickupPulseNearVibrationStats |
| : mPickupPulseNotNearVibrationStats).append(); |
| } |
| |
| /** |
| * Appends pulse started event to the logs. |
| * @param reason why the pulse started |
| */ |
| public void tracePulseStart(@Reason int reason) { |
| mLogger.logPulseStart(reason); |
| mPulsing = true; |
| } |
| |
| /** |
| * Appends pulse finished event to the logs |
| */ |
| public void tracePulseFinish() { |
| mLogger.logPulseFinish(); |
| mPulsing = false; |
| } |
| |
| /** |
| * Appends pulse event to the logs |
| */ |
| public void traceNotificationPulse() { |
| mLogger.logNotificationPulse(); |
| mNotificationPulseStats.append(); |
| } |
| |
| /** |
| * Appends dozing event to the logs |
| * @param dozing true if dozing, else false |
| */ |
| public void traceDozing(boolean dozing) { |
| mLogger.logDozing(dozing); |
| mPulsing = false; |
| } |
| |
| /** |
| * Appends fling event to the logs |
| */ |
| public void traceFling(boolean expand, boolean aboveThreshold, boolean thresholdNeeded, |
| boolean screenOnFromTouch) { |
| mLogger.logFling(expand, aboveThreshold, thresholdNeeded, screenOnFromTouch); |
| } |
| |
| /** |
| * Appends emergency call event to the logs |
| */ |
| public void traceEmergencyCall() { |
| mLogger.logEmergencyCall(); |
| mEmergencyCallStats.append(); |
| } |
| |
| /** |
| * Appends keyguard bouncer changed event to the logs |
| * @param showing true if the keyguard bouncer is showing, else false |
| */ |
| public void traceKeyguardBouncerChanged(boolean showing) { |
| mLogger.logKeyguardBouncerChanged(showing); |
| } |
| |
| /** |
| * Appends screen-on event to the logs |
| */ |
| public void traceScreenOn() { |
| mLogger.logScreenOn(mPulsing); |
| (mPulsing ? mScreenOnPulsingStats : mScreenOnNotPulsingStats).append(); |
| mPulsing = false; |
| } |
| |
| /** |
| * Appends screen-off event to the logs |
| * @param why reason the screen is off |
| */ |
| public void traceScreenOff(int why) { |
| mLogger.logScreenOff(why); |
| } |
| |
| /** |
| * Appends missed tick event to the logs |
| * @param delay of the missed tick |
| */ |
| public void traceMissedTick(String delay) { |
| mLogger.logMissedTick(delay); |
| } |
| |
| /** |
| * Appends time tick scheduled event to the logs |
| * @param when time tick scheduled at |
| * @param triggerAt time tick trigger at |
| */ |
| public void traceTimeTickScheduled(long when, long triggerAt) { |
| mLogger.logTimeTickScheduled(when, triggerAt); |
| } |
| |
| /** |
| * Appends keyguard visibility change event to the logs |
| * @param showing whether the keyguard is now showing |
| */ |
| public void traceKeyguard(boolean showing) { |
| mLogger.logKeyguardVisibilityChange(showing); |
| if (!showing) mPulsing = false; |
| } |
| |
| /** |
| * Appends doze state changed event to the logs |
| * @param state new DozeMachine state |
| */ |
| public void traceState(DozeMachine.State state) { |
| mLogger.logDozeStateChanged(state); |
| } |
| |
| /** |
| * Appends wake-display event to the logs. |
| * @param wake if we're waking up or sleeping. |
| */ |
| public void traceWakeDisplay(boolean wake) { |
| mLogger.logWakeDisplay(wake); |
| } |
| |
| /** |
| * Appends proximity result event to the logs |
| * @param near true if near, else false |
| * @param reason why proximity result was triggered |
| */ |
| public void traceProximityResult(boolean near, long millis, @Reason int reason) { |
| mLogger.logProximityResult(near, millis, reason); |
| mProxStats[reason][near ? 0 : 1].append(); |
| } |
| |
| @Override |
| public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { |
| synchronized (DozeLog.class) { |
| pw.print(" Doze summary stats (for "); |
| TimeUtils.formatDuration(System.currentTimeMillis() - mSince, pw); |
| pw.println("):"); |
| mPickupPulseNearVibrationStats.dump(pw, "Pickup pulse (near vibration)"); |
| mPickupPulseNotNearVibrationStats.dump(pw, "Pickup pulse (not near vibration)"); |
| mNotificationPulseStats.dump(pw, "Notification pulse"); |
| mScreenOnPulsingStats.dump(pw, "Screen on (pulsing)"); |
| mScreenOnNotPulsingStats.dump(pw, "Screen on (not pulsing)"); |
| mEmergencyCallStats.dump(pw, "Emergency call"); |
| for (int i = 0; i < TOTAL_REASONS; i++) { |
| final String reason = reasonToString(i); |
| mProxStats[i][0].dump(pw, "Proximity near (" + reason + ")"); |
| mProxStats[i][1].dump(pw, "Proximity far (" + reason + ")"); |
| } |
| } |
| } |
| |
| /** |
| * Appends pulse dropped event to logs |
| */ |
| public void tracePulseDropped(boolean pulsePending, DozeMachine.State state, boolean blocked) { |
| mLogger.logPulseDropped(pulsePending, state, blocked); |
| } |
| |
| /** |
| * Appends pulse dropped event to logs |
| * @param reason why the pulse was dropped |
| */ |
| public void tracePulseDropped(String reason) { |
| mLogger.logPulseDropped(reason); |
| } |
| |
| /** |
| * Appends pulse touch displayed by prox sensor event to logs |
| * @param disabled |
| */ |
| public void tracePulseTouchDisabledByProx(boolean disabled) { |
| mLogger.logPulseTouchDisabledByProx(disabled); |
| } |
| |
| /** |
| * Appends sensor triggered event to logs |
| * @param reason why the sensor was triggered |
| */ |
| public void traceSensor(@Reason int reason) { |
| mLogger.logSensorTriggered(reason); |
| } |
| |
| private class SummaryStats { |
| private int mCount; |
| |
| public void append() { |
| mCount++; |
| } |
| |
| public void dump(PrintWriter pw, String type) { |
| if (mCount == 0) return; |
| pw.print(" "); |
| pw.print(type); |
| pw.print(": n="); |
| pw.print(mCount); |
| pw.print(" ("); |
| final double perHr = (double) mCount / (System.currentTimeMillis() - mSince) |
| * 1000 * 60 * 60; |
| pw.print(perHr); |
| pw.print("/hr)"); |
| pw.println(); |
| } |
| } |
| |
| private final KeyguardUpdateMonitorCallback mKeyguardCallback = |
| new KeyguardUpdateMonitorCallback() { |
| @Override |
| public void onEmergencyCallAction() { |
| traceEmergencyCall(); |
| } |
| |
| @Override |
| public void onKeyguardBouncerChanged(boolean bouncer) { |
| traceKeyguardBouncerChanged(bouncer); |
| } |
| |
| @Override |
| public void onStartedWakingUp() { |
| traceScreenOn(); |
| } |
| |
| @Override |
| public void onFinishedGoingToSleep(int why) { |
| traceScreenOff(why); |
| } |
| |
| @Override |
| public void onKeyguardVisibilityChanged(boolean showing) { |
| traceKeyguard(showing); |
| } |
| }; |
| |
| /** |
| * Converts the reason (integer) to a user-readable string |
| */ |
| public static String reasonToString(@Reason int pulseReason) { |
| switch (pulseReason) { |
| case PULSE_REASON_INTENT: return "intent"; |
| case PULSE_REASON_NOTIFICATION: return "notification"; |
| case PULSE_REASON_SENSOR_SIGMOTION: return "sigmotion"; |
| case REASON_SENSOR_PICKUP: return "pickup"; |
| case REASON_SENSOR_DOUBLE_TAP: return "doubletap"; |
| case PULSE_REASON_SENSOR_LONG_PRESS: return "longpress"; |
| case PULSE_REASON_DOCKING: return "docking"; |
| case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "wakelockscreen"; |
| case REASON_SENSOR_WAKE_UP: return "wakeup"; |
| case REASON_SENSOR_TAP: return "tap"; |
| default: throw new IllegalArgumentException("invalid reason: " + pulseReason); |
| } |
| } |
| |
| @Retention(RetentionPolicy.SOURCE) |
| @IntDef({PULSE_REASON_NONE, PULSE_REASON_INTENT, PULSE_REASON_NOTIFICATION, |
| PULSE_REASON_SENSOR_SIGMOTION, REASON_SENSOR_PICKUP, REASON_SENSOR_DOUBLE_TAP, |
| PULSE_REASON_SENSOR_LONG_PRESS, PULSE_REASON_DOCKING, REASON_SENSOR_WAKE_UP, |
| PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, REASON_SENSOR_TAP}) |
| public @interface Reason {} |
| public static final int PULSE_REASON_NONE = -1; |
| public static final int PULSE_REASON_INTENT = 0; |
| public static final int PULSE_REASON_NOTIFICATION = 1; |
| public static final int PULSE_REASON_SENSOR_SIGMOTION = 2; |
| public static final int REASON_SENSOR_PICKUP = 3; |
| public static final int REASON_SENSOR_DOUBLE_TAP = 4; |
| public static final int PULSE_REASON_SENSOR_LONG_PRESS = 5; |
| public static final int PULSE_REASON_DOCKING = 6; |
| public static final int REASON_SENSOR_WAKE_UP = 7; |
| public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 8; |
| public static final int REASON_SENSOR_TAP = 9; |
| |
| public static final int TOTAL_REASONS = 10; |
| } |