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

Commit 3fec5277 authored by Amith Yamasani's avatar Amith Yamasani Committed by Android (Google) Code Review
Browse files

Merge "Allow exemption to idle apps at periodic intervals" into mnc-dev

parents 5401b4cd 520d8f2a
Loading
Loading
Loading
Loading
+17 −6
Original line number Diff line number Diff line
@@ -45,17 +45,20 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.TimeUtils;
import android.util.Xml;
import android.view.Display;

import com.android.internal.app.IBatteryStats;
import com.android.internal.os.AtomicFile;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
import com.android.server.am.BatteryStatsService;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -75,6 +78,8 @@ import java.io.PrintWriter;
public class DeviceIdleController extends SystemService {
    private static final String TAG = "DeviceIdleController";

    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

    public static final String SERVICE_NAME = "deviceidle";

    private static final String ACTION_STEP_IDLE_STATE =
@@ -88,17 +93,20 @@ public class DeviceIdleController extends SystemService {
     * immediately after going inactive just because we don't want to be continually running
     * the significant motion sensor whenever the screen is off.
     */
    private static final long DEFAULT_INACTIVE_TIMEOUT = 30*60*1000L;
    private static final long DEFAULT_INACTIVE_TIMEOUT = !DEBUG ? 30*60*1000L
            : 2 * 60 * 1000L;
    /**
     * This is the time, after seeing motion, that we wait after becoming inactive from
     * that until we start looking for motion again.
     */
    private static final long DEFAULT_MOTION_INACTIVE_TIMEOUT = 10*60*1000L;
    private static final long DEFAULT_MOTION_INACTIVE_TIMEOUT = !DEBUG ? 10*60*1000L
            : 60 * 1000L;
    /**
     * This is the time, after the inactive timeout elapses, that we will wait looking
     * for significant motion until we truly consider the device to be idle.
     */
    private static final long DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT = 30*60*1000L;
    private static final long DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT = !DEBUG ? 30*60*1000L
            : 2 * 60 * 1000L;
    /**
     * This is the initial time, after being idle, that we will allow ourself to be back
     * in the IDLE_PENDING state allowing the system to run normally until we return to idle.
@@ -117,11 +125,13 @@ public class DeviceIdleController extends SystemService {
     * This is the initial time that we want to sit in the idle state before waking up
     * again to return to pending idle and allowing normal work to run.
     */
    private static final long DEFAULT_IDLE_TIMEOUT = 60*60*1000L;
    private static final long DEFAULT_IDLE_TIMEOUT = !DEBUG ? 60*60*1000L
            : 5 * 60 * 1000L;
    /**
     * Maximum idle duration we will be allowed to use.
     */
    private static final long DEFAULT_MAX_IDLE_TIMEOUT = 6*60*60*1000L;
    private static final long DEFAULT_MAX_IDLE_TIMEOUT = !DEBUG ? 6*60*60*1000L
            : 10 * 60 * 1000L;
    /**
     * Scaling factor to apply to current idle timeout each time we cycle through that state.
     */
@@ -130,7 +140,8 @@ public class DeviceIdleController extends SystemService {
     * This is the minimum time we will allow until the next upcoming alarm for us to
     * actually go in to idle mode.
     */
    private static final long DEFAULT_MIN_TIME_TO_ALARM = 60*60*1000L;
    private static final long DEFAULT_MIN_TIME_TO_ALARM = !DEBUG ? 60*60*1000L
            : 5 * 60 * 1000L;

    private AlarmManager mAlarmManager;
    private IBatteryStats mBatteryStats;
+103 −5
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ import android.os.Handler;
import android.os.IDeviceIdleController;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -58,6 +59,7 @@ import android.os.UserManager;
import android.provider.Settings;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
@@ -87,13 +89,21 @@ public class UsageStatsService extends SystemService implements

    static final String TAG = "UsageStatsService";

    static final boolean DEBUG = false;
    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    private static final long TEN_SECONDS = 10 * 1000;
    private static final long ONE_MINUTE = 60 * 1000;
    private static final long TWENTY_MINUTES = 20 * 60 * 1000;
    private static final long FLUSH_INTERVAL = DEBUG ? TEN_SECONDS : TWENTY_MINUTES;
    private static final long TIME_CHANGE_THRESHOLD_MILLIS = 2 * 1000; // Two seconds.
    static final long DEFAULT_APP_IDLE_THRESHOLD_MILLIS = 2L * 24 * 60 * 60 * 1000; // 1 day
    static final long DEFAULT_CHECK_IDLE_INTERVAL = 8 * 3600 * 1000; // 8 hours

    static final long DEFAULT_APP_IDLE_THRESHOLD_MILLIS = DEBUG ? ONE_MINUTE * 4 
            : 1L * 24 * 60 * 60 * 1000; // 1 day
    static final long DEFAULT_CHECK_IDLE_INTERVAL = DEBUG ? ONE_MINUTE
            : 8 * 3600 * 1000; // 8 hours
    static final long DEFAULT_PAROLE_INTERVAL = DEBUG ? ONE_MINUTE * 10
            : 24 * 60 * 60 * 1000L; // 24 hours between paroles
    static final long DEFAULT_PAROLE_DURATION = DEBUG ? ONE_MINUTE * 2
            : 10 * 60 * 1000L; // 10 minutes

    // Handler message types.
    static final int MSG_REPORT_EVENT = 0;
@@ -102,6 +112,8 @@ public class UsageStatsService extends SystemService implements
    static final int MSG_INFORM_LISTENERS = 3;
    static final int MSG_FORCE_IDLE_STATE = 4;
    static final int MSG_CHECK_IDLE_STATES = 5;
    static final int MSG_CHECK_PAROLE_TIMEOUT = 6;
    static final int MSG_PAROLE_END_TIMEOUT = 7;

    private final Object mLock = new Object();
    Handler mHandler;
@@ -110,14 +122,16 @@ public class UsageStatsService extends SystemService implements
    AppWidgetManager mAppWidgetManager;
    IDeviceIdleController mDeviceIdleController;
    private DisplayManager mDisplayManager;
    private PowerManager mPowerManager;

    private final SparseArray<UserUsageStatsService> mUserState = new SparseArray<>();
    private File mUsageStatsDir;
    long mRealTimeSnapshot;
    long mSystemTimeSnapshot;

    boolean mAppIdleParoled;
    private boolean mScreenOn;

    private long mLastAppIdleParoledTime;
    long mAppIdleDurationMillis;
    long mCheckIdleIntervalMillis = DEFAULT_CHECK_IDLE_INTERVAL;
    long mScreenOnTime;
@@ -152,6 +166,7 @@ public class UsageStatsService extends SystemService implements

        IntentFilter deviceStates = new IntentFilter(BatteryManager.ACTION_CHARGING);
        deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
        deviceStates.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
        getContext().registerReceiver(new DeviceStateReceiver(), deviceStates);
        synchronized (mLock) {
            cleanUpRemovedUsersLocked();
@@ -179,6 +194,8 @@ public class UsageStatsService extends SystemService implements
                    ServiceManager.getService(DeviceIdleController.SERVICE_NAME));
            mDisplayManager = (DisplayManager) getContext().getSystemService(
                    Context.DISPLAY_SERVICE);
            mPowerManager = getContext().getSystemService(PowerManager.class);

            mScreenOnSystemTimeSnapshot = System.currentTimeMillis();
            synchronized (this) {
                mScreenOnTime = readScreenOnTimeLocked();
@@ -216,6 +233,8 @@ public class UsageStatsService extends SystemService implements
            if (BatteryManager.ACTION_CHARGING.equals(action)
                    || BatteryManager.ACTION_DISCHARGING.equals(action)) {
                setAppIdleParoled(BatteryManager.ACTION_CHARGING.equals(action));
            } else if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
                onDeviceIdleModeChanged();
            }
        }
    }
@@ -270,15 +289,41 @@ public class UsageStatsService extends SystemService implements
        }
    }

    /** Paroled here means temporary pardon from being inactive */
    void setAppIdleParoled(boolean paroled) {
        synchronized (mLock) {
            if (mAppIdleParoled != paroled) {
                mAppIdleParoled = paroled;
                if (DEBUG) Slog.d(TAG, "Changing paroled to " + mAppIdleParoled);
                if (paroled) {
                    mLastAppIdleParoledTime = checkAndGetTimeLocked();
                    postNextParoleTimeout();
                }
                postCheckIdleStates();
            }
        }
    }

    private void postNextParoleTimeout() {
        if (DEBUG) Slog.d(TAG, "Posting MSG_CHECK_PAROLE_TIMEOUT");
        mHandler.removeMessages(MSG_CHECK_PAROLE_TIMEOUT);
        // Compute when the next parole needs to happen. We check more frequently than necessary
        // since the message handler delays are based on elapsedRealTime and not wallclock time.
        // The comparison is done in wallclock time.
        long timeLeft = (mLastAppIdleParoledTime + DEFAULT_PAROLE_INTERVAL)
                - checkAndGetTimeLocked();
        if (timeLeft < 0) {
            timeLeft = 0;
        }
        mHandler.sendEmptyMessageDelayed(MSG_CHECK_PAROLE_TIMEOUT, timeLeft / 10);
    }

    private void postParoleEndTimeout() {
        if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_END_TIMEOUT");
        mHandler.removeMessages(MSG_PAROLE_END_TIMEOUT);
        mHandler.sendEmptyMessageDelayed(MSG_PAROLE_END_TIMEOUT, DEFAULT_PAROLE_DURATION);
    }

    void postCheckIdleStates() {
        mHandler.removeMessages(MSG_CHECK_IDLE_STATES);
        mHandler.sendEmptyMessage(MSG_CHECK_IDLE_STATES);
@@ -313,6 +358,24 @@ public class UsageStatsService extends SystemService implements
        mHandler.sendEmptyMessageDelayed(MSG_CHECK_IDLE_STATES, mCheckIdleIntervalMillis);
    }

    /** Check if it's been a while since last parole and let idle apps do some work */
    void checkParoleTimeout() {
        synchronized (mLock) {
            if (!mAppIdleParoled) {
                final long timeSinceLastParole = checkAndGetTimeLocked() - mLastAppIdleParoledTime;
                if (timeSinceLastParole > DEFAULT_PAROLE_INTERVAL) {
                    if (DEBUG) Slog.d(TAG, "Crossed default parole interval");
                    setAppIdleParoled(true);
                    // Make sure it ends at some point
                    postParoleEndTimeout();
                } else {
                    if (DEBUG) Slog.d(TAG, "Not long enough to go to parole");
                    postNextParoleTimeout();
                }
            }
        }
    }

    void updateDisplayLocked() {
        boolean screenOn = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).getState()
                != Display.STATE_OFF;
@@ -368,6 +431,23 @@ public class UsageStatsService extends SystemService implements
        }
    }

    void onDeviceIdleModeChanged() {
        final boolean deviceIdle = mPowerManager.isDeviceIdleMode();
        if (DEBUG) Slog.i(TAG, "DeviceIdleMode changed to " + deviceIdle);
        synchronized (mLock) {
            final long timeSinceLastParole = checkAndGetTimeLocked() - mLastAppIdleParoledTime;
            if (!deviceIdle
                    && timeSinceLastParole >= DEFAULT_PAROLE_INTERVAL) {
                if (DEBUG) Slog.i(TAG, "Bringing idle apps out of inactive state due to deviceIdleMode=false");
                postNextParoleTimeout();
                setAppIdleParoled(true);
            } else if (deviceIdle) {
                if (DEBUG) Slog.i(TAG, "Device idle, back to prison");
                setAppIdleParoled(false);
            }
        }
    }

    private static void deleteRecursively(File f) {
        File[] files = f.listFiles();
        if (files != null) {
@@ -400,12 +480,20 @@ 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) {
            // 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;
            }
            final int userCount = mUserState.size();
            for (int i = 0; i < userCount; i++) {
                final UserUsageStatsService service = mUserState.valueAt(i);
                service.onTimeChanged(expectedSystemTime, actualSystemTime);
                service.onTimeChanged(expectedSystemTime, actualSystemTime, resetBeginIdleTime);
            }
            mRealTimeSnapshot = actualRealtime;
            mSystemTimeSnapshot = actualSystemTime;
@@ -718,6 +806,16 @@ public class UsageStatsService extends SystemService implements
                case MSG_CHECK_IDLE_STATES:
                    checkIdleStates();
                    break;

                case MSG_CHECK_PAROLE_TIMEOUT:
                    checkParoleTimeout();
                    break;

                case MSG_PAROLE_END_TIMEOUT:
                    if (DEBUG) Slog.d(TAG, "Ending parole");
                    setAppIdleParoled(false);
                    break;

                default:
                    super.handleMessage(msg);
                    break;
+12 −5
Original line number Diff line number Diff line
@@ -104,7 +104,7 @@ class UserUsageStatsService {

            // By calling loadActiveStats, we will
            // generate new stats for each bucket.
            loadActiveStats(currentTimeMillis, false);
            loadActiveStats(currentTimeMillis,/*force=*/ false, /*resetBeginIdleTime=*/ false);
        } else {
            // Set up the expiry date to be one day from the latest daily stat.
            // This may actually be today and we will rollover on the first event
@@ -167,10 +167,10 @@ class UserUsageStatsService {
        persistActiveStats();
    }

    void onTimeChanged(long oldTime, long newTime) {
    void onTimeChanged(long oldTime, long newTime, boolean resetBeginIdleTime) {
        persistActiveStats();
        mDatabase.onTimeChanged(newTime - oldTime);
        loadActiveStats(newTime, true);
        loadActiveStats(newTime, /* force= */ true, resetBeginIdleTime);
    }

    void reportEvent(UsageEvents.Event event, long deviceUsageTime) {
@@ -431,7 +431,7 @@ class UserUsageStatsService {

        persistActiveStats();
        mDatabase.prune(currentTimeMillis);
        loadActiveStats(currentTimeMillis, false);
        loadActiveStats(currentTimeMillis, /*force=*/ false, /*resetBeginIdleTime=*/ false);

        final int continueCount = continuePreviousDay.size();
        for (int i = 0; i < continueCount; i++) {
@@ -460,7 +460,8 @@ class UserUsageStatsService {
    /**
     * @param force To force all in-memory stats to be reloaded.
     */
    private void loadActiveStats(final long currentTimeMillis, boolean force) {
    private void loadActiveStats(final long currentTimeMillis, boolean force,
            boolean resetBeginIdleTime) {
        final UnixCalendar tempCal = mDailyExpiryDate;
        for (int intervalType = 0; intervalType < mCurrentStats.length; intervalType++) {
            tempCal.setTimeInMillis(currentTimeMillis);
@@ -496,6 +497,12 @@ class UserUsageStatsService {
                mCurrentStats[intervalType].beginTime = tempCal.getTimeInMillis();
                mCurrentStats[intervalType].endTime = currentTimeMillis;
            }

            if (resetBeginIdleTime) {
                for (UsageStats usageStats : mCurrentStats[intervalType].packageStats.values()) {
                    usageStats.mBeginIdleTime = 0;
                }
            }
        }
        mStatsChanged = false;
        mDailyExpiryDate.setTimeInMillis(currentTimeMillis);