Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit db63677c authored by Svetoslav Ganov's avatar Svetoslav Ganov
Browse files

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
parent 9470cd95
Loading
Loading
Loading
Loading
+165 −43
Original line number Diff line number Diff line
@@ -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,8 +474,50 @@ public final class PrintManager {
        private void doFinish() {
            mDocumentAdapter = null;
            mHandler = 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 {
            public static final int MSG_START = 1;
@@ -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();
                        final CancellationSignal cancellation;
                        final LayoutSpec layoutSpec;

                        CancellationSignal cancellation = new CancellationSignal();
                        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();
                        final CancellationSignal cancellation;
                        final WriteSpec writeSpec;

                        CancellationSignal cancellation = new CancellationSignal();
                        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();
            }
        }
    }