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 { onPrintersAdded(List<PrinterInfo> printers)106 public void onPrintersAdded(List<PrinterInfo> printers); onPrintersRemoved(List<PrinterId> printerIds)107 public void onPrintersRemoved(List<PrinterId> printerIds); onServiceDied(RemotePrintService service)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 */ onCustomPrinterIconLoaded(PrinterId printerId, Icon icon)117 public void onCustomPrinterIconLoaded(PrinterId printerId, Icon icon); 118 } 119 RemotePrintService(Context context, ComponentName componentName, int userId, RemotePrintSpooler spooler, PrintServiceCallbacks callbacks)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 getComponentName()131 public ComponentName getComponentName() { 132 return mComponentName; 133 } 134 destroy()135 public void destroy() { 136 Handler.getMain().sendMessage(obtainMessage( 137 RemotePrintService::handleDestroy, this)); 138 } 139 handleDestroy()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 binderDied()162 public void binderDied() { 163 Handler.getMain().sendMessage(obtainMessage( 164 RemotePrintService::handleBinderDied, this)); 165 } 166 handleBinderDied()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 onAllPrintJobsHandled()177 public void onAllPrintJobsHandled() { 178 Handler.getMain().sendMessage(obtainMessage( 179 RemotePrintService::handleOnAllPrintJobsHandled, this)); 180 } 181 handleOnAllPrintJobsHandled()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 onRequestCancelPrintJob(PrintJobInfo printJob)210 public void onRequestCancelPrintJob(PrintJobInfo printJob) { 211 Handler.getMain().sendMessage(obtainMessage( 212 RemotePrintService::handleRequestCancelPrintJob, this, printJob)); 213 } 214 handleRequestCancelPrintJob(final PrintJobInfo printJob)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 onPrintJobQueued(PrintJobInfo printJob)236 public void onPrintJobQueued(PrintJobInfo printJob) { 237 Handler.getMain().sendMessage(obtainMessage( 238 RemotePrintService::handleOnPrintJobQueued, this, printJob)); 239 } 240 handleOnPrintJobQueued(final PrintJobInfo printJob)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 createPrinterDiscoverySession()263 public void createPrinterDiscoverySession() { 264 Handler.getMain().sendMessage(obtainMessage( 265 RemotePrintService::handleCreatePrinterDiscoverySession, this)); 266 } 267 handleCreatePrinterDiscoverySession()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 destroyPrinterDiscoverySession()290 public void destroyPrinterDiscoverySession() { 291 Handler.getMain().sendMessage(obtainMessage( 292 RemotePrintService::handleDestroyPrinterDiscoverySession, this)); 293 } 294 handleDestroyPrinterDiscoverySession()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 startPrinterDiscovery(List<PrinterId> priorityList)328 public void startPrinterDiscovery(List<PrinterId> priorityList) { 329 Handler.getMain().sendMessage(obtainMessage( 330 RemotePrintService::handleStartPrinterDiscovery, this, priorityList)); 331 } 332 handleStartPrinterDiscovery(final List<PrinterId> priorityList)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 stopPrinterDiscovery()359 public void stopPrinterDiscovery() { 360 Handler.getMain().sendMessage(obtainMessage( 361 RemotePrintService::handleStopPrinterDiscovery, this)); 362 } 363 handleStopPrinterDiscovery()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 validatePrinters(List<PrinterId> printerIds)391 public void validatePrinters(List<PrinterId> printerIds) { 392 Handler.getMain().sendMessage(obtainMessage( 393 RemotePrintService::handleValidatePrinters, this, printerIds)); 394 } 395 handleValidatePrinters(final List<PrinterId> printerIds)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 startPrinterStateTracking(@onNull PrinterId printerId)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 */ requestCustomPrinterIcon(@onNull PrinterId printerId)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 */ handleRequestCustomPrinterIcon(@onNull PrinterId printerId)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 handleStartPrinterStateTracking(final @NonNull PrinterId printerId)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 stopPrinterStateTracking(PrinterId printerId)485 public void stopPrinterStateTracking(PrinterId printerId) { 486 Handler.getMain().sendMessage(obtainMessage( 487 RemotePrintService::handleStopPrinterStateTracking, this, printerId)); 488 } 489 handleStopPrinterStateTracking(final PrinterId printerId)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 stopTrackingAllPrinters()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 dump(@onNull DualDumpOutputStream proto)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 isBound()560 private boolean isBound() { 561 return mPrintService != null; 562 } 563 ensureBound()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_INCLUDE_CAPABILITIES | Context.BIND_ALLOW_INSTANT, 576 new UserHandle(mUserId)); 577 578 if (!wasBound) { 579 if (DEBUG) { 580 Slog.i(LOG_TAG, "[user: " + mUserId + "] could not bind to " + mIntent); 581 } 582 mBinding = false; 583 584 if (!mServiceDied) { 585 handleBinderDied(); 586 } 587 } 588 } 589 ensureUnbound()590 private void ensureUnbound() { 591 if (!isBound() && !mBinding) { 592 return; 593 } 594 if (DEBUG) { 595 Slog.i(LOG_TAG, "[user: " + mUserId + "] ensureUnbound()"); 596 } 597 mBinding = false; 598 mPendingCommands.clear(); 599 mHasActivePrintJobs = false; 600 mHasPrinterDiscoverySession = false; 601 mDiscoveryPriorityList = null; 602 603 synchronized (mLock) { 604 mTrackedPrinterList = null; 605 } 606 607 if (isBound()) { 608 try { 609 mPrintService.setClient(null); 610 } catch (RemoteException re) { 611 /* ignore */ 612 } 613 mPrintService.asBinder().unlinkToDeath(this, 0); 614 mPrintService = null; 615 mContext.unbindService(mServiceConnection); 616 } 617 } 618 619 private class RemoteServiceConneciton implements ServiceConnection { 620 @Override onServiceConnected(ComponentName name, IBinder service)621 public void onServiceConnected(ComponentName name, IBinder service) { 622 if (mDestroyed || !mBinding) { 623 mContext.unbindService(mServiceConnection); 624 return; 625 } 626 mBinding = false; 627 mPrintService = IPrintService.Stub.asInterface(service); 628 try { 629 service.linkToDeath(RemotePrintService.this, 0); 630 } catch (RemoteException re) { 631 handleBinderDied(); 632 return; 633 } 634 try { 635 mPrintService.setClient(mPrintServiceClient); 636 } catch (RemoteException re) { 637 Slog.e(LOG_TAG, "Error setting client for: " + service, re); 638 handleBinderDied(); 639 return; 640 } 641 // If the service died and there is a discovery session, recreate it. 642 if (mServiceDied && mHasPrinterDiscoverySession) { 643 handleCreatePrinterDiscoverySession(); 644 } 645 // If the service died and there is discovery started, restart it. 646 if (mServiceDied && mDiscoveryPriorityList != null) { 647 handleStartPrinterDiscovery(mDiscoveryPriorityList); 648 } 649 synchronized (mLock) { 650 // If the service died and printers were tracked, start tracking. 651 if (mServiceDied && mTrackedPrinterList != null) { 652 final int trackedPrinterCount = mTrackedPrinterList.size(); 653 for (int i = 0; i < trackedPrinterCount; i++) { 654 handleStartPrinterStateTracking(mTrackedPrinterList.get(i)); 655 } 656 } 657 } 658 // Finally, do all the pending work. 659 while (!mPendingCommands.isEmpty()) { 660 Runnable pendingCommand = mPendingCommands.remove(0); 661 pendingCommand.run(); 662 } 663 // We did a best effort to get to the last state if we crashed. 664 // If we do not have print jobs and no discovery is in progress, 665 // then no need to be bound. 666 if (!mHasPrinterDiscoverySession && !mHasActivePrintJobs) { 667 ensureUnbound(); 668 } 669 mServiceDied = false; 670 } 671 672 @Override onServiceDisconnected(ComponentName name)673 public void onServiceDisconnected(ComponentName name) { 674 mBinding = true; 675 } 676 } 677 678 private static final class RemotePrintServiceClient extends IPrintServiceClient.Stub { 679 private final WeakReference<RemotePrintService> mWeakService; 680 RemotePrintServiceClient(RemotePrintService service)681 public RemotePrintServiceClient(RemotePrintService service) { 682 mWeakService = new WeakReference<RemotePrintService>(service); 683 } 684 685 @Override getPrintJobInfos()686 public List<PrintJobInfo> getPrintJobInfos() { 687 RemotePrintService service = mWeakService.get(); 688 if (service != null) { 689 final long identity = Binder.clearCallingIdentity(); 690 try { 691 return service.mSpooler.getPrintJobInfos(service.mComponentName, 692 PrintJobInfo.STATE_ANY_SCHEDULED, PrintManager.APP_ID_ANY); 693 } finally { 694 Binder.restoreCallingIdentity(identity); 695 } 696 } 697 return null; 698 } 699 700 @Override getPrintJobInfo(PrintJobId printJobId)701 public PrintJobInfo getPrintJobInfo(PrintJobId printJobId) { 702 RemotePrintService service = mWeakService.get(); 703 if (service != null) { 704 final long identity = Binder.clearCallingIdentity(); 705 try { 706 return service.mSpooler.getPrintJobInfo(printJobId, 707 PrintManager.APP_ID_ANY); 708 } finally { 709 Binder.restoreCallingIdentity(identity); 710 } 711 } 712 return null; 713 } 714 715 @Override setPrintJobState(PrintJobId printJobId, int state, String error)716 public boolean setPrintJobState(PrintJobId printJobId, int state, String error) { 717 RemotePrintService service = mWeakService.get(); 718 if (service != null) { 719 final long identity = Binder.clearCallingIdentity(); 720 try { 721 return service.mSpooler.setPrintJobState(printJobId, state, error); 722 } finally { 723 Binder.restoreCallingIdentity(identity); 724 } 725 } 726 return false; 727 } 728 729 @Override setPrintJobTag(PrintJobId printJobId, String tag)730 public boolean setPrintJobTag(PrintJobId printJobId, String tag) { 731 RemotePrintService service = mWeakService.get(); 732 if (service != null) { 733 final long identity = Binder.clearCallingIdentity(); 734 try { 735 return service.mSpooler.setPrintJobTag(printJobId, tag); 736 } finally { 737 Binder.restoreCallingIdentity(identity); 738 } 739 } 740 return false; 741 } 742 743 @Override writePrintJobData(ParcelFileDescriptor fd, PrintJobId printJobId)744 public void writePrintJobData(ParcelFileDescriptor fd, PrintJobId printJobId) { 745 RemotePrintService service = mWeakService.get(); 746 if (service != null) { 747 final long identity = Binder.clearCallingIdentity(); 748 try { 749 service.mSpooler.writePrintJobData(fd, printJobId); 750 } finally { 751 Binder.restoreCallingIdentity(identity); 752 } 753 } 754 } 755 756 @Override setProgress(@onNull PrintJobId printJobId, @FloatRange(from=0.0, to=1.0) float progress)757 public void setProgress(@NonNull PrintJobId printJobId, 758 @FloatRange(from=0.0, to=1.0) float progress) { 759 RemotePrintService service = mWeakService.get(); 760 if (service != null) { 761 final long identity = Binder.clearCallingIdentity(); 762 try { 763 service.mSpooler.setProgress(printJobId, progress); 764 } finally { 765 Binder.restoreCallingIdentity(identity); 766 } 767 } 768 } 769 770 @Override setStatus(@onNull PrintJobId printJobId, @Nullable CharSequence status)771 public void setStatus(@NonNull PrintJobId printJobId, @Nullable CharSequence status) { 772 RemotePrintService service = mWeakService.get(); 773 if (service != null) { 774 final long identity = Binder.clearCallingIdentity(); 775 try { 776 service.mSpooler.setStatus(printJobId, status); 777 } finally { 778 Binder.restoreCallingIdentity(identity); 779 } 780 } 781 } 782 783 @Override setStatusRes(@onNull PrintJobId printJobId, @StringRes int status, @NonNull CharSequence appPackageName)784 public void setStatusRes(@NonNull PrintJobId printJobId, @StringRes int status, 785 @NonNull CharSequence appPackageName) { 786 RemotePrintService service = mWeakService.get(); 787 if (service != null) { 788 final long identity = Binder.clearCallingIdentity(); 789 try { 790 service.mSpooler.setStatus(printJobId, status, appPackageName); 791 } finally { 792 Binder.restoreCallingIdentity(identity); 793 } 794 } 795 } 796 797 @Override 798 @SuppressWarnings({"rawtypes", "unchecked"}) onPrintersAdded(ParceledListSlice printers)799 public void onPrintersAdded(ParceledListSlice printers) { 800 RemotePrintService service = mWeakService.get(); 801 if (service != null) { 802 List<PrinterInfo> addedPrinters = (List<PrinterInfo>) printers.getList(); 803 throwIfPrinterIdsForPrinterInfoTampered(service.mComponentName, addedPrinters); 804 final long identity = Binder.clearCallingIdentity(); 805 try { 806 service.mCallbacks.onPrintersAdded(addedPrinters); 807 } finally { 808 Binder.restoreCallingIdentity(identity); 809 } 810 } 811 } 812 813 @Override 814 @SuppressWarnings({"rawtypes", "unchecked"}) onPrintersRemoved(ParceledListSlice printerIds)815 public void onPrintersRemoved(ParceledListSlice printerIds) { 816 RemotePrintService service = mWeakService.get(); 817 if (service != null) { 818 List<PrinterId> removedPrinterIds = (List<PrinterId>) printerIds.getList(); 819 throwIfPrinterIdsTampered(service.mComponentName, removedPrinterIds); 820 final long identity = Binder.clearCallingIdentity(); 821 try { 822 service.mCallbacks.onPrintersRemoved(removedPrinterIds); 823 } finally { 824 Binder.restoreCallingIdentity(identity); 825 } 826 } 827 } 828 throwIfPrinterIdsForPrinterInfoTampered(ComponentName serviceName, List<PrinterInfo> printerInfos)829 private void throwIfPrinterIdsForPrinterInfoTampered(ComponentName serviceName, 830 List<PrinterInfo> printerInfos) { 831 final int printerInfoCount = printerInfos.size(); 832 for (int i = 0; i < printerInfoCount; i++) { 833 PrinterId printerId = printerInfos.get(i).getId(); 834 throwIfPrinterIdTampered(serviceName, printerId); 835 } 836 } 837 throwIfPrinterIdsTampered(ComponentName serviceName, List<PrinterId> printerIds)838 private void throwIfPrinterIdsTampered(ComponentName serviceName, 839 List<PrinterId> printerIds) { 840 final int printerIdCount = printerIds.size(); 841 for (int i = 0; i < printerIdCount; i++) { 842 PrinterId printerId = printerIds.get(i); 843 throwIfPrinterIdTampered(serviceName, printerId); 844 } 845 } 846 throwIfPrinterIdTampered(ComponentName serviceName, PrinterId printerId)847 private void throwIfPrinterIdTampered(ComponentName serviceName, PrinterId printerId) { 848 if (printerId == null || !printerId.getServiceName().equals(serviceName)) { 849 throw new IllegalArgumentException("Invalid printer id: " + printerId); 850 } 851 } 852 853 @Override onCustomPrinterIconLoaded(PrinterId printerId, Icon icon)854 public void onCustomPrinterIconLoaded(PrinterId printerId, Icon icon) 855 throws RemoteException { 856 RemotePrintService service = mWeakService.get(); 857 if (service != null) { 858 final long identity = Binder.clearCallingIdentity(); 859 try { 860 service.mCallbacks.onCustomPrinterIconLoaded(printerId, icon); 861 } finally { 862 Binder.restoreCallingIdentity(identity); 863 } 864 } 865 } 866 } 867 } 868