Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6c18bcf9 authored by yumeichen's avatar yumeichen
Browse files

Log screen dim event

Flag: EXEMPT statsd atoms/metrics changes
Test: Manually test by statsd_testdrive
Bug: 315246849
Bug: 316021336
Bug: 336474167
Change-Id: I46ea4c6ee3014e348512ea2586567d9f90d85f01
parent f6a1ed56
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -206,7 +206,6 @@ public class Notifier {
        mPolicy = policy;
        mFaceDownDetector = faceDownDetector;
        mScreenUndimDetector = screenUndimDetector;
        mWakefulnessSessionObserver = new WakefulnessSessionObserver(mContext, null);
        mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
        mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
        mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class);
@@ -214,6 +213,7 @@ public class Notifier {
        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
        mTrustManager = mContext.getSystemService(TrustManager.class);
        mVibrator = mContext.getSystemService(Vibrator.class);
        mWakefulnessSessionObserver = new WakefulnessSessionObserver(mContext, null);

        mHandler = new NotifierHandler(looper);
        mBackgroundExecutor = backgroundExecutor;
@@ -813,6 +813,8 @@ public class Notifier {
        if (DEBUG) {
            Slog.d(TAG, "onScreenPolicyUpdate: newPolicy=" + newPolicy);
        }
        mWakefulnessSessionObserver.onScreenPolicyUpdate(
                SystemClock.uptimeMillis(), displayGroupId, newPolicy);

        synchronized (mLock) {
            Message msg = mHandler.obtainMessage(MSG_SCREEN_POLICY);
+276 −9
Original line number Diff line number Diff line
@@ -16,8 +16,11 @@

package com.android.server.power;

import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DIM;
import static android.os.PowerManager.USER_ACTIVITY_EVENT_OTHER;
import static android.os.PowerManagerInternal.isInteractive;
import static android.view.Display.DEFAULT_DISPLAY;

import static com.android.server.power.PowerManagerService.DEFAULT_SCREEN_OFF_TIMEOUT;
import static com.android.server.power.ScreenTimeoutOverridePolicy.RELEASE_REASON_NON_INTERACTIVE;
@@ -34,6 +37,9 @@ import android.app.ActivityManager;
import android.app.SynchronousUserSwitchObserver;
import android.content.Context;
import android.database.ContentObserver;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.os.Handler;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
@@ -44,9 +50,13 @@ import android.provider.Settings;
import android.util.IndentingPrintWriter;
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayAddress;
import android.view.DisplayInfo;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;

import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -117,9 +127,42 @@ public class WakefulnessSessionObserver {
    @Retention(RetentionPolicy.SOURCE)
    private @interface OverrideOutcome {}

    private static final int DEFAULT_USER_ACTIVITY = USER_ACTIVITY_EVENT_OTHER;
    private static final long TIMEOUT_USER_INITIATED_REVERT_THRESHOLD_MILLIS = 5000L;
    private static final int POLICY_REASON_UNKNOWN = FrameworkStatsLog
            .SCREEN_DIM_REPORTED__POLICY_REASON__UNKNOWN;
    @VisibleForTesting
    protected static final int POLICY_REASON_OFF_TIMEOUT = FrameworkStatsLog
            .SCREEN_DIM_REPORTED__POLICY_REASON__OFF_TIMEOUT;
    @VisibleForTesting
    protected static final int POLICY_REASON_OFF_POWER_BUTTON = FrameworkStatsLog
            .SCREEN_DIM_REPORTED__POLICY_REASON__OFF_POWER_BUTTON;
    @VisibleForTesting
    protected static final int POLICY_REASON_BRIGHT_UNDIM = FrameworkStatsLog
            .SCREEN_DIM_REPORTED__POLICY_REASON__BRIGHT_UNDIM;
    @VisibleForTesting
    protected static final int POLICY_REASON_BRIGHT_INITIATED_REVERT = FrameworkStatsLog
            .SCREEN_DIM_REPORTED__POLICY_REASON__BRIGHT_INITIATED_REVERT;

    /**
     * Policy Reason
     * {@link android.os.statsd.power.ScreenDimReported.PolicyReason}.
     */
    @IntDef(prefix = {"POLICY_REASON_"}, value = {
            POLICY_REASON_UNKNOWN,
            POLICY_REASON_OFF_TIMEOUT,
            POLICY_REASON_OFF_POWER_BUTTON,
            POLICY_REASON_BRIGHT_UNDIM,
            POLICY_REASON_BRIGHT_INITIATED_REVERT
    })
    @Retention(RetentionPolicy.SOURCE)
    private @interface PolicyReason {}

    @VisibleForTesting protected static final int DEFAULT_USER_ACTIVITY = USER_ACTIVITY_EVENT_OTHER;
    private static final long USER_INITIATED_REVERT_THRESHOLD_MILLIS = 5000L;
    private static final long SEND_OVERRIDE_TIMEOUT_LOG_THRESHOLD_MILLIS = 1000L;
    @VisibleForTesting
    protected static final long SCREEN_POLICY_DIM_POWER_OFF_BRIGHT_THRESHOLD_MILLIS = 500L;

    @VisibleForTesting protected static final Object HANDLER_TOKEN = new Object();

    private Context mContext;
    private int mScreenOffTimeoutMs;
@@ -130,17 +173,24 @@ public class WakefulnessSessionObserver {
    protected WakefulnessSessionFrameworkStatsLogger mWakefulnessSessionFrameworkStatsLogger;
    private final Clock mClock;
    private final Object mLock = new Object();
    private final Handler mHandler;

    public WakefulnessSessionObserver(Context context, Injector injector) {
    private DisplayManagerInternal mDisplayManagerInternal;
    private int mPhysicalDisplayPortIdForDefaultDisplay;

    public WakefulnessSessionObserver(
            Context context, Injector injector) {
        if (injector == null) {
            injector = new Injector();
        }

        mContext = context;
        mDisplayManagerInternal = injector.getDisplayManagerInternal();
        mWakefulnessSessionFrameworkStatsLogger = injector
                .getWakefulnessSessionFrameworkStatsLogger();
        mClock = injector.getClock();
        updateSettingScreenOffTimeout(context);
        mHandler = injector.getHandler();
        updateSettingScreenOffTimeout(mContext);

        try {
            final UserSwitchObserver observer = new UserSwitchObserver();
@@ -164,6 +214,31 @@ public class WakefulnessSessionObserver {
                        },
                        UserHandle.USER_ALL);

        mPhysicalDisplayPortIdForDefaultDisplay = getPhysicalDisplayPortId(DEFAULT_DISPLAY);
        DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
        if (displayManager != null) {
            displayManager.registerDisplayListener(
                    new DisplayManager.DisplayListener() {
                        @Override
                        public void onDisplayChanged(int displayId) {
                            if (displayId == DEFAULT_DISPLAY) {
                                mPhysicalDisplayPortIdForDefaultDisplay = getPhysicalDisplayPortId(
                                        DEFAULT_DISPLAY);
                            }
                        }

                        @Override
                        public void onDisplayAdded(int i) {
                        }

                        @Override
                        public void onDisplayRemoved(int i) {
                        }
                    },
                    mHandler,
                    DisplayManager.EVENT_FLAG_DISPLAY_CHANGED);
        }

        mPowerGroups.append(
                Display.DEFAULT_DISPLAY_GROUP,
                new WakefulnessSessionPowerGroup(Display.DEFAULT_DISPLAY_GROUP));
@@ -185,6 +260,20 @@ public class WakefulnessSessionObserver {
        mPowerGroups.get(powerGroupId).notifyUserActivity(eventTime, event);
    }

    /**
     * Track the screen policy
     *
     * @param eventTime policy changing time, in uptime millis.
     * @param powerGroupId Power Group Id for this screen policy
     * @param newPolicy Screen Policy defined in {@link DisplayPowerRequest}
     */
    public void onScreenPolicyUpdate(long eventTime, int powerGroupId, int newPolicy) {
        if (!mPowerGroups.contains(powerGroupId)) {
            mPowerGroups.append(powerGroupId, new WakefulnessSessionPowerGroup(powerGroupId));
        }
        mPowerGroups.get(powerGroupId).onScreenPolicyUpdate(eventTime, newPolicy);
    }

    /**
     * Track the system wakefulness
     *
@@ -267,6 +356,14 @@ public class WakefulnessSessionObserver {
        }
    }

    private int getPhysicalDisplayPortId(int displayId) {
        if (mDisplayManagerInternal == null) {
            return -1;
        }
        DisplayInfo display = mDisplayManagerInternal.getDisplayInfo(displayId);
        return ((DisplayAddress.Physical) display.address).getPort();
    }

    private int getScreenOffTimeout() {
        synchronized (mLock) {
            return mScreenOffTimeoutMs;
@@ -277,10 +374,9 @@ public class WakefulnessSessionObserver {
    @VisibleForTesting
    protected class WakefulnessSessionPowerGroup {
        private static final long TIMEOUT_OFF_RESET_TIMESTAMP = -1;

        private int mPowerGroupId;
        private int mCurrentWakefulness;
        private boolean mIsInteractive = false;
        @VisibleForTesting protected boolean mIsInteractive = false;
        // state on start timestamp: will be used in state off to calculate the duration of state on
        private long mInteractiveStateOnStartTimestamp;
        @VisibleForTesting
@@ -295,6 +391,17 @@ public class WakefulnessSessionObserver {
        private int mTimeoutOverrideWakeLockCounter = 0;
        // The timestamp when Override Timeout is set to false
        private @ScreenTimeoutOverridePolicy.ReleaseReason int mTimeoutOverrideReleaseReason;
        // The timestamp when current screen policy is set
        private long mCurrentScreenPolicyTimestamp;
        // current screen policy
        private int mCurrentScreenPolicy;
        // The screen policy before the current one
        private int mPrevScreenPolicy;
        // The previous screen policy duration
        private int mPrevScreenPolicyDurationMs;
        // The past dim duration
        @VisibleForTesting protected int mPastDimDurationMs;
        private long mInteractiveOffTimestamp;
        // The timestamp when state off by timeout occurs
        // will set TIMEOUT_OFF_RESET_TIMESTAMP if state on or state off by power button
        private long mTimeoutOffTimestamp;
@@ -307,6 +414,10 @@ public class WakefulnessSessionObserver {
            mPrevUserActivityEvent = DEFAULT_USER_ACTIVITY;
            mPrevUserActivityTimestamp = -1;
            mPowerGroupId = powerGroupId;
            mCurrentScreenPolicy = mPrevScreenPolicy = POLICY_BRIGHT;
            mCurrentScreenPolicyTimestamp = 0;
            mPrevScreenPolicyDurationMs = 0;
            mPastDimDurationMs = 0;
        }

        public void notifyUserActivity(long eventTime, @PowerManager.UserActivityEvent int event) {
@@ -320,6 +431,21 @@ public class WakefulnessSessionObserver {
            mCurrentUserActivityTimestamp = eventTime;
        }

        public void onScreenPolicyUpdate(long eventTime, int newPolicy) {
            if (newPolicy == mCurrentScreenPolicy) {
                return;
            }

            if (newPolicy == POLICY_BRIGHT) {
                checkAndLogDimIfQualified(POLICY_REASON_BRIGHT_UNDIM, eventTime);
            }

            mPrevScreenPolicy = mCurrentScreenPolicy;
            mCurrentScreenPolicy = newPolicy;
            mPrevScreenPolicyDurationMs = (int) (eventTime - mCurrentScreenPolicyTimestamp);
            mCurrentScreenPolicyTimestamp = eventTime;
        }

        public void onWakefulnessChangeStarted(int wakefulness, int changeReason, long eventTime) {
            mCurrentWakefulness = wakefulness;
            if (mIsInteractive == isInteractive(wakefulness)) {
@@ -331,10 +457,10 @@ public class WakefulnessSessionObserver {
                mInteractiveStateOnStartTimestamp = eventTime;

                // Log the outcome of screen timeout override (USER INITIATED REVERT),
                // when user initiates to revert the screen off state in a short period.
                // when user initiates to revert the off state in a short period.
                if (mTimeoutOffTimestamp != TIMEOUT_OFF_RESET_TIMESTAMP) {
                    long offToOnDurationMs = eventTime - mTimeoutOffTimestamp;
                    if (offToOnDurationMs < TIMEOUT_USER_INITIATED_REVERT_THRESHOLD_MILLIS) {
                    long timeoutOffToOnDurationMs = eventTime - mTimeoutOffTimestamp;
                    if (timeoutOffToOnDurationMs < USER_INITIATED_REVERT_THRESHOLD_MILLIS) {
                        mWakefulnessSessionFrameworkStatsLogger.logTimeoutOverrideEvent(
                                mPowerGroupId,
                                OVERRIDE_OUTCOME_TIMEOUT_USER_INITIATED_REVERT,
@@ -344,11 +470,15 @@ public class WakefulnessSessionObserver {
                    }
                    mTimeoutOffTimestamp = TIMEOUT_OFF_RESET_TIMESTAMP;
                }

                checkAndLogDimIfQualified(POLICY_REASON_BRIGHT_INITIATED_REVERT, eventTime);

            } else {
                int lastUserActivity = mCurrentUserActivityEvent;
                long lastUserActivityDurationMs = eventTime - mCurrentUserActivityTimestamp;
                @OffReason int interactiveStateOffReason = OFF_REASON_UNKNOWN;
                int reducedInteractiveStateOnDurationMs = 0;
                mInteractiveOffTimestamp = eventTime;

                if (changeReason == PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON) {
                    interactiveStateOffReason = OFF_REASON_POWER_BUTTON;
@@ -369,6 +499,9 @@ public class WakefulnessSessionObserver {
                        mSendOverrideTimeoutLogTimestamp = eventTime;
                        mTimeoutOverrideReleaseReason = RELEASE_REASON_UNKNOWN; // reset the reason
                    }

                    checkAndLogDimIfQualified(POLICY_REASON_OFF_POWER_BUTTON, eventTime);

                } else if (changeReason == PowerManager.GO_TO_SLEEP_REASON_TIMEOUT) {
                    // Interactive Off reason is timeout
                    interactiveStateOffReason = OFF_REASON_TIMEOUT;
@@ -393,6 +526,8 @@ public class WakefulnessSessionObserver {
                        // state instantly
                        mTimeoutOffTimestamp = eventTime;
                    }

                    checkAndLogDimIfQualified(POLICY_REASON_OFF_TIMEOUT, eventTime);
                }

                long interactiveStateOnDurationMs =
@@ -462,6 +597,106 @@ public class WakefulnessSessionObserver {
            }
        }

        private void checkAndLogDimIfQualified(
                @PolicyReason int reasonToBeChecked, long eventTime) {
            // Only log dim event when DEFAULT_DISPLAY
            if (mPowerGroupId != DEFAULT_DISPLAY) {
                return;
            }

            int dimDurationMs = 0;
            int lastUserActivity = mCurrentUserActivityEvent;
            int lastUserActivityDurationMs = (int) (eventTime - mCurrentUserActivityTimestamp);
            switch (reasonToBeChecked) {
                case POLICY_REASON_OFF_TIMEOUT: {
                    // The policy ordering:
                    // (1) --DIM--OFF/DOZE->| or (2) --DIM->| because OFF/DOZE hasn't been updated.
                    dimDurationMs = (int) (eventTime - mCurrentScreenPolicyTimestamp); //(1)--DIM->|
                    if (mPrevScreenPolicy == POLICY_DIM) {  // for (2) --DIM--OFF/DOZE->|
                        dimDurationMs = mPrevScreenPolicyDurationMs;
                    }
                    mWakefulnessSessionFrameworkStatsLogger.logDimEvent(
                            mPhysicalDisplayPortIdForDefaultDisplay,
                            reasonToBeChecked,
                            lastUserActivity,
                            lastUserActivityDurationMs,
                            dimDurationMs,
                            mScreenOffTimeoutMs);
                    mPastDimDurationMs = dimDurationMs;
                    return;
                }
                case POLICY_REASON_OFF_POWER_BUTTON: {
                    // Power Off will be triggered by USER_ACTIVITY_EVENT_BUTTON
                    // The metric wants to record the previous activity before EVENT_BUTTON
                    lastUserActivity = mPrevUserActivityEvent;
                    lastUserActivityDurationMs = (int) (eventTime - mPrevUserActivityTimestamp);
                    // the policy ordering:
                    // (1) ---BRIGHT->| or (2) ---DIM->| because OFF/DOZE hasn't been updated
                    dimDurationMs = 0; // for (1) ---BRIGHT->| which doesn't have dim (no need log)
                    if (mCurrentScreenPolicy == POLICY_DIM) { // for (2) ---DIM->|
                        dimDurationMs = (int) (eventTime - mCurrentScreenPolicyTimestamp);
                        mWakefulnessSessionFrameworkStatsLogger.logDimEvent(
                                mPhysicalDisplayPortIdForDefaultDisplay,
                                reasonToBeChecked,
                                lastUserActivity,
                                lastUserActivityDurationMs,
                                dimDurationMs,
                                mScreenOffTimeoutMs);
                        mHandler.removeCallbacksAndMessages(HANDLER_TOKEN);
                    }

                    mPastDimDurationMs = dimDurationMs;
                    return;
                }
                case POLICY_REASON_BRIGHT_UNDIM: {
                    // Has checked the latest screen policy is POLICY_BRIGHT in onScreenPolicyUpdate
                    if (mCurrentScreenPolicy == POLICY_DIM) { // policy ordering: --DIM--BRIGHT->|
                        int savedDimDurationMs = (int) (eventTime - mCurrentScreenPolicyTimestamp);
                        int savedLastUserActivity = lastUserActivity;
                        int savedLastUserActivityDurationMs = lastUserActivityDurationMs;

                        // For the undim case --DIM--BRIGHT->|, it needs wait 500 ms to
                        // differentiate between "power button off" case, which is
                        // --DIM--BRIGHT(<500ms)--OFF/DOZE->|
                        // [Method] Wait 500 ms to see whether triggers power button off or not.
                        // [Reason] We got --DIM--BRIGHT->|. However, if BRIGHT is so short (<500ms)
                        //          and follows OFF/DOZE, it represents power button off, not undim.
                        //          It is normal to have a short BRIGHT for power button off because
                        //          the system need to play an animation before off.
                        mHandler.postDelayed(() -> {
                            mWakefulnessSessionFrameworkStatsLogger.logDimEvent(
                                    mPhysicalDisplayPortIdForDefaultDisplay,
                                    reasonToBeChecked,
                                    savedLastUserActivity,
                                    savedLastUserActivityDurationMs,
                                    savedDimDurationMs,
                                    mScreenOffTimeoutMs);
                            mPastDimDurationMs = savedDimDurationMs;
                        }, HANDLER_TOKEN, SCREEN_POLICY_DIM_POWER_OFF_BRIGHT_THRESHOLD_MILLIS);
                    }
                    return;
                }
                case POLICY_REASON_BRIGHT_INITIATED_REVERT: {
                    // the dimDuration in BRIGHT_INITIATE_REVERT is for the dim duration before
                    // screen interactive off (mPastDimDurationMs)
                    long offToOnDurationMs = eventTime - mInteractiveOffTimestamp;
                    if (mPastDimDurationMs > 0
                            && offToOnDurationMs < USER_INITIATED_REVERT_THRESHOLD_MILLIS) {
                        mWakefulnessSessionFrameworkStatsLogger.logDimEvent(
                                mPhysicalDisplayPortIdForDefaultDisplay,
                                reasonToBeChecked,
                                lastUserActivity,
                                lastUserActivityDurationMs,
                                mPastDimDurationMs,
                                mScreenOffTimeoutMs);
                    }
                    return;
                }
                default:
                    return;
            }
        }

        void dump(IndentingPrintWriter writer) {
            final long now = mClock.uptimeMillis();

@@ -475,6 +710,12 @@ public class WakefulnessSessionObserver {
            final long prevUserActivityDurationMs = now - mPrevUserActivityTimestamp;
            writer.println("previous user activity duration: " + prevUserActivityDurationMs);
            writer.println("is in override timeout: " + isInOverrideTimeout());
            writer.println("mIsInteractive: " + mIsInteractive);
            writer.println("current screen policy: " + mCurrentScreenPolicy);
            final long currentScreenPolicyDurationMs = now - mCurrentScreenPolicyTimestamp;
            writer.println("current screen policy duration: " + currentScreenPolicyDurationMs);
            writer.println("previous screen policy: " + mPrevScreenPolicy);
            writer.println("past screen policy duration: " + mPrevScreenPolicyDurationMs);
            writer.decreaseIndent();
        }
    }
@@ -512,6 +753,24 @@ public class WakefulnessSessionObserver {
                    (long) defaultTimeoutMs);
        }

        public void logDimEvent(
                int physicalDisplayPortId,
                @PolicyReason int policyReason,
                @PowerManager.UserActivityEvent int userActivityEvent,
                int lastUserActivityEventDurationMs,
                int dimDurationMs,
                int defaultTimeoutMs) {
            int logUserActivityEvent = convertToLogUserActivityEvent(userActivityEvent);
            FrameworkStatsLog.write(
                    FrameworkStatsLog.SCREEN_DIM_REPORTED,
                    physicalDisplayPortId,
                    policyReason,
                    logUserActivityEvent,
                    lastUserActivityEventDurationMs,
                    dimDurationMs,
                    defaultTimeoutMs);
        }

        private static final int USER_ACTIVITY_OTHER = FrameworkStatsLog
                .SCREEN_INTERACTIVE_SESSION_REPORTED__LAST_USER_ACTIVITY_EVENT__OTHER;

@@ -591,5 +850,13 @@ public class WakefulnessSessionObserver {
        Clock getClock() {
            return SystemClock::uptimeMillis;
        }

        Handler getHandler() {
            return BackgroundThread.getHandler();
        }

        DisplayManagerInternal getDisplayManagerInternal() {
            return LocalServices.getService(DisplayManagerInternal.class);
        }
    }
}