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