Merge "Move print rendering in an isolated process." into lmp-dev
diff --git a/graphics/java/android/graphics/pdf/PdfRenderer.java b/graphics/java/android/graphics/pdf/PdfRenderer.java
index b5d9729..1072b3c 100644
--- a/graphics/java/android/graphics/pdf/PdfRenderer.java
+++ b/graphics/java/android/graphics/pdf/PdfRenderer.java
@@ -195,6 +195,7 @@
     public Page openPage(int index) {
         throwIfClosed();
         throwIfPageOpened();
+        throwIfPageNotInDocument(index);
         mCurrentPage = new Page(index);
         return mCurrentPage;
     }
@@ -237,6 +238,12 @@
         }
     }
 
+    private void throwIfPageNotInDocument(int pageIndex) {
+        if (pageIndex >= mPageCount) {
+            throw new IllegalArgumentException("Invalid page index");
+        }
+    }
+
     /**
      * This class represents a PDF document page for rendering.
      */
diff --git a/packages/PrintSpooler/Android.mk b/packages/PrintSpooler/Android.mk
index 3fbd4d8..4948a02 100644
--- a/packages/PrintSpooler/Android.mk
+++ b/packages/PrintSpooler/Android.mk
@@ -19,6 +19,7 @@
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES += src/com/android/printspooler/renderer/IPdfRenderer.aidl
 
 LOCAL_PACKAGE_NAME := PrintSpooler
 
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index 540a2f3..9efda2f 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -53,6 +53,11 @@
             android:permission="android.permission.BIND_PRINT_SPOOLER_SERVICE">
         </service>
 
+        <service
+            android:name=".renderer.PdfRendererService"
+            android:isolatedProcess="true">
+        </service>
+
         <activity
             android:name=".ui.PrintActivity"
             android:configChanges="orientation|screenSize"
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
index c3ddad9..cd2ccbd 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
@@ -17,24 +17,31 @@
 package com.android.printspooler.model;
 
 import android.app.ActivityManager;
+import android.content.ComponentName;
 import android.content.Context;
-import android.content.res.Configuration;
+import android.content.Intent;
+import android.content.ServiceConnection;
 import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.graphics.Color;
-import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
-import android.graphics.pdf.PdfRenderer;
 import android.os.AsyncTask;
+import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
-import android.print.PrintAttributes.MediaSize;
-import android.print.PrintAttributes.Margins;
+import android.os.RemoteException;
+import android.print.PrintAttributes;
 import android.print.PrintDocumentInfo;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.view.View;
+import com.android.internal.annotations.GuardedBy;
+import com.android.printspooler.renderer.IPdfRenderer;
+import com.android.printspooler.renderer.PdfRendererService;
 import dalvik.system.CloseGuard;
+import libcore.io.IoUtils;
 
+import java.io.FileDescriptor;
 import java.io.IOException;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
@@ -55,9 +62,6 @@
 
     private static final int BYTES_PER_MEGABYTE = 1048576;
 
-    private static final int MILS_PER_INCH = 1000;
-    private static final int POINTS_IN_INCH = 72;
-
     private final CloseGuard mCloseGuard = CloseGuard.get();
 
     private final ArrayMap<Integer, PageContentProvider> mPageContentProviders =
@@ -115,7 +119,6 @@
         if (DEBUG) {
             Log.i(LOG_TAG, "STATE_DESTROYED");
         }
-        throwIfNotClosed();
         doDestroy();
     }
 
@@ -351,15 +354,13 @@
     public static final class RenderSpec {
         final int bitmapWidth;
         final int bitmapHeight;
-        final MediaSize mediaSize;
-        final Margins minMargins;
+        final PrintAttributes printAttributes;
 
         public RenderSpec(int bitmapWidth, int bitmapHeight,
-                MediaSize mediaSize, Margins minMargins) {
+                PrintAttributes printAttributes) {
             this.bitmapWidth = bitmapWidth;
             this.bitmapHeight = bitmapHeight;
-            this.mediaSize = mediaSize;
-            this.minMargins = minMargins;
+            this.printAttributes = printAttributes;
         }
 
         @Override
@@ -380,18 +381,11 @@
             if (bitmapWidth != other.bitmapWidth) {
                 return false;
             }
-            if (mediaSize != null) {
-                if (!mediaSize.equals(other.mediaSize)) {
+            if (printAttributes != null) {
+                if (!printAttributes.equals(other.printAttributes)) {
                     return false;
                 }
-            } else if (other.mediaSize != null) {
-                return false;
-            }
-            if (minMargins != null) {
-                if (!minMargins.equals(other.minMargins)) {
-                    return false;
-                }
-            } else if (other.minMargins != null) {
+            } else if (other.printAttributes != null) {
                 return false;
             }
             return true;
@@ -407,8 +401,7 @@
         public int hashCode() {
             int result = bitmapWidth;
             result = 31 * result + bitmapHeight;
-            result = 31 * result + (mediaSize != null ? mediaSize.hashCode() : 0);
-            result = 31 * result + (minMargins != null ? minMargins.hashCode() : 0);
+            result = 31 * result + (printAttributes != null ? printAttributes.hashCode() : 0);
             return result;
         }
     }
@@ -440,13 +433,11 @@
         }
     }
 
-    private static int pointsFromMils(int mils) {
-        return (int) (((float) mils / MILS_PER_INCH) * POINTS_IN_INCH);
-    }
-
-    private static class AsyncRenderer {
+    private static final class AsyncRenderer implements ServiceConnection {
         private static final int MALFORMED_PDF_FILE_ERROR = -2;
 
+        private final Object mLock = new Object();
+
         private final Context mContext;
 
         private final PageContentLruCache mPageContentCache;
@@ -457,8 +448,8 @@
 
         private int mPageCount = PrintDocumentInfo.PAGE_COUNT_UNKNOWN;
 
-        // Accessed only by the executor thread.
-        private PdfRenderer mRenderer;
+        @GuardedBy("mLock")
+        private IPdfRenderer mRenderer;
 
         public AsyncRenderer(Context context, OnMalformedPdfFileListener malformedPdfFileListener) {
             mContext = context;
@@ -470,6 +461,21 @@
             mPageContentCache = new PageContentLruCache(cacheSizeInBytes);
         }
 
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            synchronized (mLock) {
+                mRenderer = IPdfRenderer.Stub.asInterface(service);
+                mLock.notifyAll();
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            synchronized (mLock) {
+                mRenderer = null;
+            }
+        }
+
         public void open(final ParcelFileDescriptor source, final Runnable callback) {
             // Opening a new document invalidates the cache as it has pages
             // from the last document. We keep the cache even when the document
@@ -479,13 +485,27 @@
 
             new AsyncTask<Void, Void, Integer>() {
                 @Override
+                protected void onPreExecute() {
+                    Intent intent = new Intent(mContext, PdfRendererService.class);
+                    mContext.bindService(intent, AsyncRenderer.this, Context.BIND_AUTO_CREATE);
+                }
+
+                @Override
                 protected Integer doInBackground(Void... params) {
-                    try {
-                        mRenderer = new PdfRenderer(source);
-                        return mRenderer.getPageCount();
-                    } catch (IOException ioe) {
-                        Log.e(LOG_TAG, "Cannot open PDF document");
-                        return MALFORMED_PDF_FILE_ERROR;
+                    synchronized (mLock) {
+                        while (mRenderer == null) {
+                            try {
+                                mLock.wait();
+                            } catch (InterruptedException ie) {
+                                /* ignore */
+                            }
+                        }
+                        try {
+                            return mRenderer.openDocument(source);
+                        } catch (RemoteException re) {
+                            Log.e(LOG_TAG, "Cannot open PDF document");
+                            return MALFORMED_PDF_FILE_ERROR;
+                        }
                     }
                 }
 
@@ -510,7 +530,13 @@
             new AsyncTask<Void, Void, Void>() {
                 @Override
                 protected Void doInBackground(Void... params) {
-                    mRenderer.close();
+                    synchronized (mLock) {
+                        try {
+                            mRenderer.closeDocument();
+                        } catch (RemoteException re) {
+                            /* ignore */
+                        }
+                    }
                     return null;
                 }
 
@@ -525,8 +551,19 @@
         }
 
         public void destroy() {
-            mPageContentCache.invalidate();
-            mPageContentCache.clear();
+            new AsyncTask<Void, Void, Void>() {
+                @Override
+                protected Void doInBackground(Void... params) {
+                    return null;
+                }
+
+                @Override
+                public void onPostExecute(Void result) {
+                    mContext.unbindService(AsyncRenderer.this);
+                    mPageContentCache.invalidate();
+                    mPageContentCache.clear();
+                }
+            }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
         }
 
         public void startPreload(int firstShownPage, int lastShownPage, RenderSpec renderSpec) {
@@ -752,62 +789,30 @@
                     return mRenderedPage;
                 }
 
-                PdfRenderer.Page page = mRenderer.openPage(mPageIndex);
-
-                if (isCancelled()) {
-                    page.close();
-                    return mRenderedPage;
-                }
-
                 Bitmap bitmap = mRenderedPage.content.getBitmap();
 
-                final int srcWidthPts = page.getWidth();
-                final int srcHeightPts = page.getHeight();
+                ParcelFileDescriptor[] pipe = null;
+                try {
+                    pipe = ParcelFileDescriptor.createPipe();
+                    ParcelFileDescriptor source = pipe[0];
+                    ParcelFileDescriptor destination = pipe[1];
 
-                final int dstWidthPts = pointsFromMils(mRenderSpec.mediaSize.getWidthMils());
-                final int dstHeightPts = pointsFromMils(mRenderSpec.mediaSize.getHeightMils());
+                    mRenderer.renderPage(mPageIndex, bitmap.getWidth(), bitmap.getHeight(),
+                            mRenderSpec.printAttributes, destination);
 
-                final boolean scaleContent = mRenderer.shouldScaleForPrinting();
-                final boolean contentLandscape = !mRenderSpec.mediaSize.isPortrait();
+                    // We passed the file descriptor to the other side which took
+                    // ownership, so close our copy for the write to complete.
+                    destination.close();
 
-                final float displayScale;
-                Matrix matrix = new Matrix();
-
-                if (scaleContent) {
-                    displayScale = Math.min((float) bitmap.getWidth() / srcWidthPts,
-                            (float) bitmap.getHeight() / srcHeightPts);
-                } else {
-                    if (contentLandscape) {
-                        displayScale = (float) bitmap.getHeight() / dstHeightPts;
-                    } else {
-                        displayScale = (float) bitmap.getWidth() / dstWidthPts;
-                    }
+                    BitmapFactory.Options options = new BitmapFactory.Options();
+                    options.inBitmap = bitmap;
+                    BitmapFactory.decodeFileDescriptor(source.getFileDescriptor(), null, options);
+                } catch (IOException|RemoteException e) {
+                    Log.e(LOG_TAG, "Error rendering page:" + mPageIndex, e);
+                } finally {
+                    IoUtils.closeQuietly(pipe[0]);
+                    IoUtils.closeQuietly(pipe[1]);
                 }
-                matrix.postScale(displayScale, displayScale);
-
-                Configuration configuration = mContext.getResources().getConfiguration();
-                if (configuration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
-                    matrix.postTranslate(bitmap.getWidth() - srcWidthPts * displayScale, 0);
-                }
-
-                final int paddingLeftPts = pointsFromMils(mRenderSpec.minMargins.getLeftMils());
-                final int paddingTopPts = pointsFromMils(mRenderSpec.minMargins.getTopMils());
-                final int paddingRightPts = pointsFromMils(mRenderSpec.minMargins.getRightMils());
-                final int paddingBottomPts = pointsFromMils(mRenderSpec.minMargins.getBottomMils());
-
-                Rect clip = new Rect();
-                clip.left = (int) (paddingLeftPts * displayScale);
-                clip.top = (int) (paddingTopPts * displayScale);
-                clip.right = (int) (bitmap.getWidth() - paddingRightPts * displayScale);
-                clip.bottom = (int) (bitmap.getHeight() - paddingBottomPts * displayScale);
-
-                if (DEBUG) {
-                    Log.i(LOG_TAG, "Rendering page:" + mPageIndex + " of " + mPageCount);
-                }
-
-                page.render(bitmap, clip, matrix, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
-
-                page.close();
 
                 return mRenderedPage;
             }
diff --git a/packages/PrintSpooler/src/com/android/printspooler/renderer/IPdfRenderer.aidl b/packages/PrintSpooler/src/com/android/printspooler/renderer/IPdfRenderer.aidl
new file mode 100644
index 0000000..1fba2b1
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/renderer/IPdfRenderer.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014 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.printspooler.renderer;
+
+import android.graphics.Rect;
+import android.os.ParcelFileDescriptor;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+
+/**
+ * Interface for communication with a remote pdf renderer.
+ */
+interface IPdfRenderer {
+    int openDocument(in ParcelFileDescriptor source);
+    oneway void renderPage(int pageIndex, int bitmapWidth, int bitmapHeight,
+        in PrintAttributes attributes, in ParcelFileDescriptor destination);
+    oneway void closeDocument();
+    oneway void writePages(in PageRange[] pages);
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfRendererService.java b/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfRendererService.java
new file mode 100644
index 0000000..a4c6932
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfRendererService.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2014 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.printspooler.renderer;
+
+import android.app.Service;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.pdf.PdfRenderer;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintAttributes.Margins;
+import android.util.Log;
+import android.view.View;
+import libcore.io.IoUtils;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Service for rendering PDF documents in an isolated process.
+ */
+public final class PdfRendererService extends Service {
+    private static final String LOG_TAG = "PdfRendererService";
+    private static final boolean DEBUG = false;
+
+    private static final int MILS_PER_INCH = 1000;
+    private static final int POINTS_IN_INCH = 72;
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return new PdfRendererImpl();
+    }
+
+    private final class PdfRendererImpl extends IPdfRenderer.Stub {
+        private final Object mLock = new Object();
+
+        private Bitmap mBitmap;
+        private PdfRenderer mRenderer;
+
+        @Override
+        public int openDocument(ParcelFileDescriptor source) throws RemoteException {
+            synchronized (mLock) {
+                throwIfOpened();
+                if (DEBUG) {
+                    Log.i(LOG_TAG, "openDocument()");
+                }
+                try {
+                    mRenderer = new PdfRenderer(source);
+                    return mRenderer.getPageCount();
+                } catch (IOException ioe) {
+                    throw new RemoteException("Cannot open file");
+                }
+            }
+        }
+
+        @Override
+        public void renderPage(int pageIndex, int bitmapWidth, int bitmapHeight,
+                PrintAttributes attributes, ParcelFileDescriptor destination) {
+            FileOutputStream out = null;
+            synchronized (mLock) {
+                try {
+                    throwIfNotOpened();
+
+                    PdfRenderer.Page page = mRenderer.openPage(pageIndex);
+
+                    final int srcWidthPts = page.getWidth();
+                    final int srcHeightPts = page.getHeight();
+
+                    final int dstWidthPts = pointsFromMils(
+                            attributes.getMediaSize().getWidthMils());
+                    final int dstHeightPts = pointsFromMils(
+                            attributes.getMediaSize().getHeightMils());
+
+                    final boolean scaleContent = mRenderer.shouldScaleForPrinting();
+                    final boolean contentLandscape = !attributes.getMediaSize().isPortrait();
+
+                    final float displayScale;
+                    Matrix matrix = new Matrix();
+
+                    if (scaleContent) {
+                        displayScale = Math.min((float) bitmapWidth / srcWidthPts,
+                                (float) bitmapHeight / srcHeightPts);
+                    } else {
+                        if (contentLandscape) {
+                            displayScale = (float) bitmapHeight / dstHeightPts;
+                        } else {
+                            displayScale = (float) bitmapWidth / dstWidthPts;
+                        }
+                    }
+                    matrix.postScale(displayScale, displayScale);
+
+                    Configuration configuration = PdfRendererService.this.getResources()
+                            .getConfiguration();
+                    if (configuration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+                        matrix.postTranslate(bitmapWidth - srcWidthPts * displayScale, 0);
+                    }
+
+                    Margins minMargins = attributes.getMinMargins();
+                    final int paddingLeftPts = pointsFromMils(minMargins.getLeftMils());
+                    final int paddingTopPts = pointsFromMils(minMargins.getTopMils());
+                    final int paddingRightPts = pointsFromMils(minMargins.getRightMils());
+                    final int paddingBottomPts = pointsFromMils(minMargins.getBottomMils());
+
+                    Rect clip = new Rect();
+                    clip.left = (int) (paddingLeftPts * displayScale);
+                    clip.top = (int) (paddingTopPts * displayScale);
+                    clip.right = (int) (bitmapWidth - paddingRightPts * displayScale);
+                    clip.bottom = (int) (bitmapHeight - paddingBottomPts * displayScale);
+
+                    if (DEBUG) {
+                        Log.i(LOG_TAG, "Rendering page:" + pageIndex);
+                    }
+
+                    Bitmap bitmap = getBitmapForSize(bitmapWidth, bitmapHeight);
+                    page.render(bitmap, clip, matrix, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
+
+                    page.close();
+
+                    out = new FileOutputStream(destination.getFileDescriptor());
+                    bitmap.compress(Bitmap.CompressFormat.PNG, 0, out);
+                } finally {
+                    IoUtils.closeQuietly(out);
+                    IoUtils.closeQuietly(destination);
+                }
+            }
+        }
+
+        @Override
+        public void closeDocument() {
+            synchronized (mLock) {
+                throwIfNotOpened();
+                if (DEBUG) {
+                    Log.i(LOG_TAG, "openDocument()");
+                }
+                mRenderer.close();
+                mRenderer = null;
+            }
+        }
+
+        @Override
+        public void writePages(PageRange[] pages) {
+            synchronized (mLock) {
+                throwIfNotOpened();
+                if (DEBUG) {
+                    Log.i(LOG_TAG, "writePages()");
+                }
+                // TODO: Implement dropping undesired pages.
+            }
+        }
+
+        private Bitmap getBitmapForSize(int width, int height) {
+            if (mBitmap != null) {
+                if (mBitmap.getWidth() == width && mBitmap.getHeight() == height) {
+                    return mBitmap;
+                }
+                mBitmap.recycle();
+            }
+            mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+            return mBitmap;
+        }
+
+        private void throwIfOpened() {
+            if (mRenderer != null) {
+                throw new IllegalStateException("Already opened");
+            }
+        }
+
+        private void throwIfNotOpened() {
+            if (mRenderer == null) {
+                throw new IllegalStateException("Not opened");
+            }
+        }
+    }
+
+    private static int pointsFromMils(int mils) {
+        return (int) (((float) mils / MILS_PER_INCH) * POINTS_IN_INCH);
+    }
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 01c9746..022e0d0 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -319,6 +319,7 @@
 
             mProgressMessageController.cancel();
             mPrinterRegistry.setTrackedPrinter(null);
+            mPrintPreviewController.destroy();
             mSpoolerProvider.destroy();
             mPrintedDocument.finish();
             mPrintedDocument.destroy();
@@ -2185,4 +2186,4 @@
             updateOptionsUi();
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewController.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewController.java
index 4a23ec4..ddf637e 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewController.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewController.java
@@ -191,6 +191,9 @@
     }
 
     public void destroy() {
+        if (mPageAdapter.isOpened()) {
+            mPageAdapter.close(null);
+        }
         mPageAdapter.destroy();
     }
 
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/PageContentView.java b/packages/PrintSpooler/src/com/android/printspooler/widget/PageContentView.java
index 8365373..76ff167 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/widget/PageContentView.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/widget/PageContentView.java
@@ -20,6 +20,7 @@
 import android.graphics.Canvas;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
+import android.print.PrintAttributes;
 import android.print.PrintAttributes.MediaSize;
 import android.print.PrintAttributes.Margins;
 import android.util.AttributeSet;
@@ -38,13 +39,12 @@
 public class PageContentView extends View
         implements PageContentRepository.OnPageContentAvailableCallback {
 
+    private final PrintAttributes mAttributes = new PrintAttributes.Builder().build();
+
     private final ColorDrawable mEmptyState;
 
     private PageContentProvider mProvider;
 
-    private MediaSize mMediaSize;
-    private Margins mMinMargins;
-
     private boolean mContentRequested;
 
     public PageContentView(Context context, AttributeSet attrs) {
@@ -83,15 +83,17 @@
     }
 
     public void init(PageContentProvider provider, MediaSize mediaSize, Margins minMargins) {
-        if (mProvider == provider
-                && ((mMediaSize == null) ? mediaSize == null : mMediaSize.equals(mediaSize))
-                && ((mMinMargins == null) ? minMargins == null : mMinMargins.equals(minMargins))) {
+        if (mProvider == null ? provider == null : mProvider.equals(provider)
+                && ((mAttributes.getMediaSize() == null)
+                        ? mediaSize == null : mAttributes.getMediaSize().equals(mediaSize))
+                && ((mAttributes.getMinMargins() == null)
+                        ? minMargins == null : mAttributes.getMinMargins().equals(minMargins))) {
             return;
         }
 
         mProvider = provider;
-        mMediaSize = mediaSize;
-        mMinMargins = minMargins;
+        mAttributes.setMediaSize(mediaSize);
+        mAttributes.setMinMargins(minMargins);
         mContentRequested = false;
 
         // If there is no provider we want immediately to switch to
@@ -106,8 +108,7 @@
     private void requestPageContentIfNeeded() {
         if (getWidth() > 0 && getHeight() > 0 && !mContentRequested && mProvider != null) {
             mContentRequested = true;
-            mProvider.getPageContent(new RenderSpec(getWidth(), getHeight(), mMediaSize,
-                    mMinMargins), this);
+            mProvider.getPageContent(new RenderSpec(getWidth(), getHeight(), mAttributes), this);
         }
     }
 }