Merge "Recycle the bitmap as soon as possible" into rvc-dev
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
index 54eca0e..703d591 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.glwallpaper;
 
+import static com.android.systemui.glwallpaper.ImageWallpaperRenderer.WallpaperTexture;
+
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -66,15 +68,15 @@
 
     private float mThreshold = DEFAULT_THRESHOLD;
 
-    void start(Bitmap bitmap) {
-        new ThresholdComputeTask(mHandler).execute(bitmap);
+    void start(WallpaperTexture texture) {
+        new ThresholdComputeTask(mHandler).execute(texture);
     }
 
     float getThreshold() {
         return Math.min(mThreshold, MAX_THRESHOLD);
     }
 
-    private static class ThresholdComputeTask extends AsyncTask<Bitmap, Void, Float> {
+    private static class ThresholdComputeTask extends AsyncTask<WallpaperTexture, Void, Float> {
         private Handler mUpdateHandler;
 
         ThresholdComputeTask(Handler handler) {
@@ -83,13 +85,22 @@
         }
 
         @Override
-        protected Float doInBackground(Bitmap... bitmaps) {
-            Bitmap bitmap = bitmaps[0];
-            if (bitmap != null) {
-                return new Threshold().compute(bitmap);
+        protected Float doInBackground(WallpaperTexture... textures) {
+            WallpaperTexture texture = textures[0];
+            final float[] threshold = new float[] {DEFAULT_THRESHOLD};
+            if (texture == null) {
+                Log.e(TAG, "ThresholdComputeTask: WallpaperTexture not initialized");
+                return threshold[0];
             }
-            Log.e(TAG, "ThresholdComputeTask: Can't get bitmap");
-            return DEFAULT_THRESHOLD;
+
+            texture.use(bitmap -> {
+                if (bitmap != null) {
+                    threshold[0] = new Threshold().compute(bitmap);
+                } else {
+                    Log.e(TAG, "ThresholdComputeTask: Can't get bitmap");
+                }
+            });
+            return threshold[0];
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
index ed6675dc..e9ddb38 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
@@ -36,6 +36,8 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
 
 /**
  * A GL renderer for image wallpaper.
@@ -47,7 +49,6 @@
     private static final float SCALE_VIEWPORT_MAX = 1.1f;
     private static final boolean DEBUG = true;
 
-    private final WallpaperManager mWallpaperManager;
     private final ImageGLProgram mProgram;
     private final ImageGLWallpaper mWallpaper;
     private final ImageProcessHelper mImageProcessHelper;
@@ -57,18 +58,18 @@
     private final Rect mScissor;
     private final Rect mSurfaceSize = new Rect();
     private final Rect mViewport = new Rect();
-    private Bitmap mBitmap;
     private boolean mScissorMode;
     private float mXOffset;
     private float mYOffset;
-    private boolean mWcgContent;
+    private final WallpaperTexture mTexture;
 
     public ImageWallpaperRenderer(Context context, SurfaceProxy proxy) {
-        mWallpaperManager = context.getSystemService(WallpaperManager.class);
-        if (mWallpaperManager == null) {
+        final WallpaperManager wpm = context.getSystemService(WallpaperManager.class);
+        if (wpm == null) {
             Log.w(TAG, "WallpaperManager not available");
         }
 
+        mTexture = new WallpaperTexture(wpm);
         DisplayInfo displayInfo = new DisplayInfo();
         context.getDisplay().getDisplayInfo(displayInfo);
 
@@ -90,15 +91,13 @@
     }
 
     protected void startProcessingImage() {
-        if (loadBitmap()) {
-            // Compute threshold of the image, this is an async work.
-            mImageProcessHelper.start(mBitmap);
-        }
+        // Compute threshold of the image, this is an async work.
+        mImageProcessHelper.start(mTexture);
     }
 
     @Override
     public boolean isWcgContent() {
-        return mWcgContent;
+        return mTexture.isWcgContent();
     }
 
     @Override
@@ -107,30 +106,12 @@
         mProgram.useGLProgram(
                 R.raw.image_wallpaper_vertex_shader, R.raw.image_wallpaper_fragment_shader);
 
-        if (!loadBitmap()) {
-            Log.w(TAG, "reload bitmap failed!");
-        }
-
-        mWallpaper.setup(mBitmap);
-        mBitmap = null;
-    }
-
-    protected boolean loadBitmap() {
-        if (DEBUG) {
-            Log.d(TAG, "loadBitmap: mBitmap=" + mBitmap);
-        }
-        if (mWallpaperManager != null && mBitmap == null) {
-            mBitmap = mWallpaperManager.getBitmap(false /* hardware */);
-            mWcgContent = mWallpaperManager.wallpaperSupportsWcg(WallpaperManager.FLAG_SYSTEM);
-            mWallpaperManager.forgetLoadedWallpaper();
-            if (mBitmap != null) {
-                mSurfaceSize.set(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
+        mTexture.use(bitmap -> {
+            if (bitmap == null) {
+                Log.w(TAG, "reload texture failed!");
             }
-        }
-        if (DEBUG) {
-            Log.d(TAG, "loadBitmap done");
-        }
-        return mBitmap != null;
+            mWallpaper.setup(bitmap);
+        });
     }
 
     @Override
@@ -174,6 +155,8 @@
 
     @Override
     public Size reportSurfaceSize() {
+        mTexture.use(null);
+        mSurfaceSize.set(mTexture.getTextureDimensions());
         return new Size(mSurfaceSize.width(), mSurfaceSize.height());
     }
 
@@ -235,7 +218,69 @@
         out.print(prefix); out.print("mYOffset="); out.print(mYOffset);
         out.print(prefix); out.print("threshold="); out.print(mImageProcessHelper.getThreshold());
         out.print(prefix); out.print("mReveal="); out.print(mImageRevealHelper.getReveal());
-        out.print(prefix); out.print("mWcgContent="); out.print(mWcgContent);
+        out.print(prefix); out.print("mWcgContent="); out.print(isWcgContent());
         mWallpaper.dump(prefix, fd, out, args);
     }
+
+    static class WallpaperTexture {
+        private final AtomicInteger mRefCount;
+        private final Rect mDimensions;
+        private final WallpaperManager mWallpaperManager;
+        private Bitmap mBitmap;
+        private boolean mWcgContent;
+
+        private WallpaperTexture(WallpaperManager wallpaperManager) {
+            mWallpaperManager = wallpaperManager;
+            mRefCount = new AtomicInteger();
+            mDimensions = new Rect();
+        }
+
+        public void use(Consumer<Bitmap> consumer) {
+            mRefCount.incrementAndGet();
+            synchronized (mRefCount) {
+                if (mBitmap == null) {
+                    mBitmap = mWallpaperManager.getBitmap(false /* hardware */);
+                    mWcgContent = mWallpaperManager.wallpaperSupportsWcg(
+                            WallpaperManager.FLAG_SYSTEM);
+                    mWallpaperManager.forgetLoadedWallpaper();
+                    if (mBitmap != null) {
+                        mDimensions.set(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
+                    } else {
+                        Log.w(TAG, "Can't get bitmap");
+                    }
+                }
+            }
+            if (consumer != null) {
+                consumer.accept(mBitmap);
+            }
+            synchronized (mRefCount) {
+                final int count = mRefCount.decrementAndGet();
+                if (count == 0 && mBitmap != null) {
+                    if (DEBUG) {
+                        Log.v(TAG, "WallpaperTexture: release 0x" + getHash()
+                                + ", refCount=" + count);
+                    }
+                    mBitmap.recycle();
+                    mBitmap = null;
+                }
+            }
+        }
+
+        private boolean isWcgContent() {
+            return mWcgContent;
+        }
+
+        private String getHash() {
+            return mBitmap != null ? Integer.toHexString(mBitmap.hashCode()) : "null";
+        }
+
+        private Rect getTextureDimensions() {
+            return mDimensions;
+        }
+
+        @Override
+        public String toString() {
+            return "{" + getHash() + ", " + mRefCount.get() + "}";
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
index 678cfd2..475023e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
@@ -134,7 +134,7 @@
         return new ImageWallpaperRenderer(mMockContext, engine) {
             @Override
             public void startProcessingImage() {
-                loadBitmap();
+                // No - Op
             }
         };
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java
index d881fd5..d61be37 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java
@@ -92,6 +92,7 @@
 
             mWpmSpy.setBitmap(p3Bitmap);
             ImageWallpaperRenderer rendererP3 = new ImageWallpaperRenderer(mContext, mSurfaceProxy);
+            rendererP3.reportSurfaceSize();
             assertThat(rendererP3.isWcgContent()).isTrue();
 
             mWpmSpy.setBitmap(srgbBitmap);