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

Commit b8bd3024 authored by Adam Lesinski's avatar Adam Lesinski Committed by The Android Automerger
Browse files

Don't change screen on time on time changes

Screen on time should be measured in elapsed realtime, not wallclock.

Cause a checkIdleStates to occur when reloading stats
(on rollover and on time change).

When time changes occur in the negative direction, the new stats file
we create can overlap the previous one with regards to its end timestamp.
Use the begin timestamp to determine which of the latest stats to merge.
(b/22716352)

Bug: 26488100
Change-Id: If31b29bbbee9e98401205b5e26bce86e181286e7
parent 78ba120c
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -165,14 +165,18 @@ public final class UsageStats implements Parcelable {
                    mPackageName + "' with UsageStats for package '" + right.mPackageName + "'.");
        }

        if (right.mEndTimeStamp > mEndTimeStamp) {
        if (right.mBeginTimeStamp > mBeginTimeStamp) {
            // The incoming UsageStat begins after this one, so use its last time used fields
            // as the source of truth.
            // We use the mBeginTimeStamp due to a bug where UsageStats files can overlap with
            // regards to their mEndTimeStamp.
            mLastEvent = right.mLastEvent;
            mEndTimeStamp = right.mEndTimeStamp;
            mLastTimeUsed = right.mLastTimeUsed;
            mBeginIdleTime = right.mBeginIdleTime;
            mLastTimeSystemUsed = right.mLastTimeSystemUsed;
        }
        mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp);
        mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp);
        mTotalTimeInForeground += right.mTotalTimeInForeground;
        mLaunchCount += right.mLaunchCount;
    }
+52 −39
Original line number Diff line number Diff line
@@ -123,6 +123,7 @@ public class UsageStatsService extends SystemService implements
    static final int MSG_PAROLE_END_TIMEOUT = 7;
    static final int MSG_REPORT_CONTENT_PROVIDER_USAGE = 8;
    static final int MSG_PAROLE_STATE_CHANGED = 9;
    static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10;

    private final Object mLock = new Object();
    Handler mHandler;
@@ -145,7 +146,7 @@ public class UsageStatsService extends SystemService implements
    private long mLastAppIdleParoledTime;

    long mScreenOnTime;
    long mScreenOnSystemTimeSnapshot;
    long mLastScreenOnEventRealtime;

    @GuardedBy("mLock")
    private AppIdleHistory mAppIdleHistory = new AppIdleHistory();
@@ -188,6 +189,8 @@ public class UsageStatsService extends SystemService implements

        synchronized (mLock) {
            cleanUpRemovedUsersLocked();
            mLastScreenOnEventRealtime = SystemClock.elapsedRealtime();
            mScreenOnTime = readScreenOnTimeLocked();
        }

        mRealTimeSnapshot = SystemClock.elapsedRealtime();
@@ -214,10 +217,6 @@ public class UsageStatsService extends SystemService implements
                    Context.DISPLAY_SERVICE);
            mPowerManager = getContext().getSystemService(PowerManager.class);

            mScreenOnSystemTimeSnapshot = System.currentTimeMillis();
            synchronized (this) {
                mScreenOnTime = readScreenOnTimeLocked();
            }
            mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
            synchronized (this) {
                updateDisplayLocked();
@@ -280,6 +279,11 @@ public class UsageStatsService extends SystemService implements
        mHandler.sendEmptyMessageDelayed(MSG_FLUSH_TO_DISK, FLUSH_INTERVAL);
    }

    @Override
    public void onStatsReloaded() {
        postOneTimeCheckIdleStates();
    }

    @Override
    public long getAppIdleRollingWindowDurationMillis() {
        return mAppIdleWallclockThresholdMillis * 2;
@@ -359,6 +363,14 @@ public class UsageStatsService extends SystemService implements
        mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
    }

    /**
     * We send a different message to check idle states once, otherwise we would end up
     * scheduling a series of repeating checkIdleStates each time we fired off one.
     */
    void postOneTimeCheckIdleStates() {
        mHandler.sendEmptyMessage(MSG_ONE_TIME_CHECK_IDLE_STATES);
    }

    /** Check all running users' or specified user's apps to see if they enter an idle state. */
    void checkIdleStates(int checkUserId) {
        if (!mAppIdleEnabled) {
@@ -385,7 +397,7 @@ public class UsageStatsService extends SystemService implements
                            userId);
            synchronized (mLock) {
                final long timeNow = checkAndGetTimeLocked();
                final long screenOnTime = getScreenOnTimeLocked(timeNow);
                final long screenOnTime = getScreenOnTimeLocked();
                UserUsageStatsService service = getUserDataAndInitializeIfNeededLocked(userId,
                        timeNow);
                final int packageCount = packages.size();
@@ -401,8 +413,6 @@ public class UsageStatsService extends SystemService implements
                }
            }
        }
        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, checkUserId, 0),
                mCheckIdleIntervalMillis);
    }

    /** Check if it's been a while since last parole and let idle apps do some work */
@@ -442,21 +452,21 @@ public class UsageStatsService extends SystemService implements
        if (screenOn == mScreenOn) return;

        mScreenOn = screenOn;
        long now = System.currentTimeMillis();
        long now = SystemClock.elapsedRealtime();
        if (mScreenOn) {
            mScreenOnSystemTimeSnapshot = now;
            mLastScreenOnEventRealtime = now;
        } else {
            mScreenOnTime += now - mScreenOnSystemTimeSnapshot;
            mScreenOnTime += now - mLastScreenOnEventRealtime;
            writeScreenOnTimeLocked(mScreenOnTime);
        }
    }

    private long getScreenOnTimeLocked(long now) {
    long getScreenOnTimeLocked() {
        long screenOnTime = mScreenOnTime;
        if (mScreenOn) {
            return now - mScreenOnSystemTimeSnapshot + mScreenOnTime;
        } else {
            return mScreenOnTime;
            screenOnTime += SystemClock.elapsedRealtime() - mLastScreenOnEventRealtime;
        }
        return screenOnTime;
    }

    private File getScreenOnTimeFile() {
@@ -526,7 +536,7 @@ public class UsageStatsService extends SystemService implements
        if (service == null) {
            service = new UserUsageStatsService(getContext(), userId,
                    new File(mUsageStatsDir, Integer.toString(userId)), this);
            service.init(currentTimeMillis, getScreenOnTimeLocked(currentTimeMillis));
            service.init(currentTimeMillis, getScreenOnTimeLocked());
            mUserState.put(userId, service);
        }
        return service;
@@ -539,25 +549,18 @@ public class UsageStatsService extends SystemService implements
        final long actualSystemTime = System.currentTimeMillis();
        final long actualRealtime = SystemClock.elapsedRealtime();
        final long expectedSystemTime = (actualRealtime - mRealTimeSnapshot) + mSystemTimeSnapshot;
        boolean resetBeginIdleTime = false;
        if (Math.abs(actualSystemTime - expectedSystemTime) > TIME_CHANGE_THRESHOLD_MILLIS) {
        final long diffSystemTime = actualSystemTime - expectedSystemTime;
        if (Math.abs(diffSystemTime) > TIME_CHANGE_THRESHOLD_MILLIS) {
            // The time has changed.

            // Check if it's severe enough a change to reset screenOnTime
            if (Math.abs(actualSystemTime - expectedSystemTime) > mAppIdleDurationMillis) {
                mScreenOnSystemTimeSnapshot = actualSystemTime;
                mScreenOnTime = 0;
                resetBeginIdleTime = true;
            }
            Slog.i(TAG, "Time changed in UsageStats by " + (diffSystemTime / 1000) + " seconds");
            final int userCount = mUserState.size();
            for (int i = 0; i < userCount; i++) {
                final UserUsageStatsService service = mUserState.valueAt(i);
                service.onTimeChanged(expectedSystemTime, actualSystemTime, mScreenOnTime,
                        resetBeginIdleTime);
                service.onTimeChanged(expectedSystemTime, actualSystemTime, getScreenOnTimeLocked(),
                        false);
            }
            mRealTimeSnapshot = actualRealtime;
            mSystemTimeSnapshot = actualSystemTime;
            postCheckIdleStates(UserHandle.USER_ALL);
        }
        return actualSystemTime;
    }
@@ -586,7 +589,7 @@ public class UsageStatsService extends SystemService implements
    void reportEvent(UsageEvents.Event event, int userId) {
        synchronized (mLock) {
            final long timeNow = checkAndGetTimeLocked();
            final long screenOnTime = getScreenOnTimeLocked(timeNow);
            final long screenOnTime = getScreenOnTimeLocked();
            convertToSystemTimeLocked(event);

            final UserUsageStatsService service =
@@ -602,7 +605,6 @@ public class UsageStatsService extends SystemService implements
                    || event.mEventType == Event.SYSTEM_INTERACTION
                    || event.mEventType == Event.USER_INTERACTION)) {
                if (previouslyIdle) {
                    //Slog.d(TAG, "Informing listeners of out-of-idle " + event.mPackage);
                    mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
                            /* idle = */ 0, event.mPackage));
                    notifyBatteryStats(event.mPackage, userId, false);
@@ -643,7 +645,7 @@ public class UsageStatsService extends SystemService implements
    void forceIdleState(String packageName, int userId, boolean idle) {
        synchronized (mLock) {
            final long timeNow = checkAndGetTimeLocked();
            final long screenOnTime = getScreenOnTimeLocked(timeNow);
            final long screenOnTime = getScreenOnTimeLocked();
            final long deviceUsageTime = screenOnTime - (idle ? mAppIdleDurationMillis : 0) - 5000;

            final UserUsageStatsService service =
@@ -657,7 +659,6 @@ public class UsageStatsService extends SystemService implements
                    timeNow - (idle ? mAppIdleWallclockThresholdMillis : 0) - 5000);
            // Inform listeners if necessary
            if (previouslyIdle != idle) {
                // Slog.d(TAG, "Informing listeners of out-of-idle " + packageName);
                mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
                        /* idle = */ idle ? 1 : 0, packageName));
                if (!idle) {
@@ -796,7 +797,7 @@ public class UsageStatsService extends SystemService implements
                timeNow = checkAndGetTimeLocked();
            }
            userService = getUserDataAndInitializeIfNeededLocked(userId, timeNow);
            screenOnTime = getScreenOnTimeLocked(timeNow);
            screenOnTime = getScreenOnTimeLocked();
        }
        return isAppIdleFiltered(packageName, UserHandle.getAppId(uidForAppId), userId,
                userService, timeNow, screenOnTime);
@@ -865,7 +866,7 @@ public class UsageStatsService extends SystemService implements
        synchronized (mLock) {
            timeNow = checkAndGetTimeLocked();
            userService = getUserDataAndInitializeIfNeededLocked(userId, timeNow);
            screenOnTime = getScreenOnTimeLocked(timeNow);
            screenOnTime = getScreenOnTimeLocked();
        }

        List<ApplicationInfo> apps;
@@ -987,7 +988,7 @@ public class UsageStatsService extends SystemService implements
     */
    void dump(String[] args, PrintWriter pw) {
        synchronized (mLock) {
            final long screenOnTime = getScreenOnTimeLocked(checkAndGetTimeLocked());
            final long screenOnTime = getScreenOnTimeLocked();
            IndentingPrintWriter idpw = new IndentingPrintWriter(pw, "  ");
            ArraySet<String> argSet = new ArraySet<>();
            argSet.addAll(Arrays.asList(args));
@@ -1008,7 +1009,11 @@ public class UsageStatsService extends SystemService implements
                }
                idpw.decreaseIndent();
            }
            pw.println("Screen On Timebase:" + mScreenOnTime);
            pw.print("Screen On Timebase: ");
            pw.print(screenOnTime);
            pw.print(" (");
            TimeUtils.formatDuration(screenOnTime, pw);
            pw.println(")");

            pw.println();
            pw.println("Settings:");
@@ -1042,8 +1047,8 @@ public class UsageStatsService extends SystemService implements
            pw.println();
            pw.print("mScreenOnTime="); TimeUtils.formatDuration(mScreenOnTime, pw);
            pw.println();
            pw.print("mScreenOnSystemTimeSnapshot=");
            TimeUtils.formatDuration(mScreenOnSystemTimeSnapshot, pw);
            pw.print("mLastScreenOnEventRealtime=");
            TimeUtils.formatDuration(mLastScreenOnEventRealtime, pw);
            pw.println();
        }
    }
@@ -1078,6 +1083,14 @@ public class UsageStatsService extends SystemService implements

                case MSG_CHECK_IDLE_STATES:
                    checkIdleStates(msg.arg1);
                    mHandler.sendMessageDelayed(mHandler.obtainMessage(
                            MSG_CHECK_IDLE_STATES, msg.arg1, 0),
                            mCheckIdleIntervalMillis);
                    break;

                case MSG_ONE_TIME_CHECK_IDLE_STATES:
                    mHandler.removeMessages(MSG_ONE_TIME_CHECK_IDLE_STATES);
                    checkIdleStates(UserHandle.USER_ALL);
                    break;

                case MSG_CHECK_PAROLE_TIMEOUT:
@@ -1138,7 +1151,7 @@ public class UsageStatsService extends SystemService implements
        @Override
        public void onChange(boolean selfChange) {
            updateSettings();
            postCheckIdleStates(UserHandle.USER_ALL);
            postOneTimeCheckIdleStates();
        }

        void updateSettings() {
+4 −0
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@ class UserUsageStatsService {

    interface StatsUpdatedListener {
        void onStatsUpdated();
        void onStatsReloaded();
        long getAppIdleRollingWindowDurationMillis();
    }

@@ -545,6 +546,9 @@ class UserUsageStatsService {
        Slog.i(TAG, mLogPrefix + "Rollover scheduled @ " +
                sDateFormat.format(mDailyExpiryDate.getTimeInMillis()) + "(" +
                tempCal.getTimeInMillis() + ")");

        // Tell the listener that the stats reloaded, which may have changed idle states.
        mListener.onStatsReloaded();
    }

    private static void mergePackageStats(IntervalStats dst, IntervalStats src,