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 com.android.server.print; 18 19 import android.annotation.FloatRange; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.StringRes; 23 import android.content.ComponentName; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.ServiceConnection; 27 import android.content.pm.ParceledListSlice; 28 import android.graphics.drawable.Icon; 29 import android.os.Binder; 30 import android.os.Handler; 31 import android.os.IBinder; 32 import android.os.IBinder.DeathRecipient; 33 import android.os.Looper; 34 import android.os.Message; 35 import android.os.ParcelFileDescriptor; 36 import android.os.RemoteException; 37 import android.os.UserHandle; 38 import android.print.PrintJobId; 39 import android.print.PrintJobInfo; 40 import android.print.PrintManager; 41 import android.print.PrinterId; 42 import android.print.PrinterInfo; 43 import android.printservice.IPrintService; 44 import android.printservice.IPrintServiceClient; 45 import android.util.Slog; 46 47 import java.io.PrintWriter; 48 import java.lang.ref.WeakReference; 49 import java.util.ArrayList; 50 import java.util.List; 51 52 /** 53 * This class represents a remote print service. It abstracts away the binding 54 * and unbinding from the remote implementation. Clients can call methods of 55 * this class without worrying about when and how to bind/unbind. 56 */ 57 final class RemotePrintService implements DeathRecipient { 58 59 private static final String LOG_TAG = "RemotePrintService"; 60 61 private static final boolean DEBUG = false; 62 63 private final Context mContext; 64 65 private final ComponentName mComponentName; 66 67 private final Intent mIntent; 68 69 private final RemotePrintSpooler mSpooler; 70 71 private final PrintServiceCallbacks mCallbacks; 72 73 private final int mUserId; 74 75 private final List<Runnable> mPendingCommands = new ArrayList<Runnable>(); 76 77 private final ServiceConnection mServiceConnection = new RemoteServiceConneciton(); 78 79 private final RemotePrintServiceClient mPrintServiceClient; 80 81 private final Handler mHandler; 82 83 private IPrintService mPrintService; 84 85 private boolean mBinding; 86 87 private boolean mDestroyed; 88 89 private boolean mHasActivePrintJobs; 90 91 private boolean mHasPrinterDiscoverySession; 92 93 private boolean mServiceDied; 94 95 private List<PrinterId> mDiscoveryPriorityList; 96 97 private List<PrinterId> mTrackedPrinterList; 98 99 public static interface PrintServiceCallbacks { onPrintersAdded(List<PrinterInfo> printers)100 public void onPrintersAdded(List<PrinterInfo> printers); onPrintersRemoved(List<PrinterId> printerIds)101 public void onPrintersRemoved(List<PrinterId> printerIds); onServiceDied(RemotePrintService service)102 public void onServiceDied(RemotePrintService service); 103 104 /** 105 * Handle that a custom icon for a printer was loaded. 106 * 107 * @param printerId the id of the printer the icon belongs to 108 * @param icon the icon that was loaded 109 * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon() 110 */ onCustomPrinterIconLoaded(PrinterId printerId, Icon icon)111 public void onCustomPrinterIconLoaded(PrinterId printerId, Icon icon); 112 } 113 RemotePrintService(Context context, ComponentName componentName, int userId, RemotePrintSpooler spooler, PrintServiceCallbacks callbacks)114 public RemotePrintService(Context context, ComponentName componentName, int userId, 115 RemotePrintSpooler spooler, PrintServiceCallbacks callbacks) { 116 mContext = context; 117 mCallbacks = callbacks; 118 mComponentName = componentName; 119 mIntent = new Intent().setComponent(mComponentName); 120 mUserId = userId; 121 mSpooler = spooler; 122 mHandler = new MyHandler(context.getMainLooper()); 123 mPrintServiceClient = new RemotePrintServiceClient(this); 124 } 125 getComponentName()126 public ComponentName getComponentName() { 127 return mComponentName; 128 } 129 destroy()130 public void destroy() { 131 mHandler.sendEmptyMessage(MyHandler.MSG_DESTROY); 132 } 133 handleDestroy()134 private void handleDestroy() { 135 // Stop tracking printers. 136 stopTrackingAllPrinters(); 137 138 // Stop printer discovery. 139 if (mDiscoveryPriorityList != null) { 140 handleStopPrinterDiscovery(); 141 } 142 143 // Destroy the discovery session. 144 if (mHasPrinterDiscoverySession) { 145 handleDestroyPrinterDiscoverySession(); 146 } 147 148 // Unbind. 149 ensureUnbound(); 150 151 // Done 152 mDestroyed = true; 153 } 154 155 @Override binderDied()156 public void binderDied() { 157 mHandler.sendEmptyMessage(MyHandler.MSG_BINDER_DIED); 158 } 159 handleBinderDied()160 private void handleBinderDied() { 161 if (mPrintService != null) { 162 mPrintService.asBinder().unlinkToDeath(this, 0); 163 } 164 165 mPrintService = null; 166 mServiceDied = true; 167 mCallbacks.onServiceDied(this); 168 } 169 onAllPrintJobsHandled()170 public void onAllPrintJobsHandled() { 171 mHandler.sendEmptyMessage(MyHandler.MSG_ON_ALL_PRINT_JOBS_HANDLED); 172 } 173 handleOnAllPrintJobsHandled()174 private void handleOnAllPrintJobsHandled() { 175 mHasActivePrintJobs = false; 176 if (!isBound()) { 177 // The service is dead and neither has active jobs nor discovery 178 // session, so ensure we are unbound since the service has no work. 179 if (mServiceDied && !mHasPrinterDiscoverySession) { 180 ensureUnbound(); 181 return; 182 } 183 ensureBound(); 184 mPendingCommands.add(new Runnable() { 185 @Override 186 public void run() { 187 handleOnAllPrintJobsHandled(); 188 } 189 }); 190 } else { 191 if (DEBUG) { 192 Slog.i(LOG_TAG, "[user: " + mUserId + "] onAllPrintJobsHandled()"); 193 } 194 // If the service has a printer discovery session 195 // created we should not disconnect from it just yet. 196 if (!mHasPrinterDiscoverySession) { 197 ensureUnbound(); 198 } 199 } 200 } 201 onRequestCancelPrintJob(PrintJobInfo printJob)202 public void onRequestCancelPrintJob(PrintJobInfo printJob) { 203 mHandler.obtainMessage(MyHandler.MSG_ON_REQUEST_CANCEL_PRINT_JOB, 204 printJob).sendToTarget(); 205 } 206 handleRequestCancelPrintJob(final PrintJobInfo printJob)207 private void handleRequestCancelPrintJob(final PrintJobInfo printJob) { 208 if (!isBound()) { 209 ensureBound(); 210 mPendingCommands.add(new Runnable() { 211 @Override 212 public void run() { 213 handleRequestCancelPrintJob(printJob); 214 } 215 }); 216 } else { 217 if (DEBUG) { 218 Slog.i(LOG_TAG, "[user: " + mUserId + "] requestCancelPrintJob()"); 219 } 220 try { 221 mPrintService.requestCancelPrintJob(printJob); 222 } catch (RemoteException re) { 223 Slog.e(LOG_TAG, "Error canceling a pring job.", re); 224 } 225 } 226 } 227 onPrintJobQueued(PrintJobInfo printJob)228 public void onPrintJobQueued(PrintJobInfo printJob) { 229 mHandler.obtainMessage(MyHandler.MSG_ON_PRINT_JOB_QUEUED, 230 printJob).sendToTarget(); 231 } 232 handleOnPrintJobQueued(final PrintJobInfo printJob)233 private void handleOnPrintJobQueued(final PrintJobInfo printJob) { 234 mHasActivePrintJobs = true; 235 if (!isBound()) { 236 ensureBound(); 237 mPendingCommands.add(new Runnable() { 238 @Override 239 public void run() { 240 handleOnPrintJobQueued(printJob); 241 } 242 }); 243 } else { 244 if (DEBUG) { 245 Slog.i(LOG_TAG, "[user: " + mUserId + "] onPrintJobQueued()"); 246 } 247 try { 248 mPrintService.onPrintJobQueued(printJob); 249 } catch (RemoteException re) { 250 Slog.e(LOG_TAG, "Error announcing queued pring job.", re); 251 } 252 } 253 } 254 createPrinterDiscoverySession()255 public void createPrinterDiscoverySession() { 256 mHandler.sendEmptyMessage(MyHandler.MSG_CREATE_PRINTER_DISCOVERY_SESSION); 257 } 258 handleCreatePrinterDiscoverySession()259 private void handleCreatePrinterDiscoverySession() { 260 mHasPrinterDiscoverySession = true; 261 if (!isBound()) { 262 ensureBound(); 263 mPendingCommands.add(new Runnable() { 264 @Override 265 public void run() { 266 handleCreatePrinterDiscoverySession(); 267 } 268 }); 269 } else { 270 if (DEBUG) { 271 Slog.i(LOG_TAG, "[user: " + mUserId + "] createPrinterDiscoverySession()"); 272 } 273 try { 274 mPrintService.createPrinterDiscoverySession(); 275 } catch (RemoteException re) { 276 Slog.e(LOG_TAG, "Error creating printer discovery session.", re); 277 } 278 } 279 } 280 destroyPrinterDiscoverySession()281 public void destroyPrinterDiscoverySession() { 282 mHandler.sendEmptyMessage(MyHandler.MSG_DESTROY_PRINTER_DISCOVERY_SESSION); 283 } 284 handleDestroyPrinterDiscoverySession()285 private void handleDestroyPrinterDiscoverySession() { 286 mHasPrinterDiscoverySession = false; 287 if (!isBound()) { 288 // The service is dead and neither has active jobs nor discovery 289 // session, so ensure we are unbound since the service has no work. 290 if (mServiceDied && !mHasActivePrintJobs) { 291 ensureUnbound(); 292 return; 293 } 294 ensureBound(); 295 mPendingCommands.add(new Runnable() { 296 @Override 297 public void run() { 298 handleDestroyPrinterDiscoverySession(); 299 } 300 }); 301 } else { 302 if (DEBUG) { 303 Slog.i(LOG_TAG, "[user: " + mUserId + "] destroyPrinterDiscoverySession()"); 304 } 305 try { 306 mPrintService.destroyPrinterDiscoverySession(); 307 } catch (RemoteException re) { 308 Slog.e(LOG_TAG, "Error destroying printer dicovery session.", re); 309 } 310 // If the service has no print jobs and no active discovery 311 // session anymore we should disconnect from it. 312 if (!mHasActivePrintJobs) { 313 ensureUnbound(); 314 } 315 } 316 } 317 startPrinterDiscovery(List<PrinterId> priorityList)318 public void startPrinterDiscovery(List<PrinterId> priorityList) { 319 mHandler.obtainMessage(MyHandler.MSG_START_PRINTER_DISCOVERY, 320 priorityList).sendToTarget(); 321 } 322 handleStartPrinterDiscovery(final List<PrinterId> priorityList)323 private void handleStartPrinterDiscovery(final List<PrinterId> priorityList) { 324 // Take a note that we are doing discovery. 325 mDiscoveryPriorityList = new ArrayList<PrinterId>(); 326 if (priorityList != null) { 327 mDiscoveryPriorityList.addAll(priorityList); 328 } 329 if (!isBound()) { 330 ensureBound(); 331 mPendingCommands.add(new Runnable() { 332 @Override 333 public void run() { 334 handleStartPrinterDiscovery(priorityList); 335 } 336 }); 337 } else { 338 if (DEBUG) { 339 Slog.i(LOG_TAG, "[user: " + mUserId + "] startPrinterDiscovery()"); 340 } 341 try { 342 mPrintService.startPrinterDiscovery(priorityList); 343 } catch (RemoteException re) { 344 Slog.e(LOG_TAG, "Error starting printer dicovery.", re); 345 } 346 } 347 } 348 stopPrinterDiscovery()349 public void stopPrinterDiscovery() { 350 mHandler.sendEmptyMessage(MyHandler.MSG_STOP_PRINTER_DISCOVERY); 351 } 352 handleStopPrinterDiscovery()353 private void handleStopPrinterDiscovery() { 354 // We are not doing discovery anymore. 355 mDiscoveryPriorityList = null; 356 if (!isBound()) { 357 ensureBound(); 358 mPendingCommands.add(new Runnable() { 359 @Override 360 public void run() { 361 handleStopPrinterDiscovery(); 362 } 363 }); 364 } else { 365 if (DEBUG) { 366 Slog.i(LOG_TAG, "[user: " + mUserId + "] stopPrinterDiscovery()"); 367 } 368 369 // Stop tracking printers. 370 stopTrackingAllPrinters(); 371 372 try { 373 mPrintService.stopPrinterDiscovery(); 374 } catch (RemoteException re) { 375 Slog.e(LOG_TAG, "Error stopping printer discovery.", re); 376 } 377 } 378 } 379 validatePrinters(List<PrinterId> printerIds)380 public void validatePrinters(List<PrinterId> printerIds) { 381 mHandler.obtainMessage(MyHandler.MSG_VALIDATE_PRINTERS, 382 printerIds).sendToTarget(); 383 } 384 handleValidatePrinters(final List<PrinterId> printerIds)385 private void handleValidatePrinters(final List<PrinterId> printerIds) { 386 if (!isBound()) { 387 ensureBound(); 388 mPendingCommands.add(new Runnable() { 389 @Override 390 public void run() { 391 handleValidatePrinters(printerIds); 392 } 393 }); 394 } else { 395 if (DEBUG) { 396 Slog.i(LOG_TAG, "[user: " + mUserId + "] validatePrinters()"); 397 } 398 try { 399 mPrintService.validatePrinters(printerIds); 400 } catch (RemoteException re) { 401 Slog.e(LOG_TAG, "Error requesting printers validation.", re); 402 } 403 } 404 } 405 startPrinterStateTracking(@onNull PrinterId printerId)406 public void startPrinterStateTracking(@NonNull PrinterId printerId) { 407 mHandler.obtainMessage(MyHandler.MSG_START_PRINTER_STATE_TRACKING, 408 printerId).sendToTarget(); 409 } 410 411 /** 412 * Queue a request for a custom printer icon for a printer. 413 * 414 * @param printerId the id of the printer the icon should be loaded for 415 * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon 416 */ requestCustomPrinterIcon(@onNull PrinterId printerId)417 public void requestCustomPrinterIcon(@NonNull PrinterId printerId) { 418 mHandler.obtainMessage(MyHandler.MSG_REQUEST_CUSTOM_PRINTER_ICON, 419 printerId).sendToTarget(); 420 } 421 422 /** 423 * Request a custom printer icon for a printer. 424 * 425 * @param printerId the id of the printer the icon should be loaded for 426 * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon 427 */ handleRequestCustomPrinterIcon(@onNull PrinterId printerId)428 private void handleRequestCustomPrinterIcon(@NonNull PrinterId printerId) { 429 if (!isBound()) { 430 ensureBound(); 431 mPendingCommands.add(() -> handleRequestCustomPrinterIcon(printerId)); 432 } else { 433 if (DEBUG) { 434 Slog.i(LOG_TAG, "[user: " + mUserId + "] requestCustomPrinterIcon()"); 435 } 436 437 try { 438 mPrintService.requestCustomPrinterIcon(printerId); 439 } catch (RemoteException re) { 440 Slog.e(LOG_TAG, "Error requesting icon for " + printerId, re); 441 } 442 } 443 } 444 handleStartPrinterStateTracking(final @NonNull PrinterId printerId)445 private void handleStartPrinterStateTracking(final @NonNull PrinterId printerId) { 446 // Take a note we are tracking the printer. 447 if (mTrackedPrinterList == null) { 448 mTrackedPrinterList = new ArrayList<PrinterId>(); 449 } 450 mTrackedPrinterList.add(printerId); 451 if (!isBound()) { 452 ensureBound(); 453 mPendingCommands.add(new Runnable() { 454 @Override 455 public void run() { 456 handleStartPrinterStateTracking(printerId); 457 } 458 }); 459 } else { 460 if (DEBUG) { 461 Slog.i(LOG_TAG, "[user: " + mUserId + "] startPrinterTracking()"); 462 } 463 try { 464 mPrintService.startPrinterStateTracking(printerId); 465 } catch (RemoteException re) { 466 Slog.e(LOG_TAG, "Error requesting start printer tracking.", re); 467 } 468 } 469 } 470 stopPrinterStateTracking(PrinterId printerId)471 public void stopPrinterStateTracking(PrinterId printerId) { 472 mHandler.obtainMessage(MyHandler.MSG_STOP_PRINTER_STATE_TRACKING, 473 printerId).sendToTarget(); 474 } 475 handleStopPrinterStateTracking(final PrinterId printerId)476 private void handleStopPrinterStateTracking(final PrinterId printerId) { 477 // We are no longer tracking the printer. 478 if (mTrackedPrinterList == null || !mTrackedPrinterList.remove(printerId)) { 479 return; 480 } 481 if (mTrackedPrinterList.isEmpty()) { 482 mTrackedPrinterList = null; 483 } 484 if (!isBound()) { 485 ensureBound(); 486 mPendingCommands.add(new Runnable() { 487 @Override 488 public void run() { 489 handleStopPrinterStateTracking(printerId); 490 } 491 }); 492 } else { 493 if (DEBUG) { 494 Slog.i(LOG_TAG, "[user: " + mUserId + "] stopPrinterTracking()"); 495 } 496 try { 497 mPrintService.stopPrinterStateTracking(printerId); 498 } catch (RemoteException re) { 499 Slog.e(LOG_TAG, "Error requesting stop printer tracking.", re); 500 } 501 } 502 } 503 stopTrackingAllPrinters()504 private void stopTrackingAllPrinters() { 505 if (mTrackedPrinterList == null) { 506 return; 507 } 508 final int trackedPrinterCount = mTrackedPrinterList.size(); 509 for (int i = trackedPrinterCount - 1; i >= 0; i--) { 510 PrinterId printerId = mTrackedPrinterList.get(i); 511 if (printerId.getServiceName().equals(mComponentName)) { 512 handleStopPrinterStateTracking(printerId); 513 } 514 } 515 } 516 dump(PrintWriter pw, String prefix)517 public void dump(PrintWriter pw, String prefix) { 518 String tab = " "; 519 pw.append(prefix).append("service:").println(); 520 pw.append(prefix).append(tab).append("componentName=") 521 .append(mComponentName.flattenToString()).println(); 522 pw.append(prefix).append(tab).append("destroyed=") 523 .append(String.valueOf(mDestroyed)).println(); 524 pw.append(prefix).append(tab).append("bound=") 525 .append(String.valueOf(isBound())).println(); 526 pw.append(prefix).append(tab).append("hasDicoverySession=") 527 .append(String.valueOf(mHasPrinterDiscoverySession)).println(); 528 pw.append(prefix).append(tab).append("hasActivePrintJobs=") 529 .append(String.valueOf(mHasActivePrintJobs)).println(); 530 pw.append(prefix).append(tab).append("isDiscoveringPrinters=") 531 .append(String.valueOf(mDiscoveryPriorityList != null)).println(); 532 pw.append(prefix).append(tab).append("trackedPrinters=") 533 .append((mTrackedPrinterList != null) ? mTrackedPrinterList.toString() : "null"); 534 } 535 isBound()536 private boolean isBound() { 537 return mPrintService != null; 538 } 539 ensureBound()540 private void ensureBound() { 541 if (isBound() || mBinding) { 542 return; 543 } 544 if (DEBUG) { 545 Slog.i(LOG_TAG, "[user: " + mUserId + "] ensureBound()"); 546 } 547 mBinding = true; 548 549 boolean wasBound = mContext.bindServiceAsUser(mIntent, mServiceConnection, 550 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, 551 new UserHandle(mUserId)); 552 553 if (!wasBound) { 554 if (DEBUG) { 555 Slog.i(LOG_TAG, "[user: " + mUserId + "] could not bind to " + mIntent); 556 } 557 mBinding = false; 558 559 if (!mServiceDied) { 560 handleBinderDied(); 561 } 562 } 563 } 564 ensureUnbound()565 private void ensureUnbound() { 566 if (!isBound() && !mBinding) { 567 return; 568 } 569 if (DEBUG) { 570 Slog.i(LOG_TAG, "[user: " + mUserId + "] ensureUnbound()"); 571 } 572 mBinding = false; 573 mPendingCommands.clear(); 574 mHasActivePrintJobs = false; 575 mHasPrinterDiscoverySession = false; 576 mDiscoveryPriorityList = null; 577 mTrackedPrinterList = null; 578 if (isBound()) { 579 try { 580 mPrintService.setClient(null); 581 } catch (RemoteException re) { 582 /* ignore */ 583 } 584 mPrintService.asBinder().unlinkToDeath(this, 0); 585 mPrintService = null; 586 mContext.unbindService(mServiceConnection); 587 } 588 } 589 590 private class RemoteServiceConneciton implements ServiceConnection { 591 @Override onServiceConnected(ComponentName name, IBinder service)592 public void onServiceConnected(ComponentName name, IBinder service) { 593 if (mDestroyed || !mBinding) { 594 mContext.unbindService(mServiceConnection); 595 return; 596 } 597 mBinding = false; 598 mPrintService = IPrintService.Stub.asInterface(service); 599 try { 600 service.linkToDeath(RemotePrintService.this, 0); 601 } catch (RemoteException re) { 602 handleBinderDied(); 603 return; 604 } 605 try { 606 mPrintService.setClient(mPrintServiceClient); 607 } catch (RemoteException re) { 608 Slog.e(LOG_TAG, "Error setting client for: " + service, re); 609 handleBinderDied(); 610 return; 611 } 612 // If the service died and there is a discovery session, recreate it. 613 if (mServiceDied && mHasPrinterDiscoverySession) { 614 handleCreatePrinterDiscoverySession(); 615 } 616 // If the service died and there is discovery started, restart it. 617 if (mServiceDied && mDiscoveryPriorityList != null) { 618 handleStartPrinterDiscovery(mDiscoveryPriorityList); 619 } 620 // If the service died and printers were tracked, start tracking. 621 if (mServiceDied && mTrackedPrinterList != null) { 622 final int trackedPrinterCount = mTrackedPrinterList.size(); 623 for (int i = 0; i < trackedPrinterCount; i++) { 624 handleStartPrinterStateTracking(mTrackedPrinterList.get(i)); 625 } 626 } 627 // Finally, do all the pending work. 628 while (!mPendingCommands.isEmpty()) { 629 Runnable pendingCommand = mPendingCommands.remove(0); 630 pendingCommand.run(); 631 } 632 // We did a best effort to get to the last state if we crashed. 633 // If we do not have print jobs and no discovery is in progress, 634 // then no need to be bound. 635 if (!mHasPrinterDiscoverySession && !mHasActivePrintJobs) { 636 ensureUnbound(); 637 } 638 mServiceDied = false; 639 } 640 641 @Override onServiceDisconnected(ComponentName name)642 public void onServiceDisconnected(ComponentName name) { 643 mBinding = true; 644 } 645 } 646 647 private final class MyHandler extends Handler { 648 public static final int MSG_CREATE_PRINTER_DISCOVERY_SESSION = 1; 649 public static final int MSG_DESTROY_PRINTER_DISCOVERY_SESSION = 2; 650 public static final int MSG_START_PRINTER_DISCOVERY = 3; 651 public static final int MSG_STOP_PRINTER_DISCOVERY = 4; 652 public static final int MSG_VALIDATE_PRINTERS = 5; 653 public static final int MSG_START_PRINTER_STATE_TRACKING = 6; 654 public static final int MSG_STOP_PRINTER_STATE_TRACKING = 7; 655 public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 8; 656 public static final int MSG_ON_REQUEST_CANCEL_PRINT_JOB = 9; 657 public static final int MSG_ON_PRINT_JOB_QUEUED = 10; 658 public static final int MSG_DESTROY = 11; 659 public static final int MSG_BINDER_DIED = 12; 660 public static final int MSG_REQUEST_CUSTOM_PRINTER_ICON = 13; 661 MyHandler(Looper looper)662 public MyHandler(Looper looper) { 663 super(looper, null, false); 664 } 665 666 @Override 667 @SuppressWarnings("unchecked") handleMessage(Message message)668 public void handleMessage(Message message) { 669 if (mDestroyed) { 670 Slog.w(LOG_TAG, "Not handling " + message + " as service for " + mComponentName 671 + " is already destroyed"); 672 return; 673 } 674 switch (message.what) { 675 case MSG_CREATE_PRINTER_DISCOVERY_SESSION: { 676 handleCreatePrinterDiscoverySession(); 677 } break; 678 679 case MSG_DESTROY_PRINTER_DISCOVERY_SESSION: { 680 handleDestroyPrinterDiscoverySession(); 681 } break; 682 683 case MSG_START_PRINTER_DISCOVERY: { 684 List<PrinterId> priorityList = (ArrayList<PrinterId>) message.obj; 685 handleStartPrinterDiscovery(priorityList); 686 } break; 687 688 case MSG_STOP_PRINTER_DISCOVERY: { 689 handleStopPrinterDiscovery(); 690 } break; 691 692 case MSG_VALIDATE_PRINTERS: { 693 List<PrinterId> printerIds = (List<PrinterId>) message.obj; 694 handleValidatePrinters(printerIds); 695 } break; 696 697 case MSG_START_PRINTER_STATE_TRACKING: { 698 PrinterId printerId = (PrinterId) message.obj; 699 handleStartPrinterStateTracking(printerId); 700 } break; 701 702 case MSG_STOP_PRINTER_STATE_TRACKING: { 703 PrinterId printerId = (PrinterId) message.obj; 704 handleStopPrinterStateTracking(printerId); 705 } break; 706 707 case MSG_ON_ALL_PRINT_JOBS_HANDLED: { 708 handleOnAllPrintJobsHandled(); 709 } break; 710 711 case MSG_ON_REQUEST_CANCEL_PRINT_JOB: { 712 PrintJobInfo printJob = (PrintJobInfo) message.obj; 713 handleRequestCancelPrintJob(printJob); 714 } break; 715 716 case MSG_ON_PRINT_JOB_QUEUED: { 717 PrintJobInfo printJob = (PrintJobInfo) message.obj; 718 handleOnPrintJobQueued(printJob); 719 } break; 720 721 case MSG_DESTROY: { 722 handleDestroy(); 723 } break; 724 725 case MSG_BINDER_DIED: { 726 handleBinderDied(); 727 } break; 728 729 case MSG_REQUEST_CUSTOM_PRINTER_ICON: { 730 PrinterId printerId = (PrinterId) message.obj; 731 handleRequestCustomPrinterIcon(printerId); 732 } break; 733 } 734 } 735 } 736 737 private static final class RemotePrintServiceClient extends IPrintServiceClient.Stub { 738 private final WeakReference<RemotePrintService> mWeakService; 739 RemotePrintServiceClient(RemotePrintService service)740 public RemotePrintServiceClient(RemotePrintService service) { 741 mWeakService = new WeakReference<RemotePrintService>(service); 742 } 743 744 @Override getPrintJobInfos()745 public List<PrintJobInfo> getPrintJobInfos() { 746 RemotePrintService service = mWeakService.get(); 747 if (service != null) { 748 final long identity = Binder.clearCallingIdentity(); 749 try { 750 return service.mSpooler.getPrintJobInfos(service.mComponentName, 751 PrintJobInfo.STATE_ANY_SCHEDULED, PrintManager.APP_ID_ANY); 752 } finally { 753 Binder.restoreCallingIdentity(identity); 754 } 755 } 756 return null; 757 } 758 759 @Override getPrintJobInfo(PrintJobId printJobId)760 public PrintJobInfo getPrintJobInfo(PrintJobId printJobId) { 761 RemotePrintService service = mWeakService.get(); 762 if (service != null) { 763 final long identity = Binder.clearCallingIdentity(); 764 try { 765 return service.mSpooler.getPrintJobInfo(printJobId, 766 PrintManager.APP_ID_ANY); 767 } finally { 768 Binder.restoreCallingIdentity(identity); 769 } 770 } 771 return null; 772 } 773 774 @Override setPrintJobState(PrintJobId printJobId, int state, String error)775 public boolean setPrintJobState(PrintJobId printJobId, int state, String error) { 776 RemotePrintService service = mWeakService.get(); 777 if (service != null) { 778 final long identity = Binder.clearCallingIdentity(); 779 try { 780 return service.mSpooler.setPrintJobState(printJobId, state, error); 781 } finally { 782 Binder.restoreCallingIdentity(identity); 783 } 784 } 785 return false; 786 } 787 788 @Override setPrintJobTag(PrintJobId printJobId, String tag)789 public boolean setPrintJobTag(PrintJobId printJobId, String tag) { 790 RemotePrintService service = mWeakService.get(); 791 if (service != null) { 792 final long identity = Binder.clearCallingIdentity(); 793 try { 794 return service.mSpooler.setPrintJobTag(printJobId, tag); 795 } finally { 796 Binder.restoreCallingIdentity(identity); 797 } 798 } 799 return false; 800 } 801 802 @Override writePrintJobData(ParcelFileDescriptor fd, PrintJobId printJobId)803 public void writePrintJobData(ParcelFileDescriptor fd, PrintJobId printJobId) { 804 RemotePrintService service = mWeakService.get(); 805 if (service != null) { 806 final long identity = Binder.clearCallingIdentity(); 807 try { 808 service.mSpooler.writePrintJobData(fd, printJobId); 809 } finally { 810 Binder.restoreCallingIdentity(identity); 811 } 812 } 813 } 814 815 @Override setProgress(@onNull PrintJobId printJobId, @FloatRange(from=0.0, to=1.0) float progress)816 public void setProgress(@NonNull PrintJobId printJobId, 817 @FloatRange(from=0.0, to=1.0) float progress) { 818 RemotePrintService service = mWeakService.get(); 819 if (service != null) { 820 final long identity = Binder.clearCallingIdentity(); 821 try { 822 service.mSpooler.setProgress(printJobId, progress); 823 } finally { 824 Binder.restoreCallingIdentity(identity); 825 } 826 } 827 } 828 829 @Override setStatus(@onNull PrintJobId printJobId, @Nullable CharSequence status)830 public void setStatus(@NonNull PrintJobId printJobId, @Nullable CharSequence status) { 831 RemotePrintService service = mWeakService.get(); 832 if (service != null) { 833 final long identity = Binder.clearCallingIdentity(); 834 try { 835 service.mSpooler.setStatus(printJobId, status); 836 } finally { 837 Binder.restoreCallingIdentity(identity); 838 } 839 } 840 } 841 842 @Override setStatusRes(@onNull PrintJobId printJobId, @StringRes int status, @NonNull CharSequence appPackageName)843 public void setStatusRes(@NonNull PrintJobId printJobId, @StringRes int status, 844 @NonNull CharSequence appPackageName) { 845 RemotePrintService service = mWeakService.get(); 846 if (service != null) { 847 final long identity = Binder.clearCallingIdentity(); 848 try { 849 service.mSpooler.setStatus(printJobId, status, appPackageName); 850 } finally { 851 Binder.restoreCallingIdentity(identity); 852 } 853 } 854 } 855 856 @Override 857 @SuppressWarnings({"rawtypes", "unchecked"}) onPrintersAdded(ParceledListSlice printers)858 public void onPrintersAdded(ParceledListSlice printers) { 859 RemotePrintService service = mWeakService.get(); 860 if (service != null) { 861 List<PrinterInfo> addedPrinters = (List<PrinterInfo>) printers.getList(); 862 throwIfPrinterIdsForPrinterInfoTampered(service.mComponentName, addedPrinters); 863 final long identity = Binder.clearCallingIdentity(); 864 try { 865 service.mCallbacks.onPrintersAdded(addedPrinters); 866 } finally { 867 Binder.restoreCallingIdentity(identity); 868 } 869 } 870 } 871 872 @Override 873 @SuppressWarnings({"rawtypes", "unchecked"}) onPrintersRemoved(ParceledListSlice printerIds)874 public void onPrintersRemoved(ParceledListSlice printerIds) { 875 RemotePrintService service = mWeakService.get(); 876 if (service != null) { 877 List<PrinterId> removedPrinterIds = (List<PrinterId>) printerIds.getList(); 878 throwIfPrinterIdsTampered(service.mComponentName, removedPrinterIds); 879 final long identity = Binder.clearCallingIdentity(); 880 try { 881 service.mCallbacks.onPrintersRemoved(removedPrinterIds); 882 } finally { 883 Binder.restoreCallingIdentity(identity); 884 } 885 } 886 } 887 throwIfPrinterIdsForPrinterInfoTampered(ComponentName serviceName, List<PrinterInfo> printerInfos)888 private void throwIfPrinterIdsForPrinterInfoTampered(ComponentName serviceName, 889 List<PrinterInfo> printerInfos) { 890 final int printerInfoCount = printerInfos.size(); 891 for (int i = 0; i < printerInfoCount; i++) { 892 PrinterId printerId = printerInfos.get(i).getId(); 893 throwIfPrinterIdTampered(serviceName, printerId); 894 } 895 } 896 throwIfPrinterIdsTampered(ComponentName serviceName, List<PrinterId> printerIds)897 private void throwIfPrinterIdsTampered(ComponentName serviceName, 898 List<PrinterId> printerIds) { 899 final int printerIdCount = printerIds.size(); 900 for (int i = 0; i < printerIdCount; i++) { 901 PrinterId printerId = printerIds.get(i); 902 throwIfPrinterIdTampered(serviceName, printerId); 903 } 904 } 905 throwIfPrinterIdTampered(ComponentName serviceName, PrinterId printerId)906 private void throwIfPrinterIdTampered(ComponentName serviceName, PrinterId printerId) { 907 if (printerId == null || !printerId.getServiceName().equals(serviceName)) { 908 throw new IllegalArgumentException("Invalid printer id: " + printerId); 909 } 910 } 911 912 @Override onCustomPrinterIconLoaded(PrinterId printerId, Icon icon)913 public void onCustomPrinterIconLoaded(PrinterId printerId, Icon icon) 914 throws RemoteException { 915 RemotePrintService service = mWeakService.get(); 916 if (service != null) { 917 final long identity = Binder.clearCallingIdentity(); 918 try { 919 service.mCallbacks.onCustomPrinterIconLoaded(printerId, icon); 920 } finally { 921 Binder.restoreCallingIdentity(identity); 922 } 923 } 924 } 925 } 926 } 927