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 { onPrintJobQueued(PrintJobInfo printJob)121 public void onPrintJobQueued(PrintJobInfo printJob); onAllPrintJobsForServiceHandled(ComponentName printService)122 public void onAllPrintJobsForServiceHandled(ComponentName printService); onPrintJobStateChanged(PrintJobInfo printJob)123 public void onPrintJobStateChanged(PrintJobInfo printJob); 124 } 125 RemotePrintSpooler(Context context, int userId, boolean lowPriority, PrintSpoolerCallbacks callbacks)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 increasePriority()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 getPrintJobInfos(ComponentName componentName, int state, int appId)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 createPrintJob(PrintJobInfo printJob)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 writePrintJobData(ParcelFileDescriptor fd, PrintJobId printJobId)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 getPrintJobInfo(PrintJobId printJobId, int appId)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 setPrintJobState(PrintJobId printJobId, int state, String error)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 */ setProgress(@onNull PrintJobId printJobId, @FloatRange(from=0.0, to=1.0) float progress)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 */ setStatus(@onNull PrintJobId printJobId, @Nullable CharSequence status)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 */ setStatus(@onNull PrintJobId printJobId, @StringRes int status, @NonNull CharSequence appPackageName)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 */ onCustomPrinterIconLoaded(@onNull PrinterId printerId, @Nullable Icon icon)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 */ getCustomPrinterIcon(@onNull PrinterId printerId)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 */ clearCustomPrinterIconCache()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 setPrintJobTag(PrintJobId printJobId, String tag)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 setPrintJobCancelling(PrintJobId printJobId, boolean cancelling)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 */ pruneApprovedPrintServices(List<ComponentName> servicesToKeep)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 removeObsoletePrintJobs()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 destroy()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 dump(@onNull DualDumpOutputStream dumpStream)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 onAllPrintJobsHandled()578 private void onAllPrintJobsHandled() { 579 synchronized (mLock) { 580 throwIfDestroyedLocked(); 581 unbindLocked(); 582 } 583 } 584 onPrintJobStateChanged(PrintJobInfo printJob)585 private void onPrintJobStateChanged(PrintJobInfo printJob) { 586 mCallbacks.onPrintJobStateChanged(printJob); 587 } 588 getRemoteInstanceLazy()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") bindLocked()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 unbindLocked()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 setClientLocked()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 clearClientLocked()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 throwIfDestroyedLocked()686 private void throwIfDestroyedLocked() { 687 if (mDestroyed) { 688 throw new IllegalStateException("Cannot interact with a destroyed instance."); 689 } 690 } 691 throwIfCalledOnMainThread()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 onServiceConnected(ComponentName name, IBinder service)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 onServiceDisconnected(ComponentName name)709 public void onServiceDisconnected(ComponentName name) { 710 synchronized (mLock) { 711 if (mRemoteInstance != null) { 712 clearClientLocked(); 713 mRemoteInstance = null; 714 } 715 } 716 } 717 } 718 719 private static final class GetPrintJobInfosCaller 720 extends TimedRemoteCaller<List<PrintJobInfo>> { 721 private final IPrintSpoolerCallbacks mCallback; 722 GetPrintJobInfosCaller()723 public GetPrintJobInfosCaller() { 724 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); 725 mCallback = new BasePrintSpoolerServiceCallbacks() { 726 @Override 727 public void onGetPrintJobInfosResult(List<PrintJobInfo> printJobs, int sequence) { 728 onRemoteMethodResult(printJobs, sequence); 729 } 730 }; 731 } 732 getPrintJobInfos(IPrintSpooler target, ComponentName componentName, int state, int appId)733 public List<PrintJobInfo> getPrintJobInfos(IPrintSpooler target, 734 ComponentName componentName, int state, int appId) 735 throws RemoteException, TimeoutException { 736 final int sequence = onBeforeRemoteCall(); 737 target.getPrintJobInfos(mCallback, componentName, state, appId, sequence); 738 return getResultTimed(sequence); 739 } 740 } 741 742 private static final class GetPrintJobInfoCaller extends TimedRemoteCaller<PrintJobInfo> { 743 private final IPrintSpoolerCallbacks mCallback; 744 GetPrintJobInfoCaller()745 public GetPrintJobInfoCaller() { 746 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); 747 mCallback = new BasePrintSpoolerServiceCallbacks() { 748 @Override 749 public void onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence) { 750 onRemoteMethodResult(printJob, sequence); 751 } 752 }; 753 } 754 getPrintJobInfo(IPrintSpooler target, PrintJobId printJobId, int appId)755 public PrintJobInfo getPrintJobInfo(IPrintSpooler target, PrintJobId printJobId, 756 int appId) throws RemoteException, TimeoutException { 757 final int sequence = onBeforeRemoteCall(); 758 target.getPrintJobInfo(printJobId, mCallback, appId, sequence); 759 return getResultTimed(sequence); 760 } 761 } 762 763 private static final class SetPrintJobStateCaller extends TimedRemoteCaller<Boolean> { 764 private final IPrintSpoolerCallbacks mCallback; 765 SetPrintJobStateCaller()766 public SetPrintJobStateCaller() { 767 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); 768 mCallback = new BasePrintSpoolerServiceCallbacks() { 769 @Override 770 public void onSetPrintJobStateResult(boolean success, int sequence) { 771 onRemoteMethodResult(success, sequence); 772 } 773 }; 774 } 775 setPrintJobState(IPrintSpooler target, PrintJobId printJobId, int status, String error)776 public boolean setPrintJobState(IPrintSpooler target, PrintJobId printJobId, 777 int status, String error) throws RemoteException, TimeoutException { 778 final int sequence = onBeforeRemoteCall(); 779 target.setPrintJobState(printJobId, status, error, mCallback, sequence); 780 return getResultTimed(sequence); 781 } 782 } 783 784 private static final class SetPrintJobTagCaller extends TimedRemoteCaller<Boolean> { 785 private final IPrintSpoolerCallbacks mCallback; 786 SetPrintJobTagCaller()787 public SetPrintJobTagCaller() { 788 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); 789 mCallback = new BasePrintSpoolerServiceCallbacks() { 790 @Override 791 public void onSetPrintJobTagResult(boolean success, int sequence) { 792 onRemoteMethodResult(success, sequence); 793 } 794 }; 795 } 796 setPrintJobTag(IPrintSpooler target, PrintJobId printJobId, String tag)797 public boolean setPrintJobTag(IPrintSpooler target, PrintJobId printJobId, 798 String tag) throws RemoteException, TimeoutException { 799 final int sequence = onBeforeRemoteCall(); 800 target.setPrintJobTag(printJobId, tag, mCallback, sequence); 801 return getResultTimed(sequence); 802 } 803 } 804 805 private static final class OnCustomPrinterIconLoadedCaller extends TimedRemoteCaller<Void> { 806 private final IPrintSpoolerCallbacks mCallback; 807 OnCustomPrinterIconLoadedCaller()808 public OnCustomPrinterIconLoadedCaller() { 809 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); 810 mCallback = new BasePrintSpoolerServiceCallbacks() { 811 @Override 812 public void onCustomPrinterIconCached(int sequence) { 813 onRemoteMethodResult(null, sequence); 814 } 815 }; 816 } 817 onCustomPrinterIconLoaded(IPrintSpooler target, PrinterId printerId, Icon icon)818 public Void onCustomPrinterIconLoaded(IPrintSpooler target, PrinterId printerId, 819 Icon icon) throws RemoteException, TimeoutException { 820 final int sequence = onBeforeRemoteCall(); 821 target.onCustomPrinterIconLoaded(printerId, icon, mCallback, sequence); 822 return getResultTimed(sequence); 823 } 824 } 825 826 private static final class ClearCustomPrinterIconCacheCaller extends TimedRemoteCaller<Void> { 827 private final IPrintSpoolerCallbacks mCallback; 828 ClearCustomPrinterIconCacheCaller()829 public ClearCustomPrinterIconCacheCaller() { 830 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); 831 mCallback = new BasePrintSpoolerServiceCallbacks() { 832 @Override 833 public void customPrinterIconCacheCleared(int sequence) { 834 onRemoteMethodResult(null, sequence); 835 } 836 }; 837 } 838 clearCustomPrinterIconCache(IPrintSpooler target)839 public Void clearCustomPrinterIconCache(IPrintSpooler target) 840 throws RemoteException, TimeoutException { 841 final int sequence = onBeforeRemoteCall(); 842 target.clearCustomPrinterIconCache(mCallback, sequence); 843 return getResultTimed(sequence); 844 } 845 } 846 847 private static final class GetCustomPrinterIconCaller extends TimedRemoteCaller<Icon> { 848 private final IPrintSpoolerCallbacks mCallback; 849 GetCustomPrinterIconCaller()850 public GetCustomPrinterIconCaller() { 851 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); 852 mCallback = new BasePrintSpoolerServiceCallbacks() { 853 @Override 854 public void onGetCustomPrinterIconResult(Icon icon, int sequence) { 855 onRemoteMethodResult(icon, sequence); 856 } 857 }; 858 } 859 getCustomPrinterIcon(IPrintSpooler target, PrinterId printerId)860 public Icon getCustomPrinterIcon(IPrintSpooler target, PrinterId printerId) 861 throws RemoteException, TimeoutException { 862 final int sequence = onBeforeRemoteCall(); 863 target.getCustomPrinterIcon(printerId, mCallback, sequence); 864 return getResultTimed(sequence); 865 } 866 } 867 868 private static abstract class BasePrintSpoolerServiceCallbacks 869 extends IPrintSpoolerCallbacks.Stub { 870 @Override onGetPrintJobInfosResult(List<PrintJobInfo> printJobIds, int sequence)871 public void onGetPrintJobInfosResult(List<PrintJobInfo> printJobIds, int sequence) { 872 /* do nothing */ 873 } 874 875 @Override onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence)876 public void onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence) { 877 /* do nothing */ 878 } 879 880 @Override onCancelPrintJobResult(boolean canceled, int sequence)881 public void onCancelPrintJobResult(boolean canceled, int sequence) { 882 /* do nothing */ 883 } 884 885 @Override onSetPrintJobStateResult(boolean success, int sequece)886 public void onSetPrintJobStateResult(boolean success, int sequece) { 887 /* do nothing */ 888 } 889 890 @Override onSetPrintJobTagResult(boolean success, int sequence)891 public void onSetPrintJobTagResult(boolean success, int sequence) { 892 /* do nothing */ 893 } 894 895 @Override onCustomPrinterIconCached(int sequence)896 public void onCustomPrinterIconCached(int sequence) { 897 /* do nothing */ 898 } 899 900 @Override onGetCustomPrinterIconResult(@ullable Icon icon, int sequence)901 public void onGetCustomPrinterIconResult(@Nullable Icon icon, int sequence) { 902 /* do nothing */ 903 } 904 905 @Override customPrinterIconCacheCleared(int sequence)906 public void customPrinterIconCacheCleared(int sequence) { 907 /* do nothing */ 908 } 909 } 910 911 private static final class PrintSpoolerClient extends IPrintSpoolerClient.Stub { 912 913 private final WeakReference<RemotePrintSpooler> mWeakSpooler; 914 PrintSpoolerClient(RemotePrintSpooler spooler)915 public PrintSpoolerClient(RemotePrintSpooler spooler) { 916 mWeakSpooler = new WeakReference<RemotePrintSpooler>(spooler); 917 } 918 919 @Override onPrintJobQueued(PrintJobInfo printJob)920 public void onPrintJobQueued(PrintJobInfo printJob) { 921 RemotePrintSpooler spooler = mWeakSpooler.get(); 922 if (spooler != null) { 923 final long identity = Binder.clearCallingIdentity(); 924 try { 925 spooler.mCallbacks.onPrintJobQueued(printJob); 926 } finally { 927 Binder.restoreCallingIdentity(identity); 928 } 929 } 930 } 931 932 @Override onAllPrintJobsForServiceHandled(ComponentName printService)933 public void onAllPrintJobsForServiceHandled(ComponentName printService) { 934 RemotePrintSpooler spooler = mWeakSpooler.get(); 935 if (spooler != null) { 936 final long identity = Binder.clearCallingIdentity(); 937 try { 938 spooler.mCallbacks.onAllPrintJobsForServiceHandled(printService); 939 } finally { 940 Binder.restoreCallingIdentity(identity); 941 } 942 } 943 } 944 945 @Override onAllPrintJobsHandled()946 public void onAllPrintJobsHandled() { 947 RemotePrintSpooler spooler = mWeakSpooler.get(); 948 if (spooler != null) { 949 final long identity = Binder.clearCallingIdentity(); 950 try { 951 spooler.onAllPrintJobsHandled(); 952 } finally { 953 Binder.restoreCallingIdentity(identity); 954 } 955 } 956 } 957 958 @Override onPrintJobStateChanged(PrintJobInfo printJob)959 public void onPrintJobStateChanged(PrintJobInfo printJob) { 960 RemotePrintSpooler spooler = mWeakSpooler.get(); 961 if (spooler != null) { 962 final long identity = Binder.clearCallingIdentity(); 963 try { 964 spooler.onPrintJobStateChanged(printJob); 965 } finally { 966 Binder.restoreCallingIdentity(identity); 967 } 968 } 969 } 970 } 971 } 972