| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.print; |
| |
| import static android.content.pm.PackageManager.GET_META_DATA; |
| import static android.content.pm.PackageManager.GET_SERVICES; |
| import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; |
| |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.app.PendingIntent; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentSender; |
| import android.content.pm.ParceledListSlice; |
| import android.content.pm.ResolveInfo; |
| import android.graphics.drawable.Icon; |
| import android.net.Uri; |
| import android.os.AsyncTask; |
| import android.os.Binder; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.IBinder; |
| import android.os.IBinder.DeathRecipient; |
| import android.os.IInterface; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.os.RemoteCallbackList; |
| import android.os.RemoteException; |
| import android.os.UserHandle; |
| import android.print.IPrintDocumentAdapter; |
| import android.print.IPrintJobStateChangeListener; |
| import android.printservice.recommendation.IRecommendationsChangeListener; |
| import android.print.IPrintServicesChangeListener; |
| import android.print.IPrinterDiscoveryObserver; |
| import android.print.PrintAttributes; |
| import android.print.PrintJobId; |
| import android.print.PrintJobInfo; |
| import android.print.PrintManager; |
| import android.printservice.recommendation.RecommendationInfo; |
| import android.print.PrinterId; |
| import android.print.PrinterInfo; |
| import android.printservice.PrintServiceInfo; |
| import android.provider.DocumentsContract; |
| import android.provider.Settings; |
| import android.text.TextUtils; |
| import android.text.TextUtils.SimpleStringSplitter; |
| import android.util.ArrayMap; |
| import android.util.ArraySet; |
| import android.util.Log; |
| import android.util.Slog; |
| import android.util.SparseArray; |
| |
| import com.android.internal.R; |
| import com.android.internal.os.BackgroundThread; |
| import com.android.internal.os.SomeArgs; |
| import com.android.server.print.RemotePrintService.PrintServiceCallbacks; |
| import com.android.server.print.RemotePrintSpooler.PrintSpoolerCallbacks; |
| import com.android.server.print.RemotePrintServiceRecommendationService.RemotePrintServiceRecommendationServiceCallbacks; |
| |
| import java.io.FileDescriptor; |
| import java.io.PrintWriter; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * Represents the print state for a user. |
| */ |
| final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, |
| RemotePrintServiceRecommendationServiceCallbacks { |
| |
| private static final String LOG_TAG = "UserState"; |
| |
| private static final boolean DEBUG = false; |
| |
| private static final char COMPONENT_NAME_SEPARATOR = ':'; |
| |
| private final SimpleStringSplitter mStringColonSplitter = |
| new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR); |
| |
| private final Intent mQueryIntent = |
| new Intent(android.printservice.PrintService.SERVICE_INTERFACE); |
| |
| private final ArrayMap<ComponentName, RemotePrintService> mActiveServices = |
| new ArrayMap<ComponentName, RemotePrintService>(); |
| |
| private final List<PrintServiceInfo> mInstalledServices = |
| new ArrayList<PrintServiceInfo>(); |
| |
| private final Set<ComponentName> mDisabledServices = |
| new ArraySet<ComponentName>(); |
| |
| private final PrintJobForAppCache mPrintJobForAppCache = |
| new PrintJobForAppCache(); |
| |
| private final Object mLock; |
| |
| private final Context mContext; |
| |
| private final int mUserId; |
| |
| private final RemotePrintSpooler mSpooler; |
| |
| private final Handler mHandler; |
| |
| private PrinterDiscoverySessionMediator mPrinterDiscoverySession; |
| |
| private List<PrintJobStateChangeListenerRecord> mPrintJobStateChangeListenerRecords; |
| |
| private List<ListenerRecord<IPrintServicesChangeListener>> mPrintServicesChangeListenerRecords; |
| |
| private List<ListenerRecord<IRecommendationsChangeListener>> |
| mPrintServiceRecommendationsChangeListenerRecords; |
| |
| private boolean mDestroyed; |
| |
| /** Currently known list of print service recommendations */ |
| private List<RecommendationInfo> mPrintServiceRecommendations; |
| |
| /** |
| * Connection to the service updating the {@link #mPrintServiceRecommendations print service |
| * recommendations}. |
| */ |
| private RemotePrintServiceRecommendationService mPrintServiceRecommendationsService; |
| |
| public UserState(Context context, int userId, Object lock, boolean lowPriority) { |
| mContext = context; |
| mUserId = userId; |
| mLock = lock; |
| mSpooler = new RemotePrintSpooler(context, userId, lowPriority, this); |
| mHandler = new UserStateHandler(context.getMainLooper()); |
| |
| synchronized (mLock) { |
| readInstalledPrintServicesLocked(); |
| upgradePersistentStateIfNeeded(); |
| readDisabledPrintServicesLocked(); |
| |
| // Some print services might have gotten installed before the User State came up |
| prunePrintServices(); |
| |
| onConfigurationChangedLocked(); |
| } |
| } |
| |
| public void increasePriority() { |
| mSpooler.increasePriority(); |
| } |
| |
| @Override |
| public void onPrintJobQueued(PrintJobInfo printJob) { |
| final RemotePrintService service; |
| synchronized (mLock) { |
| throwIfDestroyedLocked(); |
| ComponentName printServiceName = printJob.getPrinterId().getServiceName(); |
| service = mActiveServices.get(printServiceName); |
| } |
| if (service != null) { |
| service.onPrintJobQueued(printJob); |
| } else { |
| // The service for the job is no longer enabled, so just |
| // fail the job with the appropriate message. |
| mSpooler.setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED, |
| mContext.getString(R.string.reason_service_unavailable)); |
| } |
| } |
| |
| @Override |
| public void onAllPrintJobsForServiceHandled(ComponentName printService) { |
| final RemotePrintService service; |
| synchronized (mLock) { |
| throwIfDestroyedLocked(); |
| service = mActiveServices.get(printService); |
| } |
| if (service != null) { |
| service.onAllPrintJobsHandled(); |
| } |
| } |
| |
| public void removeObsoletePrintJobs() { |
| mSpooler.removeObsoletePrintJobs(); |
| } |
| |
| @SuppressWarnings("deprecation") |
| public Bundle print(@NonNull String printJobName, @NonNull IPrintDocumentAdapter adapter, |
| @Nullable PrintAttributes attributes, @NonNull String packageName, int appId) { |
| // Create print job place holder. |
| final PrintJobInfo printJob = new PrintJobInfo(); |
| printJob.setId(new PrintJobId()); |
| printJob.setAppId(appId); |
| printJob.setLabel(printJobName); |
| printJob.setAttributes(attributes); |
| printJob.setState(PrintJobInfo.STATE_CREATED); |
| printJob.setCopies(1); |
| printJob.setCreationTime(System.currentTimeMillis()); |
| |
| // Track this job so we can forget it when the creator dies. |
| if (!mPrintJobForAppCache.onPrintJobCreated(adapter.asBinder(), appId, |
| printJob)) { |
| // Not adding a print job means the client is dead - done. |
| return null; |
| } |
| |
| // Spin the spooler to add the job and show the config UI. |
| new AsyncTask<Void, Void, Void>() { |
| @Override |
| protected Void doInBackground(Void... params) { |
| mSpooler.createPrintJob(printJob); |
| return null; |
| } |
| }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); |
| |
| final long identity = Binder.clearCallingIdentity(); |
| try { |
| Intent intent = new Intent(PrintManager.ACTION_PRINT_DIALOG); |
| intent.setData(Uri.fromParts("printjob", printJob.getId().flattenToString(), null)); |
| intent.putExtra(PrintManager.EXTRA_PRINT_DOCUMENT_ADAPTER, adapter.asBinder()); |
| intent.putExtra(PrintManager.EXTRA_PRINT_JOB, printJob); |
| intent.putExtra(DocumentsContract.EXTRA_PACKAGE_NAME, packageName); |
| |
| IntentSender intentSender = PendingIntent.getActivityAsUser( |
| mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT |
| | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE, |
| null, new UserHandle(mUserId)) .getIntentSender(); |
| |
| Bundle result = new Bundle(); |
| result.putParcelable(PrintManager.EXTRA_PRINT_JOB, printJob); |
| result.putParcelable(PrintManager.EXTRA_PRINT_DIALOG_INTENT, intentSender); |
| |
| return result; |
| } finally { |
| Binder.restoreCallingIdentity(identity); |
| } |
| } |
| |
| public List<PrintJobInfo> getPrintJobInfos(int appId) { |
| List<PrintJobInfo> cachedPrintJobs = mPrintJobForAppCache.getPrintJobs(appId); |
| // Note that the print spooler is not storing print jobs that |
| // are in a terminal state as it is non-trivial to properly update |
| // the spooler state for when to forget print jobs in terminal state. |
| // Therefore, we fuse the cached print jobs for running apps (some |
| // jobs are in a terminal state) with the ones that the print |
| // spooler knows about (some jobs are being processed). |
| ArrayMap<PrintJobId, PrintJobInfo> result = |
| new ArrayMap<PrintJobId, PrintJobInfo>(); |
| |
| // Add the cached print jobs for running apps. |
| final int cachedPrintJobCount = cachedPrintJobs.size(); |
| for (int i = 0; i < cachedPrintJobCount; i++) { |
| PrintJobInfo cachedPrintJob = cachedPrintJobs.get(i); |
| result.put(cachedPrintJob.getId(), cachedPrintJob); |
| // Strip out the tag and the advanced print options. |
| // They are visible only to print services. |
| cachedPrintJob.setTag(null); |
| cachedPrintJob.setAdvancedOptions(null); |
| } |
| |
| // Add everything else the spooler knows about. |
| List<PrintJobInfo> printJobs = mSpooler.getPrintJobInfos(null, |
| PrintJobInfo.STATE_ANY, appId); |
| if (printJobs != null) { |
| final int printJobCount = printJobs.size(); |
| for (int i = 0; i < printJobCount; i++) { |
| PrintJobInfo printJob = printJobs.get(i); |
| result.put(printJob.getId(), printJob); |
| // Strip out the tag and the advanced print options. |
| // They are visible only to print services. |
| printJob.setTag(null); |
| printJob.setAdvancedOptions(null); |
| } |
| } |
| |
| return new ArrayList<PrintJobInfo>(result.values()); |
| } |
| |
| public PrintJobInfo getPrintJobInfo(@NonNull PrintJobId printJobId, int appId) { |
| PrintJobInfo printJob = mPrintJobForAppCache.getPrintJob(printJobId, appId); |
| if (printJob == null) { |
| printJob = mSpooler.getPrintJobInfo(printJobId, appId); |
| } |
| if (printJob != null) { |
| // Strip out the tag and the advanced print options. |
| // They are visible only to print services. |
| printJob.setTag(null); |
| printJob.setAdvancedOptions(null); |
| } |
| return printJob; |
| } |
| |
| /** |
| * Get the custom icon for a printer. If the icon is not cached, the icon is |
| * requested asynchronously. Once it is available the printer is updated. |
| * |
| * @param printerId the id of the printer the icon should be loaded for |
| * @return the custom icon to be used for the printer or null if the icon is |
| * not yet available |
| * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon() |
| */ |
| public @Nullable Icon getCustomPrinterIcon(@NonNull PrinterId printerId) { |
| Icon icon = mSpooler.getCustomPrinterIcon(printerId); |
| |
| if (icon == null) { |
| RemotePrintService service = mActiveServices.get(printerId.getServiceName()); |
| if (service != null) { |
| service.requestCustomPrinterIcon(printerId); |
| } |
| } |
| |
| return icon; |
| } |
| |
| public void cancelPrintJob(@NonNull PrintJobId printJobId, int appId) { |
| PrintJobInfo printJobInfo = mSpooler.getPrintJobInfo(printJobId, appId); |
| if (printJobInfo == null) { |
| return; |
| } |
| |
| // Take a note that we are trying to cancel the job. |
| mSpooler.setPrintJobCancelling(printJobId, true); |
| |
| if (printJobInfo.getState() != PrintJobInfo.STATE_FAILED) { |
| PrinterId printerId = printJobInfo.getPrinterId(); |
| |
| if (printerId != null) { |
| ComponentName printServiceName = printerId.getServiceName(); |
| RemotePrintService printService = null; |
| synchronized (mLock) { |
| printService = mActiveServices.get(printServiceName); |
| } |
| if (printService == null) { |
| return; |
| } |
| printService.onRequestCancelPrintJob(printJobInfo); |
| } |
| } else { |
| // If the print job is failed we do not need cooperation |
| // from the print service. |
| mSpooler.setPrintJobState(printJobId, PrintJobInfo.STATE_CANCELED, null); |
| } |
| } |
| |
| public void restartPrintJob(@NonNull PrintJobId printJobId, int appId) { |
| PrintJobInfo printJobInfo = getPrintJobInfo(printJobId, appId); |
| if (printJobInfo == null || printJobInfo.getState() != PrintJobInfo.STATE_FAILED) { |
| return; |
| } |
| mSpooler.setPrintJobState(printJobId, PrintJobInfo.STATE_QUEUED, null); |
| } |
| |
| public @Nullable List<PrintServiceInfo> getPrintServices(int selectionFlags) { |
| synchronized (mLock) { |
| List<PrintServiceInfo> selectedServices = null; |
| final int installedServiceCount = mInstalledServices.size(); |
| for (int i = 0; i < installedServiceCount; i++) { |
| PrintServiceInfo installedService = mInstalledServices.get(i); |
| |
| ComponentName componentName = new ComponentName( |
| installedService.getResolveInfo().serviceInfo.packageName, |
| installedService.getResolveInfo().serviceInfo.name); |
| |
| // Update isEnabled under the same lock the final returned list is created |
| installedService.setIsEnabled(mActiveServices.containsKey(componentName)); |
| |
| if (installedService.isEnabled()) { |
| if ((selectionFlags & PrintManager.ENABLED_SERVICES) == 0) { |
| continue; |
| } |
| } else { |
| if ((selectionFlags & PrintManager.DISABLED_SERVICES) == 0) { |
| continue; |
| } |
| } |
| |
| if (selectedServices == null) { |
| selectedServices = new ArrayList<>(); |
| } |
| selectedServices.add(installedService); |
| } |
| return selectedServices; |
| } |
| } |
| |
| public void setPrintServiceEnabled(@NonNull ComponentName serviceName, boolean isEnabled) { |
| synchronized (mLock) { |
| boolean isChanged = false; |
| if (isEnabled) { |
| isChanged = mDisabledServices.remove(serviceName); |
| } else { |
| // Make sure to only disable services that are currently installed |
| final int numServices = mInstalledServices.size(); |
| for (int i = 0; i < numServices; i++) { |
| PrintServiceInfo service = mInstalledServices.get(i); |
| |
| if (service.getComponentName().equals(serviceName)) { |
| mDisabledServices.add(serviceName); |
| isChanged = true; |
| break; |
| } |
| } |
| } |
| |
| if (isChanged) { |
| writeDisabledPrintServicesLocked(mDisabledServices); |
| |
| onConfigurationChangedLocked(); |
| } |
| } |
| } |
| |
| /** |
| * @return The currently known print service recommendations |
| */ |
| public @Nullable List<RecommendationInfo> getPrintServiceRecommendations() { |
| return mPrintServiceRecommendations; |
| } |
| |
| public void createPrinterDiscoverySession(@NonNull IPrinterDiscoveryObserver observer) { |
| synchronized (mLock) { |
| throwIfDestroyedLocked(); |
| |
| if (mPrinterDiscoverySession == null) { |
| mSpooler.clearCustomPrinterIconCache(); |
| |
| // If we do not have a session, tell all service to create one. |
| mPrinterDiscoverySession = new PrinterDiscoverySessionMediator(mContext) { |
| @Override |
| public void onDestroyed() { |
| mPrinterDiscoverySession = null; |
| } |
| }; |
| // Add the observer to the brand new session. |
| mPrinterDiscoverySession.addObserverLocked(observer); |
| } else { |
| // If services have created session, just add the observer. |
| mPrinterDiscoverySession.addObserverLocked(observer); |
| } |
| } |
| } |
| |
| public void destroyPrinterDiscoverySession(@NonNull IPrinterDiscoveryObserver observer) { |
| synchronized (mLock) { |
| // Already destroyed - nothing to do. |
| if (mPrinterDiscoverySession == null) { |
| return; |
| } |
| // Remove this observer. |
| mPrinterDiscoverySession.removeObserverLocked(observer); |
| } |
| } |
| |
| public void startPrinterDiscovery(@NonNull IPrinterDiscoveryObserver observer, |
| @Nullable List<PrinterId> printerIds) { |
| synchronized (mLock) { |
| throwIfDestroyedLocked(); |
| |
| // No session - nothing to do. |
| if (mPrinterDiscoverySession == null) { |
| return; |
| } |
| // Kick of discovery. |
| mPrinterDiscoverySession.startPrinterDiscoveryLocked(observer, |
| printerIds); |
| } |
| } |
| |
| public void stopPrinterDiscovery(@NonNull IPrinterDiscoveryObserver observer) { |
| synchronized (mLock) { |
| throwIfDestroyedLocked(); |
| |
| // No session - nothing to do. |
| if (mPrinterDiscoverySession == null) { |
| return; |
| } |
| // Kick of discovery. |
| mPrinterDiscoverySession.stopPrinterDiscoveryLocked(observer); |
| } |
| } |
| |
| public void validatePrinters(@NonNull List<PrinterId> printerIds) { |
| synchronized (mLock) { |
| throwIfDestroyedLocked(); |
| // No services - nothing to do. |
| if (mActiveServices.isEmpty()) { |
| return; |
| } |
| // No session - nothing to do. |
| if (mPrinterDiscoverySession == null) { |
| return; |
| } |
| // Request an updated. |
| mPrinterDiscoverySession.validatePrintersLocked(printerIds); |
| } |
| } |
| |
| public void startPrinterStateTracking(@NonNull PrinterId printerId) { |
| synchronized (mLock) { |
| throwIfDestroyedLocked(); |
| // No services - nothing to do. |
| if (mActiveServices.isEmpty()) { |
| return; |
| } |
| // No session - nothing to do. |
| if (mPrinterDiscoverySession == null) { |
| return; |
| } |
| // Request start tracking the printer. |
| mPrinterDiscoverySession.startPrinterStateTrackingLocked(printerId); |
| } |
| } |
| |
| public void stopPrinterStateTracking(PrinterId printerId) { |
| synchronized (mLock) { |
| throwIfDestroyedLocked(); |
| // No services - nothing to do. |
| if (mActiveServices.isEmpty()) { |
| return; |
| } |
| // No session - nothing to do. |
| if (mPrinterDiscoverySession == null) { |
| return; |
| } |
| // Request stop tracking the printer. |
| mPrinterDiscoverySession.stopPrinterStateTrackingLocked(printerId); |
| } |
| } |
| |
| public void addPrintJobStateChangeListener(@NonNull IPrintJobStateChangeListener listener, |
| int appId) throws RemoteException { |
| synchronized (mLock) { |
| throwIfDestroyedLocked(); |
| if (mPrintJobStateChangeListenerRecords == null) { |
| mPrintJobStateChangeListenerRecords = |
| new ArrayList<PrintJobStateChangeListenerRecord>(); |
| } |
| mPrintJobStateChangeListenerRecords.add( |
| new PrintJobStateChangeListenerRecord(listener, appId) { |
| @Override |
| public void onBinderDied() { |
| synchronized (mLock) { |
| if (mPrintJobStateChangeListenerRecords != null) { |
| mPrintJobStateChangeListenerRecords.remove(this); |
| } |
| } |
| } |
| }); |
| } |
| } |
| |
| public void removePrintJobStateChangeListener(@NonNull IPrintJobStateChangeListener listener) { |
| synchronized (mLock) { |
| throwIfDestroyedLocked(); |
| if (mPrintJobStateChangeListenerRecords == null) { |
| return; |
| } |
| final int recordCount = mPrintJobStateChangeListenerRecords.size(); |
| for (int i = 0; i < recordCount; i++) { |
| PrintJobStateChangeListenerRecord record = |
| mPrintJobStateChangeListenerRecords.get(i); |
| if (record.listener.asBinder().equals(listener.asBinder())) { |
| mPrintJobStateChangeListenerRecords.remove(i); |
| break; |
| } |
| } |
| if (mPrintJobStateChangeListenerRecords.isEmpty()) { |
| mPrintJobStateChangeListenerRecords = null; |
| } |
| } |
| } |
| |
| public void addPrintServicesChangeListener(@NonNull IPrintServicesChangeListener listener) |
| throws RemoteException { |
| synchronized (mLock) { |
| throwIfDestroyedLocked(); |
| if (mPrintServicesChangeListenerRecords == null) { |
| mPrintServicesChangeListenerRecords = new ArrayList<>(); |
| } |
| mPrintServicesChangeListenerRecords.add( |
| new ListenerRecord<IPrintServicesChangeListener>(listener) { |
| @Override |
| public void onBinderDied() { |
| synchronized (mLock) { |
| if (mPrintServicesChangeListenerRecords != null) { |
| mPrintServicesChangeListenerRecords.remove(this); |
| } |
| } |
| } |
| }); |
| } |
| } |
| |
| public void removePrintServicesChangeListener(@NonNull IPrintServicesChangeListener listener) { |
| synchronized (mLock) { |
| throwIfDestroyedLocked(); |
| if (mPrintServicesChangeListenerRecords == null) { |
| return; |
| } |
| final int recordCount = mPrintServicesChangeListenerRecords.size(); |
| for (int i = 0; i < recordCount; i++) { |
| ListenerRecord<IPrintServicesChangeListener> record = |
| mPrintServicesChangeListenerRecords.get(i); |
| if (record.listener.asBinder().equals(listener.asBinder())) { |
| mPrintServicesChangeListenerRecords.remove(i); |
| break; |
| } |
| } |
| if (mPrintServicesChangeListenerRecords.isEmpty()) { |
| mPrintServicesChangeListenerRecords = null; |
| } |
| } |
| } |
| |
| public void addPrintServiceRecommendationsChangeListener( |
| @NonNull IRecommendationsChangeListener listener) throws RemoteException { |
| synchronized (mLock) { |
| throwIfDestroyedLocked(); |
| if (mPrintServiceRecommendationsChangeListenerRecords == null) { |
| mPrintServiceRecommendationsChangeListenerRecords = new ArrayList<>(); |
| |
| mPrintServiceRecommendationsService = |
| new RemotePrintServiceRecommendationService(mContext, |
| UserHandle.getUserHandleForUid(mUserId), this); |
| } |
| mPrintServiceRecommendationsChangeListenerRecords.add( |
| new ListenerRecord<IRecommendationsChangeListener>(listener) { |
| @Override |
| public void onBinderDied() { |
| synchronized (mLock) { |
| if (mPrintServiceRecommendationsChangeListenerRecords != null) { |
| mPrintServiceRecommendationsChangeListenerRecords.remove(this); |
| } |
| } |
| } |
| }); |
| } |
| } |
| |
| public void removePrintServiceRecommendationsChangeListener( |
| @NonNull IRecommendationsChangeListener listener) { |
| synchronized (mLock) { |
| throwIfDestroyedLocked(); |
| if (mPrintServiceRecommendationsChangeListenerRecords == null) { |
| return; |
| } |
| final int recordCount = mPrintServiceRecommendationsChangeListenerRecords.size(); |
| for (int i = 0; i < recordCount; i++) { |
| ListenerRecord<IRecommendationsChangeListener> record = |
| mPrintServiceRecommendationsChangeListenerRecords.get(i); |
| if (record.listener.asBinder().equals(listener.asBinder())) { |
| mPrintServiceRecommendationsChangeListenerRecords.remove(i); |
| break; |
| } |
| } |
| if (mPrintServiceRecommendationsChangeListenerRecords.isEmpty()) { |
| mPrintServiceRecommendationsChangeListenerRecords = null; |
| |
| mPrintServiceRecommendations = null; |
| |
| mPrintServiceRecommendationsService.close(); |
| mPrintServiceRecommendationsService = null; |
| } |
| } |
| } |
| |
| @Override |
| public void onPrintJobStateChanged(PrintJobInfo printJob) { |
| mPrintJobForAppCache.onPrintJobStateChanged(printJob); |
| mHandler.obtainMessage(UserStateHandler.MSG_DISPATCH_PRINT_JOB_STATE_CHANGED, |
| printJob.getAppId(), 0, printJob.getId()).sendToTarget(); |
| } |
| |
| public void onPrintServicesChanged() { |
| mHandler.obtainMessage(UserStateHandler.MSG_DISPATCH_PRINT_SERVICES_CHANGED).sendToTarget(); |
| } |
| |
| @Override |
| public void onPrintServiceRecommendationsUpdated(List<RecommendationInfo> recommendations) { |
| mHandler.obtainMessage(UserStateHandler.MSG_DISPATCH_PRINT_SERVICES_RECOMMENDATIONS_UPDATED, |
| 0, 0, recommendations).sendToTarget(); |
| } |
| |
| @Override |
| public void onPrintersAdded(List<PrinterInfo> printers) { |
| synchronized (mLock) { |
| throwIfDestroyedLocked(); |
| // No services - nothing to do. |
| if (mActiveServices.isEmpty()) { |
| return; |
| } |
| // No session - nothing to do. |
| if (mPrinterDiscoverySession == null) { |
| return; |
| } |
| mPrinterDiscoverySession.onPrintersAddedLocked(printers); |
| } |
| } |
| |
| @Override |
| public void onPrintersRemoved(List<PrinterId> printerIds) { |
| synchronized (mLock) { |
| throwIfDestroyedLocked(); |
| // No services - nothing to do. |
| if (mActiveServices.isEmpty()) { |
| return; |
| } |
| // No session - nothing to do. |
| if (mPrinterDiscoverySession == null) { |
| return; |
| } |
| mPrinterDiscoverySession.onPrintersRemovedLocked(printerIds); |
| } |
| } |
| |
| @Override |
| public void onCustomPrinterIconLoaded(PrinterId printerId, Icon icon) { |
| synchronized (mLock) { |
| throwIfDestroyedLocked(); |
| |
| // No session - nothing to do. |
| if (mPrinterDiscoverySession == null) { |
| return; |
| } |
| mSpooler.onCustomPrinterIconLoaded(printerId, icon); |
| mPrinterDiscoverySession.onCustomPrinterIconLoadedLocked(printerId); |
| } |
| } |
| |
| @Override |
| public void onServiceDied(RemotePrintService service) { |
| synchronized (mLock) { |
| throwIfDestroyedLocked(); |
| // No services - nothing to do. |
| if (mActiveServices.isEmpty()) { |
| return; |
| } |
| // Fail all print jobs. |
| failActivePrintJobsForService(service.getComponentName()); |
| service.onAllPrintJobsHandled(); |
| // No session - nothing to do. |
| if (mPrinterDiscoverySession == null) { |
| return; |
| } |
| mPrinterDiscoverySession.onServiceDiedLocked(service); |
| } |
| } |
| |
| public void updateIfNeededLocked() { |
| throwIfDestroyedLocked(); |
| if (readConfigurationLocked()) { |
| onConfigurationChangedLocked(); |
| } |
| } |
| |
| public void destroyLocked() { |
| throwIfDestroyedLocked(); |
| mSpooler.destroy(); |
| for (RemotePrintService service : mActiveServices.values()) { |
| service.destroy(); |
| } |
| mActiveServices.clear(); |
| mInstalledServices.clear(); |
| mDisabledServices.clear(); |
| if (mPrinterDiscoverySession != null) { |
| mPrinterDiscoverySession.destroyLocked(); |
| mPrinterDiscoverySession = null; |
| } |
| mDestroyed = true; |
| } |
| |
| public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String prefix) { |
| pw.append(prefix).append("user state ").append(String.valueOf(mUserId)).append(":"); |
| pw.println(); |
| |
| String tab = " "; |
| |
| pw.append(prefix).append(tab).append("installed services:").println(); |
| final int installedServiceCount = mInstalledServices.size(); |
| for (int i = 0; i < installedServiceCount; i++) { |
| PrintServiceInfo installedService = mInstalledServices.get(i); |
| String installedServicePrefix = prefix + tab + tab; |
| pw.append(installedServicePrefix).append("service:").println(); |
| ResolveInfo resolveInfo = installedService.getResolveInfo(); |
| ComponentName componentName = new ComponentName( |
| resolveInfo.serviceInfo.packageName, |
| resolveInfo.serviceInfo.name); |
| pw.append(installedServicePrefix).append(tab).append("componentName=") |
| .append(componentName.flattenToString()).println(); |
| pw.append(installedServicePrefix).append(tab).append("settingsActivity=") |
| .append(installedService.getSettingsActivityName()).println(); |
| pw.append(installedServicePrefix).append(tab).append("addPrintersActivity=") |
| .append(installedService.getAddPrintersActivityName()).println(); |
| pw.append(installedServicePrefix).append(tab).append("avancedOptionsActivity=") |
| .append(installedService.getAdvancedOptionsActivityName()).println(); |
| } |
| |
| pw.append(prefix).append(tab).append("disabled services:").println(); |
| for (ComponentName disabledService : mDisabledServices) { |
| String disabledServicePrefix = prefix + tab + tab; |
| pw.append(disabledServicePrefix).append("service:").println(); |
| pw.append(disabledServicePrefix).append(tab).append("componentName=") |
| .append(disabledService.flattenToString()); |
| pw.println(); |
| } |
| |
| pw.append(prefix).append(tab).append("active services:").println(); |
| final int activeServiceCount = mActiveServices.size(); |
| for (int i = 0; i < activeServiceCount; i++) { |
| RemotePrintService activeService = mActiveServices.valueAt(i); |
| activeService.dump(pw, prefix + tab + tab); |
| pw.println(); |
| } |
| |
| pw.append(prefix).append(tab).append("cached print jobs:").println(); |
| mPrintJobForAppCache.dump(pw, prefix + tab + tab); |
| |
| pw.append(prefix).append(tab).append("discovery mediator:").println(); |
| if (mPrinterDiscoverySession != null) { |
| mPrinterDiscoverySession.dump(pw, prefix + tab + tab); |
| } |
| |
| pw.append(prefix).append(tab).append("print spooler:").println(); |
| mSpooler.dump(fd, pw, prefix + tab + tab); |
| pw.println(); |
| } |
| |
| private boolean readConfigurationLocked() { |
| boolean somethingChanged = false; |
| somethingChanged |= readInstalledPrintServicesLocked(); |
| somethingChanged |= readDisabledPrintServicesLocked(); |
| return somethingChanged; |
| } |
| |
| private boolean readInstalledPrintServicesLocked() { |
| Set<PrintServiceInfo> tempPrintServices = new HashSet<PrintServiceInfo>(); |
| |
| List<ResolveInfo> installedServices = mContext.getPackageManager() |
| .queryIntentServicesAsUser(mQueryIntent, |
| GET_SERVICES | GET_META_DATA | MATCH_DEBUG_TRIAGED_MISSING, mUserId); |
| |
| final int installedCount = installedServices.size(); |
| for (int i = 0, count = installedCount; i < count; i++) { |
| ResolveInfo installedService = installedServices.get(i); |
| if (!android.Manifest.permission.BIND_PRINT_SERVICE.equals( |
| installedService.serviceInfo.permission)) { |
| ComponentName serviceName = new ComponentName( |
| installedService.serviceInfo.packageName, |
| installedService.serviceInfo.name); |
| Slog.w(LOG_TAG, "Skipping print service " |
| + serviceName.flattenToShortString() |
| + " since it does not require permission " |
| + android.Manifest.permission.BIND_PRINT_SERVICE); |
| continue; |
| } |
| tempPrintServices.add(PrintServiceInfo.create(installedService, mContext)); |
| } |
| |
| boolean someServiceChanged = false; |
| |
| if (tempPrintServices.size() != mInstalledServices.size()) { |
| someServiceChanged = true; |
| } else { |
| for (PrintServiceInfo newService: tempPrintServices) { |
| final int oldServiceIndex = mInstalledServices.indexOf(newService); |
| if (oldServiceIndex < 0) { |
| someServiceChanged = true; |
| break; |
| } |
| // PrintServiceInfo#equals compares only the id not all members, |
| // so we are also comparing the members coming from meta-data. |
| PrintServiceInfo oldService = mInstalledServices.get(oldServiceIndex); |
| if (!TextUtils.equals(oldService.getAddPrintersActivityName(), |
| newService.getAddPrintersActivityName()) |
| || !TextUtils.equals(oldService.getAdvancedOptionsActivityName(), |
| newService.getAdvancedOptionsActivityName()) |
| || !TextUtils.equals(oldService.getSettingsActivityName(), |
| newService.getSettingsActivityName())) { |
| someServiceChanged = true; |
| break; |
| } |
| } |
| } |
| |
| if (someServiceChanged) { |
| mInstalledServices.clear(); |
| mInstalledServices.addAll(tempPrintServices); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Update persistent state from a previous version of Android. |
| */ |
| private void upgradePersistentStateIfNeeded() { |
| String enabledSettingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(), |
| Settings.Secure.ENABLED_PRINT_SERVICES, mUserId); |
| |
| // Pre N we store the enabled services, in N and later we store the disabled services. |
| // Hence if enabledSettingValue is still set, we need to upgrade. |
| if (enabledSettingValue != null) { |
| Set<ComponentName> enabledServiceNameSet = new HashSet<ComponentName>(); |
| readPrintServicesFromSettingLocked(Settings.Secure.ENABLED_PRINT_SERVICES, |
| enabledServiceNameSet); |
| |
| ArraySet<ComponentName> disabledServices = new ArraySet<>(); |
| final int numInstalledServices = mInstalledServices.size(); |
| for (int i = 0; i < numInstalledServices; i++) { |
| ComponentName serviceName = mInstalledServices.get(i).getComponentName(); |
| if (!enabledServiceNameSet.contains(serviceName)) { |
| disabledServices.add(serviceName); |
| } |
| } |
| |
| writeDisabledPrintServicesLocked(disabledServices); |
| |
| // We won't needed ENABLED_PRINT_SERVICES anymore, set to null to prevent upgrade to run |
| // again. |
| Settings.Secure.putStringForUser(mContext.getContentResolver(), |
| Settings.Secure.ENABLED_PRINT_SERVICES, null, mUserId); |
| } |
| } |
| |
| /** |
| * Read the set of disabled print services from the secure settings. |
| * |
| * @return true if the state changed. |
| */ |
| private boolean readDisabledPrintServicesLocked() { |
| Set<ComponentName> tempDisabledServiceNameSet = new HashSet<ComponentName>(); |
| readPrintServicesFromSettingLocked(Settings.Secure.DISABLED_PRINT_SERVICES, |
| tempDisabledServiceNameSet); |
| if (!tempDisabledServiceNameSet.equals(mDisabledServices)) { |
| mDisabledServices.clear(); |
| mDisabledServices.addAll(tempDisabledServiceNameSet); |
| return true; |
| } |
| return false; |
| } |
| |
| private void readPrintServicesFromSettingLocked(String setting, |
| Set<ComponentName> outServiceNames) { |
| String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(), |
| setting, mUserId); |
| if (!TextUtils.isEmpty(settingValue)) { |
| TextUtils.SimpleStringSplitter splitter = mStringColonSplitter; |
| splitter.setString(settingValue); |
| while (splitter.hasNext()) { |
| String string = splitter.next(); |
| if (TextUtils.isEmpty(string)) { |
| continue; |
| } |
| ComponentName componentName = ComponentName.unflattenFromString(string); |
| if (componentName != null) { |
| outServiceNames.add(componentName); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Persist the disabled print services to the secure settings. |
| */ |
| private void writeDisabledPrintServicesLocked(Set<ComponentName> disabledServices) { |
| StringBuilder builder = new StringBuilder(); |
| for (ComponentName componentName : disabledServices) { |
| if (builder.length() > 0) { |
| builder.append(COMPONENT_NAME_SEPARATOR); |
| } |
| builder.append(componentName.flattenToShortString()); |
| } |
| Settings.Secure.putStringForUser(mContext.getContentResolver(), |
| Settings.Secure.DISABLED_PRINT_SERVICES, builder.toString(), mUserId); |
| } |
| |
| /** |
| * Get the {@link ComponentName names} of the installed print services |
| * |
| * @return The names of the installed print services |
| */ |
| private ArrayList<ComponentName> getInstalledComponents() { |
| ArrayList<ComponentName> installedComponents = new ArrayList<ComponentName>(); |
| |
| final int installedCount = mInstalledServices.size(); |
| for (int i = 0; i < installedCount; i++) { |
| ResolveInfo resolveInfo = mInstalledServices.get(i).getResolveInfo(); |
| ComponentName serviceName = new ComponentName(resolveInfo.serviceInfo.packageName, |
| resolveInfo.serviceInfo.name); |
| |
| installedComponents.add(serviceName); |
| } |
| |
| return installedComponents; |
| } |
| |
| /** |
| * Prune persistent state if a print service was uninstalled |
| */ |
| public void prunePrintServices() { |
| synchronized (mLock) { |
| ArrayList<ComponentName> installedComponents = getInstalledComponents(); |
| |
| // Remove unnecessary entries from persistent state "disabled services" |
| boolean disabledServicesUninstalled = mDisabledServices.retainAll(installedComponents); |
| if (disabledServicesUninstalled) { |
| writeDisabledPrintServicesLocked(mDisabledServices); |
| } |
| |
| // Remove unnecessary entries from persistent state "approved services" |
| mSpooler.pruneApprovedPrintServices(installedComponents); |
| } |
| } |
| |
| private void onConfigurationChangedLocked() { |
| ArrayList<ComponentName> installedComponents = getInstalledComponents(); |
| |
| final int installedCount = installedComponents.size(); |
| for (int i = 0; i < installedCount; i++) { |
| ComponentName serviceName = installedComponents.get(i); |
| |
| if (!mDisabledServices.contains(serviceName)) { |
| if (!mActiveServices.containsKey(serviceName)) { |
| RemotePrintService service = new RemotePrintService( |
| mContext, serviceName, mUserId, mSpooler, this); |
| addServiceLocked(service); |
| } |
| } else { |
| RemotePrintService service = mActiveServices.remove(serviceName); |
| if (service != null) { |
| removeServiceLocked(service); |
| } |
| } |
| } |
| |
| Iterator<Map.Entry<ComponentName, RemotePrintService>> iterator = |
| mActiveServices.entrySet().iterator(); |
| while (iterator.hasNext()) { |
| Map.Entry<ComponentName, RemotePrintService> entry = iterator.next(); |
| ComponentName serviceName = entry.getKey(); |
| RemotePrintService service = entry.getValue(); |
| if (!installedComponents.contains(serviceName)) { |
| removeServiceLocked(service); |
| iterator.remove(); |
| } |
| } |
| |
| onPrintServicesChanged(); |
| } |
| |
| private void addServiceLocked(RemotePrintService service) { |
| mActiveServices.put(service.getComponentName(), service); |
| if (mPrinterDiscoverySession != null) { |
| mPrinterDiscoverySession.onServiceAddedLocked(service); |
| } |
| } |
| |
| private void removeServiceLocked(RemotePrintService service) { |
| // Fail all print jobs. |
| failActivePrintJobsForService(service.getComponentName()); |
| // If discovery is in progress, tear down the service. |
| if (mPrinterDiscoverySession != null) { |
| mPrinterDiscoverySession.onServiceRemovedLocked(service); |
| } else { |
| // Otherwise, just destroy it. |
| service.destroy(); |
| } |
| } |
| |
| private void failActivePrintJobsForService(final ComponentName serviceName) { |
| // Makes sure all active print jobs are failed since the service |
| // just died. Do this off the main thread since we do to allow |
| // calls into the spooler on the main thread. |
| if (Looper.getMainLooper().isCurrentThread()) { |
| BackgroundThread.getHandler().post(new Runnable() { |
| @Override |
| public void run() { |
| failScheduledPrintJobsForServiceInternal(serviceName); |
| } |
| }); |
| } else { |
| failScheduledPrintJobsForServiceInternal(serviceName); |
| } |
| } |
| |
| private void failScheduledPrintJobsForServiceInternal(ComponentName serviceName) { |
| List<PrintJobInfo> printJobs = mSpooler.getPrintJobInfos(serviceName, |
| PrintJobInfo.STATE_ANY_SCHEDULED, PrintManager.APP_ID_ANY); |
| if (printJobs == null) { |
| return; |
| } |
| final long identity = Binder.clearCallingIdentity(); |
| try { |
| final int printJobCount = printJobs.size(); |
| for (int i = 0; i < printJobCount; i++) { |
| PrintJobInfo printJob = printJobs.get(i); |
| mSpooler.setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED, |
| mContext.getString(R.string.reason_service_unavailable)); |
| } |
| } finally { |
| Binder.restoreCallingIdentity(identity); |
| } |
| } |
| |
| private void throwIfDestroyedLocked() { |
| if (mDestroyed) { |
| throw new IllegalStateException("Cannot interact with a destroyed instance."); |
| } |
| } |
| |
| private void handleDispatchPrintJobStateChanged(PrintJobId printJobId, int appId) { |
| final List<PrintJobStateChangeListenerRecord> records; |
| synchronized (mLock) { |
| if (mPrintJobStateChangeListenerRecords == null) { |
| return; |
| } |
| records = new ArrayList<PrintJobStateChangeListenerRecord>( |
| mPrintJobStateChangeListenerRecords); |
| } |
| final int recordCount = records.size(); |
| for (int i = 0; i < recordCount; i++) { |
| PrintJobStateChangeListenerRecord record = records.get(i); |
| if (record.appId == PrintManager.APP_ID_ANY |
| || record.appId == appId) |
| try { |
| record.listener.onPrintJobStateChanged(printJobId); |
| } catch (RemoteException re) { |
| Log.e(LOG_TAG, "Error notifying for print job state change", re); |
| } |
| } |
| } |
| |
| private void handleDispatchPrintServicesChanged() { |
| final List<ListenerRecord<IPrintServicesChangeListener>> records; |
| synchronized (mLock) { |
| if (mPrintServicesChangeListenerRecords == null) { |
| return; |
| } |
| records = new ArrayList<>(mPrintServicesChangeListenerRecords); |
| } |
| final int recordCount = records.size(); |
| for (int i = 0; i < recordCount; i++) { |
| ListenerRecord<IPrintServicesChangeListener> record = records.get(i); |
| |
| try { |
| record.listener.onPrintServicesChanged();; |
| } catch (RemoteException re) { |
| Log.e(LOG_TAG, "Error notifying for print services change", re); |
| } |
| } |
| } |
| |
| private void handleDispatchPrintServiceRecommendationsUpdated( |
| @Nullable List<RecommendationInfo> recommendations) { |
| final List<ListenerRecord<IRecommendationsChangeListener>> records; |
| synchronized (mLock) { |
| if (mPrintServiceRecommendationsChangeListenerRecords == null) { |
| return; |
| } |
| records = new ArrayList<>(mPrintServiceRecommendationsChangeListenerRecords); |
| |
| mPrintServiceRecommendations = recommendations; |
| } |
| final int recordCount = records.size(); |
| for (int i = 0; i < recordCount; i++) { |
| ListenerRecord<IRecommendationsChangeListener> record = records.get(i); |
| |
| try { |
| record.listener.onRecommendationsChanged(); |
| } catch (RemoteException re) { |
| Log.e(LOG_TAG, "Error notifying for print service recommendations change", re); |
| } |
| } |
| } |
| |
| private final class UserStateHandler extends Handler { |
| public static final int MSG_DISPATCH_PRINT_JOB_STATE_CHANGED = 1; |
| public static final int MSG_DISPATCH_PRINT_SERVICES_CHANGED = 2; |
| public static final int MSG_DISPATCH_PRINT_SERVICES_RECOMMENDATIONS_UPDATED = 3; |
| |
| public UserStateHandler(Looper looper) { |
| super(looper, null, false); |
| } |
| |
| @Override |
| public void handleMessage(Message message) { |
| switch (message.what) { |
| case MSG_DISPATCH_PRINT_JOB_STATE_CHANGED: |
| PrintJobId printJobId = (PrintJobId) message.obj; |
| final int appId = message.arg1; |
| handleDispatchPrintJobStateChanged(printJobId, appId); |
| break; |
| case MSG_DISPATCH_PRINT_SERVICES_CHANGED: |
| handleDispatchPrintServicesChanged(); |
| break; |
| case MSG_DISPATCH_PRINT_SERVICES_RECOMMENDATIONS_UPDATED: |
| handleDispatchPrintServiceRecommendationsUpdated( |
| (List<RecommendationInfo>) message.obj); |
| break; |
| default: |
| // not reached |
| } |
| } |
| } |
| |
| private abstract class PrintJobStateChangeListenerRecord implements DeathRecipient { |
| @NonNull final IPrintJobStateChangeListener listener; |
| final int appId; |
| |
| public PrintJobStateChangeListenerRecord(@NonNull IPrintJobStateChangeListener listener, |
| int appId) throws RemoteException { |
| this.listener = listener; |
| this.appId = appId; |
| listener.asBinder().linkToDeath(this, 0); |
| } |
| |
| @Override |
| public void binderDied() { |
| listener.asBinder().unlinkToDeath(this, 0); |
| onBinderDied(); |
| } |
| |
| public abstract void onBinderDied(); |
| } |
| |
| private abstract class ListenerRecord<T extends IInterface> implements DeathRecipient { |
| @NonNull final T listener; |
| |
| public ListenerRecord(@NonNull T listener) throws RemoteException { |
| this.listener = listener; |
| listener.asBinder().linkToDeath(this, 0); |
| } |
| |
| @Override |
| public void binderDied() { |
| listener.asBinder().unlinkToDeath(this, 0); |
| onBinderDied(); |
| } |
| |
| public abstract void onBinderDied(); |
| } |
| |
| private class PrinterDiscoverySessionMediator { |
| private final ArrayMap<PrinterId, PrinterInfo> mPrinters = |
| new ArrayMap<PrinterId, PrinterInfo>(); |
| |
| private final RemoteCallbackList<IPrinterDiscoveryObserver> mDiscoveryObservers = |
| new RemoteCallbackList<IPrinterDiscoveryObserver>() { |
| @Override |
| public void onCallbackDied(IPrinterDiscoveryObserver observer) { |
| synchronized (mLock) { |
| stopPrinterDiscoveryLocked(observer); |
| removeObserverLocked(observer); |
| } |
| } |
| }; |
| |
| private final List<IBinder> mStartedPrinterDiscoveryTokens = new ArrayList<IBinder>(); |
| |
| private final List<PrinterId> mStateTrackedPrinters = new ArrayList<PrinterId>(); |
| |
| private final Handler mSessionHandler; |
| |
| private boolean mIsDestroyed; |
| |
| public PrinterDiscoverySessionMediator(Context context) { |
| mSessionHandler = new SessionHandler(context.getMainLooper()); |
| // Kick off the session creation. |
| List<RemotePrintService> services = new ArrayList<RemotePrintService>( |
| mActiveServices.values()); |
| mSessionHandler.obtainMessage(SessionHandler |
| .MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION, services) |
| .sendToTarget(); |
| } |
| |
| public void addObserverLocked(@NonNull IPrinterDiscoveryObserver observer) { |
| // Add the observer. |
| mDiscoveryObservers.register(observer); |
| |
| // Bring the added observer up to speed with the printers. |
| if (!mPrinters.isEmpty()) { |
| List<PrinterInfo> printers = new ArrayList<PrinterInfo>(mPrinters.values()); |
| SomeArgs args = SomeArgs.obtain(); |
| args.arg1 = observer; |
| args.arg2 = printers; |
| mSessionHandler.obtainMessage(SessionHandler.MSG_PRINTERS_ADDED, |
| args).sendToTarget(); |
| } |
| } |
| |
| public void removeObserverLocked(@NonNull IPrinterDiscoveryObserver observer) { |
| // Remove the observer. |
| mDiscoveryObservers.unregister(observer); |
| // No one else observing - then kill it. |
| if (mDiscoveryObservers.getRegisteredCallbackCount() == 0) { |
| destroyLocked(); |
| } |
| } |
| |
| public final void startPrinterDiscoveryLocked(@NonNull IPrinterDiscoveryObserver observer, |
| @Nullable List<PrinterId> priorityList) { |
| if (mIsDestroyed) { |
| Log.w(LOG_TAG, "Not starting dicovery - session destroyed"); |
| return; |
| } |
| |
| final boolean discoveryStarted = !mStartedPrinterDiscoveryTokens.isEmpty(); |
| |
| // Remember we got a start request to match with an end. |
| mStartedPrinterDiscoveryTokens.add(observer.asBinder()); |
| |
| // If printer discovery is ongoing and the start request has a list |
| // of printer to be checked, then we just request validating them. |
| if (discoveryStarted && priorityList != null && !priorityList.isEmpty()) { |
| validatePrinters(priorityList); |
| return; |
| } |
| |
| // The service are already performing discovery - nothing to do. |
| if (mStartedPrinterDiscoveryTokens.size() > 1) { |
| return; |
| } |
| |
| List<RemotePrintService> services = new ArrayList<RemotePrintService>( |
| mActiveServices.values()); |
| SomeArgs args = SomeArgs.obtain(); |
| args.arg1 = services; |
| args.arg2 = priorityList; |
| mSessionHandler.obtainMessage(SessionHandler |
| .MSG_DISPATCH_START_PRINTER_DISCOVERY, args) |
| .sendToTarget(); |
| } |
| |
| public final void stopPrinterDiscoveryLocked(@NonNull IPrinterDiscoveryObserver observer) { |
| if (mIsDestroyed) { |
| Log.w(LOG_TAG, "Not stopping dicovery - session destroyed"); |
| return; |
| } |
| // This one did not make an active discovery request - nothing to do. |
| if (!mStartedPrinterDiscoveryTokens.remove(observer.asBinder())) { |
| return; |
| } |
| // There are other interested observers - do not stop discovery. |
| if (!mStartedPrinterDiscoveryTokens.isEmpty()) { |
| return; |
| } |
| List<RemotePrintService> services = new ArrayList<RemotePrintService>( |
| mActiveServices.values()); |
| mSessionHandler.obtainMessage(SessionHandler |
| .MSG_DISPATCH_STOP_PRINTER_DISCOVERY, services) |
| .sendToTarget(); |
| } |
| |
| public void validatePrintersLocked(@NonNull List<PrinterId> printerIds) { |
| if (mIsDestroyed) { |
| Log.w(LOG_TAG, "Not validating pritners - session destroyed"); |
| return; |
| } |
| |
| List<PrinterId> remainingList = new ArrayList<PrinterId>(printerIds); |
| while (!remainingList.isEmpty()) { |
| Iterator<PrinterId> iterator = remainingList.iterator(); |
| // Gather the printers per service and request a validation. |
| List<PrinterId> updateList = new ArrayList<PrinterId>(); |
| ComponentName serviceName = null; |
| while (iterator.hasNext()) { |
| PrinterId printerId = iterator.next(); |
| if (printerId != null) { |
| if (updateList.isEmpty()) { |
| updateList.add(printerId); |
| serviceName = printerId.getServiceName(); |
| iterator.remove(); |
| } else if (printerId.getServiceName().equals(serviceName)) { |
| updateList.add(printerId); |
| iterator.remove(); |
| } |
| } |
| } |
| // Schedule a notification of the service. |
| RemotePrintService service = mActiveServices.get(serviceName); |
| if (service != null) { |
| SomeArgs args = SomeArgs.obtain(); |
| args.arg1 = service; |
| args.arg2 = updateList; |
| mSessionHandler.obtainMessage(SessionHandler |
| .MSG_VALIDATE_PRINTERS, args) |
| .sendToTarget(); |
| } |
| } |
| } |
| |
| public final void startPrinterStateTrackingLocked(@NonNull PrinterId printerId) { |
| if (mIsDestroyed) { |
| Log.w(LOG_TAG, "Not starting printer state tracking - session destroyed"); |
| return; |
| } |
| // If printer discovery is not started - nothing to do. |
| if (mStartedPrinterDiscoveryTokens.isEmpty()) { |
| return; |
| } |
| final boolean containedPrinterId = mStateTrackedPrinters.contains(printerId); |
| // Keep track of the number of requests to track this one. |
| mStateTrackedPrinters.add(printerId); |
| // If we were tracking this printer - nothing to do. |
| if (containedPrinterId) { |
| return; |
| } |
| // No service - nothing to do. |
| RemotePrintService service = mActiveServices.get(printerId.getServiceName()); |
| if (service == null) { |
| return; |
| } |
| // Ask the service to start tracking. |
| SomeArgs args = SomeArgs.obtain(); |
| args.arg1 = service; |
| args.arg2 = printerId; |
| mSessionHandler.obtainMessage(SessionHandler |
| .MSG_START_PRINTER_STATE_TRACKING, args) |
| .sendToTarget(); |
| } |
| |
| public final void stopPrinterStateTrackingLocked(PrinterId printerId) { |
| if (mIsDestroyed) { |
| Log.w(LOG_TAG, "Not stopping printer state tracking - session destroyed"); |
| return; |
| } |
| // If printer discovery is not started - nothing to do. |
| if (mStartedPrinterDiscoveryTokens.isEmpty()) { |
| return; |
| } |
| // If we did not track this printer - nothing to do. |
| if (!mStateTrackedPrinters.remove(printerId)) { |
| return; |
| } |
| // No service - nothing to do. |
| RemotePrintService service = mActiveServices.get(printerId.getServiceName()); |
| if (service == null) { |
| return; |
| } |
| // Ask the service to start tracking. |
| SomeArgs args = SomeArgs.obtain(); |
| args.arg1 = service; |
| args.arg2 = printerId; |
| mSessionHandler.obtainMessage(SessionHandler |
| .MSG_STOP_PRINTER_STATE_TRACKING, args) |
| .sendToTarget(); |
| } |
| |
| public void onDestroyed() { |
| /* do nothing */ |
| } |
| |
| public void destroyLocked() { |
| if (mIsDestroyed) { |
| Log.w(LOG_TAG, "Not destroying - session destroyed"); |
| return; |
| } |
| mIsDestroyed = true; |
| // Make sure printer tracking is stopped. |
| final int printerCount = mStateTrackedPrinters.size(); |
| for (int i = 0; i < printerCount; i++) { |
| PrinterId printerId = mStateTrackedPrinters.get(i); |
| stopPrinterStateTracking(printerId); |
| } |
| // Make sure discovery is stopped. |
| final int observerCount = mStartedPrinterDiscoveryTokens.size(); |
| for (int i = 0; i < observerCount; i++) { |
| IBinder token = mStartedPrinterDiscoveryTokens.get(i); |
| stopPrinterDiscoveryLocked(IPrinterDiscoveryObserver.Stub.asInterface(token)); |
| } |
| // Tell the services we are done. |
| List<RemotePrintService> services = new ArrayList<RemotePrintService>( |
| mActiveServices.values()); |
| mSessionHandler.obtainMessage(SessionHandler |
| .MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION, services) |
| .sendToTarget(); |
| } |
| |
| public void onPrintersAddedLocked(List<PrinterInfo> printers) { |
| if (DEBUG) { |
| Log.i(LOG_TAG, "onPrintersAddedLocked()"); |
| } |
| if (mIsDestroyed) { |
| Log.w(LOG_TAG, "Not adding printers - session destroyed"); |
| return; |
| } |
| List<PrinterInfo> addedPrinters = null; |
| final int addedPrinterCount = printers.size(); |
| for (int i = 0; i < addedPrinterCount; i++) { |
| PrinterInfo printer = printers.get(i); |
| PrinterInfo oldPrinter = mPrinters.put(printer.getId(), printer); |
| if (oldPrinter == null || !oldPrinter.equals(printer)) { |
| if (addedPrinters == null) { |
| addedPrinters = new ArrayList<PrinterInfo>(); |
| } |
| addedPrinters.add(printer); |
| } |
| } |
| if (addedPrinters != null) { |
| mSessionHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_ADDED, |
| addedPrinters).sendToTarget(); |
| } |
| } |
| |
| public void onPrintersRemovedLocked(List<PrinterId> printerIds) { |
| if (DEBUG) { |
| Log.i(LOG_TAG, "onPrintersRemovedLocked()"); |
| } |
| if (mIsDestroyed) { |
| Log.w(LOG_TAG, "Not removing printers - session destroyed"); |
| return; |
| } |
| List<PrinterId> removedPrinterIds = null; |
| final int removedPrinterCount = printerIds.size(); |
| for (int i = 0; i < removedPrinterCount; i++) { |
| PrinterId removedPrinterId = printerIds.get(i); |
| if (mPrinters.remove(removedPrinterId) != null) { |
| if (removedPrinterIds == null) { |
| removedPrinterIds = new ArrayList<PrinterId>(); |
| } |
| removedPrinterIds.add(removedPrinterId); |
| } |
| } |
| if (removedPrinterIds != null) { |
| mSessionHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_REMOVED, |
| removedPrinterIds).sendToTarget(); |
| } |
| } |
| |
| public void onServiceRemovedLocked(RemotePrintService service) { |
| if (mIsDestroyed) { |
| Log.w(LOG_TAG, "Not updating removed service - session destroyed"); |
| return; |
| } |
| // Remove the reported and tracked printers for that service. |
| ComponentName serviceName = service.getComponentName(); |
| removePrintersForServiceLocked(serviceName); |
| service.destroy(); |
| } |
| |
| /** |
| * Handle that a custom icon for a printer was loaded. |
| * |
| * This increments the icon generation and adds the printer again which triggers an update |
| * in all users of the currently known printers. |
| * |
| * @param printerId the id of the printer the icon belongs to |
| * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon() |
| */ |
| public void onCustomPrinterIconLoadedLocked(PrinterId printerId) { |
| if (DEBUG) { |
| Log.i(LOG_TAG, "onCustomPrinterIconLoadedLocked()"); |
| } |
| if (mIsDestroyed) { |
| Log.w(LOG_TAG, "Not updating printer - session destroyed"); |
| return; |
| } |
| |
| PrinterInfo printer = mPrinters.get(printerId); |
| if (printer != null) { |
| PrinterInfo newPrinter = (new PrinterInfo.Builder(printer)) |
| .incCustomPrinterIconGen().build(); |
| mPrinters.put(printerId, newPrinter); |
| |
| ArrayList<PrinterInfo> addedPrinters = new ArrayList<>(1); |
| addedPrinters.add(newPrinter); |
| mSessionHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_ADDED, |
| addedPrinters).sendToTarget(); |
| } |
| } |
| |
| public void onServiceDiedLocked(RemotePrintService service) { |
| // Remove the reported by that service. |
| removePrintersForServiceLocked(service.getComponentName()); |
| } |
| |
| public void onServiceAddedLocked(RemotePrintService service) { |
| if (mIsDestroyed) { |
| Log.w(LOG_TAG, "Not updating added service - session destroyed"); |
| return; |
| } |
| // Tell the service to create a session. |
| mSessionHandler.obtainMessage( |
| SessionHandler.MSG_CREATE_PRINTER_DISCOVERY_SESSION, |
| service).sendToTarget(); |
| // Start printer discovery if necessary. |
| if (!mStartedPrinterDiscoveryTokens.isEmpty()) { |
| mSessionHandler.obtainMessage( |
| SessionHandler.MSG_START_PRINTER_DISCOVERY, |
| service).sendToTarget(); |
| } |
| // Start tracking printers if necessary |
| final int trackedPrinterCount = mStateTrackedPrinters.size(); |
| for (int i = 0; i < trackedPrinterCount; i++) { |
| PrinterId printerId = mStateTrackedPrinters.get(i); |
| if (printerId.getServiceName().equals(service.getComponentName())) { |
| SomeArgs args = SomeArgs.obtain(); |
| args.arg1 = service; |
| args.arg2 = printerId; |
| mSessionHandler.obtainMessage(SessionHandler |
| .MSG_START_PRINTER_STATE_TRACKING, args) |
| .sendToTarget(); |
| } |
| } |
| } |
| |
| public void dump(PrintWriter pw, String prefix) { |
| pw.append(prefix).append("destroyed=") |
| .append(String.valueOf(mDestroyed)).println(); |
| |
| pw.append(prefix).append("printDiscoveryInProgress=") |
| .append(String.valueOf(!mStartedPrinterDiscoveryTokens.isEmpty())).println(); |
| |
| String tab = " "; |
| |
| pw.append(prefix).append(tab).append("printer discovery observers:").println(); |
| final int observerCount = mDiscoveryObservers.beginBroadcast(); |
| for (int i = 0; i < observerCount; i++) { |
| IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i); |
| pw.append(prefix).append(prefix).append(observer.toString()); |
| pw.println(); |
| } |
| mDiscoveryObservers.finishBroadcast(); |
| |
| pw.append(prefix).append(tab).append("start discovery requests:").println(); |
| final int tokenCount = this.mStartedPrinterDiscoveryTokens.size(); |
| for (int i = 0; i < tokenCount; i++) { |
| IBinder token = mStartedPrinterDiscoveryTokens.get(i); |
| pw.append(prefix).append(tab).append(tab).append(token.toString()).println(); |
| } |
| |
| pw.append(prefix).append(tab).append("tracked printer requests:").println(); |
| final int trackedPrinters = mStateTrackedPrinters.size(); |
| for (int i = 0; i < trackedPrinters; i++) { |
| PrinterId printer = mStateTrackedPrinters.get(i); |
| pw.append(prefix).append(tab).append(tab).append(printer.toString()).println(); |
| } |
| |
| pw.append(prefix).append(tab).append("printers:").println(); |
| final int pritnerCount = mPrinters.size(); |
| for (int i = 0; i < pritnerCount; i++) { |
| PrinterInfo printer = mPrinters.valueAt(i); |
| pw.append(prefix).append(tab).append(tab).append( |
| printer.toString()).println(); |
| } |
| } |
| |
| private void removePrintersForServiceLocked(ComponentName serviceName) { |
| // No printers - nothing to do. |
| if (mPrinters.isEmpty()) { |
| return; |
| } |
| // Remove the printers for that service. |
| List<PrinterId> removedPrinterIds = null; |
| final int printerCount = mPrinters.size(); |
| for (int i = 0; i < printerCount; i++) { |
| PrinterId printerId = mPrinters.keyAt(i); |
| if (printerId.getServiceName().equals(serviceName)) { |
| if (removedPrinterIds == null) { |
| removedPrinterIds = new ArrayList<PrinterId>(); |
| } |
| removedPrinterIds.add(printerId); |
| } |
| } |
| if (removedPrinterIds != null) { |
| final int removedPrinterCount = removedPrinterIds.size(); |
| for (int i = 0; i < removedPrinterCount; i++) { |
| mPrinters.remove(removedPrinterIds.get(i)); |
| } |
| mSessionHandler.obtainMessage( |
| SessionHandler.MSG_DISPATCH_PRINTERS_REMOVED, |
| removedPrinterIds).sendToTarget(); |
| } |
| } |
| |
| private void handleDispatchPrintersAdded(List<PrinterInfo> addedPrinters) { |
| final int observerCount = mDiscoveryObservers.beginBroadcast(); |
| for (int i = 0; i < observerCount; i++) { |
| IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i); |
| handlePrintersAdded(observer, addedPrinters); |
| } |
| mDiscoveryObservers.finishBroadcast(); |
| } |
| |
| private void handleDispatchPrintersRemoved(List<PrinterId> removedPrinterIds) { |
| final int observerCount = mDiscoveryObservers.beginBroadcast(); |
| for (int i = 0; i < observerCount; i++) { |
| IPrinterDiscoveryObserver observer = mDiscoveryObservers.getBroadcastItem(i); |
| handlePrintersRemoved(observer, removedPrinterIds); |
| } |
| mDiscoveryObservers.finishBroadcast(); |
| } |
| |
| private void handleDispatchCreatePrinterDiscoverySession( |
| List<RemotePrintService> services) { |
| final int serviceCount = services.size(); |
| for (int i = 0; i < serviceCount; i++) { |
| RemotePrintService service = services.get(i); |
| service.createPrinterDiscoverySession(); |
| } |
| } |
| |
| private void handleDispatchDestroyPrinterDiscoverySession( |
| List<RemotePrintService> services) { |
| final int serviceCount = services.size(); |
| for (int i = 0; i < serviceCount; i++) { |
| RemotePrintService service = services.get(i); |
| service.destroyPrinterDiscoverySession(); |
| } |
| onDestroyed(); |
| } |
| |
| private void handleDispatchStartPrinterDiscovery( |
| List<RemotePrintService> services, List<PrinterId> printerIds) { |
| final int serviceCount = services.size(); |
| for (int i = 0; i < serviceCount; i++) { |
| RemotePrintService service = services.get(i); |
| service.startPrinterDiscovery(printerIds); |
| } |
| } |
| |
| private void handleDispatchStopPrinterDiscovery(List<RemotePrintService> services) { |
| final int serviceCount = services.size(); |
| for (int i = 0; i < serviceCount; i++) { |
| RemotePrintService service = services.get(i); |
| service.stopPrinterDiscovery(); |
| } |
| } |
| |
| private void handleValidatePrinters(RemotePrintService service, |
| List<PrinterId> printerIds) { |
| service.validatePrinters(printerIds); |
| } |
| |
| private void handleStartPrinterStateTracking(@NonNull RemotePrintService service, |
| @NonNull PrinterId printerId) { |
| service.startPrinterStateTracking(printerId); |
| } |
| |
| private void handleStopPrinterStateTracking(RemotePrintService service, |
| PrinterId printerId) { |
| service.stopPrinterStateTracking(printerId); |
| } |
| |
| private void handlePrintersAdded(IPrinterDiscoveryObserver observer, |
| List<PrinterInfo> printers) { |
| try { |
| observer.onPrintersAdded(new ParceledListSlice<PrinterInfo>(printers)); |
| } catch (RemoteException re) { |
| Log.e(LOG_TAG, "Error sending added printers", re); |
| } |
| } |
| |
| private void handlePrintersRemoved(IPrinterDiscoveryObserver observer, |
| List<PrinterId> printerIds) { |
| try { |
| observer.onPrintersRemoved(new ParceledListSlice<PrinterId>(printerIds)); |
| } catch (RemoteException re) { |
| Log.e(LOG_TAG, "Error sending removed printers", re); |
| } |
| } |
| |
| private final class SessionHandler extends Handler { |
| public static final int MSG_PRINTERS_ADDED = 1; |
| public static final int MSG_PRINTERS_REMOVED = 2; |
| public static final int MSG_DISPATCH_PRINTERS_ADDED = 3; |
| public static final int MSG_DISPATCH_PRINTERS_REMOVED = 4; |
| |
| public static final int MSG_CREATE_PRINTER_DISCOVERY_SESSION = 5; |
| public static final int MSG_DESTROY_PRINTER_DISCOVERY_SESSION = 6; |
| public static final int MSG_START_PRINTER_DISCOVERY = 7; |
| public static final int MSG_STOP_PRINTER_DISCOVERY = 8; |
| public static final int MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION = 9; |
| public static final int MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION = 10; |
| public static final int MSG_DISPATCH_START_PRINTER_DISCOVERY = 11; |
| public static final int MSG_DISPATCH_STOP_PRINTER_DISCOVERY = 12; |
| public static final int MSG_VALIDATE_PRINTERS = 13; |
| public static final int MSG_START_PRINTER_STATE_TRACKING = 14; |
| public static final int MSG_STOP_PRINTER_STATE_TRACKING = 15; |
| public static final int MSG_DESTROY_SERVICE = 16; |
| |
| SessionHandler(Looper looper) { |
| super(looper, null, false); |
| } |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public void handleMessage(Message message) { |
| switch (message.what) { |
| case MSG_PRINTERS_ADDED: { |
| SomeArgs args = (SomeArgs) message.obj; |
| IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) args.arg1; |
| List<PrinterInfo> addedPrinters = (List<PrinterInfo>) args.arg2; |
| args.recycle(); |
| handlePrintersAdded(observer, addedPrinters); |
| } break; |
| |
| case MSG_PRINTERS_REMOVED: { |
| SomeArgs args = (SomeArgs) message.obj; |
| IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) args.arg1; |
| List<PrinterId> removedPrinterIds = (List<PrinterId>) args.arg2; |
| args.recycle(); |
| handlePrintersRemoved(observer, removedPrinterIds); |
| } |
| |
| case MSG_DISPATCH_PRINTERS_ADDED: { |
| List<PrinterInfo> addedPrinters = (List<PrinterInfo>) message.obj; |
| handleDispatchPrintersAdded(addedPrinters); |
| } break; |
| |
| case MSG_DISPATCH_PRINTERS_REMOVED: { |
| List<PrinterId> removedPrinterIds = (List<PrinterId>) message.obj; |
| handleDispatchPrintersRemoved(removedPrinterIds); |
| } break; |
| |
| case MSG_CREATE_PRINTER_DISCOVERY_SESSION: { |
| RemotePrintService service = (RemotePrintService) message.obj; |
| service.createPrinterDiscoverySession(); |
| } break; |
| |
| case MSG_DESTROY_PRINTER_DISCOVERY_SESSION: { |
| RemotePrintService service = (RemotePrintService) message.obj; |
| service.destroyPrinterDiscoverySession(); |
| } break; |
| |
| case MSG_START_PRINTER_DISCOVERY: { |
| RemotePrintService service = (RemotePrintService) message.obj; |
| service.startPrinterDiscovery(null); |
| } break; |
| |
| case MSG_STOP_PRINTER_DISCOVERY: { |
| RemotePrintService service = (RemotePrintService) message.obj; |
| service.stopPrinterDiscovery(); |
| } break; |
| |
| case MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION: { |
| List<RemotePrintService> services = (List<RemotePrintService>) message.obj; |
| handleDispatchCreatePrinterDiscoverySession(services); |
| } break; |
| |
| case MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION: { |
| List<RemotePrintService> services = (List<RemotePrintService>) message.obj; |
| handleDispatchDestroyPrinterDiscoverySession(services); |
| } break; |
| |
| case MSG_DISPATCH_START_PRINTER_DISCOVERY: { |
| SomeArgs args = (SomeArgs) message.obj; |
| List<RemotePrintService> services = (List<RemotePrintService>) args.arg1; |
| List<PrinterId> printerIds = (List<PrinterId>) args.arg2; |
| args.recycle(); |
| handleDispatchStartPrinterDiscovery(services, printerIds); |
| } break; |
| |
| case MSG_DISPATCH_STOP_PRINTER_DISCOVERY: { |
| List<RemotePrintService> services = (List<RemotePrintService>) message.obj; |
| handleDispatchStopPrinterDiscovery(services); |
| } break; |
| |
| case MSG_VALIDATE_PRINTERS: { |
| SomeArgs args = (SomeArgs) message.obj; |
| RemotePrintService service = (RemotePrintService) args.arg1; |
| List<PrinterId> printerIds = (List<PrinterId>) args.arg2; |
| args.recycle(); |
| handleValidatePrinters(service, printerIds); |
| } break; |
| |
| case MSG_START_PRINTER_STATE_TRACKING: { |
| SomeArgs args = (SomeArgs) message.obj; |
| RemotePrintService service = (RemotePrintService) args.arg1; |
| PrinterId printerId = (PrinterId) args.arg2; |
| args.recycle(); |
| handleStartPrinterStateTracking(service, printerId); |
| } break; |
| |
| case MSG_STOP_PRINTER_STATE_TRACKING: { |
| SomeArgs args = (SomeArgs) message.obj; |
| RemotePrintService service = (RemotePrintService) args.arg1; |
| PrinterId printerId = (PrinterId) args.arg2; |
| args.recycle(); |
| handleStopPrinterStateTracking(service, printerId); |
| } break; |
| |
| case MSG_DESTROY_SERVICE: { |
| RemotePrintService service = (RemotePrintService) message.obj; |
| service.destroy(); |
| } break; |
| } |
| } |
| } |
| } |
| |
| private final class PrintJobForAppCache { |
| private final SparseArray<List<PrintJobInfo>> mPrintJobsForRunningApp = |
| new SparseArray<List<PrintJobInfo>>(); |
| |
| public boolean onPrintJobCreated(final IBinder creator, final int appId, |
| PrintJobInfo printJob) { |
| try { |
| creator.linkToDeath(new DeathRecipient() { |
| @Override |
| public void binderDied() { |
| creator.unlinkToDeath(this, 0); |
| synchronized (mLock) { |
| mPrintJobsForRunningApp.remove(appId); |
| } |
| } |
| }, 0); |
| } catch (RemoteException re) { |
| /* The process is already dead - we just failed. */ |
| return false; |
| } |
| synchronized (mLock) { |
| List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get(appId); |
| if (printJobsForApp == null) { |
| printJobsForApp = new ArrayList<PrintJobInfo>(); |
| mPrintJobsForRunningApp.put(appId, printJobsForApp); |
| } |
| printJobsForApp.add(printJob); |
| } |
| return true; |
| } |
| |
| public void onPrintJobStateChanged(PrintJobInfo printJob) { |
| synchronized (mLock) { |
| List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get( |
| printJob.getAppId()); |
| if (printJobsForApp == null) { |
| return; |
| } |
| final int printJobCount = printJobsForApp.size(); |
| for (int i = 0; i < printJobCount; i++) { |
| PrintJobInfo oldPrintJob = printJobsForApp.get(i); |
| if (oldPrintJob.getId().equals(printJob.getId())) { |
| printJobsForApp.set(i, printJob); |
| } |
| } |
| } |
| } |
| |
| public PrintJobInfo getPrintJob(PrintJobId printJobId, int appId) { |
| synchronized (mLock) { |
| List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get(appId); |
| if (printJobsForApp == null) { |
| return null; |
| } |
| final int printJobCount = printJobsForApp.size(); |
| for (int i = 0; i < printJobCount; i++) { |
| PrintJobInfo printJob = printJobsForApp.get(i); |
| if (printJob.getId().equals(printJobId)) { |
| return printJob; |
| } |
| } |
| } |
| return null; |
| } |
| |
| public List<PrintJobInfo> getPrintJobs(int appId) { |
| synchronized (mLock) { |
| List<PrintJobInfo> printJobs = null; |
| if (appId == PrintManager.APP_ID_ANY) { |
| final int bucketCount = mPrintJobsForRunningApp.size(); |
| for (int i = 0; i < bucketCount; i++) { |
| List<PrintJobInfo> bucket = mPrintJobsForRunningApp.valueAt(i); |
| if (printJobs == null) { |
| printJobs = new ArrayList<PrintJobInfo>(); |
| } |
| printJobs.addAll(bucket); |
| } |
| } else { |
| List<PrintJobInfo> bucket = mPrintJobsForRunningApp.get(appId); |
| if (bucket != null) { |
| if (printJobs == null) { |
| printJobs = new ArrayList<PrintJobInfo>(); |
| } |
| printJobs.addAll(bucket); |
| } |
| } |
| if (printJobs != null) { |
| return printJobs; |
| } |
| return Collections.emptyList(); |
| } |
| } |
| |
| public void dump(PrintWriter pw, String prefix) { |
| synchronized (mLock) { |
| String tab = " "; |
| final int bucketCount = mPrintJobsForRunningApp.size(); |
| for (int i = 0; i < bucketCount; i++) { |
| final int appId = mPrintJobsForRunningApp.keyAt(i); |
| pw.append(prefix).append("appId=" + appId).append(':').println(); |
| List<PrintJobInfo> bucket = mPrintJobsForRunningApp.valueAt(i); |
| final int printJobCount = bucket.size(); |
| for (int j = 0; j < printJobCount; j++) { |
| PrintJobInfo printJob = bucket.get(j); |
| pw.append(prefix).append(tab).append(printJob.toString()).println(); |
| } |
| } |
| } |
| } |
| } |
| } |