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