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