summaryrefslogtreecommitdiffstats
path: root/core/java/android/printservice
diff options
context:
space:
mode:
authorSvetoslav Ganov <svetoslavganov@google.com>2013-08-11 12:29:39 -0700
committerSvetoslav Ganov <svetoslavganov@google.com>2013-08-11 14:40:05 -0700
commit798bed6cc7d273e72b0253288605db9cd2b57740 (patch)
treeb4278847c40cf910b69773c6205395ada02543ed /core/java/android/printservice
parent5893a97cbf398ca3e1bff5444454343d94e25a4c (diff)
downloadframeworks_base-798bed6cc7d273e72b0253288605db9cd2b57740.zip
frameworks_base-798bed6cc7d273e72b0253288605db9cd2b57740.tar.gz
frameworks_base-798bed6cc7d273e72b0253288605db9cd2b57740.tar.bz2
Refinement of the print service APIs.
1. Factored out the printer discovery APIs of a print service in a dedicated session object that is created by the print service on demand. This ensures that added/removed/updated printers from one session do not interfere with another session. 2. Updated the app facing APIs to pass in a document info along with a printed file. Also exposed the print file adapter so apps that create a temporary file for printing can intercept when it is read by the system so the file can be deleted. 3. Updated the print service documentation. Change-Id: I3473d586c26d8bda1cf7e2bdacb441aa9df982ed
Diffstat (limited to 'core/java/android/printservice')
-rw-r--r--core/java/android/printservice/IPrintService.aidl12
-rw-r--r--core/java/android/printservice/PrintDocument.java10
-rw-r--r--core/java/android/printservice/PrintJob.java57
-rw-r--r--core/java/android/printservice/PrintService.java433
-rw-r--r--core/java/android/printservice/PrinterDiscoverySession.java357
-rw-r--r--core/java/android/printservice/package.html24
6 files changed, 567 insertions, 326 deletions
diff --git a/core/java/android/printservice/IPrintService.aidl b/core/java/android/printservice/IPrintService.aidl
index e6fdbf9..16b7a26 100644
--- a/core/java/android/printservice/IPrintService.aidl
+++ b/core/java/android/printservice/IPrintService.aidl
@@ -10,16 +10,14 @@
* 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
+ * See the License for the specific languag˙e governing permissions and
* limitations under the License.
*/
package android.printservice;
-import android.os.ICancellationSignal;
-import android.print.IPrinterDiscoveryObserver;
+import android.print.IPrinterDiscoverySessionObserver;
import android.print.PrintJobInfo;
-import android.print.PrinterId;
import android.printservice.IPrintServiceClient;
/**
@@ -29,9 +27,7 @@ import android.printservice.IPrintServiceClient;
*/
oneway interface IPrintService {
void setClient(IPrintServiceClient client);
- void onRequestUpdatePrinters(in List<PrinterId> printerIds);
- void onRequestCancelPrintJob(in PrintJobInfo printJobInfo);
+ void requestCancelPrintJob(in PrintJobInfo printJobInfo);
void onPrintJobQueued(in PrintJobInfo printJobInfo);
- void onStartPrinterDiscovery(IPrinterDiscoveryObserver observer);
- void onStopPrinterDiscovery();
+ void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer);
}
diff --git a/core/java/android/printservice/PrintDocument.java b/core/java/android/printservice/PrintDocument.java
index 2a1581a..7437dc5 100644
--- a/core/java/android/printservice/PrintDocument.java
+++ b/core/java/android/printservice/PrintDocument.java
@@ -55,14 +55,14 @@ public final class PrintDocument {
}
/**
- * Gets the data associated with this document. It is a responsibility of the
- * client to open a stream to the returned file descriptor and fully read the
- * data.
+ * Gets the data associated with this document.
* <p>
- * <strong>Note:</strong> It is your responsibility to close the file descriptor.
+ * <strong>Note: </strong> It is a responsibility of the client to open a
+ * stream to the returned file descriptor, fully read the data, and close
+ * the file descriptor.
* </p>
*
- * @return A file descriptor for reading the data or <code>null</code>.
+ * @return A file descriptor for reading the data.
*/
public FileDescriptor getData() {
ParcelFileDescriptor source = null;
diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java
index 64c079e..5f9a730 100644
--- a/core/java/android/printservice/PrintJob.java
+++ b/core/java/android/printservice/PrintJob.java
@@ -21,9 +21,9 @@ import android.print.PrintJobInfo;
import android.util.Log;
/**
- * This class represents a print job from the perspective of a
- * print service. It provides APIs for observing the print job
- * state and performing operations on the print job.
+ * This class represents a print job from the perspective of a print
+ * service. It provides APIs for observing the print job state and
+ * performing operations on the print job.
*/
public final class PrintJob {
@@ -38,7 +38,8 @@ public final class PrintJob {
PrintJob(PrintJobInfo jobInfo, IPrintServiceClient client) {
mCachedInfo = jobInfo;
mPrintServiceClient = client;
- mDocument = new PrintDocument(mCachedInfo.getId(), client, jobInfo.getDocumentInfo());
+ mDocument = new PrintDocument(mCachedInfo.getId(), client,
+ jobInfo.getDocumentInfo());
}
/**
@@ -77,7 +78,7 @@ public final class PrintJob {
}
/**
- * Gets the document of this print job.
+ * Gets the printed document.
*
* @return The document.
*/
@@ -87,11 +88,12 @@ public final class PrintJob {
/**
* Gets whether this print job is queued. Such a print job is
- * ready to be printed and can be started.
+ * ready to be printed and can be started or cancelled.
*
* @return Whether the print job is queued.
*
* @see #start()
+ * @see #cancel()
*/
public boolean isQueued() {
return getInfo().getState() == PrintJobInfo.STATE_QUEUED;
@@ -112,6 +114,42 @@ public final class PrintJob {
}
/**
+ * Gets whether this print job is completed. Such a print job
+ * is successfully printed. This is a final state.
+ *
+ * @return Whether the print job is completed.
+ *
+ * @see #complete()
+ */
+ public boolean isCompleted() {
+ return getInfo().getState() == PrintJobInfo.STATE_COMPLETED;
+ }
+
+ /**
+ * Gets whether this print job is failed. Such a print job is
+ * not successfully printed due to an error. This is a final state.
+ *
+ * @return Whether the print job is failed.
+ *
+ * @see #fail(CharSequence)
+ */
+ public boolean isFailed() {
+ return getInfo().getState() == PrintJobInfo.STATE_FAILED;
+ }
+
+ /**
+ * Gets whether this print job is cancelled. Such a print job was
+ * cancelled as a result of a user request. This is a final state.
+ *
+ * @return Whether the print job is cancelled.
+ *
+ * @see #cancel()
+ */
+ public boolean isCancelled() {
+ return getInfo().getState() == PrintJobInfo.STATE_FAILED;
+ }
+
+ /**
* Starts the print job. You should call this method if {@link
* #isQueued()} returns true and you started printing.
*
@@ -163,12 +201,13 @@ public final class PrintJob {
/**
* Cancels the print job. You should call this method if {@link
* #isQueued()} or {@link #isStarted()} returns true and you canceled
- * the print job as a response to a call to {@link PrintService
- * #onRequestCancelPrintJob(PrintJob)}.
+ * the print job as a response to a call to {@link
+ * PrintService#onRequestCancelPrintJob(PrintJob)}.
*
- * @return Whether the job as canceled.
+ * @return Whether the job is canceled.
*
* @see #isStarted()
+ * @see #isQueued()
*/
public boolean cancel() {
if (isQueued() || isStarted()) {
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
index 49384db..92bccd4 100644
--- a/core/java/android/printservice/PrintService.java
+++ b/core/java/android/printservice/PrintService.java
@@ -25,10 +25,9 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
-import android.print.IPrinterDiscoveryObserver;
+import android.print.IPrinterDiscoverySessionObserver;
import android.print.PrintJobInfo;
import android.print.PrinterId;
-import android.print.PrinterInfo;
import android.util.Log;
import java.util.ArrayList;
@@ -37,84 +36,82 @@ import java.util.List;
/**
* <p>
- * This is the base class for implementing print services. A print service
- * knows how to discover and interact one or more printers via one or more
- * protocols.
+ * This is the base class for implementing print services. A print service knows
+ * how to discover and interact one or more printers via one or more protocols.
* </p>
* <h3>Printer discovery</h3>
* <p>
- * A print service is responsible for discovering and reporting printers.
- * A printer discovery period starts with a call to
- * {@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 reports added printers
- * that disappeared by calling {@link #removeDiscoveredPrinters(List)}.
- * Calls to {@link #addDiscoveredPrinters(List)} and
- * {@link #removeDiscoveredPrinters(List)} before a call to
- * {@link #onStartPrinterDiscovery()} and after a call to
- * {@link #onStopPrinterDiscovery()} are a no-op.
+ * A print service is responsible for discovering printers, adding discovered printers,
+ * removing added printers, and updating added printers. When the system is interested
+ * in printers managed by your service it will call {@link
+ * #onCreatePrinterDiscoverySession()} from which you must return a new {@link
+ * PrinterDiscoverySession} instance. The returned session encapsulates the interaction
+ * between the system and your service during printer discovery. For description of this
+ * interaction refer to the documentation for {@link PrinterDiscoverySession}.
* </p>
* <p>
- * For every printer discovery period all printers have to be added. Each
- * printer known to this print service should be added only once during a
- * discovery period, unless it was added and then removed before that.
- * Only an already added printer can be removed.
+ * For every printer discovery session all printers have to be added since system does
+ * not retain printers across sessions. Hence, each printer known to this print service
+ * should be added only once during a discovery session. Only an already added printer
+ * can be removed or updated. Removed printers can be added again.
* </p>
* <h3>Print jobs</h3>
* <p>
- * When a new print job targeted to the printers managed by this print
- * service is queued, i.e. ready for processing by the print service,
- * 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()}.
+ * When a new print job targeted to a printer managed by this print service is is queued,
+ * i.e. ready for processing by the print service, you will receive a call to {@link
+ * #onPrintJobQueued(PrintJob)}. The print service may handle the print job immediately
+ * or schedule that for an appropriate time in the future. The list of all active print
+ * jobs for this service is obtained by calling {@link #getActivePrintJobs()}. Active
+ * print jobs are ones that are queued or started.
* </p>
* <p>
- * 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#getDocument()}. A queued print
- * job's {@link PrintJob#isQueued()} method returns true.
+ * A print service is responsible for setting a print job's state as appropriate
+ * while processing it. Initially, a print job is queued, i.e. {@link PrintJob#isQueued()
+ * PrintJob.isQueued()} returns true, which means that the document to be printed is
+ * spooled by the system and the print service can begin processing it. You can obtain
+ * the printed document by calling {@link PrintJob#getDocument() PrintJob.getDocument()}
+ * whose data is accessed via {@link PrintDocument#getData() PrintDocument.getData()}.
+ * After the print service starts printing the data it should set the print job's
+ * state to started by calling {@link PrintJob#start()} after which
+ * {@link PrintJob#isStarted() PrintJob.isStarted()} would return true. Upon successful
+ * completion, the print job should be marked as completed by calling {@link
+ * PrintJob#complete() PrintJob.complete()} after which {@link PrintJob#isCompleted()
+ * PrintJob.isCompleted()} would return true. In case of a failure, the print job should
+ * be marked as failed by calling {@link PrintJob#fail(CharSequence) PrintJob.fail(
+ * CharSequence)} after which {@link PrintJob#isFailed() PrintJob.isFailed()} would
+ * return true.
* </p>
* <p>
- * 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()}.
+ * If a print job is queued or started 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 best effort in canceling the job. In case the job
+ * is successfully canceled, its state has to be marked as cancelled by calling {@link
+ * PrintJob#cancel() PrintJob.cancel()} after which {@link PrintJob#isCancelled()
+ * PrintJob.isCacnelled()} would return true.
* </p>
* <h3>Lifecycle</h3>
* <p>
- * The lifecycle of a print service is managed exclusively by the system
- * and follows the established service lifecycle. Additionally, starting
- * or stopping a print service is triggered exclusively by an explicit
- * user action through enabling or disabling it in the device settings.
- * After the system binds to a print service, it calls {@link #onConnected()}.
- * This method can be overriden by clients to perform post binding setup.
- * Also after the system unbinds from a print service, it calls
- * {@link #onDisconnected()}. This method can be overriden by clients to
- * perform post unbinding cleanup.
+ * The lifecycle of a print service is managed exclusively by the system and follows
+ * the established service lifecycle. Additionally, starting or stopping a print service
+ * is triggered exclusively by an explicit user action through enabling or disabling it
+ * in the device settings. After the system binds to a print service, it calls {@link
+ * #onConnected()}. This method can be overriden by clients to perform post binding setup.
+ * Also after the system unbinds from a print service, it calls {@link #onDisconnected()}.
+ * This method can be overriden by clients to perform post unbinding cleanup. Your should
+ * not do any work after the system disconnected from your print service since the
+ * service can be killed at any time to reclaim memory. The system will not disconnect
+ * from a print service if there are active print jobs for the printers managed by it.
* </p>
* <h3>Declaration</h3>
* <p>
- * A print service is declared as any other service in an AndroidManifest.xml
- * but it must also specify that it handles the {@link android.content.Intent}
- * with action {@link #SERVICE_INTERFACE}. Failure to declare this intent
- * will cause the system to ignore the print service. Additionally, a print
- * service must request the {@link android.Manifest.permission#BIND_PRINT_SERVICE}
- * permission to ensure that only the system can bind to it. Failure to
- * declare this intent will cause the system to ignore the print service.
- * Following is an example declaration:
+ * A print service is declared as any other service in an AndroidManifest.xml but it must
+ * also specify that it handles the {@link android.content.Intent} with action {@link
+ * #SERVICE_INTERFACE android.printservice.PrintService}. Failure to declare this intent
+ * will cause the system to ignore the print service. Additionally, a print service must
+ * request the {@link android.Manifest.permission#BIND_PRINT_SERVICE
+ * android.permission.BIND_PRINT_SERVICE} permission to ensure that only the system can
+ * bind to it. Failure to declare this intent will cause the system to ignore the print
+ * service. Following is an example declaration:
* </p>
* <pre>
* &lt;service android:name=".MyPrintService"
@@ -127,17 +124,15 @@ import java.util.List;
* </pre>
* <h3>Configuration</h3>
* <p>
- * 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, vendor
- * name ,etc. It is a responsibility of the system to launch the settings
- * and add printers activities when appropriate.
+ * A print service can be configured by specifying an optional settings activity which
+ * exposes service specific settings, an optional add printers 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.
* </p>
* <p>
- * A print service is configured by providing a
- * {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring
- * the service. A service declaration with a meta-data tag is presented
- * below:
+ * A print service is configured by providing a {@link #SERVICE_META_DATA meta-data}
+ * entry in the manifest when declaring the service. A service declaration with a meta-data
+ * tag is presented below:
* <pre> &lt;service android:name=".MyPrintService"
* android:permission="android.permission.BIND_PRINT_SERVICE"&gt;
* &lt;intent-filter&gt;
@@ -147,8 +142,9 @@ import java.util.List;
* &lt;/service&gt;</pre>
* </p>
* <p>
- * For more details refer to {@link #SERVICE_META_DATA} and
- * <code>&lt;{@link android.R.styleable#PrintService print-service}&gt;</code>.
+ * For more details for how to configure your print service via the meta-data refer to
+ * {@link #SERVICE_META_DATA} and <code>&lt;{@link android.R.styleable#PrintService
+ * print-service}&gt;</code>.
* </p>
*/
public abstract class PrintService extends Service {
@@ -157,21 +153,25 @@ public abstract class PrintService extends Service {
/**
* The {@link Intent} action that must be declared as handled by a service
- * in its manifest to allow the system to recognize it as a print service.
+ * in its manifest for the system to recognize it as a print service.
*/
public static final String SERVICE_INTERFACE = "android.printservice.PrintService";
/**
- * Name under which a PrintService component publishes additional information
- * about itself. This meta-data must reference an XML resource containing a
- * <code>&lt;{@link android.R.styleable#PrintService print-service}&gt;</code>
- * tag. This is a a sample XML file configuring a print service:
+ * Name under which a {@link PrintService} component publishes additional information
+ * about itself. This meta-data must reference a XML resource containing a <code>
+ * &lt;{@link android.R.styleable#PrintService print-service}&gt;</code> tag. This is
+ * a sample XML file configuring a print service:
* <pre> &lt;print-service
* android:vendor="SomeVendor"
* android:settingsActivity="foo.bar.MySettingsActivity"
* andorid:addPrintersActivity="foo.bar.MyAddPrintersActivity."
* . . .
* /&gt;</pre>
+ * <p>
+ * For detailed configuration options that can be specified via the meta-data
+ * refer to {@link android.R.styleable#PrintService android.R.styleable.PrintService}.
+ * </p>
*/
public static final String SERVICE_META_DATA = "android.printservice";
@@ -181,12 +181,12 @@ public abstract class PrintService extends Service {
private IPrintServiceClient mClient;
- private IPrinterDiscoveryObserver mDiscoveryObserver;
+ private int mLastSessionId = -1;
@Override
- protected void attachBaseContext(Context base) {
+ protected final void attachBaseContext(Context base) {
super.attachBaseContext(base);
- mHandler = new MyHandler(base.getMainLooper());
+ mHandler = new ServiceHandler(base.getMainLooper());
}
/**
@@ -204,203 +204,47 @@ public abstract class PrintService extends Service {
}
/**
- * Callback requesting from this service to start printer discovery.
- * At the end of the printer discovery period the system will call
- * {@link #onStopPrinterDiscovery()}. Discovered printers should be
- * reported by calling {@link #addDiscoveredPrinters(List)} and reported
- * ones that disappear should be reported by calling
- * {@link #removeDiscoveredPrinters(List)}.
- *
- * @see #onStopPrinterDiscovery()
- * @see #addDiscoveredPrinters(List)
- * @see #removeDiscoveredPrinters(List)
- * @see #updateDiscoveredPrinters(List)
- */
- protected abstract void onStartPrinterDiscovery();
-
- /**
- * Callback requesting from this service to stop printer discovery.
- *
- * @see #onStartPrinterDiscovery()
- * @see #addDiscoveredPrinters(List)
- * @see #removeDiscoveredPrinters(List)
- * @see #updateDiscoveredPrinters(List)
- */
- protected abstract void onStopPrinterDiscovery();
-
- /**
- * Adds discovered printers. This method should be called during a
- * printer discovery period, i.e. after a call to
- * {@link #onStartPrinterDiscovery()} and before the corresponding
- * call to {@link #onStopPrinterDiscovery()}, otherwise it does nothing.
- * <p>
- * <strong>Note:</strong> For every printer discovery period all
- * 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.
- * </p>
- * <p>
- * A {@link PrinterInfo} can have all of its required attributes specified,
- * or not. Whether all attributes are specified can be verified by calling
- * {@link PrinterInfo#hasAllRequiredAttributes()}. You can add printers
- * regardless if all required attributes are specified. When the system
- * (and the user) needs to interact with a printer, you will receive a
- * call to {@link #onRequestUpdatePrinters(List)}. If you fail to update
- * a printer that was added without all required attributes via calling
- * {@link #updateDiscoveredPrinters(List)}, then this printer will be
- * ignored, i.e. considered unavailable.
- * <p>
- *
- * @param printers A list with discovered printers.
- *
- * @see #updateDiscoveredPrinters(List)
- * @see #removeDiscoveredPrinters(List)
- * @see #onStartPrinterDiscovery()
- * @see #onStopPrinterDiscovery()
- */
- public final void addDiscoveredPrinters(List<PrinterInfo> printers) {
- final IPrinterDiscoveryObserver observer;
- synchronized (mLock) {
- observer = mDiscoveryObserver;
- }
- if (observer != null) {
- try {
- observer.onPrintersAdded(printers);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error adding discovered printers", re);
- }
- }
- }
-
- /**
- * Removes discovered printers given their ids. This method should be called
- * during a printer discovery period, i.e. after a call to
- * {@link #onStartPrinterDiscovery()} and before the corresponding
- * call to {@link #onStopPrinterDiscovery()}, otherwise it does nothing.
- * <p>
- * For every printer discovery period all printers have to be added. You
- * should remove only printers that were added in this printer discovery
- * period by a call to {@link #addDiscoveredPrinters(List)}. You can call
- * 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, it will be ignored.
- * </p>
+ * Callback asking you to create a new {@link PrinterDiscoverySession}.
*
- * @param printerIds A list with disappeared printer ids.
- *
- * @see #addDiscoveredPrinters(List)
- * @see #updateDiscoveredPrinters(List)
- * @see #onStartPrinterDiscovery()
- * @see #onStopPrinterDiscovery()
+ * @see PrinterDiscoverySession
*/
- public final void removeDiscoveredPrinters(List<PrinterId> printerIds) {
- final IPrinterDiscoveryObserver observer;
- synchronized (mLock) {
- observer = mDiscoveryObserver;
- }
- if (observer != null) {
- try {
- observer.onPrintersRemoved(printerIds);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error removing discovered printers", re);
- }
- }
- }
+ protected abstract PrinterDiscoverySession onCreatePrinterDiscoverySession();
/**
- * Updates discovered printers that are already added. This method should
- * be called during a printer discovery period, i.e. after a call to
- * {@link #onStartPrinterDiscovery()} and before the corresponding
- * call to {@link #onStopPrinterDiscovery()}, otherwise it does nothing.
- * <p>
- * For every printer discovery period all printers have to be added. You
- * should update only printers that were added in this printer discovery
- * period by a call to {@link #addDiscoveredPrinters(List)}. You can call
- * this method as many times as necessary during the discovery period
- * but should not try to update already removed or never added printers.
- * If a printer is already removed or never added, it will be ignored.
- * </p>
- *
- * @param printers A list with updated printers.
- *
- * @see #addDiscoveredPrinters(List)
- * @see #removeDiscoveredPrinters(List)
- * @see #onStartPrinterDiscovery()
- * @see #onStopPrinterDiscovery()
- */
- public final void updateDiscoveredPrinters(List<PrinterInfo> printers) {
- final IPrinterDiscoveryObserver observer;
- synchronized (mLock) {
- observer = mDiscoveryObserver;
- }
- if (observer != null) {
- try {
- observer.onPrintersUpdated(printers);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error updating discovered printers", re);
- }
- }
- }
-
- /**
- * Called when the system will start interacting with a printer
- * giving you a change to update it in case some of its capabilities
- * have changed. For example, this method will be called when the
- * user selects a printer. Hence, it updating this printer should
- * be done as quickly as possible in order to achieve maximally
- * smooth user experience.
- * <p>
- * A {@link PrinterInfo} can have all of its required attributes specified,
- * or not. Whether all attributes are specified can be verified by calling
- * {@link PrinterInfo#hasAllRequiredAttributes()}. You can add printers
- * regardless if all required attributes are specified. When the system
- * (and the user) needs to interact with a printer, you will receive a
- * call to this method. If you fail to update a printer that was added
- * without all required attributes via calling
- * {@link #updateDiscoveredPrinters(List)}, then this printer will be
- * ignored, i.e. considered unavailable.
- * </p>
- *
- * @param printerIds The printers to be updated.
- */
- protected void onRequestUpdatePrinters(List<PrinterId> printerIds) {
- }
-
- /**
- * Called when canceling of a print job is requested. The service
+ * Called when cancellation of a print job is requested. The service
* should do best effort to fulfill the request. After the cancellation
- * is performed, the print job should be set to a cancelled state by
+ * is performed, the print job should be marked as cancelled state by
* calling {@link PrintJob#cancel()}.
*
- * @param printJob The print job to be canceled.
+ * @param printJob The print job to cancel.
+ *
+ * @see PrintJob#cancel() PrintJob.cancel()
+ * @see PrintJob#isCancelled() PrintJob.isCancelled()
*/
- protected void onRequestCancelPrintJob(PrintJob printJob) {
- }
+ protected abstract void onRequestCancelPrintJob(PrintJob printJob);
/**
* 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#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} by
- * calling {@link PrintJob#isQueued()}.
+ * managed by this print service.
*
* @param printJob The new queued print job.
*
- * @see #getPrintJobs()
+ * @see PrintJob#isQueued() PrintJob.isQueued()
+ * @see #getActivePrintJobs()
*/
protected abstract void onPrintJobQueued(PrintJob printJob);
/**
- * Gets the print jobs for the printers managed by this service.
+ * Gets the active print jobs for the printers managed by this service.
+ * Active print jobs are ones that are not in a final state, i.e. whose
+ * state is queued or started.
*
- * @return The print jobs.
+ * @return The active print jobs.
+ *
+ * @see PrintJob#isQueued() PrintJob.isQueued()
+ * @see PrintJob#isStarted() PrintJob.isStarted()
*/
- public final List<PrintJob> getPrintJobs() {
+ public final List<PrintJob> getActivePrintJobs() {
final IPrintServiceClient client;
synchronized (mLock) {
client = mClient;
@@ -428,14 +272,14 @@ public abstract class PrintService extends Service {
}
/**
- * Generates a global printer id given the printer's locally unique name.
+ * Generates a global printer id given the printer's locally unique one.
*
- * @param printerName The printer name.
+ * @param localId A locally unique id in the context of your print service.
* @return Global printer id.
*/
- public final PrinterId generatePrinterId(String printerName) {
+ public final PrinterId generatePrinterId(String localId) {
return new PrinterId(new ComponentName(getPackageName(),
- getClass().getName()), printerName);
+ getClass().getName()), localId);
}
@Override
@@ -443,69 +287,58 @@ public abstract class PrintService extends Service {
return new IPrintService.Stub() {
@Override
public void setClient(IPrintServiceClient client) {
- mHandler.obtainMessage(MyHandler.MSG_SET_CLEINT, client).sendToTarget();
+ mHandler.obtainMessage(ServiceHandler.MSG_SET_CLEINT, client)
+ .sendToTarget();
}
@Override
- public void onStartPrinterDiscovery(IPrinterDiscoveryObserver observer) {
- mHandler.obtainMessage(MyHandler.MSG_ON_START_PRINTER_DISCOVERY,
+ public void createPrinterDiscoverySession(IPrinterDiscoverySessionObserver observer) {
+ mHandler.obtainMessage(ServiceHandler.MSG_ON_CREATE_PRINTER_DISCOVERY_SESSION,
observer).sendToTarget();
}
@Override
- public void onStopPrinterDiscovery() {
- mHandler.sendEmptyMessage(MyHandler.MSG_ON_STOP_PRINTER_DISCOVERY);
- }
-
- @Override
- public void onRequestUpdatePrinters(List<PrinterId> printerIds) {
- mHandler.obtainMessage(MyHandler.MSG_ON_REQUEST_UPDATE_PRINTERS,
- printerIds).sendToTarget();
- }
-
- @Override
- public void onRequestCancelPrintJob(PrintJobInfo printJobInfo) {
- mHandler.obtainMessage(MyHandler.MSG_ON_REQUEST_CANCEL_PRINTJOB,
+ public void requestCancelPrintJob(PrintJobInfo printJobInfo) {
+ mHandler.obtainMessage(ServiceHandler.MSG_ON_REQUEST_CANCEL_PRINTJOB,
printJobInfo).sendToTarget();
}
@Override
public void onPrintJobQueued(PrintJobInfo printJobInfo) {
- mHandler.obtainMessage(MyHandler.MSG_ON_PRINTJOB_QUEUED,
+ mHandler.obtainMessage(ServiceHandler.MSG_ON_PRINTJOB_QUEUED,
printJobInfo).sendToTarget();
}
};
}
- private final class MyHandler extends Handler {
- public static final int MSG_ON_START_PRINTER_DISCOVERY = 1;
- public static final int MSG_ON_STOP_PRINTER_DISCOVERY = 2;
+ private final class ServiceHandler extends Handler {
+ public static final int MSG_ON_CREATE_PRINTER_DISCOVERY_SESSION = 1;
+ public static final int MSG_ON_PRINTJOB_QUEUED = 2;
public static final int MSG_ON_REQUEST_CANCEL_PRINTJOB = 3;
- public static final int MSG_ON_REQUEST_UPDATE_PRINTERS = 4;
- public static final int MSG_ON_PRINTJOB_QUEUED = 5;
- public static final int MSG_SET_CLEINT = 6;
+ public static final int MSG_SET_CLEINT = 4;
- public MyHandler(Looper looper) {
+ public ServiceHandler(Looper looper) {
super(looper, null, true);
}
@Override
- @SuppressWarnings("unchecked")
public void handleMessage(Message message) {
final int action = message.what;
switch (action) {
- case MSG_ON_START_PRINTER_DISCOVERY: {
- synchronized (mLock) {
- mDiscoveryObserver = (IPrinterDiscoveryObserver) message.obj;
+ case MSG_ON_CREATE_PRINTER_DISCOVERY_SESSION: {
+ IPrinterDiscoverySessionObserver observer =
+ (IPrinterDiscoverySessionObserver) message.obj;
+ PrinterDiscoverySession session = onCreatePrinterDiscoverySession();
+ if (session == null) {
+ throw new NullPointerException("session cannot be null");
}
- onStartPrinterDiscovery();
- } break;
-
- case MSG_ON_STOP_PRINTER_DISCOVERY: {
synchronized (mLock) {
- mDiscoveryObserver = null;
+ if (session.getId() == mLastSessionId) {
+ throw new IllegalStateException("cannot reuse sessions");
+ }
+ mLastSessionId = session.getId();
}
- onStopPrinterDiscovery();
+ session.setObserver(observer);
} break;
case MSG_ON_REQUEST_CANCEL_PRINTJOB: {
@@ -513,11 +346,6 @@ public abstract class PrintService extends Service {
onRequestCancelPrintJob(new PrintJob(printJobInfo, mClient));
} break;
- case MSG_ON_REQUEST_UPDATE_PRINTERS: {
- List<PrinterId> printerIds = (List<PrinterId>) message.obj;
- onRequestUpdatePrinters(printerIds);
- } break;
-
case MSG_ON_PRINTJOB_QUEUED: {
PrintJobInfo printJobInfo = (PrintJobInfo) message.obj;
onPrintJobQueued(new PrintJob(printJobInfo, mClient));
@@ -527,9 +355,6 @@ public abstract class PrintService extends Service {
IPrintServiceClient client = (IPrintServiceClient) message.obj;
synchronized (mLock) {
mClient = client;
- if (client == null) {
- mDiscoveryObserver = null;
- }
}
if (client != null) {
onConnected();
diff --git a/core/java/android/printservice/PrinterDiscoverySession.java b/core/java/android/printservice/PrinterDiscoverySession.java
new file mode 100644
index 0000000..5bc0f2e
--- /dev/null
+++ b/core/java/android/printservice/PrinterDiscoverySession.java
@@ -0,0 +1,357 @@
+/*
+ * 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 android.printservice;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.print.IPrinterDiscoverySessionController;
+import android.print.IPrinterDiscoverySessionObserver;
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+import android.util.Log;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+
+/**
+ * This class encapsulates the interaction between a print service and the
+ * system during printer discovery. During printer discovery you are responsible
+ * for adding discovered printers, removing already added printers that
+ * disappeared, and updating already added printers.
+ * <p>
+ * The opening of the session is announced by a call to {@link
+ * PrinterDiscoverySession#onOpen(List)} at which point you should start printer
+ * discovery. The closing of the session is announced by a call to {@link
+ * PrinterDiscoverySession#onClose()} at which point you should stop printer
+ * discovery. Discovered printers are added by invoking {@link
+ * PrinterDiscoverySession#addPrinters(List)}. Added printers that disappeared
+ * are removed by invoking {@link PrinterDiscoverySession#removePrinters(List)}.
+ * Added printers whose properties or capabilities changed are updated through
+ * a call to {@link PrinterDiscoverySession#updatePrinters(List)}.
+ * </p>
+ * <p>
+ * The system will make a call to
+ * {@link PrinterDiscoverySession#onRequestPrinterUpdate(PrinterId)} if you
+ * need to update a given printer. It is possible that you add a printer without
+ * specifying its capabilities. This enables you to avoid querying all
+ * discovered printers for their capabilities, rather querying the capabilities
+ * of a printer only if necessary. For example, the system will require that you
+ * update a printer if it gets selected by the user. If you did not report the
+ * printer capabilities when adding it, you must do so after the system requests
+ * a printer update. Otherwise, the printer will be ignored.
+ * </p>
+ * <p>
+ * During printer discovery all printers that are known to your print service
+ * have to be added. The system does not retain any printers from previous
+ * sessions.
+ * </p>
+ */
+public abstract class PrinterDiscoverySession {
+ private static final String LOG_TAG = "PrinterDiscoverySession";
+
+ private static int sIdCounter = 0;
+
+ private final Object mLock = new Object();
+
+ private final Handler mHandler;
+
+ private final int mId;
+
+ private IPrinterDiscoverySessionController mController;
+
+ private IPrinterDiscoverySessionObserver mObserver;
+
+ /**
+ * Constructor.
+ *
+ * @param context A context instance.
+ */
+ public PrinterDiscoverySession(Context context) {
+ mId = sIdCounter++;
+ mHandler = new SessionHandler(context.getMainLooper());
+ mController = new PrinterDiscoverySessionController(this);
+ }
+
+ void setObserver(IPrinterDiscoverySessionObserver observer) {
+ synchronized (mLock) {
+ mObserver = observer;
+ try {
+ mObserver.setController(mController);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error setting session controller", re);
+ }
+ }
+ }
+
+ int getId() {
+ return mId;
+ }
+
+ /**
+ * Adds discovered printers. Adding an already added printer has no effect.
+ * Removed printers can be added again. You can call this method multiple
+ * times during printer discovery.
+ * <p>
+ * <strong>Note: </strong> Calling this method when the session is closed,
+ * which is if {@link #isClosed()} returns true, will throw an {@link
+ * IllegalStateException}.
+ * </p>
+ *
+ * @param printers The printers to add.
+ *
+ * @see #removePrinters(List)
+ * @see #updatePrinters(List)
+ * @see #isClosed()
+ */
+ public final void addPrinters(List<PrinterInfo> printers) {
+ final IPrinterDiscoverySessionObserver observer;
+ synchronized (mLock) {
+ throwIfClosedLocked();
+ observer = mObserver;
+ }
+ if (observer != null) {
+ try {
+ observer.onPrintersAdded(printers);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error adding printers", re);
+ }
+ }
+ }
+
+ /**
+ * Removes added printers. Removing an already removed or never added
+ * printer has no effect. Removed printers can be added again. You
+ * can call this method multiple times during printer discovery.
+ * <p>
+ * <strong>Note: </strong> Calling this method when the session is closed,
+ * which is if {@link #isClosed()} returns true, will throw an {@link
+ * IllegalStateException}.
+ * </p>
+ *
+ * @param printerIds The ids of the removed printers.
+ *
+ * @see #addPrinters(List)
+ * @see #updatePrinters(List)
+ * @see #isClosed()
+ */
+ public final void removePrinters(List<PrinterId> printerIds) {
+ final IPrinterDiscoverySessionObserver observer;
+ synchronized (mLock) {
+ throwIfClosedLocked();
+ observer = mObserver;
+ }
+ if (observer != null) {
+ try {
+ observer.onPrintersRemoved(printerIds);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error removing printers", re);
+ }
+ }
+ }
+
+ /**
+ * Updates added printers. Updating a printer that was not added or that
+ * was removed has no effect. You can call this method multiple times
+ * during printer discovery.
+ * <p>
+ * <strong>Note: </strong> Calling this method when the session is closed,
+ * which is if {@link #isClosed()} returns true, will throw an {@link
+ * IllegalStateException}.
+ * </p>
+ *
+ * @param printers The printers to update.
+ *
+ * @see #addPrinters(List)
+ * @see #removePrinters(List)
+ * @see #isClosed()
+ */
+ public final void updatePrinters(List<PrinterInfo> printers) {
+ final IPrinterDiscoverySessionObserver observer;
+ synchronized (mLock) {
+ throwIfClosedLocked();
+ observer = mObserver;
+ }
+ if (observer != null) {
+ try {
+ observer.onPrintersUpdated(printers);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error updating printers", re);
+ }
+ }
+ }
+
+ /**
+ * Callback notifying you that the session is open and you should start
+ * printer discovery. Discovered printers should be added via calling
+ * {@link #addPrinters(List)}. Added printers that disappeared should be
+ * removed via calling {@link #removePrinters(List)}. Added printers whose
+ * properties or capabilities changes should be updated via calling {@link
+ * #updatePrinters(List)}. When the session is closed you will receive a
+ * call to {@link #onClose()}.
+ * <p>
+ * During printer discovery all printers that are known to your print
+ * service have to be added. The system does not retain any printers from
+ * previous sessions.
+ * </p>
+ * <p>
+ * <strong>Note: </strong>You are also given a list of printers whose
+ * availability has to be checked first. For example, these printers could
+ * be the user's favorite ones, therefore they have to be verified first.
+ * </p>
+ *
+ * @see #onClose()
+ * @see #isClosed()
+ * @see #addPrinters(List)
+ * @see #removePrinters(List)
+ * @see #updatePrinters(List)
+ */
+ public abstract void onOpen(List<PrinterId> priorityList);
+
+ /**
+ * Callback notifying you that the session is closed and you should stop
+ * printer discovery. After the session is closed and any attempt to call
+ * any of its methods will throw an exception. Whether a session is closed
+ * can be checked by calling {@link #isClosed()}. Once the session is closed
+ * it will never be opened again.
+ *
+ * @see #onOpen(List)
+ * @see #isClosed()
+ * @see #addPrinters(List)
+ * @see #removePrinters(List)
+ * @see #updatePrinters(List)
+ */
+ public abstract void onClose();
+
+ /**
+ * Requests that you update a printer. You are responsible for updating
+ * the printer by also reporting its capabilities via calling {@link
+ * #updatePrinters(List)}.
+ * <p>
+ * <strong>Note: </strong> A printer can be initially added without its
+ * capabilities to avoid polling printers that the user will not select.
+ * However, after this method is called you are expected to update the
+ * printer <strong>including</strong> its capabilities. Otherwise, the
+ * printer will be ignored.
+ * <p>
+ * A scenario when you may be requested to update a printer is if the user
+ * selects it and the system has to present print options UI based on the
+ * printer's capabilities.
+ * </p>
+ *
+ * @param printerId The printer id.
+ *
+ * @see #updatePrinters(List)
+ * @see PrinterInfo.Builder#setCapabilities(PrinterCapabilitiesInfo)
+ * PrinterInfo.Builder.setCapabilities(PrinterCapabilitiesInfo)
+ */
+ public abstract void onRequestPrinterUpdate(PrinterId printerId);
+
+ /**
+ * Gets whether this session is closed.
+ *
+ * @return Whether the session is closed.
+ */
+ public final boolean isClosed() {
+ synchronized (mLock) {
+ return (mController == null && mObserver == null);
+ }
+ }
+
+ void close() {
+ synchronized (mLock) {
+ throwIfClosedLocked();
+ mController = null;
+ mObserver = null;
+ }
+ }
+
+ private void throwIfClosedLocked() {
+ if (isClosed()) {
+ throw new IllegalStateException("Session is closed");
+ }
+ }
+
+ private final class SessionHandler extends Handler {
+ public static final int MSG_OPEN = 1;
+ public static final int MSG_CLOSE = 2;
+ public static final int MSG_REQUEST_PRINTER_UPDATE = 3;
+
+ public SessionHandler(Looper looper) {
+ super(looper, null, true);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MSG_OPEN: {
+ List<PrinterId> priorityList = (List<PrinterId>) message.obj;
+ onOpen(priorityList);
+ } break;
+
+ case MSG_CLOSE: {
+ onClose();
+ close();
+ } break;
+
+ case MSG_REQUEST_PRINTER_UPDATE: {
+ PrinterId printerId = (PrinterId) message.obj;
+ onRequestPrinterUpdate(printerId);
+ } break;
+ }
+ }
+ }
+
+ private static final class PrinterDiscoverySessionController extends
+ IPrinterDiscoverySessionController.Stub {
+ private final WeakReference<PrinterDiscoverySession> mWeakSession;
+
+ public PrinterDiscoverySessionController(PrinterDiscoverySession session) {
+ mWeakSession = new WeakReference<PrinterDiscoverySession>(session);
+ }
+
+ @Override
+ public void open(List<PrinterId> priorityList) {
+ PrinterDiscoverySession session = mWeakSession.get();
+ if (session != null) {
+ session.mHandler.obtainMessage(SessionHandler.MSG_OPEN,
+ priorityList).sendToTarget();
+ }
+ }
+
+ @Override
+ public void close() {
+ PrinterDiscoverySession session = mWeakSession.get();
+ if (session != null) {
+ session.mHandler.sendEmptyMessage(SessionHandler.MSG_CLOSE);
+ }
+ }
+
+ @Override
+ public void requestPrinterUpdate(PrinterId printerId) {
+ PrinterDiscoverySession session = mWeakSession.get();
+ if (session != null) {
+ session.mHandler.obtainMessage(
+ SessionHandler.MSG_REQUEST_PRINTER_UPDATE,
+ printerId).sendToTarget();
+ }
+ }
+ };
+}
diff --git a/core/java/android/printservice/package.html b/core/java/android/printservice/package.html
new file mode 100644
index 0000000..6b0327c
--- /dev/null
+++ b/core/java/android/printservice/package.html
@@ -0,0 +1,24 @@
+<HTML>
+<BODY>
+<p>
+Provides classes for implementing print services. Print services are plug-in components
+that know how to talk to printers via some standard protocols. These services serve as a
+bridge between the system and the printers. Hence, the printer and print protocol specific
+implementation is factored out of the system and can by independently developed and updated.
+</p>
+<p>
+A print service implementation should extend {@link android.printservice.PrintService}
+and implement its abstract methods. Also the print service has to follow the contract for
+managing print {@link android.printservice.PrintJob}s to ensure correct interaction with
+the system and consistent user experience.
+<p/>
+<p>
+The system is responsible for starting and stopping a print service depending on whether
+there are active print jobs for the printers managed by the service. The print service
+should also perform printer discovery in a timely fashion to ensure good user experience.
+The interaction between the system and the print service during printer discovery is
+encapsulated by a {@link android.printservice.PrinterDiscoverySession} instance created
+by the print service when requested by the system.
+</p>
+</BODY>
+</HTML>