Merge "Force cancel pending RemotePrintDocument commands when the PrintActivity exits" into nyc-dev

This commit is contained in:
Philip P. Moltmann
2016-02-26 00:28:51 +00:00
committed by Android (Google) Code Review
2 changed files with 123 additions and 22 deletions

View File

@@ -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;
}
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,14 +595,25 @@ public final class RemotePrintDocument {
Log.w(LOG_TAG, "Error while canceling", re);
}
}
} else if (isCanceling()) {
// Nothing to do
} else {
canceled();
// Done.
mDoneCallback.onDone();
}
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() {
@@ -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;

View File

@@ -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();