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.graphics.drawable.Icon; 28 import android.os.Binder; 29 import android.os.Build; 30 import android.os.IBinder; 31 import android.os.ParcelFileDescriptor; 32 import android.os.RemoteException; 33 import android.os.SystemClock; 34 import android.os.UserHandle; 35 import android.print.IPrintSpooler; 36 import android.print.IPrintSpoolerCallbacks; 37 import android.print.IPrintSpoolerClient; 38 import android.print.PrintJobId; 39 import android.print.PrintJobInfo; 40 import android.print.PrintManager; 41 import android.print.PrinterId; 42 import android.printservice.PrintService; 43 import android.util.Slog; 44 import android.util.TimedRemoteCaller; 45 46 import com.android.internal.annotations.GuardedBy; 47 import com.android.internal.os.TransferPipe; 48 49 import libcore.io.IoUtils; 50 51 import java.io.FileDescriptor; 52 import java.io.IOException; 53 import java.io.PrintWriter; 54 import java.lang.ref.WeakReference; 55 import java.util.List; 56 import java.util.concurrent.TimeoutException; 57 58 /** 59 * This represents the remote print spooler as a local object to the 60 * PrintManagerService. It is responsible to connecting to the remote 61 * spooler if needed, to make the timed remote calls, to handle 62 * remote exceptions, and to bind/unbind to the remote instance as 63 * needed. 64 * 65 * The calls might be blocking and need the main thread of to be unblocked to finish. Hence do not 66 * call this while holding any monitors that might need to be acquired the main thread. 67 */ 68 final class RemotePrintSpooler { 69 70 private static final String LOG_TAG = "RemotePrintSpooler"; 71 72 private static final boolean DEBUG = false; 73 74 private static final long BIND_SPOOLER_SERVICE_TIMEOUT = 75 (Build.IS_ENG) ? 120000 : 10000; 76 77 private final Object mLock = new Object(); 78 79 private final GetPrintJobInfosCaller mGetPrintJobInfosCaller = new GetPrintJobInfosCaller(); 80 81 private final GetPrintJobInfoCaller mGetPrintJobInfoCaller = new GetPrintJobInfoCaller(); 82 83 private final SetPrintJobStateCaller mSetPrintJobStatusCaller = new SetPrintJobStateCaller(); 84 85 private final SetPrintJobTagCaller mSetPrintJobTagCaller = new SetPrintJobTagCaller(); 86 87 private final OnCustomPrinterIconLoadedCaller mCustomPrinterIconLoadedCaller = 88 new OnCustomPrinterIconLoadedCaller(); 89 90 private final ClearCustomPrinterIconCacheCaller mClearCustomPrinterIconCache = 91 new ClearCustomPrinterIconCacheCaller(); 92 93 private final GetCustomPrinterIconCaller mGetCustomPrinterIconCaller = 94 new GetCustomPrinterIconCaller(); 95 96 private final ServiceConnection mServiceConnection = new MyServiceConnection(); 97 98 private final Context mContext; 99 100 private final UserHandle mUserHandle; 101 102 private final PrintSpoolerClient mClient; 103 104 private final Intent mIntent; 105 106 private final PrintSpoolerCallbacks mCallbacks; 107 108 private boolean mIsLowPriority; 109 110 private IPrintSpooler mRemoteInstance; 111 112 private boolean mDestroyed; 113 114 private boolean mCanUnbind; 115 116 /** Whether a thread is currently trying to {@link #bindLocked() bind to the print service} */ 117 @GuardedBy("mLock") 118 private boolean mIsBinding; 119 120 public static interface PrintSpoolerCallbacks { 121 public void onPrintJobQueued(PrintJobInfo printJob); 122 public void onAllPrintJobsForServiceHandled(ComponentName printService); 123 public void onPrintJobStateChanged(PrintJobInfo printJob); 124 } 125 126 public RemotePrintSpooler(Context context, int userId, boolean lowPriority, 127 PrintSpoolerCallbacks callbacks) { 128 mContext = context; 129 mUserHandle = new UserHandle(userId); 130 mCallbacks = callbacks; 131 mIsLowPriority = lowPriority; 132 mClient = new PrintSpoolerClient(this); 133 mIntent = new Intent(); 134 mIntent.setComponent(new ComponentName(PrintManager.PRINT_SPOOLER_PACKAGE_NAME, 135 PrintManager.PRINT_SPOOLER_PACKAGE_NAME + ".model.PrintSpoolerService")); 136 } 137 138 public void increasePriority() { 139 if (mIsLowPriority) { 140 mIsLowPriority = false; 141 142 synchronized (mLock) { 143 throwIfDestroyedLocked(); 144 145 while (!mCanUnbind) { 146 try { 147 mLock.wait(); 148 } catch (InterruptedException e) { 149 Slog.e(LOG_TAG, "Interrupted while waiting for operation to complete"); 150 } 151 } 152 153 if (DEBUG) { 154 Slog.i(LOG_TAG, "Unbinding as previous binding was low priority"); 155 } 156 157 unbindLocked(); 158 } 159 } 160 } 161 162 public final List<PrintJobInfo> getPrintJobInfos(ComponentName componentName, int state, 163 int appId) { 164 throwIfCalledOnMainThread(); 165 synchronized (mLock) { 166 throwIfDestroyedLocked(); 167 mCanUnbind = false; 168 } 169 try { 170 return mGetPrintJobInfosCaller.getPrintJobInfos(getRemoteInstanceLazy(), 171 componentName, state, appId); 172 } catch (RemoteException | TimeoutException | InterruptedException e) { 173 Slog.e(LOG_TAG, "Error getting print jobs.", e); 174 } finally { 175 if (DEBUG) { 176 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] getPrintJobInfos()"); 177 } 178 synchronized (mLock) { 179 mCanUnbind = true; 180 mLock.notifyAll(); 181 } 182 } 183 return null; 184 } 185 186 public final void createPrintJob(PrintJobInfo printJob) { 187 throwIfCalledOnMainThread(); 188 synchronized (mLock) { 189 throwIfDestroyedLocked(); 190 mCanUnbind = false; 191 } 192 try { 193 getRemoteInstanceLazy().createPrintJob(printJob); 194 } catch (RemoteException | TimeoutException | InterruptedException e) { 195 Slog.e(LOG_TAG, "Error creating print job.", e); 196 } finally { 197 if (DEBUG) { 198 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] createPrintJob()"); 199 } 200 synchronized (mLock) { 201 mCanUnbind = true; 202 mLock.notifyAll(); 203 } 204 } 205 } 206 207 public final void writePrintJobData(ParcelFileDescriptor fd, PrintJobId printJobId) { 208 throwIfCalledOnMainThread(); 209 synchronized (mLock) { 210 throwIfDestroyedLocked(); 211 mCanUnbind = false; 212 } 213 try { 214 getRemoteInstanceLazy().writePrintJobData(fd, printJobId); 215 } catch (RemoteException | TimeoutException | InterruptedException e) { 216 Slog.e(LOG_TAG, "Error writing print job data.", e); 217 } finally { 218 if (DEBUG) { 219 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] writePrintJobData()"); 220 } 221 // We passed the file descriptor across and now the other 222 // side is responsible to close it, so close the local copy. 223 IoUtils.closeQuietly(fd); 224 synchronized (mLock) { 225 mCanUnbind = true; 226 mLock.notifyAll(); 227 } 228 } 229 } 230 231 public final PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId) { 232 throwIfCalledOnMainThread(); 233 synchronized (mLock) { 234 throwIfDestroyedLocked(); 235 mCanUnbind = false; 236 } 237 try { 238 return mGetPrintJobInfoCaller.getPrintJobInfo(getRemoteInstanceLazy(), 239 printJobId, appId); 240 } catch (RemoteException | TimeoutException | InterruptedException e) { 241 Slog.e(LOG_TAG, "Error getting print job info.", e); 242 } finally { 243 if (DEBUG) { 244 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] getPrintJobInfo()"); 245 } 246 synchronized (mLock) { 247 mCanUnbind = true; 248 mLock.notifyAll(); 249 } 250 } 251 return null; 252 } 253 254 public final boolean setPrintJobState(PrintJobId printJobId, int state, String error) { 255 throwIfCalledOnMainThread(); 256 synchronized (mLock) { 257 throwIfDestroyedLocked(); 258 mCanUnbind = false; 259 } 260 try { 261 return mSetPrintJobStatusCaller.setPrintJobState(getRemoteInstanceLazy(), 262 printJobId, state, error); 263 } catch (RemoteException | TimeoutException | InterruptedException e) { 264 Slog.e(LOG_TAG, "Error setting print job state.", e); 265 } finally { 266 if (DEBUG) { 267 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setPrintJobState()"); 268 } 269 synchronized (mLock) { 270 mCanUnbind = true; 271 mLock.notifyAll(); 272 } 273 } 274 return false; 275 } 276 277 /** 278 * Set progress of a print job. 279 * 280 * @param printJobId The print job to update 281 * @param progress The new progress 282 */ 283 public final void setProgress(@NonNull PrintJobId printJobId, 284 @FloatRange(from=0.0, to=1.0) float progress) { 285 throwIfCalledOnMainThread(); 286 synchronized (mLock) { 287 throwIfDestroyedLocked(); 288 mCanUnbind = false; 289 } 290 try { 291 getRemoteInstanceLazy().setProgress(printJobId, progress); 292 } catch (RemoteException | TimeoutException | InterruptedException re) { 293 Slog.e(LOG_TAG, "Error setting progress.", re); 294 } finally { 295 if (DEBUG) { 296 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setProgress()"); 297 } 298 synchronized (mLock) { 299 mCanUnbind = true; 300 mLock.notifyAll(); 301 } 302 } 303 } 304 305 /** 306 * Set status of a print job. 307 * 308 * @param printJobId The print job to update 309 * @param status The new status 310 */ 311 public final void setStatus(@NonNull PrintJobId printJobId, @Nullable CharSequence status) { 312 throwIfCalledOnMainThread(); 313 synchronized (mLock) { 314 throwIfDestroyedLocked(); 315 mCanUnbind = false; 316 } 317 try { 318 getRemoteInstanceLazy().setStatus(printJobId, status); 319 } catch (RemoteException | TimeoutException | InterruptedException e) { 320 Slog.e(LOG_TAG, "Error setting status.", e); 321 } finally { 322 if (DEBUG) { 323 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setStatus()"); 324 } 325 synchronized (mLock) { 326 mCanUnbind = true; 327 mLock.notifyAll(); 328 } 329 } 330 } 331 332 /** 333 * Set status of a print job. 334 * 335 * @param printJobId The print job to update 336 * @param status The new status as a string resource 337 * @param appPackageName The app package name the string res belongs to 338 */ 339 public final void setStatus(@NonNull PrintJobId printJobId, @StringRes int status, 340 @NonNull CharSequence appPackageName) { 341 throwIfCalledOnMainThread(); 342 synchronized (mLock) { 343 throwIfDestroyedLocked(); 344 mCanUnbind = false; 345 } 346 try { 347 getRemoteInstanceLazy().setStatusRes(printJobId, status, appPackageName); 348 } catch (RemoteException | TimeoutException | InterruptedException e) { 349 Slog.e(LOG_TAG, "Error setting status.", e); 350 } finally { 351 if (DEBUG) { 352 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setStatus()"); 353 } 354 synchronized (mLock) { 355 mCanUnbind = true; 356 mLock.notifyAll(); 357 } 358 } 359 } 360 361 /** 362 * Handle that a custom icon for a printer was loaded. 363 * 364 * @param printerId the id of the printer the icon belongs to 365 * @param icon the icon that was loaded 366 * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon() 367 */ 368 public final void onCustomPrinterIconLoaded(@NonNull PrinterId printerId, 369 @Nullable Icon icon) { 370 throwIfCalledOnMainThread(); 371 synchronized (mLock) { 372 throwIfDestroyedLocked(); 373 mCanUnbind = false; 374 } 375 try { 376 mCustomPrinterIconLoadedCaller.onCustomPrinterIconLoaded(getRemoteInstanceLazy(), 377 printerId, icon); 378 } catch (RemoteException | TimeoutException | InterruptedException re) { 379 Slog.e(LOG_TAG, "Error loading new custom printer icon.", re); 380 } finally { 381 if (DEBUG) { 382 Slog.i(LOG_TAG, 383 "[user: " + mUserHandle.getIdentifier() + "] onCustomPrinterIconLoaded()"); 384 } 385 synchronized (mLock) { 386 mCanUnbind = true; 387 mLock.notifyAll(); 388 } 389 } 390 } 391 392 /** 393 * Get the custom icon for a printer. If the icon is not cached, the icon is 394 * requested asynchronously. Once it is available the printer is updated. 395 * 396 * @param printerId the id of the printer the icon should be loaded for 397 * @return the custom icon to be used for the printer or null if the icon is 398 * not yet available 399 * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon() 400 */ 401 public final @Nullable Icon getCustomPrinterIcon(@NonNull PrinterId printerId) { 402 throwIfCalledOnMainThread(); 403 synchronized (mLock) { 404 throwIfDestroyedLocked(); 405 mCanUnbind = false; 406 } 407 try { 408 return mGetCustomPrinterIconCaller.getCustomPrinterIcon(getRemoteInstanceLazy(), 409 printerId); 410 } catch (RemoteException | TimeoutException | InterruptedException e) { 411 Slog.e(LOG_TAG, "Error getting custom printer icon.", e); 412 return null; 413 } finally { 414 if (DEBUG) { 415 Slog.i(LOG_TAG, 416 "[user: " + mUserHandle.getIdentifier() + "] getCustomPrinterIcon()"); 417 } 418 synchronized (mLock) { 419 mCanUnbind = true; 420 mLock.notifyAll(); 421 } 422 } 423 } 424 425 /** 426 * Clear the custom printer icon cache 427 */ 428 public void clearCustomPrinterIconCache() { 429 throwIfCalledOnMainThread(); 430 synchronized (mLock) { 431 throwIfDestroyedLocked(); 432 mCanUnbind = false; 433 } 434 try { 435 mClearCustomPrinterIconCache.clearCustomPrinterIconCache(getRemoteInstanceLazy()); 436 } catch (RemoteException | TimeoutException | InterruptedException e) { 437 Slog.e(LOG_TAG, "Error clearing custom printer icon cache.", e); 438 } finally { 439 if (DEBUG) { 440 Slog.i(LOG_TAG, 441 "[user: " + mUserHandle.getIdentifier() 442 + "] clearCustomPrinterIconCache()"); 443 } 444 synchronized (mLock) { 445 mCanUnbind = true; 446 mLock.notifyAll(); 447 } 448 } 449 } 450 451 public final boolean setPrintJobTag(PrintJobId printJobId, String tag) { 452 throwIfCalledOnMainThread(); 453 synchronized (mLock) { 454 throwIfDestroyedLocked(); 455 mCanUnbind = false; 456 } 457 try { 458 return mSetPrintJobTagCaller.setPrintJobTag(getRemoteInstanceLazy(), 459 printJobId, tag); 460 } catch (RemoteException | TimeoutException | InterruptedException e) { 461 Slog.e(LOG_TAG, "Error setting print job tag.", e); 462 } finally { 463 if (DEBUG) { 464 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setPrintJobTag()"); 465 } 466 synchronized (mLock) { 467 mCanUnbind = true; 468 mLock.notifyAll(); 469 } 470 } 471 return false; 472 } 473 474 public final void setPrintJobCancelling(PrintJobId printJobId, boolean cancelling) { 475 throwIfCalledOnMainThread(); 476 synchronized (mLock) { 477 throwIfDestroyedLocked(); 478 mCanUnbind = false; 479 } 480 try { 481 getRemoteInstanceLazy().setPrintJobCancelling(printJobId, 482 cancelling); 483 } catch (RemoteException | TimeoutException | InterruptedException e) { 484 Slog.e(LOG_TAG, "Error setting print job cancelling.", e); 485 } finally { 486 if (DEBUG) { 487 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() 488 + "] setPrintJobCancelling()"); 489 } 490 synchronized (mLock) { 491 mCanUnbind = true; 492 mLock.notifyAll(); 493 } 494 } 495 } 496 497 /** 498 * Remove all approved {@link PrintService print services} that are not in the given set. 499 * 500 * @param servicesToKeep The {@link ComponentName names } of the services to keep 501 */ 502 public final void pruneApprovedPrintServices(List<ComponentName> servicesToKeep) { 503 throwIfCalledOnMainThread(); 504 synchronized (mLock) { 505 throwIfDestroyedLocked(); 506 mCanUnbind = false; 507 } 508 try { 509 getRemoteInstanceLazy().pruneApprovedPrintServices(servicesToKeep); 510 } catch (RemoteException | TimeoutException | InterruptedException e) { 511 Slog.e(LOG_TAG, "Error pruning approved print services.", e); 512 } finally { 513 if (DEBUG) { 514 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() 515 + "] pruneApprovedPrintServices()"); 516 } 517 synchronized (mLock) { 518 mCanUnbind = true; 519 mLock.notifyAll(); 520 } 521 } 522 } 523 524 public final void removeObsoletePrintJobs() { 525 throwIfCalledOnMainThread(); 526 synchronized (mLock) { 527 throwIfDestroyedLocked(); 528 mCanUnbind = false; 529 } 530 try { 531 getRemoteInstanceLazy().removeObsoletePrintJobs(); 532 } catch (RemoteException | TimeoutException | InterruptedException te) { 533 Slog.e(LOG_TAG, "Error removing obsolete print jobs .", te); 534 } finally { 535 if (DEBUG) { 536 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() 537 + "] removeObsoletePrintJobs()"); 538 } 539 synchronized (mLock) { 540 mCanUnbind = true; 541 mLock.notifyAll(); 542 } 543 } 544 } 545 546 public final void destroy() { 547 throwIfCalledOnMainThread(); 548 if (DEBUG) { 549 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] destroy()"); 550 } 551 synchronized (mLock) { 552 throwIfDestroyedLocked(); 553 unbindLocked(); 554 mDestroyed = true; 555 mCanUnbind = false; 556 } 557 } 558 559 public void dump(FileDescriptor fd, PrintWriter pw, String prefix) { 560 synchronized (mLock) { 561 pw.append(prefix).append("destroyed=") 562 .append(String.valueOf(mDestroyed)).println(); 563 pw.append(prefix).append("bound=") 564 .append((mRemoteInstance != null) ? "true" : "false").println(); 565 566 pw.flush(); 567 try { 568 TransferPipe.dumpAsync(getRemoteInstanceLazy().asBinder(), fd, 569 new String[] { prefix }); 570 } catch (IOException | TimeoutException | RemoteException | InterruptedException e) { 571 pw.println("Failed to dump remote instance: " + e); 572 } 573 } 574 } 575 576 private void onAllPrintJobsHandled() { 577 synchronized (mLock) { 578 throwIfDestroyedLocked(); 579 unbindLocked(); 580 } 581 } 582 583 private void onPrintJobStateChanged(PrintJobInfo printJob) { 584 mCallbacks.onPrintJobStateChanged(printJob); 585 } 586 587 private IPrintSpooler getRemoteInstanceLazy() throws TimeoutException, InterruptedException { 588 synchronized (mLock) { 589 if (mRemoteInstance != null) { 590 return mRemoteInstance; 591 } 592 bindLocked(); 593 return mRemoteInstance; 594 } 595 } 596 597 private void bindLocked() throws TimeoutException, InterruptedException { 598 while (mIsBinding) { 599 mLock.wait(); 600 } 601 602 if (mRemoteInstance != null) { 603 return; 604 } 605 606 mIsBinding = true; 607 608 if (DEBUG) { 609 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] bindLocked() " + 610 (mIsLowPriority ? "low priority" : "")); 611 } 612 613 try { 614 int flags; 615 if (mIsLowPriority) { 616 flags = Context.BIND_AUTO_CREATE; 617 } else { 618 flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE; 619 } 620 621 mContext.bindServiceAsUser(mIntent, mServiceConnection, flags, mUserHandle); 622 623 final long startMillis = SystemClock.uptimeMillis(); 624 while (true) { 625 if (mRemoteInstance != null) { 626 break; 627 } 628 final long elapsedMillis = SystemClock.uptimeMillis() - startMillis; 629 final long remainingMillis = BIND_SPOOLER_SERVICE_TIMEOUT - elapsedMillis; 630 if (remainingMillis <= 0) { 631 throw new TimeoutException("Cannot get spooler!"); 632 } 633 mLock.wait(remainingMillis); 634 } 635 636 mCanUnbind = true; 637 } finally { 638 mIsBinding = false; 639 mLock.notifyAll(); 640 } 641 } 642 643 private void unbindLocked() { 644 if (mRemoteInstance == null) { 645 return; 646 } 647 while (true) { 648 if (mCanUnbind) { 649 if (DEBUG) { 650 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] unbindLocked()"); 651 } 652 clearClientLocked(); 653 mRemoteInstance = null; 654 mContext.unbindService(mServiceConnection); 655 return; 656 } 657 try { 658 mLock.wait(); 659 } catch (InterruptedException ie) { 660 /* ignore */ 661 } 662 } 663 664 } 665 666 private void setClientLocked() { 667 try { 668 mRemoteInstance.setClient(mClient); 669 } catch (RemoteException re) { 670 Slog.d(LOG_TAG, "Error setting print spooler client", re); 671 } 672 } 673 674 private void clearClientLocked() { 675 try { 676 mRemoteInstance.setClient(null); 677 } catch (RemoteException re) { 678 Slog.d(LOG_TAG, "Error clearing print spooler client", re); 679 } 680 681 } 682 683 private void throwIfDestroyedLocked() { 684 if (mDestroyed) { 685 throw new IllegalStateException("Cannot interact with a destroyed instance."); 686 } 687 } 688 689 private void throwIfCalledOnMainThread() { 690 if (Thread.currentThread() == mContext.getMainLooper().getThread()) { 691 throw new RuntimeException("Cannot invoke on the main thread"); 692 } 693 } 694 695 private final class MyServiceConnection implements ServiceConnection { 696 @Override 697 public void onServiceConnected(ComponentName name, IBinder service) { 698 synchronized (mLock) { 699 mRemoteInstance = IPrintSpooler.Stub.asInterface(service); 700 setClientLocked(); 701 mLock.notifyAll(); 702 } 703 } 704 705 @Override 706 public void onServiceDisconnected(ComponentName name) { 707 synchronized (mLock) { 708 clearClientLocked(); 709 mRemoteInstance = null; 710 } 711 } 712 } 713 714 private static final class GetPrintJobInfosCaller 715 extends TimedRemoteCaller<List<PrintJobInfo>> { 716 private final IPrintSpoolerCallbacks mCallback; 717 718 public GetPrintJobInfosCaller() { 719 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); 720 mCallback = new BasePrintSpoolerServiceCallbacks() { 721 @Override 722 public void onGetPrintJobInfosResult(List<PrintJobInfo> printJobs, int sequence) { 723 onRemoteMethodResult(printJobs, sequence); 724 } 725 }; 726 } 727 728 public List<PrintJobInfo> getPrintJobInfos(IPrintSpooler target, 729 ComponentName componentName, int state, int appId) 730 throws RemoteException, TimeoutException { 731 final int sequence = onBeforeRemoteCall(); 732 target.getPrintJobInfos(mCallback, componentName, state, appId, sequence); 733 return getResultTimed(sequence); 734 } 735 } 736 737 private static final class GetPrintJobInfoCaller extends TimedRemoteCaller<PrintJobInfo> { 738 private final IPrintSpoolerCallbacks mCallback; 739 740 public GetPrintJobInfoCaller() { 741 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); 742 mCallback = new BasePrintSpoolerServiceCallbacks() { 743 @Override 744 public void onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence) { 745 onRemoteMethodResult(printJob, sequence); 746 } 747 }; 748 } 749 750 public PrintJobInfo getPrintJobInfo(IPrintSpooler target, PrintJobId printJobId, 751 int appId) throws RemoteException, TimeoutException { 752 final int sequence = onBeforeRemoteCall(); 753 target.getPrintJobInfo(printJobId, mCallback, appId, sequence); 754 return getResultTimed(sequence); 755 } 756 } 757 758 private static final class SetPrintJobStateCaller extends TimedRemoteCaller<Boolean> { 759 private final IPrintSpoolerCallbacks mCallback; 760 761 public SetPrintJobStateCaller() { 762 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); 763 mCallback = new BasePrintSpoolerServiceCallbacks() { 764 @Override 765 public void onSetPrintJobStateResult(boolean success, int sequence) { 766 onRemoteMethodResult(success, sequence); 767 } 768 }; 769 } 770 771 public boolean setPrintJobState(IPrintSpooler target, PrintJobId printJobId, 772 int status, String error) throws RemoteException, TimeoutException { 773 final int sequence = onBeforeRemoteCall(); 774 target.setPrintJobState(printJobId, status, error, mCallback, sequence); 775 return getResultTimed(sequence); 776 } 777 } 778 779 private static final class SetPrintJobTagCaller extends TimedRemoteCaller<Boolean> { 780 private final IPrintSpoolerCallbacks mCallback; 781 782 public SetPrintJobTagCaller() { 783 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); 784 mCallback = new BasePrintSpoolerServiceCallbacks() { 785 @Override 786 public void onSetPrintJobTagResult(boolean success, int sequence) { 787 onRemoteMethodResult(success, sequence); 788 } 789 }; 790 } 791 792 public boolean setPrintJobTag(IPrintSpooler target, PrintJobId printJobId, 793 String tag) throws RemoteException, TimeoutException { 794 final int sequence = onBeforeRemoteCall(); 795 target.setPrintJobTag(printJobId, tag, mCallback, sequence); 796 return getResultTimed(sequence); 797 } 798 } 799 800 private static final class OnCustomPrinterIconLoadedCaller extends TimedRemoteCaller<Void> { 801 private final IPrintSpoolerCallbacks mCallback; 802 803 public OnCustomPrinterIconLoadedCaller() { 804 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); 805 mCallback = new BasePrintSpoolerServiceCallbacks() { 806 @Override 807 public void onCustomPrinterIconCached(int sequence) { 808 onRemoteMethodResult(null, sequence); 809 } 810 }; 811 } 812 813 public Void onCustomPrinterIconLoaded(IPrintSpooler target, PrinterId printerId, 814 Icon icon) throws RemoteException, TimeoutException { 815 final int sequence = onBeforeRemoteCall(); 816 target.onCustomPrinterIconLoaded(printerId, icon, mCallback, sequence); 817 return getResultTimed(sequence); 818 } 819 } 820 821 private static final class ClearCustomPrinterIconCacheCaller extends TimedRemoteCaller<Void> { 822 private final IPrintSpoolerCallbacks mCallback; 823 824 public ClearCustomPrinterIconCacheCaller() { 825 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); 826 mCallback = new BasePrintSpoolerServiceCallbacks() { 827 @Override 828 public void customPrinterIconCacheCleared(int sequence) { 829 onRemoteMethodResult(null, sequence); 830 } 831 }; 832 } 833 834 public Void clearCustomPrinterIconCache(IPrintSpooler target) 835 throws RemoteException, TimeoutException { 836 final int sequence = onBeforeRemoteCall(); 837 target.clearCustomPrinterIconCache(mCallback, sequence); 838 return getResultTimed(sequence); 839 } 840 } 841 842 private static final class GetCustomPrinterIconCaller extends TimedRemoteCaller<Icon> { 843 private final IPrintSpoolerCallbacks mCallback; 844 845 public GetCustomPrinterIconCaller() { 846 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); 847 mCallback = new BasePrintSpoolerServiceCallbacks() { 848 @Override 849 public void onGetCustomPrinterIconResult(Icon icon, int sequence) { 850 onRemoteMethodResult(icon, sequence); 851 } 852 }; 853 } 854 855 public Icon getCustomPrinterIcon(IPrintSpooler target, PrinterId printerId) 856 throws RemoteException, TimeoutException { 857 final int sequence = onBeforeRemoteCall(); 858 target.getCustomPrinterIcon(printerId, mCallback, sequence); 859 return getResultTimed(sequence); 860 } 861 } 862 863 private static abstract class BasePrintSpoolerServiceCallbacks 864 extends IPrintSpoolerCallbacks.Stub { 865 @Override 866 public void onGetPrintJobInfosResult(List<PrintJobInfo> printJobIds, int sequence) { 867 /* do nothing */ 868 } 869 870 @Override 871 public void onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence) { 872 /* do nothing */ 873 } 874 875 @Override 876 public void onCancelPrintJobResult(boolean canceled, int sequence) { 877 /* do nothing */ 878 } 879 880 @Override 881 public void onSetPrintJobStateResult(boolean success, int sequece) { 882 /* do nothing */ 883 } 884 885 @Override 886 public void onSetPrintJobTagResult(boolean success, int sequence) { 887 /* do nothing */ 888 } 889 890 @Override 891 public void onCustomPrinterIconCached(int sequence) { 892 /* do nothing */ 893 } 894 895 @Override 896 public void onGetCustomPrinterIconResult(@Nullable Icon icon, int sequence) { 897 /* do nothing */ 898 } 899 900 @Override 901 public void customPrinterIconCacheCleared(int sequence) { 902 /* do nothing */ 903 } 904 } 905 906 private static final class PrintSpoolerClient extends IPrintSpoolerClient.Stub { 907 908 private final WeakReference<RemotePrintSpooler> mWeakSpooler; 909 910 public PrintSpoolerClient(RemotePrintSpooler spooler) { 911 mWeakSpooler = new WeakReference<RemotePrintSpooler>(spooler); 912 } 913 914 @Override 915 public void onPrintJobQueued(PrintJobInfo printJob) { 916 RemotePrintSpooler spooler = mWeakSpooler.get(); 917 if (spooler != null) { 918 final long identity = Binder.clearCallingIdentity(); 919 try { 920 spooler.mCallbacks.onPrintJobQueued(printJob); 921 } finally { 922 Binder.restoreCallingIdentity(identity); 923 } 924 } 925 } 926 927 @Override 928 public void onAllPrintJobsForServiceHandled(ComponentName printService) { 929 RemotePrintSpooler spooler = mWeakSpooler.get(); 930 if (spooler != null) { 931 final long identity = Binder.clearCallingIdentity(); 932 try { 933 spooler.mCallbacks.onAllPrintJobsForServiceHandled(printService); 934 } finally { 935 Binder.restoreCallingIdentity(identity); 936 } 937 } 938 } 939 940 @Override 941 public void onAllPrintJobsHandled() { 942 RemotePrintSpooler spooler = mWeakSpooler.get(); 943 if (spooler != null) { 944 final long identity = Binder.clearCallingIdentity(); 945 try { 946 spooler.onAllPrintJobsHandled(); 947 } finally { 948 Binder.restoreCallingIdentity(identity); 949 } 950 } 951 } 952 953 @Override 954 public void onPrintJobStateChanged(PrintJobInfo printJob) { 955 RemotePrintSpooler spooler = mWeakSpooler.get(); 956 if (spooler != null) { 957 final long identity = Binder.clearCallingIdentity(); 958 try { 959 spooler.onPrintJobStateChanged(printJob); 960 } finally { 961 Binder.restoreCallingIdentity(identity); 962 } 963 } 964 } 965 } 966 } 967