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