From db63677c7c02cad7c25627533e5add5ed46870f8 Mon Sep 17 00:00:00 2001 From: Svetoslav Ganov Date: Mon, 7 Oct 2013 14:31:18 -0700 Subject: Do not do a layout and write while a cancellation is in progress. While printing the app gets callbacks to do a layout and a write. Since these operations can take a very long time they are implemented in a async fashion. For example, if there is a layout request while we are still doing a layout, we ask the application to cancel the previous layout. The problem was that we were not waiting for the cancellation result before calling the subsequent layout. Hence, the app ends up having to do two layouts at the same time which breaks the printing contract. Same for write. bug:11118426 Change-Id: If7b191c44387d88d5cec195d1785a47f986965ba --- core/java/android/print/PrintManager.java | 208 ++++++++++++++++++++++++------ 1 file changed, 165 insertions(+), 43 deletions(-) (limited to 'core/java/android/print/PrintManager.java') diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java index 0859fdd..9c7c1fe 100644 --- a/core/java/android/print/PrintManager.java +++ b/core/java/android/print/PrintManager.java @@ -57,6 +57,8 @@ public final class PrintManager { private static final String LOG_TAG = "PrintManager"; + private static final boolean DEBUG = false; + /** @hide */ public static final int APP_ID_ANY = -2; @@ -350,6 +352,16 @@ public final class PrintManager { private Handler mHandler; // Strong reference OK - cleared in finish() + private LayoutSpec mLastLayoutSpec; + + private WriteSpec mLastWriteSpec; + + private boolean mStartReqeusted; + private boolean mStarted; + + private boolean mFinishRequested; + private boolean mFinished; + public PrintDocumentAdapterDelegate(PrintDocumentAdapter documentAdapter, Looper looper) { mDocumentAdapter = documentAdapter; mHandler = new MyHandler(looper); @@ -357,47 +369,102 @@ public final class PrintManager { @Override public void start() { - mHandler.sendEmptyMessage(MyHandler.MSG_START); + synchronized (mLock) { + // Started or finished - nothing to do. + if (mStartReqeusted || mFinishRequested) { + return; + } + + mStartReqeusted = true; + + doPendingWorkLocked(); + } } @Override public void layout(PrintAttributes oldAttributes, PrintAttributes newAttributes, ILayoutResultCallback callback, Bundle metadata, int sequence) { synchronized (mLock) { - if (mLayoutOrWriteCancellation != null) { - mLayoutOrWriteCancellation.cancel(); + // Start not called or finish called - nothing to do. + if (!mStartReqeusted || mFinishRequested) { + return; + } + + // Layout cancels write and overrides layout. + if (mLastWriteSpec != null) { + IoUtils.closeQuietly(mLastWriteSpec.fd); + mLastWriteSpec = null; } + + mLastLayoutSpec = new LayoutSpec(); + mLastLayoutSpec.callback = callback; + mLastLayoutSpec.oldAttributes = oldAttributes; + mLastLayoutSpec.newAttributes = newAttributes; + mLastLayoutSpec.metadata = metadata; + mLastLayoutSpec.sequence = sequence; + + // Cancel the previous cancellable operation.When the + // cancellation completes we will do the pending work. + if (cancelPreviousCancellableOperationLocked()) { + return; + } + + doPendingWorkLocked(); } - SomeArgs args = SomeArgs.obtain(); - args.arg1 = oldAttributes; - args.arg2 = newAttributes; - args.arg3 = callback; - args.arg4 = metadata; - args.argi1 = sequence; - mHandler.removeMessages(MyHandler.MSG_LAYOUT); - mHandler.obtainMessage(MyHandler.MSG_LAYOUT, args).sendToTarget(); } @Override public void write(PageRange[] pages, ParcelFileDescriptor fd, IWriteResultCallback callback, int sequence) { synchronized (mLock) { - if (mLayoutOrWriteCancellation != null) { - mLayoutOrWriteCancellation.cancel(); + // Start not called or finish called - nothing to do. + if (!mStartReqeusted || mFinishRequested) { + return; + } + + // Write cancels previous writes. + if (mLastWriteSpec != null) { + IoUtils.closeQuietly(mLastWriteSpec.fd); + mLastWriteSpec = null; } + + mLastWriteSpec = new WriteSpec(); + mLastWriteSpec.callback = callback; + mLastWriteSpec.pages = pages; + mLastWriteSpec.fd = fd; + mLastWriteSpec.sequence = sequence; + + // Cancel the previous cancellable operation.When the + // cancellation completes we will do the pending work. + if (cancelPreviousCancellableOperationLocked()) { + return; + } + + doPendingWorkLocked(); } - SomeArgs args = SomeArgs.obtain(); - args.arg1 = pages; - args.arg2 = fd; - args.arg3 = callback; - args.argi1 = sequence; - mHandler.removeMessages(MyHandler.MSG_WRITE); - mHandler.obtainMessage(MyHandler.MSG_WRITE, args).sendToTarget(); } @Override public void finish() { - mHandler.sendEmptyMessage(MyHandler.MSG_FINISH); + synchronized (mLock) { + // Start not called or finish called - nothing to do. + if (!mStartReqeusted || mFinishRequested) { + return; + } + + mFinishRequested = true; + + // When the current write or layout complete we + // will do the pending work. + if (mLastLayoutSpec != null || mLastWriteSpec != null) { + if (DEBUG) { + Log.i(LOG_TAG, "Waiting for current operation"); + } + return; + } + + doPendingWorkLocked(); + } } private boolean isFinished() { @@ -407,7 +474,49 @@ public final class PrintManager { private void doFinish() { mDocumentAdapter = null; mHandler = null; - mLayoutOrWriteCancellation = null; + synchronized (mLock) { + mLayoutOrWriteCancellation = null; + } + } + + private boolean cancelPreviousCancellableOperationLocked() { + if (mLayoutOrWriteCancellation != null) { + mLayoutOrWriteCancellation.cancel(); + if (DEBUG) { + Log.i(LOG_TAG, "Cancelling previous operation"); + } + return true; + } + return false; + } + + private void doPendingWorkLocked() { + if (mStartReqeusted && !mStarted) { + mStarted = true; + mHandler.sendEmptyMessage(MyHandler.MSG_START); + } else if (mLastLayoutSpec != null) { + mHandler.sendEmptyMessage(MyHandler.MSG_LAYOUT); + } else if (mLastWriteSpec != null) { + mHandler.sendEmptyMessage(MyHandler.MSG_WRITE); + } else if (mFinishRequested && !mFinished) { + mFinished = true; + mHandler.sendEmptyMessage(MyHandler.MSG_FINISH); + } + } + + private class LayoutSpec { + ILayoutResultCallback callback; + PrintAttributes oldAttributes; + PrintAttributes newAttributes; + Bundle metadata; + int sequence; + } + + private class WriteSpec { + IWriteResultCallback callback; + PageRange[] pages; + ParcelFileDescriptor fd; + int sequence; } private final class MyHandler extends Handler { @@ -431,41 +540,52 @@ public final class PrintManager { } break; case MSG_LAYOUT: { - SomeArgs args = (SomeArgs) message.obj; - PrintAttributes oldAttributes = (PrintAttributes) args.arg1; - PrintAttributes newAttributes = (PrintAttributes) args.arg2; - ILayoutResultCallback callback = (ILayoutResultCallback) args.arg3; - Bundle metadata = (Bundle) args.arg4; - final int sequence = args.argi1; - args.recycle(); - - CancellationSignal cancellation = new CancellationSignal(); + final CancellationSignal cancellation; + final LayoutSpec layoutSpec; + synchronized (mLock) { + layoutSpec = mLastLayoutSpec; + mLastLayoutSpec = null; + cancellation = new CancellationSignal(); mLayoutOrWriteCancellation = cancellation; } - mDocumentAdapter.onLayout(oldAttributes, newAttributes, cancellation, - new MyLayoutResultCallback(callback, sequence), metadata); + if (layoutSpec != null) { + if (DEBUG) { + Log.i(LOG_TAG, "Performing layout"); + } + mDocumentAdapter.onLayout(layoutSpec.oldAttributes, + layoutSpec.newAttributes, cancellation, + new MyLayoutResultCallback(layoutSpec.callback, + layoutSpec.sequence), layoutSpec.metadata); + } } break; case MSG_WRITE: { - SomeArgs args = (SomeArgs) message.obj; - PageRange[] pages = (PageRange[]) args.arg1; - ParcelFileDescriptor fd = (ParcelFileDescriptor) args.arg2; - IWriteResultCallback callback = (IWriteResultCallback) args.arg3; - final int sequence = args.argi1; - args.recycle(); - - CancellationSignal cancellation = new CancellationSignal(); + final CancellationSignal cancellation; + final WriteSpec writeSpec; + synchronized (mLock) { + writeSpec= mLastWriteSpec; + mLastWriteSpec = null; + cancellation = new CancellationSignal(); mLayoutOrWriteCancellation = cancellation; } - mDocumentAdapter.onWrite(pages, fd, cancellation, - new MyWriteResultCallback(callback, fd, sequence)); + if (writeSpec != null) { + if (DEBUG) { + Log.i(LOG_TAG, "Performing write"); + } + mDocumentAdapter.onWrite(writeSpec.pages, writeSpec.fd, + cancellation, new MyWriteResultCallback(writeSpec.callback, + writeSpec.fd, writeSpec.sequence)); + } } break; case MSG_FINISH: { + if (DEBUG) { + Log.i(LOG_TAG, "Performing finish"); + } mDocumentAdapter.onFinish(); doFinish(); } break; @@ -533,6 +653,7 @@ public final class PrintManager { private void clearLocked() { mLayoutOrWriteCancellation = null; mCallback = null; + doPendingWorkLocked(); } } @@ -598,6 +719,7 @@ public final class PrintManager { IoUtils.closeQuietly(mFd); mCallback = null; mFd = null; + doPendingWorkLocked(); } } } -- cgit v1.1