UserState.java revision 9158825f9c41869689d6b1786d7c7aa8bdd524ce
1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.print;
18
19import android.app.PendingIntent;
20import android.content.ComponentName;
21import android.content.Context;
22import android.content.Intent;
23import android.content.IntentSender;
24import android.content.pm.ApplicationInfo;
25import android.content.pm.PackageManager;
26import android.content.pm.ParceledListSlice;
27import android.content.pm.ResolveInfo;
28import android.content.pm.ServiceInfo;
29import android.net.Uri;
30import android.os.AsyncTask;
31import android.os.Binder;
32import android.os.Bundle;
33import android.os.Handler;
34import android.os.IBinder;
35import android.os.IBinder.DeathRecipient;
36import android.os.Looper;
37import android.os.Message;
38import android.os.RemoteCallbackList;
39import android.os.RemoteException;
40import android.os.UserHandle;
41import android.print.IPrintDocumentAdapter;
42import android.print.IPrintJobStateChangeListener;
43import android.print.IPrinterDiscoveryObserver;
44import android.print.PrintAttributes;
45import android.print.PrintJobId;
46import android.print.PrintJobInfo;
47import android.print.PrintManager;
48import android.print.PrinterId;
49import android.print.PrinterInfo;
50import android.printservice.PrintServiceInfo;
51import android.provider.DocumentsContract;
52import android.provider.Settings;
53import android.text.TextUtils;
54import android.text.TextUtils.SimpleStringSplitter;
55import android.util.ArrayMap;
56import android.util.ArraySet;
57import android.util.Log;
58import android.util.Slog;
59import android.util.SparseArray;
60
61import com.android.internal.R;
62import com.android.internal.os.BackgroundThread;
63import com.android.internal.os.SomeArgs;
64import com.android.server.print.RemotePrintService.PrintServiceCallbacks;
65import com.android.server.print.RemotePrintSpooler.PrintSpoolerCallbacks;
66
67import java.io.FileDescriptor;
68import java.io.PrintWriter;
69import java.util.ArrayList;
70import java.util.Collections;
71import java.util.HashSet;
72import java.util.Iterator;
73import java.util.List;
74import java.util.Set;
75
76/**
77 * Represents the print state for a user.
78 */
79final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks {
80
81    private static final String LOG_TAG = "UserState";
82
83    private static final boolean DEBUG = false;
84
85    private static final char COMPONENT_NAME_SEPARATOR = ':';
86
87    private final SimpleStringSplitter mStringColonSplitter =
88            new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
89
90    private final Intent mQueryIntent =
91            new Intent(android.printservice.PrintService.SERVICE_INTERFACE);
92
93    private final ArrayMap<ComponentName, RemotePrintService> mActiveServices =
94            new ArrayMap<ComponentName, RemotePrintService>();
95
96    private final List<PrintServiceInfo> mInstalledServices =
97            new ArrayList<PrintServiceInfo>();
98
99    private final Set<ComponentName> mEnabledServices =
100            new ArraySet<ComponentName>();
101
102    private final PrintJobForAppCache mPrintJobForAppCache =
103            new PrintJobForAppCache();
104
105    private final Object mLock;
106
107    private final Context mContext;
108
109    private final int mUserId;
110
111    private final RemotePrintSpooler mSpooler;
112
113    private final Handler mHandler;
114
115    private PrinterDiscoverySessionMediator mPrinterDiscoverySession;
116
117    private List<PrintJobStateChangeListenerRecord> mPrintJobStateChangeListenerRecords;
118
119    private boolean mDestroyed;
120
121    public UserState(Context context, int userId, Object lock) {
122        mContext = context;
123        mUserId = userId;
124        mLock = lock;
125        mSpooler = new RemotePrintSpooler(context, userId, this);
126        mHandler = new UserStateHandler(context.getMainLooper());
127        synchronized (mLock) {
128            enableSystemPrintServicesOnFirstBootLocked();
129        }
130    }
131
132    @Override
133    public void onPrintJobQueued(PrintJobInfo printJob) {
134        final RemotePrintService service;
135        synchronized (mLock) {
136            throwIfDestroyedLocked();
137            ComponentName printServiceName = printJob.getPrinterId().getServiceName();
138            service = mActiveServices.get(printServiceName);
139        }
140        if (service != null) {
141            service.onPrintJobQueued(printJob);
142        } else {
143            // The service for the job is no longer enabled, so just
144            // fail the job with the appropriate message.
145            mSpooler.setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED,
146                    mContext.getString(R.string.reason_service_unavailable));
147        }
148    }
149
150    @Override
151    public void onAllPrintJobsForServiceHandled(ComponentName printService) {
152        final RemotePrintService service;
153        synchronized (mLock) {
154            throwIfDestroyedLocked();
155            service = mActiveServices.get(printService);
156        }
157        if (service != null) {
158            service.onAllPrintJobsHandled();
159        }
160    }
161
162    public void removeObsoletePrintJobs() {
163        mSpooler.removeObsoletePrintJobs();
164    }
165
166    @SuppressWarnings("deprecation")
167    public Bundle print(String printJobName, IPrintDocumentAdapter adapter,
168            PrintAttributes attributes, String packageName, int appId) {
169        // Create print job place holder.
170        final PrintJobInfo printJob = new PrintJobInfo();
171        printJob.setId(new PrintJobId());
172        printJob.setAppId(appId);
173        printJob.setLabel(printJobName);
174        printJob.setAttributes(attributes);
175        printJob.setState(PrintJobInfo.STATE_CREATED);
176        printJob.setCopies(1);
177        printJob.setCreationTime(System.currentTimeMillis());
178
179        // Track this job so we can forget it when the creator dies.
180        if (!mPrintJobForAppCache.onPrintJobCreated(adapter.asBinder(), appId,
181                printJob)) {
182            // Not adding a print job means the client is dead - done.
183            return null;
184        }
185
186        // Spin the spooler to add the job and show the config UI.
187        new AsyncTask<Void, Void, Void>() {
188            @Override
189            protected Void doInBackground(Void... params) {
190                mSpooler.createPrintJob(printJob);
191                return null;
192            }
193        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
194
195        final long identity = Binder.clearCallingIdentity();
196        try {
197            Intent intent = new Intent(PrintManager.ACTION_PRINT_DIALOG);
198            intent.setData(Uri.fromParts("printjob", printJob.getId().flattenToString(), null));
199            intent.putExtra(PrintManager.EXTRA_PRINT_DOCUMENT_ADAPTER, adapter.asBinder());
200            intent.putExtra(PrintManager.EXTRA_PRINT_JOB, printJob);
201            intent.putExtra(DocumentsContract.EXTRA_PACKAGE_NAME, packageName);
202
203            IntentSender intentSender = PendingIntent.getActivityAsUser(
204                    mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT
205                    | PendingIntent.FLAG_CANCEL_CURRENT, null, new UserHandle(mUserId))
206                    .getIntentSender();
207
208            Bundle result = new Bundle();
209            result.putParcelable(PrintManager.EXTRA_PRINT_JOB, printJob);
210            result.putParcelable(PrintManager.EXTRA_PRINT_DIALOG_INTENT, intentSender);
211
212            return result;
213        } finally {
214            Binder.restoreCallingIdentity(identity);
215        }
216    }
217
218    public List<PrintJobInfo> getPrintJobInfos(int appId) {
219        List<PrintJobInfo> cachedPrintJobs = mPrintJobForAppCache.getPrintJobs(appId);
220        // Note that the print spooler is not storing print jobs that
221        // are in a terminal state as it is non-trivial to properly update
222        // the spooler state for when to forget print jobs in terminal state.
223        // Therefore, we fuse the cached print jobs for running apps (some
224        // jobs are in a terminal state) with the ones that the print
225        // spooler knows about (some jobs are being processed).
226        ArrayMap<PrintJobId, PrintJobInfo> result =
227                new ArrayMap<PrintJobId, PrintJobInfo>();
228
229        // Add the cached print jobs for running apps.
230        final int cachedPrintJobCount = cachedPrintJobs.size();
231        for (int i = 0; i < cachedPrintJobCount; i++) {
232            PrintJobInfo cachedPrintJob = cachedPrintJobs.get(i);
233            result.put(cachedPrintJob.getId(), cachedPrintJob);
234            // Strip out the tag 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