OSDN Git Service

Allow exemption to idle apps at periodic intervals
authorAmith Yamasani <yamasani@google.com>
Fri, 8 May 2015 23:36:21 +0000 (16:36 -0700)
committerAmith Yamasani <yamasani@google.com>
Tue, 12 May 2015 23:08:50 +0000 (16:08 -0700)
Triggers are device idle mode changing as well as
internal delayed message handlers.

Bug: 20066058
Change-Id: I0627cfbcc16cfc2b8ac7d298fd2c681a5a6571dd

services/core/java/com/android/server/DeviceIdleController.java
services/usage/java/com/android/server/usage/UsageStatsService.java
services/usage/java/com/android/server/usage/UserUsageStatsService.java

index 0b33812..5dbf4c9 100644 (file)
@@ -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;
index 197daed..d18f7c2 100644 (file)
@@ -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;
index b638c8e..b7e1c22 100644 (file)
@@ -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);