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