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