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.service.print.PrintSpoolerStateProto; 44 import android.util.Slog; 45 import android.util.TimedRemoteCaller; 46 47 import com.android.internal.annotations.GuardedBy; 48 import com.android.internal.os.TransferPipe; 49 import com.android.internal.util.dump.DualDumpOutputStream; 50 51 import libcore.io.IoUtils; 52 53 import java.io.IOException; 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(@NonNull DualDumpOutputStream dumpStream) { 560 synchronized (mLock) { 561 dumpStream.write("is_destroyed", PrintSpoolerStateProto.IS_DESTROYED, mDestroyed); 562 dumpStream.write("is_bound", PrintSpoolerStateProto.IS_BOUND, mRemoteInstance != null); 563 } 564 565 try { 566 if (dumpStream.isProto()) { 567 dumpStream.write(null, PrintSpoolerStateProto.INTERNAL_STATE, 568 TransferPipe.dumpAsync(getRemoteInstanceLazy().asBinder(), "--proto")); 569 } else { 570 dumpStream.writeNested("internal_state", TransferPipe.dumpAsync( 571 getRemoteInstanceLazy().asBinder())); 572 } 573 } catch (IOException | TimeoutException | RemoteException | InterruptedException e) { 574 Slog.e(LOG_TAG, "Failed to dump remote instance", e); 575 } 576 } 577 578 private void onAllPrintJobsHandled() { 579 synchronized (mLock) { 580 throwIfDestroyedLocked(); 581 unbindLocked(); 582 } 583 } 584 585 private void onPrintJobStateChanged(PrintJobInfo printJob) { 586 mCallbacks.onPrintJobStateChanged(printJob); 587 } 588 589 private IPrintSpooler getRemoteInstanceLazy() throws TimeoutException, InterruptedException { 590 synchronized (mLock) { 591 if (mRemoteInstance != null) { 592 return mRemoteInstance; 593 } 594 bindLocked(); 595 return mRemoteInstance; 596 } 597 } 598 599 @GuardedBy("mLock") 600 private void bindLocked() throws TimeoutException, InterruptedException { 601 while (mIsBinding) { 602 mLock.wait(); 603 } 604 605 if (mRemoteInstance != null) { 606 return; 607 } 608 609 mIsBinding = true; 610 611 if (DEBUG) { 612 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] bindLocked() " + 613 (mIsLowPriority ? "low priority" : "")); 614 } 615 616 try { 617 int flags; 618 if (mIsLowPriority) { 619 flags = Context.BIND_AUTO_CREATE; 620 } else { 621 flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE; 622 } 623 624 mContext.bindServiceAsUser(mIntent, mServiceConnection, flags, mUserHandle); 625 626 final long startMillis = SystemClock.uptimeMillis(); 627 while (true) { 628 if (mRemoteInstance != null) { 629 break; 630 } 631 final long elapsedMillis = SystemClock.uptimeMillis() - startMillis; 632 final long remainingMillis = BIND_SPOOLER_SERVICE_TIMEOUT - elapsedMillis; 633 if (remainingMillis <= 0) { 634 throw new TimeoutException("Cannot get spooler!"); 635 } 636 mLock.wait(remainingMillis); 637 } 638 639 mCanUnbind = true; 640 } finally { 641 mIsBinding = false; 642 mLock.notifyAll(); 643 } 644 } 645 646 private void unbindLocked() { 647 if (mRemoteInstance == null) { 648 return; 649 } 650 while (true) { 651 if (mCanUnbind) { 652 if (DEBUG) { 653 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] unbindLocked()"); 654 } 655 clearClientLocked(); 656 mRemoteInstance = null; 657 mContext.unbindService(mServiceConnection); 658 return; 659 } 660 try { 661 mLock.wait(); 662 } catch (InterruptedException ie) { 663 /* ignore */ 664 } 665 } 666 667 } 668 669 private void setClientLocked() { 670 try { 671 mRemoteInstance.setClient(mClient); 672 } catch (RemoteException re) { 673 Slog.d(LOG_TAG, "Error setting print spooler client", re); 674 } 675 } 676 677 private void clearClientLocked() { 678 try { 679 mRemoteInstance.setClient(null); 680 } catch (RemoteException re) { 681 Slog.d(LOG_TAG, "Error clearing print spooler client", re); 682 } 683 684 } 685 686 private void throwIfDestroyedLocked() { 687 if (mDestroyed) { 688 throw new IllegalStateException("Cannot interact with a destroyed instance."); 689 } 690 } 691 692 private void throwIfCalledOnMainThread() { 693 if (Thread.currentThread() == mContext.getMainLooper().getThread()) { 694 throw new RuntimeException("Cannot invoke on the main thread"); 695 } 696 } 697 698 private final class MyServiceConnection implements ServiceConnection { 699 @Override 700 public void onServiceConnected(ComponentName name, IBinder service) { 701 synchronized (mLock) { 702 mRemoteInstance = IPrintSpooler.Stub.asInterface(service); 703 setClientLocked(); 704 mLock.notifyAll(); 705 } 706 } 707 708 @Override 709 public void onServiceDisconnected(ComponentName name) { 710 synchronized (mLock) { 711 clearClientLocked(); 712 mRemoteInstance = null; 713 } 714 } 715 } 716 717 private static final class GetPrintJobInfosCaller 718 extends TimedRemoteCaller<List<PrintJobInfo>> { 719 private final IPrintSpoolerCallbacks mCallback; 720 721 public GetPrintJobInfosCaller() { 722 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); 723 mCallback = new BasePrintSpoolerServiceCallbacks() { 724 @Override 725 public void onGetPrintJobInfosResult(List<PrintJobInfo> printJobs, int sequence) { 726 onRemoteMethodResult(printJobs, sequence); 727 } 728 }; 729 } 730 731 public List<PrintJobInfo> getPrintJobInfos(IPrintSpooler target, 732 ComponentName componentName, int state, int appId) 733 throws RemoteException, TimeoutException { 734 final int sequence = onBeforeRemoteCall(); 735 target.getPrintJobInfos(mCallback, componentName, state, appId, sequence); 736 return getResultTimed(sequence); 737 } 738 } 739 740 private static final class GetPrintJobInfoCaller extends TimedRemoteCaller<PrintJobInfo> { 741 private final IPrintSpoolerCallbacks mCallback; 742 743 public GetPrintJobInfoCaller() { 744 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); 745 mCallback = new BasePrintSpoolerServiceCallbacks() { 746 @Override 747 public void onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence) { 748 onRemoteMethodResult(printJob, sequence); 749 } 750 }; 751 } 752 753 public PrintJobInfo getPrintJobInfo(IPrintSpooler target, PrintJobId printJobId, 754 int appId) throws RemoteException, TimeoutException { 755 final int sequence = onBeforeRemoteCall(); 756 target.getPrintJobInfo(printJobId, mCallback, appId, sequence); 757 return getResultTimed(sequence); 758 } 759 } 760 761 private static final class SetPrintJobStateCaller extends TimedRemoteCaller<Boolean> { 762 private final IPrintSpoolerCallbacks mCallback; 763 764 public SetPrintJobStateCaller() { 765 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); 766 mCallback = new BasePrintSpoolerServiceCallbacks() { 767 @Override 768 public void onSetPrintJobStateResult(boolean success, int sequence) { 769 onRemoteMethodResult(success, sequence); 770 } 771 }; 772 } 773 774 public boolean setPrintJobState(IPrintSpooler target, PrintJobId printJobId, 775 int status, String error) throws RemoteException, TimeoutException { 776 final int sequence = onBeforeRemoteCall(); 777 target.setPrintJobState(printJobId, status, error, mCallback, sequence); 778 return getResultTimed(sequence); 779 } 780 } 781 782 private static final class SetPrintJobTagCaller extends TimedRemoteCaller<Boolean> { 783 private final IPrintSpoolerCallbacks mCallback; 784 785 public SetPrintJobTagCaller() { 786 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); 787 mCallback = new BasePrintSpoolerServiceCallbacks() { 788 @Override 789 public void onSetPrintJobTagResult(boolean success, int sequence) { 790 onRemoteMethodResult(success, sequence); 791 } 792 }; 793 } 794 795 public boolean setPrintJobTag(IPrintSpooler target, PrintJobId printJobId, 796 String tag) throws RemoteException, TimeoutException { 797 final int sequence = onBeforeRemoteCall(); 798 target.setPrintJobTag(printJobId, tag, mCallback, sequence); 799 return getResultTimed(sequence); 800 } 801 } 802 803 private static final class OnCustomPrinterIconLoadedCaller extends TimedRemoteCaller<Void> { 804 private final IPrintSpoolerCallbacks mCallback; 805 806 public OnCustomPrinterIconLoadedCaller() { 807 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); 808 mCallback = new BasePrintSpoolerServiceCallbacks() { 809 @Override 810 public void onCustomPrinterIconCached(int sequence) { 811 onRemoteMethodResult(null, sequence); 812 } 813 }; 814 } 815 816 public Void onCustomPrinterIconLoaded(IPrintSpooler target, PrinterId printerId, 817 Icon icon) throws RemoteException, TimeoutException { 818 final int sequence = onBeforeRemoteCall(); 819 target.onCustomPrinterIconLoaded(printerId, icon, mCallback, sequence); 820 return getResultTimed(sequence); 821 } 822 } 823 824 private static final class ClearCustomPrinterIconCacheCaller extends TimedRemoteCaller<Void> { 825 private final IPrintSpoolerCallbacks mCallback; 826 827 public ClearCustomPrinterIconCacheCaller() { 828 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); 829 mCallback = new BasePrintSpoolerServiceCallbacks() { 830 @Override 831 public void customPrinterIconCacheCleared(int sequence) { 832 onRemoteMethodResult(null, sequence); 833 } 834 }; 835 } 836 837 public Void clearCustomPrinterIconCache(IPrintSpooler target) 838 throws RemoteException, TimeoutException { 839 final int sequence = onBeforeRemoteCall(); 840 target.clearCustomPrinterIconCache(mCallback, sequence); 841 return getResultTimed(sequence); 842 } 843 } 844 845 private static final class GetCustomPrinterIconCaller extends TimedRemoteCaller<Icon> { 846 private final IPrintSpoolerCallbacks mCallback; 847 848 public GetCustomPrinterIconCaller() { 849 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); 850 mCallback = new BasePrintSpoolerServiceCallbacks() { 851 @Override 852 public void onGetCustomPrinterIconResult(Icon icon, int sequence) { 853 onRemoteMethodResult(icon, sequence); 854 } 855 }; 856 } 857 858 public Icon getCustomPrinterIcon(IPrintSpooler target, PrinterId printerId) 859 throws RemoteException, TimeoutException { 860 final int sequence = onBeforeRemoteCall(); 861 target.getCustomPrinterIcon(printerId, mCallback, sequence); 862 return getResultTimed(sequence); 863 } 864 } 865 866 private static abstract class BasePrintSpoolerServiceCallbacks 867 extends IPrintSpoolerCallbacks.Stub { 868 @Override 869 public void onGetPrintJobInfosResult(List<PrintJobInfo> printJobIds, int sequence) { 870 /* do nothing */ 871 } 872 873 @Override 874 public void onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence) { 875 /* do nothing */ 876 } 877 878 @Override 879 public void onCancelPrintJobResult(boolean canceled, int sequence) { 880 /* do nothing */ 881 } 882 883 @Override 884 public void onSetPrintJobStateResult(boolean success, int sequece) { 885 /* do nothing */ 886 } 887 888 @Override 889 public void onSetPrintJobTagResult(boolean success, int sequence) { 890 /* do nothing */ 891 } 892 893 @Override 894 public void onCustomPrinterIconCached(int sequence) { 895 /* do nothing */ 896 } 897 898 @Override 899 public void onGetCustomPrinterIconResult(@Nullable Icon icon, int sequence) { 900 /* do nothing */ 901 } 902 903 @Override 904 public void customPrinterIconCacheCleared(int sequence) { 905 /* do nothing */ 906 } 907 } 908 909 private static final class PrintSpoolerClient extends IPrintSpoolerClient.Stub { 910 911 private final WeakReference<RemotePrintSpooler> mWeakSpooler; 912 913 public PrintSpoolerClient(RemotePrintSpooler spooler) { 914 mWeakSpooler = new WeakReference<RemotePrintSpooler>(spooler); 915 } 916 917 @Override 918 public void onPrintJobQueued(PrintJobInfo printJob) { 919 RemotePrintSpooler spooler = mWeakSpooler.get(); 920 if (spooler != null) { 921 final long identity = Binder.clearCallingIdentity(); 922 try { 923 spooler.mCallbacks.onPrintJobQueued(printJob); 924 } finally { 925 Binder.restoreCallingIdentity(identity); 926 } 927 } 928 } 929 930 @Override 931 public void onAllPrintJobsForServiceHandled(ComponentName printService) { 932 RemotePrintSpooler spooler = mWeakSpooler.get(); 933 if (spooler != null) { 934 final long identity = Binder.clearCallingIdentity(); 935 try { 936 spooler.mCallbacks.onAllPrintJobsForServiceHandled(printService); 937 } finally { 938 Binder.restoreCallingIdentity(identity); 939 } 940 } 941 } 942 943 @Override 944 public void onAllPrintJobsHandled() { 945 RemotePrintSpooler spooler = mWeakSpooler.get(); 946 if (spooler != null) { 947 final long identity = Binder.clearCallingIdentity(); 948 try { 949 spooler.onAllPrintJobsHandled(); 950 } finally { 951 Binder.restoreCallingIdentity(identity); 952 } 953 } 954 } 955 956 @Override 957 public void onPrintJobStateChanged(PrintJobInfo printJob) { 958 RemotePrintSpooler spooler = mWeakSpooler.get(); 959 if (spooler != null) { 960 final long identity = Binder.clearCallingIdentity(); 961 try { 962 spooler.onPrintJobStateChanged(printJob); 963 } finally { 964 Binder.restoreCallingIdentity(identity); 965 } 966 } 967 } 968 } 969 } 970