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