DO NOT MERGE Updating AnimationSpec and related internal APIs to use GraphicBuffer.

- This reduces the copy of the hardware bitmap when it is
  parceled/unparceled.

Bug: 38507414
Bug: 62021436
Test: Launch Overview to/from app, ensure that the header bar shows
Test: go/wm-smoke

Change-Id: I85a9a59a0a3699d1642158061d10fddef34393c3
Signed-off-by: Winson Chung <winsonc@google.com>
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 53608fb..c4d5116 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -25,6 +25,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.GraphicBuffer;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Handler;
@@ -826,7 +828,11 @@
             case ANIM_THUMBNAIL_SCALE_DOWN:
             case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
             case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
-                mThumbnail = (Bitmap) opts.getParcelable(KEY_ANIM_THUMBNAIL);
+                // Unpackage the GraphicBuffer from the parceled thumbnail
+                final GraphicBuffer buffer = opts.getParcelable(KEY_ANIM_THUMBNAIL);
+                if (buffer != null) {
+                    mThumbnail = Bitmap.createHardwareBitmap(buffer);
+                }
                 mStartX = opts.getInt(KEY_ANIM_START_X, 0);
                 mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
                 mWidth = opts.getInt(KEY_ANIM_WIDTH, 0);
@@ -919,9 +925,14 @@
         return mCustomInPlaceResId;
     }
 
-    /** @hide */
-    public Bitmap getThumbnail() {
-        return mThumbnail;
+    /**
+     * The thumbnail is copied into a hardware bitmap when it is bundled and sent to the system, so
+     * it should always be backed by a GraphicBuffer on the other end.
+     *
+     * @hide
+     */
+    public GraphicBuffer getThumbnail() {
+        return mThumbnail.createGraphicBufferHandle();
     }
 
     /** @hide */
@@ -1230,7 +1241,14 @@
             case ANIM_THUMBNAIL_SCALE_DOWN:
             case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
             case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
-                b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail);
+                // Once we parcel the thumbnail for transfering over to the system, create a copy of
+                // the bitmap to a hardware bitmap and pass through the GraphicBuffer
+                if (mThumbnail == null) {
+                    b.putParcelable(KEY_ANIM_THUMBNAIL, null);
+                } else {
+                    final Bitmap hwBitmap = mThumbnail.copy(Config.HARDWARE, true /* immutable */);
+                    b.putParcelable(KEY_ANIM_THUMBNAIL, hwBitmap.createGraphicBufferHandle());
+                }
                 b.putInt(KEY_ANIM_START_X, mStartX);
                 b.putInt(KEY_ANIM_START_Y, mStartY);
                 b.putInt(KEY_ANIM_WIDTH, mWidth);
diff --git a/core/java/android/view/AppTransitionAnimationSpec.java b/core/java/android/view/AppTransitionAnimationSpec.java
index c6e1989..86a5fb7 100644
--- a/core/java/android/view/AppTransitionAnimationSpec.java
+++ b/core/java/android/view/AppTransitionAnimationSpec.java
@@ -1,6 +1,6 @@
 package android.view;
 
-import android.graphics.Bitmap;
+import android.graphics.GraphicBuffer;
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -15,19 +15,19 @@
  */
 public class AppTransitionAnimationSpec implements Parcelable {
     public final int taskId;
-    public final Bitmap bitmap;
+    public final GraphicBuffer buffer;
     public final Rect rect;
 
-    public AppTransitionAnimationSpec(int taskId, Bitmap bitmap, Rect rect) {
+    public AppTransitionAnimationSpec(int taskId, GraphicBuffer buffer, Rect rect) {
         this.taskId = taskId;
-        this.bitmap = bitmap;
         this.rect = rect;
+        this.buffer = buffer;
     }
 
     public AppTransitionAnimationSpec(Parcel in) {
         taskId = in.readInt();
-        bitmap = in.readParcelable(null);
         rect = in.readParcelable(null);
+        buffer = in.readParcelable(null);
     }
 
     @Override
@@ -38,9 +38,8 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(taskId);
-        dest.writeParcelable(bitmap, 0 /* flags */);
         dest.writeParcelable(rect, 0 /* flags */);
-
+        dest.writeParcelable(buffer, 0);
     }
 
     public static final Parcelable.Creator<AppTransitionAnimationSpec> CREATOR
@@ -56,6 +55,6 @@
 
     @Override
     public String toString() {
-        return "{taskId: " + taskId + ", bitmap: " + bitmap + ", rect: " + rect + "}";
+        return "{taskId: " + taskId + ", buffer: " + buffer + ", rect: " + rect + "}";
     }
 }
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index a2ff4f7..58a6a5e 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -26,6 +26,7 @@
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
+import android.graphics.GraphicBuffer;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -96,9 +97,9 @@
             int startHeight);
     void overridePendingAppTransitionClipReveal(int startX, int startY,
             int startWidth, int startHeight);
-    void overridePendingAppTransitionThumb(in Bitmap srcThumb, int startX, int startY,
+    void overridePendingAppTransitionThumb(in GraphicBuffer srcThumb, int startX, int startY,
             IRemoteCallback startedCallback, boolean scaleUp);
-    void overridePendingAppTransitionAspectScaledThumb(in Bitmap srcThumb, int startX,
+    void overridePendingAppTransitionAspectScaledThumb(in GraphicBuffer srcThumb, int startX,
             int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback,
             boolean scaleUp);
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index e2e9b1b..611169f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -30,6 +30,7 @@
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.GraphicBuffer;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
@@ -239,8 +240,6 @@
         }
     });
 
-    protected Bitmap mThumbTransitionBitmapCache;
-
     public RecentsImpl(Context context) {
         mContext = context;
         mHandler = new Handler();
@@ -775,14 +774,6 @@
                 }
                 mHeaderBar.layout(0, 0, taskViewWidth, mTaskBarHeight);
             }
-
-            // Update the transition bitmap to match the new header bar height
-            if (mThumbTransitionBitmapCache == null ||
-                    (mThumbTransitionBitmapCache.getWidth() != taskViewWidth) ||
-                    (mThumbTransitionBitmapCache.getHeight() != mTaskBarHeight)) {
-                mThumbTransitionBitmapCache = Bitmap.createBitmap(taskViewWidth,
-                        mTaskBarHeight, Bitmap.Config.ARGB_8888);
-            }
         }
     }
 
@@ -864,8 +855,7 @@
                     mTmpTransform = stackLayout.getStackTransformScreenCoordinates(task,
                             stackScroller.getStackScroll(), mTmpTransform, null,
                             windowOverrideRect);
-                    Bitmap thumbnail = drawThumbnailTransitionBitmap(task, mTmpTransform,
-                            mThumbTransitionBitmapCache);
+                    GraphicBuffer thumbnail = drawThumbnailTransitionBitmap(task, mTmpTransform);
                     Rect toTaskRect = new Rect();
                     mTmpTransform.rect.round(toTaskRect);
                     specs.add(new AppTransitionAnimationSpec(task.key.id, thumbnail, toTaskRect));
@@ -887,8 +877,8 @@
                             () -> {
                         Rect rect = new Rect();
                         toTaskRect.round(rect);
-                        Bitmap thumbnail = drawThumbnailTransitionBitmap(toTask, toTransform,
-                                mThumbTransitionBitmapCache);
+                        GraphicBuffer thumbnail = drawThumbnailTransitionBitmap(toTask,
+                                toTransform);
                         return Lists.newArrayList(new AppTransitionAnimationSpec(
                                 toTask.key.id, thumbnail, rect));
                     });
@@ -924,19 +914,19 @@
     /**
      * Draws the header of a task used for the window animation into a bitmap.
      */
-    private Bitmap drawThumbnailTransitionBitmap(Task toTask, TaskViewTransform toTransform,
-            Bitmap thumbnail) {
+    private GraphicBuffer drawThumbnailTransitionBitmap(Task toTask,
+            TaskViewTransform toTransform) {
         SystemServicesProxy ssp = Recents.getSystemServices();
         if (toTransform != null && toTask.key != null) {
             synchronized (mHeaderBarLock) {
                 boolean disabledInSafeMode = !toTask.isSystemApp && ssp.isInSafeMode();
-                mHeaderBar.onTaskViewSizeChanged((int) toTransform.rect.width(),
-                        (int) toTransform.rect.height());
+                int width = (int) toTransform.rect.width();
+                int height = (int) toTransform.rect.height();
+                mHeaderBar.onTaskViewSizeChanged(width, height);
                 if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
-                    thumbnail.eraseColor(0xFFff0000);
+                    return RecentsTransitionHelper.drawViewIntoGraphicBuffer(width, mTaskBarHeight,
+                            null, 1f, 0xFFff0000);
                 } else {
-                    thumbnail.eraseColor(0);
-                    Canvas c = new Canvas(thumbnail);
                     // Workaround for b/27815919, reset the callback so that we do not trigger an
                     // invalidate on the header bar as a result of updating the icon
                     Drawable icon = mHeaderBar.getIconView().getDrawable();
@@ -947,11 +937,10 @@
                             disabledInSafeMode);
                     mHeaderBar.onTaskDataLoaded();
                     mHeaderBar.setDimAlpha(toTransform.dimAlpha);
-                    mHeaderBar.draw(c);
-                    c.setBitmap(null);
+                    return RecentsTransitionHelper.drawViewIntoGraphicBuffer(width, mTaskBarHeight,
+                            mHeaderBar, 1f, 0);
                 }
             }
-            return thumbnail.createAshmemBitmap();
         }
         return null;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index cd0d515..21dfe8c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -30,6 +30,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.GraphicBuffer;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Handler;
@@ -41,6 +42,7 @@
 import android.view.IAppTransitionAnimationSpecsFuture;
 import android.view.RenderNode;
 import android.view.ThreadedRenderer;
+import android.view.View;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.systemui.recents.Recents;
@@ -267,8 +269,8 @@
             Rect bounds) {
         mTmpTransform.fillIn(taskView);
         Task task = taskView.getTask();
-        Bitmap thumbnail = RecentsTransitionHelper.composeTaskBitmap(taskView, mTmpTransform);
-        return Collections.singletonList(new AppTransitionAnimationSpec(task.key.id, thumbnail,
+        GraphicBuffer buffer = RecentsTransitionHelper.composeTaskBitmap(taskView, mTmpTransform);
+        return Collections.singletonList(new AppTransitionAnimationSpec(task.key.id, buffer,
                 bounds));
     }
 
@@ -349,7 +351,7 @@
         return new AppTransitionAnimationSpec(task.key.id, null, taskRect);
     }
 
-    public static Bitmap composeTaskBitmap(TaskView taskView, TaskViewTransform transform) {
+    public static GraphicBuffer composeTaskBitmap(TaskView taskView, TaskViewTransform transform) {
         float scale = transform.scale;
         int fromWidth = (int) (transform.rect.width() * scale);
         int fromHeight = (int) (transform.rect.height() * scale);
@@ -357,31 +359,17 @@
             Log.e(TAG, "Could not compose thumbnail for task: " + taskView.getTask() +
                     " at transform: " + transform);
 
-            Bitmap b = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
-            b.eraseColor(Color.TRANSPARENT);
-            return b;
+            return drawViewIntoGraphicBuffer(1, 1, null, 1f, 0x00ffffff);
         } else {
             if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
-                Bitmap b = Bitmap.createBitmap(fromWidth, fromHeight, Bitmap.Config.ARGB_8888);
-                b.eraseColor(0xFFff0000);
-                return b.createAshmemBitmap();
+                return drawViewIntoGraphicBuffer(fromWidth, fromHeight, null, 1f, 0xFFff0000);
             } else {
-
-                // Create a hardware bitmap to render the TaskView into because it may be bound to a
-                // snapshot that is backed by a GraphicBuffer
-                RenderNode node = RenderNode.create("RecentsTransitionHelper", null);
-                node.setLeftTopRightBottom(0, 0, fromWidth, fromHeight);
-                node.setClipToBounds(false);
-                DisplayListCanvas c = node.start(fromWidth, fromHeight);
-                c.scale(scale, scale);
-                taskView.draw(c);
-                node.end(c);
-                return ThreadedRenderer.createHardwareBitmap(node, fromWidth, fromHeight);
+                return drawViewIntoGraphicBuffer(fromWidth, fromHeight, taskView, scale, 0);
             }
         }
     }
 
-    private static Bitmap composeHeaderBitmap(TaskView taskView,
+    private static GraphicBuffer composeHeaderBitmap(TaskView taskView,
             TaskViewTransform transform) {
         float scale = transform.scale;
         int headerWidth = (int) (transform.rect.width());
@@ -390,16 +378,30 @@
             return null;
         }
 
-        Bitmap b = Bitmap.createBitmap(headerWidth, headerHeight, Bitmap.Config.ARGB_8888);
         if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
-            b.eraseColor(0xFFff0000);
+            return drawViewIntoGraphicBuffer(headerWidth, headerHeight, null, 1f, 0xFFff0000);
         } else {
-            Canvas c = new Canvas(b);
-            c.scale(scale, scale);
-            taskView.mHeaderView.draw(c);
-            c.setBitmap(null);
+            return drawViewIntoGraphicBuffer(headerWidth, headerHeight, taskView.mHeaderView,
+                    scale, 0);
         }
-        return b.createAshmemBitmap();
+    }
+
+    public static GraphicBuffer drawViewIntoGraphicBuffer(int bufferWidth, int bufferHeight,
+            View view, float scale, int eraseColor) {
+        RenderNode node = RenderNode.create("RecentsTransition", null);
+        node.setLeftTopRightBottom(0, 0, bufferWidth, bufferHeight);
+        node.setClipToBounds(false);
+        DisplayListCanvas c = node.start(bufferWidth, bufferHeight);
+        c.scale(scale, scale);
+        if (eraseColor != 0) {
+            c.drawColor(eraseColor);
+        }
+        if (view != null) {
+            view.draw(c);
+        }
+        node.end(c);
+        return ThreadedRenderer.createHardwareBitmap(node, bufferWidth, bufferHeight)
+                .createGraphicBufferHandle();
     }
 
     /**
@@ -407,7 +409,7 @@
      */
     private static AppTransitionAnimationSpec composeAnimationSpec(TaskStackView stackView,
             TaskView taskView, TaskViewTransform transform, boolean addHeaderBitmap) {
-        Bitmap b = null;
+        GraphicBuffer b = null;
         if (addHeaderBitmap) {
             b = composeHeaderBitmap(taskView, transform);
             if (b == null) {
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 5636e19..9698a8f 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -130,6 +130,7 @@
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
+import android.graphics.GraphicBuffer;
 import android.graphics.Rect;
 import android.os.Build;
 import android.os.Bundle;
@@ -1419,19 +1420,17 @@
                     break;
                 case ANIM_THUMBNAIL_SCALE_UP:
                 case ANIM_THUMBNAIL_SCALE_DOWN:
-                    boolean scaleUp = (animationType == ANIM_THUMBNAIL_SCALE_UP);
-                    service.mWindowManager.overridePendingAppTransitionThumb(
-                            pendingOptions.getThumbnail(),
+                    final boolean scaleUp = (animationType == ANIM_THUMBNAIL_SCALE_UP);
+                    final GraphicBuffer buffer = pendingOptions.getThumbnail();
+                    service.mWindowManager.overridePendingAppTransitionThumb(buffer,
                             pendingOptions.getStartX(), pendingOptions.getStartY(),
                             pendingOptions.getOnAnimationStartListener(),
                             scaleUp);
                     if (intent.getSourceBounds() == null) {
                         intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
                                 pendingOptions.getStartY(),
-                                pendingOptions.getStartX()
-                                        + pendingOptions.getThumbnail().getWidth(),
-                                pendingOptions.getStartY()
-                                        + pendingOptions.getThumbnail().getHeight()));
+                                pendingOptions.getStartX() + buffer.getWidth(),
+                                pendingOptions.getStartY() + buffer.getHeight()));
                     }
                     break;
                 case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index a38addb..9d8f124 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -52,6 +52,7 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
+import android.graphics.GraphicBuffer;
 import android.graphics.Path;
 import android.graphics.Rect;
 import android.os.Binder;
@@ -346,12 +347,12 @@
         mAppTransitionState = APP_STATE_TIMEOUT;
     }
 
-    Bitmap getAppTransitionThumbnailHeader(int taskId) {
+    GraphicBuffer getAppTransitionThumbnailHeader(int taskId) {
         AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(taskId);
         if (spec == null) {
             spec = mDefaultNextAppTransitionAnimationSpec;
         }
-        return spec != null ? spec.bitmap : null;
+        return spec != null ? spec.buffer : null;
     }
 
     /** Returns whether the next thumbnail transition is aspect scaled up. */
@@ -716,9 +717,9 @@
     }
 
     private void putDefaultNextAppTransitionCoordinates(int left, int top, int width, int height,
-            Bitmap bitmap) {
+            GraphicBuffer buffer) {
         mDefaultNextAppTransitionAnimationSpec = new AppTransitionAnimationSpec(-1 /* taskId */,
-                bitmap, new Rect(left, top, left + width, top + height));
+                buffer, new Rect(left, top, left + width, top + height));
     }
 
     /**
@@ -943,7 +944,7 @@
      * when a thumbnail is specified with the pending animation override.
      */
     Animation createThumbnailAspectScaleAnimationLocked(Rect appRect, @Nullable Rect contentInsets,
-            Bitmap thumbnailHeader, final int taskId, int uiMode, int orientation) {
+            GraphicBuffer thumbnailHeader, final int taskId, int uiMode, int orientation) {
         Animation a;
         final int thumbWidthI = thumbnailHeader.getWidth();
         final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
@@ -1296,7 +1297,7 @@
      * when a thumbnail is specified with the pending animation override.
      */
     Animation createThumbnailScaleAnimationLocked(int appWidth, int appHeight, int transit,
-            Bitmap thumbnailHeader) {
+            GraphicBuffer thumbnailHeader) {
         Animation a;
         getDefaultNextAppTransitionStartRect(mTmpRect);
         final int thumbWidthI = thumbnailHeader.getWidth();
@@ -1341,7 +1342,7 @@
             int transit, int taskId) {
         final int appWidth = containingFrame.width();
         final int appHeight = containingFrame.height();
-        Bitmap thumbnailHeader = getAppTransitionThumbnailHeader(taskId);
+        final GraphicBuffer thumbnailHeader = getAppTransitionThumbnailHeader(taskId);
         Animation a;
         getDefaultNextAppTransitionStartRect(mTmpRect);
         final int thumbWidthI = thumbnailHeader != null ? thumbnailHeader.getWidth() : appWidth;
@@ -1714,7 +1715,7 @@
         }
     }
 
-    void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY,
+    void overridePendingAppTransitionThumb(GraphicBuffer srcThumb, int startX, int startY,
                                            IRemoteCallback startedCallback, boolean scaleUp) {
         if (isTransitionSet()) {
             clear();
@@ -1729,7 +1730,7 @@
         }
     }
 
-    void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX, int startY,
+    void overridePendingAppTransitionAspectScaledThumb(GraphicBuffer srcThumb, int startX, int startY,
             int targetWidth, int targetHeight, IRemoteCallback startedCallback, boolean scaleUp) {
         if (isTransitionSet()) {
             clear();
@@ -1763,7 +1764,7 @@
                             // to be set.
                             Rect rect = spec.rect;
                             putDefaultNextAppTransitionCoordinates(rect.left, rect.top,
-                                    rect.width(), rect.height(), spec.bitmap);
+                                    rect.width(), rect.height(), spec.buffer);
                         }
                     }
                 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a15891b..6eb6d01 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -123,6 +123,7 @@
 import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
+import android.graphics.GraphicBuffer;
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
@@ -2629,7 +2630,7 @@
     }
 
     @Override
-    public void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX,
+    public void overridePendingAppTransitionThumb(GraphicBuffer srcThumb, int startX,
             int startY, IRemoteCallback startedCallback, boolean scaleUp) {
         synchronized(mWindowMap) {
             mAppTransition.overridePendingAppTransitionThumb(srcThumb, startX, startY,
@@ -2638,7 +2639,7 @@
     }
 
     @Override
-    public void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX,
+    public void overridePendingAppTransitionAspectScaledThumb(GraphicBuffer srcThumb, int startX,
             int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback,
             boolean scaleUp) {
         synchronized(mWindowMap) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 4a1a705..cb1b2e6 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -38,11 +38,9 @@
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
 
 import android.content.res.Configuration;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
+import android.graphics.GraphicBuffer;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
-import android.os.Binder;
 import android.os.Debug;
 import android.os.Trace;
 import android.util.ArraySet;
@@ -675,8 +673,9 @@
             return;
         }
         final int taskId = appToken.getTask().mTaskId;
-        Bitmap thumbnailHeader = mService.mAppTransition.getAppTransitionThumbnailHeader(taskId);
-        if (thumbnailHeader == null || thumbnailHeader.getConfig() == Bitmap.Config.ALPHA_8) {
+        final GraphicBuffer thumbnailHeader =
+                mService.mAppTransition.getAppTransitionThumbnailHeader(taskId);
+        if (thumbnailHeader == null) {
             if (DEBUG_APP_TRANSITIONS) Slog.d(TAG, "No thumbnail header bitmap for: " + taskId);
             return;
         }
@@ -700,12 +699,10 @@
                 Slog.i(TAG, "  THUMBNAIL " + surfaceControl + ": CREATE");
             }
 
-            // Draw the thumbnail onto the surface
+            // Transfer the thumbnail to the surface
             Surface drawSurface = new Surface();
             drawSurface.copyFrom(surfaceControl);
-            Canvas c = drawSurface.lockCanvas(dirty);
-            c.drawBitmap(thumbnailHeader, 0, 0, null);
-            drawSurface.unlockCanvasAndPost(c);
+            drawSurface.attachAndQueueBuffer(thumbnailHeader);
             drawSurface.release();
 
             // Get the thumbnail animation
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 15fd2ce..deb0e09 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -18,6 +18,7 @@
 
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
+import android.graphics.GraphicBuffer;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -210,13 +211,13 @@
     }
 
     @Override
-    public void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY,
+    public void overridePendingAppTransitionThumb(GraphicBuffer srcThumb, int startX, int startY,
             IRemoteCallback startedCallback, boolean scaleUp) throws RemoteException {
         // TODO Auto-generated method stub
     }
 
     @Override
-    public void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX,
+    public void overridePendingAppTransitionAspectScaledThumb(GraphicBuffer srcThumb, int startX,
             int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback,
             boolean scaleUp) {
         // TODO Auto-generated method stub