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

Commit 17ddfd5f authored by Amith Yamasani's avatar Amith Yamasani Committed by Android Git Automerger
Browse files

am 3fec5277: Merge "Allow exemption to idle apps at periodic intervals" into mnc-dev

* commit '3fec5277':
  Allow exemption to idle apps at periodic intervals
parents 24fc09ac 3fec5277
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);