Fix issue #25357209: Could not send SMS or MMS messages, had to reboot

I think what probably happened is that since we only report an app
going in to the "interaction" state as an interaction event to usage
stats, apps that sit around in that state forever will only see one
interaction at the start and never again.  So usage stats could start
thinking they are idle.

Fix this by having the activity manager report an interaction event
for such long running applications at least once a day.

Also, because it is correct and for paranoia by protected us another
way, system uids should never go in to standby.

Change-Id: I8a3805bfca86cbe78560488a649ecd07427da99a
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index 948ea1e..498ff81 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -71,10 +71,11 @@
      * Could be hours, could be days, who knows?
      *
      * @param packageName
+     * @param uidForAppId The uid of the app, which will be used for its app id
      * @param userId
      * @return
      */
-    public abstract boolean isAppIdle(String packageName, int userId);
+    public abstract boolean isAppIdle(String packageName, int uidForAppId, int userId);
 
     /**
      * Returns all of the uids for a given user where all packages associating with that uid
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e3b5651..b4d2746 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -368,6 +368,10 @@
     // we will consider it to be doing interaction for usage stats.
     static final int SERVICE_USAGE_INTERACTION_TIME = 30*60*1000;
 
+    // Maximum amount of time we will allow to elapse before re-reporting usage stats
+    // interaction with foreground processes.
+    static final long USAGE_STATS_INTERACTION_INTERVAL = 24*60*60*1000L;
+
     // Maximum number of users we allow to be running at a time.
     static final int MAX_RUNNING_USERS = 3;
 
@@ -18656,7 +18660,8 @@
         }
     }
 
-    private final boolean applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now) {
+    private final boolean applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now,
+            long nowElapsed) {
         boolean success = true;
 
         if (app.curRawAdj != app.setRawAdj) {
@@ -18760,7 +18765,7 @@
         if (app.setProcState != app.curProcState) {
             if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
                     "Proc state change of " + app.processName
-                    + " to " + app.curProcState);
+                            + " to " + app.curProcState);
             boolean setImportant = app.setProcState < ActivityManager.PROCESS_STATE_SERVICE;
             boolean curImportant = app.curProcState < ActivityManager.PROCESS_STATE_SERVICE;
             if (setImportant && !curImportant) {
@@ -18771,14 +18776,14 @@
                 BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
                 synchronized (stats) {
                     app.lastWakeTime = stats.getProcessWakeTime(app.info.uid,
-                            app.pid, SystemClock.elapsedRealtime());
+                            app.pid, nowElapsed);
                 }
                 app.lastCpuTime = app.curCpuTime;
 
             }
             // Inform UsageStats of important process state change
             // Must be called before updating setProcState
-            maybeUpdateUsageStatsLocked(app);
+            maybeUpdateUsageStatsLocked(app, nowElapsed);
 
             app.setProcState = app.curProcState;
             if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) {
@@ -18789,6 +18794,11 @@
             } else {
                 app.procStateChanged = true;
             }
+        } else if (app.reportedInteraction && (nowElapsed-app.interactionEventTime)
+                > USAGE_STATS_INTERACTION_INTERVAL) {
+            // For apps that sit around for a long time in the interactive state, we need
+            // to report this at least once a day so they don't go idle.
+            maybeUpdateUsageStatsLocked(app, nowElapsed);
         }
 
         if (changes != 0) {
@@ -18883,7 +18893,7 @@
         }
     }
 
-    private void maybeUpdateUsageStatsLocked(ProcessRecord app) {
+    private void maybeUpdateUsageStatsLocked(ProcessRecord app, long nowElapsed) {
         if (DEBUG_USAGE_STATS) {
             Slog.d(TAG, "Checking proc [" + Arrays.toString(app.getPackageList())
                     + "] state changes: old = " + app.setProcState + ", new = "
@@ -18900,19 +18910,20 @@
             isInteraction = true;
             app.fgInteractionTime = 0;
         } else if (app.curProcState <= ActivityManager.PROCESS_STATE_TOP_SLEEPING) {
-            final long now = SystemClock.elapsedRealtime();
             if (app.fgInteractionTime == 0) {
-                app.fgInteractionTime = now;
+                app.fgInteractionTime = nowElapsed;
                 isInteraction = false;
             } else {
-                isInteraction = now > app.fgInteractionTime + SERVICE_USAGE_INTERACTION_TIME;
+                isInteraction = nowElapsed > app.fgInteractionTime + SERVICE_USAGE_INTERACTION_TIME;
             }
         } else {
             isInteraction = app.curProcState
                     <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
             app.fgInteractionTime = 0;
         }
-        if (isInteraction && !app.reportedInteraction) {
+        if (isInteraction && (!app.reportedInteraction
+                || (nowElapsed-app.interactionEventTime) > USAGE_STATS_INTERACTION_INTERVAL)) {
+            app.interactionEventTime = nowElapsed;
             String[] packages = app.getPackageList();
             if (packages != null) {
                 for (int i = 0; i < packages.length; i++) {
@@ -18922,6 +18933,9 @@
             }
         }
         app.reportedInteraction = isInteraction;
+        if (!isInteraction) {
+            app.interactionEventTime = 0;
+        }
     }
 
     private final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) {
@@ -18944,7 +18958,7 @@
 
         computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now);
 
-        return applyOomAdjLocked(app, doingAll, now);
+        return applyOomAdjLocked(app, doingAll, now, SystemClock.elapsedRealtime());
     }
 
     final void updateProcessForegroundLocked(ProcessRecord proc, boolean isForeground,
@@ -19036,6 +19050,7 @@
         final ActivityRecord TOP_ACT = resumedAppLocked();
         final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
         final long now = SystemClock.uptimeMillis();
+        final long nowElapsed = SystemClock.elapsedRealtime();
         final long oldTime = now - ProcessList.MAX_EMPTY_TIME;
         final int N = mLruProcesses.size();
 
@@ -19162,7 +19177,7 @@
                     }
                 }
 
-                applyOomAdjLocked(app, true, now);
+                applyOomAdjLocked(app, true, now, nowElapsed);
 
                 // Count the number of process types.
                 switch (app.curProcState) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index bd31a21..697b4e2 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -114,6 +114,7 @@
     boolean killed;             // True once we know the process has been killed
     boolean procStateChanged;   // Keep track of whether we changed 'setAdj'.
     boolean reportedInteraction;// Whether we have told usage stats about it being an interaction
+    long interactionEventTime;  // The time we sent the last interaction event
     long fgInteractionTime;     // When we became foreground for interaction purposes
     String waitingToKill;       // Process is waiting to be killed when in the bg, and reason
     IBinder forcingToForeground;// Token that is forcing this process to be foreground
@@ -297,6 +298,10 @@
         if (reportedInteraction || fgInteractionTime != 0) {
             pw.print(prefix); pw.print("reportedInteraction=");
             pw.print(reportedInteraction);
+            if (interactionEventTime != 0) {
+                pw.print(" time=");
+                TimeUtils.formatDuration(interactionEventTime, SystemClock.elapsedRealtime(), pw);
+            }
             if (fgInteractionTime != 0) {
                 pw.print(" fgInteractionTime=");
                 TimeUtils.formatDuration(fgInteractionTime, SystemClock.elapsedRealtime(), pw);
diff --git a/services/core/java/com/android/server/content/AppIdleMonitor.java b/services/core/java/com/android/server/content/AppIdleMonitor.java
index fe5c2da..2d768d8 100644
--- a/services/core/java/com/android/server/content/AppIdleMonitor.java
+++ b/services/core/java/com/android/server/content/AppIdleMonitor.java
@@ -50,8 +50,8 @@
         }
     }
 
-    boolean isAppIdle(String packageName, int userId) {
-        return !mAppIdleParoleOn && mUsageStats.isAppIdle(packageName, userId);
+    boolean isAppIdle(String packageName, int uidForAppId, int userId) {
+        return !mAppIdleParoleOn && mUsageStats.isAppIdle(packageName, uidForAppId, userId);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index b61f90e..3ec0bee 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -2623,9 +2623,18 @@
                         continue;
                     }
                     String packageName = getPackageName(op.target);
+                    ApplicationInfo ai = null;
+                    if (packageName != null) {
+                        try {
+                            ai = mContext.getPackageManager().getApplicationInfo(packageName,
+                                    PackageManager.GET_UNINSTALLED_PACKAGES
+                                    | PackageManager.GET_DISABLED_COMPONENTS);
+                        } catch (NameNotFoundException e) {
+                        }
+                    }
                     // If app is considered idle, then skip for now and backoff
-                    if (packageName != null
-                            && mAppIdleMonitor.isAppIdle(packageName, op.target.userId)) {
+                    if (ai != null
+                            && mAppIdleMonitor.isAppIdle(packageName, ai.uid, op.target.userId)) {
                         increaseBackoffSetting(op);
                         op.appIdle = true;
                         if (isLoggable) {
diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java
index 02d4f40..6fc02f6 100644
--- a/services/core/java/com/android/server/job/controllers/AppIdleController.java
+++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java
@@ -67,7 +67,7 @@
             mTrackedTasks.add(jobStatus);
             String packageName = jobStatus.job.getService().getPackageName();
             final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName,
-                    jobStatus.getUserId());
+                    jobStatus.uId, jobStatus.getUserId());
             if (DEBUG) {
                 Slog.d(LOG_TAG, "Start tracking, setting idle state of "
                         + packageName + " to " + appIdle);
@@ -108,7 +108,7 @@
             for (JobStatus task : mTrackedTasks) {
                 String packageName = task.job.getService().getPackageName();
                 final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName,
-                        task.getUserId());
+                        task.uId, task.getUserId());
                 if (DEBUG) {
                     Slog.d(LOG_TAG, "Setting idle state of " + packageName + " to " + appIdle);
                 }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index bc8957f..b428322 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -2229,7 +2229,7 @@
         final int userId = UserHandle.getUserId(uid);
 
         for (String packageName : packages) {
-            if (!mUsageStats.isAppIdle(packageName, userId)) {
+            if (!mUsageStats.isAppIdle(packageName, uid, userId)) {
                 return false;
             }
         }
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 5ad796f..2b8afba 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -385,9 +385,11 @@
                         timeNow);
                 final int packageCount = packages.size();
                 for (int p = 0; p < packageCount; p++) {
-                    final String packageName = packages.get(p).packageName;
-                    final boolean isIdle = isAppIdleFiltered(packageName, userId, service, timeNow,
-                            screenOnTime);
+                    final PackageInfo pi = packages.get(p);
+                    final String packageName = pi.packageName;
+                    final boolean isIdle = isAppIdleFiltered(packageName,
+                            UserHandle.getAppId(pi.applicationInfo.uid),
+                            userId, service, timeNow, screenOnTime);
                     mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
                             userId, isIdle ? 1 : 0, packageName));
                     mAppIdleHistory.addEntry(packageName, userId, isIdle, timeNow);
@@ -769,10 +771,17 @@
         if (mAppIdleParoled) {
             return false;
         }
-        return isAppIdleFiltered(packageName, userId, timeNow);
+        try {
+            ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(packageName,
+                    PackageManager.GET_UNINSTALLED_PACKAGES
+                            | PackageManager.GET_DISABLED_COMPONENTS);
+            return isAppIdleFiltered(packageName, ai.uid, userId, timeNow);
+        } catch (PackageManager.NameNotFoundException e) {
+        }
+        return false;
     }
 
-    boolean isAppIdleFiltered(String packageName, int userId, long timeNow) {
+    boolean isAppIdleFiltered(String packageName, int uidForAppId, int userId, long timeNow) {
         final UserUsageStatsService userService;
         final long screenOnTime;
         synchronized (mLock) {
@@ -782,7 +791,8 @@
             userService = getUserDataAndInitializeIfNeededLocked(userId, timeNow);
             screenOnTime = getScreenOnTimeLocked(timeNow);
         }
-        return isAppIdleFiltered(packageName, userId, userService, timeNow, screenOnTime);
+        return isAppIdleFiltered(packageName, UserHandle.getAppId(uidForAppId), userId,
+                userService, timeNow, screenOnTime);
     }
 
     /**
@@ -791,14 +801,22 @@
      * This happens if the device is plugged in or temporarily allowed to make exceptions.
      * Called by interface impls.
      */
-    private boolean isAppIdleFiltered(String packageName, int userId,
+    private boolean isAppIdleFiltered(String packageName, int appId, int userId,
             UserUsageStatsService userService, long timeNow, long screenOnTime) {
         if (packageName == null) return false;
         // If not enabled at all, of course nobody is ever idle.
         if (!mAppIdleEnabled) {
             return false;
         }
-        if (packageName.equals("android")) return false;
+        if (appId < Process.FIRST_APPLICATION_UID) {
+            // System uids never go idle.
+            return false;
+        }
+        if (packageName.equals("android")) {
+            // Nor does the framework (which should be redundant with the above, but for MR1 we will
+            // retain this for safety).
+            return false;
+        }
         try {
             // We allow all whitelisted apps, including those that don't want to be whitelisted
             // for idle mode, because app idle (aka app standby) is really not as big an issue
@@ -865,8 +883,8 @@
             ApplicationInfo ai = apps.get(i);
 
             // Check whether this app is idle.
-            boolean idle = isAppIdleFiltered(ai.packageName, userId, userService, timeNow,
-                    screenOnTime);
+            boolean idle = isAppIdleFiltered(ai.packageName, UserHandle.getAppId(ai.uid),
+                    userId, userService, timeNow, screenOnTime);
 
             int index = uidStates.indexOfKey(ai.uid);
             if (index < 0) {
@@ -1352,8 +1370,8 @@
         }
 
         @Override
-        public boolean isAppIdle(String packageName, int userId) {
-            return UsageStatsService.this.isAppIdleFiltered(packageName, userId, -1);
+        public boolean isAppIdle(String packageName, int uidForAppId, int userId) {
+            return UsageStatsService.this.isAppIdleFiltered(packageName, uidForAppId, userId, -1);
         }
 
         @Override