1. We used to show a single notificaiton for every print job but this is against th UX guidelines. Since we have to lead by example, this change adds coalescing of multiple notifications. bug:11155212 2. Print job state callback in the PrintManager now correctly invoked on the main thread. bug:10983508 Change-Id: I906e9e62198fa37cb4d2f71fce07ed475d61e1bd
1595 lines
65 KiB
Java
1595 lines
65 KiB
Java
/*
|
|
* 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 android.content.ComponentName;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.pm.ApplicationInfo;
|
|
import android.content.pm.PackageManager;
|
|
import android.content.pm.ParceledListSlice;
|
|
import android.content.pm.ResolveInfo;
|
|
import android.content.pm.ServiceInfo;
|
|
import android.os.AsyncTask;
|
|
import android.os.Binder;
|
|
import android.os.Handler;
|
|
import android.os.IBinder;
|
|
import android.os.IBinder.DeathRecipient;
|
|
import android.os.Looper;
|
|
import android.os.Message;
|
|
import android.os.RemoteCallbackList;
|
|
import android.os.RemoteException;
|
|
import android.print.IPrintClient;
|
|
import android.print.IPrintDocumentAdapter;
|
|
import android.print.IPrintJobStateChangeListener;
|
|
import android.print.IPrinterDiscoveryObserver;
|
|
import android.print.PrintAttributes;
|
|
import android.print.PrintJobId;
|
|
import android.print.PrintJobInfo;
|
|
import android.print.PrintManager;
|
|
import android.print.PrinterId;
|
|
import android.print.PrinterInfo;
|
|
import android.printservice.PrintServiceInfo;
|
|
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 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.Set;
|
|
|
|
/**
|
|
* Represents the print state for a user.
|
|
*/
|
|
final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks {
|
|
|
|
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> mEnabledServices =
|
|
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 boolean mDestroyed;
|
|
|
|
public UserState(Context context, int userId, Object lock) {
|
|
mContext = context;
|
|
mUserId = userId;
|
|
mLock = lock;
|
|
mSpooler = new RemotePrintSpooler(context, userId, this);
|
|
mHandler = new UserStateHandler(context.getMainLooper());
|
|
synchronized (mLock) {
|
|
enableSystemPrintServicesOnFirstBootLocked();
|
|
}
|
|
}
|
|
|
|
@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();
|
|
}
|
|
|
|
public PrintJobInfo print(String printJobName, final IPrintClient client,
|
|
final IPrintDocumentAdapter documentAdapter, PrintAttributes attributes,
|
|
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);
|
|
|
|
// Track this job so we can forget it when the creator dies.
|
|
if (!mPrintJobForAppCache.onPrintJobCreated(client.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, client, documentAdapter);
|
|
return null;
|
|
}
|
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
|
|
|
|
return printJob;
|
|
}
|
|
|
|
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 - it is visible only to print services.
|
|
// Also the cached print jobs are delivered only to apps, so
|
|
// stripping the tag of a cached print job is fine.
|
|
cachedPrintJob.setTag(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 - it is visible only to print services.
|
|
printJob.setTag(null);
|
|
}
|
|
}
|
|
|
|
return new ArrayList<PrintJobInfo>(result.values());
|
|
}
|
|
|
|
public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId) {
|
|
PrintJobInfo printJob = mPrintJobForAppCache.getPrintJob(printJobId, appId);
|
|
if (printJob != null) {
|
|
return printJob;
|
|
}
|
|
return mSpooler.getPrintJobInfo(printJobId, appId);
|
|
}
|
|
|
|
public void cancelPrintJob(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) {
|
|
ComponentName printServiceName = printJobInfo.getPrinterId().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(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 List<PrintServiceInfo> getEnabledPrintServices() {
|
|
synchronized (mLock) {
|
|
List<PrintServiceInfo> enabledServices = 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);
|
|
if (mActiveServices.containsKey(componentName)) {
|
|
if (enabledServices == null) {
|
|
enabledServices = new ArrayList<PrintServiceInfo>();
|
|
}
|
|
enabledServices.add(installedService);
|
|
}
|
|
}
|
|
return enabledServices;
|
|
}
|
|
}
|
|
|
|
public List<PrintServiceInfo> getInstalledPrintServices() {
|
|
synchronized (mLock) {
|
|
return mInstalledServices;
|
|
}
|
|
}
|
|
|
|
public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer) {
|
|
synchronized (mLock) {
|
|
throwIfDestroyedLocked();
|
|
if (mActiveServices.isEmpty()) {
|
|
return;
|
|
}
|
|
if (mPrinterDiscoverySession == null) {
|
|
// 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(IPrinterDiscoveryObserver observer) {
|
|
synchronized (mLock) {
|
|
// Already destroyed - nothing to do.
|
|
if (mPrinterDiscoverySession == null) {
|
|
return;
|
|
}
|
|
// Remove this observer.
|
|
mPrinterDiscoverySession.removeObserverLocked(observer);
|
|
}
|
|
}
|
|
|
|
public void startPrinterDiscovery(IPrinterDiscoveryObserver observer,
|
|
List<PrinterId> printerIds) {
|
|
synchronized (mLock) {
|
|
throwIfDestroyedLocked();
|
|
// No services - nothing to do.
|
|
if (mActiveServices.isEmpty()) {
|
|
return;
|
|
}
|
|
// No session - nothing to do.
|
|
if (mPrinterDiscoverySession == null) {
|
|
return;
|
|
}
|
|
// Kick of discovery.
|
|
mPrinterDiscoverySession.startPrinterDiscoveryLocked(observer,
|
|
printerIds);
|
|
}
|
|
}
|
|
|
|
public void stopPrinterDiscovery(IPrinterDiscoveryObserver observer) {
|
|
synchronized (mLock) {
|
|
throwIfDestroyedLocked();
|
|
// No services - nothing to do.
|
|
if (mActiveServices.isEmpty()) {
|
|
return;
|
|
}
|
|
// No session - nothing to do.
|
|
if (mPrinterDiscoverySession == null) {
|
|
return;
|
|
}
|
|
// Kick of discovery.
|
|
mPrinterDiscoverySession.stopPrinterDiscoveryLocked(observer);
|
|
}
|
|
}
|
|
|
|
public void validatePrinters(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(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(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() {
|
|
mPrintJobStateChangeListenerRecords.remove(this);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
public void removePrintJobStateChangeListener(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;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onPrintJobStateChanged(PrintJobInfo printJob) {
|
|
mPrintJobForAppCache.onPrintJobStateChanged(printJob);
|
|
mHandler.obtainMessage(UserStateHandler.MSG_DISPATCH_PRINT_JOB_STATE_CHANGED,
|
|
printJob.getAppId(), 0, printJob.getId()).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 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 Set<ComponentName> getEnabledServices() {
|
|
synchronized(mLock) {
|
|
throwIfDestroyedLocked();
|
|
return mEnabledServices;
|
|
}
|
|
}
|
|
|
|
public void destroyLocked() {
|
|
throwIfDestroyedLocked();
|
|
mSpooler.destroy();
|
|
for (RemotePrintService service : mActiveServices.values()) {
|
|
service.destroy();
|
|
}
|
|
mActiveServices.clear();
|
|
mInstalledServices.clear();
|
|
mEnabledServices.clear();
|
|
if (mPrinterDiscoverySession != null) {
|
|
mPrinterDiscoverySession.destroyLocked();
|
|
mPrinterDiscoverySession = null;
|
|
}
|
|
mDestroyed = true;
|
|
}
|
|
|
|
public void dump(FileDescriptor fd, PrintWriter pw, 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(prefix).append(tab).append("enabled services:").println();
|
|
for (ComponentName enabledService : mEnabledServices) {
|
|
String enabledServicePrefix = prefix + tab + tab;
|
|
pw.append(enabledServicePrefix).append("service:").println();
|
|
pw.append(enabledServicePrefix).append(tab).append("componentName=")
|
|
.append(enabledService.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 |= readEnabledPrintServicesLocked();
|
|
return somethingChanged;
|
|
}
|
|
|
|
private boolean readInstalledPrintServicesLocked() {
|
|
Set<PrintServiceInfo> tempPrintServices = new HashSet<PrintServiceInfo>();
|
|
|
|
List<ResolveInfo> installedServices = mContext.getPackageManager()
|
|
.queryIntentServicesAsUser(mQueryIntent, PackageManager.GET_SERVICES
|
|
| PackageManager.GET_META_DATA, 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));
|
|
}
|
|
|
|
if (!tempPrintServices.equals(mInstalledServices)) {
|
|
mInstalledServices.clear();
|
|
mInstalledServices.addAll(tempPrintServices);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private boolean readEnabledPrintServicesLocked() {
|
|
Set<ComponentName> tempEnabledServiceNameSet = new HashSet<ComponentName>();
|
|
readPrintServicesFromSettingLocked(Settings.Secure.ENABLED_PRINT_SERVICES,
|
|
tempEnabledServiceNameSet);
|
|
if (!tempEnabledServiceNameSet.equals(mEnabledServices)) {
|
|
mEnabledServices.clear();
|
|
mEnabledServices.addAll(tempEnabledServiceNameSet);
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void enableSystemPrintServicesOnFirstBootLocked() {
|
|
// Load enabled and installed services.
|
|
readEnabledPrintServicesLocked();
|
|
readInstalledPrintServicesLocked();
|
|
|
|
// Load the system services once enabled on first boot.
|
|
Set<ComponentName> enabledOnFirstBoot = new HashSet<ComponentName>();
|
|
readPrintServicesFromSettingLocked(
|
|
Settings.Secure.ENABLED_ON_FIRST_BOOT_SYSTEM_PRINT_SERVICES,
|
|
enabledOnFirstBoot);
|
|
|
|
StringBuilder builder = new StringBuilder();
|
|
|
|
final int serviceCount = mInstalledServices.size();
|
|
for (int i = 0; i < serviceCount; i++) {
|
|
ServiceInfo serviceInfo = mInstalledServices.get(i).getResolveInfo().serviceInfo;
|
|
// Enable system print services if we never did that and are not enabled.
|
|
if ((serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
|
|
ComponentName serviceName = new ComponentName(
|
|
serviceInfo.packageName, serviceInfo.name);
|
|
if (!mEnabledServices.contains(serviceName)
|
|
&& !enabledOnFirstBoot.contains(serviceName)) {
|
|
if (builder.length() > 0) {
|
|
builder.append(":");
|
|
}
|
|
builder.append(serviceName.flattenToString());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Nothing to be enabled - done.
|
|
if (builder.length() <= 0) {
|
|
return;
|
|
}
|
|
|
|
String servicesToEnable = builder.toString();
|
|
|
|
// Update the enabled services setting.
|
|
String enabledServices = Settings.Secure.getStringForUser(
|
|
mContext.getContentResolver(), Settings.Secure.ENABLED_PRINT_SERVICES, mUserId);
|
|
if (TextUtils.isEmpty(enabledServices)) {
|
|
enabledServices = servicesToEnable;
|
|
} else {
|
|
enabledServices = enabledServices + ":" + servicesToEnable;
|
|
}
|
|
Settings.Secure.putStringForUser(mContext.getContentResolver(),
|
|
Settings.Secure.ENABLED_PRINT_SERVICES, enabledServices, mUserId);
|
|
|
|
// Update the enabled on first boot services setting.
|
|
String enabledOnFirstBootServices = Settings.Secure.getStringForUser(
|
|
mContext.getContentResolver(),
|
|
Settings.Secure.ENABLED_ON_FIRST_BOOT_SYSTEM_PRINT_SERVICES, mUserId);
|
|
if (TextUtils.isEmpty(enabledOnFirstBootServices)) {
|
|
enabledOnFirstBootServices = servicesToEnable;
|
|
} else {
|
|
enabledOnFirstBootServices = enabledOnFirstBootServices + ":" + enabledServices;
|
|
}
|
|
Settings.Secure.putStringForUser(mContext.getContentResolver(),
|
|
Settings.Secure.ENABLED_ON_FIRST_BOOT_SYSTEM_PRINT_SERVICES,
|
|
enabledOnFirstBootServices, mUserId);
|
|
}
|
|
|
|
private void onConfigurationChangedLocked() {
|
|
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);
|
|
if (mEnabledServices.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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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() {
|
|
failActivePrintJobsForServiceInternal(serviceName);
|
|
}
|
|
});
|
|
} else {
|
|
failActivePrintJobsForServiceInternal(serviceName);
|
|
}
|
|
}
|
|
|
|
private void failActivePrintJobsForServiceInternal(ComponentName serviceName) {
|
|
List<PrintJobInfo> printJobs = mSpooler.getPrintJobInfos(serviceName,
|
|
PrintJobInfo.STATE_ANY_ACTIVE, 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 final class UserStateHandler extends Handler {
|
|
public static final int MSG_DISPATCH_PRINT_JOB_STATE_CHANGED = 1;
|
|
|
|
public UserStateHandler(Looper looper) {
|
|
super(looper, null, false);
|
|
}
|
|
|
|
@Override
|
|
public void handleMessage(Message message) {
|
|
if (message.what == MSG_DISPATCH_PRINT_JOB_STATE_CHANGED) {
|
|
PrintJobId printJobId = (PrintJobId) message.obj;
|
|
final int appId = message.arg1;
|
|
handleDispatchPrintJobStateChanged(printJobId, appId);
|
|
}
|
|
}
|
|
}
|
|
|
|
private abstract class PrintJobStateChangeListenerRecord implements DeathRecipient {
|
|
final IPrintJobStateChangeListener listener;
|
|
final int appId;
|
|
|
|
public PrintJobStateChangeListenerRecord(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 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 mHandler;
|
|
|
|
private boolean mIsDestroyed;
|
|
|
|
public PrinterDiscoverySessionMediator(Context context) {
|
|
mHandler = new SessionHandler(context.getMainLooper());
|
|
// Kick off the session creation.
|
|
List<RemotePrintService> services = new ArrayList<RemotePrintService>(
|
|
mActiveServices.values());
|
|
mHandler.obtainMessage(SessionHandler
|
|
.MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION, services)
|
|
.sendToTarget();
|
|
}
|
|
|
|
public void addObserverLocked(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;
|
|
mHandler.obtainMessage(SessionHandler.MSG_PRINTERS_ADDED,
|
|
args).sendToTarget();
|
|
}
|
|
}
|
|
|
|
public void removeObserverLocked(IPrinterDiscoveryObserver observer) {
|
|
// Remove the observer.
|
|
mDiscoveryObservers.unregister(observer);
|
|
// No one else observing - then kill it.
|
|
if (mDiscoveryObservers.getRegisteredCallbackCount() == 0) {
|
|
destroyLocked();
|
|
}
|
|
}
|
|
|
|
public final void startPrinterDiscoveryLocked(IPrinterDiscoveryObserver observer,
|
|
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;
|
|
mHandler.obtainMessage(SessionHandler
|
|
.MSG_DISPATCH_START_PRINTER_DISCOVERY, args)
|
|
.sendToTarget();
|
|
}
|
|
|
|
public final void stopPrinterDiscoveryLocked(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());
|
|
mHandler.obtainMessage(SessionHandler
|
|
.MSG_DISPATCH_STOP_PRINTER_DISCOVERY, services)
|
|
.sendToTarget();
|
|
}
|
|
|
|
public void validatePrintersLocked(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 (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;
|
|
mHandler.obtainMessage(SessionHandler
|
|
.MSG_VALIDATE_PRINTERS, args)
|
|
.sendToTarget();
|
|
}
|
|
}
|
|
}
|
|
|
|
public final void startPrinterStateTrackingLocked(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;
|
|
mHandler.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;
|
|
mHandler.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;
|
|
}
|
|
// 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());
|
|
mHandler.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) {
|
|
mHandler.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) {
|
|
mHandler.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();
|
|
}
|
|
|
|
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.
|
|
mHandler.obtainMessage(
|
|
SessionHandler.MSG_CREATE_PRINTER_DISCOVERY_SESSION,
|
|
service).sendToTarget();
|
|
// Start printer discovery if necessary.
|
|
if (!mStartedPrinterDiscoveryTokens.isEmpty()) {
|
|
mHandler.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;
|
|
mHandler.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));
|
|
}
|
|
mHandler.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(RemotePrintService service,
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|