Various work on out of memory managment.

- Improve how we handle processes that have shown UI, to take care
  of more cases where we want to push them into the background LRU
  list.
- New trim memory level for when an application that has done UI
  is no longer visible to the user.
- Add APIs to get new trim memory callback.
- Add a host of new bind flags to tweak how the system will adjust
  the OOM level of the target process.

Change-Id: I23ba354112f411a9f8773a67426b4dff85fa2439
diff --git a/api/current.txt b/api/current.txt
index 91cf3fa..fed520c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2331,7 +2331,7 @@
     method public abstract void onTabUnselected(android.app.ActionBar.Tab, android.app.FragmentTransaction);
   }
 
-  public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
+  public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
     ctor public Activity();
     method public void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
     method public void closeContextMenu();
@@ -2436,6 +2436,7 @@
     method protected void onTitleChanged(java.lang.CharSequence, int);
     method public boolean onTouchEvent(android.view.MotionEvent);
     method public boolean onTrackballEvent(android.view.MotionEvent);
+    method public void onTrimMemory(int);
     method public void onUserInteraction();
     method protected void onUserLeaveHint();
     method public void onWindowAttributesChanged(android.view.WindowManager.LayoutParams);
@@ -2731,12 +2732,25 @@
     ctor public AliasActivity();
   }
 
-  public class Application extends android.content.ContextWrapper implements android.content.ComponentCallbacks {
+  public class Application extends android.content.ContextWrapper implements android.content.ComponentCallbacks2 {
     ctor public Application();
     method public void onConfigurationChanged(android.content.res.Configuration);
     method public void onCreate();
     method public void onLowMemory();
     method public void onTerminate();
+    method public void onTrimMemory(int);
+    method public void registerActivityLifecycleCallbacks(android.app.Application.ActivityLifecycleCallbacks);
+    method public void unregisterActivityLifecycleCallbacks(android.app.Application.ActivityLifecycleCallbacks);
+  }
+
+  public static abstract interface Application.ActivityLifecycleCallbacks {
+    method public abstract void onActivityCreated(android.app.Activity, android.os.Bundle);
+    method public abstract void onActivityDestroyed(android.app.Activity);
+    method public abstract void onActivityPaused(android.app.Activity);
+    method public abstract void onActivityResumed(android.app.Activity);
+    method public abstract void onActivitySaveInstanceState(android.app.Activity, android.os.Bundle);
+    method public abstract void onActivityStarted(android.app.Activity);
+    method public abstract void onActivityStopped(android.app.Activity);
   }
 
   public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener {
@@ -2955,7 +2969,7 @@
     method public void setSelectedGroup(int);
   }
 
-  public class Fragment implements android.content.ComponentCallbacks android.view.View.OnCreateContextMenuListener {
+  public class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener {
     ctor public Fragment();
     method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public final boolean equals(java.lang.Object);
@@ -3009,6 +3023,7 @@
     method public void onSaveInstanceState(android.os.Bundle);
     method public void onStart();
     method public void onStop();
+    method public void onTrimMemory(int);
     method public void onViewCreated(android.view.View, android.os.Bundle);
     method public void registerForContextMenu(android.view.View);
     method public void setArguments(android.os.Bundle);
@@ -3545,7 +3560,7 @@
     field public static final android.os.Parcelable.Creator CREATOR;
   }
 
-  public abstract class Service extends android.content.ContextWrapper implements android.content.ComponentCallbacks {
+  public abstract class Service extends android.content.ContextWrapper implements android.content.ComponentCallbacks2 {
     ctor public Service();
     method protected void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public final android.app.Application getApplication();
@@ -3558,6 +3573,7 @@
     method public deprecated void onStart(android.content.Intent, int);
     method public int onStartCommand(android.content.Intent, int, int);
     method public void onTaskRemoved(android.content.Intent);
+    method public void onTrimMemory(int);
     method public boolean onUnbind(android.content.Intent);
     method public final void startForeground(int, android.app.Notification);
     method public final void stopForeground(boolean);
@@ -4442,6 +4458,14 @@
     method public abstract void onLowMemory();
   }
 
+  public abstract interface ComponentCallbacks2 implements android.content.ComponentCallbacks {
+    method public abstract void onTrimMemory(int);
+    field public static final int TRIM_MEMORY_BACKGROUND = 40; // 0x28
+    field public static final int TRIM_MEMORY_COMPLETE = 80; // 0x50
+    field public static final int TRIM_MEMORY_MODERATE = 60; // 0x3c
+    field public static final int TRIM_MEMORY_UI_HIDDEN = 20; // 0x14
+  }
+
   public final class ComponentName implements java.lang.Cloneable java.lang.Comparable android.os.Parcelable {
     ctor public ComponentName(java.lang.String, java.lang.String);
     ctor public ComponentName(android.content.Context, java.lang.String);
@@ -4463,7 +4487,7 @@
     field public static final android.os.Parcelable.Creator CREATOR;
   }
 
-  public abstract class ContentProvider implements android.content.ComponentCallbacks {
+  public abstract class ContentProvider implements android.content.ComponentCallbacks2 {
     ctor public ContentProvider();
     method public android.content.ContentProviderResult[] applyBatch(java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException;
     method public void attachInfo(android.content.Context, android.content.pm.ProviderInfo);
@@ -4481,6 +4505,7 @@
     method public void onConfigurationChanged(android.content.res.Configuration);
     method public abstract boolean onCreate();
     method public void onLowMemory();
+    method public void onTrimMemory(int);
     method public android.content.res.AssetFileDescriptor openAssetFile(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
     method public android.os.ParcelFileDescriptor openFile(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
     method protected final android.os.ParcelFileDescriptor openFileHelper(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
@@ -4734,6 +4759,7 @@
     method public abstract android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
     method public abstract android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory, android.database.DatabaseErrorHandler);
     method public abstract deprecated android.graphics.drawable.Drawable peekWallpaper();
+    method public void registerComponentCallbacks(android.content.ComponentCallbacks);
     method public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter);
     method public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler);
     method public abstract void removeStickyBroadcast(android.content.Intent);
@@ -4754,15 +4780,21 @@
     method public abstract android.content.ComponentName startService(android.content.Intent);
     method public abstract boolean stopService(android.content.Intent);
     method public abstract void unbindService(android.content.ServiceConnection);
+    method public void unregisterComponentCallbacks(android.content.ComponentCallbacks);
     method public abstract void unregisterReceiver(android.content.BroadcastReceiver);
     field public static final java.lang.String ACCESSIBILITY_SERVICE = "accessibility";
     field public static final java.lang.String ACCOUNT_SERVICE = "account";
     field public static final java.lang.String ACTIVITY_SERVICE = "activity";
     field public static final java.lang.String ALARM_SERVICE = "alarm";
     field public static final java.lang.String AUDIO_SERVICE = "audio";
+    field public static final int BIND_ABOVE_CLIENT = 8; // 0x8
+    field public static final int BIND_ADJUST_WITH_ACTIVITY = 64; // 0x40
+    field public static final int BIND_ALLOW_OOM_MANAGEMENT = 16; // 0x10
     field public static final int BIND_AUTO_CREATE = 1; // 0x1
     field public static final int BIND_DEBUG_UNBIND = 2; // 0x2
+    field public static final int BIND_IMPORTANT = 64; // 0x40
     field public static final int BIND_NOT_FOREGROUND = 4; // 0x4
+    field public static final int BIND_WAIVE_PRIORITY = 32; // 0x20
     field public static final java.lang.String CLIPBOARD_SERVICE = "clipboard";
     field public static final java.lang.String CONNECTIVITY_SERVICE = "connectivity";
     field public static final int CONTEXT_IGNORE_SECURITY = 2; // 0x2
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index fb1570e..d5b669e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -19,7 +19,7 @@
 import com.android.internal.app.ActionBarImpl;
 import com.android.internal.policy.PolicyManager;
 
-import android.content.ComponentCallbacks;
+import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -626,7 +626,7 @@
 public class Activity extends ContextThemeWrapper
         implements LayoutInflater.Factory2,
         Window.Callback, KeyEvent.Callback,
-        OnCreateContextMenuListener, ComponentCallbacks {
+        OnCreateContextMenuListener, ComponentCallbacks2 {
     private static final String TAG = "Activity";
 
     /** Standard activity result: operation canceled. */
@@ -859,6 +859,7 @@
                     ? mLastNonConfigurationInstances.fragments : null);
         }
         mFragments.dispatchCreate();
+        getApplication().dispatchActivityCreated(this, savedInstanceState);
         mCalled = true;
     }
 
@@ -1001,6 +1002,8 @@
             }
             mCheckedForLoaderManager = true;
         }
+
+        getApplication().dispatchActivityStarted(this);
     }
 
     /**
@@ -1048,6 +1051,7 @@
      * @see #onPause
      */
     protected void onResume() {
+        getApplication().dispatchActivityResumed(this);
         mCalled = true;
     }
 
@@ -1158,6 +1162,7 @@
         if (p != null) {
             outState.putParcelable(FRAGMENTS_TAG, p);
         }
+        getApplication().dispatchActivitySaveInstanceState(this, outState);
     }
 
     /**
@@ -1234,6 +1239,7 @@
      * @see #onStop
      */
     protected void onPause() {
+        getApplication().dispatchActivityPaused(this);
         mCalled = true;
     }
 
@@ -1320,6 +1326,7 @@
      */
     protected void onStop() {
         if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false);
+        getApplication().dispatchActivityStopped(this);
         mCalled = true;
     }
 
@@ -1382,6 +1389,8 @@
         if (mSearchManager != null) {
             mSearchManager.stopSearch();
         }
+
+        getApplication().dispatchActivityDestroyed(this);
     }
 
     /**
@@ -1580,12 +1589,17 @@
         nci.loaders = mAllLoaderManagers;
         return nci;
     }
-    
+
     public void onLowMemory() {
         mCalled = true;
         mFragments.dispatchLowMemory();
     }
-    
+
+    public void onTrimMemory(int level) {
+        mCalled = true;
+        mFragments.dispatchTrimMemory(level);
+    }
+
     /**
      * Return the FragmentManager for interacting with fragments associated
      * with this activity.
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1e93f88..8931675 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -18,7 +18,7 @@
 
 import android.app.backup.BackupAgent;
 import android.content.BroadcastReceiver;
-import android.content.ComponentCallbacks;
+import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
 import android.content.ContentProvider;
 import android.content.Context;
@@ -3258,10 +3258,10 @@
         }
     }
 
-    ArrayList<ComponentCallbacks> collectComponentCallbacksLocked(
+    ArrayList<ComponentCallbacks2> collectComponentCallbacksLocked(
             boolean allActivities, Configuration newConfig) {
-        ArrayList<ComponentCallbacks> callbacks
-                = new ArrayList<ComponentCallbacks>();
+        ArrayList<ComponentCallbacks2> callbacks
+                = new ArrayList<ComponentCallbacks2>();
 
         if (mActivities.size() > 0) {
             Iterator<ActivityClientRecord> it = mActivities.values().iterator();
@@ -3311,10 +3311,10 @@
         return callbacks;
     }
 
-    private void performConfigurationChanged(
-            ComponentCallbacks cb, Configuration config) {
+    private final void performConfigurationChanged(
+            ComponentCallbacks2 cb, Configuration config) {
         // Only for Activity objects, check that they actually call up to their
-        // superclass implementation.  ComponentCallbacks is an interface, so
+        // superclass implementation.  ComponentCallbacks2 is an interface, so
         // we check the runtime type and act accordingly.
         Activity activity = (cb instanceof Activity) ? (Activity) cb : null;
         if (activity != null) {
@@ -3418,7 +3418,7 @@
     
     final void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) {
 
-        ArrayList<ComponentCallbacks> callbacks = null;
+        ArrayList<ComponentCallbacks2> callbacks = null;
 
         synchronized (mPackages) {
             if (mPendingConfiguration != null) {
@@ -3558,7 +3558,7 @@
     }
         
     final void handleLowMemory() {
-        ArrayList<ComponentCallbacks> callbacks;
+        ArrayList<ComponentCallbacks2> callbacks;
 
         synchronized (mPackages) {
             callbacks = collectComponentCallbacksLocked(true, null);
@@ -3583,6 +3583,16 @@
 
     final void handleTrimMemory(int level) {
         WindowManagerImpl.getDefault().trimMemory(level);
+        ArrayList<ComponentCallbacks2> callbacks;
+
+        synchronized (mPackages) {
+            callbacks = collectComponentCallbacksLocked(true, null);
+        }
+
+        final int N = callbacks.size();
+        for (int i=0; i<N; i++) {
+            callbacks.get(i).onTrimMemory(level);
+        }
     }
 
     private void handleBindApplication(AppBindData data) {
@@ -4128,7 +4138,7 @@
             }
         }
         
-        ViewRootImpl.addConfigCallback(new ComponentCallbacks() {
+        ViewRootImpl.addConfigCallback(new ComponentCallbacks2() {
             public void onConfigurationChanged(Configuration newConfig) {
                 synchronized (mPackages) {
                     // We need to apply this change to the resources
@@ -4148,6 +4158,8 @@
             }
             public void onLowMemory() {
             }
+            public void onTrimMemory(int level) {
+            }
         });
     }
 
diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java
index 10cc9f8..dd9ea26 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -16,10 +16,14 @@
 
 package android.app;
 
+import java.util.ArrayList;
+
 import android.content.ComponentCallbacks;
+import android.content.ComponentCallbacks2;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.res.Configuration;
+import android.os.Bundle;
 
 /**
  * Base class for those who need to maintain global application state. You can
@@ -36,10 +40,25 @@
  * {@link android.content.Context#getApplicationContext() Context.getApplicationContext()}
  * when first constructing the singleton.</p>
  */
-public class Application extends ContextWrapper implements ComponentCallbacks {
+public class Application extends ContextWrapper implements ComponentCallbacks2 {
+    private ArrayList<ComponentCallbacks> mComponentCallbacks =
+            new ArrayList<ComponentCallbacks>();
+    private ArrayList<ActivityLifecycleCallbacks> mActivityLifecycleCallbacks =
+            new ArrayList<ActivityLifecycleCallbacks>();
+
     /** @hide */
     public LoadedApk mLoadedApk;
-    
+
+    public interface ActivityLifecycleCallbacks {
+        void onActivityCreated(Activity activity, Bundle savedInstanceState);
+        void onActivityStarted(Activity activity);
+        void onActivityResumed(Activity activity);
+        void onActivityPaused(Activity activity);
+        void onActivityStopped(Activity activity);
+        void onActivitySaveInstanceState(Activity activity, Bundle outState);
+        void onActivityDestroyed(Activity activity);
+    }
+
     public Application() {
         super(null);
     }
@@ -63,11 +82,59 @@
      */
     public void onTerminate() {
     }
-    
+
     public void onConfigurationChanged(Configuration newConfig) {
+        Object[] callbacks = collectComponentCallbacks();
+        if (callbacks != null) {
+            for (int i=0; i<callbacks.length; i++) {
+                ((ComponentCallbacks)callbacks[i]).onConfigurationChanged(newConfig);
+            }
+        }
     }
-    
+
     public void onLowMemory() {
+        Object[] callbacks = collectComponentCallbacks();
+        if (callbacks != null) {
+            for (int i=0; i<callbacks.length; i++) {
+                ((ComponentCallbacks)callbacks[i]).onLowMemory();
+            }
+        }
+    }
+
+    public void onTrimMemory(int level) {
+        Object[] callbacks = collectComponentCallbacks();
+        if (callbacks != null) {
+            for (int i=0; i<callbacks.length; i++) {
+                Object c = callbacks[i];
+                if (c instanceof ComponentCallbacks2) {
+                    ((ComponentCallbacks2)c).onTrimMemory(level);
+                }
+            }
+        }
+    }
+
+    public void registerComponentCallbacks(ComponentCallbacks callback) {
+        synchronized (mComponentCallbacks) {
+            mComponentCallbacks.add(callback);
+        }
+    }
+
+    public void unregisterComponentCallbacks(ComponentCallbacks callback) {
+        synchronized (mComponentCallbacks) {
+            mComponentCallbacks.remove(callback);
+        }
+    }
+
+    public void registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) {
+        synchronized (mActivityLifecycleCallbacks) {
+            mActivityLifecycleCallbacks.add(callback);
+        }
+    }
+
+    public void unregisterActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) {
+        synchronized (mActivityLifecycleCallbacks) {
+            mActivityLifecycleCallbacks.remove(callback);
+        }
     }
     
     // ------------------ Internal API ------------------
@@ -79,4 +146,89 @@
         attachBaseContext(context);
         mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
     }
+
+    /* package */ void dispatchActivityCreated(Activity activity, Bundle savedInstanceState) {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i=0; i<callbacks.length; i++) {
+                ((ActivityLifecycleCallbacks)callbacks[i]).onActivityCreated(activity,
+                        savedInstanceState);
+            }
+        }
+    }
+
+    /* package */ void dispatchActivityStarted(Activity activity) {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i=0; i<callbacks.length; i++) {
+                ((ActivityLifecycleCallbacks)callbacks[i]).onActivityStarted(activity);
+            }
+        }
+    }
+
+    /* package */ void dispatchActivityResumed(Activity activity) {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i=0; i<callbacks.length; i++) {
+                ((ActivityLifecycleCallbacks)callbacks[i]).onActivityResumed(activity);
+            }
+        }
+    }
+
+    /* package */ void dispatchActivityPaused(Activity activity) {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i=0; i<callbacks.length; i++) {
+                ((ActivityLifecycleCallbacks)callbacks[i]).onActivityPaused(activity);
+            }
+        }
+    }
+
+    /* package */ void dispatchActivityStopped(Activity activity) {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i=0; i<callbacks.length; i++) {
+                ((ActivityLifecycleCallbacks)callbacks[i]).onActivityStopped(activity);
+            }
+        }
+    }
+
+    /* package */ void dispatchActivitySaveInstanceState(Activity activity, Bundle outState) {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i=0; i<callbacks.length; i++) {
+                ((ActivityLifecycleCallbacks)callbacks[i]).onActivitySaveInstanceState(activity,
+                        outState);
+            }
+        }
+    }
+
+    /* package */ void dispatchActivityDestroyed(Activity activity) {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i=0; i<callbacks.length; i++) {
+                ((ActivityLifecycleCallbacks)callbacks[i]).onActivityDestroyed(activity);
+            }
+        }
+    }
+
+    private Object[] collectComponentCallbacks() {
+        Object[] callbacks = null;
+        synchronized (mComponentCallbacks) {
+            if (mComponentCallbacks.size() > 0) {
+                callbacks = mComponentCallbacks.toArray();
+            }
+        }
+        return callbacks;
+    }
+
+    private Object[] collectActivityLifecycleCallbacks() {
+        Object[] callbacks = null;
+        synchronized (mActivityLifecycleCallbacks) {
+            if (mActivityLifecycleCallbacks.size() > 0) {
+                callbacks = mActivityLifecycleCallbacks.toArray();
+            }
+        }
+        return callbacks;
+    }
 }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index a99cec2..b4bdb2f 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1109,6 +1109,12 @@
             throw new RuntimeException("Not supported in system context");
         }
         try {
+            IBinder token = getActivityToken();
+            if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
+                    && mPackageInfo.getApplicationInfo().targetSdkVersion
+                    < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+                flags |= BIND_WAIVE_PRIORITY;
+            }
             int res = ActivityManagerNative.getDefault().bindService(
                 mMainThread.getApplicationThread(), getActivityToken(),
                 service, service.resolveTypeIfNeeded(getContentResolver()),
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index e2746d4..371e7ad 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -17,7 +17,7 @@
 package android.app;
 
 import android.animation.Animator;
-import android.content.ComponentCallbacks;
+import android.content.ComponentCallbacks2;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
@@ -332,7 +332,7 @@
  * pressing back will pop it to return the user to whatever previous state
  * the activity UI was in.
  */
-public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener {
+public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListener {
     private static final HashMap<String, Class<?>> sClassMap =
             new HashMap<String, Class<?>>();
     
@@ -1182,6 +1182,10 @@
         mCalled = true;
     }
     
+    public void onTrimMemory(int level) {
+        mCalled = true;
+    }
+
     /**
      * Called when the view previously created by {@link #onCreateView} has
      * been detached from the fragment.  The next time the fragment needs
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 24550c5..c33ab2c 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -1722,6 +1722,17 @@
         }
     }
 
+    public void dispatchTrimMemory(int level) {
+        if (mActive != null) {
+            for (int i=0; i<mAdded.size(); i++) {
+                Fragment f = mAdded.get(i);
+                if (f != null) {
+                    f.onTrimMemory(level);
+                }
+            }
+        }
+    }
+
     public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
         boolean show = false;
         ArrayList<Fragment> newMenus = null;
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 4c21d04..ebde6e0 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -16,7 +16,7 @@
 
 package android.app;
 
-import android.content.ComponentCallbacks;
+import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.ContextWrapper;
@@ -274,7 +274,7 @@
  * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/MessengerServiceActivities.java
  *      bind}
  */
-public abstract class Service extends ContextWrapper implements ComponentCallbacks {
+public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {
     private static final String TAG = "Service";
 
     public Service() {
@@ -451,7 +451,10 @@
     
     public void onLowMemory() {
     }
-    
+
+    public void onTrimMemory(int level) {
+    }
+
     /**
      * Return the communication channel to the service.  May return null if 
      * clients can not bind to the service.  The returned
diff --git a/core/java/android/content/ComponentCallbacks.java b/core/java/android/content/ComponentCallbacks.java
index 37cc141..dad60b0 100644
--- a/core/java/android/content/ComponentCallbacks.java
+++ b/core/java/android/content/ComponentCallbacks.java
@@ -51,13 +51,4 @@
      * The system will perform a gc for you after returning from this method.
      */
     void onLowMemory();
-
-    /** @hide */
-    static final int TRIM_MEMORY_COMPLETE = 80;
-
-    /** @hide */
-    static final int TRIM_MEMORY_MODERATE = 50;
-
-    /** @hide */
-    static final int TRIM_MEMORY_BACKGROUND = 20;
 }
diff --git a/core/java/android/content/ComponentCallbacks2.java b/core/java/android/content/ComponentCallbacks2.java
new file mode 100644
index 0000000..8b9f97c
--- /dev/null
+++ b/core/java/android/content/ComponentCallbacks2.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+/**
+ * Extended {@link ComponentCallbacks} interface with a new callback for
+ * finer-grained memory management.
+ */
+public interface ComponentCallbacks2 extends ComponentCallbacks {
+
+    /**
+     * Level for {@link #onTrimMemory(int)}: the process is nearing the end
+     * of the background LRU list, and if more memory isn't found soon it will
+     * be killed.
+     */
+    static final int TRIM_MEMORY_COMPLETE = 80;
+    
+    /**
+     * Level for {@link #onTrimMemory(int)}: the process is around the middle
+     * of the background LRU list; freeing memory can help the system keep
+     * other processes running later in the list for better overall performance.
+     */
+    static final int TRIM_MEMORY_MODERATE = 60;
+    
+    /**
+     * Level for {@link #onTrimMemory(int)}: the process has gone on to the
+     * LRU list.  This is a good opportunity to clean up resources that can
+     * efficiently and quickly be re-built if the user returns to the app.
+     */
+    static final int TRIM_MEMORY_BACKGROUND = 40;
+    
+    /**
+     * Level for {@link #onTrimMemory(int)}: the process had been showing
+     * a user interface, and is no longer doing so.  Large allocations with
+     * the UI should be released at this point to allow memory to be better
+     * managed.
+     */
+    static final int TRIM_MEMORY_UI_HIDDEN = 20;
+
+    /**
+     * Called when the operating system has determined that it is a good
+     * time for a process to trim unneeded memory from its process.  This will
+     * happen for example when it goes in the background and there is not enough
+     * memory to keep as many background processes running as desired.
+     * 
+     * @param level The context of the trim, giving a hint of the amount of
+     * trimming the application may like to perform.  May be
+     * {@link #TRIM_MEMORY_COMPLETE}, {@link #TRIM_MEMORY_MODERATE},
+     * {@link #TRIM_MEMORY_BACKGROUND}, or {@link #TRIM_MEMORY_UI_HIDDEN}.
+     */
+    void onTrimMemory(int level);
+}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 1a5c675..8057d4b 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -78,7 +78,7 @@
  * ContentProvider instance, so subclasses don't have to worry about the details of
  * cross-process calls.</p>
  */
-public abstract class ContentProvider implements ComponentCallbacks {
+public abstract class ContentProvider implements ComponentCallbacks2 {
     private static final String TAG = "ContentProvider";
 
     /*
@@ -491,6 +491,9 @@
     public void onLowMemory() {
     }
 
+    public void onTrimMemory(int level) {
+    }
+
     /**
      * Implement this to handle query requests from clients.
      * This method can be called from multiple threads, as described in
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2a02446..46712a9 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -107,12 +107,17 @@
      * this still provides you with access to the service object while the
      * service is created.
      *
-     * <p>Specifying this flag also tells the system to treat the service
-     * as being as important as your own process -- that is, when deciding
-     * which process should be killed to free memory, the service will only
-     * be considered a candidate as long as the processes of any such bindings
-     * is also a candidate to be killed.  This is to avoid situations where
-     * the service is being continually created and killed due to low memory.
+     * <p>Note that prior to {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH},
+     * not supplying this flag would also impact how important the system
+     * consider's the target service's process to be.  When set, the only way
+     * for it to be raised was by binding from a service in which case it will
+     * only be important when that activity is in the foreground.  Now to
+     * achieve this behavior you must explicitly supply the new flag
+     * {@link #BIND_ADJUST_WITH_ACTIVITY}.  For compatibility, old applications
+     * that don't specify {@link #BIND_AUTO_CREATE} will automatically have
+     * the flags {@link #BIND_WAIVE_PRIORITY} and
+     * {@link #BIND_ADJUST_WITH_ACTIVITY} set for them in order to achieve
+     * the same result.
      */
     public static final int BIND_AUTO_CREATE = 0x0001;
 
@@ -139,14 +144,48 @@
     public static final int BIND_NOT_FOREGROUND = 0x0004;
 
     /**
+     * Flag for {@link #bindService}: indicates that the client application
+     * binding to this service considers the service to be more important than
+     * the app itself.  When set, the platform will try to have the out of
+     * memory kill the app before it kills the service it is bound to, though
+     * this is not guaranteed to be the case.
+     */
+    public static final int BIND_ABOVE_CLIENT = 0x0008;
+
+    /**
      * Flag for {@link #bindService}: allow the process hosting the bound
      * service to go through its normal memory management.  It will be
      * treated more like a running service, allowing the system to
      * (temporarily) expunge the process if low on memory or for some other
-     * whim it may have.
-     * @hide
+     * whim it may have, and being more aggressive about making it a candidate
+     * to be killed (and restarted) if running for a long time.
      */
-    public static final int BIND_ALLOW_OOM_MANAGEMENT = 0x0008;
+    public static final int BIND_ALLOW_OOM_MANAGEMENT = 0x0010;
+
+    /**
+     * Flag for {@link #bindService}: don't impact the scheduling or
+     * memory management priority of the target service's hosting process.
+     * Allows the service's process to be managed on the background LRU list
+     * just like a regular application process in the background.
+     */
+    public static final int BIND_WAIVE_PRIORITY = 0x0020;
+
+    /**
+     * Flag for {@link #bindService}: this service is very important to
+     * the client, so should be brought to the foreground process level
+     * when the client is.  Normally a process can only be raised to the
+     * visibility level by a client, even if that client is in the foreground.
+     */
+    public static final int BIND_IMPORTANT = 0x0040;
+
+    /**
+     * Flag for {@link #bindService}: If binding from an activity, allow the
+     * target service's process importance to be raised based on whether the
+     * activity is visible to the user, regardless whether another flag is
+     * used to reduce the amount that the client process's overall importance
+     * is used to impact it.
+     */
+    public static final int BIND_ADJUST_WITH_ACTIVITY = 0x0040;
 
     /** Return an AssetManager instance for your application's package. */
     public abstract AssetManager getAssets();
@@ -195,6 +234,25 @@
     public abstract Context getApplicationContext();
 
     /**
+     * Add a new {@link ComponentCallbacks} to the base application of the
+     * Context, which will be called at the same times as the ComponentCallbacks
+     * methods of activities and other components are called.  Note that you
+     * <em>must</em> be sure to use {@link #unregisterComponentCallbacks} when
+     * appropriate in the future; this will not be removed for you.
+     */
+    public void registerComponentCallbacks(ComponentCallbacks callback) {
+        getApplicationContext().registerComponentCallbacks(callback);
+    }
+
+    /**
+     * Remove a {@link ComponentCallbacks} objec that was previously registered
+     * with {@link #registerComponentCallbacks(ComponentCallbacks)}.
+     */
+    public void unregisterComponentCallbacks(ComponentCallbacks callback) {
+        getApplicationContext().unregisterComponentCallbacks(callback);
+    }
+
+    /**
      * Return a localized, styled CharSequence from the application's package's
      * default string table.
      *
@@ -1219,8 +1277,10 @@
      *      {@link IntentFilter} published by a service.
      * @param conn Receives information as the service is started and stopped.
      * @param flags Operation options for the binding.  May be 0,
-     *          {@link #BIND_AUTO_CREATE}, {@link #BIND_DEBUG_UNBIND}, or
-     *          {@link #BIND_NOT_FOREGROUND}.
+     *          {@link #BIND_AUTO_CREATE}, {@link #BIND_DEBUG_UNBIND},
+     *          {@link #BIND_NOT_FOREGROUND}, {@link #BIND_ABOVE_CLIENT},
+     *          {@link #BIND_ALLOW_OOM_MANAGEMENT}, or
+     *          {@link #BIND_WAIVE_PRIORITY}.
      * @return If you have successfully bound to the service, true is returned;
      *         false is returned if the connection is not made so you will not
      *         receive the service object.
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 4e4923b..4e20358 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -17,7 +17,7 @@
 
 package android.view;
 
-import android.content.ComponentCallbacks;
+import android.content.ComponentCallbacks2;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
@@ -975,10 +975,10 @@
             }
 
             switch (level) {
-                case ComponentCallbacks.TRIM_MEMORY_MODERATE:
+                case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
                     GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_MODERATE);
                     break;
-                case ComponentCallbacks.TRIM_MEMORY_COMPLETE:
+                case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
                     GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_FULL);
                     break;
             }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0bd5a2a..b22ab7e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -21,6 +21,7 @@
 import android.app.ActivityManagerNative;
 import android.content.ClipDescription;
 import android.content.ComponentCallbacks;
+import android.content.ComponentCallbacks2;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.CompatibilityInfo;
@@ -554,7 +555,7 @@
         if (mThread != Thread.currentThread()) {
             if (mAttachInfo.mHardwareRenderer != null &&
                     mAttachInfo.mHardwareRenderer.isEnabled()) {
-                HardwareRenderer.trimMemory(ComponentCallbacks.TRIM_MEMORY_MODERATE);
+                HardwareRenderer.trimMemory(ComponentCallbacks2.TRIM_MEMORY_MODERATE);
             }
         } else {
             if (mAttachInfo.mHardwareRenderer != null &&
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 66f88fc..14c6306 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -53,7 +53,7 @@
 import android.app.backup.IBackupManager;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
-import android.content.ComponentCallbacks;
+import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -8067,6 +8067,21 @@
 
             if (needSep) pw.println(" ");
             needSep = true;
+            pw.println("  OOM levels:");
+            pw.print("    SYSTEM_ADJ: "); pw.println(SYSTEM_ADJ);
+            pw.print("    CORE_SERVER_ADJ: "); pw.println(CORE_SERVER_ADJ);
+            pw.print("    FOREGROUND_APP_ADJ: "); pw.println(FOREGROUND_APP_ADJ);
+            pw.print("    VISIBLE_APP_ADJ: "); pw.println(VISIBLE_APP_ADJ);
+            pw.print("    PERCEPTIBLE_APP_ADJ: "); pw.println(PERCEPTIBLE_APP_ADJ);
+            pw.print("    HEAVY_WEIGHT_APP_ADJ: "); pw.println(HEAVY_WEIGHT_APP_ADJ);
+            pw.print("    BACKUP_APP_ADJ: "); pw.println(BACKUP_APP_ADJ);
+            pw.print("    SECONDARY_SERVER_ADJ: "); pw.println(SECONDARY_SERVER_ADJ);
+            pw.print("    HOME_APP_ADJ: "); pw.println(HOME_APP_ADJ);
+            pw.print("    HIDDEN_APP_MIN_ADJ: "); pw.println(HIDDEN_APP_MIN_ADJ);
+            pw.print("    EMPTY_APP_ADJ: "); pw.println(EMPTY_APP_ADJ);
+
+            if (needSep) pw.println(" ");
+            needSep = true;
             pw.println("  Process OOM control:");
             dumpProcessOomList(pw, this, procs, "    ",
                     "Proc", "PERS", true);
@@ -8814,7 +8829,8 @@
                 pw.print("    ");
                 pw.print("keeping="); pw.print(r.keeping);
                 pw.print(" hidden="); pw.print(r.hidden);
-                pw.print(" empty="); pw.println(r.empty);
+                pw.print(" empty="); pw.print(r.empty);
+                pw.print(" hasAboveClient="); pw.println(r.hasAboveClient);
 
                 if (!r.keeping) {
                     if (r.lastWakeTime != 0) {
@@ -9226,6 +9242,7 @@
         app.foregroundServices = false;
         app.foregroundActivities = false;
         app.hasShownUi = false;
+        app.hasAboveClient = false;
 
         killServicesLocked(app, allowRestart);
 
@@ -10452,6 +10469,9 @@
                 activity.connections.add(c);
             }
             b.client.connections.add(c);
+            if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
+                b.client.hasAboveClient = true;
+            }
             clist = mServiceConnections.get(binder);
             if (clist == null) {
                 clist = new ArrayList<ConnectionRecord>();
@@ -10523,6 +10543,9 @@
         }
         if (b.client != skipApp) {
             b.client.connections.remove(c);
+            if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
+                b.client.updateHasAboveClientLocked();
+            }
         }
         clist = mServiceConnections.get(binder);
         if (clist != null) {
@@ -12577,9 +12600,9 @@
             // an earlier hidden adjustment that isn't really for us... if
             // so, use the new hidden adjustment.
             if (!recursed && app.hidden) {
-                app.curAdj = hiddenAdj;
+                app.curAdj = app.curRawAdj = hiddenAdj;
             }
-            return app.curAdj;
+            return app.curRawAdj;
         }
 
         if (app.thread == null) {
@@ -12588,28 +12611,47 @@
             return (app.curAdj=EMPTY_APP_ADJ);
         }
 
+        app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;
+        app.adjSource = null;
+        app.adjTarget = null;
+        app.empty = false;
+        app.hidden = false;
+
+        final int activitiesSize = app.activities.size();
+
         if (app.maxAdj <= FOREGROUND_APP_ADJ) {
             // The max adjustment doesn't allow this app to be anything
             // below foreground, so it is not worth doing work for it.
             app.adjType = "fixed";
             app.adjSeq = mAdjSeq;
             app.curRawAdj = app.maxAdj;
+            app.foregroundActivities = false;
             app.keeping = true;
             app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
+            // System process can do UI, and when they do we want to have
+            // them trim their memory after the user leaves the UI.  To
+            // facilitate this, here we need to determine whether or not it
+            // is currently showing UI.
+            app.systemNoUi = true;
+            if (app == TOP_APP) {
+                app.systemNoUi = false;
+            } else if (activitiesSize > 0) {
+                for (int j = 0; j < activitiesSize; j++) {
+                    final ActivityRecord r = app.activities.get(j);
+                    if (r.visible) {
+                        app.systemNoUi = false;
+                        break;
+                    }
+                }
+            }
             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();
+        app.keeping = false;
+        app.systemNoUi = false;
 
         // Determine the importance of the process, starting with most
         // important to least, and assign an appropriate OOM adjustment.
@@ -12784,7 +12826,7 @@
                                 // Binding to ourself is not interesting.
                                 continue;
                             }
-                            if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) {
+                            if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
                                 ProcessRecord client = cr.binding.client;
                                 int clientAdj = adj;
                                 int myHiddenAdj = hiddenAdj;
@@ -12825,15 +12867,32 @@
                                     }
                                 }
                                 if (adj > clientAdj) {
-                                    adj = clientAdj >= VISIBLE_APP_ADJ
-                                            ? clientAdj : VISIBLE_APP_ADJ;
-                                    if (!client.hidden) {
-                                        app.hidden = false;
+                                    // If this process has recently shown UI, and
+                                    // the process that is binding to it is less
+                                    // important than being visible, then we don't
+                                    // care about the binding as much as we care
+                                    // about letting this process get into the LRU
+                                    // list to be killed and restarted if needed for
+                                    // memory.
+                                    if (app.hasShownUi && clientAdj > PERCEPTIBLE_APP_ADJ) {
+                                        adjType = "bound-bg-ui-services";
+                                    } else {
+                                        if ((cr.flags&(Context.BIND_ABOVE_CLIENT
+                                                |Context.BIND_IMPORTANT)) != 0) {
+                                            adj = clientAdj;
+                                        } else if (clientAdj >= VISIBLE_APP_ADJ) {
+                                            adj = clientAdj;
+                                        } else {
+                                            adj = VISIBLE_APP_ADJ;
+                                        }
+                                        if (!client.hidden) {
+                                            app.hidden = false;
+                                        }
+                                        if (client.keeping) {
+                                            app.keeping = true;
+                                        }
+                                        adjType = "service";
                                     }
-                                    if (client.keeping) {
-                                        app.keeping = true;
-                                    }
-                                    adjType = "service";
                                 }
                                 if (adjType != null) {
                                     app.adjType = adjType;
@@ -12848,21 +12907,22 @@
                                     }
                                 }
                             }
-                            ActivityRecord a = cr.activity;
-                            //if (a != null) {
-                            //    Slog.i(TAG, "Connection to " + a ": state=" + a.state);
-                            //}
-                            if (a != null && adj > FOREGROUND_APP_ADJ &&
-                                    (a.state == ActivityState.RESUMED
-                                     || a.state == ActivityState.PAUSING)) {
-                                adj = FOREGROUND_APP_ADJ;
-                                schedGroup = Process.THREAD_GROUP_DEFAULT;
-                                app.hidden = false;
-                                app.adjType = "service";
-                                app.adjTypeCode = ActivityManager.RunningAppProcessInfo
-                                        .REASON_SERVICE_IN_USE;
-                                app.adjSource = a;
-                                app.adjTarget = s.name;
+                            if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
+                                ActivityRecord a = cr.activity;
+                                if (a != null && adj > FOREGROUND_APP_ADJ &&
+                                        (a.visible || a.state == ActivityState.RESUMED
+                                         || a.state == ActivityState.PAUSING)) {
+                                    adj = FOREGROUND_APP_ADJ;
+                                    if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
+                                        schedGroup = Process.THREAD_GROUP_DEFAULT;
+                                    }
+                                    app.hidden = false;
+                                    app.adjType = "service";
+                                    app.adjTypeCode = ActivityManager.RunningAppProcessInfo
+                                            .REASON_SERVICE_IN_USE;
+                                    app.adjSource = a;
+                                    app.adjTarget = s.name;
+                                }
                             }
                         }
                     }
@@ -12906,15 +12966,19 @@
                         int clientAdj = computeOomAdjLocked(
                             client, myHiddenAdj, TOP_APP, true);
                         if (adj > clientAdj) {
-                            adj = clientAdj > FOREGROUND_APP_ADJ
-                                    ? clientAdj : FOREGROUND_APP_ADJ;
+                            if (app.hasShownUi && clientAdj > PERCEPTIBLE_APP_ADJ) {
+                                app.adjType = "bg-ui-provider";
+                            } else {
+                                adj = clientAdj > FOREGROUND_APP_ADJ
+                                        ? clientAdj : FOREGROUND_APP_ADJ;
+                                app.adjType = "provider";
+                            }
                             if (!client.hidden) {
                                 app.hidden = false;
                             }
                             if (client.keeping) {
                                 app.keeping = true;
                             }
-                            app.adjType = "provider";
                             app.adjTypeCode = ActivityManager.RunningAppProcessInfo
                                     .REASON_PROVIDER_IN_USE;
                             app.adjSource = client;
@@ -12955,6 +13019,25 @@
             app.keeping = true;
         }
 
+        if (app.hasAboveClient) {
+            // If this process has bound to any services with BIND_ABOVE_CLIENT,
+            // then we need to drop its adjustment to be lower than the service's
+            // in order to honor the request.  We want to drop it by one adjustment
+            // level...  but there is special meaning applied to various levels so
+            // we will skip some of them.
+            if (adj < FOREGROUND_APP_ADJ) {
+                // System process will not get dropped, ever
+            } else if (adj < VISIBLE_APP_ADJ) {
+                adj = VISIBLE_APP_ADJ;
+            } else if (adj < PERCEPTIBLE_APP_ADJ) {
+                adj = PERCEPTIBLE_APP_ADJ;
+            } else if (adj < HIDDEN_APP_MIN_ADJ) {
+                adj = HIDDEN_APP_MIN_ADJ;
+            } else if (adj < EMPTY_APP_ADJ) {
+                adj++;
+            }
+        }
+
         app.curAdj = adj;
         app.curSchedGroup = schedGroup;
 
@@ -12963,7 +13046,7 @@
                     app.foregroundActivities).sendToTarget();
         }
 
-        return adj;
+        return app.curRawAdj;
     }
 
     /**
@@ -13204,7 +13287,7 @@
 
         final boolean wasKeeping = app.keeping;
 
-        int adj = computeOomAdjLocked(app, hiddenAdj, TOP_APP, false);
+        computeOomAdjLocked(app, hiddenAdj, TOP_APP, false);
 
         if (app.curRawAdj != app.setRawAdj) {
             if (app.curRawAdj > FOREGROUND_APP_ADJ
@@ -13233,14 +13316,14 @@
 
             app.setRawAdj = app.curRawAdj;
         }
-        if (adj != app.setAdj) {
-            if (Process.setOomAdj(app.pid, adj)) {
+        if (app.curAdj != app.setAdj) {
+            if (Process.setOomAdj(app.pid, app.curAdj)) {
                 if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(
                     TAG, "Set app " + app.processName +
-                    " oom adj to " + adj + " because " + app.adjType);
-                app.setAdj = adj;
+                    " oom adj to " + app.curAdj + " because " + app.adjType);
+                app.setAdj = app.curAdj;
             } else {
-                Slog.w(TAG, "Failed setting oom adj of " + app + " to " + adj);
+                Slog.w(TAG, "Failed setting oom adj of " + app + " to " + app.curAdj);
             }
         }
         if (app.setSchedGroup != app.curSchedGroup) {
@@ -13377,7 +13460,7 @@
             final int N = mLruProcesses.size();
             factor = numBg/3;
             step = 0;
-            int curLevel = ComponentCallbacks.TRIM_MEMORY_COMPLETE;
+            int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
             for (i=0; i<N; i++) {
                 ProcessRecord app = mLruProcesses.get(i);
                 if (app.curAdj >= HIDDEN_APP_MIN_ADJ && !app.killedBackground) {
@@ -13386,7 +13469,7 @@
                             app.thread.scheduleTrimMemory(curLevel);
                         } catch (RemoteException e) {
                         }
-                        if (curLevel >= ComponentCallbacks.TRIM_MEMORY_COMPLETE) {
+                        if (curLevel >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
                             // For these apps we will also finish their activities
                             // to help them free memory.
                             mMainStack.destroyActivitiesLocked(app, false);
@@ -13396,23 +13479,36 @@
                     step++;
                     if (step >= factor) {
                         switch (curLevel) {
-                            case ComponentCallbacks.TRIM_MEMORY_COMPLETE:
-                                curLevel = ComponentCallbacks.TRIM_MEMORY_MODERATE;
+                            case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
+                                curLevel = ComponentCallbacks2.TRIM_MEMORY_MODERATE;
                                 break;
-                            case ComponentCallbacks.TRIM_MEMORY_MODERATE:
-                                curLevel = ComponentCallbacks.TRIM_MEMORY_BACKGROUND;
+                            case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
+                                curLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
                                 break;
                         }
                     }
                 } else if (app.curAdj == HEAVY_WEIGHT_APP_ADJ) {
-                    if (app.trimMemoryLevel < ComponentCallbacks.TRIM_MEMORY_BACKGROUND
+                    if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND
                             && app.thread != null) {
                         try {
-                            app.thread.scheduleTrimMemory(ComponentCallbacks.TRIM_MEMORY_BACKGROUND);
+                            app.thread.scheduleTrimMemory(
+                                    ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
                         } catch (RemoteException e) {
                         }
                     }
-                    app.trimMemoryLevel = ComponentCallbacks.TRIM_MEMORY_BACKGROUND;
+                    app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
+                } else if ((app.curAdj > VISIBLE_APP_ADJ || app.systemNoUi)
+                        && app.pendingUiClean) {
+                    if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
+                            && app.thread != null) {
+                        try {
+                            app.thread.scheduleTrimMemory(
+                                    ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
+                        } catch (RemoteException e) {
+                        }
+                    }
+                    app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
+                    app.pendingUiClean = false;
                 } else {
                     app.trimMemoryLevel = 0;
                 }
@@ -13421,7 +13517,21 @@
             final int N = mLruProcesses.size();
             for (i=0; i<N; i++) {
                 ProcessRecord app = mLruProcesses.get(i);
-                app.trimMemoryLevel = 0;
+                if ((app.curAdj > VISIBLE_APP_ADJ || app.systemNoUi)
+                        && app.pendingUiClean) {
+                    if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
+                            && app.thread != null) {
+                        try {
+                            app.thread.scheduleTrimMemory(
+                                    ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
+                        } catch (RemoteException e) {
+                        }
+                    }
+                    app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
+                    app.pendingUiClean = false;
+                } else {
+                    app.trimMemoryLevel = 0;
+                }
             }
         }
 
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index cc58eaf..33b21ab 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -560,6 +560,7 @@
             showAskCompatModeDialogLocked(r);
             r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
             app.hasShownUi = true;
+            app.pendingUiClean = true;
             app.thread.scheduleLaunchActivity(new Intent(r.intent), r,
                     System.identityHashCode(r),
                     r.info, r.compat, r.icicle, results, newIntents, !andResume,
diff --git a/services/java/com/android/server/am/AppBindRecord.java b/services/java/com/android/server/am/AppBindRecord.java
index 9c57360..f1c54fa 100644
--- a/services/java/com/android/server/am/AppBindRecord.java
+++ b/services/java/com/android/server/am/AppBindRecord.java
@@ -26,7 +26,7 @@
 class AppBindRecord {
     final ServiceRecord service;    // The running service.
     final IntentBindRecord intent;  // The intent we are bound to.
-    final ProcessRecord client; // Who has started/bound the service.
+    final ProcessRecord client;     // Who has started/bound the service.
 
     final HashSet<ConnectionRecord> connections = new HashSet<ConnectionRecord>();
                                     // All ConnectionRecord for this client.
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 5b59363..a896ce4 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -23,6 +23,7 @@
 import android.app.IApplicationThread;
 import android.app.IInstrumentationWatcher;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.res.CompatibilityInfo;
 import android.os.Bundle;
@@ -66,7 +67,10 @@
     boolean setIsForeground;    // Running foreground UI when last set?
     boolean foregroundServices; // Running any services that are foreground?
     boolean foregroundActivities; // Running any activities that are foreground?
+    boolean systemNoUi;         // This is a system process, but not currently showing UI.
     boolean hasShownUi;         // Has UI been shown in this process since it was started?
+    boolean pendingUiClean;     // Want to clean up resources from showing UI?
+    boolean hasAboveClient;     // Bound using BIND_ABOVE_CLIENT, so want to be lower
     boolean bad;                // True if disabled in the bad process list
     boolean killedBackground;   // True when proc has been killed due to too many bg
     String waitingToKill;       // Process is waiting to be killed when in the bg; reason
@@ -185,8 +189,11 @@
                 pw.print(" set="); pw.println(setAdj);
         pw.print(prefix); pw.print("curSchedGroup="); pw.print(curSchedGroup);
                 pw.print(" setSchedGroup="); pw.print(setSchedGroup);
+                pw.print(" systemNoUi="); pw.print(systemNoUi);
                 pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel);
-                pw.print(" hasShownUi="); pw.println(hasShownUi);
+        pw.print(prefix); pw.print("hasShownUi="); pw.print(hasShownUi);
+                pw.print(" pendingUiClean="); pw.print(pendingUiClean);
+                pw.print(" hasAboveClient="); pw.println(hasAboveClient);
         pw.print(prefix); pw.print("setIsForeground="); pw.print(setIsForeground);
                 pw.print(" foregroundServices="); pw.print(foregroundServices);
                 pw.print(" forcingToForeground="); pw.println(forcingToForeground);
@@ -307,6 +314,18 @@
         deathRecipient = null;
     }
 
+    void updateHasAboveClientLocked() {
+        hasAboveClient = false;
+        if (connections.size() > 0) {
+            for (ConnectionRecord cr : connections) {
+                if ((cr.flags&Context.BIND_ABOVE_CLIENT) != 0) {
+                    hasAboveClient = true;
+                    break;
+                }
+            }
+        }
+    }
+
     public String toShortString() {
         if (shortStringName != null) {
             return shortStringName;