Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2013 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package android.printservice; |
| 18 | |
| 19 | import android.app.Service; |
| 20 | import android.content.ComponentName; |
| 21 | import android.content.Context; |
| 22 | import android.content.Intent; |
| 23 | import android.os.Handler; |
| 24 | import android.os.IBinder; |
| 25 | import android.os.Looper; |
| 26 | import android.os.Message; |
| 27 | import android.os.RemoteException; |
| 28 | import android.print.PrintJobInfo; |
| 29 | import android.print.PrinterId; |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 30 | import android.util.Log; |
| 31 | |
| 32 | import java.util.ArrayList; |
| 33 | import java.util.Collections; |
| 34 | import java.util.List; |
| 35 | |
| 36 | /** |
| 37 | * <p> |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 38 | * This is the base class for implementing print services. A print service knows |
| 39 | * how to discover and interact one or more printers via one or more protocols. |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 40 | * </p> |
| 41 | * <h3>Printer discovery</h3> |
| 42 | * <p> |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 43 | * A print service is responsible for discovering printers, adding discovered printers, |
| 44 | * removing added printers, and updating added printers. When the system is interested |
| 45 | * in printers managed by your service it will call {@link |
| 46 | * #onCreatePrinterDiscoverySession()} from which you must return a new {@link |
| 47 | * PrinterDiscoverySession} instance. The returned session encapsulates the interaction |
| 48 | * between the system and your service during printer discovery. For description of this |
| 49 | * interaction refer to the documentation for {@link PrinterDiscoverySession}. |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 50 | * </p> |
| 51 | * <p> |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 52 | * For every printer discovery session all printers have to be added since system does |
| 53 | * not retain printers across sessions. Hence, each printer known to this print service |
| 54 | * should be added only once during a discovery session. Only an already added printer |
| 55 | * can be removed or updated. Removed printers can be added again. |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 56 | * </p> |
| 57 | * <h3>Print jobs</h3> |
| 58 | * <p> |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 59 | * When a new print job targeted to a printer managed by this print service is is queued, |
| 60 | * i.e. ready for processing by the print service, you will receive a call to {@link |
| 61 | * #onPrintJobQueued(PrintJob)}. The print service may handle the print job immediately |
| 62 | * or schedule that for an appropriate time in the future. The list of all active print |
| 63 | * jobs for this service is obtained by calling {@link #getActivePrintJobs()}. Active |
| 64 | * print jobs are ones that are queued or started. |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 65 | * </p> |
| 66 | * <p> |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 67 | * A print service is responsible for setting a print job's state as appropriate |
| 68 | * while processing it. Initially, a print job is queued, i.e. {@link PrintJob#isQueued() |
| 69 | * PrintJob.isQueued()} returns true, which means that the document to be printed is |
| 70 | * spooled by the system and the print service can begin processing it. You can obtain |
| 71 | * the printed document by calling {@link PrintJob#getDocument() PrintJob.getDocument()} |
| 72 | * whose data is accessed via {@link PrintDocument#getData() PrintDocument.getData()}. |
| 73 | * After the print service starts printing the data it should set the print job's |
| 74 | * state to started by calling {@link PrintJob#start()} after which |
| 75 | * {@link PrintJob#isStarted() PrintJob.isStarted()} would return true. Upon successful |
| 76 | * completion, the print job should be marked as completed by calling {@link |
| 77 | * PrintJob#complete() PrintJob.complete()} after which {@link PrintJob#isCompleted() |
| 78 | * PrintJob.isCompleted()} would return true. In case of a failure, the print job should |
Svetoslav Ganov | 3bf7976 | 2013-08-20 16:40:13 -0700 | [diff] [blame] | 79 | * be marked as failed by calling {@link PrintJob#fail(String) PrintJob.fail( |
| 80 | * String)} after which {@link PrintJob#isFailed() PrintJob.isFailed()} would |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 81 | * return true. |
Svetoslav Ganov | a002715 | 2013-06-25 14:59:53 -0700 | [diff] [blame] | 82 | * </p> |
| 83 | * <p> |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 84 | * If a print job is queued or started and the user requests to cancel it, the print |
| 85 | * service will receive a call to {@link #onRequestCancelPrintJob(PrintJob)} which |
| 86 | * requests from the service to do best effort in canceling the job. In case the job |
| 87 | * is successfully canceled, its state has to be marked as cancelled by calling {@link |
| 88 | * PrintJob#cancel() PrintJob.cancel()} after which {@link PrintJob#isCancelled() |
| 89 | * PrintJob.isCacnelled()} would return true. |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 90 | * </p> |
| 91 | * <h3>Lifecycle</h3> |
| 92 | * <p> |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 93 | * The lifecycle of a print service is managed exclusively by the system and follows |
| 94 | * the established service lifecycle. Additionally, starting or stopping a print service |
| 95 | * is triggered exclusively by an explicit user action through enabling or disabling it |
| 96 | * in the device settings. After the system binds to a print service, it calls {@link |
| 97 | * #onConnected()}. This method can be overriden by clients to perform post binding setup. |
| 98 | * Also after the system unbinds from a print service, it calls {@link #onDisconnected()}. |
| 99 | * This method can be overriden by clients to perform post unbinding cleanup. Your should |
| 100 | * not do any work after the system disconnected from your print service since the |
| 101 | * service can be killed at any time to reclaim memory. The system will not disconnect |
| 102 | * from a print service if there are active print jobs for the printers managed by it. |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 103 | * </p> |
| 104 | * <h3>Declaration</h3> |
| 105 | * <p> |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 106 | * A print service is declared as any other service in an AndroidManifest.xml but it must |
| 107 | * also specify that it handles the {@link android.content.Intent} with action {@link |
| 108 | * #SERVICE_INTERFACE android.printservice.PrintService}. Failure to declare this intent |
| 109 | * will cause the system to ignore the print service. Additionally, a print service must |
| 110 | * request the {@link android.Manifest.permission#BIND_PRINT_SERVICE |
| 111 | * android.permission.BIND_PRINT_SERVICE} permission to ensure that only the system can |
| 112 | * bind to it. Failure to declare this intent will cause the system to ignore the print |
| 113 | * service. Following is an example declaration: |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 114 | * </p> |
| 115 | * <pre> |
| 116 | * <service android:name=".MyPrintService" |
| 117 | * android:permission="android.permission.BIND_PRINT_SERVICE"> |
| 118 | * <intent-filter> |
| 119 | * <action android:name="android.printservice.PrintService" /> |
| 120 | * </intent-filter> |
| 121 | * . . . |
| 122 | * </service> |
| 123 | * </pre> |
| 124 | * <h3>Configuration</h3> |
| 125 | * <p> |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 126 | * A print service can be configured by specifying an optional settings activity which |
| 127 | * exposes service specific settings, an optional add printers activity which is used for |
| 128 | * manual addition of printers, vendor name ,etc. It is a responsibility of the system |
| 129 | * to launch the settings and add printers activities when appropriate. |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 130 | * </p> |
| 131 | * <p> |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 132 | * A print service is configured by providing a {@link #SERVICE_META_DATA meta-data} |
| 133 | * entry in the manifest when declaring the service. A service declaration with a meta-data |
| 134 | * tag is presented below: |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 135 | * <pre> <service android:name=".MyPrintService" |
| 136 | * android:permission="android.permission.BIND_PRINT_SERVICE"> |
| 137 | * <intent-filter> |
| 138 | * <action android:name="android.printservice.PrintService" /> |
| 139 | * </intent-filter> |
| 140 | * <meta-data android:name="android.printservice" android:resource="@xml/printservice" /> |
| 141 | * </service></pre> |
| 142 | * </p> |
| 143 | * <p> |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 144 | * For more details for how to configure your print service via the meta-data refer to |
| 145 | * {@link #SERVICE_META_DATA} and <code><{@link android.R.styleable#PrintService |
| 146 | * print-service}></code>. |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 147 | * </p> |
Svetoslav | 269403b | 2013-08-14 17:31:04 -0700 | [diff] [blame] | 148 | * <p> |
| 149 | * <strong>Note: </strong> All callbacks in this class are executed on the main |
| 150 | * application thread. You should also invoke any method of this class on the main |
| 151 | * application thread. |
| 152 | * </p> |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 153 | */ |
| 154 | public abstract class PrintService extends Service { |
| 155 | |
Svetoslav Ganov | a002715 | 2013-06-25 14:59:53 -0700 | [diff] [blame] | 156 | private static final String LOG_TAG = "PrintService"; |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 157 | |
Svetoslav Ganov | b669917 | 2013-09-07 22:42:47 -0700 | [diff] [blame] | 158 | private static final boolean DEBUG = false; |
| 159 | |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 160 | /** |
| 161 | * The {@link Intent} action that must be declared as handled by a service |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 162 | * in its manifest for the system to recognize it as a print service. |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 163 | */ |
| 164 | public static final String SERVICE_INTERFACE = "android.printservice.PrintService"; |
| 165 | |
| 166 | /** |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 167 | * Name under which a {@link PrintService} component publishes additional information |
| 168 | * about itself. This meta-data must reference a XML resource containing a <code> |
| 169 | * <{@link android.R.styleable#PrintService print-service}></code> tag. This is |
| 170 | * a sample XML file configuring a print service: |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 171 | * <pre> <print-service |
Svetoslav Ganov | a002715 | 2013-06-25 14:59:53 -0700 | [diff] [blame] | 172 | * android:vendor="SomeVendor" |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 173 | * android:settingsActivity="foo.bar.MySettingsActivity" |
| 174 | * andorid:addPrintersActivity="foo.bar.MyAddPrintersActivity." |
| 175 | * . . . |
| 176 | * /></pre> |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 177 | * <p> |
| 178 | * For detailed configuration options that can be specified via the meta-data |
| 179 | * refer to {@link android.R.styleable#PrintService android.R.styleable.PrintService}. |
| 180 | * </p> |
Svetoslav Ganov | 860f8a6 | 2013-09-14 00:59:03 -0700 | [diff] [blame] | 181 | * <p> |
| 182 | * If you declare a settings or add a printers activity, they have to be exported, |
| 183 | * by setting the {@link android.R.attr#exported} activity attribute to <code>true |
| 184 | * </code>. Also in case you want only the system to be able to start any of these |
| 185 | * activities you can specify that they request the android.permission |
| 186 | * .START_PRINT_SERVICE_CONFIG_ACTIVITY permission by setting the |
| 187 | * {@link android.R.attr#permission} activity attribute. |
| 188 | * </p> |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 189 | */ |
| 190 | public static final String SERVICE_META_DATA = "android.printservice"; |
| 191 | |
Svetoslav | 1c43fce | 2013-10-16 10:57:48 -0700 | [diff] [blame] | 192 | /** |
| 193 | * If you declared an optional activity with advanced print options via the |
Narayan Kamath | 0d9ada9 | 2015-02-06 13:10:53 +0000 | [diff] [blame] | 194 | * {@link android.R.attr#advancedPrintOptionsActivity advancedPrintOptionsActivity} |
Svetoslav | 1c43fce | 2013-10-16 10:57:48 -0700 | [diff] [blame] | 195 | * attribute, this extra is used to pass in the currently constructed {@link |
| 196 | * PrintJobInfo} to your activity allowing you to modify it. After you are |
| 197 | * done, you must return the modified {@link PrintJobInfo} via the same extra. |
| 198 | * <p> |
| 199 | * You cannot modify the passed in {@link PrintJobInfo} directly, rather you |
| 200 | * should build another one using the {@link PrintJobInfo.Builder} class. You |
| 201 | * can specify any standard properties and add advanced, printer specific, |
| 202 | * ones via {@link PrintJobInfo.Builder#putAdvancedOption(String, String) |
Svetoslav | a798c0a | 2014-05-15 10:47:19 -0700 | [diff] [blame] | 203 | * PrintJobInfo.Builder.putAdvancedOption(String, String)} and {@link |
Svetoslav | 1c43fce | 2013-10-16 10:57:48 -0700 | [diff] [blame] | 204 | * PrintJobInfo.Builder#putAdvancedOption(String, int) |
Svetoslav | a798c0a | 2014-05-15 10:47:19 -0700 | [diff] [blame] | 205 | * PrintJobInfo.Builder.putAdvancedOption(String, int)}. The advanced options |
Svetoslav | 1c43fce | 2013-10-16 10:57:48 -0700 | [diff] [blame] | 206 | * are not interpreted by the system, they will not be visible to applications, |
| 207 | * and can only be accessed by your print service via {@link |
| 208 | * PrintJob#getAdvancedStringOption(String) PrintJob.getAdvancedStringOption(String)} |
| 209 | * and {@link PrintJob#getAdvancedIntOption(String) PrintJob.getAdvancedIntOption(String)}. |
| 210 | * </p> |
Svetoslav | b4fda13 | 2013-10-25 18:57:43 -0700 | [diff] [blame] | 211 | * <p> |
| 212 | * If the advanced print options activity offers changes to the standard print |
| 213 | * options, you can get the current {@link android.print.PrinterInfo} using the |
Svetoslav | a798c0a | 2014-05-15 10:47:19 -0700 | [diff] [blame] | 214 | * {@link #EXTRA_PRINTER_INFO} extra which will allow you to present the user |
| 215 | * with UI options supported by the current printer. For example, if the current |
| 216 | * printer does not support a given media size, you should not offer it in the |
| 217 | * advanced print options UI. |
Svetoslav | b4fda13 | 2013-10-25 18:57:43 -0700 | [diff] [blame] | 218 | * </p> |
Svetoslav | a798c0a | 2014-05-15 10:47:19 -0700 | [diff] [blame] | 219 | * |
| 220 | * @see #EXTRA_PRINTER_INFO |
Svetoslav | 1c43fce | 2013-10-16 10:57:48 -0700 | [diff] [blame] | 221 | */ |
| 222 | public static final String EXTRA_PRINT_JOB_INFO = "android.intent.extra.print.PRINT_JOB_INFO"; |
| 223 | |
Svetoslav | a798c0a | 2014-05-15 10:47:19 -0700 | [diff] [blame] | 224 | /** |
| 225 | * If you declared an optional activity with advanced print options via the |
Narayan Kamath | 0d9ada9 | 2015-02-06 13:10:53 +0000 | [diff] [blame] | 226 | * {@link android.R.attr#advancedPrintOptionsActivity advancedPrintOptionsActivity} |
Svetoslav | a798c0a | 2014-05-15 10:47:19 -0700 | [diff] [blame] | 227 | * attribute, this extra is used to pass in the currently selected printer's |
| 228 | * {@link android.print.PrinterInfo} to your activity allowing you to inspect it. |
| 229 | * |
| 230 | * @see #EXTRA_PRINT_JOB_INFO |
| 231 | */ |
Svet Ganov | 8fbd0dc | 2014-07-14 10:49:40 -0700 | [diff] [blame] | 232 | public static final String EXTRA_PRINTER_INFO = "android.intent.extra.print.EXTRA_PRINTER_INFO"; |
Svetoslav | a798c0a | 2014-05-15 10:47:19 -0700 | [diff] [blame] | 233 | |
Svet Ganov | 5772b5c | 2015-06-11 02:46:45 -0700 | [diff] [blame] | 234 | /** |
| 235 | * If you declared an optional activity with advanced print options via the |
| 236 | * {@link android.R.attr#advancedPrintOptionsActivity advancedPrintOptionsActivity} |
| 237 | * attribute, this extra is used to pass in the meta-data for the currently printed |
| 238 | * document as a {@link android.print.PrintDocumentInfo} to your activity allowing |
| 239 | * you to inspect it. |
| 240 | * |
| 241 | * @see #EXTRA_PRINT_JOB_INFO |
| 242 | * @see #EXTRA_PRINTER_INFO |
| 243 | */ |
| 244 | public static final String EXTRA_PRINT_DOCUMENT_INFO = |
| 245 | "android.printservice.extra.PRINT_DOCUMENT_INFO"; |
| 246 | |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 247 | private Handler mHandler; |
| 248 | |
| 249 | private IPrintServiceClient mClient; |
| 250 | |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 251 | private int mLastSessionId = -1; |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 252 | |
Svetoslav | 269403b | 2013-08-14 17:31:04 -0700 | [diff] [blame] | 253 | private PrinterDiscoverySession mDiscoverySession; |
| 254 | |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 255 | @Override |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 256 | protected final void attachBaseContext(Context base) { |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 257 | super.attachBaseContext(base); |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 258 | mHandler = new ServiceHandler(base.getMainLooper()); |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 259 | } |
| 260 | |
| 261 | /** |
| 262 | * The system has connected to this service. |
| 263 | */ |
| 264 | protected void onConnected() { |
| 265 | /* do nothing */ |
| 266 | } |
| 267 | |
| 268 | /** |
| 269 | * The system has disconnected from this service. |
| 270 | */ |
| 271 | protected void onDisconnected() { |
| 272 | /* do nothing */ |
| 273 | } |
| 274 | |
| 275 | /** |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 276 | * Callback asking you to create a new {@link PrinterDiscoverySession}. |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 277 | * |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 278 | * @see PrinterDiscoverySession |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 279 | */ |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 280 | protected abstract PrinterDiscoverySession onCreatePrinterDiscoverySession(); |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 281 | |
| 282 | /** |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 283 | * Called when cancellation of a print job is requested. The service |
Svetoslav Ganov | a002715 | 2013-06-25 14:59:53 -0700 | [diff] [blame] | 284 | * should do best effort to fulfill the request. After the cancellation |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 285 | * is performed, the print job should be marked as cancelled state by |
Svetoslav Ganov | a002715 | 2013-06-25 14:59:53 -0700 | [diff] [blame] | 286 | * calling {@link PrintJob#cancel()}. |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 287 | * |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 288 | * @param printJob The print job to cancel. |
| 289 | * |
| 290 | * @see PrintJob#cancel() PrintJob.cancel() |
| 291 | * @see PrintJob#isCancelled() PrintJob.isCancelled() |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 292 | */ |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 293 | protected abstract void onRequestCancelPrintJob(PrintJob printJob); |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 294 | |
| 295 | /** |
| 296 | * Called when there is a queued print job for one of the printers |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 297 | * managed by this print service. |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 298 | * |
| 299 | * @param printJob The new queued print job. |
| 300 | * |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 301 | * @see PrintJob#isQueued() PrintJob.isQueued() |
| 302 | * @see #getActivePrintJobs() |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 303 | */ |
| 304 | protected abstract void onPrintJobQueued(PrintJob printJob); |
| 305 | |
| 306 | /** |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 307 | * Gets the active print jobs for the printers managed by this service. |
| 308 | * Active print jobs are ones that are not in a final state, i.e. whose |
| 309 | * state is queued or started. |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 310 | * |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 311 | * @return The active print jobs. |
| 312 | * |
| 313 | * @see PrintJob#isQueued() PrintJob.isQueued() |
| 314 | * @see PrintJob#isStarted() PrintJob.isStarted() |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 315 | */ |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 316 | public final List<PrintJob> getActivePrintJobs() { |
Svetoslav | 269403b | 2013-08-14 17:31:04 -0700 | [diff] [blame] | 317 | throwIfNotCalledOnMainThread(); |
| 318 | if (mClient == null) { |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 319 | return Collections.emptyList(); |
| 320 | } |
Svetoslav Ganov | a002715 | 2013-06-25 14:59:53 -0700 | [diff] [blame] | 321 | try { |
| 322 | List<PrintJob> printJobs = null; |
Svetoslav | 269403b | 2013-08-14 17:31:04 -0700 | [diff] [blame] | 323 | List<PrintJobInfo> printJobInfos = mClient.getPrintJobInfos(); |
Svetoslav Ganov | a002715 | 2013-06-25 14:59:53 -0700 | [diff] [blame] | 324 | if (printJobInfos != null) { |
| 325 | final int printJobInfoCount = printJobInfos.size(); |
| 326 | printJobs = new ArrayList<PrintJob>(printJobInfoCount); |
| 327 | for (int i = 0; i < printJobInfoCount; i++) { |
Svetoslav | 269403b | 2013-08-14 17:31:04 -0700 | [diff] [blame] | 328 | printJobs.add(new PrintJob(printJobInfos.get(i), mClient)); |
Svetoslav Ganov | a002715 | 2013-06-25 14:59:53 -0700 | [diff] [blame] | 329 | } |
| 330 | } |
| 331 | if (printJobs != null) { |
| 332 | return printJobs; |
| 333 | } |
| 334 | } catch (RemoteException re) { |
| 335 | Log.e(LOG_TAG, "Error calling getPrintJobs()", re); |
| 336 | } |
| 337 | return Collections.emptyList(); |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 338 | } |
| 339 | |
| 340 | /** |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 341 | * Generates a global printer id given the printer's locally unique one. |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 342 | * |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 343 | * @param localId A locally unique id in the context of your print service. |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 344 | * @return Global printer id. |
| 345 | */ |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 346 | public final PrinterId generatePrinterId(String localId) { |
Svetoslav | 269403b | 2013-08-14 17:31:04 -0700 | [diff] [blame] | 347 | throwIfNotCalledOnMainThread(); |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 348 | return new PrinterId(new ComponentName(getPackageName(), |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 349 | getClass().getName()), localId); |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 350 | } |
| 351 | |
Svetoslav | 269403b | 2013-08-14 17:31:04 -0700 | [diff] [blame] | 352 | static void throwIfNotCalledOnMainThread() { |
| 353 | if (!Looper.getMainLooper().isCurrentThread()) { |
| 354 | throw new IllegalAccessError("must be called from the main thread"); |
| 355 | } |
| 356 | } |
| 357 | |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 358 | @Override |
| 359 | public final IBinder onBind(Intent intent) { |
| 360 | return new IPrintService.Stub() { |
| 361 | @Override |
Svetoslav | 269403b | 2013-08-14 17:31:04 -0700 | [diff] [blame] | 362 | public void createPrinterDiscoverySession() { |
| 363 | mHandler.sendEmptyMessage(ServiceHandler.MSG_CREATE_PRINTER_DISCOVERY_SESSION); |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 364 | } |
| 365 | |
| 366 | @Override |
Svetoslav | 269403b | 2013-08-14 17:31:04 -0700 | [diff] [blame] | 367 | public void destroyPrinterDiscoverySession() { |
| 368 | mHandler.sendEmptyMessage(ServiceHandler.MSG_DESTROY_PRINTER_DISCOVERY_SESSION); |
| 369 | } |
| 370 | |
| 371 | public void startPrinterDiscovery(List<PrinterId> priorityList) { |
| 372 | mHandler.obtainMessage(ServiceHandler.MSG_START_PRINTER_DISCOVERY, |
| 373 | priorityList).sendToTarget(); |
| 374 | } |
| 375 | |
| 376 | @Override |
| 377 | public void stopPrinterDiscovery() { |
| 378 | mHandler.sendEmptyMessage(ServiceHandler.MSG_STOP_PRINTER_DISCOVERY); |
| 379 | } |
| 380 | |
| 381 | @Override |
Svetoslav Ganov | d26d489 | 2013-08-28 14:37:54 -0700 | [diff] [blame] | 382 | public void validatePrinters(List<PrinterId> printerIds) { |
| 383 | mHandler.obtainMessage(ServiceHandler.MSG_VALIDATE_PRINTERS, |
| 384 | printerIds).sendToTarget(); |
| 385 | } |
| 386 | |
| 387 | @Override |
| 388 | public void startPrinterStateTracking(PrinterId printerId) { |
| 389 | mHandler.obtainMessage(ServiceHandler.MSG_START_PRINTER_STATE_TRACKING, |
| 390 | printerId).sendToTarget(); |
| 391 | } |
| 392 | |
| 393 | @Override |
| 394 | public void stopPrinterStateTracking(PrinterId printerId) { |
| 395 | mHandler.obtainMessage(ServiceHandler.MSG_STOP_PRINTER_STATE_TRACKING, |
Svetoslav | 269403b | 2013-08-14 17:31:04 -0700 | [diff] [blame] | 396 | printerId).sendToTarget(); |
| 397 | } |
| 398 | |
| 399 | @Override |
| 400 | public void setClient(IPrintServiceClient client) { |
jangwon.lee | eae626f | 2014-02-06 11:13:24 +0900 | [diff] [blame] | 401 | mHandler.obtainMessage(ServiceHandler.MSG_SET_CLIENT, client) |
Svetoslav | 269403b | 2013-08-14 17:31:04 -0700 | [diff] [blame] | 402 | .sendToTarget(); |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 403 | } |
| 404 | |
| 405 | @Override |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 406 | public void requestCancelPrintJob(PrintJobInfo printJobInfo) { |
| 407 | mHandler.obtainMessage(ServiceHandler.MSG_ON_REQUEST_CANCEL_PRINTJOB, |
Svetoslav Ganov | a002715 | 2013-06-25 14:59:53 -0700 | [diff] [blame] | 408 | printJobInfo).sendToTarget(); |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 409 | } |
| 410 | |
| 411 | @Override |
Svetoslav Ganov | a002715 | 2013-06-25 14:59:53 -0700 | [diff] [blame] | 412 | public void onPrintJobQueued(PrintJobInfo printJobInfo) { |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 413 | mHandler.obtainMessage(ServiceHandler.MSG_ON_PRINTJOB_QUEUED, |
Svetoslav Ganov | a002715 | 2013-06-25 14:59:53 -0700 | [diff] [blame] | 414 | printJobInfo).sendToTarget(); |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 415 | } |
| 416 | }; |
| 417 | } |
| 418 | |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 419 | private final class ServiceHandler extends Handler { |
Svetoslav | 269403b | 2013-08-14 17:31:04 -0700 | [diff] [blame] | 420 | public static final int MSG_CREATE_PRINTER_DISCOVERY_SESSION = 1; |
| 421 | public static final int MSG_DESTROY_PRINTER_DISCOVERY_SESSION = 2; |
| 422 | public static final int MSG_START_PRINTER_DISCOVERY = 3; |
| 423 | public static final int MSG_STOP_PRINTER_DISCOVERY = 4; |
Svetoslav Ganov | d26d489 | 2013-08-28 14:37:54 -0700 | [diff] [blame] | 424 | public static final int MSG_VALIDATE_PRINTERS = 5; |
| 425 | public static final int MSG_START_PRINTER_STATE_TRACKING = 6; |
| 426 | public static final int MSG_STOP_PRINTER_STATE_TRACKING = 7; |
| 427 | public static final int MSG_ON_PRINTJOB_QUEUED = 8; |
| 428 | public static final int MSG_ON_REQUEST_CANCEL_PRINTJOB = 9; |
jangwon.lee | eae626f | 2014-02-06 11:13:24 +0900 | [diff] [blame] | 429 | public static final int MSG_SET_CLIENT = 10; |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 430 | |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 431 | public ServiceHandler(Looper looper) { |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 432 | super(looper, null, true); |
| 433 | } |
| 434 | |
| 435 | @Override |
Svetoslav | 269403b | 2013-08-14 17:31:04 -0700 | [diff] [blame] | 436 | @SuppressWarnings("unchecked") |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 437 | public void handleMessage(Message message) { |
| 438 | final int action = message.what; |
| 439 | switch (action) { |
Svetoslav | 269403b | 2013-08-14 17:31:04 -0700 | [diff] [blame] | 440 | case MSG_CREATE_PRINTER_DISCOVERY_SESSION: { |
Svetoslav | b5f1806 | 2013-09-23 18:48:34 -0700 | [diff] [blame] | 441 | if (DEBUG) { |
| 442 | Log.i(LOG_TAG, "MSG_CREATE_PRINTER_DISCOVERY_SESSION " |
| 443 | + getPackageName()); |
| 444 | } |
Svetoslav Ganov | 798bed6 | 2013-08-11 12:29:39 -0700 | [diff] [blame] | 445 | PrinterDiscoverySession session = onCreatePrinterDiscoverySession(); |
| 446 | if (session == null) { |
| 447 | throw new NullPointerException("session cannot be null"); |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 448 | } |
Svetoslav | 269403b | 2013-08-14 17:31:04 -0700 | [diff] [blame] | 449 | if (session.getId() == mLastSessionId) { |
| 450 | throw new IllegalStateException("cannot reuse session instances"); |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 451 | } |
Svetoslav | 269403b | 2013-08-14 17:31:04 -0700 | [diff] [blame] | 452 | mDiscoverySession = session; |
| 453 | mLastSessionId = session.getId(); |
| 454 | session.setObserver(mClient); |
| 455 | } break; |
| 456 | |
| 457 | case MSG_DESTROY_PRINTER_DISCOVERY_SESSION: { |
Svetoslav | b5f1806 | 2013-09-23 18:48:34 -0700 | [diff] [blame] | 458 | if (DEBUG) { |
| 459 | Log.i(LOG_TAG, "MSG_DESTROY_PRINTER_DISCOVERY_SESSION " |
| 460 | + getPackageName()); |
| 461 | } |
Svetoslav | 269403b | 2013-08-14 17:31:04 -0700 | [diff] [blame] | 462 | if (mDiscoverySession != null) { |
| 463 | mDiscoverySession.destroy(); |
| 464 | mDiscoverySession = null; |
| 465 | } |
| 466 | } break; |
| 467 | |
| 468 | case MSG_START_PRINTER_DISCOVERY: { |
Svetoslav | b5f1806 | 2013-09-23 18:48:34 -0700 | [diff] [blame] | 469 | if (DEBUG) { |
| 470 | Log.i(LOG_TAG, "MSG_START_PRINTER_DISCOVERY " |
| 471 | + getPackageName()); |
| 472 | } |
Svetoslav | 269403b | 2013-08-14 17:31:04 -0700 | [diff] [blame] | 473 | if (mDiscoverySession != null) { |
| 474 | List<PrinterId> priorityList = (ArrayList<PrinterId>) message.obj; |
| 475 | mDiscoverySession.startPrinterDiscovery(priorityList); |
| 476 | } |
| 477 | } break; |
| 478 | |
| 479 | case MSG_STOP_PRINTER_DISCOVERY: { |
Svetoslav | b5f1806 | 2013-09-23 18:48:34 -0700 | [diff] [blame] | 480 | if (DEBUG) { |
| 481 | Log.i(LOG_TAG, "MSG_STOP_PRINTER_DISCOVERY " |
| 482 | + getPackageName()); |
| 483 | } |
Svetoslav | 269403b | 2013-08-14 17:31:04 -0700 | [diff] [blame] | 484 | if (mDiscoverySession != null) { |
| 485 | mDiscoverySession.stopPrinterDiscovery(); |
| 486 | } |
| 487 | } break; |
| 488 | |
Svetoslav Ganov | d26d489 | 2013-08-28 14:37:54 -0700 | [diff] [blame] | 489 | case MSG_VALIDATE_PRINTERS: { |
Svetoslav | b5f1806 | 2013-09-23 18:48:34 -0700 | [diff] [blame] | 490 | if (DEBUG) { |
| 491 | Log.i(LOG_TAG, "MSG_VALIDATE_PRINTERS " |
| 492 | + getPackageName()); |
| 493 | } |
Svetoslav Ganov | d26d489 | 2013-08-28 14:37:54 -0700 | [diff] [blame] | 494 | if (mDiscoverySession != null) { |
| 495 | List<PrinterId> printerIds = (List<PrinterId>) message.obj; |
| 496 | mDiscoverySession.validatePrinters(printerIds); |
| 497 | } |
| 498 | } break; |
| 499 | |
| 500 | case MSG_START_PRINTER_STATE_TRACKING: { |
Svetoslav | b5f1806 | 2013-09-23 18:48:34 -0700 | [diff] [blame] | 501 | if (DEBUG) { |
| 502 | Log.i(LOG_TAG, "MSG_START_PRINTER_STATE_TRACKING " |
| 503 | + getPackageName()); |
| 504 | } |
Svetoslav | 269403b | 2013-08-14 17:31:04 -0700 | [diff] [blame] | 505 | if (mDiscoverySession != null) { |
| 506 | PrinterId printerId = (PrinterId) message.obj; |
Svetoslav Ganov | d26d489 | 2013-08-28 14:37:54 -0700 | [diff] [blame] | 507 | mDiscoverySession.startPrinterStateTracking(printerId); |
| 508 | } |
| 509 | } break; |
| 510 | |
| 511 | case MSG_STOP_PRINTER_STATE_TRACKING: { |
Svetoslav | b5f1806 | 2013-09-23 18:48:34 -0700 | [diff] [blame] | 512 | if (DEBUG) { |
| 513 | Log.i(LOG_TAG, "MSG_STOP_PRINTER_STATE_TRACKING " |
| 514 | + getPackageName()); |
| 515 | } |
Svetoslav Ganov | d26d489 | 2013-08-28 14:37:54 -0700 | [diff] [blame] | 516 | if (mDiscoverySession != null) { |
| 517 | PrinterId printerId = (PrinterId) message.obj; |
| 518 | mDiscoverySession.stopPrinterStateTracking(printerId); |
Svetoslav | 269403b | 2013-08-14 17:31:04 -0700 | [diff] [blame] | 519 | } |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 520 | } break; |
| 521 | |
Svetoslav Ganov | 55b409a | 2013-07-31 17:25:13 -0700 | [diff] [blame] | 522 | case MSG_ON_REQUEST_CANCEL_PRINTJOB: { |
Svetoslav | b5f1806 | 2013-09-23 18:48:34 -0700 | [diff] [blame] | 523 | if (DEBUG) { |
| 524 | Log.i(LOG_TAG, "MSG_ON_REQUEST_CANCEL_PRINTJOB " |
| 525 | + getPackageName()); |
| 526 | } |
Svetoslav Ganov | a002715 | 2013-06-25 14:59:53 -0700 | [diff] [blame] | 527 | PrintJobInfo printJobInfo = (PrintJobInfo) message.obj; |
| 528 | onRequestCancelPrintJob(new PrintJob(printJobInfo, mClient)); |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 529 | } break; |
| 530 | |
Svetoslav Ganov | 55b409a | 2013-07-31 17:25:13 -0700 | [diff] [blame] | 531 | case MSG_ON_PRINTJOB_QUEUED: { |
Svetoslav | b5f1806 | 2013-09-23 18:48:34 -0700 | [diff] [blame] | 532 | if (DEBUG) { |
| 533 | Log.i(LOG_TAG, "MSG_ON_PRINTJOB_QUEUED " |
| 534 | + getPackageName()); |
| 535 | } |
Svetoslav Ganov | a002715 | 2013-06-25 14:59:53 -0700 | [diff] [blame] | 536 | PrintJobInfo printJobInfo = (PrintJobInfo) message.obj; |
Svetoslav Ganov | b669917 | 2013-09-07 22:42:47 -0700 | [diff] [blame] | 537 | if (DEBUG) { |
| 538 | Log.i(LOG_TAG, "Queued: " + printJobInfo); |
| 539 | } |
Svetoslav Ganov | a002715 | 2013-06-25 14:59:53 -0700 | [diff] [blame] | 540 | onPrintJobQueued(new PrintJob(printJobInfo, mClient)); |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 541 | } break; |
| 542 | |
jangwon.lee | eae626f | 2014-02-06 11:13:24 +0900 | [diff] [blame] | 543 | case MSG_SET_CLIENT: { |
Svetoslav | b5f1806 | 2013-09-23 18:48:34 -0700 | [diff] [blame] | 544 | if (DEBUG) { |
jangwon.lee | eae626f | 2014-02-06 11:13:24 +0900 | [diff] [blame] | 545 | Log.i(LOG_TAG, "MSG_SET_CLIENT " |
Svetoslav | b5f1806 | 2013-09-23 18:48:34 -0700 | [diff] [blame] | 546 | + getPackageName()); |
| 547 | } |
Svetoslav | 269403b | 2013-08-14 17:31:04 -0700 | [diff] [blame] | 548 | mClient = (IPrintServiceClient) message.obj; |
| 549 | if (mClient != null) { |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 550 | onConnected(); |
| 551 | } else { |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 552 | onDisconnected(); |
Svetoslav | 269403b | 2013-08-14 17:31:04 -0700 | [diff] [blame] | 553 | } |
Svetoslav Ganov | 4b9a4d1 | 2013-06-11 15:20:06 -0700 | [diff] [blame] | 554 | } break; |
| 555 | |
| 556 | default: { |
| 557 | throw new IllegalArgumentException("Unknown message: " + action); |
| 558 | } |
| 559 | } |
| 560 | } |
| 561 | } |
| 562 | } |