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