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