diff options
author | Svet Ganov <svetoslavganov@google.com> | 2014-09-02 22:03:58 +0000 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2014-09-02 22:03:58 +0000 |
commit | 825b4321510a20edb520a5ee10a299ba5a5e3f46 (patch) | |
tree | 60419faa38f675f5576fe63bfb885b90075e4c71 /packages | |
parent | 38778c170c5650823bbf3b25c20e2e3dfd926882 (diff) | |
parent | 561b8931742503d58ae842edea790e86f359870f (diff) | |
download | frameworks_base-825b4321510a20edb520a5ee10a299ba5a5e3f46.zip frameworks_base-825b4321510a20edb520a5ee10a299ba5a5e3f46.tar.gz frameworks_base-825b4321510a20edb520a5ee10a299ba5a5e3f46.tar.bz2 |
am 668b4765: Merge "Move print rendering in an isolated process." into lmp-dev
* commit '668b4765de3e97d69aaebb3bd58fe23eb800264d':
Move print rendering in an isolated process.
Diffstat (limited to 'packages')
8 files changed, 353 insertions, 106 deletions
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 @@ include $(CLEAR_VARS) 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 @@ public final class PageContentRepository { 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 @@ public final class PageContentRepository { if (DEBUG) { Log.i(LOG_TAG, "STATE_DESTROYED"); } - throwIfNotClosed(); doDestroy(); } @@ -351,15 +354,13 @@ public final class PageContentRepository { 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 @@ public final class PageContentRepository { 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 final class PageContentRepository { 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 @@ public final class PageContentRepository { } } - 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 @@ public final class PageContentRepository { 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 @@ public final class PageContentRepository { 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 @@ public final class PageContentRepository { 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 @@ public final class PageContentRepository { 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 final class PageContentRepository { } 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,63 +789,31 @@ public final class PageContentRepository { 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(); - - final int dstWidthPts = pointsFromMils(mRenderSpec.mediaSize.getWidthMils()); - final int dstHeightPts = pointsFromMils(mRenderSpec.mediaSize.getHeightMils()); - - final boolean scaleContent = mRenderer.shouldScaleForPrinting(); - final boolean contentLandscape = !mRenderSpec.mediaSize.isPortrait(); - - 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; - } - } - matrix.postScale(displayScale, displayScale); - - Configuration configuration = mContext.getResources().getConfiguration(); - if (configuration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { - matrix.postTranslate(bitmap.getWidth() - srcWidthPts * displayScale, 0); + ParcelFileDescriptor[] pipe = null; + try { + pipe = ParcelFileDescriptor.createPipe(); + ParcelFileDescriptor source = pipe[0]; + ParcelFileDescriptor destination = pipe[1]; + + mRenderer.renderPage(mPageIndex, bitmap.getWidth(), bitmap.getHeight(), + mRenderSpec.printAttributes, destination); + + // We passed the file descriptor to the other side which took + // ownership, so close our copy for the write to complete. + destination.close(); + + 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]); } - 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 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat mProgressMessageController.cancel(); mPrinterRegistry.setTrackedPrinter(null); + mPrintPreviewController.destroy(); mSpoolerProvider.destroy(); mPrintedDocument.finish(); mPrintedDocument.destroy(); @@ -2185,4 +2186,4 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat 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 @@ class PrintPreviewController implements MutexFileProvider.OnReleaseRequestCallba } 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.content.Context; 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 @@ import com.android.printspooler.model.PageContentRepository.RenderSpec; 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 class PageContentView extends View } 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 @@ public class PageContentView extends View 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); } } } |