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
17package android.printservice;
18
19import android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.app.Activity;
22import android.app.PendingIntent;
23import android.app.Service;
24import android.content.ComponentName;
25import android.content.Context;
26import android.content.Intent;
27import android.os.Handler;
28import android.os.IBinder;
29import android.os.Looper;
30import android.os.Message;
31import android.os.RemoteException;
32import android.print.PrintJobInfo;
33import android.print.PrinterId;
34import android.print.PrinterInfo;
35import android.util.Log;
36
37import com.android.internal.util.Preconditions;
38
39import java.util.ArrayList;
40import java.util.Collections;
41import 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 */
161public 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
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     */
290    protected void onConnected() {
291        /* do nothing */
292    }
293
294    /**
295     * The system has disconnected from this service.
296     */
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     */
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     */
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     */
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     */
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     */
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
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
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