Merge "Merge "Merge "Fix race condition when destroying view" into oc-dr1-dev am: b0e5b53bbf am: e5a48c5bcb" into oc-mr1-dev-plus-aosp am: 2db8b9fa36"
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index 8c805ac..d765fd2 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -28,7 +28,6 @@
 import android.graphics.Point;
 import android.net.Uri;
 import android.os.Binder;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.FileObserver;
@@ -73,7 +72,6 @@
 
     private Handler mHandler;
 
-    private static final String MIMETYPE_PDF = "application/pdf";
     private static final String MIMETYPE_JPEG = "image/jpeg";
     private static final String MIMETYPE_JPG = "image/jpg";
     private static final String MIMETYPE_OCTET_STREAM = "application/octet-stream";
@@ -430,13 +428,6 @@
             String documentId, Point sizeHint, CancellationSignal signal)
             throws FileNotFoundException {
         final File file = getFileForDocId(documentId);
-        if (getTypeForFile(file).equals(MIMETYPE_PDF)) {
-            try {
-                return PdfUtils.openPdfThumbnail(file, sizeHint);
-            } catch (Exception e) {
-                Log.v(TAG, "Could not load PDF's thumbnail", e);
-            }
-        }
         return DocumentsContract.openImageThumbnail(file);
     }
 
@@ -466,9 +457,7 @@
 
         final String mimeType = getTypeForFile(file);
         final String displayName = file.getName();
-        // As of right now, we aren't sure on the performance affect of loading all PDF Thumbnails
-        // Until a solution is found, it will be behind a debuggable flag.
-        if (mimeType.startsWith("image/") || mimeType.equals(MIMETYPE_PDF)) {
+        if (mimeType.startsWith("image/")) {
             flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
         }
 
diff --git a/core/java/com/android/internal/content/PdfUtils.java b/core/java/com/android/internal/content/PdfUtils.java
deleted file mode 100644
index 174a9d3..0000000
--- a/core/java/com/android/internal/content/PdfUtils.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2017 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 com.android.internal.content;
-
-import android.annotation.Nullable;
-import android.content.res.AssetFileDescriptor;
-import android.graphics.Bitmap;
-import android.graphics.Matrix;
-import android.graphics.Point;
-import android.graphics.pdf.PdfRenderer;
-import android.os.AsyncTask;
-import android.os.ParcelFileDescriptor;
-
-import libcore.io.IoUtils;
-import libcore.io.Streams;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-/**
- * Utils class for extracting PDF Thumbnails
- */
-public final class PdfUtils {
-
-    private PdfUtils() {
-    }
-
-    /**
-     * Returns the front page of the pdf as a thumbnail
-     * @param file Given PDF File
-     * @param size Cropping of the front page.
-     * @return AssetFileDescriptor containing the thumbnail as a bitmap.
-     * @throws IOException if the file isn't a pdf or if the file doesn't exist.
-     */
-    public static @Nullable AssetFileDescriptor openPdfThumbnail(File file, Point size)
-            throws IOException {
-        // Create the bitmap of the PDF's first page
-        ParcelFileDescriptor pdfDescriptor =
-                ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
-        PdfRenderer renderer = new PdfRenderer(pdfDescriptor);
-        PdfRenderer.Page frontPage = renderer.openPage(0);
-        Bitmap thumbnail = Bitmap.createBitmap(frontPage.getWidth(), frontPage.getHeight(),
-                Bitmap.Config.ARGB_8888);
-
-        frontPage.render(thumbnail, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
-
-        thumbnail = crop(thumbnail, size.x, size.y, .5f, 0f);
-
-        // Create an AssetFileDescriptor that contains the Bitmap's information
-        final ByteArrayOutputStream out = new ByteArrayOutputStream();
-        // Quality is an integer that determines how much compression is used.
-        // However, this integer is ignored when using the PNG format
-        int quality = 100;
-        // The use of Bitmap.CompressFormat.JPEG leads to a black PDF background on the thumbnail
-        thumbnail.compress(Bitmap.CompressFormat.PNG, quality, out);
-
-        final ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
-
-        final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createReliablePipe();
-        new AsyncTask<Object, Object, Object>() {
-            @Override
-            protected Object doInBackground(Object... params) {
-                final FileOutputStream fos = new FileOutputStream(fds[1].getFileDescriptor());
-                try {
-                    Streams.copy(in, fos);
-                } catch (IOException e) {
-                    throw new RuntimeException(e);
-                }
-                IoUtils.closeQuietly(fds[1]);
-                try {
-                    pdfDescriptor.close();
-                } catch (IOException e) {
-                    throw new RuntimeException(e);
-                }
-                return null;
-            }
-        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
-        pdfDescriptor.close();
-        return new AssetFileDescriptor(fds[0], 0, AssetFileDescriptor.UNKNOWN_LENGTH);
-    }
-
-    /**
-     * Returns a new Bitmap copy with a crop effect depending on the crop anchor given. 0.5f is like
-     * {@link android.widget.ImageView.ScaleType#CENTER_CROP}. The crop anchor will be be nudged
-     * so the entire cropped bitmap will fit inside the src. May return the input bitmap if no
-     * scaling is necessary.
-     *
-     *
-     * Example of changing verticalCenterPercent:
-     *   _________            _________
-     *  |         |          |         |
-     *  |         |          |_________|
-     *  |         |          |         |/___0.3f
-     *  |---------|          |_________|\
-     *  |         |<---0.5f  |         |
-     *  |---------|          |         |
-     *  |         |          |         |
-     *  |         |          |         |
-     *  |_________|          |_________|
-     *
-     * @param src original bitmap of any size
-     * @param w desired width in px
-     * @param h desired height in px
-     * @param horizontalCenterPercent determines which part of the src to crop from. Range from 0
-     *                                .0f to 1.0f. The value determines which part of the src
-     *                                maps to the horizontal center of the resulting bitmap.
-     * @param verticalCenterPercent determines which part of the src to crop from. Range from 0
-     *                              .0f to 1.0f. The value determines which part of the src maps
-     *                              to the vertical center of the resulting bitmap.
-     * @return a copy of src conforming to the given width and height, or src itself if it already
-     *         matches the given width and height
-     *
-     */
-    private static Bitmap crop(final Bitmap src, final int w, final int h,
-            final float horizontalCenterPercent, final float verticalCenterPercent) {
-        if (horizontalCenterPercent < 0 || horizontalCenterPercent > 1 || verticalCenterPercent < 0
-                || verticalCenterPercent > 1) {
-            throw new IllegalArgumentException(
-                    "horizontalCenterPercent and verticalCenterPercent must be between 0.0f and "
-                            + "1.0f, inclusive.");
-        }
-        final int srcWidth = src.getWidth();
-        final int srcHeight = src.getHeight();
-        // exit early if no resize/crop needed
-        if (w == srcWidth && h == srcHeight) {
-            return src;
-        }
-        final Matrix m = new Matrix();
-        final float scale = Math.max(
-                (float) w / srcWidth,
-                (float) h / srcHeight);
-        m.setScale(scale, scale);
-        final int srcCroppedW, srcCroppedH;
-        int srcX, srcY;
-        srcCroppedW = Math.round(w / scale);
-        srcCroppedH = Math.round(h / scale);
-        srcX = (int) (srcWidth * horizontalCenterPercent - srcCroppedW / 2);
-        srcY = (int) (srcHeight * verticalCenterPercent - srcCroppedH / 2);
-        // Nudge srcX and srcY to be within the bounds of src
-        srcX = Math.max(Math.min(srcX, srcWidth - srcCroppedW), 0);
-        srcY = Math.max(Math.min(srcY, srcHeight - srcCroppedH), 0);
-        final Bitmap cropped = Bitmap.createBitmap(src, srcX, srcY,
-                srcCroppedW, srcCroppedH, m, true);
-        return cropped;
-    }
-
-}
diff --git a/core/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp
index bfb2511..7b381b4 100644
--- a/core/jni/android/graphics/Picture.cpp
+++ b/core/jni/android/graphics/Picture.cpp
@@ -44,7 +44,7 @@
     mWidth = width;
     mHeight = height;
     SkCanvas* canvas = mRecorder->beginRecording(SkIntToScalar(width), SkIntToScalar(height));
-    return Canvas::create_canvas(canvas, Canvas::XformToSRGB::kDefer);
+    return Canvas::create_canvas(canvas);
 }
 
 void Picture::endRecording() {
diff --git a/core/jni/android/graphics/pdf/PdfDocument.cpp b/core/jni/android/graphics/pdf/PdfDocument.cpp
index abc3599..e2dc52b 100644
--- a/core/jni/android/graphics/pdf/PdfDocument.cpp
+++ b/core/jni/android/graphics/pdf/PdfDocument.cpp
@@ -21,7 +21,6 @@
 
 #include "CreateJavaOutputStreamAdaptor.h"
 
-#include "SkColorSpaceXformCanvas.h"
 #include "SkDocument.h"
 #include "SkPicture.h"
 #include "SkPictureRecorder.h"
@@ -95,10 +94,7 @@
 
             SkCanvas* canvas = document->beginPage(page->mWidth, page->mHeight,
                     &(page->mContentRect));
-            std::unique_ptr<SkCanvas> toSRGBCanvas =
-                    SkCreateColorSpaceXformCanvas(canvas, SkColorSpace::MakeSRGB());
-
-            toSRGBCanvas->drawPicture(page->mPicture);
+            canvas->drawPicture(page->mPicture);
 
             document->endPage();
         }
@@ -131,7 +127,7 @@
     PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
     SkCanvas* canvas = document->startPage(pageWidth, pageHeight,
             contentLeft, contentTop, contentRight, contentBottom);
-    return reinterpret_cast<jlong>(Canvas::create_canvas(canvas, Canvas::XformToSRGB::kDefer));
+    return reinterpret_cast<jlong>(Canvas::create_canvas(canvas));
 }
 
 static void nativeFinishPage(JNIEnv* env, jobject thiz, jlong documentPtr) {
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 9683d06..02c0e19 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -25,7 +25,6 @@
 
 #include <SkCanvasStateUtils.h>
 #include <SkColorFilter.h>
-// TODO remove me!
 #include <SkColorSpaceXformCanvas.h>
 #include <SkDrawable.h>
 #include <SkDeque.h>
@@ -48,25 +47,28 @@
     return new SkiaCanvas(bitmap);
 }
 
-Canvas* Canvas::create_canvas(SkCanvas* skiaCanvas, XformToSRGB xformToSRGB) {
-    return new SkiaCanvas(skiaCanvas, xformToSRGB);
+Canvas* Canvas::create_canvas(SkCanvas* skiaCanvas) {
+    return new SkiaCanvas(skiaCanvas);
 }
 
 SkiaCanvas::SkiaCanvas() {}
 
-SkiaCanvas::SkiaCanvas(SkCanvas* canvas, XformToSRGB xformToSRGB)
-    : mCanvas(canvas)
-{
-    LOG_ALWAYS_FATAL_IF(XformToSRGB::kImmediate == xformToSRGB);
-}
+SkiaCanvas::SkiaCanvas(SkCanvas* canvas) : mCanvas(canvas) {}
 
 SkiaCanvas::SkiaCanvas(const SkBitmap& bitmap) {
     sk_sp<SkColorSpace> cs = bitmap.refColorSpace();
     mCanvasOwned =
             std::unique_ptr<SkCanvas>(new SkCanvas(bitmap, SkCanvas::ColorBehavior::kLegacy));
-    mCanvasWrapper = SkCreateColorSpaceXformCanvas(mCanvasOwned.get(),
-            cs == nullptr ? SkColorSpace::MakeSRGB() : std::move(cs));
-    mCanvas = mCanvasWrapper.get();
+    if (cs.get() == nullptr || cs->isSRGB()) {
+        mCanvas = mCanvasOwned.get();
+    } else {
+        /** The wrapper is needed if we are drawing into a non-sRGB destination, since
+         *  we need to transform all colors (not just bitmaps via filters) into the
+         *  destination's colorspace.
+         */
+        mCanvasWrapper = SkCreateColorSpaceXformCanvas(mCanvasOwned.get(), std::move(cs));
+        mCanvas = mCanvasWrapper.get();
+    }
 }
 
 SkiaCanvas::~SkiaCanvas() {}
@@ -75,6 +77,7 @@
     if (mCanvas != skiaCanvas) {
         mCanvas = skiaCanvas;
         mCanvasOwned.reset();
+        mCanvasWrapper.reset();
     }
     mSaveStack.reset(nullptr);
     mHighContrastText = false;
@@ -88,13 +91,15 @@
     sk_sp<SkColorSpace> cs = bitmap.refColorSpace();
     std::unique_ptr<SkCanvas> newCanvas =
             std::unique_ptr<SkCanvas>(new SkCanvas(bitmap, SkCanvas::ColorBehavior::kLegacy));
-    std::unique_ptr<SkCanvas> newCanvasWrapper = SkCreateColorSpaceXformCanvas(newCanvas.get(),
-            cs == nullptr ? SkColorSpace::MakeSRGB() : std::move(cs));
+    std::unique_ptr<SkCanvas> newCanvasWrapper;
+    if (cs.get() != nullptr && !cs->isSRGB()) {
+        newCanvasWrapper = SkCreateColorSpaceXformCanvas(newCanvas.get(), std::move(cs));
+    }
 
     // deletes the previously owned canvas (if any)
     mCanvasOwned = std::move(newCanvas);
     mCanvasWrapper = std::move(newCanvasWrapper);
-    mCanvas = mCanvasWrapper.get();
+    mCanvas = mCanvasWrapper ? mCanvasWrapper.get() : mCanvasOwned.get();
 
     // clean up the old save stack
     mSaveStack.reset(nullptr);
@@ -531,13 +536,27 @@
 // Canvas draw operations: Bitmaps
 // ----------------------------------------------------------------------------
 
-inline static const SkPaint* addFilter(const SkPaint* origPaint, SkPaint* tmpPaint,
-        sk_sp<SkColorFilter> colorFilter) {
-    if (colorFilter) {
+const SkPaint* SkiaCanvas::addFilter(const SkPaint* origPaint, SkPaint* tmpPaint,
+        sk_sp<SkColorFilter> colorSpaceFilter) {
+    /* We don't apply the colorSpace filter if this canvas is already wrapped with
+     * a SkColorSpaceXformCanvas since it already takes care of converting the
+     * contents of the bitmap into the appropriate colorspace.  The mCanvasWrapper
+     * should only be used if this canvas is backed by a surface/bitmap that is known
+     * to have a non-sRGB colorspace.
+     */
+    if (!mCanvasWrapper && colorSpaceFilter) {
         if (origPaint) {
             *tmpPaint = *origPaint;
         }
-        tmpPaint->setColorFilter(colorFilter);
+
+        if (tmpPaint->getColorFilter()) {
+            tmpPaint->setColorFilter(SkColorFilter::MakeComposeFilter(
+                    tmpPaint->refColorFilter(), colorSpaceFilter));
+            LOG_ALWAYS_FATAL_IF(!tmpPaint->getColorFilter());
+        } else {
+            tmpPaint->setColorFilter(colorSpaceFilter);
+        }
+
         return tmpPaint;
     } else {
         return origPaint;
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index af2c23e..6a01f96 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -37,12 +37,8 @@
      *  @param canvas SkCanvas to handle calls made to this SkiaCanvas. Must
      *      not be NULL. This constructor does not take ownership, so the caller
      *      must guarantee that it remains valid while the SkiaCanvas is valid.
-     *  @param xformToSRGB Indicates if bitmaps should be xformed to the sRGB
-     *      color space before drawing.  This makes sense for software rendering.
-     *      For the picture case, it may make more sense to leave bitmaps as is,
-     *      and handle the xform when replaying the picture.
      */
-    explicit SkiaCanvas(SkCanvas* canvas, XformToSRGB xformToSRGB);
+    explicit SkiaCanvas(SkCanvas* canvas);
 
     virtual ~SkiaCanvas();
 
@@ -182,6 +178,9 @@
     void drawPoints(const float* points, int count, const SkPaint& paint,
             SkCanvas::PointMode mode);
 
+    const SkPaint* addFilter(const SkPaint* origPaint, SkPaint* tmpPaint,
+            sk_sp<SkColorFilter> colorSpaceFilter);
+
     class Clip;
 
     std::unique_ptr<SkCanvas> mCanvasWrapper; // might own a wrapper on the canvas
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index ac8a081..90d98f2 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -98,15 +98,6 @@
     static WARN_UNUSED_RESULT Canvas* create_recording_canvas(int width, int height,
             uirenderer::RenderNode* renderNode = nullptr);
 
-    enum class XformToSRGB {
-        // Transform any Bitmaps to the sRGB color space before drawing.
-        kImmediate,
-
-        // Draw the Bitmap as is.  This likely means that we are recording and that the
-        // transform can be handled at playback time.
-        kDefer,
-    };
-
     /**
      *  Create a new Canvas object which delegates to an SkCanvas.
      *
@@ -114,12 +105,10 @@
      *      delegated to this object. This function will call ref() on the
      *      SkCanvas, and the returned Canvas will unref() it upon
      *      destruction.
-     *  @param xformToSRGB Indicates if bitmaps should be xformed to the sRGB
-     *      color space before drawing.
      *  @return new non-null Canvas Object.  The type of DisplayList produced by this canvas is
      *      determined based on  Properties::getRenderPipelineType().
      */
-    static Canvas* create_canvas(SkCanvas* skiaCanvas, XformToSRGB xformToSRGB);
+    static Canvas* create_canvas(SkCanvas* skiaCanvas);
 
     /**
      *  Provides a Skia SkCanvas interface that acts as a proxy to this Canvas.
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index bb41607..4c1d673 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -150,8 +150,17 @@
         if (origPaint) {
             *tmpPaint = *origPaint;
         }
+
+        sk_sp<SkColorFilter> filter;
+        if (colorFilter && tmpPaint->getColorFilter()) {
+            filter = SkColorFilter::MakeComposeFilter(tmpPaint->refColorFilter(), colorFilter);
+            LOG_ALWAYS_FATAL_IF(!filter);
+        } else {
+            filter = colorFilter;
+        }
+
         tmpPaint->setAntiAlias(false);
-        tmpPaint->setColorFilter(colorFilter);
+        tmpPaint->setColorFilter(filter);
         return tmpPaint;
     } else {
         return origPaint;
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index c048dda..d84b83d 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -45,8 +45,7 @@
         // record the same text draw into a SkPicture and replay it into a Recording canvas
         SkPictureRecorder recorder;
         SkCanvas* skCanvas = recorder.beginRecording(200, 200, NULL, 0);
-        std::unique_ptr<Canvas> pictCanvas(Canvas::create_canvas(skCanvas,
-                Canvas::XformToSRGB::kDefer));
+        std::unique_ptr<Canvas> pictCanvas(Canvas::create_canvas(skCanvas));
         TestUtils::drawUtf8ToCanvas(pictCanvas.get(), text, paint, 25, 25);
         sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
 
@@ -65,7 +64,7 @@
 
 TEST(SkiaCanvas, drawShadowLayer) {
     auto surface = SkSurface::MakeRasterN32Premul(10, 10);
-    SkiaCanvas canvas(surface->getCanvas(), Canvas::XformToSRGB::kDefer);
+    SkiaCanvas canvas(surface->getCanvas());
 
     // clear to white
     canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrc);
@@ -108,27 +107,14 @@
     // The result should be less than fully red, since we convert to Adobe RGB at draw time.
     ASSERT_EQ(0xFF0000DC, *adobeSkBitmap.getAddr32(0, 0));
 
-    // Now try in kDefer mode.  This is a little strange given that, in practice, all software
-    // canvases are kImmediate.
-    SkCanvas skCanvas(skBitmap);
-    SkiaCanvas deferCanvas(&skCanvas, Canvas::XformToSRGB::kDefer);
-    deferCanvas.drawBitmap(*adobeBitmap, 0, 0, nullptr);
-    // The result should be as before, since we deferred the conversion to sRGB.
-    ASSERT_EQ(0xFF0000DC, *skBitmap.getAddr32(0, 0));
-
-    // Test picture recording.  We will kDefer the xform at recording time, but handle it when
-    // we playback to the software canvas.
+    // Test picture recording.
     SkPictureRecorder recorder;
     SkCanvas* skPicCanvas = recorder.beginRecording(1, 1, NULL, 0);
-    SkiaCanvas picCanvas(skPicCanvas, Canvas::XformToSRGB::kDefer);
+    SkiaCanvas picCanvas(skPicCanvas);
     picCanvas.drawBitmap(*adobeBitmap, 0, 0, nullptr);
     sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
 
-    // Playback to a deferred canvas.  The result should be as before.
-    deferCanvas.asSkCanvas()->drawPicture(picture);
-    ASSERT_EQ(0xFF0000DC, *skBitmap.getAddr32(0, 0));
-
-    // Playback to an immediate canvas.  The result should be fully red.
+    // Playback to an software canvas.  The result should be fully red.
     canvas.asSkCanvas()->drawPicture(picture);
     ASSERT_EQ(0xFF0000FF, *skBitmap.getAddr32(0, 0));
 }
@@ -155,7 +141,7 @@
     // Create a picture canvas.
     SkPictureRecorder recorder;
     SkCanvas* skPicCanvas = recorder.beginRecording(1, 1, NULL, 0);
-    SkiaCanvas picCanvas(skPicCanvas, Canvas::XformToSRGB::kDefer);
+    SkiaCanvas picCanvas(skPicCanvas);
     state = picCanvas.captureCanvasState();
 
     // Verify that we cannot get the CanvasState.