Rework network stats to use proc state for fg/bg.

Switch to using the process state to determine whether a
process should be foreground or background, instead of the
boolean foreground given by the activity manager.

This is for battery save mode, where we can now allow more apps
to havenetwork access: everything whose process state is at
least IMPORTANT_FOREGROUND, which allows music playback
and other use-visible things to continue to have network
access.

Note this also impact the traditional background data disabled
state, where now we allow anything top or better to have
network access.  This automatically includes all persistent
processes, the current top activity, and any other processes
hosting the top activity or being used by the top activity.
So it broadens the set of apps that get network access, but I
think this increases it to a reasonable set of things that may
actually be needed for the foreground app to work correctly.

Change-Id: Icb609a2cea280dc3fa3e83417f478ed77f3685aa
diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java
index 9d4b720..8f7aefd 100644
--- a/core/java/android/util/ArraySet.java
+++ b/core/java/android/util/ArraySet.java
@@ -372,7 +372,7 @@
      * Perform a {@link #add(Object)} of all values in <var>array</var>
      * @param array The array whose contents are to be retrieved.
      */
-    public void putAll(ArraySet<? extends E> array) {
+    public void addAll(ArraySet<? extends E> array) {
         final int N = array.mSize;
         ensureCapacity(mSize + N);
         if (mSize == 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 03c23e5..5c6d279 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -3182,7 +3182,7 @@
             }
             final boolean updates = !mVisibleNotifications.containsAll(mNewVisibleNotifications);
             mVisibleNotifications.clear();
-            mVisibleNotifications.putAll(mNewVisibleNotifications);
+            mVisibleNotifications.addAll(mNewVisibleNotifications);
 
             // We have new notifications
             if (updates && mDozeServiceHost != null) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 17268c3..45367a8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -15020,7 +15020,6 @@
         int schedGroup;
         int procState;
         boolean foregroundActivities = false;
-        boolean interesting = false;
         BroadcastQueue queue;
         if (app == TOP_APP) {
             // The last app on the list is the foreground app.
@@ -15028,14 +15027,12 @@
             schedGroup = Process.THREAD_GROUP_DEFAULT;
             app.adjType = "top-activity";
             foregroundActivities = true;
-            interesting = true;
             procState = ActivityManager.PROCESS_STATE_TOP;
         } else if (app.instrumentationClass != null) {
             // Don't want to kill running instrumentation.
             adj = ProcessList.FOREGROUND_APP_ADJ;
             schedGroup = Process.THREAD_GROUP_DEFAULT;
             app.adjType = "instrumentation";
-            interesting = true;
             procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
         } else if ((queue = isReceivingBroadcast(app)) != null) {
             // An app that is currently receiving a broadcast also
@@ -15151,10 +15148,6 @@
             }
         }
 
-        if (app.foregroundServices) {
-            interesting = true;
-        }
-
         if (app == mHeavyWeightProcess) {
             if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) {
                 // We don't want to kill the current heavy-weight process.
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 142094f..f0ec3bb 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -75,6 +75,7 @@
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
+import android.app.ActivityManager;
 import android.app.IActivityManager;
 import android.app.INotificationManager;
 import android.app.IProcessObserver;
@@ -140,8 +141,6 @@
 import com.android.server.LocalServices;
 import com.android.server.SystemConfig;
 import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
-import com.google.android.collect.Sets;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -156,8 +155,6 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
 
@@ -229,8 +226,6 @@
 
     private static final int MSG_RULES_CHANGED = 1;
     private static final int MSG_METERED_IFACES_CHANGED = 2;
-    private static final int MSG_FOREGROUND_ACTIVITIES_CHANGED = 3;
-    private static final int MSG_PROCESS_DIED = 4;
     private static final int MSG_LIMIT_REACHED = 5;
     private static final int MSG_RESTRICT_BACKGROUND_CHANGED = 6;
     private static final int MSG_ADVISE_PERSIST_THRESHOLD = 7;
@@ -247,23 +242,23 @@
     private INotificationManager mNotifManager;
     private PowerManagerInternal mPowerManagerInternal;
 
-    private final Object mRulesLock = new Object();
+    final Object mRulesLock = new Object();
 
-    private volatile boolean mScreenOn;
-    private volatile boolean mRestrictBackground;
-    private volatile boolean mRestrictPower;
+    volatile boolean mScreenOn;
+    volatile boolean mRestrictBackground;
+    volatile boolean mRestrictPower;
 
     private final boolean mSuppressDefaultPolicy;
 
     /** Defined network policies. */
-    private final ArrayMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = new ArrayMap<
+    final ArrayMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = new ArrayMap<
             NetworkTemplate, NetworkPolicy>();
     /** Currently active network rules for ifaces. */
     private final ArrayMap<NetworkPolicy, String[]> mNetworkRules = new ArrayMap<
             NetworkPolicy, String[]>();
 
     /** Defined UID policies. */
-    private final SparseIntArray mUidPolicy = new SparseIntArray();
+    final SparseIntArray mUidPolicy = new SparseIntArray();
     /** Currently derived rules for each UID. */
     private final SparseIntArray mUidRules = new SparseIntArray();
 
@@ -272,22 +267,24 @@
     private final SparseBooleanArray mPowerSaveWhitelistAppIds = new SparseBooleanArray();
 
     /** Set of ifaces that are metered. */
-    private HashSet<String> mMeteredIfaces = Sets.newHashSet();
+    private ArraySet<String> mMeteredIfaces = new ArraySet<String>();
     /** Set of over-limit templates that have been notified. */
-    private final HashSet<NetworkTemplate> mOverLimitNotified = Sets.newHashSet();
+    private final ArraySet<NetworkTemplate> mOverLimitNotified = new ArraySet<NetworkTemplate>();
 
     /** Set of currently active {@link Notification} tags. */
-    private final HashSet<String> mActiveNotifs = Sets.newHashSet();
+    private final ArraySet<String> mActiveNotifs = new ArraySet<String>();
 
     /** Foreground at both UID and PID granularity. */
-    private final SparseBooleanArray mUidForeground = new SparseBooleanArray();
-    private final SparseArray<SparseBooleanArray> mUidPidForeground = new SparseArray<
-            SparseBooleanArray>();
+    private final SparseIntArray mUidState = new SparseIntArray();
+    final SparseArray<SparseIntArray> mUidPidState = new SparseArray<SparseIntArray>();
+
+    /** The current maximum process state that we are considering to be foreground. */
+    private int mCurForegroundState = ActivityManager.PROCESS_STATE_TOP;
 
     private final RemoteCallbackList<INetworkPolicyListener> mListeners = new RemoteCallbackList<
             INetworkPolicyListener>();
 
-    private final Handler mHandler;
+    final Handler mHandler;
 
     private final AtomicFile mPolicyFile;
 
@@ -448,17 +445,38 @@
     private IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
         @Override
         public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
-            mHandler.obtainMessage(MSG_FOREGROUND_ACTIVITIES_CHANGED,
-                    pid, uid, foregroundActivities).sendToTarget();
         }
 
         @Override
         public void onProcessStateChanged(int pid, int uid, int procState) {
+            synchronized (mRulesLock) {
+                // because a uid can have multiple pids running inside, we need to
+                // remember all pid states and summarize foreground at uid level.
+
+                // record foreground for this specific pid
+                SparseIntArray pidState = mUidPidState.get(uid);
+                if (pidState == null) {
+                    pidState = new SparseIntArray(2);
+                    mUidPidState.put(uid, pidState);
+                }
+                pidState.put(pid, procState);
+                computeUidStateLocked(uid);
+            }
         }
 
         @Override
         public void onProcessDied(int pid, int uid) {
-            mHandler.obtainMessage(MSG_PROCESS_DIED, pid, uid).sendToTarget();
+            synchronized (mRulesLock) {
+                // clear records and recompute, when they exist
+                final SparseIntArray pidState = mUidPidState.get(uid);
+                if (pidState != null) {
+                    pidState.delete(pid);
+                    if (pidState.size() <= 0) {
+                        mUidPidState.remove(uid);
+                    }
+                    computeUidStateLocked(uid);
+                }
+            }
         }
     };
 
@@ -519,12 +537,11 @@
             final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
             if (userId == -1) return;
 
-            // Remove any policies for given user; both cleaning up after a
-            // USER_REMOVED, and one last sanity check during USER_ADDED
-            removePoliciesForUserLocked(userId);
-
-            // Update global restrict for new user
             synchronized (mRulesLock) {
+                // Remove any policies for given user; both cleaning up after a
+                // USER_REMOVED, and one last sanity check during USER_ADDED
+                removePoliciesForUserLocked(userId);
+                // Update global restrict for new user
                 updateRulesForGlobalChangeLocked(true);
             }
         }
@@ -663,12 +680,11 @@
      * Check {@link NetworkPolicy} against current {@link INetworkStatsService}
      * to show visible notifications as needed.
      */
-    private void updateNotificationsLocked() {
+    void updateNotificationsLocked() {
         if (LOGV) Slog.v(TAG, "updateNotificationsLocked()");
 
         // keep track of previously active notifications
-        final HashSet<String> beforeNotifs = Sets.newHashSet();
-        beforeNotifs.addAll(mActiveNotifs);
+        final ArraySet<String> beforeNotifs = new ArraySet<String>(mActiveNotifs);
         mActiveNotifs.clear();
 
         // TODO: when switching to kernel notifications, compute next future
@@ -709,7 +725,8 @@
         }
 
         // cancel stale notifications that we didn't renew above
-        for (String tag : beforeNotifs) {
+        for (int i = beforeNotifs.size()-1; i >= 0; i--) {
+            final String tag = beforeNotifs.valueAt(i);
             if (!mActiveNotifs.contains(tag)) {
                 cancelNotification(tag);
             }
@@ -949,7 +966,7 @@
      * Proactively control network data connections when they exceed
      * {@link NetworkPolicy#limitBytes}.
      */
-    private void updateNetworkEnabledLocked() {
+    void updateNetworkEnabledLocked() {
         if (LOGV) Slog.v(TAG, "updateNetworkEnabledLocked()");
 
         // TODO: reset any policy-disabled networks when any policy is removed
@@ -1012,7 +1029,7 @@
      * {@link NetworkPolicy} that need to be enforced. When matches found, set
      * remaining quota based on usage cycle and historical stats.
      */
-    private void updateNetworkRulesLocked() {
+    void updateNetworkRulesLocked() {
         if (LOGV) Slog.v(TAG, "updateIfacesLocked()");
 
         final NetworkState[] states;
@@ -1032,8 +1049,9 @@
 
         // first, derive identity for all connected networks, which can be used
         // to match against templates.
-        final ArrayMap<NetworkIdentity, String> networks = new ArrayMap();
-        final ArraySet<String> connIfaces = new ArraySet<String>();
+        final ArrayMap<NetworkIdentity, String> networks = new ArrayMap<NetworkIdentity,
+                String>(states.length);
+        final ArraySet<String> connIfaces = new ArraySet<String>(states.length);
         for (NetworkState state : states) {
             // stash identity and iface away for later use
             if (state.networkInfo.isConnected()) {
@@ -1069,7 +1087,7 @@
         }
 
         long lowestRule = Long.MAX_VALUE;
-        final HashSet<String> newMeteredIfaces = Sets.newHashSet();
+        final ArraySet<String> newMeteredIfaces = new ArraySet<String>(states.length);
 
         // apply each policy that we found ifaces for; compute remaining data
         // based on current cycle and historical stats, and push to kernel.
@@ -1146,7 +1164,8 @@
         mHandler.obtainMessage(MSG_ADVISE_PERSIST_THRESHOLD, lowestRule).sendToTarget();
 
         // remove quota on any trailing interfaces
-        for (String iface : mMeteredIfaces) {
+        for (int i = mMeteredIfaces.size() - 1; i >= 0; i--) {
+            final String iface = mMeteredIfaces.valueAt(i);
             if (!newMeteredIfaces.contains(iface)) {
                 removeInterfaceQuota(iface);
             }
@@ -1342,7 +1361,7 @@
         }
     }
 
-    private void writePolicyLocked() {
+    void writePolicyLocked() {
         if (LOGV) Slog.v(TAG, "writePolicyLocked()");
 
         FileOutputStream fos = null;
@@ -1512,7 +1531,7 @@
      * Remove any policies associated with given {@link UserHandle}, persisting
      * if any changes are made.
      */
-    private void removePoliciesForUserLocked(int userId) {
+    void removePoliciesForUserLocked(int userId) {
         if (LOGV) Slog.v(TAG, "removePoliciesForUserLocked()");
 
         int[] uids = new int[0];
@@ -1568,7 +1587,7 @@
         }
     }
 
-    private void addNetworkPolicyLocked(NetworkPolicy policy) {
+    void addNetworkPolicyLocked(NetworkPolicy policy) {
         mNetworkPolicy.put(policy.template, policy);
 
         updateNetworkEnabledLocked();
@@ -1599,7 +1618,7 @@
         }
     }
 
-    private void performSnooze(NetworkTemplate template, int type) {
+    void performSnooze(NetworkTemplate template, int type) {
         maybeRefreshTrustedTime();
         final long currentTime = currentTimeMillis();
         synchronized (mRulesLock) {
@@ -1736,7 +1755,7 @@
 
         final IndentingPrintWriter fout = new IndentingPrintWriter(writer, "  ");
 
-        final HashSet<String> argSet = new HashSet<String>();
+        final ArraySet<String> argSet = new ArraySet<String>(args.length);
         for (String arg : args) {
             argSet.add(arg);
         }
@@ -1758,6 +1777,7 @@
 
             fout.print("Restrict background: "); fout.println(mRestrictBackground);
             fout.print("Restrict power: "); fout.println(mRestrictPower);
+            fout.print("Current foreground state: "); fout.println(mCurForegroundState);
             fout.println("Network policies:");
             fout.increaseIndent();
             for (int i = 0; i < mNetworkPolicy.size(); i++) {
@@ -1794,7 +1814,7 @@
             }
 
             final SparseBooleanArray knownUids = new SparseBooleanArray();
-            collectKeys(mUidForeground, knownUids);
+            collectKeys(mUidState, knownUids);
             collectKeys(mUidRules, knownUids);
 
             fout.println("Status for known UIDs:");
@@ -1805,12 +1825,17 @@
                 fout.print("UID=");
                 fout.print(uid);
 
-                fout.print(" foreground=");
-                final int foregroundIndex = mUidPidForeground.indexOfKey(uid);
+                int state = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
+                fout.print(" state=");
+                fout.print(state);
+                fout.print(state <= mCurForegroundState ? " (fg)" : " (bg)");
+
+                fout.print(" pids=");
+                final int foregroundIndex = mUidPidState.indexOfKey(uid);
                 if (foregroundIndex < 0) {
                     fout.print("UNKNOWN");
                 } else {
-                    dumpSparseBooleanArray(fout, mUidPidForeground.valueAt(foregroundIndex));
+                    dumpSparseIntArray(fout, mUidPidState.valueAt(foregroundIndex));
                 }
 
                 fout.print(" rules=");
@@ -1832,33 +1857,44 @@
         mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
 
         synchronized (mRulesLock) {
-            // only really in foreground when screen is also on
-            return mUidForeground.get(uid, false) && mScreenOn;
+            return isUidForegroundLocked(uid);
         }
     }
 
+    boolean isUidForegroundLocked(int uid) {
+        // only really in foreground when screen is also on
+        return mScreenOn && mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY)
+                <= mCurForegroundState;
+    }
+
     /**
-     * Foreground for PID changed; recompute foreground at UID level. If
+     * Process state of PID changed; recompute state at UID level. If
      * changed, will trigger {@link #updateRulesForUidLocked(int)}.
      */
-    private void computeUidForegroundLocked(int uid) {
-        final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
+    void computeUidStateLocked(int uid) {
+        final SparseIntArray pidState = mUidPidState.get(uid);
 
         // current pid is dropping foreground; examine other pids
-        boolean uidForeground = false;
-        final int size = pidForeground.size();
-        for (int i = 0; i < size; i++) {
-            if (pidForeground.valueAt(i)) {
-                uidForeground = true;
-                break;
+        int uidState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+        if (pidState != null) {
+            final int size = pidState.size();
+            for (int i = 0; i < size; i++) {
+                final int state = pidState.valueAt(i);
+                if (state < uidState) {
+                    uidState = state;
+                }
             }
         }
 
-        final boolean oldUidForeground = mUidForeground.get(uid, false);
-        if (oldUidForeground != uidForeground) {
-            // foreground changed, push updated rules
-            mUidForeground.put(uid, uidForeground);
-            updateRulesForUidLocked(uid);
+        final int oldUidState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
+        if (oldUidState != uidState) {
+            // state changed, push updated rules
+            mUidState.put(uid, uidState);
+            final boolean oldForeground = oldUidState <= mCurForegroundState;
+            final boolean newForeground = uidState <= mCurForegroundState;
+            if (oldForeground != newForeground) {
+                updateRulesForUidLocked(uid);
+            }
         }
     }
 
@@ -1878,10 +1914,10 @@
      */
     private void updateRulesForScreenLocked() {
         // only update rules for anyone with foreground activities
-        final int size = mUidForeground.size();
+        final int size = mUidState.size();
         for (int i = 0; i < size; i++) {
-            if (mUidForeground.valueAt(i)) {
-                final int uid = mUidForeground.keyAt(i);
+            if (mUidState.valueAt(i) <= mCurForegroundState) {
+                final int uid = mUidState.keyAt(i);
                 updateRulesForUidLocked(uid);
             }
         }
@@ -1891,10 +1927,17 @@
      * Update rules that might be changed by {@link #mRestrictBackground}
      * or {@link #mRestrictPower} value.
      */
-    private void updateRulesForGlobalChangeLocked(boolean restrictedNetworksChanged) {
+    void updateRulesForGlobalChangeLocked(boolean restrictedNetworksChanged) {
         final PackageManager pm = mContext.getPackageManager();
         final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
 
+        // If we are in restrict power mode, we allow all important apps
+        // to have data access.  Otherwise, we restrict data access to only
+        // the top apps.
+        mCurForegroundState = (!mRestrictBackground && mRestrictPower)
+                ? ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
+                : ActivityManager.PROCESS_STATE_TOP;
+
         // update rules for all installed applications
         final List<UserInfo> users = um.getUsers();
         final List<ApplicationInfo> apps = pm.getInstalledApplications(
@@ -1927,11 +1970,11 @@
         return false;
     }
 
-    private void updateRulesForUidLocked(int uid) {
+    void updateRulesForUidLocked(int uid) {
         if (!isUidValidForRules(uid)) return;
 
-        final int uidPolicy = getUidPolicy(uid);
-        final boolean uidForeground = isUidForeground(uid);
+        final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE);
+        final boolean uidForeground = isUidForegroundLocked(uid);
 
         // derive active rules based on policy and active state
         int uidRules = RULE_ALLOW_ALL;
@@ -2011,40 +2054,6 @@
                     mListeners.finishBroadcast();
                     return true;
                 }
-                case MSG_FOREGROUND_ACTIVITIES_CHANGED: {
-                    final int pid = msg.arg1;
-                    final int uid = msg.arg2;
-                    final boolean foregroundActivities = (Boolean) msg.obj;
-
-                    synchronized (mRulesLock) {
-                        // because a uid can have multiple pids running inside, we need to
-                        // remember all pid states and summarize foreground at uid level.
-
-                        // record foreground for this specific pid
-                        SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
-                        if (pidForeground == null) {
-                            pidForeground = new SparseBooleanArray(2);
-                            mUidPidForeground.put(uid, pidForeground);
-                        }
-                        pidForeground.put(pid, foregroundActivities);
-                        computeUidForegroundLocked(uid);
-                    }
-                    return true;
-                }
-                case MSG_PROCESS_DIED: {
-                    final int pid = msg.arg1;
-                    final int uid = msg.arg2;
-
-                    synchronized (mRulesLock) {
-                        // clear records and recompute, when they exist
-                        final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
-                        if (pidForeground != null) {
-                            pidForeground.delete(pid);
-                            computeUidForegroundLocked(uid);
-                        }
-                    }
-                    return true;
-                }
                 case MSG_LIMIT_REACHED: {
                     final String iface = (String) msg.obj;
 
@@ -2171,7 +2180,7 @@
     /**
      * Try refreshing {@link #mTime} when stale.
      */
-    private void maybeRefreshTrustedTime() {
+    void maybeRefreshTrustedTime() {
         if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE) {
             mTime.forceRefresh();
         }
@@ -2221,14 +2230,7 @@
         }
     }
 
-    private static void collectKeys(SparseBooleanArray source, SparseBooleanArray target) {
-        final int size = source.size();
-        for (int i = 0; i < size; i++) {
-            target.put(source.keyAt(i), true);
-        }
-    }
-
-    private static void dumpSparseBooleanArray(PrintWriter fout, SparseBooleanArray value) {
+    private static void dumpSparseIntArray(PrintWriter fout, SparseIntArray value) {
         fout.print("[");
         final int size = value.size();
         for (int i = 0; i < size; i++) {