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.content.ComponentName; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.ServiceConnection; 23 import android.content.pm.ParceledListSlice; 24 import android.os.Binder; 25 import android.os.Handler; 26 import android.os.IBinder; 27 import android.os.IBinder.DeathRecipient; 28 import android.os.Looper; 29 import android.os.Message; 30 import android.os.ParcelFileDescriptor; 31 import android.os.RemoteException; 32 import android.os.UserHandle; 33 import android.print.PrintJobId; 34 import android.print.PrintJobInfo; 35 import android.print.PrintManager; 36 import android.print.PrinterId; 37 import android.print.PrinterInfo; 38 import android.printservice.IPrintService; 39 import android.printservice.IPrintServiceClient; 40 import android.util.Slog; 41 42 import java.io.PrintWriter; 43 import java.lang.ref.WeakReference; 44 import java.util.ArrayList; 45 import java.util.List; 46 47 /** 48 * This class represents a remote print service. It abstracts away the binding 49 * and unbinding from the remote implementation. Clients can call methods of 50 * this class without worrying about when and how to bind/unbind. 51 */ 52 final class RemotePrintService implements DeathRecipient { 53 54 private static final String LOG_TAG = "RemotePrintService"; 55 56 private static final boolean DEBUG = false; 57 58 private final Context mContext; 59 60 private final ComponentName mComponentName; 61 62 private final Intent mIntent; 63 64 private final RemotePrintSpooler mSpooler; 65 66 private final PrintServiceCallbacks mCallbacks; 67 68 private final int mUserId; 69 70 private final List<Runnable> mPendingCommands = new ArrayList<Runnable>(); 71 72 private final ServiceConnection mServiceConnection = new RemoteServiceConneciton(); 73 74 private final RemotePrintServiceClient mPrintServiceClient; 75 76 private final Handler mHandler; 77 78 private IPrintService mPrintService; 79 80 private boolean mBinding; 81 82 private boolean mDestroyed; 83 84 private boolean mHasActivePrintJobs; 85 86 private boolean mHasPrinterDiscoverySession; 87 88 private boolean mServiceDied; 89 90 private List<PrinterId> mDiscoveryPriorityList; 91 92 private List<PrinterId> mTrackedPrinterList; 93 94 public static interface PrintServiceCallbacks { onPrintersAdded(List<PrinterInfo> printers)95 public void onPrintersAdded(List<PrinterInfo> printers); onPrintersRemoved(List<PrinterId> printerIds)96 public void onPrintersRemoved(List<PrinterId> printerIds); onServiceDied(RemotePrintService service)97 public void onServiceDied(RemotePrintService service); 98 } 99 RemotePrintService(Context context, ComponentName componentName, int userId, RemotePrintSpooler spooler, PrintServiceCallbacks callbacks)100 public RemotePrintService(Context context, ComponentName componentName, int userId, 101 RemotePrintSpooler spooler, PrintServiceCallbacks callbacks) { 102 mContext = context; 103 mCallbacks = callbacks; 104 mComponentName = componentName; 105 mIntent = new Intent().setComponent(mComponentName); 106 mUserId = userId; 107 mSpooler = spooler; 108 mHandler = new MyHandler(context.getMainLooper()); 109 mPrintServiceClient = new RemotePrintServiceClient(this); 110 } 111 getComponentName()112 public ComponentName getComponentName() { 113 return mComponentName; 114 } 115 destroy()116 public void destroy() { 117 mHandler.sendEmptyMessage(MyHandler.MSG_DESTROY); 118 } 119 handleDestroy()120 private void handleDestroy() { 121 throwIfDestroyed(); 122 123 // Stop tracking printers. 124 if (mTrackedPrinterList != null) { 125 final int trackedPrinterCount = mTrackedPrinterList.size(); 126 for (int i = 0; i < trackedPrinterCount; i++) { 127 PrinterId printerId = mTrackedPrinterList.get(i); 128 if (printerId.getServiceName().equals(mComponentName)) { 129 handleStopPrinterStateTracking(printerId); 130 } 131 } 132 } 133 134 // Stop printer discovery. 135 if (mDiscoveryPriorityList != null) { 136 handleStopPrinterDiscovery(); 137 } 138 139 // Destroy the discovery session. 140 if (mHasPrinterDiscoverySession) { 141 handleDestroyPrinterDiscoverySession(); 142 } 143 144 // Unbind. 145 ensureUnbound(); 146 147 // Done 148 mDestroyed = true; 149 } 150 151 @Override binderDied()152 public void binderDied() { 153 mHandler.sendEmptyMessage(MyHandler.MSG_BINDER_DIED); 154 } 155 handleBinderDied()156 private void handleBinderDied() { 157 mPrintService.asBinder().unlinkToDeath(this, 0); 158 mPrintService = null; 159 mServiceDied = true; 160 mCallbacks.onServiceDied(this); 161 } 162 onAllPrintJobsHandled()163 public void onAllPrintJobsHandled() { 164 mHandler.sendEmptyMessage(MyHandler.MSG_ON_ALL_PRINT_JOBS_HANDLED); 165 } 166 handleOnAllPrintJobsHandled()167 private void handleOnAllPrintJobsHandled() { 168 throwIfDestroyed(); 169 mHasActivePrintJobs = false; 170 if (!isBound()) { 171 // The service is dead and neither has active jobs nor discovery 172 // session, so ensure we are unbound since the service has no work. 173 if (mServiceDied && !mHasPrinterDiscoverySession) { 174 ensureUnbound(); 175 return; 176 } 177 ensureBound(); 178 mPendingCommands.add(new Runnable() { 179 @Override 180 public void run() { 181 handleOnAllPrintJobsHandled(); 182 } 183 }); 184 } else { 185 if (DEBUG) { 186 Slog.i(LOG_TAG, "[user: " + mUserId + "] onAllPrintJobsHandled()"); 187 } 188 // If the service has a printer discovery session 189 // created we should not disconnect from it just yet. 190 if (!mHasPrinterDiscoverySession) { 191 ensureUnbound(); 192 } 193 } 194 } 195 onRequestCancelPrintJob(PrintJobInfo printJob)196 public void onRequestCancelPrintJob(PrintJobInfo printJob) { 197 mHandler.obtainMessage(MyHandler.MSG_ON_REQUEST_CANCEL_PRINT_JOB, 198 printJob).sendToTarget(); 199 } 200 handleRequestCancelPrintJob(final PrintJobInfo printJob)201 private void handleRequestCancelPrintJob(final PrintJobInfo printJob) { 202 throwIfDestroyed(); 203 if (!isBound()) { 204 ensureBound(); 205 mPendingCommands.add(new Runnable() { 206 @Override 207 public void run() { 208 handleRequestCancelPrintJob(printJob); 209 } 210 }); 211 } else { 212 if (DEBUG) { 213 Slog.i(LOG_TAG, "[user: " + mUserId + "] requestCancelPrintJob()"); 214 } 215 try { 216 mPrintService.requestCancelPrintJob(printJob); 217 } catch (RemoteException re) { 218 Slog.e(LOG_TAG, "Error canceling a pring job.", re); 219 } 220 } 221 } 222 onPrintJobQueued(PrintJobInfo printJob)223 public void onPrintJobQueued(PrintJobInfo printJob) { 224 mHandler.obtainMessage(MyHandler.MSG_ON_PRINT_JOB_QUEUED, 225 printJob).sendToTarget(); 226 } 227 handleOnPrintJobQueued(final PrintJobInfo printJob)228 private void handleOnPrintJobQueued(final PrintJobInfo printJob) { 229 throwIfDestroyed(); 230 mHasActivePrintJobs = true; 231 if (!isBound()) { 232 ensureBound(); 233 mPendingCommands.add(new Runnable() { 234 @Override 235 public void run() { 236 handleOnPrintJobQueued(printJob); 237 } 238 }); 239 } else { 240 if (DEBUG) { 241 Slog.i(LOG_TAG, "[user: " + mUserId + "] onPrintJobQueued()"); 242 } 243 try { 244 mPrintService.onPrintJobQueued(printJob); 245 } catch (RemoteException re) { 246 Slog.e(LOG_TAG, "Error announcing queued pring job.", re); 247 } 248 } 249 } 250 createPrinterDiscoverySession()251 public void createPrinterDiscoverySession() { 252 mHandler.sendEmptyMessage(MyHandler.MSG_CREATE_PRINTER_DISCOVERY_SESSION); 253 } 254 handleCreatePrinterDiscoverySession()255 private void handleCreatePrinterDiscoverySession() { 256 throwIfDestroyed(); 257 mHasPrinterDiscoverySession = true; 258 if (!isBound()) { 259 ensureBound(); 260 mPendingCommands.add(new Runnable() { 261 @Override 262 public void run() { 263 handleCreatePrinterDiscoverySession(); 264 } 265 }); 266 } else { 267 if (DEBUG) { 268 Slog.i(LOG_TAG, "[user: " + mUserId + "] createPrinterDiscoverySession()"); 269 } 270 try { 271 mPrintService.createPrinterDiscoverySession(); 272 } catch (RemoteException re) { 273 Slog.e(LOG_TAG, "Error creating printer dicovery session.", re); 274 } 275 } 276 } 277 destroyPrinterDiscoverySession()278 public void destroyPrinterDiscoverySession() { 279 mHandler.sendEmptyMessage(MyHandler.MSG_DESTROY_PRINTER_DISCOVERY_SESSION); 280 } 281 handleDestroyPrinterDiscoverySession()282 private void handleDestroyPrinterDiscoverySession() { 283 throwIfDestroyed(); 284 mHasPrinterDiscoverySession = false; 285 if (!isBound()) { 286 // The service is dead and neither has active jobs nor discovery 287 // session, so ensure we are unbound since the service has no work. 288 if (mServiceDied && !mHasActivePrintJobs) { 289 ensureUnbound(); 290 return; 291 } 292 ensureBound(); 293 mPendingCommands.add(new Runnable() { 294 @Override 295 public void run() { 296 handleDestroyPrinterDiscoverySession(); 297 } 298 }); 299 } else { 300 if (DEBUG) { 301 Slog.i(LOG_TAG, "[user: " + mUserId + "] destroyPrinterDiscoverySession()"); 302 } 303 try { 304 mPrintService.destroyPrinterDiscoverySession(); 305 } catch (RemoteException re) { 306 Slog.e(LOG_TAG, "Error destroying printer dicovery session.", re); 307 } 308 // If the service has no print jobs and no active discovery 309 // session anymore we should disconnect from it. 310 if (!mHasActivePrintJobs) { 311 ensureUnbound(); 312 } 313 } 314 } 315 startPrinterDiscovery(List<PrinterId> priorityList)316 public void startPrinterDiscovery(List<PrinterId> priorityList) { 317 mHandler.obtainMessage(MyHandler.MSG_START_PRINTER_DISCOVERY, 318 priorityList).sendToTarget(); 319 } 320 handleStartPrinterDiscovery(final List<PrinterId> priorityList)321 private void handleStartPrinterDiscovery(final List<PrinterId> priorityList) { 322 throwIfDestroyed(); 323 // Take a note that we are doing discovery. 324 mDiscoveryPriorityList = new ArrayList<PrinterId>(); 325 if (priorityList != null) { 326 mDiscoveryPriorityList.addAll(priorityList); 327 } 328 if (!isBound()) { 329 ensureBound(); 330 mPendingCommands.add(new Runnable() { 331 @Override 332 public void run() { 333 handleStartPrinterDiscovery(priorityList); 334 } 335 }); 336 } else { 337 if (DEBUG) { 338 Slog.i(LOG_TAG, "[user: " + mUserId + "] startPrinterDiscovery()"); 339 } 340 try { 341 mPrintService.startPrinterDiscovery(priorityList); 342 } catch (RemoteException re) { 343 Slog.e(LOG_TAG, "Error starting printer dicovery.", re); 344 } 345 } 346 } 347 stopPrinterDiscovery()348 public void stopPrinterDiscovery() { 349 mHandler.sendEmptyMessage(MyHandler.MSG_STOP_PRINTER_DISCOVERY); 350 } 351 handleStopPrinterDiscovery()352 private void handleStopPrinterDiscovery() { 353 throwIfDestroyed(); 354 // We are not doing discovery anymore. 355 mDiscoveryPriorityList = null; 356 if (!isBound()) { 357 ensureBound(); 358 mPendingCommands.add(new Runnable() { 359 @Override 360 public void run() { 361 handleStopPrinterDiscovery(); 362 } 363 }); 364 } else { 365 if (DEBUG) { 366 Slog.i(LOG_TAG, "[user: " + mUserId + "] stopPrinterDiscovery()"); 367 } 368 try { 369 mPrintService.stopPrinterDiscovery(); 370 } catch (RemoteException re) { 371 Slog.e(LOG_TAG, "Error stopping printer dicovery.", re); 372 } 373 } 374 } 375 validatePrinters(List<PrinterId> printerIds)376 public void validatePrinters(List<PrinterId> printerIds) { 377 mHandler.obtainMessage(MyHandler.MSG_VALIDATE_PRINTERS, 378 printerIds).sendToTarget(); 379 } 380 handleValidatePrinters(final List<PrinterId> printerIds)381 private void handleValidatePrinters(final List<PrinterId> printerIds) { 382 throwIfDestroyed(); 383 if (!isBound()) { 384 ensureBound(); 385 mPendingCommands.add(new Runnable() { 386 @Override 387 public void run() { 388 handleValidatePrinters(printerIds); 389 } 390 }); 391 } else { 392 if (DEBUG) { 393 Slog.i(LOG_TAG, "[user: " + mUserId + "] validatePrinters()"); 394 } 395 try { 396 mPrintService.validatePrinters(printerIds); 397 } catch (RemoteException re) { 398 Slog.e(LOG_TAG, "Error requesting printers validation.", re); 399 } 400 } 401 } 402 startPrinterStateTracking(PrinterId printerId)403 public void startPrinterStateTracking(PrinterId printerId) { 404 mHandler.obtainMessage(MyHandler.MSG_START_PRINTER_STATE_TRACKING, 405 printerId).sendToTarget(); 406 } 407 handleStartPrinterStateTracking(final PrinterId printerId)408 private void handleStartPrinterStateTracking(final PrinterId printerId) { 409 throwIfDestroyed(); 410 // Take a note we are tracking the printer. 411 if (mTrackedPrinterList == null) { 412 mTrackedPrinterList = new ArrayList<PrinterId>(); 413 } 414 mTrackedPrinterList.add(printerId); 415 if (!isBound()) { 416 ensureBound(); 417 mPendingCommands.add(new Runnable() { 418 @Override 419 public void run() { 420 handleStartPrinterStateTracking(printerId); 421 } 422 }); 423 } else { 424 if (DEBUG) { 425 Slog.i(LOG_TAG, "[user: " + mUserId + "] startPrinterTracking()"); 426 } 427 try { 428 mPrintService.startPrinterStateTracking(printerId); 429 } catch (RemoteException re) { 430 Slog.e(LOG_TAG, "Error requesting start printer tracking.", re); 431 } 432 } 433 } 434 stopPrinterStateTracking(PrinterId printerId)435 public void stopPrinterStateTracking(PrinterId printerId) { 436 mHandler.obtainMessage(MyHandler.MSG_STOP_PRINTER_STATE_TRACKING, 437 printerId).sendToTarget(); 438 } 439 handleStopPrinterStateTracking(final PrinterId printerId)440 private void handleStopPrinterStateTracking(final PrinterId printerId) { 441 throwIfDestroyed(); 442 // We are no longer tracking the printer. 443 if (mTrackedPrinterList == null || !mTrackedPrinterList.remove(printerId)) { 444 return; 445 } 446 if (mTrackedPrinterList.isEmpty()) { 447 mTrackedPrinterList = null; 448 } 449 if (!isBound()) { 450 ensureBound(); 451 mPendingCommands.add(new Runnable() { 452 @Override 453 public void run() { 454 handleStopPrinterStateTracking(printerId); 455 } 456 }); 457 } else { 458 if (DEBUG) { 459 Slog.i(LOG_TAG, "[user: " + mUserId + "] stopPrinterTracking()"); 460 } 461 try { 462 mPrintService.stopPrinterStateTracking(printerId); 463 } catch (RemoteException re) { 464 Slog.e(LOG_TAG, "Error requesting stop printer tracking.", re); 465 } 466 } 467 } 468 dump(PrintWriter pw, String prefix)469 public void dump(PrintWriter pw, String prefix) { 470 String tab = " "; 471 pw.append(prefix).append("service:").println(); 472 pw.append(prefix).append(tab).append("componentName=") 473 .append(mComponentName.flattenToString()).println(); 474 pw.append(prefix).append(tab).append("destroyed=") 475 .append(String.valueOf(mDestroyed)).println(); 476 pw.append(prefix).append(tab).append("bound=") 477 .append(String.valueOf(isBound())).println(); 478 pw.append(prefix).append(tab).append("hasDicoverySession=") 479 .append(String.valueOf(mHasPrinterDiscoverySession)).println(); 480 pw.append(prefix).append(tab).append("hasActivePrintJobs=") 481 .append(String.valueOf(mHasActivePrintJobs)).println(); 482 pw.append(prefix).append(tab).append("isDiscoveringPrinters=") 483 .append(String.valueOf(mDiscoveryPriorityList != null)).println(); 484 pw.append(prefix).append(tab).append("trackedPrinters=") 485 .append((mTrackedPrinterList != null) ? mTrackedPrinterList.toString() : "null"); 486 } 487 isBound()488 private boolean isBound() { 489 return mPrintService != null; 490 } 491 ensureBound()492 private void ensureBound() { 493 if (isBound() || mBinding) { 494 return; 495 } 496 if (DEBUG) { 497 Slog.i(LOG_TAG, "[user: " + mUserId + "] ensureBound()"); 498 } 499 mBinding = true; 500 mContext.bindServiceAsUser(mIntent, mServiceConnection, 501 Context.BIND_AUTO_CREATE, new UserHandle(mUserId)); 502 } 503 ensureUnbound()504 private void ensureUnbound() { 505 if (!isBound() && !mBinding) { 506 return; 507 } 508 if (DEBUG) { 509 Slog.i(LOG_TAG, "[user: " + mUserId + "] ensureUnbound()"); 510 } 511 mBinding = false; 512 mPendingCommands.clear(); 513 mHasActivePrintJobs = false; 514 mHasPrinterDiscoverySession = false; 515 mDiscoveryPriorityList = null; 516 mTrackedPrinterList = null; 517 if (isBound()) { 518 try { 519 mPrintService.setClient(null); 520 } catch (RemoteException re) { 521 /* ignore */ 522 } 523 mPrintService.asBinder().unlinkToDeath(this, 0); 524 mPrintService = null; 525 mContext.unbindService(mServiceConnection); 526 } 527 } 528 throwIfDestroyed()529 private void throwIfDestroyed() { 530 if (mDestroyed) { 531 throw new IllegalStateException("Cannot interact with a destroyed service"); 532 } 533 } 534 535 private class RemoteServiceConneciton implements ServiceConnection { 536 @Override onServiceConnected(ComponentName name, IBinder service)537 public void onServiceConnected(ComponentName name, IBinder service) { 538 if (mDestroyed || !mBinding) { 539 mContext.unbindService(mServiceConnection); 540 return; 541 } 542 mBinding = false; 543 mPrintService = IPrintService.Stub.asInterface(service); 544 try { 545 service.linkToDeath(RemotePrintService.this, 0); 546 } catch (RemoteException re) { 547 handleBinderDied(); 548 return; 549 } 550 try { 551 mPrintService.setClient(mPrintServiceClient); 552 } catch (RemoteException re) { 553 Slog.e(LOG_TAG, "Error setting client for: " + service, re); 554 handleBinderDied(); 555 return; 556 } 557 // If the service died and there is a discovery session, recreate it. 558 if (mServiceDied && mHasPrinterDiscoverySession) { 559 handleCreatePrinterDiscoverySession(); 560 } 561 // If the service died and there is discovery started, restart it. 562 if (mServiceDied && mDiscoveryPriorityList != null) { 563 handleStartPrinterDiscovery(mDiscoveryPriorityList); 564 } 565 // If the service died and printers were tracked, start tracking. 566 if (mServiceDied && mTrackedPrinterList != null) { 567 final int trackedPrinterCount = mTrackedPrinterList.size(); 568 for (int i = 0; i < trackedPrinterCount; i++) { 569 handleStartPrinterStateTracking(mTrackedPrinterList.get(i)); 570 } 571 } 572 // Finally, do all the pending work. 573 while (!mPendingCommands.isEmpty()) { 574 Runnable pendingCommand = mPendingCommands.remove(0); 575 pendingCommand.run(); 576 } 577 // We did a best effort to get to the last state if we crashed. 578 // If we do not have print jobs and no discovery is in progress, 579 // then no need to be bound. 580 if (!mHasPrinterDiscoverySession && !mHasActivePrintJobs) { 581 ensureUnbound(); 582 } 583 mServiceDied = false; 584 } 585 586 @Override onServiceDisconnected(ComponentName name)587 public void onServiceDisconnected(ComponentName name) { 588 mBinding = true; 589 } 590 } 591 592 private final class MyHandler extends Handler { 593 public static final int MSG_CREATE_PRINTER_DISCOVERY_SESSION = 1; 594 public static final int MSG_DESTROY_PRINTER_DISCOVERY_SESSION = 2; 595 public static final int MSG_START_PRINTER_DISCOVERY = 3; 596 public static final int MSG_STOP_PRINTER_DISCOVERY = 4; 597 public static final int MSG_VALIDATE_PRINTERS = 5; 598 public static final int MSG_START_PRINTER_STATE_TRACKING = 6; 599 public static final int MSG_STOP_PRINTER_STATE_TRACKING = 7; 600 public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 8; 601 public static final int MSG_ON_REQUEST_CANCEL_PRINT_JOB = 9; 602 public static final int MSG_ON_PRINT_JOB_QUEUED = 10; 603 public static final int MSG_DESTROY = 11; 604 public static final int MSG_BINDER_DIED = 12; 605 MyHandler(Looper looper)606 public MyHandler(Looper looper) { 607 super(looper, null, false); 608 } 609 610 @Override 611 @SuppressWarnings("unchecked") handleMessage(Message message)612 public void handleMessage(Message message) { 613 switch (message.what) { 614 case MSG_CREATE_PRINTER_DISCOVERY_SESSION: { 615 handleCreatePrinterDiscoverySession(); 616 } break; 617 618 case MSG_DESTROY_PRINTER_DISCOVERY_SESSION: { 619 handleDestroyPrinterDiscoverySession(); 620 } break; 621 622 case MSG_START_PRINTER_DISCOVERY: { 623 List<PrinterId> priorityList = (ArrayList<PrinterId>) message.obj; 624 handleStartPrinterDiscovery(priorityList); 625 } break; 626 627 case MSG_STOP_PRINTER_DISCOVERY: { 628 handleStopPrinterDiscovery(); 629 } break; 630 631 case MSG_VALIDATE_PRINTERS: { 632 List<PrinterId> printerIds = (List<PrinterId>) message.obj; 633 handleValidatePrinters(printerIds); 634 } break; 635 636 case MSG_START_PRINTER_STATE_TRACKING: { 637 PrinterId printerId = (PrinterId) message.obj; 638 handleStartPrinterStateTracking(printerId); 639 } break; 640 641 case MSG_STOP_PRINTER_STATE_TRACKING: { 642 PrinterId printerId = (PrinterId) message.obj; 643 handleStopPrinterStateTracking(printerId); 644 } break; 645 646 case MSG_ON_ALL_PRINT_JOBS_HANDLED: { 647 handleOnAllPrintJobsHandled(); 648 } break; 649 650 case MSG_ON_REQUEST_CANCEL_PRINT_JOB: { 651 PrintJobInfo printJob = (PrintJobInfo) message.obj; 652 handleRequestCancelPrintJob(printJob); 653 } break; 654 655 case MSG_ON_PRINT_JOB_QUEUED: { 656 PrintJobInfo printJob = (PrintJobInfo) message.obj; 657 handleOnPrintJobQueued(printJob); 658 } break; 659 660 case MSG_DESTROY: { 661 handleDestroy(); 662 } break; 663 664 case MSG_BINDER_DIED: { 665 handleBinderDied(); 666 } break; 667 } 668 } 669 } 670 671 private static final class RemotePrintServiceClient extends IPrintServiceClient.Stub { 672 private final WeakReference<RemotePrintService> mWeakService; 673 RemotePrintServiceClient(RemotePrintService service)674 public RemotePrintServiceClient(RemotePrintService service) { 675 mWeakService = new WeakReference<RemotePrintService>(service); 676 } 677 678 @Override getPrintJobInfos()679 public List<PrintJobInfo> getPrintJobInfos() { 680 RemotePrintService service = mWeakService.get(); 681 if (service != null) { 682 final long identity = Binder.clearCallingIdentity(); 683 try { 684 return service.mSpooler.getPrintJobInfos(service.mComponentName, 685 PrintJobInfo.STATE_ANY_SCHEDULED, PrintManager.APP_ID_ANY); 686 } finally { 687 Binder.restoreCallingIdentity(identity); 688 } 689 } 690 return null; 691 } 692 693 @Override getPrintJobInfo(PrintJobId printJobId)694 public PrintJobInfo getPrintJobInfo(PrintJobId printJobId) { 695 RemotePrintService service = mWeakService.get(); 696 if (service != null) { 697 final long identity = Binder.clearCallingIdentity(); 698 try { 699 return service.mSpooler.getPrintJobInfo(printJobId, 700 PrintManager.APP_ID_ANY); 701 } finally { 702 Binder.restoreCallingIdentity(identity); 703 } 704 } 705 return null; 706 } 707 708 @Override setPrintJobState(PrintJobId printJobId, int state, String error)709 public boolean setPrintJobState(PrintJobId printJobId, int state, String error) { 710 RemotePrintService service = mWeakService.get(); 711 if (service != null) { 712 final long identity = Binder.clearCallingIdentity(); 713 try { 714 return service.mSpooler.setPrintJobState(printJobId, state, error); 715 } finally { 716 Binder.restoreCallingIdentity(identity); 717 } 718 } 719 return false; 720 } 721 722 @Override setPrintJobTag(PrintJobId printJobId, String tag)723 public boolean setPrintJobTag(PrintJobId printJobId, String tag) { 724 RemotePrintService service = mWeakService.get(); 725 if (service != null) { 726 final long identity = Binder.clearCallingIdentity(); 727 try { 728 return service.mSpooler.setPrintJobTag(printJobId, tag); 729 } finally { 730 Binder.restoreCallingIdentity(identity); 731 } 732 } 733 return false; 734 } 735 736 @Override writePrintJobData(ParcelFileDescriptor fd, PrintJobId printJobId)737 public void writePrintJobData(ParcelFileDescriptor fd, PrintJobId printJobId) { 738 RemotePrintService service = mWeakService.get(); 739 if (service != null) { 740 final long identity = Binder.clearCallingIdentity(); 741 try { 742 service.mSpooler.writePrintJobData(fd, printJobId); 743 } finally { 744 Binder.restoreCallingIdentity(identity); 745 } 746 } 747 } 748 749 @Override 750 @SuppressWarnings({"rawtypes", "unchecked"}) onPrintersAdded(ParceledListSlice printers)751 public void onPrintersAdded(ParceledListSlice printers) { 752 RemotePrintService service = mWeakService.get(); 753 if (service != null) { 754 List<PrinterInfo> addedPrinters = (List<PrinterInfo>) printers.getList(); 755 throwIfPrinterIdsForPrinterInfoTampered(service.mComponentName, addedPrinters); 756 final long identity = Binder.clearCallingIdentity(); 757 try { 758 service.mCallbacks.onPrintersAdded(addedPrinters); 759 } finally { 760 Binder.restoreCallingIdentity(identity); 761 } 762 } 763 } 764 765 @Override 766 @SuppressWarnings({"rawtypes", "unchecked"}) onPrintersRemoved(ParceledListSlice printerIds)767 public void onPrintersRemoved(ParceledListSlice printerIds) { 768 RemotePrintService service = mWeakService.get(); 769 if (service != null) { 770 List<PrinterId> removedPrinterIds = (List<PrinterId>) printerIds.getList(); 771 throwIfPrinterIdsTampered(service.mComponentName, removedPrinterIds); 772 final long identity = Binder.clearCallingIdentity(); 773 try { 774 service.mCallbacks.onPrintersRemoved(removedPrinterIds); 775 } finally { 776 Binder.restoreCallingIdentity(identity); 777 } 778 } 779 } 780 throwIfPrinterIdsForPrinterInfoTampered(ComponentName serviceName, List<PrinterInfo> printerInfos)781 private void throwIfPrinterIdsForPrinterInfoTampered(ComponentName serviceName, 782 List<PrinterInfo> printerInfos) { 783 final int printerInfoCount = printerInfos.size(); 784 for (int i = 0; i < printerInfoCount; i++) { 785 PrinterId printerId = printerInfos.get(i).getId(); 786 throwIfPrinterIdTampered(serviceName, printerId); 787 } 788 } 789 throwIfPrinterIdsTampered(ComponentName serviceName, List<PrinterId> printerIds)790 private void throwIfPrinterIdsTampered(ComponentName serviceName, 791 List<PrinterId> printerIds) { 792 final int printerIdCount = printerIds.size(); 793 for (int i = 0; i < printerIdCount; i++) { 794 PrinterId printerId = printerIds.get(i); 795 throwIfPrinterIdTampered(serviceName, printerId); 796 } 797 } 798 throwIfPrinterIdTampered(ComponentName serviceName, PrinterId printerId)799 private void throwIfPrinterIdTampered(ComponentName serviceName, PrinterId printerId) { 800 if (printerId == null || printerId.getServiceName() == null 801 || !printerId.getServiceName().equals(serviceName)) { 802 throw new IllegalArgumentException("Invalid printer id: " + printerId); 803 } 804 } 805 } 806 } 807