diff options
author | Amith Yamasani <yamasani@google.com> | 2015-05-08 16:36:21 -0700 |
---|---|---|
committer | Amith Yamasani <yamasani@google.com> | 2015-05-12 16:08:50 -0700 |
commit | 520d8f2ac6ad2c3cd244e1f710103b3a43a41725 (patch) | |
tree | 2ac6a93d9c759a11e166a940835f00a430976338 /services/usage | |
parent | bb9d9278aa6fe3ba3d4c21b03d3e3da4543a974c (diff) | |
download | frameworks_base-520d8f2ac6ad2c3cd244e1f710103b3a43a41725.zip frameworks_base-520d8f2ac6ad2c3cd244e1f710103b3a43a41725.tar.gz frameworks_base-520d8f2ac6ad2c3cd244e1f710103b3a43a41725.tar.bz2 |
Allow exemption to idle apps at periodic intervals
Triggers are device idle mode changing as well as
internal delayed message handlers.
Bug: 20066058
Change-Id: I0627cfbcc16cfc2b8ac7d298fd2c681a5a6571dd
Diffstat (limited to 'services/usage')
-rw-r--r-- | services/usage/java/com/android/server/usage/UsageStatsService.java | 108 | ||||
-rw-r--r-- | services/usage/java/com/android/server/usage/UserUsageStatsService.java | 17 |
2 files changed, 115 insertions, 10 deletions
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 197daed..d18f7c2 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -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; diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index b638c8e..b7e1c22 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -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); |