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