Reclaim more memory, more often.

Yay.

Change-Id: I04557ad575c307a55088549f48f0e9ad994b7275
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 9bbbd6c..1e93f88 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -123,7 +123,6 @@
     /** @hide */
     public static final String TAG = "ActivityThread";
     private static final android.graphics.Bitmap.Config THUMBNAIL_FORMAT = Bitmap.Config.RGB_565;
-    private static final boolean DEBUG = false;
     static final boolean localLOGV = false;
     static final boolean DEBUG_MESSAGES = false;
     /** @hide */
@@ -163,7 +162,7 @@
             = new ArrayList<Application>();
     // set of instantiated backup agents, keyed by package name
     final HashMap<String, BackupAgent> mBackupAgents = new HashMap<String, BackupAgent>();
-    static final ThreadLocal<ActivityThread> sThreadLocal = new ThreadLocal();
+    static final ThreadLocal<ActivityThread> sThreadLocal = new ThreadLocal<ActivityThread>();
     Instrumentation mInstrumentation;
     String mInstrumentationAppDir = null;
     String mInstrumentationAppPackage = null;
@@ -410,9 +409,9 @@
         CompatibilityInfo info;
     }
     
-    native private void dumpGraphicsInfo(FileDescriptor fd);
+    private native void dumpGraphicsInfo(FileDescriptor fd);
 
-    private final class ApplicationThread extends ApplicationThreadNative {
+    private class ApplicationThread extends ApplicationThreadNative {
         private static final String HEAP_COLUMN = "%13s %8s %8s %8s %8s %8s %8s";
         private static final String ONE_COUNT_COLUMN = "%21s %8d";
         private static final String TWO_COUNT_COLUMNS = "%21s %8d %21s %8d";
@@ -734,13 +733,13 @@
             FileOutputStream fout = new FileOutputStream(fd);
             PrintWriter pw = new PrintWriter(fout);
             try {
-                return dumpMemInfo(fd, pw, args);
+                return dumpMemInfo(pw, args);
             } finally {
                 pw.flush();
             }
         }
 
-        private Debug.MemoryInfo dumpMemInfo(FileDescriptor fd, PrintWriter pw, String[] args) {
+        private Debug.MemoryInfo dumpMemInfo(PrintWriter pw, String[] args) {
             long nativeMax = Debug.getNativeHeapSize() / 1024;
             long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024;
             long nativeFree = Debug.getNativeHeapFreeSize() / 1024;
@@ -754,7 +753,7 @@
             long dalvikFree = runtime.freeMemory() / 1024;
             long dalvikAllocated = dalvikMax - dalvikFree;
             long viewInstanceCount = ViewDebug.getViewInstanceCount();
-            long viewRootInstanceCount = ViewDebug.getViewAncestorInstanceCount();
+            long viewRootInstanceCount = ViewDebug.getViewRootImplCount();
             long appContextInstanceCount = Debug.countInstancesOfClass(ContextImpl.class);
             long activityInstanceCount = Debug.countInstancesOfClass(Activity.class);
             int globalAssetCount = AssetManager.getGlobalAssetCount();
@@ -868,7 +867,7 @@
             int otherPrivateDirty = memInfo.otherPrivateDirty;
 
             for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) {
-                printRow(pw, HEAP_COLUMN, memInfo.getOtherLabel(i),
+                printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i),
                         memInfo.getOtherPss(i), memInfo.getOtherSharedDirty(i),
                         memInfo.getOtherPrivateDirty(i), "", "", "");
                 otherPss -= memInfo.getOtherPss(i);
@@ -885,7 +884,7 @@
 
             pw.println(" ");
             pw.println(" Objects");
-            printRow(pw, TWO_COUNT_COLUMNS, "Views:", viewInstanceCount, "ViewAncestors:",
+            printRow(pw, TWO_COUNT_COLUMNS, "Views:", viewInstanceCount, "ViewRootImpl:",
                     viewRootInstanceCount);
 
             printRow(pw, TWO_COUNT_COLUMNS, "AppContexts:", appContextInstanceCount,
@@ -937,6 +936,7 @@
         @Override
         public void dumpGfxInfo(FileDescriptor fd, String[] args) {
             dumpGraphicsInfo(fd);
+            WindowManagerImpl.getDefault().dumpGfxInfo(fd);
         }
 
         private void printRow(PrintWriter pw, String format, Object...objs) {
@@ -959,7 +959,7 @@
         }
     }
 
-    private final class H extends Handler {
+    private class H extends Handler {
         public static final int LAUNCH_ACTIVITY         = 100;
         public static final int PAUSE_ACTIVITY          = 101;
         public static final int PAUSE_ACTIVITY_FINISHING= 102;
@@ -1220,7 +1220,7 @@
         }
     }
 
-    private final class Idler implements MessageQueue.IdleHandler {
+    private class Idler implements MessageQueue.IdleHandler {
         public final boolean queueIdle() {
             ActivityClientRecord a = mNewActivities;
             if (a != null) {
@@ -1231,12 +1231,13 @@
                     if (localLOGV) Slog.v(
                         TAG, "Reporting idle of " + a +
                         " finished=" +
-                        (a.activity != null ? a.activity.mFinished : false));
+                        (a.activity != null && a.activity.mFinished));
                     if (a.activity != null && !a.activity.mFinished) {
                         try {
                             am.activityIdle(a.token, a.createdConfig);
                             a.createdConfig = null;
                         } catch (RemoteException ex) {
+                            // Ignore
                         }
                     }
                     prev = a;
@@ -1256,7 +1257,7 @@
         }
     }
 
-    private final static class ResourcesKey {
+    private static class ResourcesKey {
         final private String mResDir;
         final private float mScale;
         final private int mHash;
@@ -1282,17 +1283,17 @@
         }
     }
 
-    public static final ActivityThread currentActivityThread() {
+    public static ActivityThread currentActivityThread() {
         return sThreadLocal.get();
     }
 
-    public static final String currentPackageName() {
+    public static String currentPackageName() {
         ActivityThread am = currentActivityThread();
         return (am != null && am.mBoundApplication != null)
             ? am.mBoundApplication.processName : null;
     }
 
-    public static final Application currentApplication() {
+    public static Application currentApplication() {
         ActivityThread am = currentActivityThread();
         return am != null ? am.mInitialApplication : null;
     }
@@ -1337,7 +1338,7 @@
         return config;
     }
 
-    private final Configuration mMainThreadConfig = new Configuration();
+    private Configuration mMainThreadConfig = new Configuration();
     Configuration applyConfigCompatMainThread(Configuration config, CompatibilityInfo compat) {
         if (config == null) {
             return null;
@@ -1456,6 +1457,7 @@
             ai = getPackageManager().getApplicationInfo(packageName,
                     PackageManager.GET_SHARED_LIBRARY_FILES);
         } catch (RemoteException e) {
+            // Ignore
         }
 
         if (ai != null) {
@@ -1505,7 +1507,7 @@
         }
     }
 
-    private final LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
+    private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
             ClassLoader baseLoader, boolean securityViolation, boolean includeCode) {
         synchronized (mPackages) {
             WeakReference<LoadedApk> ref;
@@ -1712,15 +1714,15 @@
 
     // if the thread hasn't started yet, we don't have the handler, so just
     // save the messages until we're ready.
-    private final void queueOrSendMessage(int what, Object obj) {
+    private void queueOrSendMessage(int what, Object obj) {
         queueOrSendMessage(what, obj, 0, 0);
     }
 
-    private final void queueOrSendMessage(int what, Object obj, int arg1) {
+    private void queueOrSendMessage(int what, Object obj, int arg1) {
         queueOrSendMessage(what, obj, arg1, 0);
     }
 
-    private final void queueOrSendMessage(int what, Object obj, int arg1, int arg2) {
+    private void queueOrSendMessage(int what, Object obj, int arg1, int arg2) {
         synchronized (this) {
             if (DEBUG_MESSAGES) Slog.v(
                 TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
@@ -1743,7 +1745,7 @@
         queueOrSendMessage(H.CLEAN_UP_CONTEXT, cci);
     }
 
-    private final Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
+    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
         // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");
 
         ActivityInfo aInfo = r.activityInfo;
@@ -1861,7 +1863,7 @@
         return activity;
     }
 
-    private final void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
+    private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
         // If we are getting ready to gc after going to the background, well
         // we are back active so skip it.
         unscheduleGcIdler();
@@ -1917,11 +1919,12 @@
                 ActivityManagerNative.getDefault()
                     .finishActivity(r.token, Activity.RESULT_CANCELED, null);
             } catch (RemoteException ex) {
+                // Ignore
             }
         }
     }
 
-    private final void deliverNewIntents(ActivityClientRecord r,
+    private void deliverNewIntents(ActivityClientRecord r,
             List<Intent> intents) {
         final int N = intents.size();
         for (int i=0; i<N; i++) {
@@ -1949,7 +1952,7 @@
         }
     }
 
-    private final void handleNewIntent(NewIntentData data) {
+    private void handleNewIntent(NewIntentData data) {
         performNewIntents(data.token, data.intents);
     }
 
@@ -1964,7 +1967,7 @@
         return sCurrentBroadcastIntent.get();
     }
 
-    private final void handleReceiver(ReceiverData data) {
+    private void handleReceiver(ReceiverData data) {
         // If we are getting ready to gc after going to the background, well
         // we are back active so skip it.
         unscheduleGcIdler();
@@ -1976,7 +1979,7 @@
 
         IActivityManager mgr = ActivityManagerNative.getDefault();
 
-        BroadcastReceiver receiver = null;
+        BroadcastReceiver receiver;
         try {
             java.lang.ClassLoader cl = packageInfo.getClassLoader();
             data.intent.setExtrasClassLoader(cl);
@@ -2026,7 +2029,7 @@
     }
 
     // Instantiate a BackupAgent and tell it that it's alive
-    private final void handleCreateBackupAgent(CreateBackupAgentData data) {
+    private void handleCreateBackupAgent(CreateBackupAgentData data) {
         if (DEBUG_BACKUP) Slog.v(TAG, "handleCreateBackupAgent: " + data);
 
         // no longer idle; we have backup work to do
@@ -2091,7 +2094,7 @@
     }
 
     // Tear down a BackupAgent
-    private final void handleDestroyBackupAgent(CreateBackupAgentData data) {
+    private void handleDestroyBackupAgent(CreateBackupAgentData data) {
         if (DEBUG_BACKUP) Slog.v(TAG, "handleDestroyBackupAgent: " + data);
 
         LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
@@ -2110,7 +2113,7 @@
         }
     }
 
-    private final void handleCreateService(CreateServiceData data) {
+    private void handleCreateService(CreateServiceData data) {
         // If we are getting ready to gc after going to the background, well
         // we are back active so skip it.
         unscheduleGcIdler();
@@ -2156,7 +2159,7 @@
         }
     }
 
-    private final void handleBindService(BindServiceData data) {
+    private void handleBindService(BindServiceData data) {
         Service s = mServices.get(data.token);
         if (s != null) {
             try {
@@ -2184,7 +2187,7 @@
         }
     }
 
-    private final void handleUnbindService(BindServiceData data) {
+    private void handleUnbindService(BindServiceData data) {
         Service s = mServices.get(data.token);
         if (s != null) {
             try {
@@ -2236,7 +2239,7 @@
         }
     }
 
-    private final void handleServiceArgs(ServiceArgsData data) {
+    private void handleServiceArgs(ServiceArgsData data) {
         Service s = mServices.get(data.token);
         if (s != null) {
             try {
@@ -2270,7 +2273,7 @@
         }
     }
 
-    private final void handleStopService(IBinder token) {
+    private void handleStopService(IBinder token) {
         Service s = mServices.remove(token);
         if (s != null) {
             try {
@@ -2465,7 +2468,7 @@
     private Bitmap mAvailThumbnailBitmap = null;
     private Canvas mThumbnailCanvas = null;
 
-    private final Bitmap createThumbnailBitmap(ActivityClientRecord r) {
+    private Bitmap createThumbnailBitmap(ActivityClientRecord r) {
         Bitmap thumbnail = mAvailThumbnailBitmap;
         try {
             if (thumbnail == null) {
@@ -2515,7 +2518,7 @@
         return thumbnail;
     }
 
-    private final void handlePauseActivity(IBinder token, boolean finished,
+    private void handlePauseActivity(IBinder token, boolean finished,
             boolean userLeaving, int configChanges) {
         ActivityClientRecord r = mActivities.get(token);
         if (r != null) {
@@ -2621,7 +2624,7 @@
         CharSequence description;
     }
 
-    private final class ProviderRefCount {
+    private class ProviderRefCount {
         public int count;
         ProviderRefCount(int pCount) {
             count = pCount;
@@ -2636,7 +2639,7 @@
      * For the client, we want to call onStop()/onStart() to indicate when
      * the activity's UI visibillity changes.
      */
-    private final void performStopActivityInner(ActivityClientRecord r,
+    private void performStopActivityInner(ActivityClientRecord r,
             StopInfo info, boolean keepShown, boolean saveState) {
         if (localLOGV) Slog.v(TAG, "Performing stop of " + r);
         Bundle state = null;
@@ -2699,7 +2702,7 @@
         }
     }
 
-    private final void updateVisibility(ActivityClientRecord r, boolean show) {
+    private void updateVisibility(ActivityClientRecord r, boolean show) {
         View v = r.activity.mDecor;
         if (v != null) {
             if (show) {
@@ -2726,7 +2729,7 @@
         }
     }
 
-    private final void handleStopActivity(IBinder token, boolean show, int configChanges) {
+    private void handleStopActivity(IBinder token, boolean show, int configChanges) {
         ActivityClientRecord r = mActivities.get(token);
         r.activity.mConfigChangeFlags |= configChanges;
 
@@ -2760,7 +2763,7 @@
         }
     }
 
-    private final void handleWindowVisibility(IBinder token, boolean show) {
+    private void handleWindowVisibility(IBinder token, boolean show) {
         ActivityClientRecord r = mActivities.get(token);
         
         if (r == null) {
@@ -2785,7 +2788,7 @@
         }
     }
 
-    private final void handleSleeping(IBinder token, boolean sleeping) {
+    private void handleSleeping(IBinder token, boolean sleeping) {
         ActivityClientRecord r = mActivities.get(token);
 
         if (r == null) {
@@ -2846,7 +2849,7 @@
         WindowManagerImpl.getDefault().reportNewConfiguration(mConfiguration);
     }
 
-    private final void deliverResults(ActivityClientRecord r, List<ResultInfo> results) {
+    private void deliverResults(ActivityClientRecord r, List<ResultInfo> results) {
         final int N = results.size();
         for (int i=0; i<N; i++) {
             ResultInfo ri = results.get(i);
@@ -2869,7 +2872,7 @@
         }
     }
 
-    private final void handleSendResult(ResultData res) {
+    private void handleSendResult(ResultData res) {
         ActivityClientRecord r = mActivities.get(res.token);
         if (DEBUG_RESULTS) Slog.v(TAG, "Handling send result to " + r);
         if (r != null) {
@@ -2915,7 +2918,7 @@
         return performDestroyActivity(token, finishing, 0, false);
     }
 
-    private final ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
+    private ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
             int configChanges, boolean getNonConfigInstance) {
         ActivityClientRecord r = mActivities.get(token);
         Class activityClass = null;
@@ -3008,7 +3011,7 @@
         return component == null ? "[Unknown]" : component.toShortString();
     }
 
-    private final void handleDestroyActivity(IBinder token, boolean finishing,
+    private void handleDestroyActivity(IBinder token, boolean finishing,
             int configChanges, boolean getNonConfigInstance) {
         ActivityClientRecord r = performDestroyActivity(token, finishing,
                 configChanges, getNonConfigInstance);
@@ -3123,7 +3126,7 @@
         }
     }
 
-    private final void handleRelaunchActivity(ActivityClientRecord tmp) {
+    private void handleRelaunchActivity(ActivityClientRecord tmp) {
         // If we are getting ready to gc after going to the background, well
         // we are back active so skip it.
         unscheduleGcIdler();
@@ -3233,7 +3236,7 @@
         handleLaunchActivity(r, currentIntent);
     }
 
-    private final void handleRequestThumbnail(IBinder token) {
+    private void handleRequestThumbnail(IBinder token) {
         ActivityClientRecord r = mActivities.get(token);
         Bitmap thumbnail = createThumbnailBitmap(r);
         CharSequence description = null;
@@ -3308,7 +3311,7 @@
         return callbacks;
     }
 
-    private final void performConfigurationChanged(
+    private void performConfigurationChanged(
             ComponentCallbacks cb, Configuration config) {
         // Only for Activity objects, check that they actually call up to their
         // superclass implementation.  ComponentCallbacks is an interface, so
@@ -3451,6 +3454,9 @@
             }
             callbacks = collectComponentCallbacksLocked(false, config);
         }
+        
+        // Cleanup hardware accelerated stuff
+        WindowManagerImpl.getDefault().trimLocalMemory();
 
         if (callbacks != null) {
             final int N = callbacks.size();
@@ -3579,7 +3585,7 @@
         WindowManagerImpl.getDefault().trimMemory(level);
     }
 
-    private final void handleBindApplication(AppBindData data) {
+    private void handleBindApplication(AppBindData data) {
         mBoundApplication = data;
         mConfiguration = new Configuration(data.config);
         mCompatConfiguration = new Configuration(data.config);
@@ -3791,7 +3797,7 @@
         }
     }
 
-    private final void installContentProviders(
+    private void installContentProviders(
             Context context, List<ProviderInfo> providers) {
         final ArrayList<IActivityManager.ContentProviderHolder> results =
             new ArrayList<IActivityManager.ContentProviderHolder>();
@@ -3825,7 +3831,7 @@
         }
     }
 
-    private final IContentProvider getExistingProvider(Context context, String name) {
+    private IContentProvider getExistingProvider(Context context, String name) {
         synchronized(mProviderMap) {
             final ProviderClientRecord pr = mProviderMap.get(name);
             if (pr != null) {
@@ -3835,7 +3841,7 @@
         }
     }
 
-    private final IContentProvider getProvider(Context context, String name) {
+    private IContentProvider getProvider(Context context, String name) {
         IContentProvider existing = getExistingProvider(context, name);
         if (existing != null) {
             return existing;
@@ -4007,7 +4013,7 @@
         }
     }
 
-    private final IContentProvider installProvider(Context context,
+    private IContentProvider installProvider(Context context,
             IContentProvider provider, ProviderInfo info, boolean noisy) {
         ContentProvider localProvider = null;
         if (provider == null) {
@@ -4027,6 +4033,7 @@
                     c = context.createPackageContext(ai.packageName,
                             Context.CONTEXT_INCLUDE_CODE);
                 } catch (PackageManager.NameNotFoundException e) {
+                    // Ignore
                 }
             }
             if (c == null) {
@@ -4086,7 +4093,7 @@
         return provider;
     }
 
-    private final void attach(boolean system) {
+    private void attach(boolean system) {
         sThreadLocal.set(this);
         mSystemThread = system;
         if (!system) {
@@ -4101,6 +4108,7 @@
             try {
                 mgr.attachApplication(mAppThread);
             } catch (RemoteException ex) {
+                // Ignore
             }
         } else {
             // Don't set application object here -- if the system crashes,
@@ -4166,7 +4174,7 @@
         }
     }
 
-    public static final void main(String[] args) {
+    public static void main(String[] args) {
         SamplingProfilerIntegration.start();
 
         // CloseGuard defaults to true and can be quite spammy.  We
diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java
index f4c0249..8f4ece0 100644
--- a/core/java/android/view/DisplayList.java
+++ b/core/java/android/view/DisplayList.java
@@ -54,4 +54,11 @@
      * @return boolean true if the display list is able to be replayed, false otherwise.
      */
     abstract boolean isValid();
+
+    /**
+     * Return the amount of memory used by this display list.
+     * 
+     * @return The size of this display list in bytes
+     */
+    abstract int getSize();
 }
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index ac0abc3..a7fe95d 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -330,6 +330,12 @@
 
     private static native void nDestroyDisplayList(int displayList);
 
+    static int getDisplayListSize(int displayList) {
+        return nGetDisplayListSize(displayList);
+    }
+
+    private static native int nGetDisplayListSize(int displayList);
+
     @Override
     public boolean drawDisplayList(DisplayList displayList, int width, int height, Rect dirty) {
         return nDrawDisplayList(mRenderer,
diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java
index 9e649cea..4ca5e98 100644
--- a/core/java/android/view/GLES20DisplayList.java
+++ b/core/java/android/view/GLES20DisplayList.java
@@ -82,6 +82,12 @@
         }
     }
 
+    @Override
+    int getSize() {
+        if (mFinalizer == null) return 0;
+        return GLES20Canvas.getDisplayListSize(mFinalizer.mNativeDisplayList);
+    }
+
     private static class DisplayListFinalizer {
         final int mNativeDisplayList;
 
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 5404e3a..4e4923b 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -934,7 +934,9 @@
         }
 
         private void destroyHardwareLayer(View view) {
-            view.destroyLayer();
+            if (view.destroyLayer()) {
+                view.invalidate(true);
+            }
             if (view instanceof ViewGroup) {
                 ViewGroup group = (ViewGroup) view;
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5b8a201..bfa525c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2294,8 +2294,8 @@
 
     private Bitmap mDrawingCache;
     private Bitmap mUnscaledDrawingCache;
-    private DisplayList mDisplayList;
     private HardwareLayer mHardwareLayer;
+    DisplayList mDisplayList;
 
     /**
      * When this view has focus and the next focus is {@link #FOCUS_LEFT},
@@ -9727,11 +9727,13 @@
         return mHardwareLayer;
     }
 
-    void destroyLayer() {
+    boolean destroyLayer() {
         if (mHardwareLayer != null) {
             mHardwareLayer.destroy();
             mHardwareLayer = null;
+            return true;
         }
+        return false;
     }
 
     /**
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 4acf48c..96e550e 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -420,7 +420,7 @@
      *
      * @hide
      */
-    public static long getViewAncestorInstanceCount() {
+    public static long getViewRootImplCount() {
         return Debug.countInstancesOfClass(ViewRootImpl.class);
     }
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 57bf17f..cddf41e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -80,6 +80,7 @@
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
@@ -235,7 +236,6 @@
     final Configuration mLastConfiguration = new Configuration();
     final Configuration mPendingConfiguration = new Configuration();
 
-    
     class ResizedInfo {
         Rect coveredInsets;
         Rect visibleInsets;
@@ -542,10 +542,26 @@
     }
 
     private void destroyHardwareResources() {
-        if (mAttachInfo.mHardwareRenderer.isEnabled()) {
-            mAttachInfo.mHardwareRenderer.destroyLayers(mView);
+        if (mAttachInfo.mHardwareRenderer != null) {
+            if (mAttachInfo.mHardwareRenderer.isEnabled()) {
+                mAttachInfo.mHardwareRenderer.destroyLayers(mView);
+            }
+            mAttachInfo.mHardwareRenderer.destroy(false);
         }
-        mAttachInfo.mHardwareRenderer.destroy(false);
+    }
+
+    void destroyHardwareLayers() {
+        if (mThread != Thread.currentThread()) {
+            if (mAttachInfo.mHardwareRenderer != null &&
+                    mAttachInfo.mHardwareRenderer.isEnabled()) {
+                HardwareRenderer.trimMemory(ComponentCallbacks.TRIM_MEMORY_MODERATE);
+            }
+        } else {
+            if (mAttachInfo.mHardwareRenderer != null &&
+                    mAttachInfo.mHardwareRenderer.isEnabled()) {
+                mAttachInfo.mHardwareRenderer.destroyLayers(mView);
+            }
+        }
     }
 
     private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
@@ -876,9 +892,7 @@
             attachInfo.mWindowVisibility = viewVisibility;
             host.dispatchWindowVisibilityChanged(viewVisibility);
             if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
-                if (mAttachInfo.mHardwareRenderer != null) {
-                    destroyHardwareResources();
-                }                
+                destroyHardwareResources();
             }
             if (viewVisibility == View.GONE) {
                 // After making a window gone, we will count it as being
@@ -3492,6 +3506,31 @@
     public void debug() {
         mView.debug();
     }
+    
+    public void dumpGfxInfo(PrintWriter pw, int[] info) {
+        if (mView != null) {
+            getGfxInfo(mView, info);
+        } else {
+            info[0] = info[1] = 0;
+        }
+    }
+
+    private void getGfxInfo(View view, int[] info) {
+        DisplayList displayList = view.mDisplayList;
+        info[0]++;
+        if (displayList != null) {
+            info[1] += displayList.getSize();
+        }
+
+        if (view instanceof ViewGroup) {
+            ViewGroup group = (ViewGroup) view;
+
+            int count = group.getChildCount();
+            for (int i = 0; i < count; i++) {
+                getGfxInfo(group.getChildAt(i), info);
+            }
+        }
+    }
 
     public void die(boolean immediate) {
         if (immediate) {
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index a451bb5..5ef4f3e 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -24,6 +24,9 @@
 import android.util.Log;
 import android.view.inputmethod.InputMethodManager;
 
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
 import java.util.HashMap;
 
 final class WindowLeaked extends AndroidRuntimeException {
@@ -392,7 +395,7 @@
                         leak.setStackTrace(root.getLocation().getStackTrace());
                         Log.e("WindowManager", leak.getMessage(), leak);
                     }
-                    
+
                     removeViewLocked(i);
                     i--;
                     count--;
@@ -410,6 +413,59 @@
         }
     }
 
+    /**
+     * @hide
+     */
+    public void trimLocalMemory() {
+        synchronized (this) {
+            if (mViews == null) return;
+            int count = mViews.length;
+            for (int i = 0; i < count; i++) {
+                mRoots[i].destroyHardwareLayers();
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public void dumpGfxInfo(FileDescriptor fd) {
+        FileOutputStream fout = new FileOutputStream(fd);
+        PrintWriter pw = new PrintWriter(fout);
+        try {
+            synchronized (this) {
+                if (mViews != null) {
+                    pw.println("View hierarchy:");
+
+                    final int count = mViews.length;
+
+                    int viewsCount = 0;
+                    int displayListsSize = 0;
+                    int[] info = new int[2];
+
+                    for (int i = 0; i < count; i++) {
+                        ViewRootImpl root = mRoots[i];
+                        root.dumpGfxInfo(pw, info);
+
+                        String name = root.getClass().getName() + '@' +
+                                Integer.toHexString(hashCode());                        
+                        pw.printf("  %s: %d views, %.2f kB (display lists)\n",
+                                name, info[0], info[1] / 1024.0f);
+
+                        viewsCount += info[0];
+                        displayListsSize += info[1];
+                    }
+
+                    pw.printf("\nTotal ViewRootImpl: %d\n", count);
+                    pw.printf("Total Views:        %d\n", viewsCount);                    
+                    pw.printf("Total DisplayList:  %.2f kB\n\n", displayListsSize / 1024.0f);                    
+                }
+            }
+        } finally {
+            pw.flush();
+        }        
+    }
+
     public void setStoppedState(IBinder token, boolean stopped) {
         synchronized (this) {
             if (mViews == null)
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index b06de9d..b3f2d51 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -580,6 +580,11 @@
     return renderer->getDisplayList(displayList);
 }
 
+static jint android_view_GLES20Canvas_getDisplayListSize(JNIEnv* env,
+        jobject clazz, DisplayList* displayList) {
+    return displayList->getSize();
+}
+
 static OpenGLRenderer* android_view_GLES20Canvas_createDisplayListRenderer(JNIEnv* env,
         jobject clazz) {
     return new DisplayListRenderer;
@@ -721,8 +726,7 @@
 // ----------------------------------------------------------------------------
 
 static void
-android_app_ActivityThread_dumpGraphics(JNIEnv* env, jobject clazz, jobject javaFileDescriptor)
-{
+android_app_ActivityThread_dumpGraphics(JNIEnv* env, jobject clazz, jobject javaFileDescriptor) {
 #ifdef USE_OPENGL_RENDERER
     int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
     android::uirenderer::DisplayList::outputLogBuffer(fd);
@@ -814,6 +818,7 @@
 
     { "nGetDisplayList",         "(II)I",      (void*) android_view_GLES20Canvas_getDisplayList },
     { "nDestroyDisplayList",     "(I)V",       (void*) android_view_GLES20Canvas_destroyDisplayList },
+    { "nGetDisplayListSize",     "(I)I",       (void*) android_view_GLES20Canvas_getDisplayListSize },
     { "nCreateDisplayListRenderer", "()I",     (void*) android_view_GLES20Canvas_createDisplayListRenderer },
     { "nResetDisplayListRenderer", "(I)V",     (void*) android_view_GLES20Canvas_resetDisplayListRenderer },
     { "nDrawDisplayList",        "(IIIILandroid/graphics/Rect;)Z",
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 886c05c..88cfc5a 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -74,12 +74,17 @@
     if (logBuffer.isEmpty()) {
         return;
     }
-    String8 cachesLog;
-    Caches::getInstance().dumpMemoryUsage(cachesLog);
+
     FILE *file = fdopen(fd, "a");
-    fprintf(file, "\nCaches:\n%s", cachesLog.string());
+
     fprintf(file, "\nRecent DisplayList operations\n");
     logBuffer.outputCommands(file, OP_NAMES);
+
+    String8 cachesLog;
+    Caches::getInstance().dumpMemoryUsage(cachesLog);
+    fprintf(file, "\nCaches:\n%s", cachesLog.string());
+    fprintf(file, "\n");
+
     fflush(file);
 }
 
@@ -143,10 +148,10 @@
         clearResources();
     }
 
-    size_t size = writer.size();
-    void* buffer = sk_malloc_throw(size);
+    mSize = writer.size();
+    void* buffer = sk_malloc_throw(mSize);
     writer.flatten(buffer);
-    mReader.setMemory(buffer, size);
+    mReader.setMemory(buffer, mSize);
 
     Caches& caches = Caches::getInstance();
 
@@ -188,6 +193,11 @@
 }
 
 void DisplayList::init() {
+    mSize = 0;
+}
+
+size_t DisplayList::getSize() {
+    return mSize;
 }
 
 /**
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index a3d346d..69e72a4 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -105,6 +105,8 @@
 
     void initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing = false);
 
+    size_t getSize();
+
     bool replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level = 0);
 
     void output(OpenGLRenderer& renderer, uint32_t level = 0);
@@ -203,6 +205,8 @@
     Vector<SkiaShader*> mShaders;
 
     mutable SkFlattenableReadBuffer mReader;
+
+    size_t mSize;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index 89c35da..0af0177 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -68,6 +68,7 @@
 
 void LayerCache::deleteLayer(Layer* layer) {
     if (layer) {
+        LAYER_LOGD("Destroying layer %dx%d", layer->getWidth(), layer->getHeight());
         mSize -= layer->getWidth() * layer->getHeight() * 4;
         layer->deleteFbo();
         layer->deleteTexture();
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 81816f6..7e8c7fd 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -301,7 +301,7 @@
             delete layer;
         } else {
             LAYER_RENDERER_LOGD("  Cached!");
-#if DEBUG_LAYERS
+#if DEBUG_LAYER_RENDERER
             Caches::getInstance().layerCache.dump();
 #endif
             layer->region.clear();
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 47049e2..923978f 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -68,7 +68,7 @@
 #define MB(s) s * 1024 * 1024
 
 #define DEFAULT_TEXTURE_CACHE_SIZE 24.0f
-#define DEFAULT_LAYER_CACHE_SIZE 24.0f
+#define DEFAULT_LAYER_CACHE_SIZE 16.0f
 #define DEFAULT_PATH_CACHE_SIZE 4.0f
 #define DEFAULT_SHAPE_CACHE_SIZE 1.0f
 #define DEFAULT_PATCH_CACHE_SIZE 512