Ensure bitmaps aren't freed while referenced from a display list

Also removes the reference queue finalizers. They aren't necessary
anymore now that Bitmaps are allocated in the heap.
diff --git a/core/java/android/util/Finalizers.java b/core/java/android/util/Finalizers.java
deleted file mode 100644
index 671f2d4..0000000
--- a/core/java/android/util/Finalizers.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2010 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.util;
-
-import java.lang.ref.PhantomReference;
-import java.lang.ref.Reference;
-import java.lang.ref.ReferenceQueue;
-
-/**
- * This class can be used to implement reliable finalizers.
- * 
- * @hide
- */
-public final class Finalizers {
-    private static final String LOG_TAG = "Finalizers";
-    
-    private static final Object[] sLock = new Object[0];
-    private static boolean sInit;
-    private static Reclaimer sReclaimer;
-
-    /**
-     * Subclass of PhantomReference used to reclaim resources.
-     */
-    public static abstract class ReclaimableReference<T> extends PhantomReference<T> {
-        public ReclaimableReference(T r, ReferenceQueue<Object> q) {
-            super(r, q);
-        }
-        
-        public abstract void reclaim();
-    }
-
-    /**
-     * Returns the queue used to reclaim ReclaimableReferences.
-     * 
-     * @return A reference queue or null before initialization
-     */
-    public static ReferenceQueue<Object> getQueue() {
-        synchronized (sLock) {
-            if (!sInit) {
-                return null;
-            }
-            if (!sReclaimer.isRunning()) {
-                sReclaimer = new Reclaimer(sReclaimer.mQueue);
-                sReclaimer.start();
-            }
-            return sReclaimer.mQueue;
-        }
-    }
-
-    /**
-     * Invoked by Zygote. Don't touch!
-     */
-    public static void init() {
-        synchronized (sLock) {
-            if (!sInit && sReclaimer == null) {
-                sReclaimer = new Reclaimer();
-                sReclaimer.start();
-                sInit = true;
-            }
-        }
-    }
-    
-    private static class Reclaimer extends Thread {
-        ReferenceQueue<Object> mQueue;
-
-        private volatile boolean mRunning = false;
-
-        Reclaimer() {
-            this(new ReferenceQueue<Object>());
-        }
-
-        Reclaimer(ReferenceQueue<Object> queue) {
-            super("Reclaimer");
-            setDaemon(true);
-            mQueue = queue;            
-        }
-
-        @Override
-        public void start() {
-            mRunning = true;
-            super.start();
-        }
-
-        boolean isRunning() {
-            return mRunning;
-        }
-
-        @SuppressWarnings({"InfiniteLoopStatement"})
-        @Override
-        public void run() {
-            try {
-                while (true) {
-                    try {
-                        cleanUp(mQueue.remove());
-                    } catch (InterruptedException e) {
-                        // Ignore
-                    }
-                }
-            } catch (Exception e) {
-                Log.e(LOG_TAG, "Reclaimer thread exiting: ", e);
-            } finally {
-                mRunning = false;
-            }
-        }
-
-        private void cleanUp(Reference<?> reference) {
-            do {
-                reference.clear();
-                ((ReclaimableReference<?>) reference).reclaim();
-            } while ((reference = mQueue.poll()) != null);
-        }
-    }
-}
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 773d734..f0bb40d 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -34,11 +34,6 @@
 import android.text.SpannableString;
 import android.text.SpannedString;
 import android.text.TextUtils;
-import android.util.Finalizers;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
 
 /**
  * An implementation of Canvas on top of OpenGL ES 2.0.
@@ -46,7 +41,11 @@
 class GLES20Canvas extends HardwareCanvas {
     private final boolean mOpaque;
     private int mRenderer;
-    
+
+    // The native renderer will be destroyed when this object dies.
+    // DO NOT overwrite this reference once it is set.
+    private CanvasFinalizer mFinalizer;
+
     private int mWidth;
     private int mHeight;
     
@@ -78,7 +77,7 @@
         this(false, translucent);
     }
     
-    GLES20Canvas(boolean record, boolean translucent) {
+    protected GLES20Canvas(boolean record, boolean translucent) {
         mOpaque = !translucent;
 
         if (record) {
@@ -90,7 +89,7 @@
         if (mRenderer == 0) {
             throw new IllegalStateException("Could not create GLES20Canvas renderer");
         } else {
-            new CanvasFinalizer(this);
+            mFinalizer = new CanvasFinalizer(mRenderer);
         }
     }
 
@@ -99,22 +98,16 @@
 
     private static native void nDestroyRenderer(int renderer);
 
-    private static class CanvasFinalizer extends Finalizers.ReclaimableReference<GLES20Canvas> {
-        private static final Set<CanvasFinalizer> sFinalizers = Collections.synchronizedSet(
-                new HashSet<CanvasFinalizer>());
+    private static class CanvasFinalizer {
+        final int mRenderer;
 
-        private int mRenderer;
-
-        CanvasFinalizer(GLES20Canvas canvas) {
-            super(canvas, Finalizers.getQueue());
-            mRenderer = canvas.mRenderer;
-            sFinalizers.add(this);
+        CanvasFinalizer(int renderer) {
+            mRenderer = renderer;
         }
 
         @Override
-        public void reclaim() {
+        protected void finalize() throws Throwable {
             nDestroyRenderer(mRenderer);
-            sFinalizers.remove(this);
         }
     }
 
diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java
index 11e6d30..adf08ea 100644
--- a/core/java/android/view/GLES20DisplayList.java
+++ b/core/java/android/view/GLES20DisplayList.java
@@ -16,12 +16,6 @@
 
 package android.view;
 
-import android.util.Finalizers;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
 /**
  * An implementation of display list for OpenGL ES 2.0.
  */
@@ -33,13 +27,18 @@
 
     int mNativeDisplayList;
 
+    // The native display list will be destroyed when this object dies.
+    // DO NOT overwrite this reference once it is set.
+    @SuppressWarnings("unused")
+    private DisplayListFinalizer mFinalizer;
+
     @Override
     HardwareCanvas start() {
         if (mStarted) {
             throw new IllegalStateException("Recording has already started");
         }
 
-        mCanvas = new GLES20Canvas(true, true);
+        mCanvas = new GLES20RecordingCanvas(true);
         mStarted = true;
         mRecorded = false;
 
@@ -53,7 +52,7 @@
             mRecorded = true;
 
             mNativeDisplayList = mCanvas.getDisplayList();
-            new DisplayListFinalizer(this);
+            mFinalizer = new DisplayListFinalizer(mNativeDisplayList);
         }
     }
 
@@ -62,22 +61,16 @@
         return !mStarted && mRecorded;
     }
 
-    private static class DisplayListFinalizer extends Finalizers.ReclaimableReference<DisplayList> {
-        private static final Set<DisplayListFinalizer> sFinalizers = Collections.synchronizedSet(
-                new HashSet<DisplayListFinalizer>());
+    private static class DisplayListFinalizer {
+        int mNativeDisplayList;
 
-        private int mNativeDisplayList;
-
-        DisplayListFinalizer(GLES20DisplayList displayList) {
-            super(displayList, Finalizers.getQueue());
-            mNativeDisplayList = displayList.mNativeDisplayList;
-            sFinalizers.add(this);
+        DisplayListFinalizer(int nativeDisplayList) {
+            mNativeDisplayList = nativeDisplayList;
         }
 
         @Override
-        public void reclaim() {
+        protected void finalize() throws Throwable {
             GLES20Canvas.destroyDisplayList(mNativeDisplayList);
-            sFinalizers.remove(this);
         }
     }
 }
diff --git a/core/java/android/view/GLES20RecordingCanvas.java b/core/java/android/view/GLES20RecordingCanvas.java
new file mode 100644
index 0000000..bd14286
--- /dev/null
+++ b/core/java/android/view/GLES20RecordingCanvas.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2010 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.view;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Shader;
+
+import java.util.HashSet;
+
+/**
+ * An implementation of a GL canvas that records drawing operations.
+ * This is intended for use with a DisplayList. This class keeps a list of all the Paint and
+ * Bitmap objects that it draws, preventing the backing memory of Bitmaps from being freed while
+ * the DisplayList is still holding a native reference to the memory.
+ */
+class GLES20RecordingCanvas extends GLES20Canvas {
+    // These lists ensure that any Bitmaps recorded by a DisplayList are kept alive as long
+    // as the DisplayList is alive.
+    private HashSet<Bitmap> mBitmaps = new HashSet<Bitmap>();
+
+    GLES20RecordingCanvas(boolean translucent) {
+        super(true, translucent);
+    }
+
+    private void recordShaderBitmap(Paint paint) {
+        if (paint != null) {
+            final Shader shader = paint.getShader();
+            if (shader instanceof BitmapShader) {
+                mBitmaps.add(((BitmapShader) shader).mBitmap);
+            }
+        }
+    }
+
+    @Override
+    public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,
+            Paint paint) {
+        super.drawArc(oval, startAngle, sweepAngle, useCenter, paint);
+    }
+
+    @Override
+    public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) {
+        super.drawPatch(bitmap, chunks, dst, paint);
+        mBitmaps.add(bitmap);
+        // Shaders in the Paint are ignored when drawing a Bitmap
+    }
+
+    @Override
+    public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
+        super.drawBitmap(bitmap, left, top, paint);
+        mBitmaps.add(bitmap);
+        // Shaders in the Paint are ignored when drawing a Bitmap
+    }
+
+    @Override
+    public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
+        super.drawBitmap(bitmap, matrix, paint);
+        mBitmaps.add(bitmap);
+        // Shaders in the Paint are ignored when drawing a Bitmap
+    }
+
+    @Override
+    public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
+        super.drawBitmap(bitmap, src, dst, paint);
+        mBitmaps.add(bitmap);
+        // Shaders in the Paint are ignored when drawing a Bitmap
+    }
+
+    @Override
+    public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) {
+        super.drawBitmap(bitmap, src, dst, paint);
+        mBitmaps.add(bitmap);
+        // Shaders in the Paint are ignored when drawing a Bitmap
+    }
+
+    @Override
+    public void drawBitmap(int[] colors, int offset, int stride, float x, float y, int width,
+            int height, boolean hasAlpha, Paint paint) {
+        super.drawBitmap(colors, offset, stride, x, y, width, height, hasAlpha, paint);
+        // Shaders in the Paint are ignored when drawing a Bitmap
+    }
+
+    @Override
+    public void drawBitmap(int[] colors, int offset, int stride, int x, int y, int width,
+            int height, boolean hasAlpha, Paint paint) {
+        super.drawBitmap(colors, offset, stride, x, y, width, height, hasAlpha, paint);
+        // Shaders in the Paint are ignored when drawing a Bitmap
+    }
+
+    @Override
+    public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts,
+            int vertOffset, int[] colors, int colorOffset, Paint paint) {
+        super.drawBitmapMesh(bitmap, meshWidth, meshHeight, verts, vertOffset, colors, colorOffset,
+                paint);
+        mBitmaps.add(bitmap);
+        // Shaders in the Paint are ignored when drawing a Bitmap
+    }
+
+    @Override
+    public void drawCircle(float cx, float cy, float radius, Paint paint) {
+        super.drawCircle(cx, cy, radius, paint);
+        recordShaderBitmap(paint);
+    }
+
+    @Override
+    public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) {
+        super.drawLine(startX, startY, stopX, stopY, paint);
+        recordShaderBitmap(paint);
+    }
+
+    @Override
+    public void drawLines(float[] pts, int offset, int count, Paint paint) {
+        super.drawLines(pts, offset, count, paint);
+        recordShaderBitmap(paint);
+    }
+
+    @Override
+    public void drawLines(float[] pts, Paint paint) {
+        super.drawLines(pts, paint);
+        recordShaderBitmap(paint);
+    }
+
+    @Override
+    public void drawOval(RectF oval, Paint paint) {
+        super.drawOval(oval, paint);
+        recordShaderBitmap(paint);
+    }
+
+    @Override
+    public void drawPaint(Paint paint) {
+        super.drawPaint(paint);
+        recordShaderBitmap(paint);
+    }
+
+    @Override
+    public void drawPath(Path path, Paint paint) {
+        super.drawPath(path, paint);
+        recordShaderBitmap(paint);
+    }
+
+    @Override
+    public void drawPoint(float x, float y, Paint paint) {
+        super.drawPoint(x, y, paint);
+        recordShaderBitmap(paint);
+    }
+
+    @Override
+    public void drawPoints(float[] pts, int offset, int count, Paint paint) {
+        super.drawPoints(pts, offset, count, paint);
+        recordShaderBitmap(paint);
+    }
+
+    @Override
+    public void drawPoints(float[] pts, Paint paint) {
+        super.drawPoints(pts, paint);
+        recordShaderBitmap(paint);
+    }
+
+    @Override
+    public void drawPosText(char[] text, int index, int count, float[] pos, Paint paint) {
+        super.drawPosText(text, index, count, pos, paint);
+        recordShaderBitmap(paint);
+    }
+
+    @Override
+    public void drawPosText(String text, float[] pos, Paint paint) {
+        super.drawPosText(text, pos, paint);
+        recordShaderBitmap(paint);
+    }
+
+    @Override
+    public void drawRect(float left, float top, float right, float bottom, Paint paint) {
+        super.drawRect(left, top, right, bottom, paint);
+        recordShaderBitmap(paint);
+    }
+
+    @Override
+    public void drawRect(Rect r, Paint paint) {
+        super.drawRect(r, paint);
+        recordShaderBitmap(paint);
+    }
+
+    @Override
+    public void drawRect(RectF r, Paint paint) {
+        super.drawRect(r, paint);
+        recordShaderBitmap(paint);
+    }
+
+    @Override
+    public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) {
+        super.drawRoundRect(rect, rx, ry, paint);
+        recordShaderBitmap(paint);
+    }
+
+    @Override
+    public void drawText(char[] text, int index, int count, float x, float y, Paint paint) {
+        super.drawText(text, index, count, x, y, paint);
+        recordShaderBitmap(paint);
+    }
+
+    @Override
+    public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) {
+        super.drawText(text, start, end, x, y, paint);
+        recordShaderBitmap(paint);
+    }
+
+    @Override
+    public void drawText(String text, int start, int end, float x, float y, Paint paint) {
+        super.drawText(text, start, end, x, y, paint);
+        recordShaderBitmap(paint);
+    }
+
+    @Override
+    public void drawText(String text, float x, float y, Paint paint) {
+        super.drawText(text, x, y, paint);
+        recordShaderBitmap(paint);
+    }
+
+    @Override
+    public void drawTextOnPath(char[] text, int index, int count, Path path, float hOffset,
+            float vOffset, Paint paint) {
+        super.drawTextOnPath(text, index, count, path, hOffset, vOffset, paint);
+        recordShaderBitmap(paint);
+    }
+
+    @Override
+    public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) {
+        super.drawTextOnPath(text, path, hOffset, vOffset, paint);
+        recordShaderBitmap(paint);
+    }
+
+    @Override
+    public void drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount,
+            float x, float y, int dir, Paint paint) {
+        super.drawTextRun(text, index, count, contextIndex, contextCount, x, y, dir, paint);
+        recordShaderBitmap(paint);
+    }
+
+    @Override
+    public void drawTextRun(CharSequence text, int start, int end, int contextStart,
+            int contextEnd, float x, float y, int dir, Paint paint) {
+        super.drawTextRun(text, start, end, contextStart, contextEnd, x, y, dir, paint);
+        recordShaderBitmap(paint);
+    }
+
+    @Override
+    public void drawVertices(VertexMode mode, int vertexCount, float[] verts, int vertOffset,
+            float[] texs, int texOffset, int[] colors, int colorOffset, short[] indices,
+            int indexOffset, int indexCount, Paint paint) {
+        super.drawVertices(mode, vertexCount, verts, vertOffset, texs, texOffset, colors,
+                colorOffset, indices, indexOffset, indexCount, paint);
+        recordShaderBitmap(paint);
+    }
+}
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 18e1b45..f58f261 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -24,7 +24,6 @@
 import android.os.Process;
 import android.os.SystemProperties;
 import android.util.Config;
-import android.util.Finalizers;
 import android.util.Log;
 import android.util.Slog;
 
@@ -142,12 +141,6 @@
             Debug.enableEmulatorTraceOutput();
         }
 
-        /**
-         * Initialize the thread used to reclaim resources without
-         * going through finalizers.
-         */
-        Finalizers.init();
-
         initialized = true;
     }
 
diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java
index 4ba679b..f74d0ef 100644
--- a/graphics/java/android/graphics/BitmapShader.java
+++ b/graphics/java/android/graphics/BitmapShader.java
@@ -23,9 +23,10 @@
 public class BitmapShader extends Shader {
     /**
      * Prevent garbage collection.
+     * @hide
      */
     @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
-    private final Bitmap mBitmap;
+    public final Bitmap mBitmap;
 
     /**
      * Call this to create a new shader that will draw with a bitmap.
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
index 1c93ea6..00de39b 100644
--- a/libs/hwui/ResourceCache.cpp
+++ b/libs/hwui/ResourceCache.cpp
@@ -60,9 +60,7 @@
 }
 
 void ResourceCache::incrementRefcount(SkBitmap* bitmapResource) {
-    SkPixelRef* pixref = bitmapResource->pixelRef();
-    if (pixref) pixref->globalRef();
-
+    bitmapResource->pixelRef()->safeRef();
     bitmapResource->getColorTable()->safeRef();
     incrementRefcount((void*)bitmapResource, kBitmap);
 }
@@ -91,9 +89,7 @@
 }
 
 void ResourceCache::decrementRefcount(SkBitmap* bitmapResource) {
-    SkPixelRef* pixref = bitmapResource->pixelRef();
-    if (pixref) pixref->globalUnref();
-
+    bitmapResource->pixelRef()->safeUnref();
     bitmapResource->getColorTable()->safeUnref();
     decrementRefcount((void*)bitmapResource);
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index 9de6e8f..c30e8e4 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -32,7 +32,6 @@
 import android.graphics.Bitmap;
 import android.graphics.Typeface_Delegate;
 import android.os.Looper;
-import android.util.Finalizers;
 
 import java.lang.ref.SoftReference;
 import java.lang.reflect.Field;
@@ -198,8 +197,6 @@
                 Capability.ANIMATE);
 
 
-        Finalizers.init();
-
         BridgeAssetManager.initSystem();
 
         // When DEBUG_LAYOUT is set and is not 0 or false, setup a default listener