Merge "Extend process observer to be usable for media routing." into jb-dev
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 17b1962..92b6f72 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1360,6 +1360,13 @@
         public int lastTrimLevel;
 
         /**
+         * Constant for {@link #importance}: this is a persistent process.
+         * Only used when reporting to process observers.
+         * @hide
+         */
+        public static final int IMPORTANCE_PERSISTENT = 50;
+
+        /**
          * Constant for {@link #importance}: this process is running the
          * foreground UI.
          */
diff --git a/core/java/android/app/IProcessObserver.aidl b/core/java/android/app/IProcessObserver.aidl
index 2094294..e587912 100644
--- a/core/java/android/app/IProcessObserver.aidl
+++ b/core/java/android/app/IProcessObserver.aidl
@@ -20,6 +20,7 @@
 oneway interface IProcessObserver {
 
     void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities);
+    void onImportanceChanged(int pid, int uid, int importance);
     void onProcessDied(int pid, int uid);
 
 }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 2c8ba22..febbd32 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -176,6 +176,7 @@
     static final boolean DEBUG_SERVICE_EXECUTING = localLOGV || false;
     static final boolean DEBUG_VISBILITY = localLOGV || false;
     static final boolean DEBUG_PROCESSES = localLOGV || false;
+    static final boolean DEBUG_PROCESS_OBSERVERS = localLOGV || false;
     static final boolean DEBUG_PROVIDER = localLOGV || false;
     static final boolean DEBUG_URI_PERMISSION = localLOGV || false;
     static final boolean DEBUG_USER_LEAVING = localLOGV || false;
@@ -764,8 +765,24 @@
     boolean mAutoStopProfiler = false;
     String mOpenGlTraceApp = null;
 
+    static class ProcessChangeItem {
+        static final int CHANGE_ACTIVITIES = 1<<0;
+        static final int CHANGE_IMPORTANCE= 1<<1;
+        int changes;
+        int uid;
+        int pid;
+        int importance;
+        boolean foregroundActivities;
+    }
+
     final RemoteCallbackList<IProcessObserver> mProcessObservers
             = new RemoteCallbackList<IProcessObserver>();
+    ProcessChangeItem[] mActiveProcessChanges = new ProcessChangeItem[5];
+
+    final ArrayList<ProcessChangeItem> mPendingProcessChanges
+            = new ArrayList<ProcessChangeItem>();
+    final ArrayList<ProcessChangeItem> mAvailProcessChanges
+            = new ArrayList<ProcessChangeItem>();
 
     /**
      * Callback of last caller to {@link #requestPss}.
@@ -855,7 +872,7 @@
     static final int CLEAR_DNS_CACHE = 28;
     static final int UPDATE_HTTP_PROXY = 29;
     static final int SHOW_COMPAT_MODE_DIALOG_MSG = 30;
-    static final int DISPATCH_FOREGROUND_ACTIVITIES_CHANGED = 31;
+    static final int DISPATCH_PROCESSES_CHANGED = 31;
     static final int DISPATCH_PROCESS_DIED = 32;
     static final int REPORT_MEM_USAGE = 33;
 
@@ -1195,11 +1212,8 @@
                 }
                 break;
             }
-            case DISPATCH_FOREGROUND_ACTIVITIES_CHANGED: {
-                final int pid = msg.arg1;
-                final int uid = msg.arg2;
-                final boolean foregroundActivities = (Boolean) msg.obj;
-                dispatchForegroundActivitiesChanged(pid, uid, foregroundActivities);
+            case DISPATCH_PROCESSES_CHANGED: {
+                dispatchProcessesChanged();
                 break;
             }
             case DISPATCH_PROCESS_DIED: {
@@ -2260,19 +2274,43 @@
 
     void reportResumedActivityLocked(ActivityRecord r) {
         //Slog.i(TAG, "**** REPORT RESUME: " + r);
-        
-        final int identHash = System.identityHashCode(r);
         updateUsageStats(r, true);
     }
 
-    private void dispatchForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
+    private void dispatchProcessesChanged() {
+        int N;
+        synchronized (this) {
+            N = mPendingProcessChanges.size();
+            if (mActiveProcessChanges.length < N) {
+                mActiveProcessChanges = new ProcessChangeItem[N];
+            }
+            mPendingProcessChanges.toArray(mActiveProcessChanges);
+            mAvailProcessChanges.addAll(mPendingProcessChanges);
+            mPendingProcessChanges.clear();
+            if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "*** Delivering " + N + " process changes");
+        }
         int i = mProcessObservers.beginBroadcast();
         while (i > 0) {
             i--;
             final IProcessObserver observer = mProcessObservers.getBroadcastItem(i);
             if (observer != null) {
                 try {
-                    observer.onForegroundActivitiesChanged(pid, uid, foregroundActivities);
+                    for (int j=0; j<N; j++) {
+                        ProcessChangeItem item = mActiveProcessChanges[j];
+                        if ((item.changes&ProcessChangeItem.CHANGE_ACTIVITIES) != 0) {
+                            if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "ACTIVITIES CHANGED pid="
+                                    + item.pid + " uid=" + item.uid + ": "
+                                    + item.foregroundActivities);
+                            observer.onForegroundActivitiesChanged(item.pid, item.uid,
+                                    item.foregroundActivities);
+                        }
+                        if ((item.changes&ProcessChangeItem.CHANGE_IMPORTANCE) != 0) {
+                            if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "IMPORTANCE CHANGED pid="
+                                    + item.pid + " uid=" + item.uid + ": " + item.importance);
+                            observer.onImportanceChanged(item.pid, item.uid,
+                                    item.importance);
+                        }
+                    }
                 } catch (RemoteException e) {
                 }
             }
@@ -10802,6 +10840,13 @@
             }
         }
 
+        for (int i = mPendingProcessChanges.size()-1; i>=0; i--) {
+            ProcessChangeItem item = mPendingProcessChanges.get(i);
+            if (item.pid == app.pid) {
+                mPendingProcessChanges.remove(i);
+                mAvailProcessChanges.add(item);
+            }
+        }
         mHandler.obtainMessage(DISPATCH_PROCESS_DIED, app.pid, app.info.uid, null).sendToTarget();
 
         // If the caller is restarting this app, then leave it in its
@@ -13734,9 +13779,6 @@
             return (app.curAdj=app.maxAdj);
         }
 
-        final boolean hadForegroundActivities = app.foregroundActivities;
-
-        app.foregroundActivities = false;
         app.keeping = false;
         app.systemNoUi = false;
 
@@ -13744,18 +13786,22 @@
         // important to least, and assign an appropriate OOM adjustment.
         int adj;
         int schedGroup;
+        boolean foregroundActivities = false;
+        boolean interesting = false;
         BroadcastQueue queue;
         if (app == TOP_APP) {
             // The last app on the list is the foreground app.
             adj = ProcessList.FOREGROUND_APP_ADJ;
             schedGroup = Process.THREAD_GROUP_DEFAULT;
             app.adjType = "top-activity";
-            app.foregroundActivities = true;
+            foregroundActivities = true;
+            interesting = true;
         } 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;
         } else if ((queue = isReceivingBroadcast(app)) != null) {
             // An app that is currently receiving a broadcast also
             // counts as being in the foreground for OOM killer purposes.
@@ -13791,7 +13837,7 @@
         boolean hasStoppingActivities = false;
 
         // Examine all activities if not already foreground.
-        if (!app.foregroundActivities && activitiesSize > 0) {
+        if (!foregroundActivities && activitiesSize > 0) {
             for (int j = 0; j < activitiesSize; j++) {
                 final ActivityRecord r = app.activities.get(j);
                 if (r.visible) {
@@ -13802,7 +13848,7 @@
                     }
                     schedGroup = Process.THREAD_GROUP_DEFAULT;
                     app.hidden = false;
-                    app.foregroundActivities = true;
+                    foregroundActivities = true;
                     break;
                 } else if (r.state == ActivityState.PAUSING || r.state == ActivityState.PAUSED) {
                     if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
@@ -13810,13 +13856,13 @@
                         app.adjType = "pausing";
                     }
                     app.hidden = false;
-                    app.foregroundActivities = true;
+                    foregroundActivities = true;
                 } else if (r.state == ActivityState.STOPPING) {
                     // We will apply the actual adjustment later, because
                     // we want to allow this process to immediately go through
                     // any memory trimming that is in effect.
                     app.hidden = false;
-                    app.foregroundActivities = true;
+                    foregroundActivities = true;
                     hasStoppingActivities = true;
                 }
             }
@@ -13839,6 +13885,10 @@
             }
         }
 
+        if (app.foregroundServices) {
+            interesting = true;
+        }
+
         if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ && app == mHeavyWeightProcess) {
             // We don't want to kill the current heavy-weight process.
             adj = ProcessList.HEAVY_WEIGHT_APP_ADJ;
@@ -14188,12 +14238,84 @@
             }
         }
 
-        app.curAdj = adj;
-        app.curSchedGroup = schedGroup;
+        int importance = app.memImportance;
+        if (importance == 0 || adj != app.curAdj || schedGroup != app.curSchedGroup) {
+            app.curAdj = adj;
+            app.curSchedGroup = schedGroup;
+            if (!interesting) {
+                // For this reporting, if there is not something explicitly
+                // interesting in this process then we will push it to the
+                // background importance.
+                importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
+            } else if (adj >= ProcessList.HIDDEN_APP_MIN_ADJ) {
+                importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
+            } else if (adj >= ProcessList.SERVICE_B_ADJ) {
+                importance =  ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
+            } else if (adj >= ProcessList.HOME_APP_ADJ) {
+                importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
+            } else if (adj >= ProcessList.SERVICE_ADJ) {
+                importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
+            } else if (adj >= ProcessList.HEAVY_WEIGHT_APP_ADJ) {
+                importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE;
+            } else if (adj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
+                importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE;
+            } else if (adj >= ProcessList.VISIBLE_APP_ADJ) {
+                importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
+            } else if (adj >= ProcessList.FOREGROUND_APP_ADJ) {
+                importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+            } else {
+                importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERSISTENT;
+            }
+        }
 
-        if (hadForegroundActivities != app.foregroundActivities) {
-            mHandler.obtainMessage(DISPATCH_FOREGROUND_ACTIVITIES_CHANGED, app.pid, app.info.uid,
-                    app.foregroundActivities).sendToTarget();
+        int changes = importance != app.memImportance ? ProcessChangeItem.CHANGE_IMPORTANCE : 0;
+        if (foregroundActivities != app.foregroundActivities) {
+            changes |= ProcessChangeItem.CHANGE_ACTIVITIES;
+        }
+        if (changes != 0) {
+            if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Changes in " + app + ": " + changes);
+            app.memImportance = importance;
+            app.foregroundActivities = foregroundActivities;
+            int i = mPendingProcessChanges.size()-1;
+            ProcessChangeItem item = null;
+            while (i >= 0) {
+                item = mPendingProcessChanges.get(i);
+                if (item.pid == app.pid) {
+                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Re-using existing item: " + item);
+                    break;
+                }
+                i--;
+            }
+            if (i < 0) {
+                // No existing item in pending changes; need a new one.
+                final int NA = mAvailProcessChanges.size();
+                if (NA > 0) {
+                    item = mAvailProcessChanges.remove(NA-1);
+                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Retreiving available item: " + item);
+                } else {
+                    item = new ProcessChangeItem();
+                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Allocating new item: " + item);
+                }
+                item.changes = 0;
+                item.pid = app.pid;
+                item.uid = app.info.uid;
+                if (mPendingProcessChanges.size() == 0) {
+                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG,
+                            "*** Enqueueing dispatch processes changed!");
+                    mHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED).sendToTarget();
+                }
+                mPendingProcessChanges.add(item);
+            }
+            item.changes |= changes;
+            item.importance = importance;
+            item.foregroundActivities = foregroundActivities;
+            if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Item "
+                    + Integer.toHexString(System.identityHashCode(item))
+                    + " " + app.toShortString() + ": changes=" + item.changes
+                    + " importance=" + item.importance
+                    + " foreground=" + item.foregroundActivities
+                    + " type=" + app.adjType + " source=" + app.adjSource
+                    + " target=" + app.adjTarget);
         }
 
         return app.curRawAdj;
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 8a3ba7f..cba9480 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -69,6 +69,7 @@
     int curSchedGroup;          // Currently desired scheduling class
     int setSchedGroup;          // Last set to background scheduling class
     int trimMemoryLevel;        // Last selected memory trimming level
+    int memImportance;          // Importance constant computed from curAdj
     boolean serviceb;           // Process currently is on the service B list
     boolean keeping;            // Actively running code so don't kill due to that?
     boolean setIsForeground;    // Running foreground UI when last set?
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index c82f37c..c4abac9 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -396,6 +396,10 @@
         }
 
         @Override
+        public void onImportanceChanged(int pid, int uid, int importance) {
+        }
+
+        @Override
         public void onProcessDied(int pid, int uid) {
             mHandler.obtainMessage(MSG_PROCESS_DIED, pid, uid).sendToTarget();
         }