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 * <service android:name=".MyPrintService" 124 * android:permission="android.permission.BIND_PRINT_SERVICE"> 125 * <intent-filter> 126 * <action android:name="android.printservice.PrintService" /> 127 * </intent-filter> 128 * . . . 129 * </service> 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> <service android:name=".MyPrintService" 143 * android:permission="android.permission.BIND_PRINT_SERVICE"> 144 * <intent-filter> 145 * <action android:name="android.printservice.PrintService" /> 146 * </intent-filter> 147 * <meta-data android:name="android.printservice" android:resource="@xml/printservice" /> 148 * </service></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><{@link android.R.styleable#PrintService 153 * print-service}></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 * <{@link android.R.styleable#PrintService print-service}></code> tag. This is 177 * a sample XML file configuring a print service: 178 * <pre> <print-service 179 * android:vendor="SomeVendor" 180 * android:settingsActivity="foo.bar.MySettingsActivity" 181 * andorid:addPrintersActivity="foo.bar.MyAddPrintersActivity." 182 * . . . 183 * /></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