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