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 17package com.android.server.print; 18 19import static android.content.pm.PackageManager.GET_META_DATA; 20import static android.content.pm.PackageManager.GET_SERVICES; 21import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; 22 23import android.annotation.NonNull; 24import android.annotation.Nullable; 25import android.app.PendingIntent; 26import android.content.ComponentName; 27import android.content.Context; 28import android.content.Intent; 29import android.content.IntentSender; 30import android.content.pm.ParceledListSlice; 31import android.content.pm.ResolveInfo; 32import android.graphics.drawable.Icon; 33import android.net.Uri; 34import android.os.AsyncTask; 35import android.os.Binder; 36import android.os.Bundle; 37import android.os.Handler; 38import android.os.IBinder; 39import android.os.IBinder.DeathRecipient; 40import android.os.IInterface; 41import android.os.Looper; 42import android.os.Message; 43import android.os.RemoteCallbackList; 44import android.os.RemoteException; 45import android.os.UserHandle; 46import android.print.IPrintDocumentAdapter; 47import android.print.IPrintJobStateChangeListener; 48import android.printservice.recommendation.IRecommendationsChangeListener; 49import android.print.IPrintServicesChangeListener; 50import android.print.IPrinterDiscoveryObserver; 51import android.print.PrintAttributes; 52import android.print.PrintJobId; 53import android.print.PrintJobInfo; 54import android.print.PrintManager; 55import android.printservice.recommendation.RecommendationInfo; 56import android.print.PrinterId; 57import android.print.PrinterInfo; 58import android.printservice.PrintServiceInfo; 59import android.provider.DocumentsContract; 60import android.provider.Settings; 61import android.text.TextUtils; 62import android.text.TextUtils.SimpleStringSplitter; 63import android.util.ArrayMap; 64import android.util.ArraySet; 65import android.util.Log; 66import android.util.Slog; 67import android.util.SparseArray; 68 69import com.android.internal.R; 70import com.android.internal.os.BackgroundThread; 71import com.android.internal.os.SomeArgs; 72import com.android.server.print.RemotePrintService.PrintServiceCallbacks; 73import com.android.server.print.RemotePrintSpooler.PrintSpoolerCallbacks; 74import com.android.server.print.RemotePrintServiceRecommendationService.RemotePrintServiceRecommendationServiceCallbacks; 75 76import java.io.FileDescriptor; 77import java.io.PrintWriter; 78import java.util.ArrayList; 79import java.util.Collections; 80import java.util.HashSet; 81import java.util.Iterator; 82import java.util.List; 83import java.util.Map; 84import java.util.Set; 85 86/** 87 * Represents the print state for a user. 88 */ 89final 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 if (readConfigurationLocked()) { 768 onConfigurationChangedLocked(); 769 } 770 } 771 772 public void destroyLocked() { 773 throwIfDestroyedLocked(); 774 mSpooler.destroy(); 775 for (RemotePrintService service : mActiveServices.values()) { 776 service.destroy(); 777 } 778 mActiveServices.clear(); 779 mInstalledServices.clear(); 780 mDisabledServices.clear(); 781 if (mPrinterDiscoverySession != null) { 782 mPrinterDiscoverySession.destroyLocked(); 783 mPrinterDiscoverySession = null; 784 } 785 mDestroyed = true; 786 } 787 788 public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String prefix) { 789 pw.append(prefix).append("user state ").append(String.valueOf(mUserId)).append(":"); 790 pw.println(); 791 792 String tab = " "; 793 794 pw.append(prefix).append(tab).append("installed services:").println(); 795 final int installedServiceCount = mInstalledServices.size(); 796 for (int i = 0; i < installedServiceCount; i++) { 797 PrintServiceInfo installedService = mInstalledServices.get(i); 798 String installedServicePrefix = prefix + tab + tab; 799 pw.append(installedServicePrefix).append("service:").println(); 800 ResolveInfo resolveInfo = installedService.getResolveInfo(); 801 ComponentName componentName = new ComponentName( 802 resolveInfo.serviceInfo.packageName, 803 resolveInfo.serviceInfo.name); 804 pw.append(installedServicePrefix).append(tab).append("componentName=") 805 .append(componentName.flattenToString()).println(); 806 pw.append(installedServicePrefix).append(tab).append("settingsActivity=") 807 .append(installedService.getSettingsActivityName()).println(); 808 pw.append(installedServicePrefix).append(tab).append("addPrintersActivity=") 809 .append(installedService.getAddPrintersActivityName()).println(); 810 pw.append(installedServicePrefix).append(tab).append("avancedOptionsActivity=") 811 .append(installedService.getAdvancedOptionsActivityName()).println(); 812 } 813 814 pw.append(prefix).append(tab).append("disabled services:").println(); 815 for (ComponentName disabledService : mDisabledServices) { 816 String disabledServicePrefix = prefix + tab + tab; 817 pw.append(disabledServicePrefix).append("service:").println(); 818 pw.append(disabledServicePrefix).append(tab).append("componentName=") 819 .append(disabledService.flattenToString()); 820 pw.println(); 821 } 822 823 pw.append(prefix).append(tab).append("active services:").println(); 824 final int activeServiceCount = mActiveServices.size(); 825 for (int i = 0; i < activeServiceCount; i++) { 826 RemotePrintService activeService = mActiveServices.valueAt(i); 827 activeService.dump(pw, prefix + tab + tab); 828 pw.println(); 829 } 830 831 pw.append(prefix).append(tab).append("cached print jobs:").println(); 832 mPrintJobForAppCache.dump(pw, prefix + tab + tab); 833 834 pw.append(prefix).append(tab).append("discovery mediator:").println(); 835 if (mPrinterDiscoverySession != null) { 836 mPrinterDiscoverySession.dump(pw, prefix + tab + tab); 837 } 838 839 pw.append(prefix).append(tab).append("print spooler:").println(); 840 mSpooler.dump(fd, pw, prefix + tab + tab); 841 pw.println(); 842 } 843 844 private boolean readConfigurationLocked() { 845 boolean somethingChanged = false; 846 somethingChanged |= readInstalledPrintServicesLocked(); 847 somethingChanged |= readDisabledPrintServicesLocked(); 848 return somethingChanged; 849 } 850 851 private boolean readInstalledPrintServicesLocked() { 852 Set<PrintServiceInfo> tempPrintServices = new HashSet<PrintServiceInfo>(); 853 854 List<ResolveInfo> installedServices = mContext.getPackageManager() 855 .queryIntentServicesAsUser(mQueryIntent, 856 GET_SERVICES | GET_META_DATA | MATCH_DEBUG_TRIAGED_MISSING, mUserId); 857 858 final int installedCount = installedServices.size(); 859 for (int i = 0, count = installedCount; i < count; i++) { 860 ResolveInfo installedService = installedServices.get(i); 861 if (!android.Manifest.permission.BIND_PRINT_SERVICE.equals( 862 installedService.serviceInfo.permission)) { 863 ComponentName serviceName = new ComponentName( 864 installedService.serviceInfo.packageName, 865 installedService.serviceInfo.name); 866 Slog.w(LOG_TAG, "Skipping print service " 867 + serviceName.flattenToShortString() 868 + " since it does not require permission " 869 + android.Manifest.permission.BIND_PRINT_SERVICE); 870 continue; 871 } 872 tempPrintServices.add(PrintServiceInfo.create(installedService, mContext)); 873 } 874 875 boolean someServiceChanged = false; 876 877 if (tempPrintServices.size() != mInstalledServices.size()) { 878 someServiceChanged = true; 879 } else { 880 for (PrintServiceInfo newService: tempPrintServices) { 881 final int oldServiceIndex = mInstalledServices.indexOf(newService); 882 if (oldServiceIndex < 0) { 883 someServiceChanged = true; 884 break; 885 } 886 // PrintServiceInfo#equals compares only the id not all members, 887 // so we are also comparing the members coming from meta-data. 888 PrintServiceInfo oldService = mInstalledServices.get(oldServiceIndex); 889 if (!TextUtils.equals(oldService.getAddPrintersActivityName(), 890 newService.getAddPrintersActivityName()) 891 || !TextUtils.equals(oldService.getAdvancedOptionsActivityName(), 892 newService.getAdvancedOptionsActivityName()) 893 || !TextUtils.equals(oldService.getSettingsActivityName(), 894 newService.getSettingsActivityName())) { 895 someServiceChanged = true; 896 break; 897 } 898 } 899 } 900 901 if (someServiceChanged) { 902 mInstalledServices.clear(); 903 mInstalledServices.addAll(tempPrintServices); 904 return true; 905 } 906 907 return false; 908 } 909 910 /** 911 * Update persistent state from a previous version of Android. 912 */ 913 private void upgradePersistentStateIfNeeded() { 914 String enabledSettingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(), 915 Settings.Secure.ENABLED_PRINT_SERVICES, mUserId); 916 917 // Pre N we store the enabled services, in N and later we store the disabled services. 918 // Hence if enabledSettingValue is still set, we need to upgrade. 919 if (enabledSettingValue != null) { 920 Set<ComponentName> enabledServiceNameSet = new HashSet<ComponentName>(); 921 readPrintServicesFromSettingLocked(Settings.Secure.ENABLED_PRINT_SERVICES, 922 enabledServiceNameSet); 923 924 ArraySet<ComponentName> disabledServices = new ArraySet<>(); 925 final int numInstalledServices = mInstalledServices.size(); 926 for (int i = 0; i < numInstalledServices; i++) { 927 ComponentName serviceName = mInstalledServices.get(i).getComponentName(); 928 if (!enabledServiceNameSet.contains(serviceName)) { 929 disabledServices.add(serviceName); 930 } 931 } 932 933 writeDisabledPrintServicesLocked(disabledServices); 934 935 // We won't needed ENABLED_PRINT_SERVICES anymore, set to null to prevent upgrade to run 936 // again. 937 Settings.Secure.putStringForUser(mContext.getContentResolver(), 938 Settings.Secure.ENABLED_PRINT_SERVICES, null, mUserId); 939 } 940 } 941 942 /** 943 * Read the set of disabled print services from the secure settings. 944 * 945 * @return true if the state changed. 946 */ 947 private boolean readDisabledPrintServicesLocked() { 948 Set<ComponentName> tempDisabledServiceNameSet = new HashSet<ComponentName>(); 949 readPrintServicesFromSettingLocked(Settings.Secure.DISABLED_PRINT_SERVICES, 950 tempDisabledServiceNameSet); 951 if (!tempDisabledServiceNameSet.equals(mDisabledServices)) { 952 mDisabledServices.clear(); 953 mDisabledServices.addAll(tempDisabledServiceNameSet); 954 return true; 955 } 956 return false; 957 } 958 959 private void readPrintServicesFromSettingLocked(String setting, 960 Set<ComponentName> outServiceNames) { 961 String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(), 962 setting, mUserId); 963 if (!TextUtils.isEmpty(settingValue)) { 964 TextUtils.SimpleStringSplitter splitter = mStringColonSplitter; 965 splitter.setString(settingValue); 966 while (splitter.hasNext()) { 967 String string = splitter.next(); 968 if (TextUtils.isEmpty(string)) { 969 continue; 970 } 971 ComponentName componentName = ComponentName.unflattenFromString(string); 972 if (componentName != null) { 973 outServiceNames.add(componentName); 974 } 975 } 976 } 977 } 978 979 /** 980 * Persist the disabled print services to the secure settings. 981 */ 982 private void writeDisabledPrintServicesLocked(Set<ComponentName> disabledServices) { 983 StringBuilder builder = new StringBuilder(); 984 for (ComponentName componentName : disabledServices) { 985 if (builder.length() > 0) { 986 builder.append(COMPONENT_NAME_SEPARATOR); 987 } 988 builder.append(componentName.flattenToShortString()); 989 } 990 Settings.Secure.putStringForUser(mContext.getContentResolver(), 991 Settings.Secure.DISABLED_PRINT_SERVICES, builder.toString(), mUserId); 992 } 993 994 /** 995 * Get the {@link ComponentName names} of the installed print services 996 * 997 * @return The names of the installed print services 998 */ 999 private ArrayList<ComponentName> getInstalledComponents() { 1000 ArrayList<ComponentName> installedComponents = new ArrayList<ComponentName>(); 1001 1002 final int installedCount = mInstalledServices.size(); 1003 for (int i = 0; i < installedCount; i++) { 1004 ResolveInfo resolveInfo = mInstalledServices.get(i).getResolveInfo(); 1005 ComponentName serviceName = new ComponentName(resolveInfo.serviceInfo.packageName, 1006 resolveInfo.serviceInfo.name); 1007 1008 installedComponents.add(serviceName); 1009 } 1010 1011 return installedComponents; 1012 } 1013 1014 /** 1015 * Prune persistent state if a print service was uninstalled 1016 */ 1017 public void prunePrintServices() { 1018 synchronized (mLock) { 1019 ArrayList<ComponentName> installedComponents = getInstalledComponents(); 1020 1021 // Remove unnecessary entries from persistent state "disabled services" 1022 boolean disabledServicesUninstalled = mDisabledServices.retainAll(installedComponents); 1023 if (disabledServicesUninstalled) { 1024 writeDisabledPrintServicesLocked(mDisabledServices); 1025 } 1026 1027 // Remove unnecessary entries from persistent state "approved services" 1028 mSpooler.pruneApprovedPrintServices(installedComponents); 1029 } 1030 } 1031 1032 private void onConfigurationChangedLocked() { 1033 ArrayList<ComponentName> installedComponents = getInstalledComponents(); 1034 1035 final int installedCount = installedComponents.size(); 1036 for (int i = 0; i < installedCount; i++) { 1037 ComponentName serviceName = installedComponents.get(i); 1038 1039 if (!mDisabledServices.contains(serviceName)) { 1040 if (!mActiveServices.containsKey(serviceName)) { 1041 RemotePrintService service = new RemotePrintService( 1042 mContext, serviceName, mUserId, mSpooler, this); 1043 addServiceLocked(service); 1044 } 1045 } else { 1046 RemotePrintService service = mActiveServices.remove(serviceName); 1047 if (service != null) { 1048 removeServiceLocked(service); 1049 } 1050 } 1051 } 1052 1053 Iterator<Map.Entry<ComponentName, RemotePrintService>> iterator = 1054 mActiveServices.entrySet().iterator(); 1055 while (iterator.hasNext()) { 1056 Map.Entry<ComponentName, RemotePrintService> entry = iterator.next(); 1057 ComponentName serviceName = entry.getKey(); 1058 RemotePrintService service = entry.getValue(); 1059 if (!installedComponents.contains(serviceName)) { 1060 removeServiceLocked(service); 1061 iterator.remove(); 1062 } 1063 } 1064 1065 onPrintServicesChanged(); 1066 } 1067 1068 private void addServiceLocked(RemotePrintService service) { 1069 mActiveServices.put(service.getComponentName(), service); 1070 if (mPrinterDiscoverySession != null) { 1071 mPrinterDiscoverySession.onServiceAddedLocked(service); 1072 } 1073 } 1074 1075 private void removeServiceLocked(RemotePrintService service) { 1076 // Fail all print jobs. 1077 failActivePrintJobsForService(service.getComponentName()); 1078 // If discovery is in progress, tear down the service. 1079 if (mPrinterDiscoverySession != null) { 1080 mPrinterDiscoverySession.onServiceRemovedLocked(service); 1081 } else { 1082 // Otherwise, just destroy it. 1083 service.destroy(); 1084 } 1085 } 1086 1087 private void failActivePrintJobsForService(final ComponentName serviceName) { 1088 // Makes sure all active print jobs are failed since the service 1089 // just died. Do this off the main thread since we do to allow 1090 // calls into the spooler on the main thread. 1091 if (Looper.getMainLooper().isCurrentThread()) { 1092 BackgroundThread.getHandler().post(new Runnable() { 1093 @Override 1094 public void run() { 1095 failScheduledPrintJobsForServiceInternal(serviceName); 1096 } 1097 }); 1098 } else { 1099 failScheduledPrintJobsForServiceInternal(serviceName); 1100 } 1101 } 1102 1103 private void failScheduledPrintJobsForServiceInternal(ComponentName serviceName) { 1104 List<PrintJobInfo> printJobs = mSpooler.getPrintJobInfos(serviceName, 1105 PrintJobInfo.STATE_ANY_SCHEDULED, PrintManager.APP_ID_ANY); 1106 if (printJobs == null) { 1107 return; 1108 } 1109 final long identity = Binder.clearCallingIdentity(); 1110 try { 1111 final int printJobCount = printJobs.size(); 1112 for (int i = 0; i < printJobCount; i++) { 1113 PrintJobInfo printJob = printJobs.get(i); 1114 mSpooler.setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED, 1115 mContext.getString(R.string.reason_service_unavailable)); 1116 } 1117 } finally { 1118 Binder.restoreCallingIdentity(identity); 1119 } 1120 } 1121 1122 private void throwIfDestroyedLocked() { 1123 if (mDestroyed) { 1124 throw new IllegalStateException("Cannot interact with a destroyed instance."); 1125 } 1126 } 1127 1128 private void handleDispatchPrintJobStateChanged(PrintJobId printJobId, int appId) { 1129 final List<PrintJobStateChangeListenerRecord> records; 1130 synchronized (mLock) { 1131 if (mPrintJobStateChangeListenerRecords == null) { 1132 return; 1133 } 1134 records = new ArrayList<PrintJobStateChangeListenerRecord>( 1135 mPrintJobStateChangeListenerRecords); 1136 } 1137 final int recordCount = records.size(); 1138 for (int i = 0; i < recordCount; i++) { 1139 PrintJobStateChangeListenerRecord record = records.get(i); 1140 if (record.appId == PrintManager.APP_ID_ANY 1141 || record.appId == appId) 1142 try { 1143 record.listener.onPrintJobStateChanged(printJobId); 1144 } catch (RemoteException re) { 1145 Log.e(LOG_TAG, "Error notifying for print job state change", re); 1146 } 1147 } 1148 } 1149 1150 private void handleDispatchPrintServicesChanged() { 1151 final List<ListenerRecord<IPrintServicesChangeListener>> records; 1152 synchronized (mLock) { 1153 if (mPrintServicesChangeListenerRecords == null) { 1154 return; 1155 } 1156 records = new ArrayList<>(mPrintServicesChangeListenerRecords); 1157 } 1158 final int recordCount = records.size(); 1159 for (int i = 0; i < recordCount; i++) { 1160 ListenerRecord<IPrintServicesChangeListener> record = records.get(i); 1161 1162 try { 1163 record.listener.onPrintServicesChanged();; 1164 } catch (RemoteException re) { 1165 Log.e(LOG_TAG, "Error notifying for print services change", re); 1166 } 1167 } 1168 } 1169 1170 private void handleDispatchPrintServiceRecommendationsUpdated( 1171 @Nullable List<RecommendationInfo> recommendations) { 1172 final List<ListenerRecord<IRecommendationsChangeListener>> records; 1173 synchronized (mLock) { 1174 if (mPrintServiceRecommendationsChangeListenerRecords == null) { 1175 return; 1176 } 1177 records = new ArrayList<>(mPrintServiceRecommendationsChangeListenerRecords); 1178 1179 mPrintServiceRecommendations = recommendations; 1180 } 1181 final int recordCount = records.size(); 1182 for (int i = 0; i < recordCount; i++) { 1183 ListenerRecord<IRecommendationsChangeListener> record = records.get(i); 1184 1185 try { 1186 record.listener.onRecommendationsChanged(); 1187 } catch (RemoteException re) { 1188 Log.e(LOG_TAG, "Error notifying for print service recommendations change", re); 1189 } 1190 } 1191 } 1192 1193 private final class UserStateHandler extends Handler { 1194 public static final int MSG_DISPATCH_PRINT_JOB_STATE_CHANGED = 1; 1195 public static final int MSG_DISPATCH_PRINT_SERVICES_CHANGED = 2; 1196 public static final int MSG_DISPATCH_PRINT_SERVICES_RECOMMENDATIONS_UPDATED = 3; 1197 1198 public UserStateHandler(Looper looper) { 1199 super(looper, null, false); 1200 } 1201 1202 @Override 1203 public void handleMessage(Message message) { 1204 switch (message.what) { 1205 case MSG_DISPATCH_PRINT_JOB_STATE_CHANGED: 1206 PrintJobId printJobId = (PrintJobId) message.obj; 1207 final int appId = message.arg1; 1208 handleDispatchPrintJobStateChanged(printJobId, appId); 1209 break; 1210 case MSG_DISPATCH_PRINT_SERVICES_CHANGED: 1211 handleDispatchPrintServicesChanged(); 1212 break; 1213 case MSG_DISPATCH_PRINT_SERVICES_RECOMMENDATIONS_UPDATED: 1214 handleDispatchPrintServiceRecommendationsUpdated( 1215 (List<RecommendationInfo>) message.obj); 1216 break; 1217 default: 1218 // not reached 1219 } 1220 } 1221 } 1222 1223 private abstract class PrintJobStateChangeListenerRecord implements DeathRecipient { 1224 @NonNull final IPrintJobStateChangeListener listener; 1225 final int appId; 1226 1227 public PrintJobStateChangeListenerRecord(@NonNull IPrintJobStateChangeListener listener, 1228 int appId) throws RemoteException { 1229 this.listener = listener; 1230 this.appId = appId; 1231 listener.asBinder().linkToDeath(this, 0); 1232 } 1233 1234 @Override 1235 public void binderDied() { 1236 listener.asBinder().unlinkToDeath(this, 0); 1237 onBinderDied(); 1238 } 1239 1240 public abstract void onBinderDied(); 1241 } 1242 1243 private abstract class ListenerRecord<T extends IInterface> implements DeathRecipient { 1244 @NonNull final T listener; 1245 1246 public ListenerRecord(@NonNull T listener) throws RemoteException { 1247 this.listener = listener; 1248 listener.asBinder().linkToDeath(this, 0); 1249 } 1250 1251 @Override 1252 public void binderDied() { 1253 listener.asBinder().unlinkToDeath(this, 0); 1254 onBinderDied(); 1255 } 1256 1257 public abstract void onBinderDied(); 1258 } 1259 1260 private class PrinterDiscoverySessionMediator { 1261 private final ArrayMap<PrinterId, PrinterInfo> mPrinters = 1262 new ArrayMap<PrinterId, PrinterInfo>(); 1263 1264 private final RemoteCallbackList<IPrinterDiscoveryObserver> mDiscoveryObservers = 1265 new RemoteCallbackList<IPrinterDiscoveryObserver>() { 1266 @Override 1267 public void onCallbackDied(IPrinterDiscoveryObserver observer) { 1268 synchronized (mLock) { 1269 stopPrinterDiscoveryLocked(observer); 1270 removeObserverLocked(observer); 1271 } 1272 } 1273 }; 1274 1275 private final List<IBinder> mStartedPrinterDiscoveryTokens = new ArrayList<IBinder>(); 1276 1277 private final List<PrinterId> mStateTrackedPrinters = new ArrayList<PrinterId>(); 1278 1279 private final Handler mSessionHandler; 1280 1281 private boolean mIsDestroyed; 1282 1283 public PrinterDiscoverySessionMediator(Context context) { 1284 mSessionHandler = new SessionHandler(context.getMainLooper()); 1285 // Kick off the session creation. 1286 List<RemotePrintService> services = new ArrayList<RemotePrintService>( 1287 mActiveServices.values()); 1288 mSessionHandler.obtainMessage(SessionHandler 1289 .MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION, services) 1290 .sendToTarget(); 1291 } 1292 1293 public void addObserverLocked(@NonNull IPrinterDiscoveryObserver observer) { 1294 // Add the observer. 1295 mDiscoveryObservers.register(observer); 1296 1297 // Bring the added observer up to speed with the printers. 1298 if (!mPrinters.isEmpty()) { 1299 List<PrinterInfo> printers = new ArrayList<PrinterInfo>(mPrinters.values()); 1300 SomeArgs args = SomeArgs.obtain(); 1301 args.arg1 = observer; 1302 args.arg2 = printers; 1303 mSessionHandler.obtainMessage(SessionHandler.MSG_PRINTERS_ADDED, 1304 args).sendToTarget(); 1305 } 1306 } 1307 1308 public void removeObserverLocked(@NonNull IPrinterDiscoveryObserver observer) { 1309 // Remove the observer. 1310 mDiscoveryObservers.unregister(observer); 1311 // No one else observing - then kill it. 1312 if (mDiscoveryObservers.getRegisteredCallbackCount() == 0) { 1313 destroyLocked(); 1314 } 1315 } 1316 1317 public final void startPrinterDiscoveryLocked(@NonNull IPrinterDiscoveryObserver observer, 1318 @Nullable List<PrinterId> priorityList) { 1319 if (mIsDestroyed) { 1320 Log.w(LOG_TAG, "Not starting dicovery - session destroyed"); 1321 return; 1322 } 1323 1324 final boolean discoveryStarted = !mStartedPrinterDiscoveryTokens.isEmpty(); 1325 1326 // Remember we got a start request to match with an end. 1327 mStartedPrinterDiscoveryTokens.add(observer.asBinder()); 1328 1329 // If printer discovery is ongoing and the start request has a list 1330 // of printer to be checked, then we just request validating them. 1331 if (discoveryStarted && priorityList != null && !priorityList.isEmpty()) { 1332 validatePrinters(priorityList); 1333 return; 1334 } 1335 1336 // The service are already performing discovery - nothing to do. 1337 if (mStartedPrinterDiscoveryTokens.size() > 1) { 1338 return; 1339 } 1340 1341 List<RemotePrintService> services = new ArrayList<RemotePrintService>( 1342 mActiveServices.values()); 1343 SomeArgs args = SomeArgs.obtain(); 1344 args.arg1 = services; 1345 args.arg2 = priorityList; 1346 mSessionHandler.obtainMessage(SessionHandler 1347 .MSG_DISPATCH_START_PRINTER_DISCOVERY, args) 1348 .sendToTarget(); 1349 } 1350 1351 public final void stopPrinterDiscoveryLocked(@NonNull IPrinterDiscoveryObserver observer) { 1352 if (mIsDestroyed) { 1353 Log.w(LOG_TAG, "Not stopping dicovery - session destroyed"); 1354 return; 1355 } 1356 // This one did not make an active discovery request - nothing to do. 1357 if (!mStartedPrinterDiscoveryTokens.remove(observer.asBinder())) { 1358 return; 1359 } 1360 // There are other interested observers - do not stop discovery. 1361 if (!mStartedPrinterDiscoveryTokens.isEmpty()) { 1362 return; 1363 } 1364 List<RemotePrintService> services = new ArrayList<RemotePrintService>( 1365 mActiveServices.values()); 1366 mSessionHandler.obtainMessage(SessionHandler 1367 .MSG_DISPATCH_STOP_PRINTER_DISCOVERY, services) 1368 .sendToTarget(); 1369 } 1370 1371 public void validatePrintersLocked(@NonNull List<PrinterId> printerIds) { 1372 if (mIsDestroyed) { 1373 Log.w(LOG_TAG, "Not validating pritners - session destroyed"); 1374 return; 1375 } 1376 1377 List<PrinterId> remainingList = new ArrayList<PrinterId>(printerIds); 1378 while (!remainingList.isEmpty()) { 1379 Iterator<PrinterId> iterator = remainingList.iterator(); 1380 // Gather the printers per service and request a validation. 1381 List<PrinterId> updateList = new ArrayList<PrinterId>(); 1382 ComponentName serviceName = null; 1383 while (iterator.hasNext()) { 1384 PrinterId printerId = iterator.next(); 1385 if (printerId != null) { 1386 if (updateList.isEmpty()) { 1387 updateList.add(printerId); 1388 serviceName = printerId.getServiceName(); 1389 iterator.remove(); 1390 } else if (printerId.getServiceName().equals(serviceName)) { 1391 updateList.add(printerId); 1392 iterator.remove(); 1393 } 1394 } 1395 } 1396 // Schedule a notification of the service. 1397 RemotePrintService service = mActiveServices.get(serviceName); 1398 if (service != null) { 1399 SomeArgs args = SomeArgs.obtain(); 1400 args.arg1 = service; 1401 args.arg2 = updateList; 1402 mSessionHandler.obtainMessage(SessionHandler 1403 .MSG_VALIDATE_PRINTERS, args) 1404 .sendToTarget(); 1405 } 1406 } 1407 } 1408 1409 public final void startPrinterStateTrackingLocked(@NonNull PrinterId printerId) { 1410 if (mIsDestroyed) { 1411 Log.w(LOG_TAG, "Not starting printer state tracking - session destroyed"); 1412 return; 1413 } 1414 // If printer discovery is not started - nothing to do. 1415 if (mStartedPrinterDiscoveryTokens.isEmpty()) { 1416 return; 1417 } 1418 final boolean containedPrinterId = mStateTrackedPrinters.contains(printerId); 1419 // Keep track of the number of requests to track this one. 1420 mStateTrackedPrinters.add(printerId); 1421 // If we were tracking this printer - nothing to do. 1422 if (containedPrinterId) { 1423 return; 1424 } 1425 // No service - nothing to do. 1426 RemotePrintService service = mActiveServices.get(printerId.getServiceName()); 1427 if (service == null) { 1428 return; 1429 } 1430 // Ask the service to start tracking. 1431 SomeArgs args = SomeArgs.obtain(); 1432 args.arg1 = service; 1433 args.arg2 = printerId; 1434 mSessionHandler.obtainMessage(SessionHandler 1435 .MSG_START_PRINTER_STATE_TRACKING, args) 1436 .sendToTarget(); 1437 } 1438 1439 public final void stopPrinterStateTrackingLocked(PrinterId printerId) { 1440 if (mIsDestroyed) { 1441 Log.w(LOG_TAG, "Not stopping printer state tracking - session destroyed"); 1442 return; 1443 } 1444 // If printer discovery is not started - nothing to do. 1445 if (mStartedPrinterDiscoveryTokens.isEmpty()) { 1446 return; 1447 } 1448 // If we did not track this printer - nothing to do. 1449 if (!mStateTrackedPrinters.remove(printerId)) { 1450 return; 1451 } 1452 // No service - nothing to do. 1453 RemotePrintService service = mActiveServices.get(printerId.getServiceName()); 1454 if (service == null) { 1455 return; 1456 } 1457 // Ask the service to start tracking. 1458 SomeArgs args = SomeArgs.obtain(); 1459 args.arg1 = service; 1460 args.arg2 = printerId; 1461 mSessionHandler.obtainMessage(SessionHandler 1462 .MSG_STOP_PRINTER_STATE_TRACKING, args) 1463 .sendToTarget(); 1464 } 1465 1466 public void onDestroyed() { 1467 /* do nothing */ 1468 } 1469 1470 public void destroyLocked() { 1471 if (mIsDestroyed) { 1472 Log.w(LOG_TAG, "Not destroying - session destroyed"); 1473 return; 1474 } 1475 mIsDestroyed = true; 1476 // Make sure printer tracking is stopped. 1477 final int printerCount = mStateTrackedPrinters.size(); 1478 for (int i = 0; i < printerCount; i++) { 1479 PrinterId printerId = mStateTrackedPrinters.get(i); 1480 stopPrinterStateTracking(printerId); 1481 } 1482 // Make sure discovery is stopped. 1483 final int observerCount = mStartedPrinterDiscoveryTokens.size(); 1484 for (int i = 0; i < observerCount; i++) { 1485 IBinder token = mStartedPrinterDiscoveryTokens.get(i); 1486 stopPrinterDiscoveryLocked(IPrinterDiscoveryObserver.Stub.asInterface(token)); 1487 } 1488 // Tell the services we are done. 1489 List<RemotePrintService> services = new ArrayList<RemotePrintService>( 1490 mActiveServices.values()); 1491 mSessionHandler.obtainMessage(SessionHandler 1492 .MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION, services) 1493 .sendToTarget(); 1494 } 1495 1496 public void onPrintersAddedLocked(List<PrinterInfo> printers) { 1497 if (DEBUG) { 1498 Log.i(LOG_TAG, "onPrintersAddedLocked()"); 1499 } 1500 if (mIsDestroyed) { 1501 Log.w(LOG_TAG, "Not adding printers - session destroyed"); 1502 return; 1503 } 1504 List<PrinterInfo> addedPrinters = null; 1505 final int addedPrinterCount = printers.size(); 1506 for (int i = 0; i < addedPrinterCount; i++) { 1507 PrinterInfo printer = printers.get(i); 1508 PrinterInfo oldPrinter = mPrinters.put(printer.getId(), printer); 1509 if (oldPrinter == null || !oldPrinter.equals(printer)) { 1510 if (addedPrinters == null) { 1511 addedPrinters = new ArrayList<PrinterInfo>(); 1512 } 1513 addedPrinters.add(printer); 1514 } 1515 } 1516 if (addedPrinters != null) { 1517 mSessionHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_ADDED, 1518 addedPrinters).sendToTarget(); 1519 } 1520 } 1521 1522 public void onPrintersRemovedLocked(List<PrinterId> printerIds) { 1523 if (DEBUG) { 1524 Log.i(LOG_TAG, "onPrintersRemovedLocked()"); 1525 } 1526 if (mIsDestroyed) { 1527 Log.w(LOG_TAG, "Not removing printers - session destroyed"); 1528 return; 1529 } 1530 List<PrinterId> removedPrinterIds = null; 1531 final int removedPrinterCount = printerIds.size(); 1532 for (int i = 0; i < removedPrinterCount; i++) { 1533 PrinterId removedPrinterId = printerIds.get(i); 1534 if (mPrinters.remove(removedPrinterId) != null) { 1535 if (removedPrinterIds == null) { 1536 removedPrinterIds = new ArrayList<PrinterId>(); 1537 } 1538 removedPrinterIds.add(removedPrinterId); 1539 } 1540 } 1541 if (removedPrinterIds != null) { 1542 mSessionHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_REMOVED, 1543 removedPrinterIds).sendToTarget(); 1544 } 1545 } 1546 1547 public void onServiceRemovedLocked(RemotePrintService service) { 1548 if (mIsDestroyed) { 1549 Log.w(LOG_TAG, "Not updating removed service - session destroyed"); 1550 return; 1551 } 1552 // Remove the reported and tracked printers for that service. 1553 ComponentName serviceName = service.getComponentName(); 1554 removePrintersForServiceLocked(serviceName); 1555 service.destroy(); 1556 } 1557 1558 /** 1559 * Handle that a custom icon for a printer was loaded. 1560 * 1561 * This increments the icon generation and adds the printer again which triggers an update 1562 * in all users of the currently known printers. 1563 * 1564 * @param printerId the id of the printer the icon belongs to 1565 * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon() 1566 */ 1567 public void onCustomPrinterIconLoadedLocked(PrinterId printerId) { 1568 if (DEBUG) { 1569 Log.i(LOG_TAG, "onCustomPrinterIconLoadedLocked()"); 1570 } 1571 if (mIsDestroyed) { 1572 Log.w(LOG_TAG, "Not updating printer - session destroyed"); 1573 return; 1574 } 1575 1576 PrinterInfo printer = mPrinters.get(printerId); 1577 if (printer != null) { 1578 PrinterInfo newPrinter = (new PrinterInfo.Builder(printer)) 1579 .incCustomPrinterIconGen().build(); 1580 mPrinters.put(printerId, newPrinter); 1581 1582 ArrayList<PrinterInfo> addedPrinters = new ArrayList<>(1); 1583 addedPrinters.add(newPrinter); 1584 mSessionHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_ADDED, 1585 addedPrinters).sendToTarget(); 1586 } 1587 } 1588 1589 public void onServiceDiedLocked(RemotePrintService service) { 1590 // Remove the reported by that service. 1591 removePrintersForServiceLocked(service.getComponentName()); 1592 } 1593 1594 public void onServiceAddedLocked(RemotePrintService service) { 1595 if (mIsDestroyed) { 1596 Log.w(LOG_TAG, "Not updating added service - session destroyed"); 1597 return; 1598 } 1599 // Tell the service to create a session. 1600 mSessionHandler.obtainMessage( 1601 SessionHandler.MSG_CREATE_PRINTER_DISCOVERY_SESSION, 1602 service).sendToTarget(); 1603 // Start printer discovery if necessary. 1604 if (!mStartedPrinterDiscoveryTokens.isEmpty()) { 1605 mSessionHandler.obtainMessage( 1606 SessionHandler.MSG_START_PRINTER_DISCOVERY, 1607 service).sendToTarget(); 1608 } 1609 // Start tracking printers if necessary 1610 final int trackedPrinterCount = mStateTrackedPrinters.size(); 1611 for (int i = 0; i < trackedPrinterCount; i++) { 1612 PrinterId printerId = mStateTrackedPrinters.get(i); 1613 if (printerId.getServiceName().equals(service.getComponentName())) { 1614 SomeArgs args = SomeArgs.obtain(); 1615 args.arg1 = service; 1616 args.arg2 = printerId; 1617 mSessionHandler.obtainMessage(SessionHandler 1618 .MSG_START_PRINTER_STATE_TRACKING, args) 1619 .sendToTarget(); 1620 } 1621 } 1622 } 1623 1624 public void dump(PrintWriter pw, String prefix) { 1625 pw.append(prefix).append("destroyed=") 1626 .append(String.valueOf(mDestroyed)).println(); 1627 1628 pw.append(prefix).append("printDiscoveryInProgress=") 1629 .append(String.valueOf(!mStartedPrinterDiscoveryTokens.isEmpty())).println(); 1630 1631 String tab = " "; 1632 1633 pw.append(prefix).append(tab).append("printer discovery observers:").println(); 1634 final int observerCount = mDiscoveryObservers.beginBroadcast(); 1635 for (int i = 0; i < observerCount; i++) { 1636 IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i); 1637 pw.append(prefix).append(prefix).append(observer.toString()); 1638 pw.println(); 1639 } 1640 mDiscoveryObservers.finishBroadcast(); 1641 1642 pw.append(prefix).append(tab).append("start discovery requests:").println(); 1643 final int tokenCount = this.mStartedPrinterDiscoveryTokens.size(); 1644 for (int i = 0; i < tokenCount; i++) { 1645 IBinder token = mStartedPrinterDiscoveryTokens.get(i); 1646 pw.append(prefix).append(tab).append(tab).append(token.toString()).println(); 1647 } 1648 1649 pw.append(prefix).append(tab).append("tracked printer requests:").println(); 1650 final int trackedPrinters = mStateTrackedPrinters.size(); 1651 for (int i = 0; i < trackedPrinters; i++) { 1652 PrinterId printer = mStateTrackedPrinters.get(i); 1653 pw.append(prefix).append(tab).append(tab).append(printer.toString()).println(); 1654 } 1655 1656 pw.append(prefix).append(tab).append("printers:").println(); 1657 final int pritnerCount = mPrinters.size(); 1658 for (int i = 0; i < pritnerCount; i++) { 1659 PrinterInfo printer = mPrinters.valueAt(i); 1660 pw.append(prefix).append(tab).append(tab).append( 1661 printer.toString()).println(); 1662 } 1663 } 1664 1665 private void removePrintersForServiceLocked(ComponentName serviceName) { 1666 // No printers - nothing to do. 1667 if (mPrinters.isEmpty()) { 1668 return; 1669 } 1670 // Remove the printers for that service. 1671 List<PrinterId> removedPrinterIds = null; 1672 final int printerCount = mPrinters.size(); 1673 for (int i = 0; i < printerCount; i++) { 1674 PrinterId printerId = mPrinters.keyAt(i); 1675 if (printerId.getServiceName().equals(serviceName)) { 1676 if (removedPrinterIds == null) { 1677 removedPrinterIds = new ArrayList<PrinterId>(); 1678 } 1679 removedPrinterIds.add(printerId); 1680 } 1681 } 1682 if (removedPrinterIds != null) { 1683 final int removedPrinterCount = removedPrinterIds.size(); 1684 for (int i = 0; i < removedPrinterCount; i++) { 1685 mPrinters.remove(removedPrinterIds.get(i)); 1686 } 1687 mSessionHandler.obtainMessage( 1688 SessionHandler.MSG_DISPATCH_PRINTERS_REMOVED, 1689 removedPrinterIds).sendToTarget(); 1690 } 1691 } 1692 1693 private void handleDispatchPrintersAdded(List<PrinterInfo> addedPrinters) { 1694 final int observerCount = mDiscoveryObservers.beginBroadcast(); 1695 for (int i = 0; i < observerCount; i++) { 1696 IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i); 1697 handlePrintersAdded(observer, addedPrinters); 1698 } 1699 mDiscoveryObservers.finishBroadcast(); 1700 } 1701 1702 private void handleDispatchPrintersRemoved(List<PrinterId> removedPrinterIds) { 1703 final int observerCount = mDiscoveryObservers.beginBroadcast(); 1704 for (int i = 0; i < observerCount; i++) { 1705 IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i); 1706 handlePrintersRemoved(observer, removedPrinterIds); 1707 } 1708 mDiscoveryObservers.finishBroadcast(); 1709 } 1710 1711 private void handleDispatchCreatePrinterDiscoverySession( 1712 List<RemotePrintService> services) { 1713 final int serviceCount = services.size(); 1714 for (int i = 0; i < serviceCount; i++) { 1715 RemotePrintService service = services.get(i); 1716 service.createPrinterDiscoverySession(); 1717 } 1718 } 1719 1720 private void handleDispatchDestroyPrinterDiscoverySession( 1721 List<RemotePrintService> services) { 1722 final int serviceCount = services.size(); 1723 for (int i = 0; i < serviceCount; i++) { 1724 RemotePrintService service = services.get(i); 1725 service.destroyPrinterDiscoverySession(); 1726 } 1727 onDestroyed(); 1728 } 1729 1730 private void handleDispatchStartPrinterDiscovery( 1731 List<RemotePrintService> services, List<PrinterId> printerIds) { 1732 final int serviceCount = services.size(); 1733 for (int i = 0; i < serviceCount; i++) { 1734 RemotePrintService service = services.get(i); 1735 service.startPrinterDiscovery(printerIds); 1736 } 1737 } 1738 1739 private void handleDispatchStopPrinterDiscovery(List<RemotePrintService> services) { 1740 final int serviceCount = services.size(); 1741 for (int i = 0; i < serviceCount; i++) { 1742 RemotePrintService service = services.get(i); 1743 service.stopPrinterDiscovery(); 1744 } 1745 } 1746 1747 private void handleValidatePrinters(RemotePrintService service, 1748 List<PrinterId> printerIds) { 1749 service.validatePrinters(printerIds); 1750 } 1751 1752 private void handleStartPrinterStateTracking(@NonNull RemotePrintService service, 1753 @NonNull PrinterId printerId) { 1754 service.startPrinterStateTracking(printerId); 1755 } 1756 1757 private void handleStopPrinterStateTracking(RemotePrintService service, 1758 PrinterId printerId) { 1759 service.stopPrinterStateTracking(printerId); 1760 } 1761 1762 private void handlePrintersAdded(IPrinterDiscoveryObserver observer, 1763 List<PrinterInfo> printers) { 1764 try { 1765 observer.onPrintersAdded(new ParceledListSlice<PrinterInfo>(printers)); 1766 } catch (RemoteException re) { 1767 Log.e(LOG_TAG, "Error sending added printers", re); 1768 } 1769 } 1770 1771 private void handlePrintersRemoved(IPrinterDiscoveryObserver observer, 1772 List<PrinterId> printerIds) { 1773 try { 1774 observer.onPrintersRemoved(new ParceledListSlice<PrinterId>(printerIds)); 1775 } catch (RemoteException re) { 1776 Log.e(LOG_TAG, "Error sending removed printers", re); 1777 } 1778 } 1779 1780 private final class SessionHandler extends Handler { 1781 public static final int MSG_PRINTERS_ADDED = 1; 1782 public static final int MSG_PRINTERS_REMOVED = 2; 1783 public static final int MSG_DISPATCH_PRINTERS_ADDED = 3; 1784 public static final int MSG_DISPATCH_PRINTERS_REMOVED = 4; 1785 1786 public static final int MSG_CREATE_PRINTER_DISCOVERY_SESSION = 5; 1787 public static final int MSG_DESTROY_PRINTER_DISCOVERY_SESSION = 6; 1788 public static final int MSG_START_PRINTER_DISCOVERY = 7; 1789 public static final int MSG_STOP_PRINTER_DISCOVERY = 8; 1790 public static final int MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION = 9; 1791 public static final int MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION = 10; 1792 public static final int MSG_DISPATCH_START_PRINTER_DISCOVERY = 11; 1793 public static final int MSG_DISPATCH_STOP_PRINTER_DISCOVERY = 12; 1794 public static final int MSG_VALIDATE_PRINTERS = 13; 1795 public static final int MSG_START_PRINTER_STATE_TRACKING = 14; 1796 public static final int MSG_STOP_PRINTER_STATE_TRACKING = 15; 1797 public static final int MSG_DESTROY_SERVICE = 16; 1798 1799 SessionHandler(Looper looper) { 1800 super(looper, null, false); 1801 } 1802 1803 @Override 1804 @SuppressWarnings("unchecked") 1805 public void handleMessage(Message message) { 1806 switch (message.what) { 1807 case MSG_PRINTERS_ADDED: { 1808 SomeArgs args = (SomeArgs) message.obj; 1809 IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) args.arg1; 1810 List<PrinterInfo> addedPrinters = (List<PrinterInfo>) args.arg2; 1811 args.recycle(); 1812 handlePrintersAdded(observer, addedPrinters); 1813 } break; 1814 1815 case MSG_PRINTERS_REMOVED: { 1816 SomeArgs args = (SomeArgs) message.obj; 1817 IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) args.arg1; 1818 List<PrinterId> removedPrinterIds = (List<PrinterId>) args.arg2; 1819 args.recycle(); 1820 handlePrintersRemoved(observer, removedPrinterIds); 1821 } 1822 1823 case MSG_DISPATCH_PRINTERS_ADDED: { 1824 List<PrinterInfo> addedPrinters = (List<PrinterInfo>) message.obj; 1825 handleDispatchPrintersAdded(addedPrinters); 1826 } break; 1827 1828 case MSG_DISPATCH_PRINTERS_REMOVED: { 1829 List<PrinterId> removedPrinterIds = (List<PrinterId>) message.obj; 1830 handleDispatchPrintersRemoved(removedPrinterIds); 1831 } break; 1832 1833 case MSG_CREATE_PRINTER_DISCOVERY_SESSION: { 1834 RemotePrintService service = (RemotePrintService) message.obj; 1835 service.createPrinterDiscoverySession(); 1836 } break; 1837 1838 case MSG_DESTROY_PRINTER_DISCOVERY_SESSION: { 1839 RemotePrintService service = (RemotePrintService) message.obj; 1840 service.destroyPrinterDiscoverySession(); 1841 } break; 1842 1843 case MSG_START_PRINTER_DISCOVERY: { 1844 RemotePrintService service = (RemotePrintService) message.obj; 1845 service.startPrinterDiscovery(null); 1846 } break; 1847 1848 case MSG_STOP_PRINTER_DISCOVERY: { 1849 RemotePrintService service = (RemotePrintService) message.obj; 1850 service.stopPrinterDiscovery(); 1851 } break; 1852 1853 case MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION: { 1854 List<RemotePrintService> services = (List<RemotePrintService>) message.obj; 1855 handleDispatchCreatePrinterDiscoverySession(services); 1856 } break; 1857 1858 case MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION: { 1859 List<RemotePrintService> services = (List<RemotePrintService>) message.obj; 1860 handleDispatchDestroyPrinterDiscoverySession(services); 1861 } break; 1862 1863 case MSG_DISPATCH_START_PRINTER_DISCOVERY: { 1864 SomeArgs args = (SomeArgs) message.obj; 1865 List<RemotePrintService> services = (List<RemotePrintService>) args.arg1; 1866 List<PrinterId> printerIds = (List<PrinterId>) args.arg2; 1867 args.recycle(); 1868 handleDispatchStartPrinterDiscovery(services, printerIds); 1869 } break; 1870 1871 case MSG_DISPATCH_STOP_PRINTER_DISCOVERY: { 1872 List<RemotePrintService> services = (List<RemotePrintService>) message.obj; 1873 handleDispatchStopPrinterDiscovery(services); 1874 } break; 1875 1876 case MSG_VALIDATE_PRINTERS: { 1877 SomeArgs args = (SomeArgs) message.obj; 1878 RemotePrintService service = (RemotePrintService) args.arg1; 1879 List<PrinterId> printerIds = (List<PrinterId>) args.arg2; 1880 args.recycle(); 1881 handleValidatePrinters(service, printerIds); 1882 } break; 1883 1884 case MSG_START_PRINTER_STATE_TRACKING: { 1885 SomeArgs args = (SomeArgs) message.obj; 1886 RemotePrintService service = (RemotePrintService) args.arg1; 1887 PrinterId printerId = (PrinterId) args.arg2; 1888 args.recycle(); 1889 handleStartPrinterStateTracking(service, printerId); 1890 } break; 1891 1892 case MSG_STOP_PRINTER_STATE_TRACKING: { 1893 SomeArgs args = (SomeArgs) message.obj; 1894 RemotePrintService service = (RemotePrintService) args.arg1; 1895 PrinterId printerId = (PrinterId) args.arg2; 1896 args.recycle(); 1897 handleStopPrinterStateTracking(service, printerId); 1898 } break; 1899 1900 case MSG_DESTROY_SERVICE: { 1901 RemotePrintService service = (RemotePrintService) message.obj; 1902 service.destroy(); 1903 } break; 1904 } 1905 } 1906 } 1907 } 1908 1909 private final class PrintJobForAppCache { 1910 private final SparseArray<List<PrintJobInfo>> mPrintJobsForRunningApp = 1911 new SparseArray<List<PrintJobInfo>>(); 1912 1913 public boolean onPrintJobCreated(final IBinder creator, final int appId, 1914 PrintJobInfo printJob) { 1915 try { 1916 creator.linkToDeath(new DeathRecipient() { 1917 @Override 1918 public void binderDied() { 1919 creator.unlinkToDeath(this, 0); 1920 synchronized (mLock) { 1921 mPrintJobsForRunningApp.remove(appId); 1922 } 1923 } 1924 }, 0); 1925 } catch (RemoteException re) { 1926 /* The process is already dead - we just failed. */ 1927 return false; 1928 } 1929 synchronized (mLock) { 1930 List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get(appId); 1931 if (printJobsForApp == null) { 1932 printJobsForApp = new ArrayList<PrintJobInfo>(); 1933 mPrintJobsForRunningApp.put(appId, printJobsForApp); 1934 } 1935 printJobsForApp.add(printJob); 1936 } 1937 return true; 1938 } 1939 1940 public void onPrintJobStateChanged(PrintJobInfo printJob) { 1941 synchronized (mLock) { 1942 List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get( 1943 printJob.getAppId()); 1944 if (printJobsForApp == null) { 1945 return; 1946 } 1947 final int printJobCount = printJobsForApp.size(); 1948 for (int i = 0; i < printJobCount; i++) { 1949 PrintJobInfo oldPrintJob = printJobsForApp.get(i); 1950 if (oldPrintJob.getId().equals(printJob.getId())) { 1951 printJobsForApp.set(i, printJob); 1952 } 1953 } 1954 } 1955 } 1956 1957 public PrintJobInfo getPrintJob(PrintJobId printJobId, int appId) { 1958 synchronized (mLock) { 1959 List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get(appId); 1960 if (printJobsForApp == null) { 1961 return null; 1962 } 1963 final int printJobCount = printJobsForApp.size(); 1964 for (int i = 0; i < printJobCount; i++) { 1965 PrintJobInfo printJob = printJobsForApp.get(i); 1966 if (printJob.getId().equals(printJobId)) { 1967 return printJob; 1968 } 1969 } 1970 } 1971 return null; 1972 } 1973 1974 public List<PrintJobInfo> getPrintJobs(int appId) { 1975 synchronized (mLock) { 1976 List<PrintJobInfo> printJobs = null; 1977 if (appId == PrintManager.APP_ID_ANY) { 1978 final int bucketCount = mPrintJobsForRunningApp.size(); 1979 for (int i = 0; i < bucketCount; i++) { 1980 List<PrintJobInfo> bucket = mPrintJobsForRunningApp.valueAt(i); 1981 if (printJobs == null) { 1982 printJobs = new ArrayList<PrintJobInfo>(); 1983 } 1984 printJobs.addAll(bucket); 1985 } 1986 } else { 1987 List<PrintJobInfo> bucket = mPrintJobsForRunningApp.get(appId); 1988 if (bucket != null) { 1989 if (printJobs == null) { 1990 printJobs = new ArrayList<PrintJobInfo>(); 1991 } 1992 printJobs.addAll(bucket); 1993 } 1994 } 1995 if (printJobs != null) { 1996 return printJobs; 1997 } 1998 return Collections.emptyList(); 1999 } 2000 } 2001 2002 public void dump(PrintWriter pw, String prefix) { 2003 synchronized (mLock) { 2004 String tab = " "; 2005 final int bucketCount = mPrintJobsForRunningApp.size(); 2006 for (int i = 0; i < bucketCount; i++) { 2007 final int appId = mPrintJobsForRunningApp.keyAt(i); 2008 pw.append(prefix).append("appId=" + appId).append(':').println(); 2009 List<PrintJobInfo> bucket = mPrintJobsForRunningApp.valueAt(i); 2010 final int printJobCount = bucket.size(); 2011 for (int j = 0; j < printJobCount; j++) { 2012 PrintJobInfo printJob = bucket.get(j); 2013 pw.append(prefix).append(tab).append(printJob.toString()).println(); 2014 } 2015 } 2016 } 2017 } 2018 } 2019} 2020