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