UserState.java revision f0847871eaf5cdcf697f07f013efee3d84d836f2
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
447            // No session - nothing to do.
448            if (mPrinterDiscoverySession == null) {
449                return;
450            }
451            // Kick of discovery.
452            mPrinterDiscoverySession.startPrinterDiscoveryLocked(observer,
453                    printerIds);
454        }
455    }
456
457    public void stopPrinterDiscovery(@NonNull IPrinterDiscoveryObserver observer) {
458        synchronized (mLock) {
459            throwIfDestroyedLocked();
460
461            // No session - nothing to do.
462            if (mPrinterDiscoverySession == null) {
463                return;
464            }
465            // Kick of discovery.
466            mPrinterDiscoverySession.stopPrinterDiscoveryLocked(observer);
467        }
468    }
469
470    public void validatePrinters(@NonNull List<PrinterId> printerIds) {
471        synchronized (mLock) {
472            throwIfDestroyedLocked();
473            // No services - nothing to do.
474            if (mActiveServices.isEmpty()) {
475                return;
476            }
477            // No session - nothing to do.
478            if (mPrinterDiscoverySession == null) {
479                return;
480            }
481            // Request an updated.
482            mPrinterDiscoverySession.validatePrintersLocked(printerIds);
483        }
484    }
485
486    public void startPrinterStateTracking(@NonNull PrinterId printerId) {
487        synchronized (mLock) {
488            throwIfDestroyedLocked();
489            // No services - nothing to do.
490            if (mActiveServices.isEmpty()) {
491                return;
492            }
493            // No session - nothing to do.
494            if (mPrinterDiscoverySession == null) {
495                return;
496            }
497            // Request start tracking the printer.
498            mPrinterDiscoverySession.startPrinterStateTrackingLocked(printerId);
499        }
500    }
501
502    public void stopPrinterStateTracking(PrinterId printerId) {
503        synchronized (mLock) {
504            throwIfDestroyedLocked();
505            // No services - nothing to do.
506            if (mActiveServices.isEmpty()) {
507                return;
508            }
509            // No session - nothing to do.
510            if (mPrinterDiscoverySession == null) {
511                return;
512            }
513            // Request stop tracking the printer.
514            mPrinterDiscoverySession.stopPrinterStateTrackingLocked(printerId);
515        }
516    }
517
518    public void addPrintJobStateChangeListener(@NonNull IPrintJobStateChangeListener listener,
519            int appId) throws RemoteException {
520        synchronized (mLock) {
521            throwIfDestroyedLocked();
522            if (mPrintJobStateChangeListenerRecords == null) {
523                mPrintJobStateChangeListenerRecords =
524                        new ArrayList<PrintJobStateChangeListenerRecord>();
525            }
526            mPrintJobStateChangeListenerRecords.add(
527                    new PrintJobStateChangeListenerRecord(listener, appId) {
528                @Override
529                public void onBinderDied() {
530                    mPrintJobStateChangeListenerRecords.remove(this);
531                }
532            });
533        }
534    }
535
536    public void removePrintJobStateChangeListener(@NonNull IPrintJobStateChangeListener listener) {
537        synchronized (mLock) {
538            throwIfDestroyedLocked();
539            if (mPrintJobStateChangeListenerRecords == null) {
540                return;
541            }
542            final int recordCount = mPrintJobStateChangeListenerRecords.size();
543            for (int i = 0; i < recordCount; i++) {
544                PrintJobStateChangeListenerRecord record =
545                        mPrintJobStateChangeListenerRecords.get(i);
546                if (record.listener.asBinder().equals(listener.asBinder())) {
547                    mPrintJobStateChangeListenerRecords.remove(i);
548                    break;
549                }
550            }
551            if (mPrintJobStateChangeListenerRecords.isEmpty()) {
552                mPrintJobStateChangeListenerRecords = null;
553            }
554        }
555    }
556
557    public void addPrintServicesChangeListener(@NonNull IPrintServicesChangeListener listener)
558            throws RemoteException {
559        synchronized (mLock) {
560            throwIfDestroyedLocked();
561            if (mPrintServicesChangeListenerRecords == null) {
562                mPrintServicesChangeListenerRecords = new ArrayList<>();
563            }
564            mPrintServicesChangeListenerRecords.add(
565                    new PrintServicesChangeListenerRecord(listener) {
566                        @Override
567                        public void onBinderDied() {
568                            mPrintServicesChangeListenerRecords.remove(this);
569                        }
570                    });
571        }
572    }
573
574    public void removePrintServicesChangeListener(@NonNull IPrintServicesChangeListener listener) {
575        synchronized (mLock) {
576            throwIfDestroyedLocked();
577            if (mPrintServicesChangeListenerRecords == null) {
578                return;
579            }
580            final int recordCount = mPrintServicesChangeListenerRecords.size();
581            for (int i = 0; i < recordCount; i++) {
582                PrintServicesChangeListenerRecord record =
583                        mPrintServicesChangeListenerRecords.get(i);
584                if (record.listener.asBinder().equals(listener.asBinder())) {
585                    mPrintServicesChangeListenerRecords.remove(i);
586                    break;
587                }
588            }
589            if (mPrintServicesChangeListenerRecords.isEmpty()) {
590                mPrintServicesChangeListenerRecords = null;
591            }
592        }
593    }
594
595    @Override
596    public void onPrintJobStateChanged(PrintJobInfo printJob) {
597        mPrintJobForAppCache.onPrintJobStateChanged(printJob);
598        mHandler.obtainMessage(UserStateHandler.MSG_DISPATCH_PRINT_JOB_STATE_CHANGED,
599                printJob.getAppId(), 0, printJob.getId()).sendToTarget();
600    }
601
602    public void onPrintServicesChanged() {
603        mHandler.obtainMessage(UserStateHandler.MSG_DISPATCH_PRINT_SERVICES_CHANGED).sendToTarget();
604    }
605
606    @Override
607    public void onPrintersAdded(List<PrinterInfo> printers) {
608        synchronized (mLock) {
609            throwIfDestroyedLocked();
610            // No services - nothing to do.
611            if (mActiveServices.isEmpty()) {
612                return;
613            }
614            // No session - nothing to do.
615            if (mPrinterDiscoverySession == null) {
616                return;
617            }
618            mPrinterDiscoverySession.onPrintersAddedLocked(printers);
619        }
620    }
621
622    @Override
623    public void onPrintersRemoved(List<PrinterId> printerIds) {
624        synchronized (mLock) {
625            throwIfDestroyedLocked();
626            // No services - nothing to do.
627            if (mActiveServices.isEmpty()) {
628                return;
629            }
630            // No session - nothing to do.
631            if (mPrinterDiscoverySession == null) {
632                return;
633            }
634            mPrinterDiscoverySession.onPrintersRemovedLocked(printerIds);
635        }
636    }
637
638    @Override
639    public void onCustomPrinterIconLoaded(PrinterId printerId, Icon icon) {
640        synchronized (mLock) {
641            throwIfDestroyedLocked();
642
643            // No session - nothing to do.
644            if (mPrinterDiscoverySession == null) {
645                return;
646            }
647            mSpooler.onCustomPrinterIconLoaded(printerId, icon);
648            mPrinterDiscoverySession.onCustomPrinterIconLoadedLocked(printerId);
649        }
650    }
651
652    @Override
653    public void onServiceDied(RemotePrintService service) {
654        synchronized (mLock) {
655            throwIfDestroyedLocked();
656            // No services - nothing to do.
657            if (mActiveServices.isEmpty()) {
658                return;
659            }
660            // Fail all print jobs.
661            failActivePrintJobsForService(service.getComponentName());
662            service.onAllPrintJobsHandled();
663            // No session - nothing to do.
664            if (mPrinterDiscoverySession == null) {
665                return;
666            }
667            mPrinterDiscoverySession.onServiceDiedLocked(service);
668        }
669    }
670
671    public void updateIfNeededLocked() {
672        throwIfDestroyedLocked();
673        if (readConfigurationLocked()) {
674            onConfigurationChangedLocked();
675        }
676    }
677
678    public void destroyLocked() {
679        throwIfDestroyedLocked();
680        mSpooler.destroy();
681        for (RemotePrintService service : mActiveServices.values()) {
682            service.destroy();
683        }
684        mActiveServices.clear();
685        mInstalledServices.clear();
686        mDisabledServices.clear();
687        if (mPrinterDiscoverySession != null) {
688            mPrinterDiscoverySession.destroyLocked();
689            mPrinterDiscoverySession = null;
690        }
691        mDestroyed = true;
692    }
693
694    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String prefix) {
695        pw.append(prefix).append("user state ").append(String.valueOf(mUserId)).append(":");
696        pw.println();
697
698        String tab = "  ";
699
700        pw.append(prefix).append(tab).append("installed services:").println();
701        final int installedServiceCount = mInstalledServices.size();
702        for (int i = 0; i < installedServiceCount; i++) {
703            PrintServiceInfo installedService = mInstalledServices.get(i);
704            String installedServicePrefix = prefix + tab + tab;
705            pw.append(installedServicePrefix).append("service:").println();
706            ResolveInfo resolveInfo = installedService.getResolveInfo();
707            ComponentName componentName = new ComponentName(
708                    resolveInfo.serviceInfo.packageName,
709                    resolveInfo.serviceInfo.name);
710            pw.append(installedServicePrefix).append(tab).append("componentName=")
711                    .append(componentName.flattenToString()).println();
712            pw.append(installedServicePrefix).append(tab).append("settingsActivity=")
713                    .append(installedService.getSettingsActivityName()).println();
714            pw.append(installedServicePrefix).append(tab).append("addPrintersActivity=")
715                    .append(installedService.getAddPrintersActivityName()).println();
716            pw.append(installedServicePrefix).append(tab).append("avancedOptionsActivity=")
717                   .append(installedService.getAdvancedOptionsActivityName()).println();
718        }
719
720        pw.append(prefix).append(tab).append("disabled services:").println();
721        for (ComponentName disabledService : mDisabledServices) {
722            String disabledServicePrefix = prefix + tab + tab;
723            pw.append(disabledServicePrefix).append("service:").println();
724            pw.append(disabledServicePrefix).append(tab).append("componentName=")
725                    .append(disabledService.flattenToString());
726            pw.println();
727        }
728
729        pw.append(prefix).append(tab).append("active services:").println();
730        final int activeServiceCount = mActiveServices.size();
731        for (int i = 0; i < activeServiceCount; i++) {
732            RemotePrintService activeService = mActiveServices.valueAt(i);
733            activeService.dump(pw, prefix + tab + tab);
734            pw.println();
735        }
736
737        pw.append(prefix).append(tab).append("cached print jobs:").println();
738        mPrintJobForAppCache.dump(pw, prefix + tab + tab);
739
740        pw.append(prefix).append(tab).append("discovery mediator:").println();
741        if (mPrinterDiscoverySession != null) {
742            mPrinterDiscoverySession.dump(pw, prefix + tab + tab);
743        }
744
745        pw.append(prefix).append(tab).append("print spooler:").println();
746        mSpooler.dump(fd, pw, prefix + tab + tab);
747        pw.println();
748    }
749
750    private boolean readConfigurationLocked() {
751        boolean somethingChanged = false;
752        somethingChanged |= readInstalledPrintServicesLocked();
753        somethingChanged |= readDisabledPrintServicesLocked();
754        return somethingChanged;
755    }
756
757    private boolean readInstalledPrintServicesLocked() {
758        Set<PrintServiceInfo> tempPrintServices = new HashSet<PrintServiceInfo>();
759
760        List<ResolveInfo> installedServices = mContext.getPackageManager()
761                .queryIntentServicesAsUser(mQueryIntent,
762                        GET_SERVICES | GET_META_DATA | MATCH_DEBUG_TRIAGED_MISSING, mUserId);
763
764        final int installedCount = installedServices.size();
765        for (int i = 0, count = installedCount; i < count; i++) {
766            ResolveInfo installedService = installedServices.get(i);
767            if (!android.Manifest.permission.BIND_PRINT_SERVICE.equals(
768                    installedService.serviceInfo.permission)) {
769                ComponentName serviceName = new ComponentName(
770                        installedService.serviceInfo.packageName,
771                        installedService.serviceInfo.name);
772                Slog.w(LOG_TAG, "Skipping print service "
773                        + serviceName.flattenToShortString()
774                        + " since it does not require permission "
775                        + android.Manifest.permission.BIND_PRINT_SERVICE);
776                continue;
777            }
778            tempPrintServices.add(PrintServiceInfo.create(installedService, mContext));
779        }
780
781        boolean someServiceChanged = false;
782
783        if (tempPrintServices.size() != mInstalledServices.size()) {
784            someServiceChanged = true;
785        } else {
786            for (PrintServiceInfo newService: tempPrintServices) {
787                final int oldServiceIndex = mInstalledServices.indexOf(newService);
788                if (oldServiceIndex < 0) {
789                    someServiceChanged = true;
790                    break;
791                }
792                // PrintServiceInfo#equals compares only the id not all members,
793                // so we are also comparing the members coming from meta-data.
794                PrintServiceInfo oldService = mInstalledServices.get(oldServiceIndex);
795                if (!TextUtils.equals(oldService.getAddPrintersActivityName(),
796                            newService.getAddPrintersActivityName())
797                        || !TextUtils.equals(oldService.getAdvancedOptionsActivityName(),
798                                newService.getAdvancedOptionsActivityName())
799                        || !TextUtils.equals(oldService.getSettingsActivityName(),
800                                newService.getSettingsActivityName())) {
801                    someServiceChanged = true;
802                    break;
803                }
804            }
805        }
806
807        if (someServiceChanged) {
808            mInstalledServices.clear();
809            mInstalledServices.addAll(tempPrintServices);
810            return true;
811        }
812
813        return false;
814    }
815
816    /**
817     * Update persistent state from a previous version of Android.
818     */
819    private void upgradePersistentStateIfNeeded() {
820        String enabledSettingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
821                Settings.Secure.ENABLED_PRINT_SERVICES, mUserId);
822
823        // Pre N we store the enabled services, in N and later we store the disabled services.
824        // Hence if enabledSettingValue is still set, we need to upgrade.
825        if (enabledSettingValue != null) {
826            Set<ComponentName> enabledServiceNameSet = new HashSet<ComponentName>();
827            readPrintServicesFromSettingLocked(Settings.Secure.ENABLED_PRINT_SERVICES,
828                    enabledServiceNameSet);
829
830            ArraySet<ComponentName> disabledServices = new ArraySet<>();
831            final int numInstalledServices = mInstalledServices.size();
832            for (int i = 0; i < numInstalledServices; i++) {
833                ComponentName serviceName = mInstalledServices.get(i).getComponentName();
834                if (!enabledServiceNameSet.contains(serviceName)) {
835                    disabledServices.add(serviceName);
836                }
837            }
838
839            writeDisabledPrintServicesLocked(disabledServices);
840
841            // We won't needed ENABLED_PRINT_SERVICES anymore, set to null to prevent upgrade to run
842            // again.
843            Settings.Secure.putStringForUser(mContext.getContentResolver(),
844                    Settings.Secure.ENABLED_PRINT_SERVICES, null, mUserId);
845        }
846    }
847
848    /**
849     * Read the set of disabled print services from the secure settings.
850     *
851     * @return true if the state changed.
852     */
853    private boolean readDisabledPrintServicesLocked() {
854        Set<ComponentName> tempDisabledServiceNameSet = new HashSet<ComponentName>();
855        readPrintServicesFromSettingLocked(Settings.Secure.DISABLED_PRINT_SERVICES,
856                tempDisabledServiceNameSet);
857        if (!tempDisabledServiceNameSet.equals(mDisabledServices)) {
858            mDisabledServices.clear();
859            mDisabledServices.addAll(tempDisabledServiceNameSet);
860            return true;
861        }
862        return false;
863    }
864
865    private void readPrintServicesFromSettingLocked(String setting,
866            Set<ComponentName> outServiceNames) {
867        String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
868                setting, mUserId);
869        if (!TextUtils.isEmpty(settingValue)) {
870            TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
871            splitter.setString(settingValue);
872            while (splitter.hasNext()) {
873                String string = splitter.next();
874                if (TextUtils.isEmpty(string)) {
875                    continue;
876                }
877                ComponentName componentName = ComponentName.unflattenFromString(string);
878                if (componentName != null) {
879                    outServiceNames.add(componentName);
880                }
881            }
882        }
883    }
884
885    /**
886     * Persist the disabled print services to the secure settings.
887     */
888    private void writeDisabledPrintServicesLocked(Set<ComponentName> disabledServices) {
889        StringBuilder builder = new StringBuilder();
890        for (ComponentName componentName : disabledServices) {
891            if (builder.length() > 0) {
892                builder.append(COMPONENT_NAME_SEPARATOR);
893            }
894            builder.append(componentName.flattenToShortString());
895        }
896        Settings.Secure.putStringForUser(mContext.getContentResolver(),
897                Settings.Secure.DISABLED_PRINT_SERVICES, builder.toString(), mUserId);
898    }
899
900    /**
901     * Get the {@link ComponentName names} of the installed print services
902     *
903     * @return The names of the installed print services
904     */
905    private ArrayList<ComponentName> getInstalledComponents() {
906        ArrayList<ComponentName> installedComponents = new ArrayList<ComponentName>();
907
908        final int installedCount = mInstalledServices.size();
909        for (int i = 0; i < installedCount; i++) {
910            ResolveInfo resolveInfo = mInstalledServices.get(i).getResolveInfo();
911            ComponentName serviceName = new ComponentName(resolveInfo.serviceInfo.packageName,
912                    resolveInfo.serviceInfo.name);
913
914            installedComponents.add(serviceName);
915        }
916
917        return installedComponents;
918    }
919
920    /**
921     * Prune persistent state if a print service was uninstalled
922     */
923    public void prunePrintServices() {
924        synchronized (mLock) {
925            ArrayList<ComponentName> installedComponents = getInstalledComponents();
926
927            // Remove unnecessary entries from persistent state "disabled services"
928            boolean disabledServicesUninstalled = mDisabledServices.retainAll(installedComponents);
929            if (disabledServicesUninstalled) {
930                writeDisabledPrintServicesLocked(mDisabledServices);
931            }
932
933            // Remove unnecessary entries from persistent state "approved services"
934            mSpooler.pruneApprovedPrintServices(installedComponents);
935        }
936    }
937
938    private void onConfigurationChangedLocked() {
939        ArrayList<ComponentName> installedComponents = getInstalledComponents();
940
941        final int installedCount = installedComponents.size();
942        for (int i = 0; i < installedCount; i++) {
943            ComponentName serviceName = installedComponents.get(i);
944
945            if (!mDisabledServices.contains(serviceName)) {
946                if (!mActiveServices.containsKey(serviceName)) {
947                    RemotePrintService service = new RemotePrintService(
948                            mContext, serviceName, mUserId, mSpooler, this);
949                    addServiceLocked(service);
950                }
951            } else {
952                RemotePrintService service = mActiveServices.remove(serviceName);
953                if (service != null) {
954                    removeServiceLocked(service);
955                }
956            }
957        }
958
959        Iterator<Map.Entry<ComponentName, RemotePrintService>> iterator =
960                mActiveServices.entrySet().iterator();
961        while (iterator.hasNext()) {
962            Map.Entry<ComponentName, RemotePrintService> entry = iterator.next();
963            ComponentName serviceName = entry.getKey();
964            RemotePrintService service = entry.getValue();
965            if (!installedComponents.contains(serviceName)) {
966                removeServiceLocked(service);
967                iterator.remove();
968            }
969        }
970
971        onPrintServicesChanged();
972    }
973
974    private void addServiceLocked(RemotePrintService service) {
975        mActiveServices.put(service.getComponentName(), service);
976        if (mPrinterDiscoverySession != null) {
977            mPrinterDiscoverySession.onServiceAddedLocked(service);
978        }
979    }
980
981    private void removeServiceLocked(RemotePrintService service) {
982        // Fail all print jobs.
983        failActivePrintJobsForService(service.getComponentName());
984        // If discovery is in progress, tear down the service.
985        if (mPrinterDiscoverySession != null) {
986            mPrinterDiscoverySession.onServiceRemovedLocked(service);
987        } else {
988            // Otherwise, just destroy it.
989            service.destroy();
990        }
991    }
992
993    private void failActivePrintJobsForService(final ComponentName serviceName) {
994        // Makes sure all active print jobs are failed since the service
995        // just died. Do this off the main thread since we do to allow
996        // calls into the spooler on the main thread.
997        if (Looper.getMainLooper().isCurrentThread()) {
998            BackgroundThread.getHandler().post(new Runnable() {
999                @Override
1000                public void run() {
1001                    failScheduledPrintJobsForServiceInternal(serviceName);
1002                }
1003            });
1004        } else {
1005            failScheduledPrintJobsForServiceInternal(serviceName);
1006        }
1007    }
1008
1009    private void failScheduledPrintJobsForServiceInternal(ComponentName serviceName) {
1010        List<PrintJobInfo> printJobs = mSpooler.getPrintJobInfos(serviceName,
1011                PrintJobInfo.STATE_ANY_SCHEDULED, PrintManager.APP_ID_ANY);
1012        if (printJobs == null) {
1013            return;
1014        }
1015        final long identity = Binder.clearCallingIdentity();
1016        try {
1017            final int printJobCount = printJobs.size();
1018            for (int i = 0; i < printJobCount; i++) {
1019                PrintJobInfo printJob = printJobs.get(i);
1020                mSpooler.setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED,
1021                        mContext.getString(R.string.reason_service_unavailable));
1022            }
1023        } finally {
1024            Binder.restoreCallingIdentity(identity);
1025        }
1026    }
1027
1028    private void throwIfDestroyedLocked() {
1029        if (mDestroyed) {
1030            throw new IllegalStateException("Cannot interact with a destroyed instance.");
1031        }
1032    }
1033
1034    private void handleDispatchPrintJobStateChanged(PrintJobId printJobId, int appId) {
1035        final List<PrintJobStateChangeListenerRecord> records;
1036        synchronized (mLock) {
1037            if (mPrintJobStateChangeListenerRecords == null) {
1038                return;
1039            }
1040            records = new ArrayList<PrintJobStateChangeListenerRecord>(
1041                    mPrintJobStateChangeListenerRecords);
1042        }
1043        final int recordCount = records.size();
1044        for (int i = 0; i < recordCount; i++) {
1045            PrintJobStateChangeListenerRecord record = records.get(i);
1046            if (record.appId == PrintManager.APP_ID_ANY
1047                    || record.appId == appId)
1048            try {
1049                record.listener.onPrintJobStateChanged(printJobId);
1050            } catch (RemoteException re) {
1051                Log.e(LOG_TAG, "Error notifying for print job state change", re);
1052            }
1053        }
1054    }
1055
1056    private void handleDispatchPrintServicesChanged() {
1057        final List<PrintServicesChangeListenerRecord> records;
1058        synchronized (mLock) {
1059            if (mPrintServicesChangeListenerRecords == null) {
1060                return;
1061            }
1062            records = new ArrayList<>(mPrintServicesChangeListenerRecords);
1063        }
1064        final int recordCount = records.size();
1065        for (int i = 0; i < recordCount; i++) {
1066            PrintServicesChangeListenerRecord record = records.get(i);
1067
1068            try {
1069                record.listener.onPrintServicesChanged();;
1070            } catch (RemoteException re) {
1071                Log.e(LOG_TAG, "Error notifying for print services change", re);
1072            }
1073        }
1074    }
1075
1076    private final class UserStateHandler extends Handler {
1077        public static final int MSG_DISPATCH_PRINT_JOB_STATE_CHANGED = 1;
1078        public static final int MSG_DISPATCH_PRINT_SERVICES_CHANGED = 2;
1079
1080        public UserStateHandler(Looper looper) {
1081            super(looper, null, false);
1082        }
1083
1084        @Override
1085        public void handleMessage(Message message) {
1086            switch (message.what) {
1087                case MSG_DISPATCH_PRINT_JOB_STATE_CHANGED:
1088                    PrintJobId printJobId = (PrintJobId) message.obj;
1089                    final int appId = message.arg1;
1090                    handleDispatchPrintJobStateChanged(printJobId, appId);
1091                    break;
1092                case MSG_DISPATCH_PRINT_SERVICES_CHANGED:
1093                    handleDispatchPrintServicesChanged();
1094                    break;
1095                default:
1096                    // not reached
1097            }
1098        }
1099    }
1100
1101    private abstract class PrintJobStateChangeListenerRecord implements DeathRecipient {
1102        @NonNull final IPrintJobStateChangeListener listener;
1103        final int appId;
1104
1105        public PrintJobStateChangeListenerRecord(@NonNull IPrintJobStateChangeListener listener,
1106                int appId) throws RemoteException {
1107            this.listener = listener;
1108            this.appId = appId;
1109            listener.asBinder().linkToDeath(this, 0);
1110        }
1111
1112        @Override
1113        public void binderDied() {
1114            listener.asBinder().unlinkToDeath(this, 0);
1115            onBinderDied();
1116        }
1117
1118        public abstract void onBinderDied();
1119    }
1120
1121    private abstract class PrintServicesChangeListenerRecord implements DeathRecipient {
1122        @NonNull final IPrintServicesChangeListener listener;
1123
1124        public PrintServicesChangeListenerRecord(@NonNull IPrintServicesChangeListener listener) throws RemoteException {
1125            this.listener = listener;
1126            listener.asBinder().linkToDeath(this, 0);
1127        }
1128
1129        @Override
1130        public void binderDied() {
1131            listener.asBinder().unlinkToDeath(this, 0);
1132            onBinderDied();
1133        }
1134
1135        public abstract void onBinderDied();
1136    }
1137
1138    private class PrinterDiscoverySessionMediator {
1139        private final ArrayMap<PrinterId, PrinterInfo> mPrinters =
1140                new ArrayMap<PrinterId, PrinterInfo>();
1141
1142        private final RemoteCallbackList<IPrinterDiscoveryObserver> mDiscoveryObservers =
1143                new RemoteCallbackList<IPrinterDiscoveryObserver>() {
1144            @Override
1145            public void onCallbackDied(IPrinterDiscoveryObserver observer) {
1146                synchronized (mLock) {
1147                    stopPrinterDiscoveryLocked(observer);
1148                    removeObserverLocked(observer);
1149                }
1150            }
1151        };
1152
1153        private final List<IBinder> mStartedPrinterDiscoveryTokens = new ArrayList<IBinder>();
1154
1155        private final List<PrinterId> mStateTrackedPrinters = new ArrayList<PrinterId>();
1156
1157        private final Handler mSessionHandler;
1158
1159        private boolean mIsDestroyed;
1160
1161        public PrinterDiscoverySessionMediator(Context context) {
1162            mSessionHandler = new SessionHandler(context.getMainLooper());
1163            // Kick off the session creation.
1164            List<RemotePrintService> services = new ArrayList<RemotePrintService>(
1165                    mActiveServices.values());
1166            mSessionHandler.obtainMessage(SessionHandler
1167                    .MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION, services)
1168                    .sendToTarget();
1169        }
1170
1171        public void addObserverLocked(@NonNull IPrinterDiscoveryObserver observer) {
1172            // Add the observer.
1173            mDiscoveryObservers.register(observer);
1174
1175            // Bring the added observer up to speed with the printers.
1176            if (!mPrinters.isEmpty()) {
1177                List<PrinterInfo> printers = new ArrayList<PrinterInfo>(mPrinters.values());
1178                SomeArgs args = SomeArgs.obtain();
1179                args.arg1 = observer;
1180                args.arg2 = printers;
1181                mSessionHandler.obtainMessage(SessionHandler.MSG_PRINTERS_ADDED,
1182                        args).sendToTarget();
1183            }
1184        }
1185
1186        public void removeObserverLocked(@NonNull IPrinterDiscoveryObserver observer) {
1187            // Remove the observer.
1188            mDiscoveryObservers.unregister(observer);
1189            // No one else observing - then kill it.
1190            if (mDiscoveryObservers.getRegisteredCallbackCount() == 0) {
1191                destroyLocked();
1192            }
1193        }
1194
1195        public final void startPrinterDiscoveryLocked(@NonNull IPrinterDiscoveryObserver observer,
1196                @Nullable List<PrinterId> priorityList) {
1197            if (mIsDestroyed) {
1198                Log.w(LOG_TAG, "Not starting dicovery - session destroyed");
1199                return;
1200            }
1201
1202            final boolean discoveryStarted = !mStartedPrinterDiscoveryTokens.isEmpty();
1203
1204            // Remember we got a start request to match with an end.
1205            mStartedPrinterDiscoveryTokens.add(observer.asBinder());
1206
1207            // If printer discovery is ongoing and the start request has a list
1208            // of printer to be checked, then we just request validating them.
1209            if (discoveryStarted && priorityList != null && !priorityList.isEmpty()) {
1210                validatePrinters(priorityList);
1211                return;
1212            }
1213
1214            // The service are already performing discovery - nothing to do.
1215            if (mStartedPrinterDiscoveryTokens.size() > 1) {
1216                return;
1217            }
1218
1219            List<RemotePrintService> services = new ArrayList<RemotePrintService>(
1220                    mActiveServices.values());
1221            SomeArgs args = SomeArgs.obtain();
1222            args.arg1 = services;
1223            args.arg2 = priorityList;
1224            mSessionHandler.obtainMessage(SessionHandler
1225                    .MSG_DISPATCH_START_PRINTER_DISCOVERY, args)
1226                    .sendToTarget();
1227        }
1228
1229        public final void stopPrinterDiscoveryLocked(@NonNull IPrinterDiscoveryObserver observer) {
1230            if (mIsDestroyed) {
1231                Log.w(LOG_TAG, "Not stopping dicovery - session destroyed");
1232                return;
1233            }
1234            // This one did not make an active discovery request - nothing to do.
1235            if (!mStartedPrinterDiscoveryTokens.remove(observer.asBinder())) {
1236                return;
1237            }
1238            // There are other interested observers - do not stop discovery.
1239            if (!mStartedPrinterDiscoveryTokens.isEmpty()) {
1240                return;
1241            }
1242            List<RemotePrintService> services = new ArrayList<RemotePrintService>(
1243                    mActiveServices.values());
1244            mSessionHandler.obtainMessage(SessionHandler
1245                    .MSG_DISPATCH_STOP_PRINTER_DISCOVERY, services)
1246                    .sendToTarget();
1247        }
1248
1249        public void validatePrintersLocked(@NonNull List<PrinterId> printerIds) {
1250            if (mIsDestroyed) {
1251                Log.w(LOG_TAG, "Not validating pritners - session destroyed");
1252                return;
1253            }
1254
1255            List<PrinterId> remainingList = new ArrayList<PrinterId>(printerIds);
1256            while (!remainingList.isEmpty()) {
1257                Iterator<PrinterId> iterator = remainingList.iterator();
1258                // Gather the printers per service and request a validation.
1259                List<PrinterId> updateList = new ArrayList<PrinterId>();
1260                ComponentName serviceName = null;
1261                while (iterator.hasNext()) {
1262                    PrinterId printerId = iterator.next();
1263                    if (printerId != null) {
1264                        if (updateList.isEmpty()) {
1265                            updateList.add(printerId);
1266                            serviceName = printerId.getServiceName();
1267                            iterator.remove();
1268                        } else if (printerId.getServiceName().equals(serviceName)) {
1269                            updateList.add(printerId);
1270                            iterator.remove();
1271                        }
1272                    }
1273                }
1274                // Schedule a notification of the service.
1275                RemotePrintService service = mActiveServices.get(serviceName);
1276                if (service != null) {
1277                    SomeArgs args = SomeArgs.obtain();
1278                    args.arg1 = service;
1279                    args.arg2 = updateList;
1280                    mSessionHandler.obtainMessage(SessionHandler
1281                            .MSG_VALIDATE_PRINTERS, args)
1282                            .sendToTarget();
1283                }
1284            }
1285        }
1286
1287        public final void startPrinterStateTrackingLocked(@NonNull PrinterId printerId) {
1288            if (mIsDestroyed) {
1289                Log.w(LOG_TAG, "Not starting printer state tracking - session destroyed");
1290                return;
1291            }
1292            // If printer discovery is not started - nothing to do.
1293            if (mStartedPrinterDiscoveryTokens.isEmpty()) {
1294                return;
1295            }
1296            final boolean containedPrinterId = mStateTrackedPrinters.contains(printerId);
1297            // Keep track of the number of requests to track this one.
1298            mStateTrackedPrinters.add(printerId);
1299            // If we were tracking this printer - nothing to do.
1300            if (containedPrinterId) {
1301                return;
1302            }
1303            // No service - nothing to do.
1304            RemotePrintService service = mActiveServices.get(printerId.getServiceName());
1305            if (service == null) {
1306                return;
1307            }
1308            // Ask the service to start tracking.
1309            SomeArgs args = SomeArgs.obtain();
1310            args.arg1 = service;
1311            args.arg2 = printerId;
1312            mSessionHandler.obtainMessage(SessionHandler
1313                    .MSG_START_PRINTER_STATE_TRACKING, args)
1314                    .sendToTarget();
1315        }
1316
1317        public final void stopPrinterStateTrackingLocked(PrinterId printerId) {
1318            if (mIsDestroyed) {
1319                Log.w(LOG_TAG, "Not stopping printer state tracking - session destroyed");
1320                return;
1321            }
1322            // If printer discovery is not started - nothing to do.
1323            if (mStartedPrinterDiscoveryTokens.isEmpty()) {
1324                return;
1325            }
1326            // If we did not track this printer - nothing to do.
1327            if (!mStateTrackedPrinters.remove(printerId)) {
1328                return;
1329            }
1330            // No service - nothing to do.
1331            RemotePrintService service = mActiveServices.get(printerId.getServiceName());
1332            if (service == null) {
1333                return;
1334            }
1335            // Ask the service to start tracking.
1336            SomeArgs args = SomeArgs.obtain();
1337            args.arg1 = service;
1338            args.arg2 = printerId;
1339            mSessionHandler.obtainMessage(SessionHandler
1340                    .MSG_STOP_PRINTER_STATE_TRACKING, args)
1341                    .sendToTarget();
1342        }
1343
1344        public void onDestroyed() {
1345            /* do nothing */
1346        }
1347
1348        public void destroyLocked() {
1349            if (mIsDestroyed) {
1350                Log.w(LOG_TAG, "Not destroying - session destroyed");
1351                return;
1352            }
1353            mIsDestroyed = true;
1354            // Make sure printer tracking is stopped.
1355            final int printerCount = mStateTrackedPrinters.size();
1356            for (int i = 0; i < printerCount; i++) {
1357                PrinterId printerId = mStateTrackedPrinters.get(i);
1358                stopPrinterStateTracking(printerId);
1359            }
1360            // Make sure discovery is stopped.
1361            final int observerCount = mStartedPrinterDiscoveryTokens.size();
1362            for (int i = 0; i < observerCount; i++) {
1363                IBinder token = mStartedPrinterDiscoveryTokens.get(i);
1364                stopPrinterDiscoveryLocked(IPrinterDiscoveryObserver.Stub.asInterface(token));
1365            }
1366            // Tell the services we are done.
1367            List<RemotePrintService> services = new ArrayList<RemotePrintService>(
1368                    mActiveServices.values());
1369            mSessionHandler.obtainMessage(SessionHandler
1370                    .MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION, services)
1371                    .sendToTarget();
1372        }
1373
1374        public void onPrintersAddedLocked(List<PrinterInfo> printers) {
1375            if (DEBUG) {
1376                Log.i(LOG_TAG, "onPrintersAddedLocked()");
1377            }
1378            if (mIsDestroyed) {
1379                Log.w(LOG_TAG, "Not adding printers - session destroyed");
1380                return;
1381            }
1382            List<PrinterInfo> addedPrinters = null;
1383            final int addedPrinterCount = printers.size();
1384            for (int i = 0; i < addedPrinterCount; i++) {
1385                PrinterInfo printer = printers.get(i);
1386                PrinterInfo oldPrinter = mPrinters.put(printer.getId(), printer);
1387                if (oldPrinter == null || !oldPrinter.equals(printer)) {
1388                    if (addedPrinters == null) {
1389                        addedPrinters = new ArrayList<PrinterInfo>();
1390                    }
1391                    addedPrinters.add(printer);
1392                }
1393            }
1394            if (addedPrinters != null) {
1395                mSessionHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_ADDED,
1396                        addedPrinters).sendToTarget();
1397            }
1398        }
1399
1400        public void onPrintersRemovedLocked(List<PrinterId> printerIds) {
1401            if (DEBUG) {
1402                Log.i(LOG_TAG, "onPrintersRemovedLocked()");
1403            }
1404            if (mIsDestroyed) {
1405                Log.w(LOG_TAG, "Not removing printers - session destroyed");
1406                return;
1407            }
1408            List<PrinterId> removedPrinterIds = null;
1409            final int removedPrinterCount = printerIds.size();
1410            for (int i = 0; i < removedPrinterCount; i++) {
1411                PrinterId removedPrinterId = printerIds.get(i);
1412                if (mPrinters.remove(removedPrinterId) != null) {
1413                    if (removedPrinterIds == null) {
1414                        removedPrinterIds = new ArrayList<PrinterId>();
1415                    }
1416                    removedPrinterIds.add(removedPrinterId);
1417                }
1418            }
1419            if (removedPrinterIds != null) {
1420                mSessionHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_REMOVED,
1421                        removedPrinterIds).sendToTarget();
1422            }
1423        }
1424
1425        public void onServiceRemovedLocked(RemotePrintService service) {
1426            if (mIsDestroyed) {
1427                Log.w(LOG_TAG, "Not updating removed service - session destroyed");
1428                return;
1429            }
1430            // Remove the reported and tracked printers for that service.
1431            ComponentName serviceName = service.getComponentName();
1432            removePrintersForServiceLocked(serviceName);
1433            service.destroy();
1434        }
1435
1436        /**
1437         * Handle that a custom icon for a printer was loaded.
1438         *
1439         * This increments the icon generation and adds the printer again which triggers an update
1440         * in all users of the currently known printers.
1441         *
1442         * @param printerId the id of the printer the icon belongs to
1443         * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon()
1444         */
1445        public void onCustomPrinterIconLoadedLocked(PrinterId printerId) {
1446            if (DEBUG) {
1447                Log.i(LOG_TAG, "onCustomPrinterIconLoadedLocked()");
1448            }
1449            if (mIsDestroyed) {
1450                Log.w(LOG_TAG, "Not updating printer - session destroyed");
1451                return;
1452            }
1453
1454            PrinterInfo printer = mPrinters.get(printerId);
1455            if (printer != null) {
1456                PrinterInfo newPrinter = (new PrinterInfo.Builder(printer))
1457                        .incCustomPrinterIconGen().build();
1458                mPrinters.put(printerId, newPrinter);
1459
1460                ArrayList<PrinterInfo> addedPrinters = new ArrayList<>(1);
1461                addedPrinters.add(newPrinter);
1462                mSessionHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_ADDED,
1463                        addedPrinters).sendToTarget();
1464            }
1465        }
1466
1467        public void onServiceDiedLocked(RemotePrintService service) {
1468            // Remove the reported by that service.
1469            removePrintersForServiceLocked(service.getComponentName());
1470        }
1471
1472        public void onServiceAddedLocked(RemotePrintService service) {
1473            if (mIsDestroyed) {
1474                Log.w(LOG_TAG, "Not updating added service - session destroyed");
1475                return;
1476            }
1477            // Tell the service to create a session.
1478            mSessionHandler.obtainMessage(
1479                    SessionHandler.MSG_CREATE_PRINTER_DISCOVERY_SESSION,
1480                    service).sendToTarget();
1481            // Start printer discovery if necessary.
1482            if (!mStartedPrinterDiscoveryTokens.isEmpty()) {
1483                mSessionHandler.obtainMessage(
1484                        SessionHandler.MSG_START_PRINTER_DISCOVERY,
1485                        service).sendToTarget();
1486            }
1487            // Start tracking printers if necessary
1488            final int trackedPrinterCount = mStateTrackedPrinters.size();
1489            for (int i = 0; i < trackedPrinterCount; i++) {
1490                PrinterId printerId = mStateTrackedPrinters.get(i);
1491                if (printerId.getServiceName().equals(service.getComponentName())) {
1492                    SomeArgs args = SomeArgs.obtain();
1493                    args.arg1 = service;
1494                    args.arg2 = printerId;
1495                    mSessionHandler.obtainMessage(SessionHandler
1496                            .MSG_START_PRINTER_STATE_TRACKING, args)
1497                            .sendToTarget();
1498                }
1499            }
1500        }
1501
1502        public void dump(PrintWriter pw, String prefix) {
1503            pw.append(prefix).append("destroyed=")
1504                    .append(String.valueOf(mDestroyed)).println();
1505
1506            pw.append(prefix).append("printDiscoveryInProgress=")
1507                    .append(String.valueOf(!mStartedPrinterDiscoveryTokens.isEmpty())).println();
1508
1509            String tab = "  ";
1510
1511            pw.append(prefix).append(tab).append("printer discovery observers:").println();
1512            final int observerCount = mDiscoveryObservers.beginBroadcast();
1513            for (int i = 0; i < observerCount; i++) {
1514                IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i);
1515                pw.append(prefix).append(prefix).append(observer.toString());
1516                pw.println();
1517            }
1518            mDiscoveryObservers.finishBroadcast();
1519
1520            pw.append(prefix).append(tab).append("start discovery requests:").println();
1521            final int tokenCount = this.mStartedPrinterDiscoveryTokens.size();
1522            for (int i = 0; i < tokenCount; i++) {
1523                IBinder token = mStartedPrinterDiscoveryTokens.get(i);
1524                pw.append(prefix).append(tab).append(tab).append(token.toString()).println();
1525            }
1526
1527            pw.append(prefix).append(tab).append("tracked printer requests:").println();
1528            final int trackedPrinters = mStateTrackedPrinters.size();
1529            for (int i = 0; i < trackedPrinters; i++) {
1530                PrinterId printer = mStateTrackedPrinters.get(i);
1531                pw.append(prefix).append(tab).append(tab).append(printer.toString()).println();
1532            }
1533
1534            pw.append(prefix).append(tab).append("printers:").println();
1535            final int pritnerCount = mPrinters.size();
1536            for (int i = 0; i < pritnerCount; i++) {
1537                PrinterInfo printer = mPrinters.valueAt(i);
1538                pw.append(prefix).append(tab).append(tab).append(
1539                        printer.toString()).println();
1540            }
1541        }
1542
1543        private void removePrintersForServiceLocked(ComponentName serviceName) {
1544            // No printers - nothing to do.
1545            if (mPrinters.isEmpty()) {
1546                return;
1547            }
1548            // Remove the printers for that service.
1549            List<PrinterId> removedPrinterIds = null;
1550            final int printerCount = mPrinters.size();
1551            for (int i = 0; i < printerCount; i++) {
1552                PrinterId printerId = mPrinters.keyAt(i);
1553                if (printerId.getServiceName().equals(serviceName)) {
1554                    if (removedPrinterIds == null) {
1555                        removedPrinterIds = new ArrayList<PrinterId>();
1556                    }
1557                    removedPrinterIds.add(printerId);
1558                }
1559            }
1560            if (removedPrinterIds != null) {
1561                final int removedPrinterCount = removedPrinterIds.size();
1562                for (int i = 0; i < removedPrinterCount; i++) {
1563                    mPrinters.remove(removedPrinterIds.get(i));
1564                }
1565                mSessionHandler.obtainMessage(
1566                        SessionHandler.MSG_DISPATCH_PRINTERS_REMOVED,
1567                        removedPrinterIds).sendToTarget();
1568            }
1569        }
1570
1571        private void handleDispatchPrintersAdded(List<PrinterInfo> addedPrinters) {
1572            final int observerCount = mDiscoveryObservers.beginBroadcast();
1573            for (int i = 0; i < observerCount; i++) {
1574                IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i);
1575                handlePrintersAdded(observer, addedPrinters);
1576            }
1577            mDiscoveryObservers.finishBroadcast();
1578        }
1579
1580        private void handleDispatchPrintersRemoved(List<PrinterId> removedPrinterIds) {
1581            final int observerCount = mDiscoveryObservers.beginBroadcast();
1582            for (int i = 0; i < observerCount; i++) {
1583                IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i);
1584                handlePrintersRemoved(observer, removedPrinterIds);
1585            }
1586            mDiscoveryObservers.finishBroadcast();
1587        }
1588
1589        private void handleDispatchCreatePrinterDiscoverySession(
1590                List<RemotePrintService> services) {
1591            final int serviceCount = services.size();
1592            for (int i = 0; i < serviceCount; i++) {
1593                RemotePrintService service = services.get(i);
1594                service.createPrinterDiscoverySession();
1595            }
1596        }
1597
1598        private void handleDispatchDestroyPrinterDiscoverySession(
1599                List<RemotePrintService> services) {
1600            final int serviceCount = services.size();
1601            for (int i = 0; i < serviceCount; i++) {
1602                RemotePrintService service = services.get(i);
1603                service.destroyPrinterDiscoverySession();
1604            }
1605            onDestroyed();
1606        }
1607
1608        private void handleDispatchStartPrinterDiscovery(
1609                List<RemotePrintService> services, List<PrinterId> printerIds) {
1610            final int serviceCount = services.size();
1611            for (int i = 0; i < serviceCount; i++) {
1612                RemotePrintService service = services.get(i);
1613                service.startPrinterDiscovery(printerIds);
1614            }
1615        }
1616
1617        private void handleDispatchStopPrinterDiscovery(List<RemotePrintService> services) {
1618            final int serviceCount = services.size();
1619            for (int i = 0; i < serviceCount; i++) {
1620                RemotePrintService service = services.get(i);
1621                service.stopPrinterDiscovery();
1622            }
1623        }
1624
1625        private void handleValidatePrinters(RemotePrintService service,
1626                List<PrinterId> printerIds) {
1627            service.validatePrinters(printerIds);
1628        }
1629
1630        private void handleStartPrinterStateTracking(@NonNull RemotePrintService service,
1631                @NonNull PrinterId printerId) {
1632            service.startPrinterStateTracking(printerId);
1633        }
1634
1635        private void handleStopPrinterStateTracking(RemotePrintService service,
1636                PrinterId printerId) {
1637            service.stopPrinterStateTracking(printerId);
1638        }
1639
1640        private void handlePrintersAdded(IPrinterDiscoveryObserver observer,
1641            List<PrinterInfo> printers) {
1642            try {
1643                observer.onPrintersAdded(new ParceledListSlice<PrinterInfo>(printers));
1644            } catch (RemoteException re) {
1645                Log.e(LOG_TAG, "Error sending added printers", re);
1646            }
1647        }
1648
1649        private void handlePrintersRemoved(IPrinterDiscoveryObserver observer,
1650            List<PrinterId> printerIds) {
1651            try {
1652                observer.onPrintersRemoved(new ParceledListSlice<PrinterId>(printerIds));
1653            } catch (RemoteException re) {
1654                Log.e(LOG_TAG, "Error sending removed printers", re);
1655            }
1656        }
1657
1658        private final class SessionHandler extends Handler {
1659            public static final int MSG_PRINTERS_ADDED = 1;
1660            public static final int MSG_PRINTERS_REMOVED = 2;
1661            public static final int MSG_DISPATCH_PRINTERS_ADDED = 3;
1662            public static final int MSG_DISPATCH_PRINTERS_REMOVED = 4;
1663
1664            public static final int MSG_CREATE_PRINTER_DISCOVERY_SESSION = 5;
1665            public static final int MSG_DESTROY_PRINTER_DISCOVERY_SESSION = 6;
1666            public static final int MSG_START_PRINTER_DISCOVERY = 7;
1667            public static final int MSG_STOP_PRINTER_DISCOVERY = 8;
1668            public static final int MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION = 9;
1669            public static final int MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION = 10;
1670            public static final int MSG_DISPATCH_START_PRINTER_DISCOVERY = 11;
1671            public static final int MSG_DISPATCH_STOP_PRINTER_DISCOVERY = 12;
1672            public static final int MSG_VALIDATE_PRINTERS = 13;
1673            public static final int MSG_START_PRINTER_STATE_TRACKING = 14;
1674            public static final int MSG_STOP_PRINTER_STATE_TRACKING = 15;
1675            public static final int MSG_DESTROY_SERVICE = 16;
1676
1677            SessionHandler(Looper looper) {
1678                super(looper, null, false);
1679            }
1680
1681            @Override
1682            @SuppressWarnings("unchecked")
1683            public void handleMessage(Message message) {
1684                switch (message.what) {
1685                    case MSG_PRINTERS_ADDED: {
1686                        SomeArgs args = (SomeArgs) message.obj;
1687                        IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) args.arg1;
1688                        List<PrinterInfo> addedPrinters = (List<PrinterInfo>) args.arg2;
1689                        args.recycle();
1690                        handlePrintersAdded(observer, addedPrinters);
1691                    } break;
1692
1693                    case MSG_PRINTERS_REMOVED: {
1694                        SomeArgs args = (SomeArgs) message.obj;
1695                        IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) args.arg1;
1696                        List<PrinterId> removedPrinterIds = (List<PrinterId>) args.arg2;
1697                        args.recycle();
1698                        handlePrintersRemoved(observer, removedPrinterIds);
1699                    }
1700
1701                    case MSG_DISPATCH_PRINTERS_ADDED: {
1702                        List<PrinterInfo> addedPrinters = (List<PrinterInfo>) message.obj;
1703                        handleDispatchPrintersAdded(addedPrinters);
1704                    } break;
1705
1706                    case MSG_DISPATCH_PRINTERS_REMOVED: {
1707                        List<PrinterId> removedPrinterIds = (List<PrinterId>) message.obj;
1708                        handleDispatchPrintersRemoved(removedPrinterIds);
1709                    } break;
1710
1711                    case MSG_CREATE_PRINTER_DISCOVERY_SESSION: {
1712                        RemotePrintService service = (RemotePrintService) message.obj;
1713                        service.createPrinterDiscoverySession();
1714                    } break;
1715
1716                    case MSG_DESTROY_PRINTER_DISCOVERY_SESSION: {
1717                        RemotePrintService service = (RemotePrintService) message.obj;
1718                        service.destroyPrinterDiscoverySession();
1719                    } break;
1720
1721                    case MSG_START_PRINTER_DISCOVERY: {
1722                        RemotePrintService service = (RemotePrintService) message.obj;
1723                        service.startPrinterDiscovery(null);
1724                    } break;
1725
1726                    case MSG_STOP_PRINTER_DISCOVERY: {
1727                        RemotePrintService service = (RemotePrintService) message.obj;
1728                        service.stopPrinterDiscovery();
1729                    } break;
1730
1731                    case MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION: {
1732                        List<RemotePrintService> services = (List<RemotePrintService>) message.obj;
1733                        handleDispatchCreatePrinterDiscoverySession(services);
1734                    } break;
1735
1736                    case MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION: {
1737                        List<RemotePrintService> services = (List<RemotePrintService>) message.obj;
1738                        handleDispatchDestroyPrinterDiscoverySession(services);
1739                    } break;
1740
1741                    case MSG_DISPATCH_START_PRINTER_DISCOVERY: {
1742                        SomeArgs args = (SomeArgs) message.obj;
1743                        List<RemotePrintService> services = (List<RemotePrintService>) args.arg1;
1744                        List<PrinterId> printerIds = (List<PrinterId>) args.arg2;
1745                        args.recycle();
1746                        handleDispatchStartPrinterDiscovery(services, printerIds);
1747                    } break;
1748
1749                    case MSG_DISPATCH_STOP_PRINTER_DISCOVERY: {
1750                        List<RemotePrintService> services = (List<RemotePrintService>) message.obj;
1751                        handleDispatchStopPrinterDiscovery(services);
1752                    } break;
1753
1754                    case MSG_VALIDATE_PRINTERS: {
1755                        SomeArgs args = (SomeArgs) message.obj;
1756                        RemotePrintService service = (RemotePrintService) args.arg1;
1757                        List<PrinterId> printerIds = (List<PrinterId>) args.arg2;
1758                        args.recycle();
1759                        handleValidatePrinters(service, printerIds);
1760                    } break;
1761
1762                    case MSG_START_PRINTER_STATE_TRACKING: {
1763                        SomeArgs args = (SomeArgs) message.obj;
1764                        RemotePrintService service = (RemotePrintService) args.arg1;
1765                        PrinterId printerId = (PrinterId) args.arg2;
1766                        args.recycle();
1767                        handleStartPrinterStateTracking(service, printerId);
1768                    } break;
1769
1770                    case MSG_STOP_PRINTER_STATE_TRACKING: {
1771                        SomeArgs args = (SomeArgs) message.obj;
1772                        RemotePrintService service = (RemotePrintService) args.arg1;
1773                        PrinterId printerId = (PrinterId) args.arg2;
1774                        args.recycle();
1775                        handleStopPrinterStateTracking(service, printerId);
1776                    } break;
1777
1778                    case MSG_DESTROY_SERVICE: {
1779                        RemotePrintService service = (RemotePrintService) message.obj;
1780                        service.destroy();
1781                    } break;
1782                }
1783            }
1784        }
1785    }
1786
1787    private final class PrintJobForAppCache {
1788        private final SparseArray<List<PrintJobInfo>> mPrintJobsForRunningApp =
1789                new SparseArray<List<PrintJobInfo>>();
1790
1791        public boolean onPrintJobCreated(final IBinder creator, final int appId,
1792                PrintJobInfo printJob) {
1793            try {
1794                creator.linkToDeath(new DeathRecipient() {
1795                    @Override
1796                    public void binderDied() {
1797                        creator.unlinkToDeath(this, 0);
1798                        synchronized (mLock) {
1799                            mPrintJobsForRunningApp.remove(appId);
1800                        }
1801                    }
1802                }, 0);
1803            } catch (RemoteException re) {
1804                /* The process is already dead - we just failed. */
1805                return false;
1806            }
1807            synchronized (mLock) {
1808                List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get(appId);
1809                if (printJobsForApp == null) {
1810                    printJobsForApp = new ArrayList<PrintJobInfo>();
1811                    mPrintJobsForRunningApp.put(appId, printJobsForApp);
1812                }
1813                printJobsForApp.add(printJob);
1814            }
1815            return true;
1816        }
1817
1818        public void onPrintJobStateChanged(PrintJobInfo printJob) {
1819            synchronized (mLock) {
1820                List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get(
1821                        printJob.getAppId());
1822                if (printJobsForApp == null) {
1823                    return;
1824                }
1825                final int printJobCount = printJobsForApp.size();
1826                for (int i = 0; i < printJobCount; i++) {
1827                    PrintJobInfo oldPrintJob = printJobsForApp.get(i);
1828                    if (oldPrintJob.getId().equals(printJob.getId())) {
1829                        printJobsForApp.set(i, printJob);
1830                    }
1831                }
1832            }
1833        }
1834
1835        public PrintJobInfo getPrintJob(PrintJobId printJobId, int appId) {
1836            synchronized (mLock) {
1837                List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get(appId);
1838                if (printJobsForApp == null) {
1839                    return null;
1840                }
1841                final int printJobCount = printJobsForApp.size();
1842                for (int i = 0; i < printJobCount; i++) {
1843                    PrintJobInfo printJob = printJobsForApp.get(i);
1844                    if (printJob.getId().equals(printJobId)) {
1845                        return printJob;
1846                    }
1847                }
1848            }
1849            return null;
1850        }
1851
1852        public List<PrintJobInfo> getPrintJobs(int appId) {
1853            synchronized (mLock) {
1854                List<PrintJobInfo> printJobs = null;
1855                if (appId == PrintManager.APP_ID_ANY) {
1856                    final int bucketCount = mPrintJobsForRunningApp.size();
1857                    for (int i = 0; i < bucketCount; i++) {
1858                        List<PrintJobInfo> bucket = mPrintJobsForRunningApp.valueAt(i);
1859                        if (printJobs == null) {
1860                            printJobs = new ArrayList<PrintJobInfo>();
1861                        }
1862                        printJobs.addAll(bucket);
1863                    }
1864                } else {
1865                    List<PrintJobInfo> bucket = mPrintJobsForRunningApp.get(appId);
1866                    if (bucket != null) {
1867                        if (printJobs == null) {
1868                            printJobs = new ArrayList<PrintJobInfo>();
1869                        }
1870                        printJobs.addAll(bucket);
1871                    }
1872                }
1873                if (printJobs != null) {
1874                    return printJobs;
1875                }
1876                return Collections.emptyList();
1877            }
1878        }
1879
1880        public void dump(PrintWriter pw, String prefix) {
1881            synchronized (mLock) {
1882                String tab = "  ";
1883                final int bucketCount = mPrintJobsForRunningApp.size();
1884                for (int i = 0; i < bucketCount; i++) {
1885                    final int appId = mPrintJobsForRunningApp.keyAt(i);
1886                    pw.append(prefix).append("appId=" + appId).append(':').println();
1887                    List<PrintJobInfo> bucket = mPrintJobsForRunningApp.valueAt(i);
1888                    final int printJobCount = bucket.size();
1889                    for (int j = 0; j < printJobCount; j++) {
1890                        PrintJobInfo printJob = bucket.get(j);
1891                        pw.append(prefix).append(tab).append(printJob.toString()).println();
1892                    }
1893                }
1894            }
1895        }
1896    }
1897}
1898