2 * Copyright (C) 2013 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.server.print;
19 import static android.content.pm.PackageManager.GET_META_DATA;
20 import static android.content.pm.PackageManager.GET_SERVICES;
21 import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
22 import static android.content.pm.PackageManager.MATCH_INSTANT;
24 import static com.android.internal.print.DumpUtils.writePrintJobInfo;
25 import static com.android.internal.print.DumpUtils.writePrinterId;
26 import static com.android.internal.print.DumpUtils.writePrinterInfo;
27 import static com.android.internal.util.dump.DumpUtils.writeComponentName;
28 import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
29 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
31 import android.annotation.NonNull;
32 import android.annotation.Nullable;
33 import android.app.PendingIntent;
34 import android.content.ComponentName;
35 import android.content.Context;
36 import android.content.Intent;
37 import android.content.IntentSender;
38 import android.content.pm.ParceledListSlice;
39 import android.content.pm.ResolveInfo;
40 import android.graphics.drawable.Icon;
41 import android.net.Uri;
42 import android.os.Binder;
43 import android.os.Bundle;
44 import android.os.Handler;
45 import android.os.IBinder;
46 import android.os.IBinder.DeathRecipient;
47 import android.os.IInterface;
48 import android.os.Looper;
49 import android.os.RemoteCallbackList;
50 import android.os.RemoteException;
51 import android.os.UserHandle;
52 import android.print.IPrintDocumentAdapter;
53 import android.print.IPrintJobStateChangeListener;
54 import android.print.IPrintServicesChangeListener;
55 import android.print.IPrinterDiscoveryObserver;
56 import android.print.PrintAttributes;
57 import android.print.PrintJobId;
58 import android.print.PrintJobInfo;
59 import android.print.PrintManager;
60 import android.print.PrinterId;
61 import android.print.PrinterInfo;
62 import android.printservice.PrintServiceInfo;
63 import android.printservice.recommendation.IRecommendationsChangeListener;
64 import android.printservice.recommendation.RecommendationInfo;
65 import android.provider.DocumentsContract;
66 import android.provider.Settings;
67 import android.service.print.CachedPrintJobProto;
68 import android.service.print.InstalledPrintServiceProto;
69 import android.service.print.PrintUserStateProto;
70 import android.service.print.PrinterDiscoverySessionProto;
71 import android.text.TextUtils;
72 import android.text.TextUtils.SimpleStringSplitter;
73 import android.util.ArrayMap;
74 import android.util.ArraySet;
75 import android.util.Log;
76 import android.util.Slog;
77 import android.util.SparseArray;
79 import com.android.internal.R;
80 import com.android.internal.logging.MetricsLogger;
81 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
82 import com.android.internal.os.BackgroundThread;
83 import com.android.internal.util.dump.DualDumpOutputStream;
84 import com.android.internal.util.function.pooled.PooledLambda;
85 import com.android.server.print.RemotePrintService.PrintServiceCallbacks;
86 import com.android.server.print.RemotePrintServiceRecommendationService
87 .RemotePrintServiceRecommendationServiceCallbacks;
88 import com.android.server.print.RemotePrintSpooler.PrintSpoolerCallbacks;
90 import java.util.ArrayList;
91 import java.util.Collections;
92 import java.util.HashSet;
93 import java.util.Iterator;
94 import java.util.List;
97 import java.util.function.IntSupplier;
100 * Represents the print state for a user.
102 final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks,
103 RemotePrintServiceRecommendationServiceCallbacks {
105 private static final String LOG_TAG = "UserState";
107 private static final boolean DEBUG = false;
109 private static final char COMPONENT_NAME_SEPARATOR = ':';
111 private static final int SERVICE_RESTART_DELAY_MILLIS = 500;
113 private final SimpleStringSplitter mStringColonSplitter =
114 new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
116 private final Intent mQueryIntent =
117 new Intent(android.printservice.PrintService.SERVICE_INTERFACE);
119 private final ArrayMap<ComponentName, RemotePrintService> mActiveServices =
120 new ArrayMap<ComponentName, RemotePrintService>();
122 private final List<PrintServiceInfo> mInstalledServices =
123 new ArrayList<PrintServiceInfo>();
125 private final Set<ComponentName> mDisabledServices =
126 new ArraySet<ComponentName>();
128 private final PrintJobForAppCache mPrintJobForAppCache =
129 new PrintJobForAppCache();
131 private final Object mLock;
133 private final Context mContext;
135 private final int mUserId;
137 private final RemotePrintSpooler mSpooler;
139 private PrinterDiscoverySessionMediator mPrinterDiscoverySession;
141 private List<PrintJobStateChangeListenerRecord> mPrintJobStateChangeListenerRecords;
143 private List<ListenerRecord<IPrintServicesChangeListener>> mPrintServicesChangeListenerRecords;
145 private List<ListenerRecord<IRecommendationsChangeListener>>
146 mPrintServiceRecommendationsChangeListenerRecords;
148 private boolean mDestroyed;
150 /** Currently known list of print service recommendations */
151 private List<RecommendationInfo> mPrintServiceRecommendations;
154 * Connection to the service updating the {@link #mPrintServiceRecommendations print service
157 private RemotePrintServiceRecommendationService mPrintServiceRecommendationsService;
160 * Can services from instant apps be bound? (usually disabled, only used by testing)
162 private boolean mIsInstantServiceAllowed;
164 public UserState(Context context, int userId, Object lock, boolean lowPriority) {
168 mSpooler = new RemotePrintSpooler(context, userId, lowPriority, this);
170 synchronized (mLock) {
171 readInstalledPrintServicesLocked();
172 upgradePersistentStateIfNeeded();
173 readDisabledPrintServicesLocked();
176 // Some print services might have gotten installed before the User State came up
177 prunePrintServices();
179 onConfigurationChanged();
182 public void increasePriority() {
183 mSpooler.increasePriority();
187 public void onPrintJobQueued(PrintJobInfo printJob) {
188 final RemotePrintService service;
189 synchronized (mLock) {
190 throwIfDestroyedLocked();
191 ComponentName printServiceName = printJob.getPrinterId().getServiceName();
192 service = mActiveServices.get(printServiceName);
194 if (service != null) {
195 service.onPrintJobQueued(printJob);
197 // The service for the job is no longer enabled, so just
198 // fail the job with the appropriate message.
199 mSpooler.setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED,
200 mContext.getString(R.string.reason_service_unavailable));
205 public void onAllPrintJobsForServiceHandled(ComponentName printService) {
206 final RemotePrintService service;
207 synchronized (mLock) {
208 throwIfDestroyedLocked();
209 service = mActiveServices.get(printService);
211 if (service != null) {
212 service.onAllPrintJobsHandled();
216 public void removeObsoletePrintJobs() {
217 mSpooler.removeObsoletePrintJobs();
220 @SuppressWarnings("deprecation")
221 public Bundle print(@NonNull String printJobName, @NonNull IPrintDocumentAdapter adapter,
222 @Nullable PrintAttributes attributes, @NonNull String packageName, int appId) {
223 // Create print job place holder.
224 final PrintJobInfo printJob = new PrintJobInfo();
225 printJob.setId(new PrintJobId());
226 printJob.setAppId(appId);
227 printJob.setLabel(printJobName);
228 printJob.setAttributes(attributes);
229 printJob.setState(PrintJobInfo.STATE_CREATED);
230 printJob.setCopies(1);
231 printJob.setCreationTime(System.currentTimeMillis());
233 // Track this job so we can forget it when the creator dies.
234 if (!mPrintJobForAppCache.onPrintJobCreated(adapter.asBinder(), appId,
236 // Not adding a print job means the client is dead - done.
240 final long identity = Binder.clearCallingIdentity();
242 Intent intent = new Intent(PrintManager.ACTION_PRINT_DIALOG);
243 intent.setData(Uri.fromParts("printjob", printJob.getId().flattenToString(), null));
244 intent.putExtra(PrintManager.EXTRA_PRINT_DOCUMENT_ADAPTER, adapter.asBinder());
245 intent.putExtra(PrintManager.EXTRA_PRINT_JOB, printJob);
246 intent.putExtra(DocumentsContract.EXTRA_PACKAGE_NAME, packageName);
248 IntentSender intentSender = PendingIntent.getActivityAsUser(
249 mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT
250 | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
251 null, new UserHandle(mUserId)) .getIntentSender();
253 Bundle result = new Bundle();
254 result.putParcelable(PrintManager.EXTRA_PRINT_JOB, printJob);
255 result.putParcelable(PrintManager.EXTRA_PRINT_DIALOG_INTENT, intentSender);
259 Binder.restoreCallingIdentity(identity);
263 public List<PrintJobInfo> getPrintJobInfos(int appId) {
264 List<PrintJobInfo> cachedPrintJobs = mPrintJobForAppCache.getPrintJobs(appId);
265 // Note that the print spooler is not storing print jobs that
266 // are in a terminal state as it is non-trivial to properly update
267 // the spooler state for when to forget print jobs in terminal state.
268 // Therefore, we fuse the cached print jobs for running apps (some
269 // jobs are in a terminal state) with the ones that the print
270 // spooler knows about (some jobs are being processed).
271 ArrayMap<PrintJobId, PrintJobInfo> result =
272 new ArrayMap<PrintJobId, PrintJobInfo>();
274 // Add the cached print jobs for running apps.
275 final int cachedPrintJobCount = cachedPrintJobs.size();
276 for (int i = 0; i < cachedPrintJobCount; i++) {
277 PrintJobInfo cachedPrintJob = cachedPrintJobs.get(i);
278 result.put(cachedPrintJob.getId(), cachedPrintJob);
279 // Strip out the tag and the advanced print options.
280 // They are visible only to print services.
281 cachedPrintJob.setTag(null);
282 cachedPrintJob.setAdvancedOptions(null);
285 // Add everything else the spooler knows about.
286 List<PrintJobInfo> printJobs = mSpooler.getPrintJobInfos(null,
287 PrintJobInfo.STATE_ANY, appId);
288 if (printJobs != null) {
289 final int printJobCount = printJobs.size();
290 for (int i = 0; i < printJobCount; i++) {
291 PrintJobInfo printJob = printJobs.get(i);
292 result.put(printJob.getId(), printJob);
293 // Strip out the tag and the advanced print options.
294 // They are visible only to print services.
295 printJob.setTag(null);
296 printJob.setAdvancedOptions(null);
300 return new ArrayList<PrintJobInfo>(result.values());
303 public PrintJobInfo getPrintJobInfo(@NonNull PrintJobId printJobId, int appId) {
304 PrintJobInfo printJob = mPrintJobForAppCache.getPrintJob(printJobId, appId);
305 if (printJob == null) {
306 printJob = mSpooler.getPrintJobInfo(printJobId, appId);
308 if (printJob != null) {
309 // Strip out the tag and the advanced print options.
310 // They are visible only to print services.
311 printJob.setTag(null);
312 printJob.setAdvancedOptions(null);
318 * Get the custom icon for a printer. If the icon is not cached, the icon is
319 * requested asynchronously. Once it is available the printer is updated.
321 * @param printerId the id of the printer the icon should be loaded for
322 * @return the custom icon to be used for the printer or null if the icon is
324 * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon
326 public @Nullable Icon getCustomPrinterIcon(@NonNull PrinterId printerId) {
327 Icon icon = mSpooler.getCustomPrinterIcon(printerId);
330 RemotePrintService service = mActiveServices.get(printerId.getServiceName());
331 if (service != null) {
332 service.requestCustomPrinterIcon(printerId);
339 public void cancelPrintJob(@NonNull PrintJobId printJobId, int appId) {
340 PrintJobInfo printJobInfo = mSpooler.getPrintJobInfo(printJobId, appId);
341 if (printJobInfo == null) {
345 // Take a note that we are trying to cancel the job.
346 mSpooler.setPrintJobCancelling(printJobId, true);
348 if (printJobInfo.getState() != PrintJobInfo.STATE_FAILED) {
349 PrinterId printerId = printJobInfo.getPrinterId();
351 if (printerId != null) {
352 ComponentName printServiceName = printerId.getServiceName();
353 RemotePrintService printService = null;
354 synchronized (mLock) {
355 printService = mActiveServices.get(printServiceName);
357 if (printService == null) {
360 printService.onRequestCancelPrintJob(printJobInfo);
363 // If the print job is failed we do not need cooperation
364 // from the print service.
365 mSpooler.setPrintJobState(printJobId, PrintJobInfo.STATE_CANCELED, null);
369 public void restartPrintJob(@NonNull PrintJobId printJobId, int appId) {
370 PrintJobInfo printJobInfo = getPrintJobInfo(printJobId, appId);
371 if (printJobInfo == null || printJobInfo.getState() != PrintJobInfo.STATE_FAILED) {
374 mSpooler.setPrintJobState(printJobId, PrintJobInfo.STATE_QUEUED, null);
377 public @Nullable List<PrintServiceInfo> getPrintServices(int selectionFlags) {
378 synchronized (mLock) {
379 List<PrintServiceInfo> selectedServices = null;
380 final int installedServiceCount = mInstalledServices.size();
381 for (int i = 0; i < installedServiceCount; i++) {
382 PrintServiceInfo installedService = mInstalledServices.get(i);
384 ComponentName componentName = new ComponentName(
385 installedService.getResolveInfo().serviceInfo.packageName,
386 installedService.getResolveInfo().serviceInfo.name);
388 // Update isEnabled under the same lock the final returned list is created
389 installedService.setIsEnabled(mActiveServices.containsKey(componentName));
391 if (installedService.isEnabled()) {
392 if ((selectionFlags & PrintManager.ENABLED_SERVICES) == 0) {
396 if ((selectionFlags & PrintManager.DISABLED_SERVICES) == 0) {
401 if (selectedServices == null) {
402 selectedServices = new ArrayList<>();
404 selectedServices.add(installedService);
406 return selectedServices;
410 public void setPrintServiceEnabled(@NonNull ComponentName serviceName, boolean isEnabled) {
411 synchronized (mLock) {
412 boolean isChanged = false;
414 isChanged = mDisabledServices.remove(serviceName);
416 // Make sure to only disable services that are currently installed
417 final int numServices = mInstalledServices.size();
418 for (int i = 0; i < numServices; i++) {
419 PrintServiceInfo service = mInstalledServices.get(i);
421 if (service.getComponentName().equals(serviceName)) {
422 mDisabledServices.add(serviceName);
430 writeDisabledPrintServicesLocked(mDisabledServices);
432 MetricsLogger.action(mContext, MetricsEvent.ACTION_PRINT_SERVICE_TOGGLE,
435 onConfigurationChangedLocked();
441 * @return The currently known print service recommendations
443 public @Nullable List<RecommendationInfo> getPrintServiceRecommendations() {
444 return mPrintServiceRecommendations;
447 public void createPrinterDiscoverySession(@NonNull IPrinterDiscoveryObserver observer) {
448 mSpooler.clearCustomPrinterIconCache();
450 synchronized (mLock) {
451 throwIfDestroyedLocked();
453 if (mPrinterDiscoverySession == null) {
454 // If we do not have a session, tell all service to create one.
455 mPrinterDiscoverySession = new PrinterDiscoverySessionMediator() {
457 public void onDestroyed() {
458 mPrinterDiscoverySession = null;
461 // Add the observer to the brand new session.
462 mPrinterDiscoverySession.addObserverLocked(observer);
464 // If services have created session, just add the observer.
465 mPrinterDiscoverySession.addObserverLocked(observer);
470 public void destroyPrinterDiscoverySession(@NonNull IPrinterDiscoveryObserver observer) {
471 synchronized (mLock) {
472 // Already destroyed - nothing to do.
473 if (mPrinterDiscoverySession == null) {
476 // Remove this observer.
477 mPrinterDiscoverySession.removeObserverLocked(observer);
481 public void startPrinterDiscovery(@NonNull IPrinterDiscoveryObserver observer,
482 @Nullable List<PrinterId> printerIds) {
483 synchronized (mLock) {
484 throwIfDestroyedLocked();
486 // No session - nothing to do.
487 if (mPrinterDiscoverySession == null) {
490 // Kick of discovery.
491 mPrinterDiscoverySession.startPrinterDiscoveryLocked(observer,
496 public void stopPrinterDiscovery(@NonNull IPrinterDiscoveryObserver observer) {
497 synchronized (mLock) {
498 throwIfDestroyedLocked();
500 // No session - nothing to do.
501 if (mPrinterDiscoverySession == null) {
504 // Kick of discovery.
505 mPrinterDiscoverySession.stopPrinterDiscoveryLocked(observer);
509 public void validatePrinters(@NonNull List<PrinterId> printerIds) {
510 synchronized (mLock) {
511 throwIfDestroyedLocked();
512 // No services - nothing to do.
513 if (mActiveServices.isEmpty()) {
516 // No session - nothing to do.
517 if (mPrinterDiscoverySession == null) {
520 // Request an updated.
521 mPrinterDiscoverySession.validatePrintersLocked(printerIds);
525 public void startPrinterStateTracking(@NonNull PrinterId printerId) {
526 synchronized (mLock) {
527 throwIfDestroyedLocked();
528 // No services - nothing to do.
529 if (mActiveServices.isEmpty()) {
532 // No session - nothing to do.
533 if (mPrinterDiscoverySession == null) {
536 // Request start tracking the printer.
537 mPrinterDiscoverySession.startPrinterStateTrackingLocked(printerId);
541 public void stopPrinterStateTracking(PrinterId printerId) {
542 synchronized (mLock) {
543 throwIfDestroyedLocked();
544 // No services - nothing to do.
545 if (mActiveServices.isEmpty()) {
548 // No session - nothing to do.
549 if (mPrinterDiscoverySession == null) {
552 // Request stop tracking the printer.
553 mPrinterDiscoverySession.stopPrinterStateTrackingLocked(printerId);
557 public void addPrintJobStateChangeListener(@NonNull IPrintJobStateChangeListener listener,
558 int appId) throws RemoteException {
559 synchronized (mLock) {
560 throwIfDestroyedLocked();
561 if (mPrintJobStateChangeListenerRecords == null) {
562 mPrintJobStateChangeListenerRecords =
563 new ArrayList<PrintJobStateChangeListenerRecord>();
565 mPrintJobStateChangeListenerRecords.add(
566 new PrintJobStateChangeListenerRecord(listener, appId) {
568 public void onBinderDied() {
569 synchronized (mLock) {
570 if (mPrintJobStateChangeListenerRecords != null) {
571 mPrintJobStateChangeListenerRecords.remove(this);
579 public void removePrintJobStateChangeListener(@NonNull IPrintJobStateChangeListener listener) {
580 synchronized (mLock) {
581 throwIfDestroyedLocked();
582 if (mPrintJobStateChangeListenerRecords == null) {
585 final int recordCount = mPrintJobStateChangeListenerRecords.size();
586 for (int i = 0; i < recordCount; i++) {
587 PrintJobStateChangeListenerRecord record =
588 mPrintJobStateChangeListenerRecords.get(i);
589 if (record.listener.asBinder().equals(listener.asBinder())) {
591 mPrintJobStateChangeListenerRecords.remove(i);
595 if (mPrintJobStateChangeListenerRecords.isEmpty()) {
596 mPrintJobStateChangeListenerRecords = null;
601 public void addPrintServicesChangeListener(@NonNull IPrintServicesChangeListener listener)
602 throws RemoteException {
603 synchronized (mLock) {
604 throwIfDestroyedLocked();
605 if (mPrintServicesChangeListenerRecords == null) {
606 mPrintServicesChangeListenerRecords = new ArrayList<>();
608 mPrintServicesChangeListenerRecords.add(
609 new ListenerRecord<IPrintServicesChangeListener>(listener) {
611 public void onBinderDied() {
612 synchronized (mLock) {
613 if (mPrintServicesChangeListenerRecords != null) {
614 mPrintServicesChangeListenerRecords.remove(this);
622 public void removePrintServicesChangeListener(@NonNull IPrintServicesChangeListener listener) {
623 synchronized (mLock) {
624 throwIfDestroyedLocked();
625 if (mPrintServicesChangeListenerRecords == null) {
628 final int recordCount = mPrintServicesChangeListenerRecords.size();
629 for (int i = 0; i < recordCount; i++) {
630 ListenerRecord<IPrintServicesChangeListener> record =
631 mPrintServicesChangeListenerRecords.get(i);
632 if (record.listener.asBinder().equals(listener.asBinder())) {
634 mPrintServicesChangeListenerRecords.remove(i);
638 if (mPrintServicesChangeListenerRecords.isEmpty()) {
639 mPrintServicesChangeListenerRecords = null;
644 public void addPrintServiceRecommendationsChangeListener(
645 @NonNull IRecommendationsChangeListener listener) throws RemoteException {
646 synchronized (mLock) {
647 throwIfDestroyedLocked();
648 if (mPrintServiceRecommendationsChangeListenerRecords == null) {
649 mPrintServiceRecommendationsChangeListenerRecords = new ArrayList<>();
651 mPrintServiceRecommendationsService =
652 new RemotePrintServiceRecommendationService(mContext,
653 UserHandle.getUserHandleForUid(mUserId), this);
655 mPrintServiceRecommendationsChangeListenerRecords.add(
656 new ListenerRecord<IRecommendationsChangeListener>(listener) {
658 public void onBinderDied() {
659 synchronized (mLock) {
660 if (mPrintServiceRecommendationsChangeListenerRecords != null) {
661 mPrintServiceRecommendationsChangeListenerRecords.remove(this);
669 public void removePrintServiceRecommendationsChangeListener(
670 @NonNull IRecommendationsChangeListener listener) {
671 synchronized (mLock) {
672 throwIfDestroyedLocked();
673 if (mPrintServiceRecommendationsChangeListenerRecords == null) {
676 final int recordCount = mPrintServiceRecommendationsChangeListenerRecords.size();
677 for (int i = 0; i < recordCount; i++) {
678 ListenerRecord<IRecommendationsChangeListener> record =
679 mPrintServiceRecommendationsChangeListenerRecords.get(i);
680 if (record.listener.asBinder().equals(listener.asBinder())) {
682 mPrintServiceRecommendationsChangeListenerRecords.remove(i);
686 if (mPrintServiceRecommendationsChangeListenerRecords.isEmpty()) {
687 mPrintServiceRecommendationsChangeListenerRecords = null;
689 mPrintServiceRecommendations = null;
691 mPrintServiceRecommendationsService.close();
692 mPrintServiceRecommendationsService = null;
698 public void onPrintJobStateChanged(PrintJobInfo printJob) {
699 mPrintJobForAppCache.onPrintJobStateChanged(printJob);
700 Handler.getMain().sendMessage(obtainMessage(
701 UserState::handleDispatchPrintJobStateChanged,
702 this, printJob.getId(),
703 PooledLambda.obtainSupplier(printJob.getAppId()).recycleOnUse()));
706 public void onPrintServicesChanged() {
707 Handler.getMain().sendMessage(obtainMessage(
708 UserState::handleDispatchPrintServicesChanged, this));
712 public void onPrintServiceRecommendationsUpdated(List<RecommendationInfo> recommendations) {
713 Handler.getMain().sendMessage(obtainMessage(
714 UserState::handleDispatchPrintServiceRecommendationsUpdated,
715 this, recommendations));
719 public void onPrintersAdded(List<PrinterInfo> printers) {
720 synchronized (mLock) {
721 throwIfDestroyedLocked();
722 // No services - nothing to do.
723 if (mActiveServices.isEmpty()) {
726 // No session - nothing to do.
727 if (mPrinterDiscoverySession == null) {
730 mPrinterDiscoverySession.onPrintersAddedLocked(printers);
735 public void onPrintersRemoved(List<PrinterId> printerIds) {
736 synchronized (mLock) {
737 throwIfDestroyedLocked();
738 // No services - nothing to do.
739 if (mActiveServices.isEmpty()) {
742 // No session - nothing to do.
743 if (mPrinterDiscoverySession == null) {
746 mPrinterDiscoverySession.onPrintersRemovedLocked(printerIds);
751 public void onCustomPrinterIconLoaded(PrinterId printerId, Icon icon) {
752 mSpooler.onCustomPrinterIconLoaded(printerId, icon);
754 synchronized (mLock) {
755 throwIfDestroyedLocked();
757 // No session - nothing to do.
758 if (mPrinterDiscoverySession == null) {
761 mPrinterDiscoverySession.onCustomPrinterIconLoadedLocked(printerId);
766 public void onServiceDied(RemotePrintService service) {
767 synchronized (mLock) {
768 throwIfDestroyedLocked();
769 // No services - nothing to do.
770 if (mActiveServices.isEmpty()) {
773 // Fail all print jobs.
774 failActivePrintJobsForService(service.getComponentName());
775 service.onAllPrintJobsHandled();
777 mActiveServices.remove(service.getComponentName());
779 // The service might need to be restarted if it died because of an update
780 Handler.getMain().sendMessageDelayed(obtainMessage(
781 UserState::onConfigurationChanged, this),
782 SERVICE_RESTART_DELAY_MILLIS);
784 // No session - nothing to do.
785 if (mPrinterDiscoverySession == null) {
788 mPrinterDiscoverySession.onServiceDiedLocked(service);
792 public void updateIfNeededLocked() {
793 throwIfDestroyedLocked();
794 readConfigurationLocked();
795 onConfigurationChangedLocked();
798 public void destroyLocked() {
799 throwIfDestroyedLocked();
801 for (RemotePrintService service : mActiveServices.values()) {
804 mActiveServices.clear();
805 mInstalledServices.clear();
806 mDisabledServices.clear();
807 if (mPrinterDiscoverySession != null) {
808 mPrinterDiscoverySession.destroyLocked();
809 mPrinterDiscoverySession = null;
814 public void dump(@NonNull DualDumpOutputStream dumpStream) {
815 synchronized (mLock) {
816 dumpStream.write("user_id", PrintUserStateProto.USER_ID, mUserId);
818 final int installedServiceCount = mInstalledServices.size();
819 for (int i = 0; i < installedServiceCount; i++) {
820 long token = dumpStream.start("installed_services",
821 PrintUserStateProto.INSTALLED_SERVICES);
822 PrintServiceInfo installedService = mInstalledServices.get(i);
824 ResolveInfo resolveInfo = installedService.getResolveInfo();
825 writeComponentName(dumpStream, "component_name",
826 InstalledPrintServiceProto.COMPONENT_NAME,
827 new ComponentName(resolveInfo.serviceInfo.packageName,
828 resolveInfo.serviceInfo.name));
830 writeStringIfNotNull(dumpStream, "settings_activity",
831 InstalledPrintServiceProto.SETTINGS_ACTIVITY,
832 installedService.getSettingsActivityName());
833 writeStringIfNotNull(dumpStream, "add_printers_activity",
834 InstalledPrintServiceProto.ADD_PRINTERS_ACTIVITY,
835 installedService.getAddPrintersActivityName());
836 writeStringIfNotNull(dumpStream, "advanced_options_activity",
837 InstalledPrintServiceProto.ADVANCED_OPTIONS_ACTIVITY,
838 installedService.getAdvancedOptionsActivityName());
840 dumpStream.end(token);
843 for (ComponentName disabledService : mDisabledServices) {
844 writeComponentName(dumpStream, "disabled_services",
845 PrintUserStateProto.DISABLED_SERVICES, disabledService);
848 final int activeServiceCount = mActiveServices.size();
849 for (int i = 0; i < activeServiceCount; i++) {
850 long token = dumpStream.start("actives_services",
851 PrintUserStateProto.ACTIVE_SERVICES);
852 mActiveServices.valueAt(i).dump(dumpStream);
853 dumpStream.end(token);
856 mPrintJobForAppCache.dumpLocked(dumpStream);
858 if (mPrinterDiscoverySession != null) {
859 long token = dumpStream.start("discovery_service",
860 PrintUserStateProto.DISCOVERY_SESSIONS);
861 mPrinterDiscoverySession.dumpLocked(dumpStream);
862 dumpStream.end(token);
867 long token = dumpStream.start("print_spooler_state",
868 PrintUserStateProto.PRINT_SPOOLER_STATE);
869 mSpooler.dump(dumpStream);
870 dumpStream.end(token);
873 private void readConfigurationLocked() {
874 readInstalledPrintServicesLocked();
875 readDisabledPrintServicesLocked();
878 private void readInstalledPrintServicesLocked() {
879 Set<PrintServiceInfo> tempPrintServices = new HashSet<PrintServiceInfo>();
881 int queryIntentFlags = GET_SERVICES | GET_META_DATA | MATCH_DEBUG_TRIAGED_MISSING;
883 if (mIsInstantServiceAllowed) {
884 queryIntentFlags |= MATCH_INSTANT;
887 List<ResolveInfo> installedServices = mContext.getPackageManager()
888 .queryIntentServicesAsUser(mQueryIntent, queryIntentFlags, mUserId);
890 final int installedCount = installedServices.size();
891 for (int i = 0, count = installedCount; i < count; i++) {
892 ResolveInfo installedService = installedServices.get(i);
893 if (!android.Manifest.permission.BIND_PRINT_SERVICE.equals(
894 installedService.serviceInfo.permission)) {
895 ComponentName serviceName = new ComponentName(
896 installedService.serviceInfo.packageName,
897 installedService.serviceInfo.name);
898 Slog.w(LOG_TAG, "Skipping print service "
899 + serviceName.flattenToShortString()
900 + " since it does not require permission "
901 + android.Manifest.permission.BIND_PRINT_SERVICE);
904 tempPrintServices.add(PrintServiceInfo.create(mContext, installedService));
907 mInstalledServices.clear();
908 mInstalledServices.addAll(tempPrintServices);
912 * Update persistent state from a previous version of Android.
914 private void upgradePersistentStateIfNeeded() {
915 String enabledSettingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
916 Settings.Secure.ENABLED_PRINT_SERVICES, mUserId);
918 // Pre N we store the enabled services, in N and later we store the disabled services.
919 // Hence if enabledSettingValue is still set, we need to upgrade.
920 if (enabledSettingValue != null) {
921 Set<ComponentName> enabledServiceNameSet = new HashSet<ComponentName>();
922 readPrintServicesFromSettingLocked(Settings.Secure.ENABLED_PRINT_SERVICES,
923 enabledServiceNameSet);
925 ArraySet<ComponentName> disabledServices = new ArraySet<>();
926 final int numInstalledServices = mInstalledServices.size();
927 for (int i = 0; i < numInstalledServices; i++) {
928 ComponentName serviceName = mInstalledServices.get(i).getComponentName();
929 if (!enabledServiceNameSet.contains(serviceName)) {
930 disabledServices.add(serviceName);
934 writeDisabledPrintServicesLocked(disabledServices);
936 // We won't needed ENABLED_PRINT_SERVICES anymore, set to null to prevent upgrade to run
938 Settings.Secure.putStringForUser(mContext.getContentResolver(),
939 Settings.Secure.ENABLED_PRINT_SERVICES, null, mUserId);
944 * Read the set of disabled print services from the secure settings.
946 * @return true if the state changed.
948 private void readDisabledPrintServicesLocked() {
949 Set<ComponentName> tempDisabledServiceNameSet = new HashSet<ComponentName>();
950 readPrintServicesFromSettingLocked(Settings.Secure.DISABLED_PRINT_SERVICES,
951 tempDisabledServiceNameSet);
952 if (!tempDisabledServiceNameSet.equals(mDisabledServices)) {
953 mDisabledServices.clear();
954 mDisabledServices.addAll(tempDisabledServiceNameSet);
958 private void readPrintServicesFromSettingLocked(String setting,
959 Set<ComponentName> outServiceNames) {
960 String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
962 if (!TextUtils.isEmpty(settingValue)) {
963 TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
964 splitter.setString(settingValue);
965 while (splitter.hasNext()) {
966 String string = splitter.next();
967 if (TextUtils.isEmpty(string)) {
970 ComponentName componentName = ComponentName.unflattenFromString(string);
971 if (componentName != null) {
972 outServiceNames.add(componentName);
979 * Persist the disabled print services to the secure settings.
981 private void writeDisabledPrintServicesLocked(Set<ComponentName> disabledServices) {
982 StringBuilder builder = new StringBuilder();
983 for (ComponentName componentName : disabledServices) {
984 if (builder.length() > 0) {
985 builder.append(COMPONENT_NAME_SEPARATOR);
987 builder.append(componentName.flattenToShortString());
989 Settings.Secure.putStringForUser(mContext.getContentResolver(),
990 Settings.Secure.DISABLED_PRINT_SERVICES, builder.toString(), mUserId);
994 * Get the {@link ComponentName names} of the installed print services
996 * @return The names of the installed print services
998 private ArrayList<ComponentName> getInstalledComponents() {
999 ArrayList<ComponentName> installedComponents = new ArrayList<ComponentName>();
1001 final int installedCount = mInstalledServices.size();
1002 for (int i = 0; i < installedCount; i++) {
1003 ResolveInfo resolveInfo = mInstalledServices.get(i).getResolveInfo();
1004 ComponentName serviceName = new ComponentName(resolveInfo.serviceInfo.packageName,
1005 resolveInfo.serviceInfo.name);
1007 installedComponents.add(serviceName);
1010 return installedComponents;
1014 * Prune persistent state if a print service was uninstalled
1016 public void prunePrintServices() {
1017 ArrayList<ComponentName> installedComponents;
1019 synchronized (mLock) {
1020 installedComponents = getInstalledComponents();
1022 // Remove unnecessary entries from persistent state "disabled services"
1023 boolean disabledServicesUninstalled = mDisabledServices.retainAll(installedComponents);
1024 if (disabledServicesUninstalled) {
1025 writeDisabledPrintServicesLocked(mDisabledServices);
1029 // Remove unnecessary entries from persistent state "approved services"
1030 mSpooler.pruneApprovedPrintServices(installedComponents);
1034 private void onConfigurationChangedLocked() {
1035 ArrayList<ComponentName> installedComponents = getInstalledComponents();
1037 final int installedCount = installedComponents.size();
1038 for (int i = 0; i < installedCount; i++) {
1039 ComponentName serviceName = installedComponents.get(i);
1041 if (!mDisabledServices.contains(serviceName)) {
1042 if (!mActiveServices.containsKey(serviceName)) {
1043 RemotePrintService service = new RemotePrintService(
1044 mContext, serviceName, mUserId, mSpooler, this);
1045 addServiceLocked(service);
1048 RemotePrintService service = mActiveServices.remove(serviceName);
1049 if (service != null) {
1050 removeServiceLocked(service);
1055 Iterator<Map.Entry<ComponentName, RemotePrintService>> iterator =
1056 mActiveServices.entrySet().iterator();
1057 while (iterator.hasNext()) {
1058 Map.Entry<ComponentName, RemotePrintService> entry = iterator.next();
1059 ComponentName serviceName = entry.getKey();
1060 RemotePrintService service = entry.getValue();
1061 if (!installedComponents.contains(serviceName)) {
1062 removeServiceLocked(service);
1067 onPrintServicesChanged();
1070 private void addServiceLocked(RemotePrintService service) {
1071 mActiveServices.put(service.getComponentName(), service);
1072 if (mPrinterDiscoverySession != null) {
1073 mPrinterDiscoverySession.onServiceAddedLocked(service);
1077 private void removeServiceLocked(RemotePrintService service) {
1078 // Fail all print jobs.
1079 failActivePrintJobsForService(service.getComponentName());
1080 // If discovery is in progress, tear down the service.
1081 if (mPrinterDiscoverySession != null) {
1082 mPrinterDiscoverySession.onServiceRemovedLocked(service);
1084 // Otherwise, just destroy it.
1089 private void failActivePrintJobsForService(final ComponentName serviceName) {
1090 // Makes sure all active print jobs are failed since the service
1091 // just died. Do this off the main thread since we do to allow
1092 // calls into the spooler on the main thread.
1093 if (Looper.getMainLooper().isCurrentThread()) {
1094 BackgroundThread.getHandler().sendMessage(obtainMessage(
1095 UserState::failScheduledPrintJobsForServiceInternal, this, serviceName));
1097 failScheduledPrintJobsForServiceInternal(serviceName);
1101 private void failScheduledPrintJobsForServiceInternal(ComponentName serviceName) {
1102 List<PrintJobInfo> printJobs = mSpooler.getPrintJobInfos(serviceName,
1103 PrintJobInfo.STATE_ANY_SCHEDULED, PrintManager.APP_ID_ANY);
1104 if (printJobs == null) {
1107 final long identity = Binder.clearCallingIdentity();
1109 final int printJobCount = printJobs.size();
1110 for (int i = 0; i < printJobCount; i++) {
1111 PrintJobInfo printJob = printJobs.get(i);
1112 mSpooler.setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED,
1113 mContext.getString(R.string.reason_service_unavailable));
1116 Binder.restoreCallingIdentity(identity);
1120 private void throwIfDestroyedLocked() {
1122 throw new IllegalStateException("Cannot interact with a destroyed instance.");
1126 private void handleDispatchPrintJobStateChanged(
1127 PrintJobId printJobId, IntSupplier appIdSupplier) {
1128 int appId = appIdSupplier.getAsInt();
1129 final List<PrintJobStateChangeListenerRecord> records;
1130 synchronized (mLock) {
1131 if (mPrintJobStateChangeListenerRecords == null) {
1134 records = new ArrayList<>(mPrintJobStateChangeListenerRecords);
1136 final int recordCount = records.size();
1137 for (int i = 0; i < recordCount; i++) {
1138 PrintJobStateChangeListenerRecord record = records.get(i);
1139 if (record.appId == PrintManager.APP_ID_ANY
1140 || record.appId == appId) {
1142 record.listener.onPrintJobStateChanged(printJobId);
1143 } catch (RemoteException re) {
1144 Log.e(LOG_TAG, "Error notifying for print job state change", re);
1150 private void handleDispatchPrintServicesChanged() {
1151 final List<ListenerRecord<IPrintServicesChangeListener>> records;
1152 synchronized (mLock) {
1153 if (mPrintServicesChangeListenerRecords == null) {
1156 records = new ArrayList<>(mPrintServicesChangeListenerRecords);
1158 final int recordCount = records.size();
1159 for (int i = 0; i < recordCount; i++) {
1160 ListenerRecord<IPrintServicesChangeListener> record = records.get(i);
1163 record.listener.onPrintServicesChanged();;
1164 } catch (RemoteException re) {
1165 Log.e(LOG_TAG, "Error notifying for print services change", re);
1170 private void handleDispatchPrintServiceRecommendationsUpdated(
1171 @Nullable List<RecommendationInfo> recommendations) {
1172 final List<ListenerRecord<IRecommendationsChangeListener>> records;
1173 synchronized (mLock) {
1174 if (mPrintServiceRecommendationsChangeListenerRecords == null) {
1177 records = new ArrayList<>(mPrintServiceRecommendationsChangeListenerRecords);
1179 mPrintServiceRecommendations = recommendations;
1181 final int recordCount = records.size();
1182 for (int i = 0; i < recordCount; i++) {
1183 ListenerRecord<IRecommendationsChangeListener> record = records.get(i);
1186 record.listener.onRecommendationsChanged();
1187 } catch (RemoteException re) {
1188 Log.e(LOG_TAG, "Error notifying for print service recommendations change", re);
1193 private void onConfigurationChanged() {
1194 synchronized (mLock) {
1195 onConfigurationChangedLocked();
1199 public boolean getBindInstantServiceAllowed() {
1200 return mIsInstantServiceAllowed;
1203 public void setBindInstantServiceAllowed(boolean allowed) {
1204 synchronized (mLock) {
1205 mIsInstantServiceAllowed = allowed;
1207 updateIfNeededLocked();
1211 private abstract class PrintJobStateChangeListenerRecord implements DeathRecipient {
1212 @NonNull final IPrintJobStateChangeListener listener;
1215 public PrintJobStateChangeListenerRecord(@NonNull IPrintJobStateChangeListener listener,
1216 int appId) throws RemoteException {
1217 this.listener = listener;
1219 listener.asBinder().linkToDeath(this, 0);
1222 public void destroy() {
1223 listener.asBinder().unlinkToDeath(this, 0);
1227 public void binderDied() {
1228 listener.asBinder().unlinkToDeath(this, 0);
1232 public abstract void onBinderDied();
1235 private abstract class ListenerRecord<T extends IInterface> implements DeathRecipient {
1236 @NonNull final T listener;
1238 public ListenerRecord(@NonNull T listener) throws RemoteException {
1239 this.listener = listener;
1240 listener.asBinder().linkToDeath(this, 0);
1243 public void destroy() {
1244 listener.asBinder().unlinkToDeath(this, 0);
1248 public void binderDied() {
1249 listener.asBinder().unlinkToDeath(this, 0);
1253 public abstract void onBinderDied();
1256 private class PrinterDiscoverySessionMediator {
1257 private final ArrayMap<PrinterId, PrinterInfo> mPrinters =
1258 new ArrayMap<PrinterId, PrinterInfo>();
1260 private final RemoteCallbackList<IPrinterDiscoveryObserver> mDiscoveryObservers =
1261 new RemoteCallbackList<IPrinterDiscoveryObserver>() {
1263 public void onCallbackDied(IPrinterDiscoveryObserver observer) {
1264 synchronized (mLock) {
1265 stopPrinterDiscoveryLocked(observer);
1266 removeObserverLocked(observer);
1271 private final List<IBinder> mStartedPrinterDiscoveryTokens = new ArrayList<IBinder>();
1273 private final List<PrinterId> mStateTrackedPrinters = new ArrayList<PrinterId>();
1275 private boolean mIsDestroyed;
1277 PrinterDiscoverySessionMediator() {
1278 // Kick off the session creation.
1279 Handler.getMain().sendMessage(obtainMessage(UserState.PrinterDiscoverySessionMediator::
1280 handleDispatchCreatePrinterDiscoverySession,
1281 this, new ArrayList<>(mActiveServices.values())));
1284 public void addObserverLocked(@NonNull IPrinterDiscoveryObserver observer) {
1285 // Add the observer.
1286 mDiscoveryObservers.register(observer);
1288 // Bring the added observer up to speed with the printers.
1289 if (!mPrinters.isEmpty()) {
1290 Handler.getMain().sendMessage(obtainMessage(
1291 UserState.PrinterDiscoverySessionMediator::handlePrintersAdded,
1292 this, observer, new ArrayList<>(mPrinters.values())));
1296 public void removeObserverLocked(@NonNull IPrinterDiscoveryObserver observer) {
1297 // Remove the observer.
1298 mDiscoveryObservers.unregister(observer);
1299 // No one else observing - then kill it.
1300 if (mDiscoveryObservers.getRegisteredCallbackCount() == 0) {
1305 public final void startPrinterDiscoveryLocked(@NonNull IPrinterDiscoveryObserver observer,
1306 @Nullable List<PrinterId> priorityList) {
1308 Log.w(LOG_TAG, "Not starting dicovery - session destroyed");
1312 final boolean discoveryStarted = !mStartedPrinterDiscoveryTokens.isEmpty();
1314 // Remember we got a start request to match with an end.
1315 mStartedPrinterDiscoveryTokens.add(observer.asBinder());
1317 // If printer discovery is ongoing and the start request has a list
1318 // of printer to be checked, then we just request validating them.
1319 if (discoveryStarted && priorityList != null && !priorityList.isEmpty()) {
1320 validatePrinters(priorityList);
1324 // The service are already performing discovery - nothing to do.
1325 if (mStartedPrinterDiscoveryTokens.size() > 1) {
1329 Handler.getMain().sendMessage(obtainMessage(UserState.PrinterDiscoverySessionMediator::
1330 handleDispatchStartPrinterDiscovery, this,
1331 new ArrayList<>(mActiveServices.values()), priorityList));
1334 public final void stopPrinterDiscoveryLocked(@NonNull IPrinterDiscoveryObserver observer) {
1336 Log.w(LOG_TAG, "Not stopping dicovery - session destroyed");
1339 // This one did not make an active discovery request - nothing to do.
1340 if (!mStartedPrinterDiscoveryTokens.remove(observer.asBinder())) {
1343 // There are other interested observers - do not stop discovery.
1344 if (!mStartedPrinterDiscoveryTokens.isEmpty()) {
1347 Handler.getMain().sendMessage(obtainMessage(UserState.PrinterDiscoverySessionMediator::
1348 handleDispatchStopPrinterDiscovery,
1349 this, new ArrayList<>(mActiveServices.values())));
1352 public void validatePrintersLocked(@NonNull List<PrinterId> printerIds) {
1354 Log.w(LOG_TAG, "Not validating pritners - session destroyed");
1358 List<PrinterId> remainingList = new ArrayList<PrinterId>(printerIds);
1359 while (!remainingList.isEmpty()) {
1360 Iterator<PrinterId> iterator = remainingList.iterator();
1361 // Gather the printers per service and request a validation.
1362 List<PrinterId> updateList = new ArrayList<PrinterId>();
1363 ComponentName serviceName = null;
1364 while (iterator.hasNext()) {
1365 PrinterId printerId = iterator.next();
1366 if (printerId != null) {
1367 if (updateList.isEmpty()) {
1368 updateList.add(printerId);
1369 serviceName = printerId.getServiceName();
1371 } else if (printerId.getServiceName().equals(serviceName)) {
1372 updateList.add(printerId);
1377 // Schedule a notification of the service.
1378 RemotePrintService service = mActiveServices.get(serviceName);
1379 if (service != null) {
1380 Handler.getMain().sendMessage(obtainMessage(
1381 UserState.PrinterDiscoverySessionMediator::handleValidatePrinters,
1382 this, service, updateList));
1387 public final void startPrinterStateTrackingLocked(@NonNull PrinterId printerId) {
1389 Log.w(LOG_TAG, "Not starting printer state tracking - session destroyed");
1392 // If printer discovery is not started - nothing to do.
1393 if (mStartedPrinterDiscoveryTokens.isEmpty()) {
1396 final boolean containedPrinterId = mStateTrackedPrinters.contains(printerId);
1397 // Keep track of the number of requests to track this one.
1398 mStateTrackedPrinters.add(printerId);
1399 // If we were tracking this printer - nothing to do.
1400 if (containedPrinterId) {
1403 // No service - nothing to do.
1404 RemotePrintService service = mActiveServices.get(printerId.getServiceName());
1405 if (service == null) {
1408 // Ask the service to start tracking.
1409 Handler.getMain().sendMessage(obtainMessage(UserState.PrinterDiscoverySessionMediator::
1410 handleStartPrinterStateTracking, this, service, printerId));
1413 public final void stopPrinterStateTrackingLocked(PrinterId printerId) {
1415 Log.w(LOG_TAG, "Not stopping printer state tracking - session destroyed");
1418 // If printer discovery is not started - nothing to do.
1419 if (mStartedPrinterDiscoveryTokens.isEmpty()) {
1422 // If we did not track this printer - nothing to do.
1423 if (!mStateTrackedPrinters.remove(printerId)) {
1426 // No service - nothing to do.
1427 RemotePrintService service = mActiveServices.get(printerId.getServiceName());
1428 if (service == null) {
1431 // Ask the service to start tracking.
1432 Handler.getMain().sendMessage(obtainMessage(UserState.PrinterDiscoverySessionMediator::
1433 handleStopPrinterStateTracking, this, service, printerId));
1436 public void onDestroyed() {
1440 public void destroyLocked() {
1442 Log.w(LOG_TAG, "Not destroying - session destroyed");
1445 mIsDestroyed = true;
1446 // Make sure printer tracking is stopped.
1447 final int printerCount = mStateTrackedPrinters.size();
1448 for (int i = 0; i < printerCount; i++) {
1449 PrinterId printerId = mStateTrackedPrinters.get(i);
1450 stopPrinterStateTracking(printerId);
1452 // Make sure discovery is stopped.
1453 final int observerCount = mStartedPrinterDiscoveryTokens.size();
1454 for (int i = 0; i < observerCount; i++) {
1455 IBinder token = mStartedPrinterDiscoveryTokens.get(i);
1456 stopPrinterDiscoveryLocked(IPrinterDiscoveryObserver.Stub.asInterface(token));
1458 // Tell the services we are done.
1459 Handler.getMain().sendMessage(obtainMessage(UserState.PrinterDiscoverySessionMediator::
1460 handleDispatchDestroyPrinterDiscoverySession,
1461 this, new ArrayList<>(mActiveServices.values())));
1464 public void onPrintersAddedLocked(List<PrinterInfo> printers) {
1466 Log.i(LOG_TAG, "onPrintersAddedLocked()");
1469 Log.w(LOG_TAG, "Not adding printers - session destroyed");
1472 List<PrinterInfo> addedPrinters = null;
1473 final int addedPrinterCount = printers.size();
1474 for (int i = 0; i < addedPrinterCount; i++) {
1475 PrinterInfo printer = printers.get(i);
1476 PrinterInfo oldPrinter = mPrinters.put(printer.getId(), printer);
1477 if (oldPrinter == null || !oldPrinter.equals(printer)) {
1478 if (addedPrinters == null) {
1479 addedPrinters = new ArrayList<PrinterInfo>();
1481 addedPrinters.add(printer);
1484 if (addedPrinters != null) {
1485 Handler.getMain().sendMessage(obtainMessage(
1486 UserState.PrinterDiscoverySessionMediator::handleDispatchPrintersAdded,
1487 this, addedPrinters));
1491 public void onPrintersRemovedLocked(List<PrinterId> printerIds) {
1493 Log.i(LOG_TAG, "onPrintersRemovedLocked()");
1496 Log.w(LOG_TAG, "Not removing printers - session destroyed");
1499 List<PrinterId> removedPrinterIds = null;
1500 final int removedPrinterCount = printerIds.size();
1501 for (int i = 0; i < removedPrinterCount; i++) {
1502 PrinterId removedPrinterId = printerIds.get(i);
1503 if (mPrinters.remove(removedPrinterId) != null) {
1504 if (removedPrinterIds == null) {
1505 removedPrinterIds = new ArrayList<PrinterId>();
1507 removedPrinterIds.add(removedPrinterId);
1510 if (removedPrinterIds != null) {
1511 Handler.getMain().sendMessage(obtainMessage(
1512 UserState.PrinterDiscoverySessionMediator::handleDispatchPrintersRemoved,
1513 this, removedPrinterIds));
1517 public void onServiceRemovedLocked(RemotePrintService service) {
1519 Log.w(LOG_TAG, "Not updating removed service - session destroyed");
1522 // Remove the reported and tracked printers for that service.
1523 ComponentName serviceName = service.getComponentName();
1524 removePrintersForServiceLocked(serviceName);
1529 * Handle that a custom icon for a printer was loaded.
1531 * This increments the icon generation and adds the printer again which triggers an update
1532 * in all users of the currently known printers.
1534 * @param printerId the id of the printer the icon belongs to
1535 * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon
1537 public void onCustomPrinterIconLoadedLocked(PrinterId printerId) {
1539 Log.i(LOG_TAG, "onCustomPrinterIconLoadedLocked()");
1542 Log.w(LOG_TAG, "Not updating printer - session destroyed");
1546 PrinterInfo printer = mPrinters.get(printerId);
1547 if (printer != null) {
1548 PrinterInfo newPrinter = (new PrinterInfo.Builder(printer))
1549 .incCustomPrinterIconGen().build();
1550 mPrinters.put(printerId, newPrinter);
1552 ArrayList<PrinterInfo> addedPrinters = new ArrayList<>(1);
1553 addedPrinters.add(newPrinter);
1554 Handler.getMain().sendMessage(obtainMessage(
1555 UserState.PrinterDiscoverySessionMediator::handleDispatchPrintersAdded,
1556 this, addedPrinters));
1560 public void onServiceDiedLocked(RemotePrintService service) {
1561 removeServiceLocked(service);
1564 public void onServiceAddedLocked(RemotePrintService service) {
1566 Log.w(LOG_TAG, "Not updating added service - session destroyed");
1569 // Tell the service to create a session.
1570 Handler.getMain().sendMessage(obtainMessage(
1571 RemotePrintService::createPrinterDiscoverySession, service));
1572 // Start printer discovery if necessary.
1573 if (!mStartedPrinterDiscoveryTokens.isEmpty()) {
1574 Handler.getMain().sendMessage(obtainMessage(
1575 RemotePrintService::startPrinterDiscovery, service, null));
1577 // Start tracking printers if necessary
1578 final int trackedPrinterCount = mStateTrackedPrinters.size();
1579 for (int i = 0; i < trackedPrinterCount; i++) {
1580 PrinterId printerId = mStateTrackedPrinters.get(i);
1581 if (printerId.getServiceName().equals(service.getComponentName())) {
1582 Handler.getMain().sendMessage(obtainMessage(
1583 RemotePrintService::startPrinterStateTracking, service, printerId));
1588 public void dumpLocked(@NonNull DualDumpOutputStream dumpStream) {
1589 dumpStream.write("is_destroyed", PrinterDiscoverySessionProto.IS_DESTROYED, mDestroyed);
1590 dumpStream.write("is_printer_discovery_in_progress",
1591 PrinterDiscoverySessionProto.IS_PRINTER_DISCOVERY_IN_PROGRESS,
1592 !mStartedPrinterDiscoveryTokens.isEmpty());
1594 final int observerCount = mDiscoveryObservers.beginBroadcast();
1595 for (int i = 0; i < observerCount; i++) {
1596 IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i);
1597 dumpStream.write("printer_discovery_observers",
1598 PrinterDiscoverySessionProto.PRINTER_DISCOVERY_OBSERVERS,
1599 observer.toString());
1601 mDiscoveryObservers.finishBroadcast();
1603 final int tokenCount = this.mStartedPrinterDiscoveryTokens.size();
1604 for (int i = 0; i < tokenCount; i++) {
1605 IBinder token = mStartedPrinterDiscoveryTokens.get(i);
1606 dumpStream.write("discovery_requests",
1607 PrinterDiscoverySessionProto.DISCOVERY_REQUESTS, token.toString());
1610 final int trackedPrinters = mStateTrackedPrinters.size();
1611 for (int i = 0; i < trackedPrinters; i++) {
1612 PrinterId printer = mStateTrackedPrinters.get(i);
1613 writePrinterId(dumpStream, "tracked_printer_requests",
1614 PrinterDiscoverySessionProto.TRACKED_PRINTER_REQUESTS, printer);
1617 final int printerCount = mPrinters.size();
1618 for (int i = 0; i < printerCount; i++) {
1619 PrinterInfo printer = mPrinters.valueAt(i);
1620 writePrinterInfo(mContext, dumpStream, "printer",
1621 PrinterDiscoverySessionProto.PRINTER, printer);
1625 private void removePrintersForServiceLocked(ComponentName serviceName) {
1626 // No printers - nothing to do.
1627 if (mPrinters.isEmpty()) {
1630 // Remove the printers for that service.
1631 List<PrinterId> removedPrinterIds = null;
1632 final int printerCount = mPrinters.size();
1633 for (int i = 0; i < printerCount; i++) {
1634 PrinterId printerId = mPrinters.keyAt(i);
1635 if (printerId.getServiceName().equals(serviceName)) {
1636 if (removedPrinterIds == null) {
1637 removedPrinterIds = new ArrayList<PrinterId>();
1639 removedPrinterIds.add(printerId);
1642 if (removedPrinterIds != null) {
1643 final int removedPrinterCount = removedPrinterIds.size();
1644 for (int i = 0; i < removedPrinterCount; i++) {
1645 mPrinters.remove(removedPrinterIds.get(i));
1647 Handler.getMain().sendMessage(obtainMessage(
1648 UserState.PrinterDiscoverySessionMediator::handleDispatchPrintersRemoved,
1649 this, removedPrinterIds));
1653 private void handleDispatchPrintersAdded(List<PrinterInfo> addedPrinters) {
1654 final int observerCount = mDiscoveryObservers.beginBroadcast();
1655 for (int i = 0; i < observerCount; i++) {
1656 IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i);
1657 handlePrintersAdded(observer, addedPrinters);
1659 mDiscoveryObservers.finishBroadcast();
1662 private void handleDispatchPrintersRemoved(List<PrinterId> removedPrinterIds) {
1663 final int observerCount = mDiscoveryObservers.beginBroadcast();
1664 for (int i = 0; i < observerCount; i++) {
1665 IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i);
1666 handlePrintersRemoved(observer, removedPrinterIds);
1668 mDiscoveryObservers.finishBroadcast();
1671 private void handleDispatchCreatePrinterDiscoverySession(
1672 List<RemotePrintService> services) {
1673 final int serviceCount = services.size();
1674 for (int i = 0; i < serviceCount; i++) {
1675 RemotePrintService service = services.get(i);
1676 service.createPrinterDiscoverySession();
1680 private void handleDispatchDestroyPrinterDiscoverySession(
1681 List<RemotePrintService> services) {
1682 final int serviceCount = services.size();
1683 for (int i = 0; i < serviceCount; i++) {
1684 RemotePrintService service = services.get(i);
1685 service.destroyPrinterDiscoverySession();
1690 private void handleDispatchStartPrinterDiscovery(
1691 List<RemotePrintService> services, List<PrinterId> printerIds) {
1692 final int serviceCount = services.size();
1693 for (int i = 0; i < serviceCount; i++) {
1694 RemotePrintService service = services.get(i);
1695 service.startPrinterDiscovery(printerIds);
1699 private void handleDispatchStopPrinterDiscovery(List<RemotePrintService> services) {
1700 final int serviceCount = services.size();
1701 for (int i = 0; i < serviceCount; i++) {
1702 RemotePrintService service = services.get(i);
1703 service.stopPrinterDiscovery();
1707 private void handleValidatePrinters(RemotePrintService service,
1708 List<PrinterId> printerIds) {
1709 service.validatePrinters(printerIds);
1712 private void handleStartPrinterStateTracking(@NonNull RemotePrintService service,
1713 @NonNull PrinterId printerId) {
1714 service.startPrinterStateTracking(printerId);
1717 private void handleStopPrinterStateTracking(RemotePrintService service,
1718 PrinterId printerId) {
1719 service.stopPrinterStateTracking(printerId);
1722 private void handlePrintersAdded(IPrinterDiscoveryObserver observer,
1723 List<PrinterInfo> printers) {
1725 observer.onPrintersAdded(new ParceledListSlice<PrinterInfo>(printers));
1726 } catch (RemoteException re) {
1727 Log.e(LOG_TAG, "Error sending added printers", re);
1731 private void handlePrintersRemoved(IPrinterDiscoveryObserver observer,
1732 List<PrinterId> printerIds) {
1734 observer.onPrintersRemoved(new ParceledListSlice<PrinterId>(printerIds));
1735 } catch (RemoteException re) {
1736 Log.e(LOG_TAG, "Error sending removed printers", re);
1741 private final class PrintJobForAppCache {
1742 private final SparseArray<List<PrintJobInfo>> mPrintJobsForRunningApp =
1743 new SparseArray<List<PrintJobInfo>>();
1745 public boolean onPrintJobCreated(final IBinder creator, final int appId,
1746 PrintJobInfo printJob) {
1748 creator.linkToDeath(new DeathRecipient() {
1750 public void binderDied() {
1751 creator.unlinkToDeath(this, 0);
1752 synchronized (mLock) {
1753 mPrintJobsForRunningApp.remove(appId);
1757 } catch (RemoteException re) {
1758 /* The process is already dead - we just failed. */
1761 synchronized (mLock) {
1762 List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get(appId);
1763 if (printJobsForApp == null) {
1764 printJobsForApp = new ArrayList<PrintJobInfo>();
1765 mPrintJobsForRunningApp.put(appId, printJobsForApp);
1767 printJobsForApp.add(printJob);
1772 public void onPrintJobStateChanged(PrintJobInfo printJob) {
1773 synchronized (mLock) {
1774 List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get(
1775 printJob.getAppId());
1776 if (printJobsForApp == null) {
1779 final int printJobCount = printJobsForApp.size();
1780 for (int i = 0; i < printJobCount; i++) {
1781 PrintJobInfo oldPrintJob = printJobsForApp.get(i);
1782 if (oldPrintJob.getId().equals(printJob.getId())) {
1783 printJobsForApp.set(i, printJob);
1789 public PrintJobInfo getPrintJob(PrintJobId printJobId, int appId) {
1790 synchronized (mLock) {
1791 List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get(appId);
1792 if (printJobsForApp == null) {
1795 final int printJobCount = printJobsForApp.size();
1796 for (int i = 0; i < printJobCount; i++) {
1797 PrintJobInfo printJob = printJobsForApp.get(i);
1798 if (printJob.getId().equals(printJobId)) {
1806 public List<PrintJobInfo> getPrintJobs(int appId) {
1807 synchronized (mLock) {
1808 List<PrintJobInfo> printJobs = null;
1809 if (appId == PrintManager.APP_ID_ANY) {
1810 final int bucketCount = mPrintJobsForRunningApp.size();
1811 for (int i = 0; i < bucketCount; i++) {
1812 List<PrintJobInfo> bucket = mPrintJobsForRunningApp.valueAt(i);
1813 if (printJobs == null) {
1814 printJobs = new ArrayList<PrintJobInfo>();
1816 printJobs.addAll(bucket);
1819 List<PrintJobInfo> bucket = mPrintJobsForRunningApp.get(appId);
1820 if (bucket != null) {
1821 if (printJobs == null) {
1822 printJobs = new ArrayList<PrintJobInfo>();
1824 printJobs.addAll(bucket);
1827 if (printJobs != null) {
1830 return Collections.emptyList();
1834 public void dumpLocked(@NonNull DualDumpOutputStream dumpStream) {
1835 final int bucketCount = mPrintJobsForRunningApp.size();
1836 for (int i = 0; i < bucketCount; i++) {
1837 final int appId = mPrintJobsForRunningApp.keyAt(i);
1838 List<PrintJobInfo> bucket = mPrintJobsForRunningApp.valueAt(i);
1839 final int printJobCount = bucket.size();
1840 for (int j = 0; j < printJobCount; j++) {
1841 long token = dumpStream.start("cached_print_jobs",
1842 PrintUserStateProto.CACHED_PRINT_JOBS);
1844 dumpStream.write("app_id", CachedPrintJobProto.APP_ID, appId);
1846 writePrintJobInfo(mContext, dumpStream, "print_job",
1847 CachedPrintJobProto.PRINT_JOB, bucket.get(j));
1849 dumpStream.end(token);