am 86d2db97: Merge "Print spooler security and some new print service facing APIs." into klp-dev

* commit '86d2db9750657401deadea6766c94300c3bfc7cd':
  Print spooler security and some new print service facing APIs.
This commit is contained in:
Svetoslav Ganov
2013-08-29 16:02:22 -07:00
committed by Android Git Automerger
27 changed files with 793 additions and 202 deletions

View File

@@ -25,6 +25,7 @@ package android {
field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
field public static final java.lang.String BIND_PRINT_SPOOLER_SERVICE = "android.permission.BIND_PRINT_SPOOLER_SERVICE";
field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS";
field public static final java.lang.String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE";
field public static final java.lang.String BIND_VPN_SERVICE = "android.permission.BIND_VPN_SERVICE";
@@ -19147,7 +19148,7 @@ package android.print {
method public void onFinish();
method public abstract void onLayout(android.print.PrintAttributes, android.print.PrintAttributes, android.os.CancellationSignal, android.print.PrintDocumentAdapter.LayoutResultCallback, android.os.Bundle);
method public void onStart();
method public abstract void onWrite(android.print.PageRange[], java.io.FileDescriptor, android.os.CancellationSignal, android.print.PrintDocumentAdapter.WriteResultCallback);
method public abstract void onWrite(android.print.PageRange[], android.os.ParcelFileDescriptor, android.os.CancellationSignal, android.print.PrintDocumentAdapter.WriteResultCallback);
field public static final java.lang.String METADATA_KEY_PRINT_PREVIEW = "KEY_METADATA_PRINT_PREVIEW";
}
@@ -19167,6 +19168,7 @@ package android.print {
method public int describeContents();
method public int getColorMode();
method public int getContentType();
method public long getDataSize();
method public int getFittingMode();
method public android.print.PrintAttributes.Margins getMargins();
method public android.print.PrintAttributes.MediaSize getMediaSize();
@@ -19198,7 +19200,7 @@ package android.print {
public class PrintFileDocumentAdapter extends android.print.PrintDocumentAdapter {
ctor public PrintFileDocumentAdapter(android.content.Context, java.io.File, android.print.PrintDocumentInfo);
method public void onLayout(android.print.PrintAttributes, android.print.PrintAttributes, android.os.CancellationSignal, android.print.PrintDocumentAdapter.LayoutResultCallback, android.os.Bundle);
method public void onWrite(android.print.PageRange[], java.io.FileDescriptor, android.os.CancellationSignal, android.print.PrintDocumentAdapter.WriteResultCallback);
method public void onWrite(android.print.PageRange[], android.os.ParcelFileDescriptor, android.os.CancellationSignal, android.print.PrintDocumentAdapter.WriteResultCallback);
}
public final class PrintJob {
@@ -19220,9 +19222,10 @@ package android.print {
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
field public static final int PRINT_JOB_ID_UNDEFINED = -1; // 0xffffffff
field public static final int STATE_CANCELED = 6; // 0x6
field public static final int STATE_COMPLETED = 4; // 0x4
field public static final int STATE_FAILED = 5; // 0x5
field public static final int STATE_BLOCKED = 4; // 0x4
field public static final int STATE_CANCELED = 7; // 0x7
field public static final int STATE_COMPLETED = 5; // 0x5
field public static final int STATE_FAILED = 6; // 0x6
field public static final int STATE_QUEUED = 2; // 0x2
field public static final int STATE_STARTED = 3; // 0x3
}
@@ -19340,17 +19343,19 @@ package android.print.pdf {
package android.printservice {
public final class PrintDocument {
method public java.io.FileDescriptor getData();
method public android.os.ParcelFileDescriptor getData();
method public android.print.PrintDocumentInfo getInfo();
}
public final class PrintJob {
method public boolean block(java.lang.String);
method public boolean cancel();
method public boolean complete();
method public boolean fail(java.lang.String);
method public android.printservice.PrintDocument getDocument();
method public int getId();
method public android.print.PrintJobInfo getInfo();
method public boolean isBlocked();
method public boolean isCancelled();
method public boolean isCompleted();
method public boolean isFailed();
@@ -19382,9 +19387,11 @@ package android.printservice {
method public final boolean isDestroyed();
method public final boolean isPrinterDiscoveryStarted();
method public abstract void onDestroy();
method public abstract void onRequestPrinterUpdate(android.print.PrinterId);
method public abstract void onStartPrinterDiscovery(java.util.List<android.print.PrinterId>);
method public abstract void onStartPrinterStateTracking(android.print.PrinterId);
method public abstract void onStopPrinterDiscovery();
method public abstract void onStopPrinterStateTracking(android.print.PrinterId);
method public abstract void onValidatePrinters(java.util.List<android.print.PrinterId>);
method public final void removePrinters(java.util.List<android.print.PrinterId>);
method public final void updatePrinters(java.util.List<android.print.PrinterInfo>);
}

View File

@@ -41,7 +41,9 @@ interface IPrintManager {
void startPrinterDiscovery(in IPrinterDiscoveryObserver observer,
in List<PrinterId> priorityList, int userId);
void stopPrinterDiscovery(in IPrinterDiscoveryObserver observer, int userId);
void requestPrinterUpdate(in PrinterId printerId, int userId);
void validatePrinters(in List<PrinterId> printerIds, int userId);
void startPrinterStateTracking(in PrinterId printerId, int userId);
void stopPrinterStateTracking(in PrinterId printerId, int userId);
void destroyPrinterDiscoverySession(in IPrinterDiscoveryObserver observer,
int userId);
}

View File

@@ -18,8 +18,8 @@ package android.print;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import java.io.FileDescriptor;
import java.util.List;
/**
@@ -41,7 +41,7 @@ import java.util.List;
* <li>
* After every call to {@link #onLayout(PrintAttributes, PrintAttributes,
* CancellationSignal, LayoutResultCallback, Bundle)}, you may get a call to
* {@link #onWrite(PageRange[], FileDescriptor, CancellationSignal, WriteResultCallback)}
* {@link #onWrite(PageRange[], ParcelFileDescriptor, CancellationSignal, WriteResultCallback)}
* asking you to write a PDF file with the content for specific pages.
* </li>
* <li>
@@ -64,7 +64,7 @@ import java.util.List;
* PrintAttributes, CancellationSignal, LayoutResultCallback, Bundle)} on
* the UI thread (assuming onStart initializes resources needed for layout).
* This will ensure that the UI does not change while you are laying out the
* printed content. Then you can handle {@link #onWrite(PageRange[], FileDescriptor,
* printed content. Then you can handle {@link #onWrite(PageRange[], ParcelFileDescriptor,
* CancellationSignal, WriteResultCallback)} and {@link #onFinish()} on another
* thread. This will ensure that the UI is frozen for the minimal amount of
* time. Also this assumes that you will generate the printed content in
@@ -150,10 +150,10 @@ public abstract class PrintDocumentAdapter {
* from of a PDF file to the given file descriptor. This method is invoked
* on the main thread.
*<p>
* After you are done writing, you should <strong>not</strong> close the
* file descriptor, rather you must invoke: {@link WriteResultCallback
* #onWriteFinished(List)}, if writing completed successfully; or {@link
* WriteResultCallback#onWriteFailed(CharSequence)}, if an error occurred.
* After you are done writing, you should close the file descriptor and
* invoke {@link WriteResultCallback #onWriteFinished(List)}, if writing
* completed successfully; or {@link WriteResultCallback#onWriteFailed(
* CharSequence)}, if an error occurred.
* </p>
* <p>
* <strong>Note:</strong> If the printed content is large, it is a good
@@ -171,7 +171,7 @@ public abstract class PrintDocumentAdapter {
* @see WriteResultCallback
* @see CancellationSignal
*/
public abstract void onWrite(PageRange[] pages, FileDescriptor destination,
public abstract void onWrite(PageRange[] pages, ParcelFileDescriptor destination,
CancellationSignal cancellationSignal, WriteResultCallback callback);
/**
@@ -185,7 +185,7 @@ public abstract class PrintDocumentAdapter {
/**
* Base class for implementing a callback for the result of {@link
* PrintDocumentAdapter#onWrite(PageRange[], FileDescriptor, CancellationSignal,
* PrintDocumentAdapter#onWrite(PageRange[], ParcelFileDescriptor, CancellationSignal,
* WriteResultCallback)}.
*/
public static abstract class WriteResultCallback {

View File

@@ -60,6 +60,7 @@ public final class PrintDocumentInfo implements Parcelable {
private int mColorMode;
private Margins mMargins;
private MediaSize mMediaSize;
private long mDataSize;
/**
* Creates a new instance.
@@ -82,6 +83,7 @@ public final class PrintDocumentInfo implements Parcelable {
mColorMode = prototype.mColorMode;
mMargins = prototype.mMargins;
mMediaSize = prototype.mMediaSize;
mDataSize = prototype.mDataSize;
}
/**
@@ -98,6 +100,7 @@ public final class PrintDocumentInfo implements Parcelable {
mColorMode = parcel.readInt();
mMargins = Margins.createFromParcel(parcel);
mMediaSize = MediaSize.createFromParcel(parcel);
mDataSize = parcel.readLong();
}
/**
@@ -188,6 +191,26 @@ public final class PrintDocumentInfo implements Parcelable {
return mMediaSize;
}
/**
* Gets the document data size in bytes.
*
* @return The data size.
*/
public long getDataSize() {
return mDataSize;
}
/**
* Sets the document data size in bytes.
*
* @param dataSize The data size.
*
* @hide
*/
public void setDataSize(long dataSize) {
mDataSize = dataSize;
}
@Override
public int describeContents() {
return 0;
@@ -203,6 +226,7 @@ public final class PrintDocumentInfo implements Parcelable {
parcel.writeInt(mColorMode);
mMargins.writeToParcel(parcel);
mMediaSize.writeToParcel(parcel);
parcel.writeLong(mDataSize);
}
@Override
@@ -217,6 +241,8 @@ public final class PrintDocumentInfo implements Parcelable {
result = prime * result + mColorMode;
result = prime * result + (mMargins != null ? mMargins.hashCode() : 0);
result = prime * result + (mMediaSize != null ? mMediaSize.hashCode() : 0);
result = prime * result + (int) mDataSize;
result = prime * result + (int) mDataSize >> 32;
return result;
}
@@ -264,6 +290,9 @@ public final class PrintDocumentInfo implements Parcelable {
} else if (!mMediaSize.equals(other.mMediaSize)) {
return false;
}
if (mDataSize != other.mDataSize) {
return false;
}
return true;
}
@@ -279,6 +308,7 @@ public final class PrintDocumentInfo implements Parcelable {
builder.append(", colorMode=").append(PrintAttributes.colorModeToString(mColorMode));
builder.append(", margins=").append(mMargins);
builder.append(", mediaSize=").append(mMediaSize);
builder.append(", size=").append(mDataSize);
builder.append("}");
return builder.toString();
}

View File

@@ -21,6 +21,7 @@ import android.os.AsyncTask;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.CancellationSignal.OnCancelListener;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import com.android.internal.R;
@@ -28,7 +29,6 @@ import com.android.internal.R;
import libcore.io.IoUtils;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -81,7 +81,7 @@ public class PrintFileDocumentAdapter extends PrintDocumentAdapter {
}
@Override
public void onWrite(PageRange[] pages, FileDescriptor destination,
public void onWrite(PageRange[] pages, ParcelFileDescriptor destination,
CancellationSignal cancellationSignal, WriteResultCallback callback) {
mWriteFileAsyncTask = new WriteFileAsyncTask(destination, cancellationSignal, callback);
mWriteFileAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
@@ -90,13 +90,13 @@ public class PrintFileDocumentAdapter extends PrintDocumentAdapter {
private final class WriteFileAsyncTask extends AsyncTask<Void, Void, Void> {
private final FileDescriptor mDestination;
private final ParcelFileDescriptor mDestination;
private final WriteResultCallback mResultCallback;
private final CancellationSignal mCancellationSignal;
public WriteFileAsyncTask(FileDescriptor destination,
public WriteFileAsyncTask(ParcelFileDescriptor destination,
CancellationSignal cancellationSignal, WriteResultCallback callback) {
mDestination = destination;
mResultCallback = callback;
@@ -112,7 +112,7 @@ public class PrintFileDocumentAdapter extends PrintDocumentAdapter {
@Override
protected Void doInBackground(Void... params) {
InputStream in = null;
OutputStream out = new FileOutputStream(mDestination);
OutputStream out = new FileOutputStream(mDestination.getFileDescriptor());
final byte[] buffer = new byte[8192];
try {
in = new FileInputStream(mFile);

View File

@@ -43,6 +43,13 @@ public final class PrintJobInfo implements Parcelable {
*/
public static final int STATE_ANY_VISIBLE_TO_CLIENTS = -2;
/**
* Constant for matching any active print job state.
*
* @hide
*/
public static final int STATE_ANY_ACTIVE = -3;
/**
* Print job state: The print job is being created but not yet
* ready to be printed.
@@ -55,7 +62,7 @@ public final class PrintJobInfo implements Parcelable {
public static final int STATE_CREATED = 1;
/**
* Print job status: The print jobs is created, it is ready
* Print job state: The print jobs is created, it is ready
* to be printed and should be processed.
* <p>
* Next valid states: {@link #STATE_STARTED}, {@link #STATE_FAILED},
@@ -65,40 +72,49 @@ public final class PrintJobInfo implements Parcelable {
public static final int STATE_QUEUED = 2;
/**
* Print job status: The print job is being printed.
* Print job state: The print job is being printed.
* <p>
* Next valid states: {@link #STATE_COMPLETED}, {@link #STATE_FAILED},
* {@link #STATE_CANCELED}
* {@link #STATE_CANCELED}, {@link #STATE_BLOCKED}
* </p>
*/
public static final int STATE_STARTED = 3;
/**
* Print job status: The print job was successfully printed.
* This is a terminal state.
* Print job state: The print job is blocked.
* <p>
* Next valid states: None
* Next valid states: {@link #STATE_FAILED}, {@link #STATE_CANCELED},
* {@link #STATE_STARTED}
* </p>
*/
public static final int STATE_COMPLETED = 4;
public static final int STATE_BLOCKED = 4;
/**
* Print job status: The print job was printing but printing failed.
* Print job state: The print job was successfully printed.
* This is a terminal state.
* <p>
* Next valid states: None
* </p>
*/
public static final int STATE_FAILED = 5;
public static final int STATE_COMPLETED = 5;
/**
* Print job status: The print job was canceled.
* Print job state: The print job was printing but printing failed.
* This is a terminal state.
* <p>
* Next valid states: None
* </p>
*/
public static final int STATE_CANCELED = 6;
public static final int STATE_FAILED = 6;
/**
* Print job state: The print job was canceled.
* This is a terminal state.
* <p>
* Next valid states: None
* </p>
*/
public static final int STATE_CANCELED = 7;
/** The unique print job id. */
private int mId;
@@ -127,8 +143,8 @@ public final class PrintJobInfo implements Parcelable {
/** How many copies to print. */
private int mCopies;
/** Failure reason if this job failed. */
private String mFailureReason;
/** Reason for the print job being in its current state. */
private String mStateReason;
/** The pages to print */
private PageRange[] mPageRanges;
@@ -155,7 +171,7 @@ public final class PrintJobInfo implements Parcelable {
mUserId = other.mUserId;
mTag = other.mTag;
mCopies = other.mCopies;
mFailureReason = other.mFailureReason;
mStateReason = other.mStateReason;
mPageRanges = other.mPageRanges;
mAttributes = other.mAttributes;
mDocumentInfo = other.mDocumentInfo;
@@ -171,7 +187,7 @@ public final class PrintJobInfo implements Parcelable {
mUserId = parcel.readInt();
mTag = parcel.readString();
mCopies = parcel.readInt();
mFailureReason = parcel.readString();
mStateReason = parcel.readString();
if (parcel.readInt() == 1) {
Parcelable[] parcelables = parcel.readParcelableArray(null);
mPageRanges = new PageRange[parcelables.length];
@@ -377,25 +393,27 @@ public final class PrintJobInfo implements Parcelable {
}
/**
* The failure reason if this print job failed.
* Gets the reason for the print job being in the current state.
*
* @return The failure reason.
* @return The reason, or null if there is no reason or the
* reason is unknown.
*
* @hide
*/
public String getFailureReason() {
return mFailureReason;
public String getStateReason() {
return mStateReason;
}
/**
* The failure reason if this print job failed.
* Sets the reason for the print job being in the current state.
*
* @param failureReason The failure reason.
* @param stateReason The reason, or null if there is no reason
* or the reason is unknown.
*
* @hide
*/
public void setFailureReason(String failureReason) {
mFailureReason = failureReason;
public void setStateReason(String stateReason) {
mStateReason = stateReason;
}
/**
@@ -476,7 +494,7 @@ public final class PrintJobInfo implements Parcelable {
parcel.writeInt(mUserId);
parcel.writeString(mTag);
parcel.writeInt(mCopies);
parcel.writeString(mFailureReason);
parcel.writeString(mStateReason);
if (mPageRanges != null) {
parcel.writeInt(1);
parcel.writeParcelableArray(mPageRanges, flags);

View File

@@ -36,7 +36,6 @@ import com.android.internal.os.SomeArgs;
import libcore.io.IoUtils;
import java.io.File;
import java.io.FileDescriptor;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
@@ -163,7 +162,7 @@ public final class PrintManager {
* @param pdfFile The PDF file to print.
* @param documentInfo Information about the printed document.
* @param attributes The default print job attributes.
* @return The created print job.
* @return The created print job on success or null on failure.
*
* @see PrintJob
*/
@@ -181,7 +180,7 @@ public final class PrintManager {
* @param printJobName A name for the new print job.
* @param documentAdapter An adapter that emits the document to print.
* @param attributes The default print job attributes.
* @return The created print job.
* @return The created print job on success or null on failure.
*
* @see PrintJob
*/
@@ -279,7 +278,7 @@ public final class PrintManager {
}
SomeArgs args = SomeArgs.obtain();
args.arg1 = pages;
args.arg2 = fd.getFileDescriptor();
args.arg2 = fd;
args.arg3 = callback;
args.argi1 = sequence;
mHandler.removeMessages(MyHandler.MSG_WRITE);
@@ -342,7 +341,7 @@ public final class PrintManager {
case MSG_WRITE: {
SomeArgs args = (SomeArgs) message.obj;
PageRange[] pages = (PageRange[]) args.arg1;
FileDescriptor fd = (FileDescriptor) args.arg2;
ParcelFileDescriptor fd = (ParcelFileDescriptor) args.arg2;
IWriteResultCallback callback = (IWriteResultCallback) args.arg3;
final int sequence = args.argi1;
args.recycle();
@@ -428,12 +427,12 @@ public final class PrintManager {
}
private final class MyWriteResultCallback extends WriteResultCallback {
private FileDescriptor mFd;
private ParcelFileDescriptor mFd;
private int mSequence;
private IWriteResultCallback mCallback;
public MyWriteResultCallback(IWriteResultCallback callback,
FileDescriptor fd, int sequence) {
ParcelFileDescriptor fd, int sequence) {
mFd = fd;
mSequence = sequence;
mCallback = callback;

View File

@@ -74,6 +74,7 @@ public final class PrinterDiscoverySession {
public final void startPrinterDisovery(List<PrinterId> priorityList) {
if (isDestroyed()) {
Log.w(LOG_TAG, "Ignoring start printers dsicovery - session destroyed");
return;
}
if (!mIsPrinterDiscoveryStarted) {
mIsPrinterDiscoveryStarted = true;
@@ -88,6 +89,7 @@ public final class PrinterDiscoverySession {
public final void stopPrinterDiscovery() {
if (isDestroyed()) {
Log.w(LOG_TAG, "Ignoring stop printers discovery - session destroyed");
return;
}
if (mIsPrinterDiscoveryStarted) {
mIsPrinterDiscoveryStarted = false;
@@ -99,14 +101,39 @@ public final class PrinterDiscoverySession {
}
}
public final void requestPrinterUpdate(PrinterId printerId) {
public final void startPrinterStateTracking(PrinterId printerId) {
if (isDestroyed()) {
Log.w(LOG_TAG, "Ignoring reqeust printer update - session destroyed");
Log.w(LOG_TAG, "Ignoring start printer state tracking - session destroyed");
return;
}
try {
mPrintManager.requestPrinterUpdate(printerId, mUserId);
mPrintManager.startPrinterStateTracking(printerId, mUserId);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error requesting printer update", re);
Log.e(LOG_TAG, "Error starting printer state tracking", re);
}
}
public final void stopPrinterStateTracking(PrinterId printerId) {
if (isDestroyed()) {
Log.w(LOG_TAG, "Ignoring stop printer state tracking - session destroyed");
return;
}
try {
mPrintManager.stopPrinterStateTracking(printerId, mUserId);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error stoping printer state tracking", re);
}
}
public final void validatePrinters(List<PrinterId> printerIds) {
if (isDestroyed()) {
Log.w(LOG_TAG, "Ignoring validate printers - session destroyed");
return;
}
try {
mPrintManager.validatePrinters(printerIds, mUserId);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error validating printers", re);
}
}

View File

@@ -111,7 +111,10 @@ public final class PrintedPdfDocument {
* @see #finishPage(Page)
*/
public Page startPage(int pageNumber) {
PageInfo pageInfo = new PageInfo.Builder(mPageSize, 0).create();
PageInfo pageInfo = new PageInfo
.Builder(mPageSize, 0)
.setContentSize(mContentSize)
.create();
Page page = mDocument.startPage(pageInfo);
return page;
}

View File

@@ -33,6 +33,8 @@ oneway interface IPrintService {
void createPrinterDiscoverySession();
void startPrinterDiscovery(in List<PrinterId> priorityList);
void stopPrinterDiscovery();
void requestPrinterUpdate(in PrinterId printerId);
void validatePrinters(in List<PrinterId> printerIds);
void startPrinterStateTracking(in PrinterId printerId);
void stopPrinterStateTracking(in PrinterId printerId);
void destroyPrinterDiscoverySession();
}

View File

@@ -21,12 +21,15 @@ import android.os.RemoteException;
import android.print.PrintDocumentInfo;
import android.util.Log;
import java.io.FileDescriptor;
import java.io.IOException;
/**
* This class represents a printed document from the perspective of a print
* service. It exposes APIs to query the document and obtain its data.
* <p>
* <strong>Note: </strong> All methods of this class must be executed on the
* main application thread.
* </p>
*/
public final class PrintDocument {
@@ -51,6 +54,7 @@ public final class PrintDocument {
* @return The document info.
*/
public PrintDocumentInfo getInfo() {
PrintService.throwIfNotCalledOnMainThread();
return mInfo;
}
@@ -64,7 +68,8 @@ public final class PrintDocument {
*
* @return A file descriptor for reading the data.
*/
public FileDescriptor getData() {
public ParcelFileDescriptor getData() {
PrintService.throwIfNotCalledOnMainThread();
ParcelFileDescriptor source = null;
ParcelFileDescriptor sink = null;
try {
@@ -72,7 +77,7 @@ public final class PrintDocument {
source = fds[0];
sink = fds[1];
mPrintServiceClient.writePrintJobData(sink, mPrintJobId);
return source.getFileDescriptor();
return source;
} catch (IOException ioe) {
Log.e(LOG_TAG, "Error calling getting print job data!", ioe);
} catch (RemoteException re) {

View File

@@ -18,6 +18,7 @@ package android.printservice;
import android.os.RemoteException;
import android.print.PrintJobInfo;
import android.text.TextUtils;
import android.util.Log;
/**
@@ -122,6 +123,21 @@ public final class PrintJob {
return getInfo().getState() == PrintJobInfo.STATE_STARTED;
}
/**
* Gets whether this print job is blocked. Such a print job is halted
* due to an abnormal condition and can be started or canceled or failed.
*
* @return Whether the print job is blocked.
*
* @see #start()
* @see #cancel()
* @see #fail(CharSequence)
*/
public boolean isBlocked() {
PrintService.throwIfNotCalledOnMainThread();
return getInfo().getState() == PrintJobInfo.STATE_BLOCKED;
}
/**
* Gets whether this print job is completed. Such a print job
* is successfully printed. This is a final state.
@@ -163,20 +179,48 @@ public final class PrintJob {
/**
* Starts the print job. You should call this method if {@link
* #isQueued()} returns true and you started printing.
* #isQueued()} or {@link #isBlocked()} returns true and you started
* resumed printing.
*
* @return Whether the job as started.
* @return Whether the job was started.
*
* @see #isQueued()
* @see #isBlocked()
*/
public boolean start() {
PrintService.throwIfNotCalledOnMainThread();
if (isQueued()) {
final int state = getInfo().getState();
if (state == PrintJobInfo.STATE_QUEUED
|| state == PrintJobInfo.STATE_BLOCKED) {
return setState(PrintJobInfo.STATE_STARTED, null);
}
return false;
}
/**
* Blocks the print job. You should call this method if {@link
* #isStarted()} or {@link #isBlocked()} returns true and you need
* to block the print job. For example, the user has to add some
* paper to continue printing. To resume the print job call {@link
* #start()}.
*
* @return Whether the job was blocked.
*
* @see #isStarted()
* @see #isBlocked()
*/
public boolean block(String reason) {
PrintService.throwIfNotCalledOnMainThread();
PrintJobInfo info = getInfo();
final int state = info.getState();
if (state == PrintJobInfo.STATE_STARTED
|| (state == PrintJobInfo.STATE_BLOCKED
&& !TextUtils.equals(info.getStateReason(), reason))) {
return setState(PrintJobInfo.STATE_BLOCKED, reason);
}
return false;
}
/**
* Completes the print job. You should call this method if {@link
* #isStarted()} returns true and you are done printing.
@@ -195,8 +239,8 @@ public final class PrintJob {
/**
* Fails the print job. You should call this method if {@link
* #isQueued()} or {@link #isStarted()} returns true you failed
* while printing.
* #isQueued()} or {@link #isStarted()} or {@link #isBlocked()}
* returns true you failed while printing.
*
* @param error The human readable, short, and translated reason
* for the failure.
@@ -204,10 +248,11 @@ public final class PrintJob {
*
* @see #isQueued()
* @see #isStarted()
* @see #isBlocked()
*/
public boolean fail(String error) {
PrintService.throwIfNotCalledOnMainThread();
if (isQueued() || isStarted()) {
if (!isInImmutableState()) {
return setState(PrintJobInfo.STATE_FAILED, error);
}
return false;
@@ -215,18 +260,19 @@ public final class PrintJob {
/**
* Cancels the print job. You should call this method if {@link
* #isQueued()} or {@link #isStarted()} returns true and you canceled
* the print job as a response to a call to {@link
* PrintService#onRequestCancelPrintJob(PrintJob)}.
* #isQueued()} or {@link #isStarted() or #isBlocked()} returns
* true and you canceled the print job as a response to a call to
* {@link PrintService#onRequestCancelPrintJob(PrintJob)}.
*
* @return Whether the job is canceled.
*
* @see #isStarted()
* @see #isQueued()
* @see #isBlocked()
*/
public boolean cancel() {
PrintService.throwIfNotCalledOnMainThread();
if (isQueued() || isStarted()) {
if (!isInImmutableState()) {
return setState(PrintJobInfo.STATE_CANCELED, null);
}
return false;
@@ -277,7 +323,8 @@ public final class PrintJob {
private boolean isInImmutableState() {
final int state = mCachedInfo.getState();
return state == PrintJobInfo.STATE_COMPLETED
|| state == PrintJobInfo.STATE_CANCELED;
|| state == PrintJobInfo.STATE_CANCELED
|| state == PrintJobInfo.STATE_FAILED;
}
private boolean setState(int state, String error) {
@@ -287,7 +334,7 @@ public final class PrintJob {
// we may not be able to re-fetch it later if the job gets
// removed from the spooler as a result of the state change.
mCachedInfo.setState(state);
mCachedInfo.setFailureReason(error);
mCachedInfo.setStateReason(error);
return true;
}
} catch (RemoteException re) {

View File

@@ -314,8 +314,20 @@ public abstract class PrintService extends Service {
}
@Override
public void requestPrinterUpdate(PrinterId printerId) {
mHandler.obtainMessage(ServiceHandler.MSG_REQUEST_PRINTER_UPDATE,
public void validatePrinters(List<PrinterId> printerIds) {
mHandler.obtainMessage(ServiceHandler.MSG_VALIDATE_PRINTERS,
printerIds).sendToTarget();
}
@Override
public void startPrinterStateTracking(PrinterId printerId) {
mHandler.obtainMessage(ServiceHandler.MSG_START_PRINTER_STATE_TRACKING,
printerId).sendToTarget();
}
@Override
public void stopPrinterStateTracking(PrinterId printerId) {
mHandler.obtainMessage(ServiceHandler.MSG_STOP_PRINTER_STATE_TRACKING,
printerId).sendToTarget();
}
@@ -344,10 +356,12 @@ public abstract class PrintService extends Service {
public static final int MSG_DESTROY_PRINTER_DISCOVERY_SESSION = 2;
public static final int MSG_START_PRINTER_DISCOVERY = 3;
public static final int MSG_STOP_PRINTER_DISCOVERY = 4;
public static final int MSG_REQUEST_PRINTER_UPDATE = 5;
public static final int MSG_ON_PRINTJOB_QUEUED = 6;
public static final int MSG_ON_REQUEST_CANCEL_PRINTJOB = 7;
public static final int MSG_SET_CLEINT = 8;
public static final int MSG_VALIDATE_PRINTERS = 5;
public static final int MSG_START_PRINTER_STATE_TRACKING = 6;
public static final int MSG_STOP_PRINTER_STATE_TRACKING = 7;
public static final int MSG_ON_PRINTJOB_QUEUED = 8;
public static final int MSG_ON_REQUEST_CANCEL_PRINTJOB = 9;
public static final int MSG_SET_CLEINT = 10;
public ServiceHandler(Looper looper) {
super(looper, null, true);
@@ -391,10 +405,24 @@ public abstract class PrintService extends Service {
}
} break;
case MSG_REQUEST_PRINTER_UPDATE: {
case MSG_VALIDATE_PRINTERS: {
if (mDiscoverySession != null) {
List<PrinterId> printerIds = (List<PrinterId>) message.obj;
mDiscoverySession.validatePrinters(printerIds);
}
} break;
case MSG_START_PRINTER_STATE_TRACKING: {
if (mDiscoverySession != null) {
PrinterId printerId = (PrinterId) message.obj;
mDiscoverySession.requestPrinterUpdate(printerId);
mDiscoverySession.startPrinterStateTracking(printerId);
}
} break;
case MSG_STOP_PRINTER_STATE_TRACKING: {
if (mDiscoverySession != null) {
PrinterId printerId = (PrinterId) message.obj;
mDiscoverySession.stopPrinterStateTracking(printerId);
}
} break;

View File

@@ -53,15 +53,23 @@ import java.util.List;
* session. Printers are <strong>not</strong> persisted across sessions.
* </p>
* <p>
* The system will make a call to
* {@link PrinterDiscoverySession#onRequestPrinterUpdate(PrinterId)} if you
* need to update a given printer. It is possible that you add a printer without
* The system will make a call to {@link #onValidatePrinters(List)} if you
* need to update some printers. It is possible that you add a printer without
* specifying its capabilities. This enables you to avoid querying all discovered
* printers for their capabilities, rather querying the capabilities of a printer
* only if necessary. For example, the system will request that you update a printer
* if it gets selected by the user. If you did not report the printer capabilities
* when adding it, you must do so after the system requests a printer update.
* Otherwise, the printer will be ignored.
* if it gets selected by the user. When validating printers you do not need to
* provide the printers' capabilities but may do so.
* </p>
* <p>
* If the system is interested in being constantly updated for the state of a
* printer you will receive a call to {@link #onStartPrinterStateTracking(PrinterId)}
* after which you will have to do a best effort to keep the system updated for
* changes in the printer state and capabilities. You also <strong>must</strong>
* update the printer capabilities if you did not provide them when adding it, or
* the printer will be ignored. When the system is no longer interested in getting
* updates for a printer you will receive a call to {@link #onStopPrinterStateTracking(
* PrinterId)}.
* </p>
* <p>
* <strong>Note: </strong> All callbacks in this class are executed on the main
@@ -115,7 +123,7 @@ public abstract class PrinterDiscoverySession {
* the printer that was added but not removed.
* <p>
* <strong>Note: </strong> Calls to this method after the session is
* destroyed, i.e. after the {@link #onDestroy()} callback, will be ignored.
* destroyed, that is after the {@link #onDestroy()} callback, will be ignored.
* </p>
*
* @return The printers.
@@ -139,7 +147,7 @@ public abstract class PrinterDiscoverySession {
* times during the life of this session. Duplicates will be ignored.
* <p>
* <strong>Note: </strong> Calls to this method after the session is
* destroyed, i.e. after the {@link #onDestroy()} callback, will be ignored.
* destroyed, that is after the {@link #onDestroy()} callback, will be ignored.
* </p>
*
* @param printers The printers to add.
@@ -218,7 +226,7 @@ public abstract class PrinterDiscoverySession {
* call this method multiple times during the lifetime of this session.
* <p>
* <strong>Note: </strong> Calls to this method after the session is
* destroyed, i.e. after the {@link #onDestroy()} callback, will be ignored.
* destroyed, that is after the {@link #onDestroy()} callback, will be ignored.
* </p>
*
* @param printerIds The ids of the removed printers.
@@ -293,7 +301,7 @@ public abstract class PrinterDiscoverySession {
* during the lifetime of this session.
* <p>
* <strong>Note: </strong> Calls to this method after the session is
* destroyed, i.e. after the {@link #onDestroy()} callback, will be ignored.
* destroyed, that is after the {@link #onDestroy()} callback, will be ignored.
* </p>
*
* @param printers The printers to update.
@@ -441,7 +449,9 @@ public abstract class PrinterDiscoverySession {
* <p>
* <strong>Note: </strong>You are also given a list of printers whose availability
* has to be checked first. For example, these printers could be the user's favorite
* ones, therefore they have to be verified first.
* ones, therefore they have to be verified first. You do <strong>not need</strong>
* to provide the capabilities of the printers, rather verify whether they exist
* similarly to {@link #onValidatePrinters(List)}.
* </p>
*
* @param priorityList The list of printers to validate first. Never null.
@@ -463,9 +473,28 @@ public abstract class PrinterDiscoverySession {
public abstract void onStopPrinterDiscovery();
/**
* Requests that you update a printer. You are responsible for updating
* the printer by also reporting its capabilities via calling {@link
* #updatePrinters(List)}.
* Callback asking you to validate that the given printers are valid, that
* is they exist. You are responsible for checking whether these printers
* exist and for the ones that do exist notify the system via calling
* {@link #updatePrinters(List)}.
* <p>
* <strong>Note: </strong> You are <strong>not required</strong> to provide
* the printer capabilities when updating the printers that do exist.
* <p>
*
* @param printerIds The printers to validate.
*
* @see #updatePrinters(List)
* @see PrinterInfo.Builder#setCapabilities(PrinterCapabilitiesInfo)
* PrinterInfo.Builder.setCapabilities(PrinterCapabilitiesInfo)
*/
public abstract void onValidatePrinters(List<PrinterId> printerIds);
/**
* Callback asking you to start tracking the state of a printer. Tracking
* the state means that you should do a best effort to observe the state
* of this printer and notify the system if that state changes via calling
* {@link #updatePrinters(List)}.
* <p>
* <strong>Note: </strong> A printer can be initially added without its
* capabilities to avoid polling printers that the user will not select.
@@ -473,18 +502,33 @@ public abstract class PrinterDiscoverySession {
* printer <strong>including</strong> its capabilities. Otherwise, the
* printer will be ignored.
* <p>
* A scenario when you may be requested to update a printer is if the user
* selects it and the system has to present print options UI based on the
* printer's capabilities.
* <p>
* A scenario when you may be requested to track a printer's state is if
* the user selects that printer and the system has to present print
* options UI based on the printer's capabilities. In this case the user
* should be promptly informed if, for example, the printer becomes
* unavailable.
* </p>
*
* @param printerId The printer id.
* @param printerId The printer to start tracking.
*
* @see #onStopPrinterStateTracking(PrinterId)
* @see #updatePrinters(List)
* @see PrinterInfo.Builder#setCapabilities(PrinterCapabilitiesInfo)
* PrinterInfo.Builder.setCapabilities(PrinterCapabilitiesInfo)
*/
public abstract void onRequestPrinterUpdate(PrinterId printerId);
public abstract void onStartPrinterStateTracking(PrinterId printerId);
/**
* Callback asking you to stop tracking the state of a printer. The passed
* in printer id is the one for which you received a call to {@link
* #onStartPrinterStateTracking(PrinterId)}.
*
* @param printerId The printer to stop tracking.
*
* @see #onStartPrinterStateTracking(PrinterId)
*/
public abstract void onStopPrinterStateTracking(PrinterId printerId);
/**
* Notifies you that the session is destroyed. After this callback is invoked
@@ -538,9 +582,21 @@ public abstract class PrinterDiscoverySession {
}
}
void requestPrinterUpdate(PrinterId printerId) {
if (!mIsDestroyed) {
onRequestPrinterUpdate(printerId);
void validatePrinters(List<PrinterId> printerIds) {
if (!mIsDestroyed && mObserver != null) {
onValidatePrinters(printerIds);
}
}
void startPrinterStateTracking(PrinterId printerId) {
if (!mIsDestroyed && mObserver != null) {
onStartPrinterStateTracking(printerId);
}
}
void stopPrinterStateTracking(PrinterId printerId) {
if (!mIsDestroyed && mObserver != null) {
onStopPrinterStateTracking(printerId);
}
}

View File

@@ -1919,13 +1919,10 @@
android:description="@string/permdesc_bindNfcService"
android:protectionLevel="signature" />
<!-- Allows an application to call APIs that give it access to all print jobs
on the device. Usually an app can access only the print jobts it created.
This permission is not available to third party applications.
@hide -->
<permission android:name="android.permission.ACCESS_ALL_PRINT_JOBS"
android:label="@string/permlab_accessAllPrintJobs"
android:description="@string/permdesc_accessAllPrintJobs"
<!-- Must be required by the PrintSpooler to ensure that only the system can bind to it. -->
<permission android:name="android.permission.BIND_PRINT_SPOOLER_SERVICE"
android:label="@string/permlab_bindPrintSpoolerService"
android:description="@string/permdesc_bindPrintSpoolerService"
android:protectionLevel="signature" />
<!-- Must be required by a TextService (e.g. SpellCheckerService)

View File

@@ -984,12 +984,13 @@
<string name="permdesc_bindPrintService">Allows the holder to bind to the top-level
interface of a print service. Should never be needed for normal apps.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_accessAllPrintJobs">access all print jobs</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_accessAllPrintJobs">Allows the holder to access print jobs
created by another app. Should never be needed for normal apps.</string>
<!-- Title of an application permission, listed so the user can choose
whether they want to allow the application to do this. -->
<string name="permlab_bindPrintSpoolerService">bind to a print spooler service</string>
<!-- Description of an application permission, listed so the user can
choose whether they want to allow the application to do this. -->
<string name="permdesc_bindPrintSpoolerService">Allows the holder to bind to the top-level
interface of a print spooler service. Should never be needed for normal apps.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_bindNfcService">bind to NFC service</string>
@@ -4292,6 +4293,9 @@
<!-- Write fail reason: couldn't write the printed content. [CHAR LIMIT=none] -->
<string name="write_fail_reason_cannot_write">Error writing content</string>
<!-- Print fail reason: unknown. [CHAR LIMIT=25] -->
<string name="reason_unknown">unknown</string>
<!-- PIN entry dialog label/hint for PIN [CHAR LIMIT=none] -->
<string name="restr_pin_enter_pin">Enter PIN</string>
<!-- PIN entry dialog label/hint for old PIN [CHAR LIMIT=none] -->

View File

@@ -869,6 +869,7 @@
<java-symbol type="string" name="mediaSize_na_junior_legal" />
<java-symbol type="string" name="mediaSize_na_ledger" />
<java-symbol type="string" name="mediaSize_na_tabloid" />
<java-symbol type="string" name="reason_unknown" />
<java-symbol type="string" name="restr_pin_enter_pin" />
<java-symbol type="string" name="write_fail_reason_cancelled" />
<java-symbol type="string" name="write_fail_reason_cannot_write" />

View File

@@ -24,8 +24,6 @@ LOCAL_PACKAGE_NAME := PrintSpooler
LOCAL_JAVA_LIBRARIES := framework-base
LOCAL_CERTIFICATE := platform
LOCAL_PROGUARD_ENABLED := disabled
include $(BUILD_PACKAGE)

View File

@@ -20,18 +20,22 @@
package="com.android.printspooler"
android:sharedUserId="android.uid.printspooler"
android:versionName="1"
android:versionCode="1"
coreApp="true">
android:versionCode="1">
<uses-sdk android:minSdkVersion="18" android:targetSdkVersion="18"/>
<!-- Allows an application to call APIs that give it access to all print jobs
on the device. Usually an app can access only the print jobs it created.
-->
<permission
android:name="com.android.printspooler.permission.ACCESS_ALL_PRINT_JOBS"
android:label="@string/permlab_accessAllPrintJobs"
android:description="@string/permdesc_accessAllPrintJobs"
android:protectionLevel="signature" />
<uses-permission android:name="com.android.printspooler.permission.ACCESS_ALL_PRINT_JOBS"/>
<uses-permission android:name="android.permission.ACCESS_ALL_PRINT_JOBS"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<permission android:name="android.permission.BIND_PRINT_SPOOLER_SERVICE"
android:label="@string/permlab_bindPrintSpoolerService"
android:description="@string/permdesc_bindPrintSpoolerService"
android:protectionLevel="signature" />
<uses-sdk android:minSdkVersion="18" android:targetSdkVersion="18"/>
<application
android:allowClearUserData="false"

View File

@@ -94,6 +94,9 @@
<!-- Template for the notificaiton label for a failed print job. [CHAR LIMIT=25] -->
<string name="failed_notification_title_template">Printer error <xliff:g id="print_job_name" example="foo.jpg">%1$s</xliff:g></string>
<!-- Template for the notificaiton label for a blocked print job. [CHAR LIMIT=25] -->
<string name="blocked_notification_title_template">Printer blocked <xliff:g id="print_job_name" example="foo.jpg">%1$s</xliff:g></string>
<!-- Label for the notification button for cancelling a print job. [CHAR LIMIT=25] -->
<string name="cancel">Cancel</string>
@@ -103,6 +106,9 @@
<!-- Message that there is no connection to a printer. [CHAR LIMIT=40] -->
<string name="no_connection_to_printer">No connection to printer</string>
<!-- Label for an unknown reason for failed or blocked print job. [CHAR LIMIT=25] -->
<string name="reason_unknown">unknown</string>
<!-- Arrays -->
<!-- Color mode labels. -->
@@ -129,12 +135,14 @@
<item>Range</item>
</string-array>
<!-- Title of an application permission, listed so the user can choose
whether they want to allow the application to do this. -->
<string name="permlab_bindPrintSpoolerService">bind to a print spooler service</string>
<!-- Description of an application permission, listed so the user can
choose whether they want to allow the application to do this. -->
<string name="permdesc_bindPrintSpoolerService">Allows the holder to bind to the top-level
interface of a print spooler service. Should never be needed for normal apps.</string>
<!-- Permissions -->
<!-- Title of an application permission, listed so the user can choose whether they want
to allow the application to do this. -->
<string name="permlab_accessAllPrintJobs">access all print jobs</string>
<!-- Description of an application permission, listed so the user can choose whether
they want to allow the application to do this. -->
<string name="permdesc_accessAllPrintJobs">Allows the holder to access print jobs
created by another app. Should never be needed for normal apps.</string>
</resources>

View File

@@ -75,6 +75,8 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> {
private List<PrinterInfo> mFavoritePrinters;
private PrinterId mTrackedPrinter;
public FusedPrintersProvider(Context context) {
super(context);
mPersistenceManager = new PersistenceManager(context);
@@ -166,6 +168,10 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> {
private boolean cancelInternal() {
if (mDiscoverySession != null
&& mDiscoverySession.isPrinterDiscoveryStarted()) {
if (mTrackedPrinter != null) {
mDiscoverySession.stopPrinterStateTracking(mTrackedPrinter);
mTrackedPrinter = null;
}
mDiscoverySession.stopPrinterDiscovery();
return true;
} else if (mPersistenceManager.isReadHistoryInProgress()) {
@@ -195,10 +201,14 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> {
onStopLoading();
}
public void refreshPrinter(PrinterId printerId) {
public void setTrackedPrinter(PrinterId printerId) {
if (isStarted() && mDiscoverySession != null
&& mDiscoverySession.isPrinterDiscoveryStarted()) {
mDiscoverySession.requestPrinterUpdate(printerId);
if (mTrackedPrinter != null) {
mDiscoverySession.stopPrinterStateTracking(mTrackedPrinter);
}
mTrackedPrinter = printerId;
mDiscoverySession.startPrinterStateTracking(printerId);
}
}

View File

@@ -32,6 +32,7 @@ import android.os.UserHandle;
import android.print.IPrintManager;
import android.print.PrintJobInfo;
import android.print.PrintManager;
import android.text.TextUtils;
import android.util.Log;
/**
@@ -64,22 +65,27 @@ public class NotificationController {
+ " state:" + PrintJobInfo.stateToString(printJob.getState()));
}
switch (printJob.getState()) {
case PrintJobInfo.STATE_QUEUED: {
createPrintingNotificaiton(printJob);
case PrintJobInfo.STATE_QUEUED:
case PrintJobInfo.STATE_STARTED: {
createPrintingNotification(printJob);
} break;
case PrintJobInfo.STATE_FAILED: {
createFailedNotificaiton(printJob);
createFailedNotification(printJob);
} break;
case PrintJobInfo.STATE_COMPLETED:
case PrintJobInfo.STATE_CANCELED: {
removeNotification(printJob.getId());
} break;
case PrintJobInfo.STATE_BLOCKED: {
createBlockedNotification(printJob);
} break;
}
}
private void createPrintingNotificaiton(PrintJobInfo printJob) {
private void createPrintingNotification(PrintJobInfo printJob) {
Notification.Builder builder = new Notification.Builder(mContext)
.setSmallIcon(R.drawable.stat_notify_print)
.setContentTitle(mContext.getString(R.string.printing_notification_title_template,
@@ -93,17 +99,36 @@ public class NotificationController {
mNotificationManager.notify(printJob.getId(), builder.build());
}
private void createFailedNotificaiton(PrintJobInfo printJob) {
private void createFailedNotification(PrintJobInfo printJob) {
String reason = !TextUtils.isEmpty(printJob.getStateReason())
? printJob.getStateReason() : mContext.getString(R.string.reason_unknown);
Notification.Builder builder = new Notification.Builder(mContext)
.setSmallIcon(R.drawable.stat_notify_error)
.setContentTitle(mContext.getString(R.string.failed_notification_title_template,
printJob.getLabel()))
.addAction(R.drawable.stat_notify_cancelling, mContext.getString(R.string.cancel),
createCancelIntent(printJob))
// TODO: Use appropriate icon when assets are ready
.addAction(android.R.drawable.ic_secure, mContext.getString(R.string.restart),
createRestartIntent(printJob.getId()))
.setContentText(printJob.getFailureReason())
.setContentText(reason)
.setWhen(System.currentTimeMillis())
.setOngoing(true)
.setShowWhen(true);
mNotificationManager.notify(printJob.getId(), builder.build());
}
private void createBlockedNotification(PrintJobInfo printJob) {
String reason = !TextUtils.isEmpty(printJob.getStateReason())
? printJob.getStateReason() : mContext.getString(R.string.reason_unknown);
Notification.Builder builder = new Notification.Builder(mContext)
.setSmallIcon(R.drawable.stat_notify_error)
.setContentTitle(mContext.getString(R.string.blocked_notification_title_template,
printJob.getLabel()))
.addAction(R.drawable.stat_notify_cancelling, mContext.getString(R.string.cancel),
createCancelIntent(printJob))
.setContentText(reason)
.setWhen(System.currentTimeMillis())
.setOngoing(true)
.setShowWhen(true);

View File

@@ -473,6 +473,11 @@ public class PrintJobConfigActivity extends Activity {
mControllerState = CONTROLLER_STATE_WRITE_COMPLETED;
// Update the document size.
File file = PrintSpoolerService.peekInstance()
.generateFileForPrintJob(mPrintJobId);
mDocument.info.setDataSize(file.length());
// Update which pages we have fetched.
mDocument.pages = PageRangeUtils.normalize(pages);
@@ -1117,7 +1122,7 @@ public class PrintJobConfigActivity extends Activity {
(Loader<?>) getLoaderManager().getLoader(
LOADER_ID_PRINTERS_LOADER);
if (printersLoader != null) {
printersLoader.refreshPrinter(printer.getId());
printersLoader.setTrackedPrinter(printer.getId());
}
}
}
@@ -1351,10 +1356,6 @@ public class PrintJobConfigActivity extends Activity {
return mEditorState == EDITOR_STATE_CONFIRMED_PRINT;
}
// public void confirmPreview() {
// mEditorState = EDITOR_STATE_CONFIRMED_PREVIEW;
// }
public PageRange[] getRequestedPages() {
if (hasErrors()) {
return null;
@@ -1374,7 +1375,7 @@ public class PrintJobConfigActivity extends Activity {
toIndex = Integer.parseInt(range.substring(
dashIndex + 1, range.length())) - 1;
} else {
fromIndex = toIndex = Integer.parseInt(range);
fromIndex = toIndex = Integer.parseInt(range) - 1;
}
PageRange pageRange = new PageRange(fromIndex, toIndex);

View File

@@ -335,7 +335,9 @@ public final class PrintSpoolerService extends Service {
final boolean sameState = (state == printJob.getState())
|| (state == PrintJobInfo.STATE_ANY)
|| (state == PrintJobInfo.STATE_ANY_VISIBLE_TO_CLIENTS
&& printJob.getState() > PrintJobInfo.STATE_CREATED);
&& isStateVisibleToUser(printJob.getState()))
|| (state == PrintJobInfo.STATE_ANY_ACTIVE
&& isActiveState(printJob.getState()));
if (sameComponent && sameAppId && sameState) {
if (foundPrintJobs == null) {
foundPrintJobs = new ArrayList<PrintJobInfo>();
@@ -347,6 +349,11 @@ public final class PrintSpoolerService extends Service {
return foundPrintJobs;
}
private boolean isStateVisibleToUser(int state) {
return (isActiveState(state) && (state == PrintJobInfo.STATE_FAILED
|| state == PrintJobInfo.STATE_COMPLETED|| state == PrintJobInfo.STATE_CANCELED));
}
public PrintJobInfo getPrintJobInfo(int printJobId, int appId) {
synchronized (mLock) {
final int printJobCount = mPrintJobs.size();
@@ -389,14 +396,12 @@ public final class PrintSpoolerService extends Service {
switch (printJob.getState()) {
case PrintJobInfo.STATE_QUEUED:
case PrintJobInfo.STATE_STARTED: {
// We have a print job that was queued or started in the
// past
// but the device battery died or a crash occurred. In this
// case
// we assume the print job failed and let the user decide
// whether
// to restart the job or just
case PrintJobInfo.STATE_STARTED:
case PrintJobInfo.STATE_BLOCKED: {
// We have a print job that was queued or started or blocked in
// the past but the device battery died or a crash occurred. In
// this case we assume the print job failed and let the user
// decide whether to restart the job or just cancel it.
setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED,
getString(R.string.no_connection_to_printer));
}
@@ -501,7 +506,7 @@ public final class PrintSpoolerService extends Service {
success = true;
printJob.setState(state);
printJob.setFailureReason(error);
printJob.setStateReason(error);
mNotificationController.onPrintJobStateChanged(printJob);
if (DEBUG_PRINT_JOB_LIFECYCLE) {
@@ -568,7 +573,8 @@ public final class PrintSpoolerService extends Service {
private boolean isActiveState(int printJobState) {
return printJobState == PrintJobInfo.STATE_CREATED
|| printJobState == PrintJobInfo.STATE_QUEUED
|| printJobState == PrintJobInfo.STATE_STARTED;
|| printJobState == PrintJobInfo.STATE_STARTED
|| printJobState == PrintJobInfo.STATE_BLOCKED;
}
public boolean setPrintJobTag(int printJobId, String tag) {

View File

@@ -254,7 +254,7 @@ public final class PrintManagerService extends IPrintManager.Stub {
}
@Override
public void requestPrinterUpdate(PrinterId printerId, int userId) {
public void validatePrinters(List<PrinterId> printerIds, int userId) {
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final UserState userState;
synchronized (mLock) {
@@ -262,7 +262,37 @@ public final class PrintManagerService extends IPrintManager.Stub {
}
final long identity = Binder.clearCallingIdentity();
try {
userState.requestPrinterUpdate(printerId);
userState.validatePrinters(printerIds);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void startPrinterStateTracking(PrinterId printerId, int userId) {
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final UserState userState;
synchronized (mLock) {
userState = getOrCreateUserStateLocked(resolvedUserId);
}
final long identity = Binder.clearCallingIdentity();
try {
userState.startPrinterStateTracking(printerId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void stopPrinterStateTracking(PrinterId printerId, int userId) {
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final UserState userState;
synchronized (mLock) {
userState = getOrCreateUserStateLocked(resolvedUserId);
}
final long identity = Binder.clearCallingIdentity();
try {
userState.stopPrinterStateTracking(printerId);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -432,10 +462,12 @@ public final class PrintManagerService extends IPrintManager.Stub {
if (appId == callingAppId) {
return appId;
}
if (mContext.checkCallingPermission(Manifest.permission.ACCESS_ALL_PRINT_JOBS)
if (mContext.checkCallingPermission(
"com.android.printspooler.permission.ACCESS_ALL_PRINT_JOBS")
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Call from app " + callingAppId + " as app "
+ appId + " without permission ACCESS_ALL_PRINT_JOBS");
+ appId + " without com.android.printspooler.permission"
+ ".ACCESS_ALL_PRINT_JOBS");
}
return appId;
}

View File

@@ -25,6 +25,7 @@ import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.AsyncTask;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
@@ -38,6 +39,8 @@ import android.printservice.IPrintService;
import android.printservice.IPrintServiceClient;
import android.util.Slog;
import com.android.internal.R;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -121,6 +124,36 @@ final class RemotePrintService implements DeathRecipient {
mHasPrinterDiscoverySession = false;
mPendingCommands.clear();
ensureUnbound();
// Makes sure all active print jobs are failed since the service
// just died. Do this off the main thread since we do to allow
// calls into the spooler on the main thread.
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
failAllActivePrintJobs();
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
}
private void failAllActivePrintJobs() {
List<PrintJobInfo> printJobs = mSpooler.getPrintJobInfos(mComponentName,
PrintJobInfo.STATE_ANY_ACTIVE, PrintManager.APP_ID_ANY);
if (printJobs == null) {
return;
}
final long identity = Binder.clearCallingIdentity();
try {
final int printJobCount = printJobs.size();
for (int i = 0; i < printJobCount; i++) {
PrintJobInfo printJob = printJobs.get(i);
mSpooler.setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED,
mContext.getString(R.string.reason_unknown));
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
private void handleOnAllPrintJobsHandled() {
@@ -308,29 +341,83 @@ final class RemotePrintService implements DeathRecipient {
}
}
public void requestPrinterUpdate(PrinterId printerId) {
mHandler.obtainMessage(MyHandler.MSG_REQUEST_PRINTER_UPDATE,
printerId).sendToTarget();
public void validatePrinters(List<PrinterId> printerIds) {
mHandler.obtainMessage(MyHandler.MSG_VALIDATE_PRINTERS,
printerIds).sendToTarget();
}
private void handleRequestPrinterUpdate(final PrinterId printerId) {
private void handleValidatePrinters(final List<PrinterId> printerIds) {
throwIfDestroyed();
if (!isBound()) {
ensureBound();
mPendingCommands.add(new Runnable() {
@Override
public void run() {
handleRequestPrinterUpdate(printerId);
handleValidatePrinters(printerIds);
}
});
} else {
if (DEBUG) {
Slog.i(LOG_TAG, "[user: " + mUserId + "] requestPrinterUpdate()");
Slog.i(LOG_TAG, "[user: " + mUserId + "] handleValidatePrinters()");
}
try {
mPrintService.requestPrinterUpdate(printerId);
mPrintService.validatePrinters(printerIds);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error requesting a printer update.", re);
Slog.e(LOG_TAG, "Error requesting printers validation.", re);
}
}
}
public void startPrinterStateTracking(PrinterId printerId) {
mHandler.obtainMessage(MyHandler.MSG_START_PRINTER_STATE_TRACKING,
printerId).sendToTarget();
}
private void handleStartPrinterStateTracking(final PrinterId printerId) {
throwIfDestroyed();
if (!isBound()) {
ensureBound();
mPendingCommands.add(new Runnable() {
@Override
public void run() {
handleStartPrinterStateTracking(printerId);
}
});
} else {
if (DEBUG) {
Slog.i(LOG_TAG, "[user: " + mUserId + "] handleStartPrinterTracking()");
}
try {
mPrintService.startPrinterStateTracking(printerId);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error requesting start printer tracking.", re);
}
}
}
public void stopPrinterStateTracking(PrinterId printerId) {
mHandler.obtainMessage(MyHandler.MSG_STOP_PRINTER_STATE_TRACKING,
printerId).sendToTarget();
}
private void handleStopPrinterStateTracking(final PrinterId printerId) {
throwIfDestroyed();
if (!isBound()) {
ensureBound();
mPendingCommands.add(new Runnable() {
@Override
public void run() {
handleStopPrinterStateTracking(printerId);
}
});
} else {
if (DEBUG) {
Slog.i(LOG_TAG, "[user: " + mUserId + "] handleStopPrinterTracking()");
}
try {
mPrintService.stopPrinterStateTracking(printerId);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error requesting stop printer tracking.", re);
}
}
}
@@ -417,12 +504,14 @@ final class RemotePrintService implements DeathRecipient {
public static final int MSG_DESTROY_PRINTER_DISCOVERY_SESSION = 2;
public static final int MSG_START_PRINTER_DISCOVERY = 3;
public static final int MSG_STOP_PRINTER_DISCOVERY = 4;
public static final int MSG_REQUEST_PRINTER_UPDATE = 5;
public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 6;
public static final int MSG_ON_REQUEST_CANCEL_PRINT_JOB = 7;
public static final int MSG_ON_PRINT_JOB_QUEUED = 8;
public static final int MSG_DESTROY = 9;
public static final int MSG_BINDER_DIED = 10;
public static final int MSG_VALIDATE_PRINTERS = 5;
public static final int MSG_START_PRINTER_STATE_TRACKING = 6;
public static final int MSG_STOP_PRINTER_STATE_TRACKING = 7;
public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 8;
public static final int MSG_ON_REQUEST_CANCEL_PRINT_JOB = 9;
public static final int MSG_ON_PRINT_JOB_QUEUED = 10;
public static final int MSG_DESTROY = 11;
public static final int MSG_BINDER_DIED = 12;
public MyHandler(Looper looper) {
super(looper, null, false);
@@ -449,9 +538,19 @@ final class RemotePrintService implements DeathRecipient {
handleStopPrinterDiscovery();
} break;
case MSG_REQUEST_PRINTER_UPDATE: {
case MSG_VALIDATE_PRINTERS: {
List<PrinterId> printerIds = (List<PrinterId>) message.obj;
handleValidatePrinters(printerIds);
} break;
case MSG_START_PRINTER_STATE_TRACKING: {
PrinterId printerId = (PrinterId) message.obj;
handleRequestPrinterUpdate(printerId);
handleStartPrinterStateTracking(printerId);
} break;
case MSG_STOP_PRINTER_STATE_TRACKING: {
PrinterId printerId = (PrinterId) message.obj;
handleStopPrinterStateTracking(printerId);
} break;
case MSG_ON_ALL_PRINT_JOBS_HANDLED: {

View File

@@ -19,8 +19,12 @@ package com.android.server.print;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -46,6 +50,7 @@ import com.android.server.print.RemotePrintSpooler.PrintSpoolerCallbacks;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -63,6 +68,11 @@ final class UserState implements PrintSpoolerCallbacks {
private static final char COMPONENT_NAME_SEPARATOR = ':';
private static final String SHARED_PREFERENCES_FILE = "shared_prefs";
private static final String KEY_SYSTEM_PRINT_SERVICES_ENABLED =
"KEY_SYSTEM_PRINT_SERVICES_ENABLED";
private final SimpleStringSplitter mStringColonSplitter =
new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
@@ -95,6 +105,7 @@ final class UserState implements PrintSpoolerCallbacks {
mUserId = userId;
mLock = lock;
mSpooler = new RemotePrintSpooler(context, userId, this);
enableSystemPrintServicesOnce();
}
@Override
@@ -190,7 +201,7 @@ final class UserState implements PrintSpoolerCallbacks {
}
}
public void requestPrinterUpdate(PrinterId printerId) {
public void validatePrinters(List<PrinterId> printerIds) {
synchronized (mLock) {
throwIfDestroyedLocked();
// No services - nothing to do.
@@ -202,7 +213,39 @@ final class UserState implements PrintSpoolerCallbacks {
return;
}
// Request an updated.
mPrinterDiscoverySession.requestPrinterUpdateLocked(printerId);
mPrinterDiscoverySession.validatePrintersLocked(printerIds);
}
}
public void startPrinterStateTracking(PrinterId printerId) {
synchronized (mLock) {
throwIfDestroyedLocked();
// No services - nothing to do.
if (mActiveServices.isEmpty()) {
return;
}
// No session - nothing to do.
if (mPrinterDiscoverySession == null) {
return;
}
// Request start tracking the printer.
mPrinterDiscoverySession.startPrinterStateTrackingLocked(printerId);
}
}
public void stopPrinterStateTracking(PrinterId printerId) {
synchronized (mLock) {
throwIfDestroyedLocked();
// No services - nothing to do.
if (mActiveServices.isEmpty()) {
return;
}
// No session - nothing to do.
if (mPrinterDiscoverySession == null) {
return;
}
// Request stop tracking the printer.
mPrinterDiscoverySession.stopPrinterStateTrackingLocked(printerId);
}
}
@@ -365,6 +408,36 @@ final class UserState implements PrintSpoolerCallbacks {
return false;
}
private void enableSystemPrintServicesOnce() {
SharedPreferences preferences = mContext.getSharedPreferences(
SHARED_PREFERENCES_FILE, Context.MODE_PRIVATE);
if (preferences.getInt(KEY_SYSTEM_PRINT_SERVICES_ENABLED, 0) == 0) {
Editor editor = preferences.edit();
editor.putInt(KEY_SYSTEM_PRINT_SERVICES_ENABLED, 1);
editor.commit();
readInstalledPrintServicesLocked();
StringBuilder builder = new StringBuilder();
final int serviceCount = mInstalledServices.size();
for (int i = 0; i < serviceCount; i++) {
ServiceInfo serviceInfo = mInstalledServices.get(i).getResolveInfo().serviceInfo;
if ((serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
ComponentName serviceName = new ComponentName(
serviceInfo.packageName, serviceInfo.name);
if (builder.length() > 0) {
builder.append(":");
}
builder.append(serviceName.flattenToString());
}
}
Settings.Secure.putStringForUser(mContext.getContentResolver(),
Settings.Secure.ENABLED_PRINT_SERVICES, builder.toString(), mUserId);
}
}
private void onConfigurationChangedLocked() {
final int installedCount = mInstalledServices.size();
for (int i = 0; i < installedCount; i++) {
@@ -415,6 +488,8 @@ final class UserState implements PrintSpoolerCallbacks {
private final List<IBinder> mStartedPrinterDiscoveryTokens = new ArrayList<IBinder>();
private final List<PrinterId> mStateTrackedPrinters = new ArrayList<PrinterId>();
private final Handler mHandler;
private boolean mIsDestroyed;
@@ -461,14 +536,10 @@ final class UserState implements PrintSpoolerCallbacks {
}
// If printer discovery is ongoing and the start request has a list
// of printer to be checked, then we just request refreshing each of
// them rather making another start discovery request.
// of printer to be checked, then we just request validating them.
if (!mStartedPrinterDiscoveryTokens.isEmpty()
&& priorityList != null && !priorityList.isEmpty()) {
final int priorityIdCount = priorityList.size();
for (int i = 0; i < priorityIdCount; i++) {
requestPrinterUpdate(priorityList.get(i));
}
validatePrinters(priorityList);
return;
}
@@ -508,22 +579,99 @@ final class UserState implements PrintSpoolerCallbacks {
.sendToTarget();
}
public void requestPrinterUpdateLocked(PrinterId printerId) {
public void validatePrintersLocked(List<PrinterId> printerIds) {
if (mIsDestroyed) {
Log.w(LOG_TAG, "Not updating pritner - session destroyed");
Log.w(LOG_TAG, "Not validating pritners - session destroyed");
return;
}
RemotePrintService service = mActiveServices.get(printerId.getServiceName());
if (service != null) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = service;
args.arg2 = printerId;
mHandler.obtainMessage(SessionHandler
.MSG_REQUEST_PRINTER_UPDATE, args)
.sendToTarget();
List<PrinterId> remainingList = new ArrayList<PrinterId>(printerIds);
while (!remainingList.isEmpty()) {
Iterator<PrinterId> iterator = remainingList.iterator();
// Gather the printers per service and request a validation.
List<PrinterId> updateList = new ArrayList<PrinterId>();
ComponentName serviceName = null;
while (iterator.hasNext()) {
PrinterId printerId = iterator.next();
if (updateList.isEmpty()) {
updateList.add(printerId);
serviceName = printerId.getServiceName();
iterator.remove();
} else if (printerId.getServiceName().equals(serviceName)) {
updateList.add(printerId);
iterator.remove();
}
}
// Schedule a notification of the service.
RemotePrintService service = mActiveServices.get(serviceName);
if (service != null) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = service;
args.arg2 = updateList;
mHandler.obtainMessage(SessionHandler
.MSG_VALIDATE_PRINTERS, args)
.sendToTarget();
}
}
}
public final void startPrinterStateTrackingLocked(PrinterId printerId) {
if (mIsDestroyed) {
Log.w(LOG_TAG, "Not starting printer state tracking - session destroyed");
return;
}
// If printer discovery is not started - nothing to do.
if (mStartedPrinterDiscoveryTokens.isEmpty()) {
return;
}
final boolean containedPrinterId = mStateTrackedPrinters.contains(printerId);
// Keep track of the number of requests to track this one.
mStateTrackedPrinters.add(printerId);
// If we were tracking this printer - nothing to do.
if (containedPrinterId) {
return;
}
// No service - nothing to do.
RemotePrintService service = mActiveServices.get(printerId.getServiceName());
if (service == null) {
return;
}
// Ask the service to start tracking.
SomeArgs args = SomeArgs.obtain();
args.arg1 = service;
args.arg2 = printerId;
mHandler.obtainMessage(SessionHandler
.MSG_START_PRINTER_STATE_TRACKING, args)
.sendToTarget();
}
public final void stopPrinterStateTrackingLocked(PrinterId printerId) {
if (mIsDestroyed) {
Log.w(LOG_TAG, "Not stopping printer state tracking - session destroyed");
return;
}
// If printer discovery is not started - nothing to do.
if (mStartedPrinterDiscoveryTokens.isEmpty()) {
return;
}
// If we did not track this printer - nothing to do.
if (!mStateTrackedPrinters.remove(printerId)) {
return;
}
// No service - nothing to do.
RemotePrintService service = mActiveServices.get(printerId.getServiceName());
if (service == null) {
return;
}
// Ask the service to start tracking.
SomeArgs args = SomeArgs.obtain();
args.arg1 = service;
args.arg2 = printerId;
mHandler.obtainMessage(SessionHandler
.MSG_STOP_PRINTER_STATE_TRACKING, args)
.sendToTarget();
}
public void onDestroyed() {
/* do nothing */
}
@@ -533,6 +681,12 @@ final class UserState implements PrintSpoolerCallbacks {
Log.w(LOG_TAG, "Not destroying - session destroyed");
return;
}
// Make sure printer tracking is stopped.
final int printerCount = mStateTrackedPrinters.size();
for (int i = 0; i < printerCount; i++) {
PrinterId printerId = mStateTrackedPrinters.get(i);
stopPrinterStateTracking(printerId);
}
// Make sure discovery is stopped.
final int observerCount = mStartedPrinterDiscoveryTokens.size();
for (int i = 0; i < observerCount; i++) {
@@ -744,9 +898,19 @@ final class UserState implements PrintSpoolerCallbacks {
}
}
private void handleRequestPrinterUpdate(RemotePrintService service,
private void handleValidatePrinters(RemotePrintService service,
List<PrinterId> printerIds) {
service.validatePrinters(printerIds);
}
private void handleStartPrinterStateTracking(RemotePrintService service,
PrinterId printerId) {
service.requestPrinterUpdate(printerId);
service.startPrinterStateTracking(printerId);
}
private void handleStopPrinterStateTracking(RemotePrintService service,
PrinterId printerId) {
service.stopPrinterStateTracking(printerId);
}
private void handlePrintersAdded(IPrinterDiscoveryObserver observer,
@@ -804,7 +968,9 @@ final class UserState implements PrintSpoolerCallbacks {
public static final int MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION = 9;
public static final int MSG_DISPATCH_START_PRINTER_DISCOVERY = 10;
public static final int MSG_DISPATCH_STOP_PRINTER_DISCOVERY = 11;
public static final int MSG_REQUEST_PRINTER_UPDATE = 12;
public static final int MSG_VALIDATE_PRINTERS = 12;
public static final int MSG_START_PRINTER_STATE_TRACKING = 13;
public static final int MSG_STOP_PRINTER_STATE_TRACKING = 14;
SessionHandler(Looper looper) {
super(looper, null, false);
@@ -878,13 +1044,29 @@ final class UserState implements PrintSpoolerCallbacks {
handleDispatchStopPrinterDiscovery(services);
} break;
case MSG_REQUEST_PRINTER_UPDATE: {
case MSG_VALIDATE_PRINTERS: {
SomeArgs args = (SomeArgs) message.obj;
RemotePrintService service = (RemotePrintService) args.arg1;
List<PrinterId> printerIds = (List<PrinterId>) args.arg2;
args.recycle();
handleValidatePrinters(service, printerIds);
} break;
case MSG_START_PRINTER_STATE_TRACKING: {
SomeArgs args = (SomeArgs) message.obj;
RemotePrintService service = (RemotePrintService) args.arg1;
PrinterId printerId = (PrinterId) args.arg2;
args.recycle();
handleRequestPrinterUpdate(service, printerId);
handleStartPrinterStateTracking(service, printerId);
} break;
case MSG_STOP_PRINTER_STATE_TRACKING: {
SomeArgs args = (SomeArgs) message.obj;
RemotePrintService service = (RemotePrintService) args.arg1;
PrinterId printerId = (PrinterId) args.arg2;
args.recycle();
handleStopPrinterStateTracking(service, printerId);
}
}
}
}