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