First pass at NetworkPolicy and activity tracking.

New system service that maintains low-level network policy rules and
collects statistics to drive those rules.  Will eventually connect to
netfilter kernel module through NetworkManagementService and "netd".

Begin tracking foreground activities in ActivityManagerService, which
is updated as part of OOM adjustment.  Eventually a network policy of
POLICY_REJECT_BACKGROUND will reject network traffic from background
processes.

Change-Id: I5ffbbaee1b9628e9c3eff6b9cb2145fc5316e64d
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 586bb6b..4ccc572 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -25,6 +25,7 @@
 import com.android.server.SystemServer;
 import com.android.server.Watchdog;
 import com.android.server.am.ActivityStack.ActivityState;
+import com.android.server.net.NetworkPolicyManagerService;
 import com.android.server.wm.WindowManagerService;
 
 import dalvik.system.Zygote;
@@ -751,6 +752,8 @@
      */
     final UsageStatsService mUsageStatsService;
 
+    final NetworkPolicyManagerService mNetworkPolicyService;
+
     /**
      * Current configuration information.  HistoryRecord objects are given
      * a reference to this object to indicate which configuration they are
@@ -971,6 +974,8 @@
     static final int CHECK_EXCESSIVE_WAKE_LOCKS_MSG = 27;
     static final int CLEAR_DNS_CACHE = 28;
     static final int UPDATE_HTTP_PROXY = 29;
+    static final int DISPATCH_FOREGROUND_ACTIVITIES_CHANGED = 30;
+    static final int DISPATCH_PROCESS_DIED = 31;
 
     AlertDialog mUidAlert;
 
@@ -1271,6 +1276,19 @@
                     sendMessageDelayed(nmsg, POWER_CHECK_DELAY);
                 }
             } break;
+            case DISPATCH_FOREGROUND_ACTIVITIES_CHANGED: {
+                // Flag might have changed during dispatch, but it's always
+                // consistent since we dispatch for every change.
+                final ProcessRecord app = (ProcessRecord) msg.obj;
+                mNetworkPolicyService.onForegroundActivitiesChanged(
+                        app.info.uid, app.pid, app.foregroundActivities);
+                break;
+            }
+            case DISPATCH_PROCESS_DIED: {
+                final ProcessRecord app = (ProcessRecord) msg.obj;
+                mNetworkPolicyService.onProcessDied(app.info.uid, app.pid);
+                break;
+            }
             }
         }
     };
@@ -1340,6 +1358,7 @@
         
         m.mBatteryStatsService.publish(context);
         m.mUsageStatsService.publish(context);
+        m.mNetworkPolicyService.publish(context);
         
         synchronized (thr) {
             thr.mReady = true;
@@ -1461,6 +1480,8 @@
         mUsageStatsService = new UsageStatsService(new File(
                 systemDir, "usagestats").toString());
 
+        mNetworkPolicyService = new NetworkPolicyManagerService();
+
         GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
             ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
 
@@ -1741,7 +1762,7 @@
         mLruSeq++;
         updateLruProcessInternalLocked(app, oomAdj, updateActivityTime, 0);
     }
-    
+
     final ProcessRecord getProcessRecordLocked(
             String processName, int uid) {
         if (uid == Process.SYSTEM_UID) {
@@ -3377,7 +3398,8 @@
 
     private final boolean forceStopPackageLocked(String name, int uid,
             boolean callerWillRestart, boolean purgeCache, boolean doit) {
-        int i, N;
+        int i;
+        int N;
 
         if (uid < 0) {
             try {
@@ -6100,6 +6122,7 @@
         
         mUsageStatsService.shutdown();
         mBatteryStatsService.shutdown();
+        mNetworkPolicyService.shutdown();
         
         return timedout;
     }
@@ -8729,9 +8752,17 @@
                     schedGroup = Integer.toString(r.setSchedGroup);
                     break;
             }
-            pw.println(String.format("%s%s #%2d: adj=%s/%s %s (%s)",
+            String foreground;
+            if (r.foregroundActivities) {
+                foreground = "A";
+            } else if (r.foregroundServices) {
+                foreground = "S";
+            } else {
+                foreground = " ";
+            }
+            pw.println(String.format("%s%s #%2d: adj=%s/%s%s %s (%s)",
                     prefix, (r.persistent ? persistentLabel : normalLabel),
-                    N-i, oomAdj, schedGroup, r.toShortString(), r.adjType));
+                    N-i, oomAdj, schedGroup, foreground, r.toShortString(), r.adjType));
             if (r.adjSource != null || r.adjTarget != null) {
                 pw.print(prefix);
                 pw.print("    ");
@@ -9107,6 +9138,7 @@
         app.thread = null;
         app.forcingToForeground = null;
         app.foregroundServices = false;
+        app.foregroundActivities = false;
 
         killServicesLocked(app, true);
 
@@ -9200,6 +9232,8 @@
             }
         }
 
+        mHandler.obtainMessage(DISPATCH_PROCESS_DIED, app).sendToTarget();
+
         // If the caller is restarting this app, then leave it in its
         // current lists and let the caller take care of it.
         if (restarting) {
@@ -12426,25 +12460,30 @@
             app.keeping = true;
             app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
             return (app.curAdj=app.maxAdj);
-       }
-        
+        }
+
+        final boolean hadForegroundActivities = app.foregroundActivities;
+
         app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;
         app.adjSource = null;
         app.adjTarget = null;
         app.keeping = false;
         app.empty = false;
         app.hidden = false;
+        app.foregroundActivities = false;
+
+        final int activitiesSize = app.activities.size();
 
         // Determine the importance of the process, starting with most
         // important to least, and assign an appropriate OOM adjustment.
         int adj;
         int schedGroup;
-        int N;
         if (app == TOP_APP) {
             // The last app on the list is the foreground app.
             adj = FOREGROUND_APP_ADJ;
             schedGroup = Process.THREAD_GROUP_DEFAULT;
             app.adjType = "top-activity";
+            app.foregroundActivities = true;
         } else if (app.instrumentationClass != null) {
             // Don't want to kill running instrumentation.
             adj = FOREGROUND_APP_ADJ;
@@ -12463,54 +12502,64 @@
             adj = FOREGROUND_APP_ADJ;
             schedGroup = Process.THREAD_GROUP_DEFAULT;
             app.adjType = "exec-service";
-        } else if ((N=app.activities.size()) != 0) {
+        } else if (activitiesSize > 0) {
             // This app is in the background with paused activities.
-            app.hidden = true;
+            // We inspect activities to potentially upgrade adjustment further below.
             adj = hiddenAdj;
             schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+            app.hidden = true;
             app.adjType = "bg-activities";
-            N = app.activities.size();
-            for (int j=0; j<N; j++) {
-                ActivityRecord r = app.activities.get(j);
-                if (r.visible) {
-                    // This app has a visible activity!
-                    app.hidden = false;
-                    adj = VISIBLE_APP_ADJ;
-                    schedGroup = Process.THREAD_GROUP_DEFAULT;
-                    app.adjType = "visible";
-                    break;
-                } else if (r.state == ActivityState.PAUSING
-                        || r.state == ActivityState.PAUSED
-                        || r.state == ActivityState.STOPPING) {
-                    adj = PERCEPTIBLE_APP_ADJ;
-                    app.adjType = "stopping";
-                }
-            }
         } else {
             // A very not-needed process.  If this is lower in the lru list,
             // we will push it in to the empty bucket.
+            adj = hiddenAdj;
+            schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
             app.hidden = true;
             app.empty = true;
-            schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
-            adj = hiddenAdj;
             app.adjType = "bg-empty";
         }
-        
+
+        // Examine all activities if not already foreground.
+        if (!app.foregroundActivities && activitiesSize > 0) {
+            for (int j = 0; j < activitiesSize; j++) {
+                final ActivityRecord r = app.activities.get(j);
+                if (r.visible) {
+                    // App has a visible activity; only upgrade adjustment.
+                    if (adj > VISIBLE_APP_ADJ) {
+                        adj = VISIBLE_APP_ADJ;
+                        app.adjType = "visible";
+                    }
+                    schedGroup = Process.THREAD_GROUP_DEFAULT;
+                    app.hidden = false;
+                    app.foregroundActivities = true;
+                    break;
+                } else if (r.state == ActivityState.PAUSING || r.state == ActivityState.PAUSED
+                        || r.state == ActivityState.STOPPING) {
+                    // Only upgrade adjustment.
+                    if (adj > PERCEPTIBLE_APP_ADJ) {
+                        adj = PERCEPTIBLE_APP_ADJ;
+                        app.adjType = "stopping";
+                    }
+                    app.foregroundActivities = true;
+                }
+            }
+        }
+
         if (adj > PERCEPTIBLE_APP_ADJ) {
             if (app.foregroundServices) {
                 // The user is aware of this app, so make it visible.
                 adj = PERCEPTIBLE_APP_ADJ;
-                schedGroup = Process.THREAD_GROUP_DEFAULT;
                 app.adjType = "foreground-service";
+                schedGroup = Process.THREAD_GROUP_DEFAULT;
             } else if (app.forcingToForeground != null) {
                 // The user is aware of this app, so make it visible.
                 adj = PERCEPTIBLE_APP_ADJ;
-                schedGroup = Process.THREAD_GROUP_DEFAULT;
                 app.adjType = "force-foreground";
                 app.adjSource = app.forcingToForeground;
+                schedGroup = Process.THREAD_GROUP_DEFAULT;
             }
         }
-        
+
         if (adj > HEAVY_WEIGHT_APP_ADJ && app == mHeavyWeightProcess) {
             // We don't want to kill the current heavy-weight process.
             adj = HEAVY_WEIGHT_APP_ADJ;
@@ -12730,7 +12779,11 @@
 
         app.curAdj = adj;
         app.curSchedGroup = schedGroup;
-        
+
+        if (hadForegroundActivities != app.foregroundActivities) {
+            mHandler.obtainMessage(DISPATCH_FOREGROUND_ACTIVITIES_CHANGED, app).sendToTarget();
+        }
+
         return adj;
     }
 
@@ -12963,13 +13016,15 @@
     }
 
     private final boolean updateOomAdjLocked(
-        ProcessRecord app, int hiddenAdj, ProcessRecord TOP_APP) {
+            ProcessRecord app, int hiddenAdj, ProcessRecord TOP_APP) {
         app.hiddenAdj = hiddenAdj;
 
         if (app.thread == null) {
             return true;
         }
 
+        boolean success = true;
+
         final boolean wasKeeping = app.keeping;
 
         int adj = computeOomAdjLocked(app, hiddenAdj, TOP_APP, false);
@@ -13009,7 +13064,7 @@
                         " oom adj to " + adj);
                     app.setAdj = adj;
                 } else {
-                    return false;
+                    success = false;
                 }
             }
             if (app.setSchedGroup != app.curSchedGroup) {
@@ -13048,7 +13103,7 @@
             }
         }
 
-        return true;
+        return success;
     }
 
     private final ActivityRecord resumedAppLocked() {