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