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