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