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