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