From a00271533f639c8ed36429c663889ac9f654bc72 Mon Sep 17 00:00:00 2001
From: Svetoslav Ganov
- * Lifecycle
- *
- *
- *
- */ -public abstract class PrintAdapter { - - /** - * Called when printing started. You can use this callback to - * allocate resources. - *
- * Note: Invoked on the main thread. - *
- */ - public void onStart() { - /* do nothing - stub */ - } - - /** - * Called when the print job attributes (page size, density, etc) - * changed giving you a chance to re-layout the content such that - * it matches the new constraints. - *- * Note: Invoked on the main thread. - *
- * - * @param attributes The print job attributes. - * @return Whether the content changed based on the provided attributes. - */ - public boolean onPrintAttributesChanged(PrintAttributes attributes) { - return false; - } - - /** - * Called when specific pages of the content have to be printed in the from of - * a PDF file to the given file descriptor. You should not - * close the file descriptor instead you have to invoke {@link PrintResultCallback - * #onPrintFinished()} or {@link PrintResultCallback#onPrintFailed(CharSequence)}. - *- * Note: If the printed content is large, it is a good - * practice to schedule writing it on a dedicated thread and register a - * callback in the provided {@link CancellationSignal} upon invocation of - * which you should stop writing data. The cancellation callback will not - * be made on the main thread. - *
- *- * Note: Invoked on the main thread. - *
- * - * @param pages The pages whose content to print. - * @param destination The destination file descriptor to which to start writing. - * @param cancellationSignal Signal for observing cancel print requests. - * @param progressListener Callback to inform the system with the write progress. - * - * @see CancellationSignal - */ - public abstract void onPrint(List- * Note: Invoked on the main thread. - *
- */ - public void onFinish() { - /* do nothing - stub */ - } - - /** - * Gets a {@link PrinterInfo} object that contains metadata about the - * printed content. - *- * Note: Invoked on the main thread. - *
- * - * @return The info object for this {@link PrintAdapter}. - * - * @see PrintAdapterInfo - */ - public abstract PrintAdapterInfo getInfo(); - - /** - * Base class for implementing a listener for the print result - * of a {@link PrintAdapter}. - */ - public static abstract class PrintResultCallback { - - PrintResultCallback() { - /* do nothing - hide constructor */ - } - - /** - * Notifies that all the data was printed. - * - * @param pages The pages that were printed. - */ - public void onPrintFinished(List+ *
+ * After you are done laying out, you must invoke: {@link LayoutResultCallback
+ * #onLayoutFinished(PrintDocumentInfo, boolean)} with the last argument true
+ * or false depending on whether the layout changed the
+ * content or not, respectively; and {@link LayoutResultCallback#onLayoutFailed(
+ * CharSequence), if an error occurred.
+ *
+ * Note: If the content is large and a layout will be + * performed, it is a good practice to schedule the work on a dedicated + * thread and register an observer in the provided {@link + * CancellationSignal} upon invocation of which you should stop the + * layout. The cancellation callback will not be made on the main + * thread. + *
+ * + * @param oldAttributes The old print attributes. + * @param newAttributes The new print attributes. + * @param cancellationSignal Signal for observing cancel layout requests. + * @param callback Callback to inform the system for the layout result. + * + * @see LayoutResultCallback + * @see CancellationSignal + */ + public abstract void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes, + CancellationSignal cancellationSignal, LayoutResultCallback callback); + + /** + * Called when specific pages of the content should be written in the + * from of a PDF file to the given file descriptor. This method is invoked + * on the main thread. + *+ * After you are done writing, you should not close the + * file descriptor, rather you must invoke: {@link WriteResultCallback + * #onWriteFinished()}, if writing completed successfully; or {@link + * WriteResultCallback#onWriteFailed(CharSequence)}, if an error occurred. + *
+ *+ * Note: If the printed content is large, it is a good + * practice to schedule writing it on a dedicated thread and register an + * observer in the provided {@link CancellationSignal} upon invocation of + * which you should stop writing. The cancellation callback will not be + * made on the main thread. + *
+ * + * @param pages The pages whose content to print. + * @param destination The destination file descriptor to which to write. + * @param cancellationSignal Signal for observing cancel writing requests. + * @param callback Callback to inform the system for the write result. + * + * @see WriteResultCallback + * @see CancellationSignal + */ + public abstract void onWrite(List+ * Note: It is your responsibility to close the file descriptor. + *
+ * + * @return A file descriptor for reading the data ornull.
+ */
+ public FileDescriptor getData() {
+ ParcelFileDescriptor source = null;
+ ParcelFileDescriptor sink = null;
+ try {
+ ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
+ source = fds[0];
+ sink = fds[1];
+ mPrintServiceClient.writePrintJobData(sink, mPrintJobId);
+ return source.getFileDescriptor();
+ } catch (IOException ioe) {
+ Log.e(LOG_TAG, "Error calling getting print job data!", ioe);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error calling getting print job data!", re);
+ } finally {
+ if (sink != null) {
+ try {
+ sink.close();
+ } catch (IOException ioe) {
+ /* ignore */
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java
index f490f911936f..80530a7cb8d6 100644
--- a/core/java/android/printservice/PrintJob.java
+++ b/core/java/android/printservice/PrintJob.java
@@ -16,10 +16,6 @@
package android.printservice;
-import java.io.FileDescriptor;
-import java.io.IOException;
-
-import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.print.PrintJobInfo;
import android.util.Log;
@@ -33,19 +29,16 @@ public final class PrintJob {
private static final String LOG_TAG = "PrintJob";
- private final int mId;
-
private final IPrintServiceClient mPrintServiceClient;
+ private final PrintDocument mDocument;
+
private PrintJobInfo mCachedInfo;
- PrintJob(PrintJobInfo info, IPrintServiceClient client) {
- if (client == null) {
- throw new IllegalStateException("Print serivice not connected!");
- }
- mCachedInfo = info;
- mId = info.getId();
+ PrintJob(PrintJobInfo jobInfo, IPrintServiceClient client) {
+ mCachedInfo = jobInfo;
mPrintServiceClient = client;
+ mDocument = new PrintDocument(mCachedInfo.getId(), client, jobInfo.getDocumentInfo());
}
/**
@@ -54,7 +47,7 @@ public final class PrintJob {
* @return The id.
*/
public int getId() {
- return mId;
+ return mCachedInfo.getId();
}
/**
@@ -70,9 +63,9 @@ public final class PrintJob {
public PrintJobInfo getInfo() {
PrintJobInfo info = null;
try {
- info = mPrintServiceClient.getPrintJob(mId);
+ info = mPrintServiceClient.getPrintJobInfo(mCachedInfo.getId());
} catch (RemoteException re) {
- Log.e(LOG_TAG, "Couldn't get info for job: " + mId, re);
+ Log.e(LOG_TAG, "Couldn't get info for job: " + mCachedInfo.getId(), re);
}
if (info != null) {
mCachedInfo = info;
@@ -80,6 +73,15 @@ public final class PrintJob {
return mCachedInfo;
}
+ /**
+ * Gets the document of this print job.
+ *
+ * @return The document.
+ */
+ public PrintDocument getDocument() {
+ return mDocument;
+ }
+
/**
* Gets whether this print job is queued. Such a print job is
* ready to be printed and can be started.
@@ -103,7 +105,7 @@ public final class PrintJob {
* @see #fail(CharSequence)
*/
public boolean isStarted() {
- return getInfo().getState() == PrintJobInfo.STATE_STARTED;
+ return getInfo().getState() == PrintJobInfo.STATE_STARTED;
}
/**
@@ -181,48 +183,13 @@ public final class PrintJob {
*/
public boolean setTag(String tag) {
try {
- return mPrintServiceClient.setPrintJobTag(mId, tag);
+ return mPrintServiceClient.setPrintJobTag(mCachedInfo.getId(), tag);
} catch (RemoteException re) {
- Log.e(LOG_TAG, "Error setting tag for job:" + mId, re);
+ Log.e(LOG_TAG, "Error setting tag for job: " + mCachedInfo.getId(), re);
}
return false;
}
- /**
- * Gets the data associated with this print job. It is a responsibility of
- * the print service to open a stream to the returned file descriptor
- * and fully read the content.
- * - * Note: It is your responsibility to close the file descriptor. - *
- * - * @return A file descriptor for reading the data ornull.
- */
- public final FileDescriptor getData() {
- ParcelFileDescriptor source = null;
- ParcelFileDescriptor sink = null;
- try {
- ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
- source = fds[0];
- sink = fds[1];
- mPrintServiceClient.writePrintJobData(sink, mId);
- return source.getFileDescriptor();
- } catch (IOException ioe) {
- Log.e(LOG_TAG, "Error calling getting print job data!", ioe);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error calling getting print job data!", re);
- } finally {
- if (sink != null) {
- try {
- sink.close();
- } catch (IOException ioe) {
- /* ignore */
- }
- }
- }
- return null;
- }
-
@Override
public boolean equals(Object obj) {
if (this == obj) {
@@ -235,23 +202,25 @@ public final class PrintJob {
return false;
}
PrintJob other = (PrintJob) obj;
- return (mId == other.mId);
+ return (mCachedInfo.getId() == other.mCachedInfo.getId());
}
@Override
public int hashCode() {
- return mId;
+ return mCachedInfo.getId();
}
private boolean setState(int state) {
- // Best effort - update the state of the cached info since
- // we may not be able to re-fetch it later if the job gets
- // removed from the spooler.
- mCachedInfo.setState(state);
try {
- return mPrintServiceClient.setPrintJobState(mId, state);
+ if (mPrintServiceClient.setPrintJobState(mCachedInfo.getId(), state)) {
+ // Best effort - update the state of the cached info since
+ // 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);
+ return true;
+ }
} catch (RemoteException re) {
- Log.e(LOG_TAG, "Error setting the state of job:" + mId, re);
+ Log.e(LOG_TAG, "Error setting the state of job: " + mCachedInfo.getId(), re);
}
return false;
}
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
index 92569661138d..820c2d8ff4ef 100644
--- a/core/java/android/printservice/PrintService.java
+++ b/core/java/android/printservice/PrintService.java
@@ -25,6 +25,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.print.IPrinterDiscoveryObserver;
import android.print.PrintJobInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
@@ -47,7 +48,7 @@ import java.util.List;
* {@link #onStartPrinterDiscovery()} and ends with a call to
* {@link #onStopPrinterDiscovery()}. During a printer discovery
* period the print service reports newly discovered printers by
- * calling {@link #addDiscoveredPrinters(List)} and added printers
+ * calling {@link #addDiscoveredPrinters(List)} and reports added printers
* that disappeared by calling {@link #removeDiscoveredPrinters(List)}.
* Calls to {@link #addDiscoveredPrinters(List)} and
* {@link #removeDiscoveredPrinters(List)} before a call to
@@ -67,26 +68,30 @@ import java.util.List;
* a call to {@link #onPrintJobQueued(PrintJob)} is made and the print
* service may handle it immediately or schedule that for an appropriate
* time in the future. The list of all print jobs for this service
- * are be available by calling {@link #getPrintJobs()}. A queued print
- * job is one whose {@link PrintJob#isQueued()} return true.
+ * are be available by calling {@link #getPrintJobs()}.
*
* * A print service is responsible for setting the print job state as * appropriate while processing it. Initially, a print job is in a * {@link PrintJobInfo#STATE_QUEUED} state which means that the data to * be printed is spooled by the system and the print service can obtain - * that data by calling {@link PrintJob#getData()}. After the print - * service starts printing the data it should set the print job state - * to {@link PrintJobInfo#STATE_STARTED}. Upon successful completion, the - * print job state has to be set to {@link PrintJobInfo#STATE_COMPLETED}. - * In a case of a failure, the print job state should be set to - * {@link PrintJobInfo#STATE_FAILED}. If a print job is in a - * {@link PrintJobInfo#STATE_STARTED} state and the user requests to - * cancel it, the print service will receive a call to - * {@link #onRequestCancelPrintJob(PrintJob)} which requests from the - * service to do a best effort in canceling the job. In case the job - * is successfully canceled, its state has to be set to - * {@link PrintJobInfo#STATE_CANCELED}. + * that data by calling {@link PrintJob#getDocument()}. A queued print + * job's {@link PrintJob#isQueued()} method returns true. + *
+ *+ * After the print service starts printing the data it should set the + * print job state to {@link PrintJobInfo#STATE_STARTED} by calling + * {@link PrintJob#start()}. Upon successful completion, the print job + * state has to be set to {@link PrintJobInfo#STATE_COMPLETED} by calling + * {@link PrintJob#complete()}. In case of a failure, the print job + * state should be set to {@link PrintJobInfo#STATE_FAILED} by calling + * {@link PrintJob#fail(CharSequence)}. If a print job is in a + * {@link PrintJobInfo#STATE_STARTED} state, i.e. {@link PrintJob#isStarted()} + * return true, and the user requests to cancel it, the print service will + * receive a call to {@link #onRequestCancelPrintJob(PrintJob)} which + * requests from the service to do a best effort in canceling the job. In + * case the job is successfully canceled, its state has to be set to + * {@link PrintJobInfo#STATE_CANCELED}. by calling {@link PrintJob#cancel()}. *
*@@ -124,9 +129,9 @@ import java.util.List; *
* A print service can be configured by specifying an optional settings * activity which exposes service specific options, an optional add - * prints activity which is used for manual addition of printers, etc. - * It is a responsibility of the system to launch the settings and add - * printers activities when appropriate. + * prints activity which is used for manual addition of printers, vendor + * name ,etc. It is a responsibility of the system to launch the settings + * and add printers activities when appropriate. *
*
* A print service is configured by providing a
@@ -148,7 +153,7 @@ import java.util.List;
*/
public abstract class PrintService extends Service {
- private static final String LOG_TAG = PrintService.class.getSimpleName();
+ private static final String LOG_TAG = "PrintService";
/**
* The {@link Intent} action that must be declared as handled by a service
@@ -162,6 +167,7 @@ public abstract class PrintService extends Service {
* <{@link android.R.styleable#PrintService print-service}>
* tag. This is a a sample XML file configuring a print service:
*
<print-service
+ * android:vendor="SomeVendor"
* android:settingsActivity="foo.bar.MySettingsActivity"
* andorid:addPrintersActivity="foo.bar.MyAddPrintersActivity."
* . . .
@@ -175,7 +181,7 @@ public abstract class PrintService extends Service {
private IPrintServiceClient mClient;
- private boolean mDiscoveringPrinters;
+ private IPrinterDiscoveryObserver mDiscoveryObserver;
@Override
protected void attachBaseContext(Context base) {
@@ -230,29 +236,29 @@ public abstract class PrintService extends Service {
* printers have to be added. You can call this method as many times as
* necessary during the discovery period but should not pass in already
* added printers. If a printer is already added in the same printer
- * discovery period, it will be ignored.
+ * discovery period, it will be ignored. If you want to update an already
+ * added printer, you should removed it and then re-add it.
*
*
* @param printers A list with discovered printers.
*
- * @throws IllegalStateException If this service is not connected.
- *
* @see #removeDiscoveredPrinters(List)
* @see #onStartPrinterDiscovery()
* @see #onStopPrinterDiscovery()
+ *
+ * @throws IllegalStateException If this service is not connected.
*/
public final void addDiscoveredPrinters(List printers) {
+ final IPrinterDiscoveryObserver observer;
synchronized (mLock) {
- if (mClient == null) {
- throw new IllegalStateException("Print serivice not connected!");
- }
- if (mDiscoveringPrinters) {
- try {
- // Calling with a lock into the system is fine.
- mClient.addDiscoveredPrinters(printers);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error adding discovered printers!", re);
- }
+ throwIfNotConnectedLocked();
+ observer = mDiscoveryObserver;
+ }
+ if (observer != null) {
+ try {
+ observer.addDiscoveredPrinters(printers);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error adding discovered printers", re);
}
}
}
@@ -269,37 +275,38 @@ public abstract class PrintService extends Service {
* this method as many times as necessary during the discovery period
* but should not pass in already removed printer ids. If a printer with
* a given id is already removed in the same discovery period, it will
- * be ignored.
+ * be ignored. If you want to update an already added printer, you should
+ * removed it and then re-add it.
*
*
* @param printerIds A list with disappeared printer ids.
*
- * @throws IllegalStateException If this service is not connected.
- *
* @see #addDiscoveredPrinters(List)
* @see #onStartPrinterDiscovery()
* @see #onStopPrinterDiscovery()
+ *
+ * @throws IllegalStateException If this service is not connected.
*/
public final void removeDiscoveredPrinters(List printerIds) {
+ final IPrinterDiscoveryObserver observer;
synchronized (mLock) {
- if (mClient == null) {
- throw new IllegalStateException("Print serivice not connected!");
- }
- if (mDiscoveringPrinters) {
- try {
- // Calling with a lock into the system is fine.
- mClient.removeDiscoveredPrinters(printerIds);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error removing discovered printers!", re);
- }
+ throwIfNotConnectedLocked();
+ observer = mDiscoveryObserver;
+ }
+ if (observer != null) {
+ try {
+ observer.removeDiscoveredPrinters(printerIds);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error removing discovered printers", re);
}
}
}
/**
* Called when canceling of a print job is requested. The service
- * should do best effort to fulfill the request. After the print
- * job is canceled by calling {@link PrintJob#cancel()}.
+ * should do best effort to fulfill the request. After the cancellation
+ * is performed, the print job should be set to a cancelled state by
+ * calling {@link PrintJob#cancel()}.
*
* @param printJob The print job to be canceled.
*/
@@ -310,11 +317,12 @@ public abstract class PrintService extends Service {
* Called when there is a queued print job for one of the printers
* managed by this print service. A queued print job is ready for
* processing by a print service which can get the data to be printed
- * by calling {@link PrintJob#getData()}. This service may start
+ * by calling {@link PrintJob#getDocument()}. This service may start
* processing the passed in print job or schedule handling of queued
* print jobs at a convenient time. The service can get the print
* jobs by a call to {@link #getPrintJobs()} and examine their state
- * to find the ones with state {@link PrintJobInfo#STATE_QUEUED}.
+ * to find the ones with state {@link PrintJobInfo#STATE_QUEUED} by
+ * calling {@link PrintJob#isQueued()}.
*
* @param printJob The new queued print job.
*
@@ -330,28 +338,31 @@ public abstract class PrintService extends Service {
* @throws IllegalStateException If this service is not connected.
*/
public final List getPrintJobs() {
+ final IPrintServiceClient client;
synchronized (mLock) {
- if (mClient == null) {
- throw new IllegalStateException("Print serivice not connected!");
- }
- try {
- List printJobs = null;
- List printJobInfos = mClient.getPrintJobs();
- if (printJobInfos != null) {
- final int printJobInfoCount = printJobInfos.size();
- printJobs = new ArrayList(printJobInfoCount);
- for (int i = 0; i < printJobInfoCount; i++) {
- printJobs.add(new PrintJob(printJobInfos.get(i), mClient));
- }
- }
- if (printJobs != null) {
- return printJobs;
+ throwIfNotConnectedLocked();
+ client = mClient;
+ }
+ if (client == null) {
+ return Collections.emptyList();
+ }
+ try {
+ List printJobs = null;
+ List printJobInfos = client.getPrintJobInfos();
+ if (printJobInfos != null) {
+ final int printJobInfoCount = printJobInfos.size();
+ printJobs = new ArrayList(printJobInfoCount);
+ for (int i = 0; i < printJobInfoCount; i++) {
+ printJobs.add(new PrintJob(printJobInfos.get(i), client));
}
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error calling getPrintJobs()", re);
}
- return Collections.emptyList();
+ if (printJobs != null) {
+ return printJobs;
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error calling getPrintJobs()", re);
}
+ return Collections.emptyList();
}
/**
@@ -375,8 +386,9 @@ public abstract class PrintService extends Service {
}
@Override
- public void startPrinterDiscovery() {
- mHandler.sendEmptyMessage(MyHandler.MESSAGE_START_PRINTER_DISCOVERY);
+ public void startPrinterDiscovery(IPrinterDiscoveryObserver observer) {
+ mHandler.obtainMessage(MyHandler.MESSAGE_START_PRINTER_DISCOVERY,
+ observer).sendToTarget();
}
@Override
@@ -385,18 +397,25 @@ public abstract class PrintService extends Service {
}
@Override
- public void requestCancelPrintJob(PrintJobInfo printJob) {
- mHandler.obtainMessage(MyHandler.MESSAGE_CANCEL_PRINTJOB, printJob).sendToTarget();
+ public void requestCancelPrintJob(PrintJobInfo printJobInfo) {
+ mHandler.obtainMessage(MyHandler.MESSAGE_CANCEL_PRINTJOB,
+ printJobInfo).sendToTarget();
}
@Override
- public void onPrintJobQueued(PrintJobInfo printJob) {
+ public void onPrintJobQueued(PrintJobInfo printJobInfo) {
mHandler.obtainMessage(MyHandler.MESSAGE_ON_PRINTJOB_QUEUED,
- printJob).sendToTarget();
+ printJobInfo).sendToTarget();
}
};
}
+ private void throwIfNotConnectedLocked() {
+ if (mClient == null) {
+ throw new IllegalStateException("Print serivice not connected");
+ }
+ }
+
private final class MyHandler extends Handler {
public static final int MESSAGE_START_PRINTER_DISCOVERY = 1;
public static final int MESSAGE_STOP_PRINTER_DISCOVERY = 2;
@@ -414,26 +433,26 @@ public abstract class PrintService extends Service {
switch (action) {
case MESSAGE_START_PRINTER_DISCOVERY: {
synchronized (mLock) {
- mDiscoveringPrinters = true;
+ mDiscoveryObserver = (IPrinterDiscoveryObserver) message.obj;
}
onStartPrinterDiscovery();
} break;
case MESSAGE_STOP_PRINTER_DISCOVERY: {
synchronized (mLock) {
- mDiscoveringPrinters = false;
+ mDiscoveryObserver = null;
}
onStopPrinterDiscovery();
} break;
case MESSAGE_CANCEL_PRINTJOB: {
- PrintJobInfo printJob = (PrintJobInfo) message.obj;
- onRequestCancelPrintJob(new PrintJob(printJob, mClient));
+ PrintJobInfo printJobInfo = (PrintJobInfo) message.obj;
+ onRequestCancelPrintJob(new PrintJob(printJobInfo, mClient));
} break;
case MESSAGE_ON_PRINTJOB_QUEUED: {
- PrintJobInfo printJob = (PrintJobInfo) message.obj;
- onPrintJobQueued(new PrintJob(printJob, mClient));
+ PrintJobInfo printJobInfo = (PrintJobInfo) message.obj;
+ onPrintJobQueued(new PrintJob(printJobInfo, mClient));
} break;
case MESSAGE_SET_CLEINT: {
@@ -441,13 +460,12 @@ public abstract class PrintService extends Service {
synchronized (mLock) {
mClient = client;
if (client == null) {
- mDiscoveringPrinters = false;
+ mDiscoveryObserver = null;
}
}
if (client != null) {
onConnected();
} else {
- onStopPrinterDiscovery();
onDisconnected();
}
} break;
diff --git a/core/java/android/printservice/PrintServiceInfo.java b/core/java/android/printservice/PrintServiceInfo.java
index 0370a252531b..43dd1b67de66 100644
--- a/core/java/android/printservice/PrintServiceInfo.java
+++ b/core/java/android/printservice/PrintServiceInfo.java
@@ -48,8 +48,6 @@ import java.io.IOException;
*/
public final class PrintServiceInfo implements Parcelable {
- private static final boolean DEBUG = false;
-
private static final String LOG_TAG = PrintServiceInfo.class.getSimpleName();
private static final String TAG_PRINT_SERVICE = "print-service";
@@ -97,7 +95,6 @@ public final class PrintServiceInfo implements Parcelable {
* @param context Context for accessing resources.
* @throws XmlPullParserException If a XML parsing error occurs.
* @throws IOException If a I/O error occurs.
- * @hide
*/
public static PrintServiceInfo create(ResolveInfo resolveInfo, Context context) {
String settingsActivityName = null;
@@ -117,7 +114,7 @@ public final class PrintServiceInfo implements Parcelable {
String nodeName = parser.getName();
if (!TAG_PRINT_SERVICE.equals(nodeName)) {
throw new XmlPullParserException(
- "Meta-data does not start with" + TAG_PRINT_SERVICE + " tag");
+ "Meta-data does not start with " + TAG_PRINT_SERVICE + " tag");
}
Resources resources = packageManager.getResourcesForApplication(
@@ -213,7 +210,7 @@ public final class PrintServiceInfo implements Parcelable {
@Override
public int hashCode() {
- return 31 * 1 + ((mId == null) ? 0 : mId.hashCode());
+ return 31 + ((mId == null) ? 0 : mId.hashCode());
}
@Override
@@ -244,12 +241,8 @@ public final class PrintServiceInfo implements Parcelable {
builder.append("PrintServiceInfo{");
builder.append("id:").append(mId).append(", ");
builder.append("resolveInfo:").append(mResolveInfo).append(", ");
- if (DEBUG) {
- builder.append("settingsActivityName:").append(mSettingsActivityName);
- builder.append("addPrintersActivityName:").append(mAddPrintersActivityName);
- } else if (mSettingsActivityName != null || mAddPrintersActivityName != null) {
- builder.append("");
- }
+ builder.append("settingsActivityName:").append(mSettingsActivityName);
+ builder.append("addPrintersActivityName:").append(mAddPrintersActivityName);
builder.append("}");
return builder.toString();
}
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 37c5d48ee28c..900f28a0ddec 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4169,86 +4169,86 @@
-
+
ISO A0
-
+
ISO A1
-
+
ISO A2
-
+
ISO A3
-
+
ISO A4
-
+
ISO A5
-
+
ISO A6
-
+
ISO A7
-
+
ISO A8
-
+
ISO A9
-
+
ISO A10
-
+
ISO B0
-
+
ISO B1
-
+
ISO B2
-
+
ISO B3
-
+
ISO B4
-
+
ISO B5
-
+
ISO B6
-
+
ISO B7
-
+
ISO B8
-
+
ISO B9
-
+
ISO B10
-
+
ISO C0
-
+
ISO C1
-
+
ISO C2
-
+
ISO C3
-
+
ISO C4
-
+
ISO C5
-
+
ISO C6
-
+
ISO C7
-
+
ISO C8
-
+
ISO C9
-
+
ISO C10
-
+
Letter
-
+
Government Letter
-
+
Legal
-
+
Junior Legal
-
+
Ledger
-
+
Tabloid
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
index ae2fe5c43c52..7d07c055dbcb 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
@@ -24,20 +24,21 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
-import android.os.IBinder.DeathRecipient;
-import android.print.IPrintAdapter;
-import android.print.IPrintManager;
+import android.print.IPrintDocumentAdapter;
import android.print.IPrinterDiscoveryObserver;
import android.print.PageRange;
import android.print.PrintAttributes;
import android.print.PrintAttributes.MediaSize;
import android.print.PrintAttributes.Resolution;
import android.print.PrintAttributes.Tray;
+import android.print.PrintDocumentAdapter.LayoutResultCallback;
+import android.print.PrintDocumentAdapter.WriteResultCallback;
+import android.print.PrintDocumentInfo;
import android.print.PrintJobInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
@@ -47,6 +48,7 @@ import android.text.Spanned;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
+import android.view.Choreographer;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -58,7 +60,6 @@ import android.widget.EditText;
import android.widget.Spinner;
import java.io.File;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -78,9 +79,7 @@ public class PrintJobConfigActivity extends Activity {
private static final int MIN_COPIES = 1;
- private final List> mTaskQueue = new ArrayList>();
-
- private IPrintManager mPrintManager;
+ private final PrintSpooler mPrintSpooler = PrintSpooler.getInstance(this);
private IPrinterDiscoveryObserver mPrinterDiscoveryObserver;
@@ -89,9 +88,7 @@ public class PrintJobConfigActivity extends Activity {
private PrintAttributes mPrintAttributes;
- private final PrintSpooler mPrintSpooler = PrintSpooler.getInstance(this);
-
- private RemotePrintAdapter mRemotePrintAdapter;
+ private RemotePrintDocumentAdapter mRemotePrintAdapter;
// UI elements
@@ -124,11 +121,11 @@ public class PrintJobConfigActivity extends Activity {
private Spinner mOrientationSpinner;
public ArrayAdapter> mOrientationSpinnerAdapter;
- private boolean mPrintStarted;
-
private boolean mPrintConfirmed;
- private IBinder mPrinable;
+ private boolean mStarted;
+
+ private IBinder mIPrintDocumentAdapter;
// TODO: Implement store/restore state.
@@ -231,9 +228,6 @@ public class PrintJobConfigActivity extends Activity {
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN
| WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
- mPrintManager = (IPrintManager) IPrintManager.Stub.asInterface(
- ServiceManager.getService(PRINT_SERVICE));
-
Bundle extras = getIntent().getExtras();
mPrintJobId = extras.getInt(EXTRA_PRINT_JOB_ID, -1);
@@ -251,15 +245,16 @@ public class PrintJobConfigActivity extends Activity {
mPrintAttributes = new PrintAttributes.Builder().create();
}
- mPrinable = extras.getBinder(EXTRA_PRINTABLE);
- if (mPrinable == null) {
+ mIPrintDocumentAdapter = extras.getBinder(EXTRA_PRINTABLE);
+ if (mIPrintDocumentAdapter == null) {
throw new IllegalArgumentException("Printable cannot be null");
}
- mRemotePrintAdapter = new RemotePrintAdapter(IPrintAdapter.Stub.asInterface(mPrinable),
+ mRemotePrintAdapter = new RemotePrintDocumentAdapter(
+ IPrintDocumentAdapter.Stub.asInterface(mIPrintDocumentAdapter),
mPrintSpooler.generateFileForPrintJob(mPrintJobId));
try {
- mPrinable.linkToDeath(mDeathRecipient, 0);
+ mIPrintDocumentAdapter.linkToDeath(mDeathRecipient, 0);
} catch (RemoteException re) {
finish();
}
@@ -271,7 +266,7 @@ public class PrintJobConfigActivity extends Activity {
@Override
protected void onDestroy() {
- mPrinable.unlinkToDeath(mDeathRecipient, 0);
+ mIPrintDocumentAdapter.unlinkToDeath(mDeathRecipient, 0);
super.onDestroy();
}
@@ -367,7 +362,6 @@ public class PrintJobConfigActivity extends Activity {
mPrintAttributes.getMediaSize());
mMediaSizeSpinner.setOnItemSelectedListener(null);
mMediaSizeSpinner.setSelection(selectedMediaSizeIndex);
- mMediaSizeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
// Resolution.
mResolutionSpinnerAdapter.clear();
@@ -382,7 +376,19 @@ public class PrintJobConfigActivity extends Activity {
mPrintAttributes.getResolution());
mResolutionSpinner.setOnItemSelectedListener(null);
mResolutionSpinner.setSelection(selectedResolutionIndex);
- mResolutionSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+
+ // AdapterView has the weird behavior to notify the selection listener for a
+ // selection event that occurred *before* the listener was registered because
+ // it does the real selection change on the next layout pass. To avoid this
+ // behavior we re-attach the listener in the next traversal window - fun!
+ Choreographer.getInstance().postCallback(
+ Choreographer.CALLBACK_TRAVERSAL, new Runnable() {
+ @Override
+ public void run() {
+ mMediaSizeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+ mResolutionSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+ }
+ }, null);
// Input tray.
mInputTraySpinnerAdapter.clear();
@@ -482,22 +488,14 @@ public class PrintJobConfigActivity extends Activity {
@Override
protected void onResume() {
super.onResume();
- try {
- mPrintManager.startDiscoverPrinters(mPrinterDiscoveryObserver);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error starting printer discovery!", re);
- }
+ mPrintSpooler.startPrinterDiscovery(mPrinterDiscoveryObserver);
notifyPrintableStartIfNeeded();
}
@Override
protected void onPause() {
super.onPause();
- try {
- mPrintManager.stopDiscoverPrinters();
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error starting printer discovery!", re);
- }
+ mPrintSpooler.stopPrinterDiscovery();
notifyPrintableFinishIfNeeded();
}
@@ -518,119 +516,83 @@ public class PrintJobConfigActivity extends Activity {
private void notifyPrintableStartIfNeeded() {
if (mDestinationSpinner.getSelectedItemPosition() < 0
- || mPrintStarted) {
+ || mStarted) {
return;
}
- mPrintStarted = true;
- new QueuedAsyncTask(mTaskQueue) {
- @Override
- protected Void doInBackground(Void... params) {
- try {
- mRemotePrintAdapter.start();
- } catch (IOException ioe) {
- Log.e(LOG_TAG, "Error reading printed data!", ioe);
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- super.onPostExecute(result);
- updatePrintableContentIfNeeded();
- }
- }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+ mStarted = true;
+ mRemotePrintAdapter.start();
+ updatePrintableContentIfNeeded();
}
private void updatePrintableContentIfNeeded() {
- if (!mPrintStarted) {
+ if (!mStarted) {
return;
}
+ // TODO: Implement old attributes tracking
mPrintSpooler.setPrintJobAttributes(mPrintJobId, mPrintAttributes);
- // TODO: Implement page selector.
- final List pages = new ArrayList();
- pages.add(PageRange.ALL_PAGES);
-
- new QueuedAsyncTask(mTaskQueue) {
+ mRemotePrintAdapter.layout(new PrintAttributes.Builder().create(),
+ mPrintAttributes, new LayoutResultCallback() {
@Override
- protected File doInBackground(Void... params) {
- try {
- mRemotePrintAdapter.printAttributesChanged(mPrintAttributes);
- mRemotePrintAdapter.cancelPrint();
- mRemotePrintAdapter.print(pages);
- return mRemotePrintAdapter.getFile();
- } catch (IOException ioe) {
- Log.e(LOG_TAG, "Error reading printed data!", ioe);
- }
- return null;
+ public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
+ // TODO: Handle the case of unchanged content
+ mPrintSpooler.setPrintJobPrintDocumentInfo(mPrintJobId, info);
+
+ // TODO: Implement page selector.
+ final List pages = new ArrayList();
+ pages.add(PageRange.ALL_PAGES);
+
+ mRemotePrintAdapter.write(pages, new WriteResultCallback() {
+ @Override
+ public void onWriteFinished(List pages) {
+ updatePrintPreview(mRemotePrintAdapter.getFile());
+ }
+
+ @Override
+ public void onWriteFailed(CharSequence error) {
+ Log.e(LOG_TAG, "Error write layout: " + error);
+ finishActivity(Activity.RESULT_CANCELED);
+ }
+ });
}
@Override
- protected void onPostExecute(File file) {
- super.onPostExecute(file);
- updatePrintPreview(file);
+ public void onLayoutFailed(CharSequence error) {
+ Log.e(LOG_TAG, "Error during layout: " + error);
+ finishActivity(Activity.RESULT_CANCELED);
}
- }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+ });
}
private void notifyPrintableFinishIfNeeded() {
- if (!mPrintStarted) {
+ if (!mStarted) {
return;
}
- mPrintStarted = false;
- // Cancel all pending async tasks if the activity was canceled.
if (!mPrintConfirmed) {
- final int taskCount = mTaskQueue.size();
- for (int i = taskCount - 1; i >= 0; i--) {
- mTaskQueue.remove(i).cancel();
- }
+ mRemotePrintAdapter.cancel();
}
+ mRemotePrintAdapter.finish();
- new AsyncTask() {
- @Override
- protected Void doInBackground(Void... params) {
- // Notify the app that printing completed.
- try {
- mRemotePrintAdapter.finish();
- } catch (IOException ioe) {
- Log.e(LOG_TAG, "Error reading printed data!", ioe);
- }
-
- // If canceled, nothing to do.
- if (!mPrintConfirmed) {
- mPrintSpooler.setPrintJobState(mPrintJobId,
- PrintJobInfo.STATE_CANCELED);
- return null;
- }
-
- // No printer, nothing to do.
- final int selectedIndex = mDestinationSpinner.getSelectedItemPosition();
- if (selectedIndex < 0) {
- // Update the print job's status.
- mPrintSpooler.setPrintJobState(mPrintJobId,
- PrintJobInfo.STATE_CANCELED);
- return null;
- }
-
- // Update the print job's printer.
- SpinnerItem printerItem =
- mDestinationSpinnerAdapter.getItem(selectedIndex);
- PrinterId printerId = printerItem.value.getId();
- mPrintSpooler.setPrintJobPrinterId(mPrintJobId, printerId);
+ // If canceled or no printer, nothing to do.
+ final int selectedIndex = mDestinationSpinner.getSelectedItemPosition();
+ if (!mPrintConfirmed || selectedIndex < 0) {
+ // Update the print job's status.
+ mPrintSpooler.setPrintJobState(mPrintJobId,
+ PrintJobInfo.STATE_CANCELED);
+ return;
+ }
- // Update the print job's status.
- mPrintSpooler.setPrintJobState(mPrintJobId,
- PrintJobInfo.STATE_QUEUED);
- return null;
- }
+ // Update the print job's printer.
+ SpinnerItem printerItem =
+ mDestinationSpinnerAdapter.getItem(selectedIndex);
+ PrinterId printerId = printerItem.value.getId();
+ mPrintSpooler.setPrintJobPrinterId(mPrintJobId, printerId);
- // Important: If we are canceling, then we do not wait for the write
- // to complete since the result will be discarded anyway, we simply
- // execute the finish immediately which will interrupt the write.
- }.executeOnExecutor(mPrintConfirmed ? AsyncTask.SERIAL_EXECUTOR
- : AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
+ // Update the print job's status.
+ mPrintSpooler.setPrintJobState(mPrintJobId,
+ PrintJobInfo.STATE_QUEUED);
if (DEBUG) {
if (mPrintConfirmed) {
@@ -689,30 +651,7 @@ public class PrintJobConfigActivity extends Activity {
}
}
- private abstract class QueuedAsyncTask extends AsyncTask {
-
- private final List> mPendingOrRunningTasks;
-
- public QueuedAsyncTask(List> pendingOrRunningTasks) {
- mPendingOrRunningTasks = pendingOrRunningTasks;
- }
-
- @Override
- protected void onPreExecute() {
- mPendingOrRunningTasks.add(this);
- }
-
- @Override
- protected void onPostExecute(T result) {
- mPendingOrRunningTasks.remove(this);
- }
-
- public void cancel() {
- super.cancel(true);
- mPendingOrRunningTasks.remove(this);
- }
- }
-
+ // Caution: Use this only for debugging
private final class ViewSpooledFileAsyncTask extends AsyncTask {
private final File mFile;
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java
index 2b27b694f1ac..0546a4316619 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java
@@ -16,30 +16,18 @@
package com.android.printspooler;
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
import android.content.ComponentName;
import android.content.Context;
import android.os.AsyncTask;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.print.IPrintClient;
-import android.print.IPrintManager;
+import android.print.IPrintSpoolerClient;
+import android.print.IPrinterDiscoveryObserver;
import android.print.PrintAttributes;
import android.print.PrintJobInfo;
import android.print.PrintManager;
+import android.print.PrintDocumentInfo;
import android.print.PrinterId;
import android.util.AtomicFile;
import android.util.Log;
@@ -48,6 +36,22 @@ import android.util.Xml;
import com.android.internal.util.FastXmlSerializer;
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
public class PrintSpooler {
private static final String LOG_TAG = PrintSpooler.class.getSimpleName();
@@ -64,17 +68,17 @@ public class PrintSpooler {
private static final Object sLock = new Object();
- private final Object mLock = new Object();
-
private static PrintSpooler sInstance;
+ private final Object mLock = new Object();
+
private final List mPrintJobs = new ArrayList();
private final PersistenceManager mPersistanceManager;
private final Context mContext;
- private final IPrintManager mPrintManager;
+ public IPrintSpoolerClient mClient;
public static PrintSpooler getInstance(Context context) {
synchronized (sLock) {
@@ -89,8 +93,40 @@ public class PrintSpooler {
mContext = context;
mPersistanceManager = new PersistenceManager();
mPersistanceManager.readStateLocked();
- mPrintManager = IPrintManager.Stub.asInterface(
- ServiceManager.getService("print"));
+ }
+
+ public void setCleint(IPrintSpoolerClient client) {
+ synchronized (mLock) {
+ mClient = client;
+ }
+ }
+
+ public void startPrinterDiscovery(IPrinterDiscoveryObserver observer) {
+ IPrintSpoolerClient client = null;
+ synchronized (mLock) {
+ client = mClient;
+ }
+ if (client != null) {
+ try {
+ client.onStartPrinterDiscovery(observer);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error notifying start printer discovery.", re);
+ }
+ }
+ }
+
+ public void stopPrinterDiscovery() {
+ IPrintSpoolerClient client = null;
+ synchronized (mLock) {
+ client = mClient;
+ }
+ if (client != null) {
+ try {
+ client.onStopPrinterDiscovery();
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error notifying stop printer discovery.", re);
+ }
+ }
}
public List getPrintJobs(ComponentName componentName, int state, int appId) {
@@ -102,7 +138,7 @@ public class PrintSpooler {
PrinterId printerId = printJob.getPrinterId();
final boolean sameComponent = (componentName == null
|| (printerId != null
- && componentName.equals(printerId.getServiceComponentName())));
+ && componentName.equals(printerId.getService())));
final boolean sameAppId = appId == PrintManager.APP_ID_ANY
|| printJob.getAppId() == appId;
final boolean sameState = state == PrintJobInfo.STATE_ANY
@@ -137,15 +173,10 @@ public class PrintSpooler {
PrintJobInfo printJob = getPrintJob(printJobId, appId);
if (printJob != null) {
switch (printJob.getState()) {
- case PrintJobInfo.STATE_CREATED: {
- removePrintJobLocked(printJob);
- } return true;
+ case PrintJobInfo.STATE_CREATED:
case PrintJobInfo.STATE_QUEUED: {
- removePrintJobLocked(printJob);
+ setPrintJobState(printJobId, PrintJobInfo.STATE_CANCELED);
} return true;
- default: {
- return false;
- }
}
}
return false;
@@ -169,6 +200,72 @@ public class PrintSpooler {
}
}
+ public void notifyClientForActivteJobs() {
+ IPrintSpoolerClient client = null;
+ Map> activeJobsPerServiceMap =
+ new HashMap>();
+
+ synchronized(mLock) {
+ if (mClient == null) {
+ throw new IllegalStateException("Client cannot be null.");
+ }
+ client = mClient;
+
+ final int printJobCount = mPrintJobs.size();
+ for (int i = 0; i < printJobCount; i++) {
+ PrintJobInfo printJob = mPrintJobs.get(i);
+ switch (printJob.getState()) {
+ case PrintJobInfo.STATE_CREATED: {
+ /* skip - not ready to be handled by a service */
+ } break;
+
+ case PrintJobInfo.STATE_QUEUED:
+ case PrintJobInfo.STATE_STARTED: {
+ ComponentName service = printJob.getPrinterId().getService();
+ List jobsPerService = activeJobsPerServiceMap.get(service);
+ if (jobsPerService == null) {
+ jobsPerService = new ArrayList();
+ activeJobsPerServiceMap.put(service, jobsPerService);
+ }
+ jobsPerService.add(printJob);
+ } break;
+
+ default: {
+ ComponentName service = printJob.getPrinterId().getService();
+ if (!activeJobsPerServiceMap.containsKey(service)) {
+ activeJobsPerServiceMap.put(service, null);
+ }
+ }
+ }
+ }
+ }
+
+ boolean allPrintJobsHandled = true;
+
+ for (Map.Entry> entry
+ : activeJobsPerServiceMap.entrySet()) {
+ ComponentName service = entry.getKey();
+ List printJobs = entry.getValue();
+
+ if (printJobs != null) {
+ allPrintJobsHandled = false;
+ final int printJobCount = printJobs.size();
+ for (int i = 0; i < printJobCount; i++) {
+ PrintJobInfo printJob = printJobs.get(i);
+ if (printJob.getState() == PrintJobInfo.STATE_QUEUED) {
+ callOnPrintJobQueuedQuietly(client, printJob);
+ }
+ }
+ } else {
+ callOnAllPrintJobsForServiceHandledQuietly(client, service);
+ }
+ }
+
+ if (allPrintJobsHandled) {
+ callOnAllPrintJobsHandledQuietly(client);
+ }
+ }
+
private int generatePrintJobIdLocked() {
int printJobId = sPrintJobIdCounter++;
while (isDuplicatePrintJobId(printJobId)) {
@@ -213,24 +310,14 @@ public class PrintSpooler {
} catch (IOException ioe) {
Log.e(LOG_TAG, "Error writing print job data!", ioe);
} finally {
- closeIfNotNullNoException(in);
- closeIfNotNullNoException(out);
- closeIfNotNullNoException(fd);
+ IoUtils.closeQuietly(in);
+ IoUtils.closeQuietly(out);
+ IoUtils.closeQuietly(fd);
}
}
return false;
}
- private void closeIfNotNullNoException(Closeable closeable) {
- if (closeable != null) {
- try {
- closeable.close();
- } catch (IOException ioe) {
- /* ignore */;
- }
- }
- }
-
public File generateFileForPrintJob(int printJobId) {
return new File(mContext.getFilesDir(), "print_job_"
+ printJobId + "." + PRINT_FILE_EXTENSION);
@@ -254,9 +341,20 @@ public class PrintSpooler {
public boolean setPrintJobState(int printJobId, int state) {
boolean success = false;
+
+ boolean allPrintJobsHandled = false;
+ boolean allPrintJobsForServiceHandled = false;
+
+ IPrintSpoolerClient client = null;
PrintJobInfo queuedPrintJob = null;
+ PrintJobInfo removedPrintJob = null;
synchronized (mLock) {
+ if (mClient == null) {
+ throw new IllegalStateException("Client cannot be null.");
+ }
+ client = mClient;
+
PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
if (printJob != null && printJob.getState() < state) {
success = true;
@@ -265,8 +363,23 @@ public class PrintSpooler {
switch (state) {
case PrintJobInfo.STATE_COMPLETED:
case PrintJobInfo.STATE_CANCELED: {
+ removedPrintJob = printJob;
removePrintJobLocked(printJob);
+
+ // No printer means creation of a print job was cancelled,
+ // therefore the state of the spooler did not change and no
+ // notifications are needed. We also do not need to persist
+ // the state.
+ PrinterId printerId = printJob.getPrinterId();
+ if (printerId == null) {
+ return true;
+ }
+
+ allPrintJobsHandled = !hasActivePrintJobsLocked();
+ allPrintJobsForServiceHandled = !hasActivePrintJobsForServiceLocked(
+ printerId.getService());
} break;
+
case PrintJobInfo.STATE_QUEUED: {
queuedPrintJob = new PrintJobInfo(printJob);
} break;
@@ -279,17 +392,77 @@ public class PrintSpooler {
}
if (queuedPrintJob != null) {
- try {
- mPrintManager.onPrintJobQueued(queuedPrintJob.getPrinterId(),
- queuedPrintJob);
- } catch (RemoteException re) {
- /* ignore */
- }
+ callOnPrintJobQueuedQuietly(client, queuedPrintJob);
+ }
+
+ if (allPrintJobsForServiceHandled) {
+ callOnAllPrintJobsForServiceHandledQuietly(client,
+ removedPrintJob.getPrinterId().getService());
+ }
+
+ if (allPrintJobsHandled) {
+ callOnAllPrintJobsHandledQuietly(client);
}
return success;
}
+ private void callOnPrintJobQueuedQuietly(IPrintSpoolerClient client,
+ PrintJobInfo printJob) {
+ try {
+ client.onPrintJobQueued(printJob);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error notify for a queued print job.", re);
+ }
+ }
+
+ private void callOnAllPrintJobsForServiceHandledQuietly(IPrintSpoolerClient client,
+ ComponentName service) {
+ try {
+ client.onAllPrintJobsForServiceHandled(service);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error notify for all print jobs per service handled.", re);
+ }
+ }
+
+ private void callOnAllPrintJobsHandledQuietly(IPrintSpoolerClient client) {
+ try {
+ client.onAllPrintJobsHandled();
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error notify for all print job handled.", re);
+ }
+ }
+
+ private boolean hasActivePrintJobsLocked() {
+ final int printJobCount = mPrintJobs.size();
+ for (int i = 0; i < printJobCount; i++) {
+ PrintJobInfo printJob = mPrintJobs.get(i);
+ switch (printJob.getState()) {
+ case PrintJobInfo.STATE_QUEUED:
+ case PrintJobInfo.STATE_STARTED: {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean hasActivePrintJobsForServiceLocked(ComponentName service) {
+ final int printJobCount = mPrintJobs.size();
+ for (int i = 0; i < printJobCount; i++) {
+ PrintJobInfo printJob = mPrintJobs.get(i);
+ switch (printJob.getState()) {
+ case PrintJobInfo.STATE_QUEUED:
+ case PrintJobInfo.STATE_STARTED: {
+ if (printJob.getPrinterId().getService().equals(service)) {
+ return true;
+ }
+ } break;
+ }
+ }
+ return false;
+ }
+
public boolean setPrintJobTag(int printJobId, String tag) {
synchronized (mLock) {
PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
@@ -302,6 +475,18 @@ public class PrintSpooler {
return false;
}
+ public final boolean setPrintJobPrintDocumentInfo(int printJobId, PrintDocumentInfo info) {
+ synchronized (mLock) {
+ PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
+ if (printJob != null) {
+ printJob.setDocumentInfo(info);
+ mPersistanceManager.writeStateLocked();
+ return true;
+ }
+ }
+ return false;
+ }
+
public void setPrintJobAttributes(int printJobId, PrintAttributes attributes) {
synchronized (mLock) {
PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
index 57c45577f8b9..050332cc0dc0 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
@@ -29,10 +29,11 @@ import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
-import android.print.IPrintAdapter;
+import android.print.IPrintDocumentAdapter;
import android.print.IPrintClient;
-import android.print.IPrintSpoolerService;
-import android.print.IPrintSpoolerServiceCallbacks;
+import android.print.IPrintSpoolerClient;
+import android.print.IPrintSpooler;
+import android.print.IPrintSpoolerCallbacks;
import android.print.PrintAttributes;
import android.print.PrintJobInfo;
import android.util.Slog;
@@ -64,21 +65,21 @@ public final class PrintSpoolerService extends Service {
@Override
public IBinder onBind(Intent intent) {
- return new IPrintSpoolerService.Stub() {
+ return new IPrintSpooler.Stub() {
@Override
- public void getPrintJobs(IPrintSpoolerServiceCallbacks callback,
+ public void getPrintJobInfos(IPrintSpoolerCallbacks callback,
ComponentName componentName, int state, int appId, int sequence)
throws RemoteException {
List printJobs = null;
try {
printJobs = mSpooler.getPrintJobs(componentName, state, appId);
} finally {
- callback.onGetPrintJobsResult(printJobs, sequence);
+ callback.onGetPrintJobInfosResult(printJobs, sequence);
}
}
@Override
- public void getPrintJob(int printJobId, IPrintSpoolerServiceCallbacks callback,
+ public void getPrintJobInfo(int printJobId, IPrintSpoolerCallbacks callback,
int appId, int sequence) throws RemoteException {
PrintJobInfo printJob = null;
try {
@@ -89,7 +90,7 @@ public final class PrintSpoolerService extends Service {
}
@Override
- public void cancelPrintJob(int printJobId, IPrintSpoolerServiceCallbacks callback,
+ public void cancelPrintJob(int printJobId, IPrintSpoolerCallbacks callback,
int appId, int sequence) throws RemoteException {
boolean success = false;
try {
@@ -102,8 +103,8 @@ public final class PrintSpoolerService extends Service {
@SuppressWarnings("deprecation")
@Override
public void createPrintJob(String printJobName, IPrintClient client,
- IPrintAdapter printAdapter, PrintAttributes attributes,
- IPrintSpoolerServiceCallbacks callback, int appId, int sequence)
+ IPrintDocumentAdapter printAdapter, PrintAttributes attributes,
+ IPrintSpoolerCallbacks callback, int appId, int sequence)
throws RemoteException {
PrintJobInfo printJob = null;
try {
@@ -134,7 +135,7 @@ public final class PrintSpoolerService extends Service {
@Override
public void setPrintJobState(int printJobId, int state,
- IPrintSpoolerServiceCallbacks callback, int sequece)
+ IPrintSpoolerCallbacks callback, int sequece)
throws RemoteException {
boolean success = false;
try {
@@ -148,7 +149,7 @@ public final class PrintSpoolerService extends Service {
@Override
public void setPrintJobTag(int printJobId, String tag,
- IPrintSpoolerServiceCallbacks callback, int sequece)
+ IPrintSpoolerCallbacks callback, int sequece)
throws RemoteException {
boolean success = false;
try {
@@ -162,6 +163,16 @@ public final class PrintSpoolerService extends Service {
public void writePrintJobData(ParcelFileDescriptor fd, int printJobId) {
mSpooler.writePrintJobData(fd, printJobId);
}
+
+ @Override
+ public void setClient(IPrintSpoolerClient client) {
+ mSpooler.setCleint(client);
+ }
+
+ @Override
+ public void notifyClientForActivteJobs() {
+ mSpooler.notifyClientForActivteJobs();
+ }
};
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/RemotePrintAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/RemotePrintAdapter.java
deleted file mode 100644
index c81b00c6b167..000000000000
--- a/packages/PrintSpooler/src/com/android/printspooler/RemotePrintAdapter.java
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.printspooler;
-
-import android.os.ICancellationSignal;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.print.IPrintAdapter;
-import android.print.IPrintResultCallback;
-import android.print.PageRange;
-import android.print.PrintAdapterInfo;
-import android.print.PrintAttributes;
-import android.util.Log;
-
-import libcore.io.IoUtils;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.List;
-
-/**
- * This class represents a remote print adapter instance.
- */
-final class RemotePrintAdapter {
- private static final String LOG_TAG = "RemotePrintAdapter";
-
- private static final boolean DEBUG = true;
-
- private final Object mLock = new Object();
-
- private final IPrintAdapter mRemoteInterface;
-
- private final File mFile;
-
- private final IPrintResultCallback mIPrintProgressListener;
-
- private PrintAdapterInfo mInfo;
-
- private ICancellationSignal mCancellationSignal;
-
- private Thread mWriteThread;
-
- public RemotePrintAdapter(IPrintAdapter printAdatper, File file) {
- mRemoteInterface = printAdatper;
- mFile = file;
- mIPrintProgressListener = new IPrintResultCallback.Stub() {
- @Override
- public void onPrintStarted(PrintAdapterInfo info,
- ICancellationSignal cancellationSignal) {
- if (DEBUG) {
- Log.i(LOG_TAG, "IPrintProgressListener#onPrintStarted()");
- }
- synchronized (mLock) {
- mInfo = info;
- mCancellationSignal = cancellationSignal;
- }
- }
-
- @Override
- public void onPrintFinished(List pages) {
- if (DEBUG) {
- Log.i(LOG_TAG, "IPrintProgressListener#onPrintFinished(" + pages + ")");
- }
- synchronized (mLock) {
- if (isPrintingLocked()) {
- mWriteThread.interrupt();
- mCancellationSignal = null;
- }
- }
- }
-
- @Override
- public void onPrintFailed(CharSequence error) {
- if (DEBUG) {
- Log.i(LOG_TAG, "IPrintProgressListener#onPrintFailed(" + error + ")");
- }
- synchronized (mLock) {
- if (isPrintingLocked()) {
- mWriteThread.interrupt();
- mCancellationSignal = null;
- }
- }
- }
- };
- }
-
- public File getFile() {
- if (DEBUG) {
- Log.i(LOG_TAG, "getFile()");
- }
- return mFile;
- }
-
- public void start() throws IOException {
- if (DEBUG) {
- Log.i(LOG_TAG, "start()");
- }
- try {
- mRemoteInterface.start();
- } catch (RemoteException re) {
- throw new IOException("Error reading file", re);
- }
- }
-
- public void printAttributesChanged(PrintAttributes attributes) throws IOException {
- if (DEBUG) {
- Log.i(LOG_TAG, "printAttributesChanged(" + attributes +")");
- }
- try {
- mRemoteInterface.printAttributesChanged(attributes);
- } catch (RemoteException re) {
- throw new IOException("Error reading file", re);
- }
- }
-
- public void print(List pages) throws IOException {
- if (DEBUG) {
- Log.i(LOG_TAG, "print(" + pages +")");
- }
- InputStream in = null;
- OutputStream out = null;
- ParcelFileDescriptor source = null;
- ParcelFileDescriptor sink = null;
- synchronized (mLock) {
- mWriteThread = Thread.currentThread();
- }
- try {
- ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
- source = pipe[0];
- sink = pipe[1];
-
- in = new FileInputStream(source.getFileDescriptor());
- out = new FileOutputStream(mFile);
-
- // Async call to initiate the other process writing the data.
- mRemoteInterface.print(pages, sink, mIPrintProgressListener);
-
- // Close the source. It is now held by the client.
- sink.close();
- sink = null;
-
- final byte[] buffer = new byte[8192];
- while (true) {
- if (Thread.currentThread().isInterrupted()) {
- Thread.currentThread().interrupt();
- break;
- }
- final int readByteCount = in.read(buffer);
- if (readByteCount < 0) {
- break;
- }
- out.write(buffer, 0, readByteCount);
- }
- } catch (RemoteException re) {
- throw new IOException("Error reading file", re);
- } catch (IOException ioe) {
- throw new IOException("Error reading file", ioe);
- } finally {
- IoUtils.closeQuietly(in);
- IoUtils.closeQuietly(out);
- IoUtils.closeQuietly(sink);
- IoUtils.closeQuietly(source);
- }
- }
-
- public void cancelPrint() throws IOException {
- if (DEBUG) {
- Log.i(LOG_TAG, "cancelPrint()");
- }
- synchronized (mLock) {
- if (isPrintingLocked()) {
- try {
- mCancellationSignal.cancel();
- } catch (RemoteException re) {
- throw new IOException("Error cancelling print", re);
- }
- }
- }
- }
-
- public void finish() throws IOException {
- if (DEBUG) {
- Log.i(LOG_TAG, "finish()");
- }
- try {
- mRemoteInterface.finish();
- } catch (RemoteException re) {
- throw new IOException("Error reading file", re);
- }
- }
-
- public PrintAdapterInfo getInfo() {
- synchronized (mLock) {
- return mInfo;
- }
- }
-
- private boolean isPrintingLocked() {
- return mCancellationSignal != null;
- }
-}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java
new file mode 100644
index 000000000000..912dd95b1663
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java
@@ -0,0 +1,448 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.printspooler;
+
+import android.os.AsyncTask;
+import android.os.ICancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.print.ILayoutResultCallback;
+import android.print.IPrintDocumentAdapter;
+import android.print.IWriteResultCallback;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintDocumentAdapter.LayoutResultCallback;
+import android.print.PrintDocumentAdapter.WriteResultCallback;
+import android.print.PrintDocumentInfo;
+import android.util.Log;
+import android.util.Slog;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class represents a remote print document adapter instance.
+ */
+final class RemotePrintDocumentAdapter {
+ private static final String LOG_TAG = "RemotePrintDocumentAdapter";
+
+ private static final boolean DEBUG = true;
+
+ public static final int STATE_INITIALIZED = 0;
+ public static final int STATE_START_COMPLETED = 1;
+ public static final int STATE_LAYOUT_COMPLETED = 2;
+ public static final int STATE_WRITE_COMPLETED = 3;
+ public static final int STATE_FINISH_COMPLETED = 4;
+
+ private final Object mLock = new Object();
+
+ private final List mTaskQueue = new ArrayList();
+
+ private final IPrintDocumentAdapter mRemoteInterface;
+
+ private final File mFile;
+
+ private int mState = STATE_INITIALIZED;
+
+ public RemotePrintDocumentAdapter(IPrintDocumentAdapter printAdatper, File file) {
+ mRemoteInterface = printAdatper;
+ mFile = file;
+ }
+
+ public File getFile() {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "getFile()");
+ }
+ synchronized (mLock) {
+ if (mState < STATE_WRITE_COMPLETED) {
+ throw new IllegalStateException("Write not completed");
+ }
+ return mFile;
+ }
+ }
+
+ public void cancel() {
+ synchronized (mLock) {
+ final int taskCount = mTaskQueue.size();
+ for (int i = 0; i < taskCount; i++) {
+ mTaskQueue.remove(i).cancel();
+ }
+ }
+ }
+
+ public void start() {
+ QueuedAsyncTask task = new QueuedAsyncTask() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "start()");
+ }
+ synchronized (mLock) {
+ if (mState != STATE_INITIALIZED) {
+ throw new IllegalStateException("Invalid state: " + mState);
+ }
+ }
+ try {
+ mRemoteInterface.start();
+ synchronized (mLock) {
+ mState = STATE_START_COMPLETED;
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error reading file", re);
+ }
+ return null;
+ }
+ };
+ synchronized (mLock) {
+ mTaskQueue.add(task);
+ task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+ }
+ }
+
+ public void layout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
+ LayoutResultCallback callback) {
+ LayoutAsyncTask task = new LayoutAsyncTask(oldAttributes, newAttributes, callback);
+ synchronized (mLock) {
+ mTaskQueue.add(task);
+ task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+ }
+ }
+
+ public void write(List pages, WriteResultCallback callback) {
+ WriteAsyncTask task = new WriteAsyncTask(pages, callback);
+ mTaskQueue.add(task);
+ task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+ }
+
+ public void finish() {
+ QueuedAsyncTask task = new QueuedAsyncTask() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "finish");
+ }
+ synchronized (mLock) {
+ if (mState != STATE_LAYOUT_COMPLETED
+ && mState != STATE_WRITE_COMPLETED) {
+ throw new IllegalStateException("Invalid state: " + mState);
+ }
+ }
+ try {
+ mRemoteInterface.finish();
+ synchronized (mLock) {
+ mState = STATE_FINISH_COMPLETED;
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error reading file", re);
+ mState = STATE_INITIALIZED;
+ }
+ return null;
+ }
+ };
+ synchronized (mLock) {
+ mTaskQueue.add(task);
+ task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+ }
+ }
+
+ private abstract class QueuedAsyncTask extends AsyncTask {
+ public void cancel() {
+ super.cancel(true);
+ }
+ }
+
+ private final class LayoutAsyncTask extends QueuedAsyncTask {
+
+ private final PrintAttributes mOldAttributes;
+
+ private final PrintAttributes mNewAttributes;
+
+ private final LayoutResultCallback mCallback;
+
+ private final ILayoutResultCallback mILayoutResultCallback =
+ new ILayoutResultCallback.Stub() {
+ @Override
+ public void onLayoutStarted(ICancellationSignal cancellationSignal) {
+ synchronized (mLock) {
+ mCancellationSignal = cancellationSignal;
+ if (isCancelled()) {
+ cancelSignalQuietlyLocked();
+ }
+ }
+ }
+
+ @Override
+ public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
+ synchronized (mLock) {
+ mCancellationSignal = null;
+ mCompleted = true;
+ mLock.notifyAll();
+ }
+ mCallback.onLayoutFinished(info, changed);
+ }
+
+ @Override
+ public void onLayoutFailed(CharSequence error) {
+ synchronized (mLock) {
+ mCancellationSignal = null;
+ mCompleted = true;
+ mLock.notifyAll();
+ }
+ Slog.e(LOG_TAG, "Error laying out print document: " + error);
+ mCallback.onLayoutFailed(error);
+ }
+ };
+
+ private ICancellationSignal mCancellationSignal;
+
+ private boolean mCompleted;
+
+ public LayoutAsyncTask(PrintAttributes oldAttributes,
+ PrintAttributes newAttributes, LayoutResultCallback callback) {
+ mOldAttributes = oldAttributes;
+ mNewAttributes = newAttributes;
+ mCallback = callback;
+ }
+
+ @Override
+ public void cancel() {
+ synchronized (mLock) {
+ throwIfCancelledLocked();
+ cancelSignalQuietlyLocked();
+ }
+ super.cancel();
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ synchronized (mLock) {
+ if (mState != STATE_START_COMPLETED
+ && mState != STATE_LAYOUT_COMPLETED
+ && mState != STATE_WRITE_COMPLETED) {
+ throw new IllegalStateException("Invalid state: " + mState);
+ }
+ }
+ try {
+ mRemoteInterface.layout(mOldAttributes, mNewAttributes,
+ mILayoutResultCallback);
+ synchronized (mLock) {
+ while (true) {
+ if (isCancelled()) {
+ mState = STATE_INITIALIZED;
+ mTaskQueue.remove(this);
+ break;
+ }
+ if (mCompleted) {
+ mState = STATE_LAYOUT_COMPLETED;
+ mTaskQueue.remove(this);
+ break;
+ }
+ try {
+ mLock.wait();
+ } catch (InterruptedException ie) {
+ /* ignore */
+ }
+ }
+ }
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error calling layout", re);
+ mState = STATE_INITIALIZED;
+ }
+ return null;
+ }
+
+ private void cancelSignalQuietlyLocked() {
+ if (mCancellationSignal != null) {
+ try {
+ mCancellationSignal.cancel();
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error cancelling layout", re);
+ }
+ }
+ }
+
+ private void throwIfCancelledLocked() {
+ if (isCancelled()) {
+ throw new IllegalStateException("Already cancelled");
+ }
+ }
+ }
+
+ private final class WriteAsyncTask extends QueuedAsyncTask {
+
+ private final List mPages;
+
+ private final WriteResultCallback mCallback;
+
+ private final IWriteResultCallback mIWriteResultCallback =
+ new IWriteResultCallback.Stub() {
+ @Override
+ public void onWriteStarted(ICancellationSignal cancellationSignal) {
+ synchronized (mLock) {
+ mCancellationSignal = cancellationSignal;
+ if (isCancelled()) {
+ cancelSignalQuietlyLocked();
+ }
+ }
+ }
+
+ @Override
+ public void onWriteFinished(List pages) {
+ synchronized (mLock) {
+ mCancellationSignal = null;
+ mCompleted = true;
+ mLock.notifyAll();
+ }
+ mCallback.onWriteFinished(pages);
+ }
+
+ @Override
+ public void onWriteFailed(CharSequence error) {
+ synchronized (mLock) {
+ mCancellationSignal = null;
+ mCompleted = true;
+ mLock.notifyAll();
+ }
+ Slog.e(LOG_TAG, "Error writing print document: " + error);
+ mCallback.onWriteFailed(error);
+ }
+ };
+
+ private ICancellationSignal mCancellationSignal;
+
+ private boolean mCompleted;
+
+ private Thread mWriteThread;
+
+ public WriteAsyncTask(List pages, WriteResultCallback callback) {
+ mPages = pages;
+ mCallback = callback;
+ }
+
+ @Override
+ public void cancel() {
+ synchronized (mLock) {
+ throwIfCancelledLocked();
+ cancelSignalQuietlyLocked();
+ mWriteThread.interrupt();
+ }
+ super.cancel();
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "print()");
+ }
+ synchronized (mLock) {
+ if (mState != STATE_LAYOUT_COMPLETED) {
+ throw new IllegalStateException("Invalid state: " + mState);
+ }
+ }
+ InputStream in = null;
+ OutputStream out = null;
+ ParcelFileDescriptor source = null;
+ ParcelFileDescriptor sink = null;
+ synchronized (mLock) {
+ mWriteThread = Thread.currentThread();
+ }
+ try {
+ ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
+ source = pipe[0];
+ sink = pipe[1];
+
+ in = new FileInputStream(source.getFileDescriptor());
+ out = new FileOutputStream(mFile);
+
+ // Async call to initiate the other process writing the data.
+ mRemoteInterface.write(mPages, sink, mIWriteResultCallback);
+
+ // Close the source. It is now held by the client.
+ sink.close();
+ sink = null;
+
+ final byte[] buffer = new byte[8192];
+ while (true) {
+ if (Thread.currentThread().isInterrupted()) {
+ Thread.currentThread().interrupt();
+ break;
+ }
+ final int readByteCount = in.read(buffer);
+ if (readByteCount < 0) {
+ break;
+ }
+ out.write(buffer, 0, readByteCount);
+ }
+ synchronized (mLock) {
+ while (true) {
+ if (isCancelled()) {
+ mState = STATE_INITIALIZED;
+ mTaskQueue.remove(this);
+ break;
+ }
+ if (mCompleted) {
+ mState = STATE_WRITE_COMPLETED;
+ mTaskQueue.remove(this);
+ break;
+ }
+ try {
+ mLock.wait();
+ } catch (InterruptedException ie) {
+ /* ignore */
+ }
+ }
+ }
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error writing print document", re);
+ mState = STATE_INITIALIZED;
+ } catch (IOException ioe) {
+ Slog.e(LOG_TAG, "Error writing print document", ioe);
+ mState = STATE_INITIALIZED;
+ } finally {
+ IoUtils.closeQuietly(in);
+ IoUtils.closeQuietly(out);
+ IoUtils.closeQuietly(sink);
+ IoUtils.closeQuietly(source);
+ }
+ return null;
+ }
+
+ private void cancelSignalQuietlyLocked() {
+ if (mCancellationSignal != null) {
+ try {
+ mCancellationSignal.cancel();
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error cancelling layout", re);
+ }
+ }
+ }
+
+ private void throwIfCancelledLocked() {
+ if (isCancelled()) {
+ throw new IllegalStateException("Already cancelled");
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 5b76f3954d97..203cca692df0 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -70,7 +70,7 @@ class AppWidgetService extends IAppWidgetService.Stub
mAppWidgetServices.append(0, primary);
}
- public void systemReady(boolean safeMode) {
+ public void systemRunning(boolean safeMode) {
mSafeMode = safeMode;
mAppWidgetServices.get(0).systemReady(safeMode);
diff --git a/services/java/com/android/server/AssetAtlasService.java b/services/java/com/android/server/AssetAtlasService.java
index 33f082c00464..26b4652f4c7d 100644
--- a/services/java/com/android/server/AssetAtlasService.java
+++ b/services/java/com/android/server/AssetAtlasService.java
@@ -186,7 +186,7 @@ public class AssetAtlasService extends IAssetAtlas.Stub {
* Callback invoked by the server thread to indicate we can now run
* 3rd party code.
*/
- public void systemReady() {
+ public void systemRunning() {
}
/**
diff --git a/services/java/com/android/server/CommonTimeManagementService.java b/services/java/com/android/server/CommonTimeManagementService.java
index c316733fe058..aa2c8b88a77e 100644
--- a/services/java/com/android/server/CommonTimeManagementService.java
+++ b/services/java/com/android/server/CommonTimeManagementService.java
@@ -153,7 +153,7 @@ class CommonTimeManagementService extends Binder {
mContext = context;
}
- void systemReady() {
+ void systemRunning() {
if (ServiceManager.checkService(CommonTimeConfig.SERVICE_NAME) == null) {
Log.i(TAG, "No common time service detected on this platform. " +
"Common time services will be unavailable.");
diff --git a/services/java/com/android/server/CountryDetectorService.java b/services/java/com/android/server/CountryDetectorService.java
index 8407fa44648f..4956dd5bd80a 100644
--- a/services/java/com/android/server/CountryDetectorService.java
+++ b/services/java/com/android/server/CountryDetectorService.java
@@ -166,7 +166,7 @@ public class CountryDetectorService extends ICountryDetector.Stub implements Run
}
}
- void systemReady() {
+ void systemRunning() {
// Shall we wait for the initialization finish.
BackgroundThread.getHandler().post(this);
}
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 1c1b0020b462..bd23cbc671e4 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -823,7 +823,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
- public void systemReady(StatusBarManagerService statusBar) {
+ public void systemRunning(StatusBarManagerService statusBar) {
synchronized (mMethodMap) {
if (DEBUG) {
Slog.d(TAG, "--- systemReady");
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index c6c9845979ad..bde9e1c95150 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -198,7 +198,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// most startup is deferred until systemReady()
}
- public void systemReady() {
+ public void systemRunning() {
synchronized (mLock) {
if (D) Log.d(TAG, "systemReady()");
diff --git a/services/java/com/android/server/NetworkTimeUpdateService.java b/services/java/com/android/server/NetworkTimeUpdateService.java
index 02b42b81de67..cbddf6797797 100644
--- a/services/java/com/android/server/NetworkTimeUpdateService.java
+++ b/services/java/com/android/server/NetworkTimeUpdateService.java
@@ -108,7 +108,7 @@ public class NetworkTimeUpdateService {
}
/** Initialize the receivers and initiate the first NTP request */
- public void systemReady() {
+ public void systemRunning() {
registerForTelephonyIntents();
registerForAlarms();
registerForConnectivityIntents();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 888064ccc7ee..0bbdcfb230e3 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -351,6 +351,7 @@ class ServerThread {
LockSettingsService lockSettings = null;
DreamManagerService dreamy = null;
AssetAtlasService atlas = null;
+ PrintManagerService printManager = null;
// Bring up services needed for UI.
if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
@@ -793,8 +794,8 @@ class ServerThread {
try {
Slog.i(TAG, "Print Service");
- ServiceManager.addService(Context.PRINT_SERVICE,
- new PrintManagerService(context));
+ printManager = new PrintManagerService(context);
+ ServiceManager.addService(Context.PRINT_SERVICE, printManager);
} catch (Throwable e) {
reportWtf("starting Print Service", e);
}
@@ -909,6 +910,7 @@ class ServerThread {
final AssetAtlasService atlasF = atlas;
final InputManagerService inputManagerF = inputManager;
final TelephonyRegistry telephonyRegistryF = telephonyRegistry;
+ final PrintManagerService printManagerF = printManager;
// We now tell the activity manager it is okay to run third party
// code. It will call back into us once it has gotten to the state
@@ -988,66 +990,73 @@ class ServerThread {
// third party code...
try {
- if (appWidgetF != null) appWidgetF.systemReady(safeMode);
+ if (appWidgetF != null) appWidgetF.systemRunning(safeMode);
} catch (Throwable e) {
- reportWtf("making App Widget Service ready", e);
+ reportWtf("Notifying AppWidgetService running", e);
}
try {
- if (wallpaperF != null) wallpaperF.systemReady();
+ if (wallpaperF != null) wallpaperF.systemRunning();
} catch (Throwable e) {
- reportWtf("making Wallpaper Service ready", e);
+ reportWtf("Notifying WallpaperService running", e);
}
try {
- if (immF != null) immF.systemReady(statusBarF);
+ if (immF != null) immF.systemRunning(statusBarF);
} catch (Throwable e) {
- reportWtf("making Input Method Service ready", e);
+ reportWtf("Notifying InputMethodService running", e);
}
try {
- if (locationF != null) locationF.systemReady();
+ if (locationF != null) locationF.systemRunning();
} catch (Throwable e) {
- reportWtf("making Location Service ready", e);
+ reportWtf("Notifying Location Service running", e);
}
try {
- if (countryDetectorF != null) countryDetectorF.systemReady();
+ if (countryDetectorF != null) countryDetectorF.systemRunning();
} catch (Throwable e) {
- reportWtf("making Country Detector Service ready", e);
+ reportWtf("Notifying CountryDetectorService running", e);
}
try {
- if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemReady();
+ if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemRunning();
} catch (Throwable e) {
- reportWtf("making Network Time Service ready", e);
+ reportWtf("Notifying NetworkTimeService running", e);
}
try {
- if (commonTimeMgmtServiceF != null) commonTimeMgmtServiceF.systemReady();
+ if (commonTimeMgmtServiceF != null) commonTimeMgmtServiceF.systemRunning();
} catch (Throwable e) {
- reportWtf("making Common time management service ready", e);
+ reportWtf("Notifying CommonTimeManagementService running", e);
}
try {
- if (textServiceManagerServiceF != null) textServiceManagerServiceF.systemReady();
+ if (textServiceManagerServiceF != null)
+ textServiceManagerServiceF.systemRunning();
} catch (Throwable e) {
- reportWtf("making Text Services Manager Service ready", e);
+ reportWtf("Notifying TextServicesManagerService running", e);
}
try {
- if (dreamyF != null) dreamyF.systemReady();
+ if (dreamyF != null) dreamyF.systemRunning();
} catch (Throwable e) {
- reportWtf("making DreamManagerService ready", e);
+ reportWtf("Notifying DreamManagerService running", e);
}
try {
- if (atlasF != null) atlasF.systemReady();
+ if (atlasF != null) atlasF.systemRunning();
} catch (Throwable e) {
- reportWtf("making AssetAtlasService ready", e);
+ reportWtf("Notifying AssetAtlasService running", e);
}
try {
// TODO(BT) Pass parameter to input manager
- if (inputManagerF != null) inputManagerF.systemReady();
+ if (inputManagerF != null) inputManagerF.systemRunning();
} catch (Throwable e) {
- reportWtf("making InputManagerService ready", e);
+ reportWtf("Notifying InputManagerService running", e);
}
try {
- if (telephonyRegistryF != null) telephonyRegistryF.systemReady();
+ if (telephonyRegistryF != null) telephonyRegistryF.systemRunning();
} catch (Throwable e) {
- reportWtf("making TelephonyRegistry ready", e);
+ reportWtf("Notifying TelephonyRegistry running", e);
+ }
+
+ try {
+ if (printManagerF != null) printManagerF.systemRuning();
+ } catch (Throwable e) {
+ reportWtf("Notifying PrintManagerService running", e);
}
}
});
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index 17260d505fdb..699d79eb0953 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -178,7 +178,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
mConnectedApns = new ArrayList();
}
- public void systemReady() {
+ public void systemRunning() {
// Watch for interesting updates
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
diff --git a/services/java/com/android/server/TextServicesManagerService.java b/services/java/com/android/server/TextServicesManagerService.java
index 7dd9988b93af..6587c416230d 100644
--- a/services/java/com/android/server/TextServicesManagerService.java
+++ b/services/java/com/android/server/TextServicesManagerService.java
@@ -76,7 +76,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
new HashMap();
private final TextServicesSettings mSettings;
- public void systemReady() {
+ public void systemRunning() {
if (!mSystemReady) {
mSystemReady = true;
}
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index 6823f1363fc8..f300642da0ba 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -449,7 +449,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
}
}
- public void systemReady() {
+ public void systemRunning() {
if (DEBUG) Slog.v(TAG, "systemReady");
WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_OWNER);
switchWallpaper(wallpaper, null);
diff --git a/services/java/com/android/server/dreams/DreamManagerService.java b/services/java/com/android/server/dreams/DreamManagerService.java
index c9e0da5b8f54..21e54fec863b 100644
--- a/services/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/java/com/android/server/dreams/DreamManagerService.java
@@ -73,7 +73,7 @@ public final class DreamManagerService extends IDreamManager.Stub {
mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
}
- public void systemReady() {
+ public void systemRunning() {
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java
index 5e4907e6820d..7b4c0775e47e 100644
--- a/services/java/com/android/server/input/InputManagerService.java
+++ b/services/java/com/android/server/input/InputManagerService.java
@@ -283,7 +283,7 @@ public class InputManagerService extends IInputManager.Stub
}
// TODO(BT) Pass in paramter for bluetooth system
- public void systemReady() {
+ public void systemRunning() {
if (DEBUG) {
Slog.d(TAG, "System ready.");
}
diff --git a/services/java/com/android/server/print/PrintManagerService.java b/services/java/com/android/server/print/PrintManagerService.java
index 51739984c613..86e76852b4d5 100644
--- a/services/java/com/android/server/print/PrintManagerService.java
+++ b/services/java/com/android/server/print/PrintManagerService.java
@@ -22,118 +22,111 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.ServiceConnection;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
import android.os.Process;
-import android.os.RemoteException;
import android.os.UserHandle;
-import android.print.IPrintAdapter;
import android.print.IPrintClient;
+import android.print.IPrintDocumentAdapter;
import android.print.IPrintManager;
-import android.print.IPrinterDiscoveryObserver;
import android.print.PrintAttributes;
import android.print.PrintJobInfo;
-import android.print.PrintManager;
-import android.print.PrinterId;
-import android.print.PrinterInfo;
-import android.printservice.IPrintService;
-import android.printservice.IPrintServiceClient;
-import android.printservice.PrintServiceInfo;
import android.provider.Settings;
-import android.text.TextUtils;
-import android.text.TextUtils.SimpleStringSplitter;
-import android.util.Slog;
+import android.util.SparseArray;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.BackgroundThread;
-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.NoSuchElementException;
import java.util.Set;
public final class PrintManagerService extends IPrintManager.Stub {
- private static final String LOG_TAG = PrintManagerService.class.getSimpleName();
-
private static final char COMPONENT_NAME_SEPARATOR = ':';
private final Object mLock = new Object();
- private final SimpleStringSplitter mStringColonSplitter =
- new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
-
- private final Map mServices =
- new HashMap();
-
- private final List mInstalledServices = new ArrayList();
-
- private final Set mEnabledServiceNames = new HashSet();
-
private final Context mContext;
- private final RemoteSpooler mSpooler;
-
- private final int mMyUid;
+ private final SparseArray mUserStates = new SparseArray();
private int mCurrentUserId = UserHandle.USER_OWNER;
- private IPrinterDiscoveryObserver mPrinterDiscoveryObserver;
-
public PrintManagerService(Context context) {
mContext = context;
- mSpooler = new RemoteSpooler(context);
- mMyUid = android.os.Process.myUid();
registerContentObservers();
- registerBoradcastreceivers();
+ registerBoradcastReceivers();
+ }
+
+ public void systemRuning() {
+ BackgroundThread.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mLock) {
+ UserState userState = getCurrentUserStateLocked();
+ userState.updateIfNeededLocked();
+ userState.getSpoolerLocked().notifyClientForActivteJobs();
+ }
+ }
+ });
}
@Override
- public PrintJobInfo print(String printJobName, IPrintClient client, IPrintAdapter printAdapter,
- PrintAttributes attributes, int appId, int userId) {
- final int resolvedAppId = resolveCallingAppEnforcingPermissionsLocked(appId);
- final int resolvedUserId = resolveCallingUserEnforcingPermissionsIdLocked(userId);
+ public PrintJobInfo print(String printJobName, IPrintClient client,
+ IPrintDocumentAdapter documentAdapter, PrintAttributes attributes, int appId,
+ int userId) {
+ final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
+ final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+ final UserState userState;
+ final RemotePrintSpooler spooler;
+ synchronized (mLock) {
+ userState = getOrCreateUserStateLocked(resolvedUserId);
+ spooler = userState.getSpoolerLocked();
+ }
final long identity = Binder.clearCallingIdentity();
try {
- return mSpooler.createPrintJob(printJobName, client, printAdapter,
- attributes, resolvedAppId, resolvedUserId);
+ return spooler.createPrintJob(printJobName, client, documentAdapter,
+ attributes, resolvedAppId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
- public List getPrintJobs(int appId, int userId) {
- final int resolvedAppId = resolveCallingAppEnforcingPermissionsLocked(appId);
- final int resolvedUserId = resolveCallingUserEnforcingPermissionsIdLocked(userId);
- // TODO: Do we want to return jobs in STATE_CREATED? We should probably
- // have additional argument for the types to get
+ public List getPrintJobInfos(int appId, int userId) {
+ final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
+ final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+ final UserState userState;
+ final RemotePrintSpooler spooler;
+ synchronized (mLock) {
+ userState = getOrCreateUserStateLocked(resolvedUserId);
+ spooler = userState.getSpoolerLocked();
+ }
final long identity = Binder.clearCallingIdentity();
try {
- return mSpooler.getPrintJobs(null, PrintJobInfo.STATE_ANY,
- resolvedAppId, resolvedUserId);
+ return spooler.getPrintJobInfos(null, PrintJobInfo.STATE_ANY,
+ resolvedAppId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
- public PrintJobInfo getPrintJob(int printJobId, int appId, int userId) {
- final int resolvedAppId = resolveCallingAppEnforcingPermissionsLocked(appId);
- final int resolvedUserId = resolveCallingUserEnforcingPermissionsIdLocked(userId);
+ public PrintJobInfo getPrintJobInfo(int printJobId, int appId, int userId) {
+ final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
+ final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+ final UserState userState;
+ final RemotePrintSpooler spooler;
+ synchronized (mLock) {
+ userState = getOrCreateUserStateLocked(resolvedUserId);
+ spooler = userState.getSpoolerLocked();
+ }
final long identity = Binder.clearCallingIdentity();
try {
- return mSpooler.getPrintJobInfo(printJobId, resolvedAppId, resolvedUserId);
+ return spooler.getPrintJobInfo(printJobId, resolvedAppId);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -141,93 +134,32 @@ public final class PrintManagerService extends IPrintManager.Stub {
@Override
public void cancelPrintJob(int printJobId, int appId, int userId) {
- final int resolvedAppId = resolveCallingAppEnforcingPermissionsLocked(appId);
- final int resolvedUserId = resolveCallingUserEnforcingPermissionsIdLocked(userId);
+ final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
+ final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+ final UserState userState;
+ final RemotePrintSpooler spooler;
+ synchronized (mLock) {
+ userState = getOrCreateUserStateLocked(resolvedUserId);
+ spooler = userState.getSpoolerLocked();
+ }
final long identity = Binder.clearCallingIdentity();
try {
- if (mSpooler.cancelPrintJob(printJobId, resolvedAppId, resolvedUserId)) {
+ if (spooler.cancelPrintJob(printJobId, resolvedAppId)) {
return;
}
- PrintJobInfo printJob = getPrintJob(printJobId, resolvedAppId, resolvedUserId);
- if (printJob == null) {
+ PrintJobInfo printJobInfo = getPrintJobInfo(printJobId, resolvedAppId, resolvedUserId);
+ if (printJobInfo == null) {
return;
}
- ComponentName printServiceName = printJob.getPrinterId().getServiceComponentName();
- PrintServiceClient printService = null;
+ ComponentName printServiceName = printJobInfo.getPrinterId().getService();
+ RemotePrintService printService = null;
synchronized (mLock) {
- printService = mServices.get(printServiceName);
+ printService = userState.getActiveServices().get(printServiceName);
}
if (printService == null) {
return;
}
- printService.requestCancelPrintJob(printJob);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- // Called only from the spooler.
- @Override
- public void onPrintJobQueued(PrinterId printerId, PrintJobInfo printJob) {
- throwIfCallerNotSignedWithSystemKey();
- PrintServiceClient printService = null;
- synchronized (mLock) {
- ComponentName printServiceName = printerId.getServiceComponentName();
- printService = mServices.get(printServiceName);
- }
- if (printService != null) {
- final long identity = Binder.clearCallingIdentity();
- try {
- printService.notifyPrintJobQueued(printJob);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
- }
-
- // Called only from the spooler.
- @Override
- public void startDiscoverPrinters(IPrinterDiscoveryObserver observer) {
- throwIfCallerNotSignedWithSystemKey();
- List services = new ArrayList();
- synchronized (mLock) {
- mPrinterDiscoveryObserver = observer;
- services.addAll(mServices.values());
- }
- final int serviceCount = services.size();
- if (serviceCount <= 0) {
- return;
- }
- final long identity = Binder.clearCallingIdentity();
- try {
- for (int i = 0; i < serviceCount; i++) {
- PrintServiceClient service = services.get(i);
- service.startPrinterDiscovery();
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- // Called only from the spooler.
- @Override
- public void stopDiscoverPrinters() {
- throwIfCallerNotSignedWithSystemKey();
- List services = new ArrayList();
- synchronized (mLock) {
- mPrinterDiscoveryObserver = null;
- services.addAll(mServices.values());
- }
- final int serviceCount = services.size();
- if (serviceCount <= 0) {
- return;
- }
- final long identity = Binder.clearCallingIdentity();
- try {
- for (int i = 0; i < serviceCount; i++) {
- PrintServiceClient service = services.get(i);
- service.stopPrintersDiscovery();
- }
+ printService.onRequestCancelPrintJob(printJobInfo);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -237,14 +169,13 @@ public final class PrintManagerService extends IPrintManager.Stub {
final Uri enabledPrintServicesUri = Settings.Secure.getUriFor(
Settings.Secure.ENABLED_PRINT_SERVICES);
- ContentObserver observer = new ContentObserver(new Handler(mContext.getMainLooper())) {
+ ContentObserver observer = new ContentObserver(BackgroundThread.getHandler()) {
@Override
public void onChange(boolean selfChange, Uri uri) {
if (enabledPrintServicesUri.equals(uri)) {
synchronized (mLock) {
- if (readEnabledPrintServicesChangedLocked()) {
- onUserStateChangedLocked();
- }
+ UserState userState = getCurrentUserStateLocked();
+ userState.updateIfNeededLocked();
}
}
}
@@ -254,32 +185,37 @@ public final class PrintManagerService extends IPrintManager.Stub {
false, observer, UserHandle.USER_ALL);
}
- private void registerBoradcastreceivers() {
+ private void registerBoradcastReceivers() {
PackageMonitor monitor = new PackageMonitor() {
@Override
- public void onSomePackagesChanged() {
+ public boolean onPackageChanged(String packageName, int uid, String[] components) {
synchronized (mLock) {
- if (getChangingUserId() != mCurrentUserId) {
- return;
- }
- if (readConfigurationForUserStateLocked()) {
- onUserStateChangedLocked();
+ UserState userState = getOrCreateUserStateLocked(getChangingUserId());
+ Iterator iterator = userState.getEnabledServices().iterator();
+ while (iterator.hasNext()) {
+ ComponentName componentName = iterator.next();
+ if (packageName.equals(componentName.getPackageName())) {
+ userState.updateIfNeededLocked();
+ return true;
+ }
}
}
+ return false;
}
@Override
public void onPackageRemoved(String packageName, int uid) {
synchronized (mLock) {
- if (getChangingUserId() != mCurrentUserId) {
- return;
- }
- Iterator iterator = mEnabledServiceNames.iterator();
+ UserState userState = getOrCreateUserStateLocked(getChangingUserId());
+ Iterator iterator = userState.getEnabledServices().iterator();
while (iterator.hasNext()) {
ComponentName componentName = iterator.next();
if (packageName.equals(componentName.getPackageName())) {
iterator.remove();
- onEnabledServiceNamesChangedLocked();
+ persistComponentNamesToSettingLocked(
+ Settings.Secure.ENABLED_PRINT_SERVICES,
+ userState.getEnabledServices(), getChangingUserId());
+ userState.updateIfNeededLocked();
return;
}
}
@@ -290,10 +226,9 @@ public final class PrintManagerService extends IPrintManager.Stub {
public boolean onHandleForceStop(Intent intent, String[] stoppedPackages,
int uid, boolean doit) {
synchronized (mLock) {
- if (getChangingUserId() != mCurrentUserId) {
- return false;
- }
- Iterator iterator = mEnabledServiceNames.iterator();
+ UserState userState = getOrCreateUserStateLocked(getChangingUserId());
+ boolean stoppedSomePackages = false;
+ Iterator iterator = userState.getEnabledServices().iterator();
while (iterator.hasNext()) {
ComponentName componentName = iterator.next();
String componentPackage = componentName.getPackageName();
@@ -302,27 +237,35 @@ public final class PrintManagerService extends IPrintManager.Stub {
if (!doit) {
return true;
}
- iterator.remove();
- onEnabledServiceNamesChangedLocked();
+ stoppedSomePackages = true;
+ break;
}
}
}
+ if (stoppedSomePackages) {
+ userState.updateIfNeededLocked();
+ }
return false;
}
}
- private void onEnabledServiceNamesChangedLocked() {
- // Update the enabled services setting.
- persistComponentNamesToSettingLocked(
- Settings.Secure.ENABLED_PRINT_SERVICES,
- mEnabledServiceNames, mCurrentUserId);
- // Update the current user state.
- onUserStateChangedLocked();
+ private void persistComponentNamesToSettingLocked(String settingName,
+ Set componentNames, int userId) {
+ StringBuilder builder = new StringBuilder();
+ for (ComponentName componentName : componentNames) {
+ if (builder.length() > 0) {
+ builder.append(COMPONENT_NAME_SEPARATOR);
+ }
+ builder.append(componentName.flattenToShortString());
+ }
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ settingName, builder.toString(), userId);
}
};
// package changes
- monitor.register(mContext, null, UserHandle.ALL, true);
+ monitor.register(mContext, BackgroundThread.getHandler().getLooper(),
+ UserHandle.ALL, true);
// user changes
IntentFilter intentFilter = new IntentFilter();
@@ -334,179 +277,67 @@ public final class PrintManagerService extends IPrintManager.Stub {
String action = intent.getAction();
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+ } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
+ removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
}
}
- }, UserHandle.ALL, intentFilter, null, null);
- }
-
- private void throwIfCallerNotSignedWithSystemKey() {
- if (mContext.getPackageManager().checkSignatures(
- mMyUid, Binder.getCallingUid()) != PackageManager.SIGNATURE_MATCH) {
- throw new SecurityException("Caller must be signed with the system key!");
- }
- }
-
- private void onUserStateChangedLocked() {
- manageServicesLocked();
- }
-
- private void manageServicesLocked() {
- final int installedCount = mInstalledServices.size();
- for (int i = 0; i < installedCount; i++) {
- ResolveInfo resolveInfo = mInstalledServices.get(i).getResolveInfo();
- ComponentName serviceName = new ComponentName(resolveInfo.serviceInfo.packageName,
- resolveInfo.serviceInfo.name);
- if (mEnabledServiceNames.contains(serviceName)) {
- if (!mServices.containsKey(serviceName)) {
- new PrintServiceClient(serviceName, mCurrentUserId).ensureBoundLocked();
- }
- } else {
- PrintServiceClient service = mServices.get(serviceName);
- if (service != null) {
- service.ensureUnboundLocked();
- }
- }
- }
+ }, UserHandle.ALL, intentFilter, null, BackgroundThread.getHandler());
}
- private boolean readConfigurationForUserStateLocked() {
- boolean somethingChanged = false;
- somethingChanged |= readInstalledPrintServiceLocked();
- somethingChanged |= readEnabledPrintServicesChangedLocked();
- return somethingChanged;
- }
-
- private boolean readEnabledPrintServicesChangedLocked() {
- Set tempEnabledServiceNameSet = new HashSet();
- readComponentNamesFromSettingLocked(Settings.Secure.ENABLED_PRINT_SERVICES,
- mCurrentUserId, tempEnabledServiceNameSet);
- if (!tempEnabledServiceNameSet.equals(mEnabledServiceNames)) {
- mEnabledServiceNames.clear();
- mEnabledServiceNames.addAll(tempEnabledServiceNameSet);
- return true;
- }
- return false;
+ private UserState getCurrentUserStateLocked() {
+ return getOrCreateUserStateLocked(mCurrentUserId);
}
- private boolean readInstalledPrintServiceLocked() {
- Set tempPrintServices = new HashSet();
-
- List installedServices = mContext.getPackageManager()
- .queryIntentServicesAsUser(
- new Intent(android.printservice.PrintService.SERVICE_INTERFACE),
- PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
- mCurrentUserId);
-
- final int installedCount = installedServices.size();
- for (int i = 0, count = installedCount; i < count; i++) {
- ResolveInfo installedService = installedServices.get(i);
- if (!android.Manifest.permission.BIND_PRINT_SERVICE.equals(
- installedService.serviceInfo.permission)) {
- ComponentName serviceName = new ComponentName(
- installedService.serviceInfo.packageName,
- installedService.serviceInfo.name);
- Slog.w(LOG_TAG, "Skipping print service "
- + serviceName.flattenToShortString()
- + " since it does not require permission "
- + android.Manifest.permission.BIND_PRINT_SERVICE);
- continue;
- }
- tempPrintServices.add(PrintServiceInfo.create(installedService, mContext));
- }
-
- if (!tempPrintServices.equals(mInstalledServices)) {
- mInstalledServices.clear();
- mInstalledServices.addAll(tempPrintServices);
- return true;
+ private UserState getOrCreateUserStateLocked(int userId) {
+ UserState userState = mUserStates.get(userId);
+ if (userState == null) {
+ userState = new UserState(mContext, userId, mLock);
+ mUserStates.put(userId, userState);
}
- return false;
- }
-
- private void readComponentNamesFromSettingLocked(String settingName, int userId,
- Set outComponentNames) {
- String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
- settingName, userId);
- outComponentNames.clear();
- if (!TextUtils.isEmpty(settingValue)) {
- TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
- splitter.setString(settingValue);
- while (splitter.hasNext()) {
- String string = splitter.next();
- if (TextUtils.isEmpty(string)) {
- continue;
- }
- ComponentName componentName = ComponentName.unflattenFromString(string);
- if (componentName != null) {
- outComponentNames.add(componentName);
- }
- }
- }
- }
-
- private void persistComponentNamesToSettingLocked(String settingName,
- Set componentNames, int userId) {
- StringBuilder builder = new StringBuilder();
- for (ComponentName componentName : componentNames) {
- if (builder.length() > 0) {
- builder.append(COMPONENT_NAME_SEPARATOR);
- }
- builder.append(componentName.flattenToShortString());
- }
- Settings.Secure.putStringForUser(mContext.getContentResolver(),
- settingName, builder.toString(), userId);
+ return userState;
}
private void switchUser(int newUserId) {
synchronized (mLock) {
- // Disconnect services for the old user.
- mEnabledServiceNames.clear();
- onUserStateChangedLocked();
-
- // The user changed.
+ if (newUserId == mCurrentUserId) {
+ return;
+ }
mCurrentUserId = newUserId;
-
- // Update the user state based on current settings.
- readConfigurationForUserStateLocked();
- onUserStateChangedLocked();
- }
-
- // Unbind the spooler for the old user).
- mSpooler.unbind();
-
- // If we have queued jobs, advertise it, or we do
- // not need the spooler for now.
- if (notifyQueuedPrintJobs()) {
- mSpooler.unbind();
+ UserState userState = getCurrentUserStateLocked();
+ userState.updateIfNeededLocked();
+ userState.getSpoolerLocked().notifyClientForActivteJobs();
}
}
- private boolean notifyQueuedPrintJobs() {
- Map> notifications =
- new HashMap>();
+ private void removeUser(int removedUserId) {
synchronized (mLock) {
- for (PrintServiceClient service : mServices.values()) {
- List printJobs = mSpooler.getPrintJobs(
- service.mComponentName, PrintJobInfo.STATE_QUEUED,
- PrintManager.APP_ID_ANY, service.mUserId);
- notifications.put(service, printJobs);
+ UserState userState = mUserStates.get(removedUserId);
+ if (userState != null) {
+ userState.destroyLocked();
+ mUserStates.remove(removedUserId);
}
}
- if (notifications.isEmpty()) {
- return false;
+ }
+
+ private int resolveCallingAppEnforcingPermissions(int appId) {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid == 0 || callingUid == Process.SYSTEM_UID
+ || callingUid == Process.SHELL_UID) {
+ return appId;
}
- for (Map.Entry> notification
- : notifications.entrySet()) {
- PrintServiceClient service = notification.getKey();
- List printJobs = notification.getValue();
- final int printJobIdCount = printJobs.size();
- for (int i = 0; i < printJobIdCount; i++) {
- service.notifyPrintJobQueued(printJobs.get(i));
- }
+ final int callingAppId = UserHandle.getAppId(callingUid);
+ if (appId == callingAppId) {
+ return appId;
+ }
+ if (mContext.checkCallingPermission(Manifest.permission.ACCESS_ALL_PRINT_JOBS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Call from app " + callingAppId + " as app "
+ + appId + " without permission ACCESS_ALL_PRINT_JOBS");
}
- return true;
+ return appId;
}
- private int resolveCallingUserEnforcingPermissionsIdLocked(int userId) {
+ private int resolveCallingUserEnforcingPermissions(int userId) {
final int callingUid = Binder.getCallingUid();
if (callingUid == 0 || callingUid == Process.SYSTEM_UID
|| callingUid == Process.SHELL_UID) {
@@ -524,8 +355,8 @@ public final class PrintManagerService extends IPrintManager.Stub {
return callingUserId;
}
throw new SecurityException("Call from user " + callingUserId + " as user "
- + userId + " without permission INTERACT_ACROSS_USERS or "
- + "INTERACT_ACROSS_USERS_FULL not allowed.");
+ + userId + " without permission INTERACT_ACROSS_USERS or "
+ + "INTERACT_ACROSS_USERS_FULL not allowed.");
}
if (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF) {
return mCurrentUserId;
@@ -533,257 +364,4 @@ public final class PrintManagerService extends IPrintManager.Stub {
throw new IllegalArgumentException("Calling user can be changed to only "
+ "UserHandle.USER_CURRENT or UserHandle.USER_CURRENT_OR_SELF.");
}
-
- private int resolveCallingAppEnforcingPermissionsLocked(int appId) {
- final int callingUid = Binder.getCallingUid();
- if (callingUid == 0 || callingUid == Process.SYSTEM_UID
- || callingUid == Process.SHELL_UID) {
- return appId;
- }
- final int callingAppId = UserHandle.getAppId(callingUid);
- if (appId == callingAppId) {
- return appId;
- }
- if (mContext.checkCallingPermission(Manifest.permission.ACCESS_ALL_PRINT_JOBS)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Call from app " + callingAppId + " as app "
- + appId + " without permission INTERACT_ACROSS_APPS");
- }
- return appId;
- }
-
- private final class PrintServiceClient extends IPrintServiceClient.Stub
- implements ServiceConnection, DeathRecipient {
-
- private final ComponentName mComponentName;
-
- private final Intent mIntent;
-
- private final int mUserId;
-
- private IPrintService mInterface;
-
- private boolean mBinding;
-
- private boolean mWasConnectedAndDied;
-
- public PrintServiceClient(ComponentName componentName, int userId) {
- mComponentName = componentName;
- mIntent = new Intent().setComponent(mComponentName);
- mUserId = userId;
- }
-
- @Override
- public List getPrintJobs() {
- return mSpooler.getPrintJobs(mComponentName, PrintJobInfo.STATE_ANY,
- PrintManager.APP_ID_ANY, mUserId);
- }
-
- @Override
- public PrintJobInfo getPrintJob(int printJobId) {
- return mSpooler.getPrintJobInfo(printJobId,
- PrintManager.APP_ID_ANY, mUserId);
- }
-
- @Override
- public boolean setPrintJobState(int printJobId, int state) {
- return mSpooler.setPrintJobState(printJobId, state, mUserId);
- }
-
- @Override
- public boolean setPrintJobTag(int printJobId, String tag) {
- return mSpooler.setPrintJobTag(printJobId, tag, mUserId);
- }
-
- @Override
- public void writePrintJobData(ParcelFileDescriptor fd, int printJobId) {
- mSpooler.writePrintJobData(fd, printJobId, mUserId);
- }
-
- @Override
- public void addDiscoveredPrinters(List printers) {
- throwIfPrinterIdsForPrinterInfoTampered(printers);
- synchronized (mLock) {
- if (mPrinterDiscoveryObserver != null) {
- try {
- mPrinterDiscoveryObserver.addDiscoveredPrinters(printers);
- } catch (RemoteException re) {
- /* ignore */
- }
- }
- }
- }
-
- @Override
- public void removeDiscoveredPrinters(List printerIds) {
- throwIfPrinterIdsTampered(printerIds);
- synchronized (mLock) {
- if (mPrinterDiscoveryObserver != null) {
- try {
- mPrinterDiscoveryObserver.removeDiscoveredPrinters(printerIds);
- } catch (RemoteException re) {
- /* ignore */
- }
- }
- }
- }
-
- public void requestCancelPrintJob(PrintJobInfo printJob) {
- synchronized (mLock) {
- try {
- mInterface.requestCancelPrintJob(printJob);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Error canceling pring job!", re);
- }
- }
- }
-
- public void notifyPrintJobQueued(PrintJobInfo printJob) {
- IPrintService service = mInterface;
- if (service != null) {
- try {
- service.onPrintJobQueued(printJob);
- } catch (RemoteException re) {
- /* ignore */
- }
- }
- }
-
- public void startPrinterDiscovery() {
- IPrintService service = mInterface;
- if (service != null) {
- try {
- service.startPrinterDiscovery();
- } catch (RemoteException re) {
- /* ignore */
- }
- }
- }
-
- public void stopPrintersDiscovery() {
- IPrintService service = mInterface;
- if (service != null) {
- try {
- service.stopPrinterDiscovery();
- } catch (RemoteException re) {
- /* ignore */
- }
- }
- }
-
- public void ensureBoundLocked() {
- if (mBinding) {
- return;
- }
- if (mInterface == null) {
- mBinding = true;
- mContext.bindServiceAsUser(mIntent, this,
- Context.BIND_AUTO_CREATE, new UserHandle(mUserId));
- }
- }
-
- public void ensureUnboundLocked() {
- if (mBinding) {
- mBinding = false;
- return;
- }
- if (mInterface != null) {
- mContext.unbindService(this);
- destroyLocked();
- }
- }
-
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- synchronized (mLock) {
- mInterface = IPrintService.Stub.asInterface(service);
- mServices.put(mComponentName, this);
- try {
- mInterface.asBinder().linkToDeath(this, 0);
- } catch (RemoteException re) {
- destroyLocked();
- return;
- }
- if (mUserId != mCurrentUserId) {
- destroyLocked();
- return;
- }
- if (mBinding || mWasConnectedAndDied) {
- mBinding = false;
- mWasConnectedAndDied = false;
- onUserStateChangedLocked();
- try {
- mInterface.setClient(this);
- } catch (RemoteException re) {
- Slog.w(LOG_TAG, "Error while setting client for service: "
- + service, re);
- }
- } else {
- destroyLocked();
- }
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- /* do nothing - #binderDied takes care */
- }
-
- @Override
- public void binderDied() {
- synchronized (mLock) {
- if (isConnectedLocked()) {
- mWasConnectedAndDied = true;
- }
- destroyLocked();
- }
- }
-
- private void destroyLocked() {
- if (mServices.remove(mComponentName) == null) {
- return;
- }
- if (isConnectedLocked()) {
- try {
- mInterface.asBinder().unlinkToDeath(this, 0);
- } catch (NoSuchElementException nse) {
- /* ignore */
- }
- try {
- mInterface.setClient(null);
- } catch (RemoteException re) {
- /* ignore */
- }
- mInterface = null;
- }
- mBinding = false;
- }
-
- private boolean isConnectedLocked() {
- return (mInterface != null);
- }
-
- private void throwIfPrinterIdsForPrinterInfoTampered(List printerInfos) {
- final int printerInfoCount = printerInfos.size();
- for (int i = 0; i < printerInfoCount; i++) {
- PrinterId printerId = printerInfos.get(i).getId();
- throwIfPrinterIdTampered(printerId);
- }
- }
-
- private void throwIfPrinterIdsTampered(List printerIds) {
- final int printerIdCount = printerIds.size();
- for (int i = 0; i < printerIdCount; i++) {
- PrinterId printerId = printerIds.get(i);
- throwIfPrinterIdTampered(printerId);
- }
- }
-
- private void throwIfPrinterIdTampered(PrinterId printerId) {
- if (printerId == null || printerId.getServiceComponentName() == null
- || !printerId.getServiceComponentName().equals(mComponentName)) {
- throw new IllegalArgumentException("Invalid printer id: " + printerId);
- }
- }
- }
}
diff --git a/services/java/com/android/server/print/RemotePrintService.java b/services/java/com/android/server/print/RemotePrintService.java
new file mode 100644
index 000000000000..b9e0280bed82
--- /dev/null
+++ b/services/java/com/android/server/print/RemotePrintService.java
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.print;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.print.IPrinterDiscoveryObserver;
+import android.print.PrintJobInfo;
+import android.print.PrintManager;
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+import android.printservice.IPrintService;
+import android.printservice.IPrintServiceClient;
+import android.util.Slog;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class represents a remote print service. It abstracts away the binding
+ * and unbinding from the remote implementation. Clients can call methods of
+ * this class without worrying about when and how to bind/unbind.
+ */
+final class RemotePrintService {
+
+ private static final String LOG_TAG = "RemotePrintService";
+
+ private static final boolean DEBUG = true;
+
+ private final Context mContext;
+
+ private final ComponentName mComponentName;
+
+ private final Intent mIntent;
+
+ private final RemotePrintSpooler mSpooler;
+
+ private final int mUserId;
+
+ private final List mPendingCommands = new ArrayList();
+
+ private final ServiceConnection mServiceConnection = new RemoteServiceConneciton();
+
+ private final RemotePrintServiceClient mPrintServiceClient;
+
+ private final Handler mHandler;
+
+ private IPrintService mPrintService;
+
+ private boolean mBinding;
+
+ private boolean mDestroyed;
+
+ public RemotePrintService(Context context, ComponentName componentName, int userId,
+ RemotePrintSpooler spooler) {
+ mContext = context;
+ mComponentName = componentName;
+ mIntent = new Intent().setComponent(mComponentName);
+ mUserId = userId;
+ mSpooler = spooler;
+ mHandler = new MyHandler(context.getMainLooper());
+ mPrintServiceClient = new RemotePrintServiceClient(this);
+ }
+
+ public void destroy() {
+ mHandler.sendEmptyMessage(MyHandler.MSG_DESTROY);
+ }
+
+ private void handleDestroy() {
+ throwIfDestroyed();
+ ensureUnbound();
+ mDestroyed = true;
+ }
+
+ public void onAllPrintJobsHandled() {
+ mHandler.sendEmptyMessage(MyHandler.MSG_ALL_PRINT_JOBS_HANDLED);
+ }
+
+ private void handleOnAllPrintJobsHandled() {
+ throwIfDestroyed();
+ if (isBound()) {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserId + "] handleOnAllPrintJobsHandled");
+ }
+ // If bound and all the work is completed, then unbind.
+ ensureUnbound();
+ }
+ }
+
+ public void onRequestCancelPrintJob(PrintJobInfo printJob) {
+ mHandler.obtainMessage(MyHandler.MSG_REQUEST_CANCEL_PRINT_JOB,
+ printJob).sendToTarget();
+ }
+
+ private void handleOnRequestCancelPrintJob(final PrintJobInfo printJob) {
+ throwIfDestroyed();
+ // If we are not bound, then we have no print jobs to handle
+ // which means that there are no print jobs to be cancelled.
+ if (isBound()) {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserId + "] handleOnRequestCancelPrintJob()");
+ }
+ try {
+ mPrintService.requestCancelPrintJob(printJob);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error canceling pring job.", re);
+ }
+ }
+ }
+
+ public void onPrintJobQueued(PrintJobInfo printJob) {
+ mHandler.obtainMessage(MyHandler.MSG_PRINT_JOB_QUEUED,
+ printJob).sendToTarget();
+ }
+
+ private void handleOnPrintJobQueued(final PrintJobInfo printJob) {
+ throwIfDestroyed();
+ if (!isBound()) {
+ ensureBound();
+ mPendingCommands.add(new Runnable() {
+ @Override
+ public void run() {
+ handleOnPrintJobQueued(printJob);
+ }
+ });
+ } else {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserId + "] handleOnPrintJobQueued()");
+ }
+ try {
+ mPrintService.onPrintJobQueued(printJob);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error announcing queued pring job.", re);
+ }
+ }
+ }
+
+ public void onStartPrinterDiscovery(IPrinterDiscoveryObserver observer) {
+ mHandler.obtainMessage(MyHandler.MSG_START_PRINTER_DISCOVERY, observer).sendToTarget();
+ }
+
+ private void handleOnStartPrinterDiscovery(final IPrinterDiscoveryObserver observer) {
+ throwIfDestroyed();
+ if (!isBound()) {
+ ensureBound();
+ mPendingCommands.add(new Runnable() {
+ @Override
+ public void run() {
+ handleOnStartPrinterDiscovery(observer);
+ }
+ });
+ } else {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserId + "] onStartPrinterDiscovery()");
+ }
+ try {
+ mPrintService.startPrinterDiscovery(observer);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error announcing start printer dicovery.", re);
+ }
+ }
+ }
+
+ public void onStopPrinterDiscovery() {
+ mHandler.sendEmptyMessage(MyHandler.MSG_STOP_PRINTER_DISCOVERY);
+ }
+
+ private void handleStopPrinterDiscovery() {
+ throwIfDestroyed();
+ if (!isBound()) {
+ ensureBound();
+ mPendingCommands.add(new Runnable() {
+ @Override
+ public void run() {
+ handleStopPrinterDiscovery();
+ }
+ });
+ } else {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserId + "] onStopPrinterDiscovery()");
+ }
+ try {
+ mPrintService.stopPrinterDiscovery();
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error announcing stop printer dicovery.", re);
+ }
+ }
+ }
+
+ private boolean isBound() {
+ return mPrintService != null;
+ }
+
+ private void ensureBound() {
+ if (isBound() || mBinding) {
+ return;
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserId + "] ensureBound()");
+ }
+ mBinding = true;
+ mContext.bindServiceAsUser(mIntent, mServiceConnection,
+ Context.BIND_AUTO_CREATE, new UserHandle(mUserId));
+ }
+
+ private void ensureUnbound() {
+ if (!isBound() && !mBinding) {
+ return;
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserId + "] ensureUnbound()");
+ }
+ mBinding = false;
+ mPendingCommands.clear();
+ if (isBound()) {
+ try {
+ mPrintService.setClient(null);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ mPrintService = null;
+ mContext.unbindService(mServiceConnection);
+ }
+ }
+
+ private void throwIfDestroyed() {
+ if (mDestroyed) {
+ throw new IllegalStateException("Cannot interact with a destroyed service");
+ }
+ }
+
+ private class RemoteServiceConneciton implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (mDestroyed || !mBinding) {
+ return;
+ }
+ mBinding = false;
+ mPrintService = IPrintService.Stub.asInterface(service);
+ try {
+ mPrintService.setClient(mPrintServiceClient);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error setting client for: " + service, re);
+ handleDestroy();
+ return;
+ }
+ final int pendingCommandCount = mPendingCommands.size();
+ for (int i = 0; i < pendingCommandCount; i++) {
+ Runnable pendingCommand = mPendingCommands.get(i);
+ pendingCommand.run();
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mBinding = true;
+ }
+ }
+
+ private final class MyHandler extends Handler {
+ public static final int MSG_ALL_PRINT_JOBS_HANDLED = 1;
+ public static final int MSG_REQUEST_CANCEL_PRINT_JOB = 2;
+ public static final int MSG_PRINT_JOB_QUEUED = 3;
+ public static final int MSG_START_PRINTER_DISCOVERY = 4;
+ public static final int MSG_STOP_PRINTER_DISCOVERY = 5;
+ public static final int MSG_DESTROY = 6;
+
+ public MyHandler(Looper looper) {
+ super(looper, null, false);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MSG_ALL_PRINT_JOBS_HANDLED: {
+ handleOnAllPrintJobsHandled();
+ } break;
+
+ case MSG_REQUEST_CANCEL_PRINT_JOB: {
+ PrintJobInfo printJob = (PrintJobInfo) message.obj;
+ handleOnRequestCancelPrintJob(printJob);
+ } break;
+
+ case MSG_PRINT_JOB_QUEUED: {
+ PrintJobInfo printJob = (PrintJobInfo) message.obj;
+ handleOnPrintJobQueued(printJob);
+ } break;
+
+ case MSG_START_PRINTER_DISCOVERY: {
+ IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) message.obj;
+ handleOnStartPrinterDiscovery(new SecurePrinterDiscoveryObserver(
+ mComponentName, observer));
+ } break;
+
+ case MSG_STOP_PRINTER_DISCOVERY: {
+ handleStopPrinterDiscovery();
+ } break;
+
+ case MSG_DESTROY: {
+ handleDestroy();
+ } break;
+ }
+ }
+ }
+
+ private static final class RemotePrintServiceClient extends IPrintServiceClient.Stub {
+ private final WeakReference mWeakService;
+
+ public RemotePrintServiceClient(RemotePrintService service) {
+ mWeakService = new WeakReference(service);
+ }
+
+ @Override
+ public List getPrintJobInfos() {
+ RemotePrintService service = mWeakService.get();
+ if (service != null) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return service.mSpooler.getPrintJobInfos(service.mComponentName,
+ PrintJobInfo.STATE_ANY, PrintManager.APP_ID_ANY);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public PrintJobInfo getPrintJobInfo(int printJobId) {
+ RemotePrintService service = mWeakService.get();
+ if (service != null) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return service.mSpooler.getPrintJobInfo(printJobId,
+ PrintManager.APP_ID_ANY);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean setPrintJobState(int printJobId, int state) {
+ RemotePrintService service = mWeakService.get();
+ if (service != null) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return service.mSpooler.setPrintJobState(printJobId, state);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean setPrintJobTag(int printJobId, String tag) {
+ RemotePrintService service = mWeakService.get();
+ if (service != null) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return service.mSpooler.setPrintJobTag(printJobId, tag);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void writePrintJobData(ParcelFileDescriptor fd, int printJobId) {
+ RemotePrintService service = mWeakService.get();
+ if (service != null) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ service.mSpooler.writePrintJobData(fd, printJobId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+ }
+
+ private static final class SecurePrinterDiscoveryObserver
+ extends IPrinterDiscoveryObserver.Stub {
+ private final ComponentName mComponentName;
+
+ private final IPrinterDiscoveryObserver mDecoratedObsever;
+
+ public SecurePrinterDiscoveryObserver(ComponentName componentName,
+ IPrinterDiscoveryObserver observer) {
+ mComponentName = componentName;
+ mDecoratedObsever = observer;
+ }
+
+ @Override
+ public void addDiscoveredPrinters(List printers) {
+ throwIfPrinterIdsForPrinterInfoTampered(printers);
+ try {
+ mDecoratedObsever.addDiscoveredPrinters(printers);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error delegating to addDiscoveredPrinters", re);
+ }
+ }
+
+ @Override
+ public void removeDiscoveredPrinters(List printerIds) {
+ throwIfPrinterIdsTampered(printerIds);
+ try {
+ mDecoratedObsever.removeDiscoveredPrinters(printerIds);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error delegating to removeDiscoveredPrinters", re);
+ }
+ }
+
+ private void throwIfPrinterIdsForPrinterInfoTampered(
+ List printerInfos) {
+ final int printerInfoCount = printerInfos.size();
+ for (int i = 0; i < printerInfoCount; i++) {
+ PrinterId printerId = printerInfos.get(i).getId();
+ throwIfPrinterIdTampered(printerId);
+ }
+ }
+
+ private void throwIfPrinterIdsTampered(List printerIds) {
+ final int printerIdCount = printerIds.size();
+ for (int i = 0; i < printerIdCount; i++) {
+ PrinterId printerId = printerIds.get(i);
+ throwIfPrinterIdTampered(printerId);
+ }
+ }
+
+ private void throwIfPrinterIdTampered(PrinterId printerId) {
+ if (printerId == null || printerId.getService() == null
+ || !printerId.getService().equals(mComponentName)) {
+ throw new IllegalArgumentException("Invalid printer id: " + printerId);
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/print/RemotePrintSpooler.java b/services/java/com/android/server/print/RemotePrintSpooler.java
new file mode 100644
index 000000000000..bf2c8e73af6c
--- /dev/null
+++ b/services/java/com/android/server/print/RemotePrintSpooler.java
@@ -0,0 +1,613 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.print;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.print.IPrintClient;
+import android.print.IPrintDocumentAdapter;
+import android.print.IPrintSpooler;
+import android.print.IPrintSpoolerCallbacks;
+import android.print.IPrintSpoolerClient;
+import android.print.IPrinterDiscoveryObserver;
+import android.print.PrintAttributes;
+import android.print.PrintJobInfo;
+import android.util.Slog;
+import android.util.TimedRemoteCaller;
+
+import libcore.io.IoUtils;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * This represents the remote print spooler as a local object to the
+ * PrintManagerSerivce. It is responsible to connecting to the remote
+ * spooler if needed, to make the timed remote calls, to handle
+ * remote exceptions, and to bind/unbind to the remote instance as
+ * needed.
+ */
+final class RemotePrintSpooler {
+
+ private static final String LOG_TAG = "RemotePrintSpooler";
+
+ private static final boolean DEBUG = true;
+
+ private static final long BIND_SPOOLER_SERVICE_TIMEOUT = 10000;
+
+ private final Object mLock = new Object();
+
+ private final GetPrintJobInfosCaller mGetPrintJobInfosCaller = new GetPrintJobInfosCaller();
+
+ private final CreatePrintJobCaller mCreatePrintJobCaller = new CreatePrintJobCaller();
+
+ private final CancelPrintJobCaller mCancelPrintJobCaller = new CancelPrintJobCaller();
+
+ private final GetPrintJobInfoCaller mGetPrintJobInfoCaller = new GetPrintJobInfoCaller();
+
+ private final SetPrintJobStateCaller mSetPrintJobStatusCaller = new SetPrintJobStateCaller();
+
+ private final SetPrintJobTagCaller mSetPrintJobTagCaller = new SetPrintJobTagCaller();
+
+ private final ServiceConnection mServiceConnection = new MyServiceConnection();
+
+ private final Context mContext;
+
+ private final UserHandle mUserHandle;
+
+ private final PrintSpoolerClient mClient;
+
+ private final Intent mIntent;
+
+ private final PrintSpoolerCallbacks mCallbacks;
+
+ private IPrintSpooler mRemoteInstance;
+
+ private boolean mDestroyed;
+
+ public static interface PrintSpoolerCallbacks {
+ public void onPrintJobQueued(PrintJobInfo printJob);
+ public void onStartPrinterDiscovery(IPrinterDiscoveryObserver observer);
+ public void onStopPrinterDiscovery();
+ public void onAllPrintJobsForServiceHandled(ComponentName printService);
+ }
+
+ public RemotePrintSpooler(Context context, int userId,
+ PrintSpoolerCallbacks callbacks) {
+ mContext = context;
+ mUserHandle = new UserHandle(userId);
+ mCallbacks = callbacks;
+ mClient = new PrintSpoolerClient(this);
+ mIntent = new Intent();
+ mIntent.setComponent(new ComponentName("com.android.printspooler",
+ "com.android.printspooler.PrintSpoolerService"));
+ }
+
+ public final List getPrintJobInfos(ComponentName componentName, int state,
+ int appId) {
+ throwIfCalledOnMainThread();
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] getPrintJobInfos()");
+ }
+ try {
+ return mGetPrintJobInfosCaller.getPrintJobInfos(getRemoteInstanceLazy(),
+ componentName, state, appId);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error getting print jobs.", re);
+ } catch (TimeoutException te) {
+ Slog.e(LOG_TAG, "Error getting print jobs.", te);
+ }
+ return null;
+ }
+
+ public final PrintJobInfo createPrintJob(String printJobName, IPrintClient client,
+ IPrintDocumentAdapter documentAdapter, PrintAttributes attributes, int appId) {
+ throwIfCalledOnMainThread();
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] createPrintJob()");
+ }
+ try {
+ return mCreatePrintJobCaller.createPrintJob(getRemoteInstanceLazy(),
+ printJobName, client, documentAdapter, attributes, appId);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error creating print job.", re);
+ } catch (TimeoutException te) {
+ Slog.e(LOG_TAG, "Error creating print job.", te);
+ }
+ return null;
+ }
+
+ public final boolean cancelPrintJob(int printJobId, int appId) {
+ throwIfCalledOnMainThread();
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] cancelPrintJob()");
+ }
+ try {
+ return mCancelPrintJobCaller.cancelPrintJob(getRemoteInstanceLazy(),
+ printJobId, appId);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error canceling print job.", re);
+ } catch (TimeoutException te) {
+ Slog.e(LOG_TAG, "Error canceling print job.", te);
+ }
+ return false;
+ }
+
+ public final void writePrintJobData(ParcelFileDescriptor fd, int printJobId) {
+ throwIfCalledOnMainThread();
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] writePrintJobData()");
+ }
+ try {
+ getRemoteInstanceLazy().writePrintJobData(fd, printJobId);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error writing print job data.", re);
+ } catch (TimeoutException te) {
+ Slog.e(LOG_TAG, "Error writing print job data.", te);
+ } finally {
+ // We passed the file descriptor across and now the other
+ // side is responsible to close it, so close the local copy.
+ IoUtils.closeQuietly(fd);
+ }
+ }
+
+ public final PrintJobInfo getPrintJobInfo(int printJobId, int appId) {
+ throwIfCalledOnMainThread();
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] getPrintJobInfo()");
+ }
+ try {
+ return mGetPrintJobInfoCaller.getPrintJobInfo(getRemoteInstanceLazy(),
+ printJobId, appId);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error getting print job info.", re);
+ } catch (TimeoutException te) {
+ Slog.e(LOG_TAG, "Error getting print job info.", te);
+ }
+ return null;
+ }
+
+ public final boolean setPrintJobState(int printJobId, int state) {
+ throwIfCalledOnMainThread();
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setPrintJobState()");
+ }
+ try {
+ return mSetPrintJobStatusCaller.setPrintJobState(getRemoteInstanceLazy(),
+ printJobId, state);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error setting print job state.", re);
+ } catch (TimeoutException te) {
+ Slog.e(LOG_TAG, "Error setting print job state.", te);
+ }
+ return false;
+ }
+
+ public final boolean setPrintJobTag(int printJobId, String tag) {
+ throwIfCalledOnMainThread();
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setPrintJobTag()");
+ }
+ try {
+ return mSetPrintJobTagCaller.setPrintJobTag(getRemoteInstanceLazy(),
+ printJobId, tag);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error setting print job tag.", re);
+ } catch (TimeoutException te) {
+ Slog.e(LOG_TAG, "Error setting print job tag.", te);
+ }
+ return false;
+ }
+
+ public final void notifyClientForActivteJobs() {
+ throwIfCalledOnMainThread();
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier()
+ + "] notifyClientForActivteJobs()");
+ }
+ try {
+ getRemoteInstanceLazy().notifyClientForActivteJobs();
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error asking for active print job notification.", re);
+ } catch (TimeoutException te) {
+ Slog.e(LOG_TAG, "Error asking for active print job notification.", te);
+ }
+ }
+
+ public final void destroy() {
+ throwIfCalledOnMainThread();
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] destroy()");
+ }
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ unbindLocked();
+ mDestroyed = true;
+ }
+ }
+
+ private void onAllPrintJobsHandled() {
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ unbindLocked();
+ }
+ }
+
+ private IPrintSpooler getRemoteInstanceLazy() throws TimeoutException {
+ synchronized (mLock) {
+ if (mRemoteInstance != null) {
+ return mRemoteInstance;
+ }
+ bindLocked();
+ return mRemoteInstance;
+ }
+ }
+
+ private void bindLocked() throws TimeoutException {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] bindLocked()");
+ }
+
+ mContext.bindServiceAsUser(mIntent, mServiceConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_OOM_MANAGEMENT,
+ mUserHandle);
+
+ final long startMillis = SystemClock.uptimeMillis();
+ while (true) {
+ if (mRemoteInstance != null) {
+ break;
+ }
+ final long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
+ final long remainingMillis = BIND_SPOOLER_SERVICE_TIMEOUT - elapsedMillis;
+ if (remainingMillis <= 0) {
+ throw new TimeoutException("Cannot get spooler!");
+ }
+ try {
+ mLock.wait(remainingMillis);
+ } catch (InterruptedException ie) {
+ /* ignore */
+ }
+ }
+ }
+
+ private void unbindLocked() {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] unbindLocked()");
+ }
+ clearClientLocked();
+ mRemoteInstance = null;
+ mContext.unbindService(mServiceConnection);
+ }
+
+ private void setClientLocked() {
+ try {
+ mRemoteInstance.setClient(mClient);
+ } catch (RemoteException re) {
+ Slog.d(LOG_TAG, "Error setting print spooler client", re);
+ }
+ }
+
+ private void clearClientLocked() {
+ try {
+ mRemoteInstance.setClient(null);
+ } catch (RemoteException re) {
+ Slog.d(LOG_TAG, "Error clearing print spooler client", re);
+ }
+
+ }
+
+ private void throwIfDestroyedLocked() {
+ if (mDestroyed) {
+ throw new IllegalStateException("Cannot interact with a destroyed instance.");
+ }
+ }
+
+ private void throwIfCalledOnMainThread() {
+ if (Thread.currentThread() == mContext.getMainLooper().getThread()) {
+ throw new RuntimeException("Cannot invoke on the main thread");
+ }
+ }
+
+ private final class MyServiceConnection implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (mLock) {
+ mRemoteInstance = IPrintSpooler.Stub.asInterface(service);
+ setClientLocked();
+ mLock.notifyAll();
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ synchronized (mLock) {
+ clearClientLocked();
+ mRemoteInstance = null;
+ }
+ }
+ }
+
+ private static final class GetPrintJobInfosCaller
+ extends TimedRemoteCaller> {
+ private final IPrintSpoolerCallbacks mCallback;
+
+ public GetPrintJobInfosCaller() {
+ super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
+ mCallback = new BasePrintSpoolerServiceCallbacks() {
+ @Override
+ public void onGetPrintJobInfosResult(List printJobs, int sequence) {
+ onRemoteMethodResult(printJobs, sequence);
+ }
+ };
+ }
+
+ public List getPrintJobInfos(IPrintSpooler target,
+ ComponentName componentName, int state, int appId)
+ throws RemoteException, TimeoutException {
+ final int sequence = onBeforeRemoteCall();
+ target.getPrintJobInfos(mCallback, componentName, state, appId, sequence);
+ return getResultTimed(sequence);
+ }
+ }
+
+ private static final class CreatePrintJobCaller extends TimedRemoteCaller {
+ private final IPrintSpoolerCallbacks mCallback;
+
+ public CreatePrintJobCaller() {
+ super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
+ mCallback = new BasePrintSpoolerServiceCallbacks() {
+ @Override
+ public void onCreatePrintJobResult(PrintJobInfo printJob, int sequence) {
+ onRemoteMethodResult(printJob, sequence);
+ }
+ };
+ }
+
+ public PrintJobInfo createPrintJob(IPrintSpooler target, String printJobName,
+ IPrintClient client, IPrintDocumentAdapter documentAdapter,
+ PrintAttributes attributes, int appId) throws RemoteException, TimeoutException {
+ final int sequence = onBeforeRemoteCall();
+ target.createPrintJob(printJobName, client, documentAdapter, attributes,
+ mCallback, appId, sequence);
+ return getResultTimed(sequence);
+ }
+ }
+
+ private static final class CancelPrintJobCaller extends TimedRemoteCaller {
+ private final IPrintSpoolerCallbacks mCallback;
+
+ public CancelPrintJobCaller() {
+ super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
+ mCallback = new BasePrintSpoolerServiceCallbacks() {
+ @Override
+ public void onCancelPrintJobResult(boolean canceled, int sequence) {
+ onRemoteMethodResult(canceled, sequence);
+ }
+ };
+ }
+
+ public boolean cancelPrintJob(IPrintSpooler target, int printJobId,
+ int appId) throws RemoteException, TimeoutException {
+ final int sequence = onBeforeRemoteCall();
+ target.cancelPrintJob(printJobId, mCallback, appId, sequence);
+ return getResultTimed(sequence);
+ }
+ }
+
+ private static final class GetPrintJobInfoCaller extends TimedRemoteCaller {
+ private final IPrintSpoolerCallbacks mCallback;
+
+ public GetPrintJobInfoCaller() {
+ super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
+ mCallback = new BasePrintSpoolerServiceCallbacks() {
+ @Override
+ public void onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence) {
+ onRemoteMethodResult(printJob, sequence);
+ }
+ };
+ }
+
+ public PrintJobInfo getPrintJobInfo(IPrintSpooler target, int printJobId,
+ int appId) throws RemoteException, TimeoutException {
+ final int sequence = onBeforeRemoteCall();
+ target.getPrintJobInfo(printJobId, mCallback, appId, sequence);
+ return getResultTimed(sequence);
+ }
+ }
+
+ private static final class SetPrintJobStateCaller extends TimedRemoteCaller {
+ private final IPrintSpoolerCallbacks mCallback;
+
+ public SetPrintJobStateCaller() {
+ super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
+ mCallback = new BasePrintSpoolerServiceCallbacks() {
+ @Override
+ public void onSetPrintJobStateResult(boolean success, int sequence) {
+ onRemoteMethodResult(success, sequence);
+ }
+ };
+ }
+
+ public boolean setPrintJobState(IPrintSpooler target, int printJobId,
+ int status) throws RemoteException, TimeoutException {
+ final int sequence = onBeforeRemoteCall();
+ target.setPrintJobState(printJobId, status, mCallback, sequence);
+ return getResultTimed(sequence);
+ }
+ }
+
+ private static final class SetPrintJobTagCaller extends TimedRemoteCaller {
+ private final IPrintSpoolerCallbacks mCallback;
+
+ public SetPrintJobTagCaller() {
+ super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
+ mCallback = new BasePrintSpoolerServiceCallbacks() {
+ @Override
+ public void onSetPrintJobTagResult(boolean success, int sequence) {
+ onRemoteMethodResult(success, sequence);
+ }
+ };
+ }
+
+ public boolean setPrintJobTag(IPrintSpooler target, int printJobId,
+ String tag) throws RemoteException, TimeoutException {
+ final int sequence = onBeforeRemoteCall();
+ target.setPrintJobTag(printJobId, tag, mCallback, sequence);
+ return getResultTimed(sequence);
+ }
+ }
+
+ private static abstract class BasePrintSpoolerServiceCallbacks
+ extends IPrintSpoolerCallbacks.Stub {
+ @Override
+ public void onGetPrintJobInfosResult(List printJobIds, int sequence) {
+ /* do nothing */
+ }
+
+ @Override
+ public void onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence) {
+ /* do nothing */
+ }
+
+ @Override
+ public void onCreatePrintJobResult(PrintJobInfo printJob, int sequence) {
+ /* do nothing */
+ }
+
+ @Override
+ public void onCancelPrintJobResult(boolean canceled, int sequence) {
+ /* do nothing */
+ }
+
+ @Override
+ public void onSetPrintJobStateResult(boolean success, int sequece) {
+ /* do nothing */
+ }
+
+ @Override
+ public void onSetPrintJobTagResult(boolean success, int sequence) {
+ /* do nothing */
+ }
+ }
+
+ private static final class PrintSpoolerClient extends IPrintSpoolerClient.Stub {
+
+ private final WeakReference mWeakSpooler;
+
+ public PrintSpoolerClient(RemotePrintSpooler spooler) {
+ mWeakSpooler = new WeakReference(spooler);
+ }
+
+ @Override
+ public void onPrintJobQueued(PrintJobInfo printJob) {
+ RemotePrintSpooler spooler = mWeakSpooler.get();
+ if (spooler != null) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ spooler.mCallbacks.onPrintJobQueued(printJob);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
+ @Override
+ public void onAllPrintJobsForServiceHandled(ComponentName printService) {
+ RemotePrintSpooler spooler = mWeakSpooler.get();
+ if (spooler != null) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ spooler.mCallbacks.onAllPrintJobsForServiceHandled(printService);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
+ @Override
+ public void onAllPrintJobsHandled() {
+ RemotePrintSpooler spooler = mWeakSpooler.get();
+ if (spooler != null) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ spooler.onAllPrintJobsHandled();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
+ @Override
+ public void onStartPrinterDiscovery(IPrinterDiscoveryObserver observer) {
+ RemotePrintSpooler spooler = mWeakSpooler.get();
+ if (spooler != null) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ spooler.mCallbacks.onStartPrinterDiscovery(observer);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
+ @Override
+ public void onStopPrinterDiscovery() throws RemoteException {
+ RemotePrintSpooler spooler = mWeakSpooler.get();
+ if (spooler != null) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ spooler.mCallbacks.onStopPrinterDiscovery();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/print/RemoteSpooler.java b/services/java/com/android/server/print/RemoteSpooler.java
deleted file mode 100644
index fef581818183..000000000000
--- a/services/java/com/android/server/print/RemoteSpooler.java
+++ /dev/null
@@ -1,416 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.print;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.os.IBinder.DeathRecipient;
-import android.print.IPrintAdapter;
-import android.print.IPrintClient;
-import android.print.IPrintSpoolerService;
-import android.print.IPrintSpoolerServiceCallbacks;
-import android.print.PrintAttributes;
-import android.print.PrintJobInfo;
-import android.util.Slog;
-import android.util.TimedRemoteCaller;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This represents the remote print spooler as a local object to the
- * PrintManagerSerivce. It is responsible to connecting to the remove
- * spooler if needed, to make the timed out remote calls, and to handle
- * remove exceptions.
- */
-final class RemoteSpooler implements ServiceConnection, DeathRecipient {
-
- private static final String LOG_TAG = "Spooler";
-
- private static final long BIND_SPOOLER_SERVICE_TIMEOUT = 10000;
-
- private final Object mLock = new Object();
-
- private final Context mContext;
-
- private final Intent mIntent;
-
- private final GetPrintJobsCaller mGetPrintJobsCaller = new GetPrintJobsCaller();
-
- private final CreatePrintJobCaller mCreatePrintJobCaller = new CreatePrintJobCaller();
-
- private final CancelPrintJobCaller mCancelPrintJobCaller = new CancelPrintJobCaller();
-
- private final GetPrintJobCaller mGetPrintJobCaller = new GetPrintJobCaller();
-
- private final SetPrintJobStateCaller mSetPrintJobStatusCaller = new SetPrintJobStateCaller();
-
- private final SetPrintJobTagCaller mSetPrintJobTagCaller = new SetPrintJobTagCaller();
-
- private IPrintSpoolerService mRemoteInterface;
-
- private int mUserId = UserHandle.USER_NULL;
-
- public RemoteSpooler(Context context) {
- mContext = context;
- mIntent = new Intent();
- mIntent.setComponent(new ComponentName("com.android.printspooler",
- "com.android.printspooler.PrintSpoolerService"));
- }
-
- public List getPrintJobs(ComponentName componentName, int state, int appId,
- int userId) {
- try {
- return mGetPrintJobsCaller.getPrintJobs(getRemoteInstance(userId),
- componentName, state, appId);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Error getting print jobs!", re);
- } catch (TimeoutException te) {
- Slog.e(LOG_TAG, "Error getting print jobs!", te);
- }
- return null;
- }
-
- public PrintJobInfo createPrintJob(String printJobName, IPrintClient client,
- IPrintAdapter printAdapter, PrintAttributes attributes, int appId, int userId) {
- try {
- return mCreatePrintJobCaller.createPrintJob(getRemoteInstance(userId),
- printJobName, client, printAdapter, attributes, appId);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Error creating print job!", re);
- } catch (TimeoutException te) {
- Slog.e(LOG_TAG, "Error creating print job!", te);
- }
- return null;
- }
-
- public boolean cancelPrintJob(int printJobId, int appId, int userId) {
- try {
- return mCancelPrintJobCaller.cancelPrintJob(getRemoteInstance(userId),
- printJobId, appId);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Error canceling print job!", re);
- } catch (TimeoutException te) {
- Slog.e(LOG_TAG, "Error canceling print job!", te);
- }
- return false;
- }
-
- public void writePrintJobData(ParcelFileDescriptor fd, int printJobId, int userId) {
- try {
- getRemoteInstance(userId).writePrintJobData(fd, printJobId);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Error writing print job data!", re);
- } catch (TimeoutException te) {
- Slog.e(LOG_TAG, "Error writing print job data!", te);
- } finally {
- // We passed the file descriptor across and now the other
- // side is responsible to close it, so close the local copy.
- try {
- fd.close();
- } catch (IOException ioe) {
- /* ignore */
- }
- }
- }
-
- public PrintJobInfo getPrintJobInfo(int printJobId, int appId, int userId) {
- try {
- return mGetPrintJobCaller.getPrintJobInfo(getRemoteInstance(userId),
- printJobId, appId);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Error getting print job!", re);
- } catch (TimeoutException te) {
- Slog.e(LOG_TAG, "Error getting print job!", te);
- }
- return null;
- }
-
- public boolean setPrintJobState(int printJobId, int state, int userId) {
- try {
- return mSetPrintJobStatusCaller.setPrintJobState(getRemoteInstance(userId),
- printJobId, state);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Error setting print job status!", re);
- } catch (TimeoutException te) {
- Slog.e(LOG_TAG, "Error setting print job status!", te);
- }
- return false;
- }
-
- public boolean setPrintJobTag(int printJobId, String tag, int userId) {
- try {
- return mSetPrintJobTagCaller.setPrintJobTag(getRemoteInstance(userId),
- printJobId, tag);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Error setting print job tag!", re);
- } catch (TimeoutException te) {
- Slog.e(LOG_TAG, "Error setting print job tag!", te);
- }
- return false;
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- binderDied();
- }
-
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- synchronized (mLock) {
- try {
- service.linkToDeath(this, 0);
- mRemoteInterface = IPrintSpoolerService.Stub.asInterface(service);
- } catch (RemoteException re) {
- /* ignore */
- }
- }
- }
-
- private IPrintSpoolerService getRemoteInstance(int userId) throws TimeoutException {
- synchronized (mLock) {
- if (mRemoteInterface != null && mUserId == userId) {
- return mRemoteInterface;
- }
-
- final long identity = Binder.clearCallingIdentity();
- try {
- if (mUserId != UserHandle.USER_NULL && mUserId != userId) {
- unbind();
- }
-
- mContext.bindServiceAsUser(mIntent, this,
- Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_OOM_MANAGEMENT,
- UserHandle.CURRENT);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
-
- final long startMillis = SystemClock.uptimeMillis();
- while (true) {
- if (mRemoteInterface != null) {
- break;
- }
- final long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
- final long remainingMillis = BIND_SPOOLER_SERVICE_TIMEOUT - elapsedMillis;
- if (remainingMillis <= 0) {
- throw new TimeoutException("Cannot get spooler!");
- }
- try {
- mLock.wait(remainingMillis);
- } catch (InterruptedException ie) {
- /* ignore */
- }
- }
-
- mUserId = userId;
-
- return mRemoteInterface;
- }
- }
-
- public void unbind() {
- synchronized (mLock) {
- if (mRemoteInterface != null) {
- mContext.unbindService(this);
- mRemoteInterface = null;
- mUserId = UserHandle.USER_NULL;
- }
- }
- }
-
- @Override
- public void binderDied() {
- synchronized (mLock) {
- if (mRemoteInterface != null) {
- mRemoteInterface.asBinder().unlinkToDeath(this, 0);
- mRemoteInterface = null;
- }
- }
- }
-
- private final class GetPrintJobsCaller extends TimedRemoteCaller> {
- private final IPrintSpoolerServiceCallbacks mCallback;
-
- public GetPrintJobsCaller() {
- super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
- mCallback = new BasePrintSpoolerServiceCallbacks() {
- @Override
- public void onGetPrintJobsResult(List printJobs, int sequence) {
- onRemoteMethodResult(printJobs, sequence);
- }
- };
- }
-
- public List getPrintJobs(IPrintSpoolerService target,
- ComponentName componentName, int state, int appId)
- throws RemoteException, TimeoutException {
- final int sequence = onBeforeRemoteCall();
- target.getPrintJobs(mCallback, componentName, state, appId, sequence);
- return getResultTimed(sequence);
- }
- }
-
- private final class CreatePrintJobCaller extends TimedRemoteCaller {
- private final IPrintSpoolerServiceCallbacks mCallback;
-
- public CreatePrintJobCaller() {
- super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
- mCallback = new BasePrintSpoolerServiceCallbacks() {
- @Override
- public void onCreatePrintJobResult(PrintJobInfo printJob, int sequence) {
- onRemoteMethodResult(printJob, sequence);
- }
- };
- }
-
- public PrintJobInfo createPrintJob(IPrintSpoolerService target, String printJobName,
- IPrintClient client, IPrintAdapter printAdapter, PrintAttributes attributes,
- int appId) throws RemoteException, TimeoutException {
- final int sequence = onBeforeRemoteCall();
- target.createPrintJob(printJobName, client, printAdapter, attributes,
- mCallback, appId, sequence);
- return getResultTimed(sequence);
- }
- }
-
- private final class CancelPrintJobCaller extends TimedRemoteCaller {
- private final IPrintSpoolerServiceCallbacks mCallback;
-
- public CancelPrintJobCaller() {
- super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
- mCallback = new BasePrintSpoolerServiceCallbacks() {
- @Override
- public void onCancelPrintJobResult(boolean canceled, int sequence) {
- onRemoteMethodResult(canceled, sequence);
- }
- };
- }
-
- public boolean cancelPrintJob(IPrintSpoolerService target, int printJobId,
- int appId) throws RemoteException, TimeoutException {
- final int sequence = onBeforeRemoteCall();
- target.cancelPrintJob(printJobId, mCallback, appId, sequence);
- return getResultTimed(sequence);
- }
- }
-
- private final class GetPrintJobCaller extends TimedRemoteCaller {
- private final IPrintSpoolerServiceCallbacks mCallback;
-
- public GetPrintJobCaller() {
- super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
- mCallback = new BasePrintSpoolerServiceCallbacks() {
- @Override
- public void onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence) {
- onRemoteMethodResult(printJob, sequence);
- }
- };
- }
-
- public PrintJobInfo getPrintJobInfo(IPrintSpoolerService target, int printJobId,
- int appId) throws RemoteException, TimeoutException {
- final int sequence = onBeforeRemoteCall();
- target.getPrintJob(printJobId, mCallback, appId, sequence);
- return getResultTimed(sequence);
- }
- }
-
- private final class SetPrintJobStateCaller extends TimedRemoteCaller {
- private final IPrintSpoolerServiceCallbacks mCallback;
-
- public SetPrintJobStateCaller() {
- super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
- mCallback = new BasePrintSpoolerServiceCallbacks() {
- @Override
- public void onSetPrintJobStateResult(boolean success, int sequence) {
- onRemoteMethodResult(success, sequence);
- }
- };
- }
-
- public boolean setPrintJobState(IPrintSpoolerService target, int printJobId,
- int status) throws RemoteException, TimeoutException {
- final int sequence = onBeforeRemoteCall();
- target.setPrintJobState(printJobId, status, mCallback, sequence);
- return getResultTimed(sequence);
- }
- }
-
- private final class SetPrintJobTagCaller extends TimedRemoteCaller {
- private final IPrintSpoolerServiceCallbacks mCallback;
-
- public SetPrintJobTagCaller() {
- super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
- mCallback = new BasePrintSpoolerServiceCallbacks() {
- @Override
- public void onSetPrintJobTagResult(boolean success, int sequence) {
- onRemoteMethodResult(success, sequence);
- }
- };
- }
-
- public boolean setPrintJobTag(IPrintSpoolerService target, int printJobId,
- String tag) throws RemoteException, TimeoutException {
- final int sequence = onBeforeRemoteCall();
- target.setPrintJobTag(printJobId, tag, mCallback, sequence);
- return getResultTimed(sequence);
- }
- }
-
- private abstract class BasePrintSpoolerServiceCallbacks
- extends IPrintSpoolerServiceCallbacks.Stub {
- @Override
- public void onGetPrintJobsResult(List printJobIds, int sequence) {
- /** do nothing */
- }
-
- @Override
- public void onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence) {
- /** do nothing */
- }
-
- @Override
- public void onCreatePrintJobResult(PrintJobInfo printJob, int sequence) {
- /** do nothing */
- }
-
- @Override
- public void onCancelPrintJobResult(boolean canceled, int sequence) {
- /** do nothing */
- }
-
- @Override
- public void onSetPrintJobStateResult(boolean success, int sequece) {
- /** do nothing */
- }
-
- @Override
- public void onSetPrintJobTagResult(boolean success, int sequence) {
- /** do nothing */
- }
- }
-}
\ No newline at end of file
diff --git a/services/java/com/android/server/print/UserState.java b/services/java/com/android/server/print/UserState.java
new file mode 100644
index 000000000000..5cef4d3ceae7
--- /dev/null
+++ b/services/java/com/android/server/print/UserState.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.print;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.print.IPrinterDiscoveryObserver;
+import android.print.PrintJobInfo;
+import android.printservice.PrintServiceInfo;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.text.TextUtils.SimpleStringSplitter;
+import android.util.Slog;
+
+import com.android.server.print.RemotePrintSpooler.PrintSpoolerCallbacks;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Represents the print state for a user.
+ */
+final class UserState implements PrintSpoolerCallbacks {
+
+ private static final String LOG_TAG = "UserState";
+
+ private static final char COMPONENT_NAME_SEPARATOR = ':';
+
+ private final SimpleStringSplitter mStringColonSplitter =
+ new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
+
+ private final Intent mQueryIntent =
+ new Intent(android.printservice.PrintService.SERVICE_INTERFACE);
+
+ private final Map mActiveServices =
+ new HashMap();
+
+ private final List mInstalledServices =
+ new ArrayList();
+
+ private final Set mEnabledServices =
+ new HashSet();
+
+ private final Object mLock;
+
+ private final Context mContext;
+
+ private final int mUserId;
+
+ private final RemotePrintSpooler mSpooler;
+
+ private boolean mDestroyed;
+
+ public UserState(Context context, int userId, Object lock) {
+ mContext = context;
+ mUserId = userId;
+ mLock = lock;
+ mSpooler = new RemotePrintSpooler(context, userId, this);
+ }
+
+ @Override
+ public void onPrintJobQueued(PrintJobInfo printJob) {
+ final RemotePrintService service;
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ ComponentName printServiceName = printJob.getPrinterId().getService();
+ service = mActiveServices.get(printServiceName);
+ }
+ if (service != null) {
+ service.onPrintJobQueued(printJob);
+ }
+ }
+
+ @Override
+ public void onAllPrintJobsForServiceHandled(ComponentName printService) {
+ final RemotePrintService service;
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ service = mActiveServices.get(printService);
+ }
+ if (service != null) {
+ service.onAllPrintJobsHandled();
+ }
+ }
+
+ @Override
+ public void onStartPrinterDiscovery(IPrinterDiscoveryObserver observer) {
+ final List services;
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ if (mActiveServices.isEmpty()) {
+ return;
+ }
+ services = new ArrayList(mActiveServices.values());
+ }
+ final int serviceCount = services.size();
+ for (int i = 0; i < serviceCount; i++) {
+ RemotePrintService service = services.get(i);
+ service.onStartPrinterDiscovery(observer);
+ }
+ }
+
+ @Override
+ public void onStopPrinterDiscovery() {
+ final List services;
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ if (mActiveServices.isEmpty()) {
+ return;
+ }
+ services = new ArrayList(mActiveServices.values());
+ }
+ final int serviceCount = services.size();
+ for (int i = 0; i < serviceCount; i++) {
+ RemotePrintService service = services.get(i);
+ service.onStopPrinterDiscovery();
+ }
+ }
+
+ public void updateIfNeededLocked() {
+ throwIfDestroyedLocked();
+ if (readConfigurationLocked()) {
+ onConfigurationChangedLocked();
+ }
+ }
+
+ public RemotePrintSpooler getSpoolerLocked() {
+ throwIfDestroyedLocked();
+ return mSpooler;
+ }
+
+ public Map getActiveServices() {
+ synchronized(mLock) {
+ throwIfDestroyedLocked();
+ return mActiveServices;
+ }
+ }
+
+ public Set getEnabledServices() {
+ synchronized(mLock) {
+ throwIfDestroyedLocked();
+ return mEnabledServices;
+ }
+ }
+
+ public void destroyLocked() {
+ throwIfDestroyedLocked();
+ mSpooler.destroy();
+ for (RemotePrintService service : mActiveServices.values()) {
+ service.destroy();
+ }
+ mActiveServices.clear();
+ mInstalledServices.clear();
+ mEnabledServices.clear();
+ mDestroyed = true;
+ }
+
+ private boolean readConfigurationLocked() {
+ boolean somethingChanged = false;
+ somethingChanged |= readInstalledPrintServicesLocked();
+ somethingChanged |= readEnabledPrintServicesLocked();
+ return somethingChanged;
+ }
+
+ private boolean readInstalledPrintServicesLocked() {
+ Set tempPrintServices = new HashSet();
+
+ List installedServices = mContext.getPackageManager()
+ .queryIntentServicesAsUser(mQueryIntent, PackageManager.GET_SERVICES
+ | PackageManager.GET_META_DATA, mUserId);
+
+ final int installedCount = installedServices.size();
+ for (int i = 0, count = installedCount; i < count; i++) {
+ ResolveInfo installedService = installedServices.get(i);
+ if (!android.Manifest.permission.BIND_PRINT_SERVICE.equals(
+ installedService.serviceInfo.permission)) {
+ ComponentName serviceName = new ComponentName(
+ installedService.serviceInfo.packageName,
+ installedService.serviceInfo.name);
+ Slog.w(LOG_TAG, "Skipping print service "
+ + serviceName.flattenToShortString()
+ + " since it does not require permission "
+ + android.Manifest.permission.BIND_PRINT_SERVICE);
+ continue;
+ }
+ tempPrintServices.add(PrintServiceInfo.create(installedService, mContext));
+ }
+
+ if (!tempPrintServices.equals(mInstalledServices)) {
+ mInstalledServices.clear();
+ mInstalledServices.addAll(tempPrintServices);
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean readEnabledPrintServicesLocked() {
+ Set tempEnabledServiceNameSet = new HashSet();
+
+ String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+ Settings.Secure.ENABLED_PRINT_SERVICES, mUserId);
+ if (!TextUtils.isEmpty(settingValue)) {
+ TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
+ splitter.setString(settingValue);
+ while (splitter.hasNext()) {
+ String string = splitter.next();
+ if (TextUtils.isEmpty(string)) {
+ continue;
+ }
+ ComponentName componentName = ComponentName.unflattenFromString(string);
+ if (componentName != null) {
+ tempEnabledServiceNameSet.add(componentName);
+ }
+ }
+ }
+
+ if (!tempEnabledServiceNameSet.equals(mEnabledServices)) {
+ mEnabledServices.clear();
+ mEnabledServices.addAll(tempEnabledServiceNameSet);
+ return true;
+ }
+
+ return false;
+ }
+
+ private void onConfigurationChangedLocked() {
+ final int installedCount = mInstalledServices.size();
+ for (int i = 0; i < installedCount; i++) {
+ ResolveInfo resolveInfo = mInstalledServices.get(i).getResolveInfo();
+ ComponentName serviceName = new ComponentName(resolveInfo.serviceInfo.packageName,
+ resolveInfo.serviceInfo.name);
+ if (mEnabledServices.contains(serviceName)) {
+ if (!mActiveServices.containsKey(serviceName)) {
+ mActiveServices.put(serviceName, new RemotePrintService(
+ mContext, serviceName, mUserId, mSpooler));
+ }
+ } else {
+ RemotePrintService service = mActiveServices.remove(serviceName);
+ if (service != null) {
+ service.destroy();
+ }
+ }
+ }
+ }
+
+ private void throwIfDestroyedLocked() {
+ if (mDestroyed) {
+ throw new IllegalStateException("Cannot interact with a destroyed instance.");
+ }
+ }
+}
+
--
cgit v1.2.3-59-g8ed1b