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