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