UserState.java revision e8ae15482eef7617494cb3187899983e27cb8a35
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 17package com.android.server.print; 18 19import static android.content.pm.PackageManager.GET_META_DATA; 20import static android.content.pm.PackageManager.GET_SERVICES; 21import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; 22 23import android.annotation.NonNull; 24import android.annotation.Nullable; 25import android.app.PendingIntent; 26import android.content.ComponentName; 27import android.content.Context; 28import android.content.Intent; 29import android.content.IntentSender; 30import android.content.pm.ParceledListSlice; 31import android.content.pm.ResolveInfo; 32import android.graphics.drawable.Icon; 33import android.net.Uri; 34import android.os.AsyncTask; 35import android.os.Binder; 36import android.os.Bundle; 37import android.os.Handler; 38import android.os.IBinder; 39import android.os.IBinder.DeathRecipient; 40import android.os.Looper; 41import android.os.Message; 42import android.os.RemoteCallbackList; 43import android.os.RemoteException; 44import android.os.UserHandle; 45import android.print.IPrintDocumentAdapter; 46import android.print.IPrintJobStateChangeListener; 47import android.print.IPrintServicesChangeListener; 48import android.print.IPrinterDiscoveryObserver; 49import android.print.PrintAttributes; 50import android.print.PrintJobId; 51import android.print.PrintJobInfo; 52import android.print.PrintManager; 53import android.print.PrinterId; 54import android.print.PrinterInfo; 55import android.printservice.PrintServiceInfo; 56import android.provider.DocumentsContract; 57import android.provider.Settings; 58import android.text.TextUtils; 59import android.text.TextUtils.SimpleStringSplitter; 60import android.util.ArrayMap; 61import android.util.ArraySet; 62import android.util.Log; 63import android.util.Slog; 64import android.util.SparseArray; 65 66import com.android.internal.R; 67import com.android.internal.os.BackgroundThread; 68import com.android.internal.os.SomeArgs; 69import com.android.server.print.RemotePrintService.PrintServiceCallbacks; 70import com.android.server.print.RemotePrintSpooler.PrintSpoolerCallbacks; 71 72import java.io.FileDescriptor; 73import java.io.PrintWriter; 74import java.util.ArrayList; 75import java.util.Collections; 76import java.util.HashSet; 77import java.util.Iterator; 78import java.util.List; 79import java.util.Map; 80import java.util.Set; 81 82/** 83 * Represents the print state for a user. 84 */ 85final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { 86 87 private static final String LOG_TAG = "UserState"; 88 89 private static final boolean DEBUG = false; 90 91 private static final char COMPONENT_NAME_SEPARATOR = ':'; 92 93 private final SimpleStringSplitter mStringColonSplitter = 94 new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR); 95 96 private final Intent mQueryIntent = 97 new Intent(android.printservice.PrintService.SERVICE_INTERFACE); 98 99 private final ArrayMap<ComponentName, RemotePrintService> mActiveServices = 100 new ArrayMap<ComponentName, RemotePrintService>(); 101 102 private final List<PrintServiceInfo> mInstalledServices = 103 new ArrayList<PrintServiceInfo>(); 104 105 private final Set<ComponentName> mDisabledServices = 106 new ArraySet<ComponentName>(); 107 108 private final PrintJobForAppCache mPrintJobForAppCache = 109 new PrintJobForAppCache(); 110 111 private final Object mLock; 112 113 private final Context mContext; 114 115 private final int mUserId; 116 117 private final RemotePrintSpooler mSpooler; 118 119 private final Handler mHandler; 120 121 private PrinterDiscoverySessionMediator mPrinterDiscoverySession; 122 123 private List<PrintJobStateChangeListenerRecord> mPrintJobStateChangeListenerRecords; 124 125 private List<PrintServicesChangeListenerRecord> mPrintServicesChangeListenerRecords; 126 127 private boolean mDestroyed; 128 129 public UserState(Context context, int userId, Object lock, boolean lowPriority) { 130 mContext = context; 131 mUserId = userId; 132 mLock = lock; 133 mSpooler = new RemotePrintSpooler(context, userId, lowPriority, this); 134 mHandler = new UserStateHandler(context.getMainLooper()); 135 136 synchronized (mLock) { 137 readInstalledPrintServicesLocked(); 138 upgradePersistentStateIfNeeded(); 139 readDisabledPrintServicesLocked(); 140 141 // Some print services might have gotten installed before the User State came up 142 prunePrintServices(); 143 144 onConfigurationChangedLocked(); 145 } 146 } 147 148 public void increasePriority() { 149 mSpooler.increasePriority(); 150 } 151 152 @Override 153 public void onPrintJobQueued(PrintJobInfo printJob) { 154 final RemotePrintService service; 155 synchronized (mLock) { 156 throwIfDestroyedLocked(); 157 ComponentName printServiceName = printJob.getPrinterId().getServiceName(); 158 service = mActiveServices.get(printServiceName); 159 } 160 if (service != null) { 161 service.onPrintJobQueued(printJob); 162 } else { 163 // The service for the job is no longer enabled, so just 164 // fail the job with the appropriate message. 165 mSpooler.setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED, 166 mContext.getString(R.string.reason_service_unavailable)); 167 } 168 } 169 170 @Override 171 public void onAllPrintJobsForServiceHandled(ComponentName printService) { 172 final RemotePrintService service; 173 synchronized (mLock) { 174 throwIfDestroyedLocked(); 175 service = mActiveServices.get(printService); 176 } 177 if (service != null) { 178 service.onAllPrintJobsHandled(); 179 } 180 } 181 182 public void removeObsoletePrintJobs() { 183 mSpooler.removeObsoletePrintJobs(); 184 } 185 186 @SuppressWarnings("deprecation") 187 public Bundle print(@NonNull String printJobName, @NonNull IPrintDocumentAdapter adapter, 188 @Nullable PrintAttributes attributes, @NonNull String packageName, int appId) { 189 // Create print job place holder. 190 final PrintJobInfo printJob = new PrintJobInfo(); 191 printJob.setId(new PrintJobId()); 192 printJob.setAppId(appId); 193 printJob.setLabel(printJobName); 194 printJob.setAttributes(attributes); 195 printJob.setState(PrintJobInfo.STATE_CREATED); 196 printJob.setCopies(1); 197 printJob.setCreationTime(System.currentTimeMillis()); 198 199 // Track this job so we can forget it when the creator dies. 200 if (!mPrintJobForAppCache.onPrintJobCreated(adapter.asBinder(), appId, 201 printJob)) { 202 // Not adding a print job means the client is dead - done. 203 return null; 204 } 205 206 // Spin the spooler to add the job and show the config UI. 207 new AsyncTask<Void, Void, Void>() { 208 @Override 209 protected Void doInBackground(Void... params) { 210 mSpooler.createPrintJob(printJob); 211 return null; 212 } 213 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); 214 215 final long identity = Binder.clearCallingIdentity(); 216 try { 217 Intent intent = new Intent(PrintManager.ACTION_PRINT_DIALOG); 218 intent.setData(Uri.fromParts("printjob", printJob.getId().flattenToString(), null)); 219 intent.putExtra(PrintManager.EXTRA_PRINT_DOCUMENT_ADAPTER, adapter.asBinder()); 220 intent.putExtra(PrintManager.EXTRA_PRINT_JOB, printJob); 221 intent.putExtra(DocumentsContract.EXTRA_PACKAGE_NAME, packageName); 222 223 IntentSender intentSender = PendingIntent.getActivityAsUser( 224 mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT 225 | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE, 226 null, new UserHandle(mUserId)) .getIntentSender(); 227 228 Bundle result = new Bundle(); 229 result.putParcelable(PrintManager.EXTRA_PRINT_JOB, printJob); 230 result.putParcelable(PrintManager.EXTRA_PRINT_DIALOG_INTENT, intentSender); 231 232 return result; 233 } finally { 234 Binder.restoreCallingIdentity(identity); 235 } 236 } 237 238 public List<PrintJobInfo> getPrintJobInfos(int appId) { 239 List<PrintJobInfo> cachedPrintJobs = mPrintJobForAppCache.getPrintJobs(appId); 240 // Note that the print spooler is not storing print jobs that 241 // are in a terminal state as it is non-trivial to properly update 242 // the spooler state for when to forget print jobs in terminal state. 243 // Therefore, we fuse the cached print jobs for running apps (some 244 // jobs are in a terminal state) with the ones that the print 245 // spooler knows about (some jobs are being processed). 246 ArrayMap<PrintJobId, PrintJobInfo> result = 247 new ArrayMap<PrintJobId, PrintJobInfo>(); 248 249 // Add the cached print jobs for running apps. 250 final int cachedPrintJobCount = cachedPrintJobs.size(); 251 for (int i = 0; i < cachedPrintJobCount; i++) { 252 PrintJobInfo cachedPrintJob = cachedPrintJobs.get(i); 253 result.put(cachedPrintJob.getId(), cachedPrintJob); 254 // Strip out the tag and the advanced print options. 255 // They are visible only to print services. 256 cachedPrintJob.setTag(null); 257 cachedPrintJob.setAdvancedOptions(null); 258 } 259 260 // Add everything else the spooler knows about. 261 List<PrintJobInfo> printJobs = mSpooler.getPrintJobInfos(null, 262 PrintJobInfo.STATE_ANY, appId); 263 if (printJobs != null) { 264 final int printJobCount = printJobs.size(); 265 for (int i = 0; i < printJobCount; i++) { 266 PrintJobInfo printJob = printJobs.get(i); 267 result.put(printJob.getId(), printJob); 268 // Strip out the tag and the advanced print options. 269 // They are visible only to print services. 270 printJob.setTag(null); 271 printJob.setAdvancedOptions(null); 272 } 273 } 274 275 return new ArrayList<PrintJobInfo>(result.values()); 276 } 277 278 public PrintJobInfo getPrintJobInfo(@NonNull PrintJobId printJobId, int appId) { 279 PrintJobInfo printJob = mPrintJobForAppCache.getPrintJob(printJobId, appId); 280 if (printJob == null) { 281 printJob = mSpooler.getPrintJobInfo(printJobId, appId); 282 } 283 if (printJob != null) { 284 // Strip out the tag and the advanced print options. 285 // They are visible only to print services. 286 printJob.setTag(null); 287 printJob.setAdvancedOptions(null); 288 } 289 return printJob; 290 } 291 292 /** 293 * Get the custom icon for a printer. If the icon is not cached, the icon is 294 * requested asynchronously. Once it is available the printer is updated. 295 * 296 * @param printerId the id of the printer the icon should be loaded for 297 * @return the custom icon to be used for the printer or null if the icon is 298 * not yet available 299 * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon() 300 */ 301 public @Nullable Icon getCustomPrinterIcon(@NonNull PrinterId printerId) { 302 Icon icon = mSpooler.getCustomPrinterIcon(printerId); 303 304 if (icon == null) { 305 RemotePrintService service = mActiveServices.get(printerId.getServiceName()); 306 if (service != null) { 307 service.requestCustomPrinterIcon(printerId); 308 } 309 } 310 311 return icon; 312 } 313 314 public void cancelPrintJob(@NonNull PrintJobId printJobId, int appId) { 315 PrintJobInfo printJobInfo = mSpooler.getPrintJobInfo(printJobId, appId); 316 if (printJobInfo == null) { 317 return; 318 } 319 320 // Take a note that we are trying to cancel the job. 321 mSpooler.setPrintJobCancelling(printJobId, true); 322 323 if (printJobInfo.getState() != PrintJobInfo.STATE_FAILED) { 324 PrinterId printerId = printJobInfo.getPrinterId(); 325 326 if (printerId != null) { 327 ComponentName printServiceName = printerId.getServiceName(); 328 RemotePrintService printService = null; 329 synchronized (mLock) { 330 printService = mActiveServices.get(printServiceName); 331 } 332 if (printService == null) { 333 return; 334 } 335 printService.onRequestCancelPrintJob(printJobInfo); 336 } 337 } else { 338 // If the print job is failed we do not need cooperation 339 // from the print service. 340 mSpooler.setPrintJobState(printJobId, PrintJobInfo.STATE_CANCELED, null); 341 } 342 } 343 344 public void restartPrintJob(@NonNull PrintJobId printJobId, int appId) { 345 PrintJobInfo printJobInfo = getPrintJobInfo(printJobId, appId); 346 if (printJobInfo == null || printJobInfo.getState() != PrintJobInfo.STATE_FAILED) { 347 return; 348 } 349 mSpooler.setPrintJobState(printJobId, PrintJobInfo.STATE_QUEUED, null); 350 } 351 352 public @Nullable List<PrintServiceInfo> getPrintServices(int selectionFlags) { 353 synchronized (mLock) { 354 List<PrintServiceInfo> selectedServices = null; 355 final int installedServiceCount = mInstalledServices.size(); 356 for (int i = 0; i < installedServiceCount; i++) { 357 PrintServiceInfo installedService = mInstalledServices.get(i); 358 359 ComponentName componentName = new ComponentName( 360 installedService.getResolveInfo().serviceInfo.packageName, 361 installedService.getResolveInfo().serviceInfo.name); 362 363 // Update isEnabled under the same lock the final returned list is created 364 installedService.setIsEnabled(mActiveServices.containsKey(componentName)); 365 366 if (installedService.isEnabled()) { 367 if ((selectionFlags & PrintManager.ENABLED_SERVICES) == 0) { 368 continue; 369 } 370 } else { 371 if ((selectionFlags & PrintManager.DISABLED_SERVICES) == 0) { 372 continue; 373 } 374 } 375 376 if (selectedServices == null) { 377 selectedServices = new ArrayList<>(); 378 } 379 selectedServices.add(installedService); 380 } 381 return selectedServices; 382 } 383 } 384 385 public void setPrintServiceEnabled(@NonNull ComponentName serviceName, boolean isEnabled) { 386 synchronized (mLock) { 387 boolean isChanged = false; 388 if (isEnabled) { 389 isChanged = mDisabledServices.remove(serviceName); 390 } else { 391 // Make sure to only disable services that are currently installed 392 final int numServices = mInstalledServices.size(); 393 for (int i = 0; i < numServices; i++) { 394 PrintServiceInfo service = mInstalledServices.get(i); 395 396 if (service.getComponentName().equals(serviceName)) { 397 mDisabledServices.add(serviceName); 398 isChanged = true; 399 break; 400 } 401 } 402 } 403 404 if (isChanged) { 405 writeDisabledPrintServicesLocked(mDisabledServices); 406 407 onConfigurationChangedLocked(); 408 } 409 } 410 } 411 412 public void createPrinterDiscoverySession(@NonNull IPrinterDiscoveryObserver observer) { 413 synchronized (mLock) { 414 throwIfDestroyedLocked(); 415 416 if (mPrinterDiscoverySession == null) { 417 mSpooler.clearCustomPrinterIconCache(); 418 419 // If we do not have a session, tell all service to create one. 420 mPrinterDiscoverySession = new PrinterDiscoverySessionMediator(mContext) { 421 @Override 422 public void onDestroyed() { 423 mPrinterDiscoverySession = null; 424 } 425 }; 426 // Add the observer to the brand new session. 427 mPrinterDiscoverySession.addObserverLocked(observer); 428 } else { 429 // If services have created session, just add the observer. 430 mPrinterDiscoverySession.addObserverLocked(observer); 431 } 432 } 433 } 434 435 public void destroyPrinterDiscoverySession(@NonNull IPrinterDiscoveryObserver observer) { 436 synchronized (mLock) { 437 // Already destroyed - nothing to do. 438 if (mPrinterDiscoverySession == null) { 439 return; 440 } 441 // Remove this observer. 442 mPrinterDiscoverySession.removeObserverLocked(observer); 443 } 444 } 445 446 public void startPrinterDiscovery(@NonNull IPrinterDiscoveryObserver observer, 447 @Nullable List<PrinterId> printerIds) { 448 synchronized (mLock) { 449 throwIfDestroyedLocked(); 450 451 // No session - nothing to do. 452 if (mPrinterDiscoverySession == null) { 453 return; 454 } 455 // Kick of discovery. 456 mPrinterDiscoverySession.startPrinterDiscoveryLocked(observer, 457 printerIds); 458 } 459 } 460 461 public void stopPrinterDiscovery(@NonNull IPrinterDiscoveryObserver observer) { 462 synchronized (mLock) { 463 throwIfDestroyedLocked(); 464 465 // No session - nothing to do. 466 if (mPrinterDiscoverySession == null) { 467 return; 468 } 469 // Kick of discovery. 470 mPrinterDiscoverySession.stopPrinterDiscoveryLocked(observer); 471 } 472 } 473 474 public void validatePrinters(@NonNull List<PrinterId> printerIds) { 475 synchronized (mLock) { 476 throwIfDestroyedLocked(); 477 // No services - nothing to do. 478 if (mActiveServices.isEmpty()) { 479 return; 480 } 481 // No session - nothing to do. 482 if (mPrinterDiscoverySession == null) { 483 return; 484 } 485 // Request an updated. 486 mPrinterDiscoverySession.validatePrintersLocked(printerIds); 487 } 488 } 489 490 public void startPrinterStateTracking(@NonNull PrinterId printerId) { 491 synchronized (mLock) { 492 throwIfDestroyedLocked(); 493 // No services - nothing to do. 494 if (mActiveServices.isEmpty()) { 495 return; 496 } 497 // No session - nothing to do. 498 if (mPrinterDiscoverySession == null) { 499 return; 500 } 501 // Request start tracking the printer. 502 mPrinterDiscoverySession.startPrinterStateTrackingLocked(printerId); 503 } 504 } 505 506 public void stopPrinterStateTracking(PrinterId printerId) { 507 synchronized (mLock) { 508 throwIfDestroyedLocked(); 509 // No services - nothing to do. 510 if (mActiveServices.isEmpty()) { 511 return; 512 } 513 // No session - nothing to do. 514 if (mPrinterDiscoverySession == null) { 515 return; 516 } 517 // Request stop tracking the printer. 518 mPrinterDiscoverySession.stopPrinterStateTrackingLocked(printerId); 519 } 520 } 521 522 public void addPrintJobStateChangeListener(@NonNull IPrintJobStateChangeListener listener, 523 int appId) throws RemoteException { 524 synchronized (mLock) { 525 throwIfDestroyedLocked(); 526 if (mPrintJobStateChangeListenerRecords == null) { 527 mPrintJobStateChangeListenerRecords = 528 new ArrayList<PrintJobStateChangeListenerRecord>(); 529 } 530 mPrintJobStateChangeListenerRecords.add( 531 new PrintJobStateChangeListenerRecord(listener, appId) { 532 @Override 533 public void onBinderDied() { 534 mPrintJobStateChangeListenerRecords.remove(this); 535 } 536 }); 537 } 538 } 539 540 public void removePrintJobStateChangeListener(@NonNull IPrintJobStateChangeListener listener) { 541 synchronized (mLock) { 542 throwIfDestroyedLocked(); 543 if (mPrintJobStateChangeListenerRecords == null) { 544 return; 545 } 546 final int recordCount = mPrintJobStateChangeListenerRecords.size(); 547 for (int i = 0; i < recordCount; i++) { 548 PrintJobStateChangeListenerRecord record = 549 mPrintJobStateChangeListenerRecords.get(i); 550 if (record.listener.asBinder().equals(listener.asBinder())) { 551 mPrintJobStateChangeListenerRecords.remove(i); 552 break; 553 } 554 } 555 if (mPrintJobStateChangeListenerRecords.isEmpty()) { 556 mPrintJobStateChangeListenerRecords = null; 557 } 558 } 559 } 560 561 public void addPrintServicesChangeListener(@NonNull IPrintServicesChangeListener listener) 562 throws RemoteException { 563 synchronized (mLock) { 564 throwIfDestroyedLocked(); 565 if (mPrintServicesChangeListenerRecords == null) { 566 mPrintServicesChangeListenerRecords = new ArrayList<>(); 567 } 568 mPrintServicesChangeListenerRecords.add( 569 new PrintServicesChangeListenerRecord(listener) { 570 @Override 571 public void onBinderDied() { 572 mPrintServicesChangeListenerRecords.remove(this); 573 } 574 }); 575 } 576 } 577 578 public void removePrintServicesChangeListener(@NonNull IPrintServicesChangeListener listener) { 579 synchronized (mLock) { 580 throwIfDestroyedLocked(); 581 if (mPrintServicesChangeListenerRecords == null) { 582 return; 583 } 584 final int recordCount = mPrintServicesChangeListenerRecords.size(); 585 for (int i = 0; i < recordCount; i++) { 586 PrintServicesChangeListenerRecord record = 587 mPrintServicesChangeListenerRecords.get(i); 588 if (record.listener.asBinder().equals(listener.asBinder())) { 589 mPrintServicesChangeListenerRecords.remove(i); 590 break; 591 } 592 } 593 if (mPrintServicesChangeListenerRecords.isEmpty()) { 594 mPrintServicesChangeListenerRecords = null; 595 } 596 } 597 } 598 599 @Override 600 public void onPrintJobStateChanged(PrintJobInfo printJob) { 601 mPrintJobForAppCache.onPrintJobStateChanged(printJob); 602 mHandler.obtainMessage(UserStateHandler.MSG_DISPATCH_PRINT_JOB_STATE_CHANGED, 603 printJob.getAppId(), 0, printJob.getId()).sendToTarget(); 604 } 605 606 public void onPrintServicesChanged() { 607 mHandler.obtainMessage(UserStateHandler.MSG_DISPATCH_PRINT_SERVICES_CHANGED).sendToTarget(); 608 } 609 610 @Override 611 public void onPrintersAdded(List<PrinterInfo> printers) { 612 synchronized (mLock) { 613 throwIfDestroyedLocked(); 614 // No services - nothing to do. 615 if (mActiveServices.isEmpty()) { 616 return; 617 } 618 // No session - nothing to do. 619 if (mPrinterDiscoverySession == null) { 620 return; 621 } 622 mPrinterDiscoverySession.onPrintersAddedLocked(printers); 623 } 624 } 625 626 @Override 627 public void onPrintersRemoved(List<PrinterId> printerIds) { 628 synchronized (mLock) { 629 throwIfDestroyedLocked(); 630 // No services - nothing to do. 631 if (mActiveServices.isEmpty()) { 632 return; 633 } 634 // No session - nothing to do. 635 if (mPrinterDiscoverySession == null) { 636 return; 637 } 638 mPrinterDiscoverySession.onPrintersRemovedLocked(printerIds); 639 } 640 } 641 642 @Override 643 public void onCustomPrinterIconLoaded(PrinterId printerId, Icon icon) { 644 synchronized (mLock) { 645 throwIfDestroyedLocked(); 646 647 // No session - nothing to do. 648 if (mPrinterDiscoverySession == null) { 649 return; 650 } 651 mSpooler.onCustomPrinterIconLoaded(printerId, icon); 652 mPrinterDiscoverySession.onCustomPrinterIconLoadedLocked(printerId); 653 } 654 } 655 656 @Override 657 public void onServiceDied(RemotePrintService service) { 658 synchronized (mLock) { 659 throwIfDestroyedLocked(); 660 // No services - nothing to do. 661 if (mActiveServices.isEmpty()) { 662 return; 663 } 664 // Fail all print jobs. 665 failActivePrintJobsForService(service.getComponentName()); 666 service.onAllPrintJobsHandled(); 667 // No session - nothing to do. 668 if (mPrinterDiscoverySession == null) { 669 return; 670 } 671 mPrinterDiscoverySession.onServiceDiedLocked(service); 672 } 673 } 674 675 public void updateIfNeededLocked() { 676 throwIfDestroyedLocked(); 677 if (readConfigurationLocked()) { 678 onConfigurationChangedLocked(); 679 } 680 } 681 682 public void destroyLocked() { 683 throwIfDestroyedLocked(); 684 mSpooler.destroy(); 685 for (RemotePrintService service : mActiveServices.values()) { 686 service.destroy(); 687 } 688 mActiveServices.clear(); 689 mInstalledServices.clear(); 690 mDisabledServices.clear(); 691 if (mPrinterDiscoverySession != null) { 692 mPrinterDiscoverySession.destroyLocked(); 693 mPrinterDiscoverySession = null; 694 } 695 mDestroyed = true; 696 } 697 698 public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String prefix) { 699 pw.append(prefix).append("user state ").append(String.valueOf(mUserId)).append(":"); 700 pw.println(); 701 702 String tab = " "; 703 704 pw.append(prefix).append(tab).append("installed services:").println(); 705 final int installedServiceCount = mInstalledServices.size(); 706 for (int i = 0; i < installedServiceCount; i++) { 707 PrintServiceInfo installedService = mInstalledServices.get(i); 708 String installedServicePrefix = prefix + tab + tab; 709 pw.append(installedServicePrefix).append("service:").println(); 710 ResolveInfo resolveInfo = installedService.getResolveInfo(); 711 ComponentName componentName = new ComponentName( 712 resolveInfo.serviceInfo.packageName, 713 resolveInfo.serviceInfo.name); 714 pw.append(installedServicePrefix).append(tab).append("componentName=") 715 .append(componentName.flattenToString()).println(); 716 pw.append(installedServicePrefix).append(tab).append("settingsActivity=") 717 .append(installedService.getSettingsActivityName()).println(); 718 pw.append(installedServicePrefix).append(tab).append("addPrintersActivity=") 719 .append(installedService.getAddPrintersActivityName()).println(); 720 pw.append(installedServicePrefix).append(tab).append("avancedOptionsActivity=") 721 .append(installedService.getAdvancedOptionsActivityName()).println(); 722 } 723 724 pw.append(prefix).append(tab).append("disabled services:").println(); 725 for (ComponentName disabledService : mDisabledServices) { 726 String disabledServicePrefix = prefix + tab + tab; 727 pw.append(disabledServicePrefix).append("service:").println(); 728 pw.append(disabledServicePrefix).append(tab).append("componentName=") 729 .append(disabledService.flattenToString()); 730 pw.println(); 731 } 732 733 pw.append(prefix).append(tab).append("active services:").println(); 734 final int activeServiceCount = mActiveServices.size(); 735 for (int i = 0; i < activeServiceCount; i++) { 736 RemotePrintService activeService = mActiveServices.valueAt(i); 737 activeService.dump(pw, prefix + tab + tab); 738 pw.println(); 739 } 740 741 pw.append(prefix).append(tab).append("cached print jobs:").println(); 742 mPrintJobForAppCache.dump(pw, prefix + tab + tab); 743 744 pw.append(prefix).append(tab).append("discovery mediator:").println(); 745 if (mPrinterDiscoverySession != null) { 746 mPrinterDiscoverySession.dump(pw, prefix + tab + tab); 747 } 748 749 pw.append(prefix).append(tab).append("print spooler:").println(); 750 mSpooler.dump(fd, pw, prefix + tab + tab); 751 pw.println(); 752 } 753 754 private boolean readConfigurationLocked() { 755 boolean somethingChanged = false; 756 somethingChanged |= readInstalledPrintServicesLocked(); 757 somethingChanged |= readDisabledPrintServicesLocked(); 758 return somethingChanged; 759 } 760 761 private boolean readInstalledPrintServicesLocked() { 762 Set<PrintServiceInfo> tempPrintServices = new HashSet<PrintServiceInfo>(); 763 764 List<ResolveInfo> installedServices = mContext.getPackageManager() 765 .queryIntentServicesAsUser(mQueryIntent, 766 GET_SERVICES | GET_META_DATA | MATCH_DEBUG_TRIAGED_MISSING, mUserId); 767 768 final int installedCount = installedServices.size(); 769 for (int i = 0, count = installedCount; i < count; i++) { 770 ResolveInfo installedService = installedServices.get(i); 771 if (!android.Manifest.permission.BIND_PRINT_SERVICE.equals( 772 installedService.serviceInfo.permission)) { 773 ComponentName serviceName = new ComponentName( 774 installedService.serviceInfo.packageName, 775 installedService.serviceInfo.name); 776 Slog.w(LOG_TAG, "Skipping print service " 777 + serviceName.flattenToShortString() 778 + " since it does not require permission " 779 + android.Manifest.permission.BIND_PRINT_SERVICE); 780 continue; 781 } 782 tempPrintServices.add(PrintServiceInfo.create(installedService, mContext)); 783 } 784 785 boolean someServiceChanged = false; 786 787 if (tempPrintServices.size() != mInstalledServices.size()) { 788 someServiceChanged = true; 789 } else { 790 for (PrintServiceInfo newService: tempPrintServices) { 791 final int oldServiceIndex = mInstalledServices.indexOf(newService); 792 if (oldServiceIndex < 0) { 793 someServiceChanged = true; 794 break; 795 } 796 // PrintServiceInfo#equals compares only the id not all members, 797 // so we are also comparing the members coming from meta-data. 798 PrintServiceInfo oldService = mInstalledServices.get(oldServiceIndex); 799 if (!TextUtils.equals(oldService.getAddPrintersActivityName(), 800 newService.getAddPrintersActivityName()) 801 || !TextUtils.equals(oldService.getAdvancedOptionsActivityName(), 802 newService.getAdvancedOptionsActivityName()) 803 || !TextUtils.equals(oldService.getSettingsActivityName(), 804 newService.getSettingsActivityName())) { 805 someServiceChanged = true; 806 break; 807 } 808 } 809 } 810 811 if (someServiceChanged) { 812 mInstalledServices.clear(); 813 mInstalledServices.addAll(tempPrintServices); 814 return true; 815 } 816 817 return false; 818 } 819 820 /** 821 * Update persistent state from a previous version of Android. 822 */ 823 private void upgradePersistentStateIfNeeded() { 824 String enabledSettingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(), 825 Settings.Secure.ENABLED_PRINT_SERVICES, mUserId); 826 827 // Pre N we store the enabled services, in N and later we store the disabled services. 828 // Hence if enabledSettingValue is still set, we need to upgrade. 829 if (enabledSettingValue != null) { 830 Set<ComponentName> enabledServiceNameSet = new HashSet<ComponentName>(); 831 readPrintServicesFromSettingLocked(Settings.Secure.ENABLED_PRINT_SERVICES, 832 enabledServiceNameSet); 833 834 ArraySet<ComponentName> disabledServices = new ArraySet<>(); 835 final int numInstalledServices = mInstalledServices.size(); 836 for (int i = 0; i < numInstalledServices; i++) { 837 ComponentName serviceName = mInstalledServices.get(i).getComponentName(); 838 if (!enabledServiceNameSet.contains(serviceName)) { 839 disabledServices.add(serviceName); 840 } 841 } 842 843 writeDisabledPrintServicesLocked(disabledServices); 844 845 // We won't needed ENABLED_PRINT_SERVICES anymore, set to null to prevent upgrade to run 846 // again. 847 Settings.Secure.putStringForUser(mContext.getContentResolver(), 848 Settings.Secure.ENABLED_PRINT_SERVICES, null, mUserId); 849 } 850 } 851 852 /** 853 * Read the set of disabled print services from the secure settings. 854 * 855 * @return true if the state changed. 856 */ 857 private boolean readDisabledPrintServicesLocked() { 858 Set<ComponentName> tempDisabledServiceNameSet = new HashSet<ComponentName>(); 859 readPrintServicesFromSettingLocked(Settings.Secure.DISABLED_PRINT_SERVICES, 860 tempDisabledServiceNameSet); 861 if (!tempDisabledServiceNameSet.equals(mDisabledServices)) { 862 mDisabledServices.clear(); 863 mDisabledServices.addAll(tempDisabledServiceNameSet); 864 return true; 865 } 866 return false; 867 } 868 869 private void readPrintServicesFromSettingLocked(String setting, 870 Set<ComponentName> outServiceNames) { 871 String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(), 872 setting, mUserId); 873 if (!TextUtils.isEmpty(settingValue)) { 874 TextUtils.SimpleStringSplitter splitter = mStringColonSplitter; 875 splitter.setString(settingValue); 876 while (splitter.hasNext()) { 877 String string = splitter.next(); 878 if (TextUtils.isEmpty(string)) { 879 continue; 880 } 881 ComponentName componentName = ComponentName.unflattenFromString(string); 882 if (componentName != null) { 883 outServiceNames.add(componentName); 884 } 885 } 886 } 887 } 888 889 /** 890 * Persist the disabled print services to the secure settings. 891 */ 892 private void writeDisabledPrintServicesLocked(Set<ComponentName> disabledServices) { 893 StringBuilder builder = new StringBuilder(); 894 for (ComponentName componentName : disabledServices) { 895 if (builder.length() > 0) { 896 builder.append(COMPONENT_NAME_SEPARATOR); 897 } 898 builder.append(componentName.flattenToShortString()); 899 } 900 Settings.Secure.putStringForUser(mContext.getContentResolver(), 901 Settings.Secure.DISABLED_PRINT_SERVICES, builder.toString(), mUserId); 902 } 903 904 /** 905 * Get the {@link ComponentName names} of the installed print services 906 * 907 * @return The names of the installed print services 908 */ 909 private ArrayList<ComponentName> getInstalledComponents() { 910 ArrayList<ComponentName> installedComponents = new ArrayList<ComponentName>(); 911 912 final int installedCount = mInstalledServices.size(); 913 for (int i = 0; i < installedCount; i++) { 914 ResolveInfo resolveInfo = mInstalledServices.get(i).getResolveInfo(); 915 ComponentName serviceName = new ComponentName(resolveInfo.serviceInfo.packageName, 916 resolveInfo.serviceInfo.name); 917 918 installedComponents.add(serviceName); 919 } 920 921 return installedComponents; 922 } 923 924 /** 925 * Prune persistent state if a print service was uninstalled 926 */ 927 public void prunePrintServices() { 928 synchronized (mLock) { 929 ArrayList<ComponentName> installedComponents = getInstalledComponents(); 930 931 // Remove unnecessary entries from persistent state "disabled services" 932 boolean disabledServicesUninstalled = mDisabledServices.retainAll(installedComponents); 933 if (disabledServicesUninstalled) { 934 writeDisabledPrintServicesLocked(mDisabledServices); 935 } 936 937 // Remove unnecessary entries from persistent state "approved services" 938 mSpooler.pruneApprovedPrintServices(installedComponents); 939 } 940 } 941 942 private void onConfigurationChangedLocked() { 943 ArrayList<ComponentName> installedComponents = getInstalledComponents(); 944 945 final int installedCount = installedComponents.size(); 946 for (int i = 0; i < installedCount; i++) { 947 ComponentName serviceName = installedComponents.get(i); 948 949 if (!mDisabledServices.contains(serviceName)) { 950 if (!mActiveServices.containsKey(serviceName)) { 951 RemotePrintService service = new RemotePrintService( 952 mContext, serviceName, mUserId, mSpooler, this); 953 addServiceLocked(service); 954 } 955 } else { 956 RemotePrintService service = mActiveServices.remove(serviceName); 957 if (service != null) { 958 removeServiceLocked(service); 959 } 960 } 961 } 962 963 Iterator<Map.Entry<ComponentName, RemotePrintService>> iterator = 964 mActiveServices.entrySet().iterator(); 965 while (iterator.hasNext()) { 966 Map.Entry<ComponentName, RemotePrintService> entry = iterator.next(); 967 ComponentName serviceName = entry.getKey(); 968 RemotePrintService service = entry.getValue(); 969 if (!installedComponents.contains(serviceName)) { 970 removeServiceLocked(service); 971 iterator.remove(); 972 } 973 } 974 975 onPrintServicesChanged(); 976 } 977 978 private void addServiceLocked(RemotePrintService service) { 979 mActiveServices.put(service.getComponentName(), service); 980 if (mPrinterDiscoverySession != null) { 981 mPrinterDiscoverySession.onServiceAddedLocked(service); 982 } 983 } 984 985 private void removeServiceLocked(RemotePrintService service) { 986 // Fail all print jobs. 987 failActivePrintJobsForService(service.getComponentName()); 988 // If discovery is in progress, tear down the service. 989 if (mPrinterDiscoverySession != null) { 990 mPrinterDiscoverySession.onServiceRemovedLocked(service); 991 } else { 992 // Otherwise, just destroy it. 993 service.destroy(); 994 } 995 } 996 997 private void failActivePrintJobsForService(final ComponentName serviceName) { 998 // Makes sure all active print jobs are failed since the service 999 // just died. Do this off the main thread since we do to allow 1000 // calls into the spooler on the main thread. 1001 if (Looper.getMainLooper().isCurrentThread()) { 1002 BackgroundThread.getHandler().post(new Runnable() { 1003 @Override 1004 public void run() { 1005 failScheduledPrintJobsForServiceInternal(serviceName); 1006 } 1007 }); 1008 } else { 1009 failScheduledPrintJobsForServiceInternal(serviceName); 1010 } 1011 } 1012 1013 private void failScheduledPrintJobsForServiceInternal(ComponentName serviceName) { 1014 List<PrintJobInfo> printJobs = mSpooler.getPrintJobInfos(serviceName, 1015 PrintJobInfo.STATE_ANY_SCHEDULED, PrintManager.APP_ID_ANY); 1016 if (printJobs == null) { 1017 return; 1018 } 1019 final long identity = Binder.clearCallingIdentity(); 1020 try { 1021 final int printJobCount = printJobs.size(); 1022 for (int i = 0; i < printJobCount; i++) { 1023 PrintJobInfo printJob = printJobs.get(i); 1024 mSpooler.setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED, 1025 mContext.getString(R.string.reason_service_unavailable)); 1026 } 1027 } finally { 1028 Binder.restoreCallingIdentity(identity); 1029 } 1030 } 1031 1032 private void throwIfDestroyedLocked() { 1033 if (mDestroyed) { 1034 throw new IllegalStateException("Cannot interact with a destroyed instance."); 1035 } 1036 } 1037 1038 private void handleDispatchPrintJobStateChanged(PrintJobId printJobId, int appId) { 1039 final List<PrintJobStateChangeListenerRecord> records; 1040 synchronized (mLock) { 1041 if (mPrintJobStateChangeListenerRecords == null) { 1042 return; 1043 } 1044 records = new ArrayList<PrintJobStateChangeListenerRecord>( 1045 mPrintJobStateChangeListenerRecords); 1046 } 1047 final int recordCount = records.size(); 1048 for (int i = 0; i < recordCount; i++) { 1049 PrintJobStateChangeListenerRecord record = records.get(i); 1050 if (record.appId == PrintManager.APP_ID_ANY 1051 || record.appId == appId) 1052 try { 1053 record.listener.onPrintJobStateChanged(printJobId); 1054 } catch (RemoteException re) { 1055 Log.e(LOG_TAG, "Error notifying for print job state change", re); 1056 } 1057 } 1058 } 1059 1060 private void handleDispatchPrintServicesChanged() { 1061 final List<PrintServicesChangeListenerRecord> records; 1062 synchronized (mLock) { 1063 if (mPrintServicesChangeListenerRecords == null) { 1064 return; 1065 } 1066 records = new ArrayList<>(mPrintServicesChangeListenerRecords); 1067 } 1068 final int recordCount = records.size(); 1069 for (int i = 0; i < recordCount; i++) { 1070 PrintServicesChangeListenerRecord record = records.get(i); 1071 1072 try { 1073 record.listener.onPrintServicesChanged();; 1074 } catch (RemoteException re) { 1075 Log.e(LOG_TAG, "Error notifying for print services change", re); 1076 } 1077 } 1078 } 1079 1080 private final class UserStateHandler extends Handler { 1081 public static final int MSG_DISPATCH_PRINT_JOB_STATE_CHANGED = 1; 1082 public static final int MSG_DISPATCH_PRINT_SERVICES_CHANGED = 2; 1083 1084 public UserStateHandler(Looper looper) { 1085 super(looper, null, false); 1086 } 1087 1088 @Override 1089 public void handleMessage(Message message) { 1090 switch (message.what) { 1091 case MSG_DISPATCH_PRINT_JOB_STATE_CHANGED: 1092 PrintJobId printJobId = (PrintJobId) message.obj; 1093 final int appId = message.arg1; 1094 handleDispatchPrintJobStateChanged(printJobId, appId); 1095 break; 1096 case MSG_DISPATCH_PRINT_SERVICES_CHANGED: 1097 handleDispatchPrintServicesChanged(); 1098 break; 1099 default: 1100 // not reached 1101 } 1102 } 1103 } 1104 1105 private abstract class PrintJobStateChangeListenerRecord implements DeathRecipient { 1106 @NonNull final IPrintJobStateChangeListener listener; 1107 final int appId; 1108 1109 public PrintJobStateChangeListenerRecord(@NonNull IPrintJobStateChangeListener listener, 1110 int appId) throws RemoteException { 1111 this.listener = listener; 1112 this.appId = appId; 1113 listener.asBinder().linkToDeath(this, 0); 1114 } 1115 1116 @Override 1117 public void binderDied() { 1118 listener.asBinder().unlinkToDeath(this, 0); 1119 onBinderDied(); 1120 } 1121 1122 public abstract void onBinderDied(); 1123 } 1124 1125 private abstract class PrintServicesChangeListenerRecord implements DeathRecipient { 1126 @NonNull final IPrintServicesChangeListener listener; 1127 1128 public PrintServicesChangeListenerRecord(@NonNull IPrintServicesChangeListener listener) throws RemoteException { 1129 this.listener = listener; 1130 listener.asBinder().linkToDeath(this, 0); 1131 } 1132 1133 @Override 1134 public void binderDied() { 1135 listener.asBinder().unlinkToDeath(this, 0); 1136 onBinderDied(); 1137 } 1138 1139 public abstract void onBinderDied(); 1140 } 1141 1142 private class PrinterDiscoverySessionMediator { 1143 private final ArrayMap<PrinterId, PrinterInfo> mPrinters = 1144 new ArrayMap<PrinterId, PrinterInfo>(); 1145 1146 private final RemoteCallbackList<IPrinterDiscoveryObserver> mDiscoveryObservers = 1147 new RemoteCallbackList<IPrinterDiscoveryObserver>() { 1148 @Override 1149 public void onCallbackDied(IPrinterDiscoveryObserver observer) { 1150 synchronized (mLock) { 1151 stopPrinterDiscoveryLocked(observer); 1152 removeObserverLocked(observer); 1153 } 1154 } 1155 }; 1156 1157 private final List<IBinder> mStartedPrinterDiscoveryTokens = new ArrayList<IBinder>(); 1158 1159 private final List<PrinterId> mStateTrackedPrinters = new ArrayList<PrinterId>(); 1160 1161 private final Handler mSessionHandler; 1162 1163 private boolean mIsDestroyed; 1164 1165 public PrinterDiscoverySessionMediator(Context context) { 1166 mSessionHandler = new SessionHandler(context.getMainLooper()); 1167 // Kick off the session creation. 1168 List<RemotePrintService> services = new ArrayList<RemotePrintService>( 1169 mActiveServices.values()); 1170 mSessionHandler.obtainMessage(SessionHandler 1171 .MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION, services) 1172 .sendToTarget(); 1173 } 1174 1175 public void addObserverLocked(@NonNull IPrinterDiscoveryObserver observer) { 1176 // Add the observer. 1177 mDiscoveryObservers.register(observer); 1178 1179 // Bring the added observer up to speed with the printers. 1180 if (!mPrinters.isEmpty()) { 1181 List<PrinterInfo> printers = new ArrayList<PrinterInfo>(mPrinters.values()); 1182 SomeArgs args = SomeArgs.obtain(); 1183 args.arg1 = observer; 1184 args.arg2 = printers; 1185 mSessionHandler.obtainMessage(SessionHandler.MSG_PRINTERS_ADDED, 1186 args).sendToTarget(); 1187 } 1188 } 1189 1190 public void removeObserverLocked(@NonNull IPrinterDiscoveryObserver observer) { 1191 // Remove the observer. 1192 mDiscoveryObservers.unregister(observer); 1193 // No one else observing - then kill it. 1194 if (mDiscoveryObservers.getRegisteredCallbackCount() == 0) { 1195 destroyLocked(); 1196 } 1197 } 1198 1199 public final void startPrinterDiscoveryLocked(@NonNull IPrinterDiscoveryObserver observer, 1200 @Nullable List<PrinterId> priorityList) { 1201 if (mIsDestroyed) { 1202 Log.w(LOG_TAG, "Not starting dicovery - session destroyed"); 1203 return; 1204 } 1205 1206 final boolean discoveryStarted = !mStartedPrinterDiscoveryTokens.isEmpty(); 1207 1208 // Remember we got a start request to match with an end. 1209 mStartedPrinterDiscoveryTokens.add(observer.asBinder()); 1210 1211 // If printer discovery is ongoing and the start request has a list 1212 // of printer to be checked, then we just request validating them. 1213 if (discoveryStarted && priorityList != null && !priorityList.isEmpty()) { 1214 validatePrinters(priorityList); 1215 return; 1216 } 1217 1218 // The service are already performing discovery - nothing to do. 1219 if (mStartedPrinterDiscoveryTokens.size() > 1) { 1220 return; 1221 } 1222 1223 List<RemotePrintService> services = new ArrayList<RemotePrintService>( 1224 mActiveServices.values()); 1225 SomeArgs args = SomeArgs.obtain(); 1226 args.arg1 = services; 1227 args.arg2 = priorityList; 1228 mSessionHandler.obtainMessage(SessionHandler 1229 .MSG_DISPATCH_START_PRINTER_DISCOVERY, args) 1230 .sendToTarget(); 1231 } 1232 1233 public final void stopPrinterDiscoveryLocked(@NonNull IPrinterDiscoveryObserver observer) { 1234 if (mIsDestroyed) { 1235 Log.w(LOG_TAG, "Not stopping dicovery - session destroyed"); 1236 return; 1237 } 1238 // This one did not make an active discovery request - nothing to do. 1239 if (!mStartedPrinterDiscoveryTokens.remove(observer.asBinder())) { 1240 return; 1241 } 1242 // There are other interested observers - do not stop discovery. 1243 if (!mStartedPrinterDiscoveryTokens.isEmpty()) { 1244 return; 1245 } 1246 List<RemotePrintService> services = new ArrayList<RemotePrintService>( 1247 mActiveServices.values()); 1248 mSessionHandler.obtainMessage(SessionHandler 1249 .MSG_DISPATCH_STOP_PRINTER_DISCOVERY, services) 1250 .sendToTarget(); 1251 } 1252 1253 public void validatePrintersLocked(@NonNull List<PrinterId> printerIds) { 1254 if (mIsDestroyed) { 1255 Log.w(LOG_TAG, "Not validating pritners - session destroyed"); 1256 return; 1257 } 1258 1259 List<PrinterId> remainingList = new ArrayList<PrinterId>(printerIds); 1260 while (!remainingList.isEmpty()) { 1261 Iterator<PrinterId> iterator = remainingList.iterator(); 1262 // Gather the printers per service and request a validation. 1263 List<PrinterId> updateList = new ArrayList<PrinterId>(); 1264 ComponentName serviceName = null; 1265 while (iterator.hasNext()) { 1266 PrinterId printerId = iterator.next(); 1267 if (printerId != null) { 1268 if (updateList.isEmpty()) { 1269 updateList.add(printerId); 1270 serviceName = printerId.getServiceName(); 1271 iterator.remove(); 1272 } else if (printerId.getServiceName().equals(serviceName)) { 1273 updateList.add(printerId); 1274 iterator.remove(); 1275 } 1276 } 1277 } 1278 // Schedule a notification of the service. 1279 RemotePrintService service = mActiveServices.get(serviceName); 1280 if (service != null) { 1281 SomeArgs args = SomeArgs.obtain(); 1282 args.arg1 = service; 1283 args.arg2 = updateList; 1284 mSessionHandler.obtainMessage(SessionHandler 1285 .MSG_VALIDATE_PRINTERS, args) 1286 .sendToTarget(); 1287 } 1288 } 1289 } 1290 1291 public final void startPrinterStateTrackingLocked(@NonNull PrinterId printerId) { 1292 if (mIsDestroyed) { 1293 Log.w(LOG_TAG, "Not starting printer state tracking - session destroyed"); 1294 return; 1295 } 1296 // If printer discovery is not started - nothing to do. 1297 if (mStartedPrinterDiscoveryTokens.isEmpty()) { 1298 return; 1299 } 1300 final boolean containedPrinterId = mStateTrackedPrinters.contains(printerId); 1301 // Keep track of the number of requests to track this one. 1302 mStateTrackedPrinters.add(printerId); 1303 // If we were tracking this printer - nothing to do. 1304 if (containedPrinterId) { 1305 return; 1306 } 1307 // No service - nothing to do. 1308 RemotePrintService service = mActiveServices.get(printerId.getServiceName()); 1309 if (service == null) { 1310 return; 1311 } 1312 // Ask the service to start tracking. 1313 SomeArgs args = SomeArgs.obtain(); 1314 args.arg1 = service; 1315 args.arg2 = printerId; 1316 mSessionHandler.obtainMessage(SessionHandler 1317 .MSG_START_PRINTER_STATE_TRACKING, args) 1318 .sendToTarget(); 1319 } 1320 1321 public final void stopPrinterStateTrackingLocked(PrinterId printerId) { 1322 if (mIsDestroyed) { 1323 Log.w(LOG_TAG, "Not stopping printer state tracking - session destroyed"); 1324 return; 1325 } 1326 // If printer discovery is not started - nothing to do. 1327 if (mStartedPrinterDiscoveryTokens.isEmpty()) { 1328 return; 1329 } 1330 // If we did not track this printer - nothing to do. 1331 if (!mStateTrackedPrinters.remove(printerId)) { 1332 return; 1333 } 1334 // No service - nothing to do. 1335 RemotePrintService service = mActiveServices.get(printerId.getServiceName()); 1336 if (service == null) { 1337 return; 1338 } 1339 // Ask the service to start tracking. 1340 SomeArgs args = SomeArgs.obtain(); 1341 args.arg1 = service; 1342 args.arg2 = printerId; 1343 mSessionHandler.obtainMessage(SessionHandler 1344 .MSG_STOP_PRINTER_STATE_TRACKING, args) 1345 .sendToTarget(); 1346 } 1347 1348 public void onDestroyed() { 1349 /* do nothing */ 1350 } 1351 1352 public void destroyLocked() { 1353 if (mIsDestroyed) { 1354 Log.w(LOG_TAG, "Not destroying - session destroyed"); 1355 return; 1356 } 1357 mIsDestroyed = true; 1358 // Make sure printer tracking is stopped. 1359 final int printerCount = mStateTrackedPrinters.size(); 1360 for (int i = 0; i < printerCount; i++) { 1361 PrinterId printerId = mStateTrackedPrinters.get(i); 1362 stopPrinterStateTracking(printerId); 1363 } 1364 // Make sure discovery is stopped. 1365 final int observerCount = mStartedPrinterDiscoveryTokens.size(); 1366 for (int i = 0; i < observerCount; i++) { 1367 IBinder token = mStartedPrinterDiscoveryTokens.get(i); 1368 stopPrinterDiscoveryLocked(IPrinterDiscoveryObserver.Stub.asInterface(token)); 1369 } 1370 // Tell the services we are done. 1371 List<RemotePrintService> services = new ArrayList<RemotePrintService>( 1372 mActiveServices.values()); 1373 mSessionHandler.obtainMessage(SessionHandler 1374 .MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION, services) 1375 .sendToTarget(); 1376 } 1377 1378 public void onPrintersAddedLocked(List<PrinterInfo> printers) { 1379 if (DEBUG) { 1380 Log.i(LOG_TAG, "onPrintersAddedLocked()"); 1381 } 1382 if (mIsDestroyed) { 1383 Log.w(LOG_TAG, "Not adding printers - session destroyed"); 1384 return; 1385 } 1386 List<PrinterInfo> addedPrinters = null; 1387 final int addedPrinterCount = printers.size(); 1388 for (int i = 0; i < addedPrinterCount; i++) { 1389 PrinterInfo printer = printers.get(i); 1390 PrinterInfo oldPrinter = mPrinters.put(printer.getId(), printer); 1391 if (oldPrinter == null || !oldPrinter.equals(printer)) { 1392 if (addedPrinters == null) { 1393 addedPrinters = new ArrayList<PrinterInfo>(); 1394 } 1395 addedPrinters.add(printer); 1396 } 1397 } 1398 if (addedPrinters != null) { 1399 mSessionHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_ADDED, 1400 addedPrinters).sendToTarget(); 1401 } 1402 } 1403 1404 public void onPrintersRemovedLocked(List<PrinterId> printerIds) { 1405 if (DEBUG) { 1406 Log.i(LOG_TAG, "onPrintersRemovedLocked()"); 1407 } 1408 if (mIsDestroyed) { 1409 Log.w(LOG_TAG, "Not removing printers - session destroyed"); 1410 return; 1411 } 1412 List<PrinterId> removedPrinterIds = null; 1413 final int removedPrinterCount = printerIds.size(); 1414 for (int i = 0; i < removedPrinterCount; i++) { 1415 PrinterId removedPrinterId = printerIds.get(i); 1416 if (mPrinters.remove(removedPrinterId) != null) { 1417 if (removedPrinterIds == null) { 1418 removedPrinterIds = new ArrayList<PrinterId>(); 1419 } 1420 removedPrinterIds.add(removedPrinterId); 1421 } 1422 } 1423 if (removedPrinterIds != null) { 1424 mSessionHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_REMOVED, 1425 removedPrinterIds).sendToTarget(); 1426 } 1427 } 1428 1429 public void onServiceRemovedLocked(RemotePrintService service) { 1430 if (mIsDestroyed) { 1431 Log.w(LOG_TAG, "Not updating removed service - session destroyed"); 1432 return; 1433 } 1434 // Remove the reported and tracked printers for that service. 1435 ComponentName serviceName = service.getComponentName(); 1436 removePrintersForServiceLocked(serviceName); 1437 service.destroy(); 1438 } 1439 1440 /** 1441 * Handle that a custom icon for a printer was loaded. 1442 * 1443 * This increments the icon generation and adds the printer again which triggers an update 1444 * in all users of the currently known printers. 1445 * 1446 * @param printerId the id of the printer the icon belongs to 1447 * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon() 1448 */ 1449 public void onCustomPrinterIconLoadedLocked(PrinterId printerId) { 1450 if (DEBUG) { 1451 Log.i(LOG_TAG, "onCustomPrinterIconLoadedLocked()"); 1452 } 1453 if (mIsDestroyed) { 1454 Log.w(LOG_TAG, "Not updating printer - session destroyed"); 1455 return; 1456 } 1457 1458 PrinterInfo printer = mPrinters.get(printerId); 1459 if (printer != null) { 1460 PrinterInfo newPrinter = (new PrinterInfo.Builder(printer)) 1461 .incCustomPrinterIconGen().build(); 1462 mPrinters.put(printerId, newPrinter); 1463 1464 ArrayList<PrinterInfo> addedPrinters = new ArrayList<>(1); 1465 addedPrinters.add(newPrinter); 1466 mSessionHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_ADDED, 1467 addedPrinters).sendToTarget(); 1468 } 1469 } 1470 1471 public void onServiceDiedLocked(RemotePrintService service) { 1472 // Remove the reported by that service. 1473 removePrintersForServiceLocked(service.getComponentName()); 1474 } 1475 1476 public void onServiceAddedLocked(RemotePrintService service) { 1477 if (mIsDestroyed) { 1478 Log.w(LOG_TAG, "Not updating added service - session destroyed"); 1479 return; 1480 } 1481 // Tell the service to create a session. 1482 mSessionHandler.obtainMessage( 1483 SessionHandler.MSG_CREATE_PRINTER_DISCOVERY_SESSION, 1484 service).sendToTarget(); 1485 // Start printer discovery if necessary. 1486 if (!mStartedPrinterDiscoveryTokens.isEmpty()) { 1487 mSessionHandler.obtainMessage( 1488 SessionHandler.MSG_START_PRINTER_DISCOVERY, 1489 service).sendToTarget(); 1490 } 1491 // Start tracking printers if necessary 1492 final int trackedPrinterCount = mStateTrackedPrinters.size(); 1493 for (int i = 0; i < trackedPrinterCount; i++) { 1494 PrinterId printerId = mStateTrackedPrinters.get(i); 1495 if (printerId.getServiceName().equals(service.getComponentName())) { 1496 SomeArgs args = SomeArgs.obtain(); 1497 args.arg1 = service; 1498 args.arg2 = printerId; 1499 mSessionHandler.obtainMessage(SessionHandler 1500 .MSG_START_PRINTER_STATE_TRACKING, args) 1501 .sendToTarget(); 1502 } 1503 } 1504 } 1505 1506 public void dump(PrintWriter pw, String prefix) { 1507 pw.append(prefix).append("destroyed=") 1508 .append(String.valueOf(mDestroyed)).println(); 1509 1510 pw.append(prefix).append("printDiscoveryInProgress=") 1511 .append(String.valueOf(!mStartedPrinterDiscoveryTokens.isEmpty())).println(); 1512 1513 String tab = " "; 1514 1515 pw.append(prefix).append(tab).append("printer discovery observers:").println(); 1516 final int observerCount = mDiscoveryObservers.beginBroadcast(); 1517 for (int i = 0; i < observerCount; i++) { 1518 IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i); 1519 pw.append(prefix).append(prefix).append(observer.toString()); 1520 pw.println(); 1521 } 1522 mDiscoveryObservers.finishBroadcast(); 1523 1524 pw.append(prefix).append(tab).append("start discovery requests:").println(); 1525 final int tokenCount = this.mStartedPrinterDiscoveryTokens.size(); 1526 for (int i = 0; i < tokenCount; i++) { 1527 IBinder token = mStartedPrinterDiscoveryTokens.get(i); 1528 pw.append(prefix).append(tab).append(tab).append(token.toString()).println(); 1529 } 1530 1531 pw.append(prefix).append(tab).append("tracked printer requests:").println(); 1532 final int trackedPrinters = mStateTrackedPrinters.size(); 1533 for (int i = 0; i < trackedPrinters; i++) { 1534 PrinterId printer = mStateTrackedPrinters.get(i); 1535 pw.append(prefix).append(tab).append(tab).append(printer.toString()).println(); 1536 } 1537 1538 pw.append(prefix).append(tab).append("printers:").println(); 1539 final int pritnerCount = mPrinters.size(); 1540 for (int i = 0; i < pritnerCount; i++) { 1541 PrinterInfo printer = mPrinters.valueAt(i); 1542 pw.append(prefix).append(tab).append(tab).append( 1543 printer.toString()).println(); 1544 } 1545 } 1546 1547 private void removePrintersForServiceLocked(ComponentName serviceName) { 1548 // No printers - nothing to do. 1549 if (mPrinters.isEmpty()) { 1550 return; 1551 } 1552 // Remove the printers for that service. 1553 List<PrinterId> removedPrinterIds = null; 1554 final int printerCount = mPrinters.size(); 1555 for (int i = 0; i < printerCount; i++) { 1556 PrinterId printerId = mPrinters.keyAt(i); 1557 if (printerId.getServiceName().equals(serviceName)) { 1558 if (removedPrinterIds == null) { 1559 removedPrinterIds = new ArrayList<PrinterId>(); 1560 } 1561 removedPrinterIds.add(printerId); 1562 } 1563 } 1564 if (removedPrinterIds != null) { 1565 final int removedPrinterCount = removedPrinterIds.size(); 1566 for (int i = 0; i < removedPrinterCount; i++) { 1567 mPrinters.remove(removedPrinterIds.get(i)); 1568 } 1569 mSessionHandler.obtainMessage( 1570 SessionHandler.MSG_DISPATCH_PRINTERS_REMOVED, 1571 removedPrinterIds).sendToTarget(); 1572 } 1573 } 1574 1575 private void handleDispatchPrintersAdded(List<PrinterInfo> addedPrinters) { 1576 final int observerCount = mDiscoveryObservers.beginBroadcast(); 1577 for (int i = 0; i < observerCount; i++) { 1578 IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i); 1579 handlePrintersAdded(observer, addedPrinters); 1580 } 1581 mDiscoveryObservers.finishBroadcast(); 1582 } 1583 1584 private void handleDispatchPrintersRemoved(List<PrinterId> removedPrinterIds) { 1585 final int observerCount = mDiscoveryObservers.beginBroadcast(); 1586 for (int i = 0; i < observerCount; i++) { 1587 IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i); 1588 handlePrintersRemoved(observer, removedPrinterIds); 1589 } 1590 mDiscoveryObservers.finishBroadcast(); 1591 } 1592 1593 private void handleDispatchCreatePrinterDiscoverySession( 1594 List<RemotePrintService> services) { 1595 final int serviceCount = services.size(); 1596 for (int i = 0; i < serviceCount; i++) { 1597 RemotePrintService service = services.get(i); 1598 service.createPrinterDiscoverySession(); 1599 } 1600 } 1601 1602 private void handleDispatchDestroyPrinterDiscoverySession( 1603 List<RemotePrintService> services) { 1604 final int serviceCount = services.size(); 1605 for (int i = 0; i < serviceCount; i++) { 1606 RemotePrintService service = services.get(i); 1607 service.destroyPrinterDiscoverySession(); 1608 } 1609 onDestroyed(); 1610 } 1611 1612 private void handleDispatchStartPrinterDiscovery( 1613 List<RemotePrintService> services, List<PrinterId> printerIds) { 1614 final int serviceCount = services.size(); 1615 for (int i = 0; i < serviceCount; i++) { 1616 RemotePrintService service = services.get(i); 1617 service.startPrinterDiscovery(printerIds); 1618 } 1619 } 1620 1621 private void handleDispatchStopPrinterDiscovery(List<RemotePrintService> services) { 1622 final int serviceCount = services.size(); 1623 for (int i = 0; i < serviceCount; i++) { 1624 RemotePrintService service = services.get(i); 1625 service.stopPrinterDiscovery(); 1626 } 1627 } 1628 1629 private void handleValidatePrinters(RemotePrintService service, 1630 List<PrinterId> printerIds) { 1631 service.validatePrinters(printerIds); 1632 } 1633 1634 private void handleStartPrinterStateTracking(@NonNull RemotePrintService service, 1635 @NonNull PrinterId printerId) { 1636 service.startPrinterStateTracking(printerId); 1637 } 1638 1639 private void handleStopPrinterStateTracking(RemotePrintService service, 1640 PrinterId printerId) { 1641 service.stopPrinterStateTracking(printerId); 1642 } 1643 1644 private void handlePrintersAdded(IPrinterDiscoveryObserver observer, 1645 List<PrinterInfo> printers) { 1646 try { 1647 observer.onPrintersAdded(new ParceledListSlice<PrinterInfo>(printers)); 1648 } catch (RemoteException re) { 1649 Log.e(LOG_TAG, "Error sending added printers", re); 1650 } 1651 } 1652 1653 private void handlePrintersRemoved(IPrinterDiscoveryObserver observer, 1654 List<PrinterId> printerIds) { 1655 try { 1656 observer.onPrintersRemoved(new ParceledListSlice<PrinterId>(printerIds)); 1657 } catch (RemoteException re) { 1658 Log.e(LOG_TAG, "Error sending removed printers", re); 1659 } 1660 } 1661 1662 private final class SessionHandler extends Handler { 1663 public static final int MSG_PRINTERS_ADDED = 1; 1664 public static final int MSG_PRINTERS_REMOVED = 2; 1665 public static final int MSG_DISPATCH_PRINTERS_ADDED = 3; 1666 public static final int MSG_DISPATCH_PRINTERS_REMOVED = 4; 1667 1668 public static final int MSG_CREATE_PRINTER_DISCOVERY_SESSION = 5; 1669 public static final int MSG_DESTROY_PRINTER_DISCOVERY_SESSION = 6; 1670 public static final int MSG_START_PRINTER_DISCOVERY = 7; 1671 public static final int MSG_STOP_PRINTER_DISCOVERY = 8; 1672 public static final int MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION = 9; 1673 public static final int MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION = 10; 1674 public static final int MSG_DISPATCH_START_PRINTER_DISCOVERY = 11; 1675 public static final int MSG_DISPATCH_STOP_PRINTER_DISCOVERY = 12; 1676 public static final int MSG_VALIDATE_PRINTERS = 13; 1677 public static final int MSG_START_PRINTER_STATE_TRACKING = 14; 1678 public static final int MSG_STOP_PRINTER_STATE_TRACKING = 15; 1679 public static final int MSG_DESTROY_SERVICE = 16; 1680 1681 SessionHandler(Looper looper) { 1682 super(looper, null, false); 1683 } 1684 1685 @Override 1686 @SuppressWarnings("unchecked") 1687 public void handleMessage(Message message) { 1688 switch (message.what) { 1689 case MSG_PRINTERS_ADDED: { 1690 SomeArgs args = (SomeArgs) message.obj; 1691 IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) args.arg1; 1692 List<PrinterInfo> addedPrinters = (List<PrinterInfo>) args.arg2; 1693 args.recycle(); 1694 handlePrintersAdded(observer, addedPrinters); 1695 } break; 1696 1697 case MSG_PRINTERS_REMOVED: { 1698 SomeArgs args = (SomeArgs) message.obj; 1699 IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) args.arg1; 1700 List<PrinterId> removedPrinterIds = (List<PrinterId>) args.arg2; 1701 args.recycle(); 1702 handlePrintersRemoved(observer, removedPrinterIds); 1703 } 1704 1705 case MSG_DISPATCH_PRINTERS_ADDED: { 1706 List<PrinterInfo> addedPrinters = (List<PrinterInfo>) message.obj; 1707 handleDispatchPrintersAdded(addedPrinters); 1708 } break; 1709 1710 case MSG_DISPATCH_PRINTERS_REMOVED: { 1711 List<PrinterId> removedPrinterIds = (List<PrinterId>) message.obj; 1712 handleDispatchPrintersRemoved(removedPrinterIds); 1713 } break; 1714 1715 case MSG_CREATE_PRINTER_DISCOVERY_SESSION: { 1716 RemotePrintService service = (RemotePrintService) message.obj; 1717 service.createPrinterDiscoverySession(); 1718 } break; 1719 1720 case MSG_DESTROY_PRINTER_DISCOVERY_SESSION: { 1721 RemotePrintService service = (RemotePrintService) message.obj; 1722 service.destroyPrinterDiscoverySession(); 1723 } break; 1724 1725 case MSG_START_PRINTER_DISCOVERY: { 1726 RemotePrintService service = (RemotePrintService) message.obj; 1727 service.startPrinterDiscovery(null); 1728 } break; 1729 1730 case MSG_STOP_PRINTER_DISCOVERY: { 1731 RemotePrintService service = (RemotePrintService) message.obj; 1732 service.stopPrinterDiscovery(); 1733 } break; 1734 1735 case MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION: { 1736 List<RemotePrintService> services = (List<RemotePrintService>) message.obj; 1737 handleDispatchCreatePrinterDiscoverySession(services); 1738 } break; 1739 1740 case MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION: { 1741 List<RemotePrintService> services = (List<RemotePrintService>) message.obj; 1742 handleDispatchDestroyPrinterDiscoverySession(services); 1743 } break; 1744 1745 case MSG_DISPATCH_START_PRINTER_DISCOVERY: { 1746 SomeArgs args = (SomeArgs) message.obj; 1747 List<RemotePrintService> services = (List<RemotePrintService>) args.arg1; 1748 List<PrinterId> printerIds = (List<PrinterId>) args.arg2; 1749 args.recycle(); 1750 handleDispatchStartPrinterDiscovery(services, printerIds); 1751 } break; 1752 1753 case MSG_DISPATCH_STOP_PRINTER_DISCOVERY: { 1754 List<RemotePrintService> services = (List<RemotePrintService>) message.obj; 1755 handleDispatchStopPrinterDiscovery(services); 1756 } break; 1757 1758 case MSG_VALIDATE_PRINTERS: { 1759 SomeArgs args = (SomeArgs) message.obj; 1760 RemotePrintService service = (RemotePrintService) args.arg1; 1761 List<PrinterId> printerIds = (List<PrinterId>) args.arg2; 1762 args.recycle(); 1763 handleValidatePrinters(service, printerIds); 1764 } break; 1765 1766 case MSG_START_PRINTER_STATE_TRACKING: { 1767 SomeArgs args = (SomeArgs) message.obj; 1768 RemotePrintService service = (RemotePrintService) args.arg1; 1769 PrinterId printerId = (PrinterId) args.arg2; 1770 args.recycle(); 1771 handleStartPrinterStateTracking(service, printerId); 1772 } break; 1773 1774 case MSG_STOP_PRINTER_STATE_TRACKING: { 1775 SomeArgs args = (SomeArgs) message.obj; 1776 RemotePrintService service = (RemotePrintService) args.arg1; 1777 PrinterId printerId = (PrinterId) args.arg2; 1778 args.recycle(); 1779 handleStopPrinterStateTracking(service, printerId); 1780 } break; 1781 1782 case MSG_DESTROY_SERVICE: { 1783 RemotePrintService service = (RemotePrintService) message.obj; 1784 service.destroy(); 1785 } break; 1786 } 1787 } 1788 } 1789 } 1790 1791 private final class PrintJobForAppCache { 1792 private final SparseArray<List<PrintJobInfo>> mPrintJobsForRunningApp = 1793 new SparseArray<List<PrintJobInfo>>(); 1794 1795 public boolean onPrintJobCreated(final IBinder creator, final int appId, 1796 PrintJobInfo printJob) { 1797 try { 1798 creator.linkToDeath(new DeathRecipient() { 1799 @Override 1800 public void binderDied() { 1801 creator.unlinkToDeath(this, 0); 1802 synchronized (mLock) { 1803 mPrintJobsForRunningApp.remove(appId); 1804 } 1805 } 1806 }, 0); 1807 } catch (RemoteException re) { 1808 /* The process is already dead - we just failed. */ 1809 return false; 1810 } 1811 synchronized (mLock) { 1812 List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get(appId); 1813 if (printJobsForApp == null) { 1814 printJobsForApp = new ArrayList<PrintJobInfo>(); 1815 mPrintJobsForRunningApp.put(appId, printJobsForApp); 1816 } 1817 printJobsForApp.add(printJob); 1818 } 1819 return true; 1820 } 1821 1822 public void onPrintJobStateChanged(PrintJobInfo printJob) { 1823 synchronized (mLock) { 1824 List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get( 1825 printJob.getAppId()); 1826 if (printJobsForApp == null) { 1827 return; 1828 } 1829 final int printJobCount = printJobsForApp.size(); 1830 for (int i = 0; i < printJobCount; i++) { 1831 PrintJobInfo oldPrintJob = printJobsForApp.get(i); 1832 if (oldPrintJob.getId().equals(printJob.getId())) { 1833 printJobsForApp.set(i, printJob); 1834 } 1835 } 1836 } 1837 } 1838 1839 public PrintJobInfo getPrintJob(PrintJobId printJobId, int appId) { 1840 synchronized (mLock) { 1841 List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get(appId); 1842 if (printJobsForApp == null) { 1843 return null; 1844 } 1845 final int printJobCount = printJobsForApp.size(); 1846 for (int i = 0; i < printJobCount; i++) { 1847 PrintJobInfo printJob = printJobsForApp.get(i); 1848 if (printJob.getId().equals(printJobId)) { 1849 return printJob; 1850 } 1851 } 1852 } 1853 return null; 1854 } 1855 1856 public List<PrintJobInfo> getPrintJobs(int appId) { 1857 synchronized (mLock) { 1858 List<PrintJobInfo> printJobs = null; 1859 if (appId == PrintManager.APP_ID_ANY) { 1860 final int bucketCount = mPrintJobsForRunningApp.size(); 1861 for (int i = 0; i < bucketCount; i++) { 1862 List<PrintJobInfo> bucket = mPrintJobsForRunningApp.valueAt(i); 1863 if (printJobs == null) { 1864 printJobs = new ArrayList<PrintJobInfo>(); 1865 } 1866 printJobs.addAll(bucket); 1867 } 1868 } else { 1869 List<PrintJobInfo> bucket = mPrintJobsForRunningApp.get(appId); 1870 if (bucket != null) { 1871 if (printJobs == null) { 1872 printJobs = new ArrayList<PrintJobInfo>(); 1873 } 1874 printJobs.addAll(bucket); 1875 } 1876 } 1877 if (printJobs != null) { 1878 return printJobs; 1879 } 1880 return Collections.emptyList(); 1881 } 1882 } 1883 1884 public void dump(PrintWriter pw, String prefix) { 1885 synchronized (mLock) { 1886 String tab = " "; 1887 final int bucketCount = mPrintJobsForRunningApp.size(); 1888 for (int i = 0; i < bucketCount; i++) { 1889 final int appId = mPrintJobsForRunningApp.keyAt(i); 1890 pw.append(prefix).append("appId=" + appId).append(':').println(); 1891 List<PrintJobInfo> bucket = mPrintJobsForRunningApp.valueAt(i); 1892 final int printJobCount = bucket.size(); 1893 for (int j = 0; j < printJobCount; j++) { 1894 PrintJobInfo printJob = bucket.get(j); 1895 pw.append(prefix).append(tab).append(printJob.toString()).println(); 1896 } 1897 } 1898 } 1899 } 1900 } 1901} 1902