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

Commit 645a3e1c authored by Philip P. Moltmann's avatar Philip P. Moltmann
Browse files

Force cancel pending RemotePrintDocument commands when the PrintActivity exits

Otherwise the print-client app can prevent the print activity from exiting as
we wait for the command to finish. As this is a violation of the
remote-print-document contract we deal with this similar to a command failure.
As the PrintActivity is exiting we don't further send any commands to the
print-client.

Bug: 27207751
Change-Id: Ieacb16786112f217ca5b8031bfb597598e28dc3d
parent 26288783
Loading
Loading
Loading
Loading
+118 −17
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.printspooler.model;

import android.annotation.NonNull;
import android.content.ContentResolver;
import android.content.Context;
import android.net.Uri;
@@ -57,6 +58,8 @@ public final class RemotePrintDocument {

    private static final boolean DEBUG = false;

    private static final long FORCE_CANCEL_TIMEOUT = 1000; // ms

    private static final int STATE_INITIAL = 0;
    private static final int STATE_STARTED = 1;
    private static final int STATE_UPDATING = 2;
@@ -212,7 +215,7 @@ public final class RemotePrintDocument {
            // cancellation and start over.
            if (mCurrentCommand != null && (mCurrentCommand.isRunning()
                    || mCurrentCommand.isPending())) {
                mCurrentCommand.cancel();
                mCurrentCommand.cancel(false);
            }

            // Schedule a layout command.
@@ -233,7 +236,7 @@ public final class RemotePrintDocument {
            // Cancel the current write as a new one is to be scheduled.
            if (mCurrentCommand instanceof WriteCommand
                    && (mCurrentCommand.isPending() || mCurrentCommand.isRunning())) {
                mCurrentCommand.cancel();
                mCurrentCommand.cancel(false);
            }

            // Schedule a write command.
@@ -277,9 +280,9 @@ public final class RemotePrintDocument {
        }
    }

    public void cancel() {
    public void cancel(boolean force) {
        if (DEBUG) {
            Log.i(LOG_TAG, "[CALLED] cancel()");
            Log.i(LOG_TAG, "[CALLED] cancel(" + force + ")");
        }

        mNextCommand = null;
@@ -290,7 +293,7 @@ public final class RemotePrintDocument {

        mState = STATE_CANCELING;

        mCurrentCommand.cancel();
        mCurrentCommand.cancel(force);
    }

    public void destroy() {
@@ -441,8 +444,9 @@ public final class RemotePrintDocument {
        if (mCurrentCommand != null) {
            if (mCurrentCommand.isPending()) {
                mCurrentCommand.run();
            }

                mState = STATE_UPDATING;
            }
        } else {
            mState = STATE_UPDATED;
        }
@@ -535,14 +539,17 @@ public final class RemotePrintDocument {

        protected final CommandDoneCallback mDoneCallback;

        private final Handler mHandler;

        protected ICancellationSignal mCancellation;

        private CharSequence mError;

        private int mState = STATE_PENDING;

        public AsyncCommand(IPrintDocumentAdapter adapter, RemotePrintDocumentInfo document,
        public AsyncCommand(Looper looper, IPrintDocumentAdapter adapter, RemotePrintDocumentInfo document,
                CommandDoneCallback doneCallback) {
            mHandler = new AsyncCommandHandler(looper);
            mAdapter = adapter;
            mDocument = document;
            mDoneCallback = doneCallback;
@@ -556,7 +563,29 @@ public final class RemotePrintDocument {
            return mState == STATE_CANCELED;
        }

        public final void cancel() {
        /**
         * If a force cancel is pending, remove it. This is usually called when a command returns
         * and thereby does not need to be canceled anymore.
         */
        protected void removeForceCancel() {
            if (DEBUG) {
                if (mHandler.hasMessages(AsyncCommandHandler.MSG_FORCE_CANCEL)) {
                    Log.i(LOG_TAG, "[FORCE CANCEL] Removed");
                }
            }

            mHandler.removeMessages(AsyncCommandHandler.MSG_FORCE_CANCEL);
        }

        /**
         * Cancel the current command.
         *
         * @param force If set, does not wait for the {@link PrintDocumentAdapter} to cancel. This
         *              should only be used if this is the last command send to the as otherwise the
         *              {@link PrintDocumentAdapter adapter} might get commands while it is still
         *              running the old one.
         */
        public final void cancel(boolean force) {
            if (isRunning()) {
                canceling();
                if (mCancellation != null) {
@@ -566,15 +595,26 @@ public final class RemotePrintDocument {
                        Log.w(LOG_TAG, "Error while canceling", re);
                    }
                }
            } else if (isCanceling()) {
                // Nothing to do
            } else {
            }

            if (isCanceling()) {
                if (force) {
                    if (DEBUG) {
                        Log.i(LOG_TAG, "[FORCE CANCEL] queued");
                    }
                    mHandler.sendMessageDelayed(
                            mHandler.obtainMessage(AsyncCommandHandler.MSG_FORCE_CANCEL),
                            FORCE_CANCEL_TIMEOUT);
                }

                return;
            }

            canceled();

            // Done.
            mDoneCallback.onDone();
        }
        }

        protected final void canceling() {
            if (mState != STATE_PENDING && mState != STATE_RUNNING) {
@@ -617,7 +657,7 @@ public final class RemotePrintDocument {
        }

        protected final void failed(CharSequence error) {
            if (mState != STATE_RUNNING) {
            if (mState != STATE_RUNNING && mState != STATE_CANCELING) {
                throw new IllegalStateException("Not running.");
            }
            mState = STATE_FAILED;
@@ -632,6 +672,37 @@ public final class RemotePrintDocument {
        public CharSequence getError() {
            return mError;
        }

        /**
         * Handler for the async command.
         */
        private class AsyncCommandHandler extends Handler {
            /** Message indicated the desire for to force cancel a command */
            final static int MSG_FORCE_CANCEL = 0;

            AsyncCommandHandler(@NonNull Looper looper) {
                super(looper);
            }

            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_FORCE_CANCEL:
                        if (isCanceling()) {
                            if (DEBUG) {
                                Log.i(LOG_TAG, "[FORCE CANCEL] executed");
                            }
                            failed("Command did not respond to cancellation in "
                                    + FORCE_CANCEL_TIMEOUT + " ms");

                            mDoneCallback.onDone();
                        }
                        break;
                    default:
                        // not reached;
                }
            }
        }
    }

    private static final class LayoutCommand extends AsyncCommand {
@@ -646,7 +717,7 @@ public final class RemotePrintDocument {
        public LayoutCommand(Looper looper, IPrintDocumentAdapter adapter,
                RemotePrintDocumentInfo document, PrintAttributes oldAttributes,
                PrintAttributes newAttributes, boolean preview, CommandDoneCallback callback) {
            super(adapter, document, callback);
            super(looper, adapter, document, callback);
            mHandler = new LayoutHandler(looper);
            mRemoteResultCallback = new LayoutResultCallback(mHandler);
            mOldAttributes.copyFrom(oldAttributes);
@@ -795,6 +866,21 @@ public final class RemotePrintDocument {

            @Override
            public void handleMessage(Message message) {
                // The command might have been force canceled, see
                // AsyncCommand.AsyncCommandHandler#handleMessage
                if (isFailed()) {
                    if (DEBUG) {
                        Log.i(LOG_TAG, "[CALLBACK] on canceled layout command");
                    }

                    return;
                } else {
                    if (message.what != MSG_ON_LAYOUT_STARTED) {
                        // No need to force cancel anymore if layout finished
                        removeForceCancel();
                    }
                }

                switch (message.what) {
                    case MSG_ON_LAYOUT_STARTED: {
                        ICancellationSignal cancellation = (ICancellationSignal) message.obj;
@@ -882,7 +968,7 @@ public final class RemotePrintDocument {
        public WriteCommand(Context context, Looper looper, IPrintDocumentAdapter adapter,
                RemotePrintDocumentInfo document, int pageCount, PageRange[] pages,
                MutexFileProvider fileProvider, CommandDoneCallback callback) {
            super(adapter, document, callback);
            super(looper, adapter, document, callback);
            mContext = context;
            mHandler = new WriteHandler(looper);
            mRemoteResultCallback = new WriteResultCallback(mHandler);
@@ -1052,6 +1138,21 @@ public final class RemotePrintDocument {

            @Override
            public void handleMessage(Message message) {
                // The command might have been force canceled, see
                // AsyncCommand.AsyncCommandHandler#handleMessage
                if (isFailed()) {
                    if (DEBUG) {
                        Log.i(LOG_TAG, "[CALLBACK] on canceled write command");
                    }

                    return;
                } else {
                    if (message.what != MSG_ON_WRITE_STARTED) {
                        // No need to force cancel anymore if write finished
                        removeForceCancel();
                    }
                }

                switch (message.what) {
                    case MSG_ON_WRITE_STARTED: {
                        ICancellationSignal cancellation = (ICancellationSignal) message.obj;
+4 −4
Original line number Diff line number Diff line
@@ -320,8 +320,8 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
                if (isFinishing() || (isFinalState(mState) && !mPrintedDocument.isUpdating())) {
                    return;
                }
                mPrintedDocument.cancel();
                setState(STATE_PRINT_CANCELED);
                mPrintedDocument.cancel(true);
                doFinish();
            }
        }, PrintActivity.this);
@@ -1013,7 +1013,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
    }

    private void requestCreatePdfFileOrFinish() {
        mPrintedDocument.cancel();
        mPrintedDocument.cancel(false);

        if (mCurrentPrinter == mDestinationSpinnerAdapter.getPdfPrinter()) {
            startCreateDocumentActivity();
@@ -1130,7 +1130,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
    private void cancelPrint() {
        setState(STATE_PRINT_CANCELED);
        updateOptionsUi();
        mPrintedDocument.cancel();
        mPrintedDocument.cancel(true);
        doFinish();
    }

@@ -1889,7 +1889,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
    public void onPrinterUnavailable(PrinterInfo printer) {
        if (mCurrentPrinter.getId().equals(printer.getId())) {
            setState(STATE_PRINTER_UNAVAILABLE);
            mPrintedDocument.cancel();
            mPrintedDocument.cancel(false);
            ensureErrorUiShown(getString(R.string.print_error_printer_unavailable),
                    PrintErrorFragment.ACTION_NONE);
            updateOptionsUi();