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