Merge "Polish the print spooler loading of stored print jobs."

This commit is contained in:
Svetoslav Ganov
2013-08-05 04:08:46 +00:00
committed by Android (Google) Code Review
10 changed files with 403 additions and 431 deletions

View File

@ -46,5 +46,4 @@ oneway interface IPrintSpooler {
int sequence);
void writePrintJobData(in ParcelFileDescriptor fd, int printJobId);
void setClient(IPrintSpoolerClient client);
void notifyClientForActivteJobs();
}

View File

@ -22,7 +22,10 @@ import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Build;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@ -43,6 +46,8 @@ public class NotificationController {
private static final String INTENT_ACTION_CANCEL_PRINTJOB = "INTENT_ACTION_CANCEL_PRINTJOB";
private static final String INTENT_ACTION_RESTART_PRINTJOB = "INTENT_ACTION_RESTART_PRINTJOB";
private static final String INTENT_EXTRA_PRINTJOB_ID = "INTENT_EXTRA_PRINTJOB_ID";
private static final String INTENT_EXTRA_PRINTJOB_LABEL = "INTENT_EXTRA_PRINTJOB_LABEL";
private static final String INTENT_EXTRA_PRINTER_NAME = "INTENT_EXTRA_PRINTER_NAME";
private final Context mContext;
private final NotificationManager mNotificationManager;
@ -53,11 +58,10 @@ public class NotificationController {
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
}
public void onPrintJobStateChanged(PrintJobInfo printJob, int oldState) {
public void onPrintJobStateChanged(PrintJobInfo printJob) {
if (DEBUG) {
Log.i(LOG_TAG, "onPrintJobStateChanged() printJobId: " + printJob.getId()
+ " oldState: " + PrintJobInfo.stateToString(oldState)
+ " newState:" + PrintJobInfo.stateToString(printJob.getState()));
+ " state:" + PrintJobInfo.stateToString(printJob.getState()));
}
switch (printJob.getState()) {
case PrintJobInfo.STATE_QUEUED: {
@ -87,10 +91,10 @@ public class NotificationController {
printJob.getLabel()))
// TODO: Use appropriate icon when assets are ready
.addAction(android.R.drawable.ic_secure, mContext.getString(R.string.cancel),
createCancelIntent(printJob.getId()))
createCancelIntent(printJob))
.setContentText(printJob.getPrinterId().getPrinterName())
.setOngoing(true)
.setWhen(System.currentTimeMillis())
.setOngoing(true)
.setShowWhen(true);
mNotificationManager.notify(printJob.getId(), builder.build());
}
@ -103,10 +107,10 @@ public class NotificationController {
printJob.getLabel()))
// TODO: Use appropriate icon when assets are ready
.addAction(android.R.drawable.ic_secure, mContext.getString(R.string.cancel),
createCancelIntent(printJob.getId()))
createCancelIntent(printJob))
.setContentText(printJob.getPrinterId().getPrinterName())
.setOngoing(true)
.setWhen(System.currentTimeMillis())
.setOngoing(true)
.setShowWhen(true);
mNotificationManager.notify(printJob.getId(), builder.build());
}
@ -119,13 +123,13 @@ public class NotificationController {
printJob.getLabel()))
// TODO: Use appropriate icon when assets are ready
.addAction(android.R.drawable.ic_secure, mContext.getString(R.string.cancel),
createCancelIntent(printJob.getId()))
createCancelIntent(printJob))
// TODO: Use appropriate icon when assets are ready
.addAction(android.R.drawable.ic_secure, mContext.getString(R.string.restart),
createRestartIntent(printJob.getId()))
.setContentText(printJob.getFailureReason())
.setOngoing(true)
.setWhen(System.currentTimeMillis())
.setOngoing(true)
.setShowWhen(true);
mNotificationManager.notify(printJob.getId(), builder.build());
}
@ -134,10 +138,12 @@ public class NotificationController {
mNotificationManager.cancel(printJobId);
}
private PendingIntent createCancelIntent(int printJobId) {
private PendingIntent createCancelIntent(PrintJobInfo printJob) {
Intent intent = new Intent(mContext, NotificationBroadcastReceiver.class);
intent.setAction(INTENT_ACTION_CANCEL_PRINTJOB + "_" + String.valueOf(printJobId));
intent.putExtra(INTENT_EXTRA_PRINTJOB_ID, printJobId);
intent.setAction(INTENT_ACTION_CANCEL_PRINTJOB + "_" + String.valueOf(printJob.getId()));
intent.putExtra(INTENT_EXTRA_PRINTJOB_ID, printJob.getId());
intent.putExtra(INTENT_EXTRA_PRINTJOB_LABEL, printJob.getLabel());
intent.putExtra(INTENT_EXTRA_PRINTER_NAME, printJob.getPrinterId().getPrinterName());
return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
}
@ -156,60 +162,68 @@ public class NotificationController {
String action = intent.getAction();
if (action != null && action.startsWith(INTENT_ACTION_CANCEL_PRINTJOB)) {
final int printJobId = intent.getExtras().getInt(INTENT_EXTRA_PRINTJOB_ID);
handleCancelPrintJob(context, printJobId);
String printJobLabel = intent.getExtras().getString(INTENT_EXTRA_PRINTJOB_LABEL);
String printerName = intent.getExtras().getString(INTENT_EXTRA_PRINTER_NAME);
handleCancelPrintJob(context, printJobId, printJobLabel, printerName);
} else if (action != null && action.startsWith(INTENT_ACTION_RESTART_PRINTJOB)) {
final int printJobId = intent.getExtras().getInt(INTENT_EXTRA_PRINTJOB_ID);
handleRestartPrintJob(context, printJobId);
}
}
private void handleCancelPrintJob(final Context context, final int printJobId) {
private void handleCancelPrintJob(final Context context, final int printJobId,
final String printJobLabel, final String printerName) {
if (DEBUG) {
Log.i(LOG_TAG, "handleCancelPrintJob() printJobId:" + printJobId);
}
PrintSpooler printSpooler = PrintSpooler.getInstance(context);
final PrintJobInfo printJob = printSpooler.getPrintJobInfo(printJobId,
PrintManager.APP_ID_ANY);
if (printJob == null || printJob.getState() == PrintJobInfo.STATE_CANCELED) {
return;
}
// Put up a notification that we are trying to cancel.
NotificationManager notificationManager = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification.Builder builder = new Notification.Builder(context)
// TODO: Use appropriate icon when assets are ready
.setSmallIcon(android.R.drawable.ic_secure)
.setContentTitle(context.getString(
R.string.cancelling_notification_title_template,
printJob.getLabel()))
.setContentText(printJob.getPrinterId().getPrinterName())
.setOngoing(true)
printJobLabel))
.setContentText(printerName)
.setWhen(System.currentTimeMillis())
.setOngoing(true)
.setShowWhen(true);
notificationManager.notify(printJobId, builder.build());
notificationManager.notify(printJob.getId(), builder.build());
// Call into the print manager service off the main thread since
// the print manager service may end up binding to the print spooler
// service which binding is handled on the main thread.
PowerManager powerManager = (PowerManager)
context.getSystemService(Context.POWER_SERVICE);
final WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
LOG_TAG);
wakeLock.acquire();
// We need to request the cancellation to be done by the print
// manager service since it has to communicate with the managing
// print service to request the cancellation. Also we need the
// system service to be bound to the spooler since canceling a
// print job will trigger persistence of current jobs which is
// done on another thread and until it finishes the spooler has
// to be kept around.
IPrintManager printManager = IPrintManager.Stub.asInterface(
ServiceManager.getService(Context.PRINT_SERVICE));
try {
printManager.cancelPrintJob(printJobId, PrintManager.APP_ID_ANY,
UserHandle.myUserId());
} catch (RemoteException re) {
Log.i(LOG_TAG, "Error requestion print job cancellation", re);
}
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
// We need to request the cancellation to be done by the print
// manager service since it has to communicate with the managing
// print service to request the cancellation. Also we need the
// system service to be bound to the spooler since canceling a
// print job will trigger persistence of current jobs which is
// done on another thread and until it finishes the spooler has
// to be kept around.
try {
IPrintManager printManager = IPrintManager.Stub.asInterface(
ServiceManager.getService(Context.PRINT_SERVICE));
printManager.cancelPrintJob(printJobId, PrintManager.APP_ID_ANY,
UserHandle.myUserId());
} catch (RemoteException re) {
Log.i(LOG_TAG, "Error requestion print job cancellation", re);
} finally {
wakeLock.release();
}
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
}
private void handleRestartPrintJob(final Context context, final int printJobId) {
@ -217,29 +231,36 @@ public class NotificationController {
Log.i(LOG_TAG, "handleRestartPrintJob() printJobId:" + printJobId);
}
PrintSpooler printSpooler = PrintSpooler.getInstance(context);
// Call into the print manager service off the main thread since
// the print manager service may end up binding to the print spooler
// service which binding is handled on the main thread.
PowerManager powerManager = (PowerManager)
context.getSystemService(Context.POWER_SERVICE);
final WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
LOG_TAG);
wakeLock.acquire();
PrintJobInfo printJob = printSpooler.getPrintJobInfo(printJobId,
PrintManager.APP_ID_ANY);
if (printJob == null || printJob.getState() != PrintJobInfo.STATE_FAILED) {
return;
}
// We need to request the restart to be done by the print manager
// service since the latter must be bound to the spooler because
// restarting a print job will trigger persistence of current jobs
// which is done on another thread and until it finishes the spooler has
// to be kept around.
IPrintManager printManager = IPrintManager.Stub.asInterface(
ServiceManager.getService(Context.PRINT_SERVICE));
try {
printManager.restartPrintJob(printJobId, PrintManager.APP_ID_ANY,
UserHandle.myUserId());
} catch (RemoteException re) {
Log.i(LOG_TAG, "Error requestion print job restart", re);
}
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
// We need to request the restart to be done by the print manager
// service since the latter must be bound to the spooler because
// restarting a print job will trigger persistence of current jobs
// which is done on another thread and until it finishes the spooler has
// to be kept around.
try {
IPrintManager printManager = IPrintManager.Stub.asInterface(
ServiceManager.getService(Context.PRINT_SERVICE));
printManager.restartPrintJob(printJobId, PrintManager.APP_ID_ANY,
UserHandle.myUserId());
} catch (RemoteException re) {
Log.i(LOG_TAG, "Error requestion print job restart", re);
} finally {
wakeLock.release();
}
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
}
}
}

View File

@ -161,7 +161,7 @@ public class PrintJobConfigActivity extends Activity {
mCurrPrintAttributes.copyFrom(attributes);
}
mSpooler = PrintSpooler.getInstance(this);
mSpooler = PrintSpooler.peekInstance();
mEditor = new Editor();
mDocument = new Document();
mController = new PrintController(new RemotePrintDocumentAdapter(

View File

@ -19,13 +19,8 @@ package com.android.printspooler;
import android.content.ComponentName;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.print.IPrintClient;
import android.print.IPrintSpoolerClient;
import android.print.IPrinterDiscoveryObserver;
import android.print.PageRange;
import android.print.PrintAttributes;
@ -42,7 +37,6 @@ import android.util.Log;
import android.util.Slog;
import android.util.Xml;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.FastXmlSerializer;
import libcore.io.IoUtils;
@ -57,9 +51,7 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class PrintSpooler {
@ -87,68 +79,38 @@ public class PrintSpooler {
private final NotificationController mNotificationController;
private final Handler mHandler;
private final PrintSpoolerService mService;
private final Context mContext;
public IPrintSpoolerClient mClient;
public static PrintSpooler getInstance(Context context) {
public static void destroyInstance() {
synchronized (sLock) {
sInstance = null;
}
}
public static void createInstance(PrintSpoolerService service) {
synchronized (sLock) {
sInstance = new PrintSpooler(service);
}
}
public static PrintSpooler peekInstance() {
synchronized (sLock) {
if (sInstance == null) {
sInstance = new PrintSpooler(context);
}
return sInstance;
}
}
private PrintSpooler(Context context) {
mContext = context;
mPersistanceManager = new PersistenceManager(context);
mNotificationController = new NotificationController(context);
mHandler = new MyHandler(context.getMainLooper());
}
public void setCleint(IPrintSpoolerClient client) {
synchronized (mLock) {
mClient = client;
}
}
public void restorePersistedState() {
private PrintSpooler(PrintSpoolerService service) {
mService = service;
mPersistanceManager = new PersistenceManager(service);
mNotificationController = new NotificationController(service);
synchronized (mLock) {
mPersistanceManager.readStateLocked();
handleReadPrintJobsLocked();
}
}
public void onReqeustUpdatePrinters(List<PrinterId> printers) {
synchronized (mLock) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = mClient;
args.arg2 = printers;
mHandler.obtainMessage(MyHandler.MSG_ON_REQUEST_UPDATE_PRINTERS,
args).sendToTarget();
}
}
public void startPrinterDiscovery(IPrinterDiscoveryObserver observer) {
synchronized (mLock) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = mClient;
args.arg2 = observer;
mHandler.obtainMessage(MyHandler.MSG_ON_START_PRINTER_DISCOVERY,
args).sendToTarget();
}
}
public void stopPrinterDiscovery() {
synchronized (mLock) {
mHandler.obtainMessage(MyHandler.MSG_ON_STOP_PRINTER_DISCOVERY,
mClient).sendToTarget();
}
}
public List<PrintJobInfo> getPrintJobInfos(ComponentName componentName, int state, int appId) {
public List<PrintJobInfo> getPrintJobInfos(ComponentName componentName,
int state, int appId) {
List<PrintJobInfo> foundPrintJobs = null;
synchronized (mLock) {
final int printJobCount = mPrintJobs.size();
@ -207,79 +169,48 @@ public class PrintSpooler {
}
}
public void notifyClientForActivteJobs() {
IPrintSpoolerClient client = null;
Map<ComponentName, List<PrintJobInfo>> activeJobsPerServiceMap =
new HashMap<ComponentName, List<PrintJobInfo>>();
private void handleReadPrintJobsLocked() {
final int printJobCount = mPrintJobs.size();
for (int i = 0; i < printJobCount; i++) {
PrintJobInfo printJob = mPrintJobs.get(i);
// Update the notification.
mNotificationController.onPrintJobStateChanged(printJob);
//TODO: Figure out what the right policy for read print jobs is.
switch (printJob.getState()) {
case PrintJobInfo.STATE_QUEUED: {
// Notify that we have a queued job.
mService.onPrintJobQueued(new PrintJobInfo(printJob));
} break;
case PrintJobInfo.STATE_STARTED: {
// We really want to restart this print job.
setPrintJobState(printJob.getId(), PrintJobInfo.STATE_QUEUED, null);
} break;
}
}
}
public void checkAllPrintJobsHandled() {
synchronized (mLock) {
if (mClient == null) {
throw new IllegalStateException("Client cannot be null.");
}
client = mClient;
final int printJobCount = mPrintJobs.size();
for (int i = 0; i < printJobCount; i++) {
PrintJobInfo printJob = mPrintJobs.get(i);
switch (printJob.getState()) {
case PrintJobInfo.STATE_CREATED: {
/* skip - not ready to be handled by a service */
} break;
case PrintJobInfo.STATE_QUEUED:
case PrintJobInfo.STATE_STARTED: {
ComponentName service = printJob.getPrinterId().getServiceName();
List<PrintJobInfo> jobsPerService = activeJobsPerServiceMap.get(service);
if (jobsPerService == null) {
jobsPerService = new ArrayList<PrintJobInfo>();
activeJobsPerServiceMap.put(service, jobsPerService);
}
jobsPerService.add(printJob);
} break;
default: {
ComponentName service = printJob.getPrinterId().getServiceName();
if (!activeJobsPerServiceMap.containsKey(service)) {
activeJobsPerServiceMap.put(service, null);
}
}
}
if (!hasActivePrintJobsLocked()) {
notifyOnAllPrintJobsHandled();
}
}
}
boolean allPrintJobsHandled = true;
public void startPrinterDiscovery(IPrinterDiscoveryObserver observer) {
mService.startPrinterDiscovery(observer);
}
for (Map.Entry<ComponentName, List<PrintJobInfo>> entry
: activeJobsPerServiceMap.entrySet()) {
ComponentName service = entry.getKey();
List<PrintJobInfo> printJobs = entry.getValue();
public void stopPrinterDiscovery() {
mService.stopPrinterDiscovery();
}
if (printJobs != null) {
allPrintJobsHandled = false;
final int printJobCount = printJobs.size();
for (int i = 0; i < printJobCount; i++) {
PrintJobInfo printJob = printJobs.get(i);
if (printJob.getState() == PrintJobInfo.STATE_QUEUED) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = client;
args.arg2 = new PrintJobInfo(printJob);
mHandler.obtainMessage(MyHandler.MSG_ON_PRINT_JOB_QUEUED,
args).sendToTarget();
}
}
} else {
SomeArgs args = SomeArgs.obtain();
args.arg1 = client;
args.arg2 = service;
mHandler.obtainMessage(MyHandler.MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED,
args).sendToTarget();
}
}
if (allPrintJobsHandled) {
mHandler.obtainMessage(MyHandler.MSG_ON_ALL_PRINT_JOBS_HANDLED,
client).sendToTarget();
}
public void onReqeustUpdatePrinters(List<PrinterId> printerIds) {
mService.onReqeustUpdatePrinters(printerIds);
}
private int generatePrintJobIdLocked() {
@ -341,7 +272,7 @@ public class PrintSpooler {
}
public File generateFileForPrintJob(int printJobId) {
return new File(mContext.getFilesDir(), "print_job_"
return new File(mService.getFilesDir(), "print_job_"
+ printJobId + "." + PRINT_FILE_EXTENSION);
}
@ -365,88 +296,67 @@ public class PrintSpooler {
boolean success = false;
synchronized (mLock) {
if (mClient == null) {
throw new IllegalStateException("Client cannot be null.");
}
PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
if (printJob != null) {
success = true;
final int oldState = printJob.getState();
printJob.setState(state);
printJob.setFailureReason(error);
mNotificationController.onPrintJobStateChanged(printJob, oldState);
mNotificationController.onPrintJobStateChanged(printJob);
if (DEBUG_PRINT_JOB_LIFECYCLE) {
Slog.i(LOG_TAG, "[STATE CHANGED] " + printJob);
}
// TODO: Update notifications.
switch (state) {
case PrintJobInfo.STATE_COMPLETED:
case PrintJobInfo.STATE_CANCELED: {
case PrintJobInfo.STATE_CANCELED:
removePrintJobLocked(printJob);
// No printer means creation of a print job was cancelled,
// therefore the state of the spooler did not change and no
// notifications are needed. We also do not need to persist
// the state.
// $fall-through$
case PrintJobInfo.STATE_FAILED: {
PrinterId printerId = printJob.getPrinterId();
if (printerId == null) {
return true;
}
ComponentName service = printerId.getServiceName();
if (!hasActivePrintJobsForServiceLocked(service)) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = mClient;
args.arg2 = service;
mHandler.obtainMessage(
MyHandler.MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED,
args).sendToTarget();
}
if (!hasActivePrintJobsLocked()) {
mHandler.obtainMessage(MyHandler.MSG_ON_ALL_PRINT_JOBS_HANDLED,
mClient).sendToTarget();
if (printerId != null) {
ComponentName service = printerId.getServiceName();
if (!hasActivePrintJobsForServiceLocked(service)) {
mService.onAllPrintJobsForServiceHandled(service);
}
}
} break;
case PrintJobInfo.STATE_QUEUED: {
SomeArgs args = SomeArgs.obtain();
args.arg1 = mClient;
args.arg2 = new PrintJobInfo(printJob);
mHandler.obtainMessage(MyHandler.MSG_ON_PRINT_JOB_QUEUED,
args).sendToTarget();
mService.onPrintJobQueued(new PrintJobInfo(printJob));
} break;
}
if (shouldPersistPrintJob(printJob)) {
mPersistanceManager.writeStateLocked();
}
if (!hasActivePrintJobsLocked()) {
notifyOnAllPrintJobsHandled();
}
}
}
return success;
}
private boolean hasActivePrintJobsLocked() {
public boolean hasActivePrintJobsLocked() {
final int printJobCount = mPrintJobs.size();
for (int i = 0; i < printJobCount; i++) {
PrintJobInfo printJob = mPrintJobs.get(i);
if (!isActiveState(printJob.getState())) {
if (isActiveState(printJob.getState())) {
return true;
}
}
return false;
}
private boolean hasActivePrintJobsForServiceLocked(ComponentName service) {
public boolean hasActivePrintJobsForServiceLocked(ComponentName service) {
final int printJobCount = mPrintJobs.size();
for (int i = 0; i < printJobCount; i++) {
PrintJobInfo printJob = mPrintJobs.get(i);
if (!isActiveState(printJob.getState())
if (isActiveState(printJob.getState())
&& printJob.getPrinterId().getServiceName().equals(service)) {
return true;
}
@ -455,9 +365,9 @@ public class PrintSpooler {
}
private static boolean isActiveState(int printJobState) {
return printJobState != PrintJobInfo.STATE_CREATED
|| printJobState != PrintJobInfo.STATE_QUEUED
|| printJobState != PrintJobInfo.STATE_STARTED;
return printJobState == PrintJobInfo.STATE_CREATED
|| printJobState == PrintJobInfo.STATE_QUEUED
|| printJobState == PrintJobInfo.STATE_STARTED;
}
public boolean setPrintJobTag(int printJobId, String tag) {
@ -531,6 +441,20 @@ public class PrintSpooler {
return printJob.getState() >= PrintJobInfo.STATE_QUEUED;
}
private void notifyOnAllPrintJobsHandled() {
// This has to run on the tread that is persisting the current state
// since this call may result in the system unbinding from the spooler
// and as a result the spooler process may get killed before the write
// completes.
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
mService.onAllPrintJobsHandled();
return null;
}
}.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
}
private final class PersistenceManager {
private static final String PERSIST_FILE_NAME = "print_spooler_state.xml";
@ -1056,108 +980,4 @@ public class PrintSpooler {
return true;
}
}
private final class MyHandler extends Handler {
public static final int MSG_ON_START_PRINTER_DISCOVERY = 1;
public static final int MSG_ON_STOP_PRINTER_DISCOVERY = 2;
public static final int MSG_ON_PRINT_JOB_QUEUED = 3;
public static final int MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED = 4;
public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 5;
public static final int MSG_ON_REQUEST_UPDATE_PRINTERS = 6;
public MyHandler(Looper looper) {
super(looper, null, false);
}
@Override
@SuppressWarnings("unchecked")
public void handleMessage(Message message) {
switch (message.what) {
case MSG_ON_START_PRINTER_DISCOVERY: {
SomeArgs args = (SomeArgs) message.obj;
IPrintSpoolerClient client = (IPrintSpoolerClient) args.arg1;
IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) args.arg2;
args.recycle();
if (client != null) {
try {
client.onStartPrinterDiscovery(observer);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error notifying start printer discovery.", re);
}
}
} break;
case MSG_ON_STOP_PRINTER_DISCOVERY: {
IPrintSpoolerClient client = (IPrintSpoolerClient) message.obj;
if (client != null) {
try {
client.onStopPrinterDiscovery();
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error notifying stop printer discovery.", re);
}
}
} break;
case MSG_ON_PRINT_JOB_QUEUED: {
SomeArgs args = (SomeArgs) message.obj;
IPrintSpoolerClient client = (IPrintSpoolerClient) args.arg1;
PrintJobInfo printJob = (PrintJobInfo) args.arg2;
args.recycle();
if (client != null) {
try {
client.onPrintJobQueued(printJob);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error notify for a queued print job.", re);
}
}
} break;
case MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED: {
SomeArgs args = (SomeArgs) message.obj;
IPrintSpoolerClient client = (IPrintSpoolerClient) args.arg1;
ComponentName service = (ComponentName) args.arg2;
args.recycle();
if (client != null) {
try {
client.onAllPrintJobsForServiceHandled(service);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error notify for all print jobs per service"
+ " handled.", re);
}
}
} break;
case MSG_ON_ALL_PRINT_JOBS_HANDLED: {
final IPrintSpoolerClient client = (IPrintSpoolerClient) message.obj;
// This has to run on the tread that is persisting the current state
// since this call may result in the system unbinding from the spooler
// and as a result the spooler process may get killed before the write
// completes.
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
try {
client.onAllPrintJobsHandled();
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error notify for all print job handled.", re);
}
return null;
}
}.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
} break;
case MSG_ON_REQUEST_UPDATE_PRINTERS: {
SomeArgs args = (SomeArgs) message.obj;
IPrintSpoolerClient client = (IPrintSpoolerClient) args.arg1;
List<PrinterId> printerIds = (List<PrinterId>) args.arg2;
args.recycle();
try {
client.onRequestUpdatePrinters(printerIds);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error requesting to update pritners.", re);
}
} break;
}
}
}
}

View File

@ -16,8 +16,6 @@
package com.android.printspooler;
import java.util.List;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ComponentName;
@ -29,44 +27,48 @@ import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.print.IPrintDocumentAdapter;
import android.print.IPrintClient;
import android.print.IPrintSpoolerClient;
import android.print.IPrintDocumentAdapter;
import android.print.IPrintSpooler;
import android.print.IPrintSpoolerCallbacks;
import android.print.IPrintSpoolerClient;
import android.print.IPrinterDiscoveryObserver;
import android.print.PrintAttributes;
import android.print.PrintJobInfo;
import android.print.PrinterId;
import android.util.Log;
import android.util.Slog;
import com.android.internal.os.SomeArgs;
import java.util.List;
/**
* Service for exposing some of the {@link PrintSpooler} functionality to
* another process.
*/
public final class PrintSpoolerService extends Service {
private static final long CHECK_ALL_PRINTJOBS_HANDLED_DELAY = 5000;
private static final String LOG_TAG = "PrintSpoolerService";
private Intent mStartPrintJobConfigActivityIntent;
private PrintSpooler mSpooler;
private IPrintSpoolerClient mClient;
private Handler mHanlder;
private Handler mHandler;
@Override
public void onCreate() {
super.onCreate();
mStartPrintJobConfigActivityIntent = new Intent(PrintSpoolerService.this,
PrintJobConfigActivity.class);
mSpooler = PrintSpooler.getInstance(this);
mHanlder = new MyHandler(getMainLooper());
mHandler = new MyHandler(getMainLooper());
}
@Override
public IBinder onBind(Intent intent) {
mSpooler.restorePersistedState();
return new IPrintSpooler.Stub() {
@Override
public void getPrintJobInfos(IPrintSpoolerCallbacks callback,
@ -74,7 +76,8 @@ public final class PrintSpoolerService extends Service {
throws RemoteException {
List<PrintJobInfo> printJobs = null;
try {
printJobs = mSpooler.getPrintJobInfos(componentName, state, appId);
printJobs = PrintSpooler.peekInstance().getPrintJobInfos(
componentName, state, appId);
} finally {
callback.onGetPrintJobInfosResult(printJobs, sequence);
}
@ -85,7 +88,7 @@ public final class PrintSpoolerService extends Service {
int appId, int sequence) throws RemoteException {
PrintJobInfo printJob = null;
try {
printJob = mSpooler.getPrintJobInfo(printJobId, appId);
printJob = PrintSpooler.peekInstance().getPrintJobInfo(printJobId, appId);
} finally {
callback.onGetPrintJobInfoResult(printJob, sequence);
}
@ -99,7 +102,7 @@ public final class PrintSpoolerService extends Service {
throws RemoteException {
PrintJobInfo printJob = null;
try {
printJob = mSpooler.createPrintJob(printJobName, client,
printJob = PrintSpooler.peekInstance().createPrintJob(printJobName, client,
attributes, appId);
if (printJob != null) {
Intent intent = mStartPrintJobConfigActivityIntent;
@ -116,7 +119,8 @@ public final class PrintSpoolerService extends Service {
SomeArgs args = SomeArgs.obtain();
args.arg1 = client;
args.arg2 = sender;
mHanlder.obtainMessage(0, args).sendToTarget();
mHandler.obtainMessage(MyHandler.MSG_START_PRINT_JOB_CONFIG_ACTIVITY,
args).sendToTarget();
}
} finally {
callback.onCreatePrintJobResult(printJob, sequence);
@ -128,7 +132,8 @@ public final class PrintSpoolerService extends Service {
IPrintSpoolerCallbacks callback, int sequece) throws RemoteException {
boolean success = false;
try {
success = mSpooler.setPrintJobState(printJobId, state, error);
success = PrintSpooler.peekInstance().setPrintJobState(
printJobId, state, error);
} finally {
callback.onSetPrintJobStateResult(success, sequece);
}
@ -136,11 +141,10 @@ public final class PrintSpoolerService extends Service {
@Override
public void setPrintJobTag(int printJobId, String tag,
IPrintSpoolerCallbacks callback, int sequece)
throws RemoteException {
IPrintSpoolerCallbacks callback, int sequece) throws RemoteException {
boolean success = false;
try {
success = mSpooler.setPrintJobTag(printJobId, tag);
success = PrintSpooler.peekInstance().setPrintJobTag(printJobId, tag);
} finally {
callback.onSetPrintJobTagResult(success, sequece);
}
@ -148,37 +152,158 @@ public final class PrintSpoolerService extends Service {
@Override
public void writePrintJobData(ParcelFileDescriptor fd, int printJobId) {
mSpooler.writePrintJobData(fd, printJobId);
PrintSpooler.peekInstance().writePrintJobData(fd, printJobId);
}
@Override
public void setClient(IPrintSpoolerClient client) {
mSpooler.setCleint(client);
}
@Override
public void notifyClientForActivteJobs() {
mSpooler.notifyClientForActivteJobs();
public void setClient(IPrintSpoolerClient client) {
mHandler.obtainMessage(MyHandler.MSG_SET_CLIENT, client).sendToTarget();
}
};
}
private static final class MyHandler extends Handler {
public void onPrintJobQueued(PrintJobInfo printJob) {
mHandler.obtainMessage(MyHandler.MSG_ON_PRINT_JOB_QUEUED,
printJob).sendToTarget();
}
public void onReqeustUpdatePrinters(List<PrinterId> printers) {
mHandler.obtainMessage(MyHandler.MSG_ON_REQUEST_UPDATE_PRINTERS,
printers).sendToTarget();
}
public void startPrinterDiscovery(IPrinterDiscoveryObserver observer) {
mHandler.obtainMessage(MyHandler.MSG_ON_START_PRINTER_DISCOVERY,
observer).sendToTarget();
}
public void stopPrinterDiscovery() {
mHandler.sendEmptyMessage(MyHandler.MSG_ON_STOP_PRINTER_DISCOVERY);
}
public void onAllPrintJobsForServiceHandled(ComponentName service) {
mHandler.obtainMessage(MyHandler.MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED,
service).sendToTarget();
}
public void onAllPrintJobsHandled() {
mHandler.sendEmptyMessage(MyHandler.MSG_ON_ALL_PRINT_JOBS_HANDLED);
}
private final class MyHandler extends Handler {
public static final int MSG_SET_CLIENT = 1;
public static final int MSG_START_PRINT_JOB_CONFIG_ACTIVITY = 2;
public static final int MSG_ON_START_PRINTER_DISCOVERY = 3;
public static final int MSG_ON_STOP_PRINTER_DISCOVERY = 4;
public static final int MSG_ON_PRINT_JOB_QUEUED = 5;
public static final int MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED = 6;
public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 7;
public static final int MSG_ON_REQUEST_UPDATE_PRINTERS = 8;
public static final int MSG_CHECK_ALL_PRINTJOBS_HANDLED = 9;
public MyHandler(Looper looper) {
super(looper, null, true);
super(looper, null, false);
}
@Override
@SuppressWarnings("unchecked")
public void handleMessage(Message message) {
SomeArgs args = (SomeArgs) message.obj;
IPrintClient client = (IPrintClient) args.arg1;
IntentSender sender = (IntentSender) args.arg2;
args.recycle();
try {
client.startPrintJobConfigActivity(sender);
} catch (RemoteException re) {
Slog.i(LOG_TAG, "Error starting print job config activity!", re);
switch (message.what) {
case MSG_SET_CLIENT: {
mClient = (IPrintSpoolerClient) message.obj;
if (mClient != null) {
PrintSpooler.createInstance(PrintSpoolerService.this);
mHandler.sendEmptyMessageDelayed(
MyHandler.MSG_CHECK_ALL_PRINTJOBS_HANDLED,
CHECK_ALL_PRINTJOBS_HANDLED_DELAY);
} else {
PrintSpooler.destroyInstance();
}
} break;
case MSG_START_PRINT_JOB_CONFIG_ACTIVITY: {
SomeArgs args = (SomeArgs) message.obj;
IPrintClient client = (IPrintClient) args.arg1;
IntentSender sender = (IntentSender) args.arg2;
args.recycle();
try {
client.startPrintJobConfigActivity(sender);
} catch (RemoteException re) {
Slog.i(LOG_TAG, "Error starting print job config activity!", re);
}
} break;
case MSG_ON_START_PRINTER_DISCOVERY: {
IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) message.obj;
if (mClient != null) {
try {
mClient.onStartPrinterDiscovery(observer);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error notifying start printer discovery.", re);
}
}
} break;
case MSG_ON_STOP_PRINTER_DISCOVERY: {
if (mClient != null) {
try {
mClient.onStopPrinterDiscovery();
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error notifying stop printer discovery.", re);
}
}
} break;
case MSG_ON_PRINT_JOB_QUEUED: {
PrintJobInfo printJob = (PrintJobInfo) message.obj;
if (mClient != null) {
try {
mClient.onPrintJobQueued(printJob);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error notify for a queued print job.", re);
}
}
} break;
case MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED: {
ComponentName service = (ComponentName) message.obj;
if (mClient != null) {
try {
mClient.onAllPrintJobsForServiceHandled(service);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error notify for all print jobs per service"
+ " handled.", re);
}
}
} break;
case MSG_ON_ALL_PRINT_JOBS_HANDLED: {
if (mClient != null) {
try {
mClient.onAllPrintJobsHandled();
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error notify for all print job handled.", re);
}
}
} break;
case MSG_ON_REQUEST_UPDATE_PRINTERS: {
List<PrinterId> printerIds = (List<PrinterId>) message.obj;
if (mClient != null) {
try {
mClient.onRequestUpdatePrinters(printerIds);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error requesting to update pritners.", re);
}
}
} break;
case MSG_CHECK_ALL_PRINTJOBS_HANDLED: {
PrintSpooler spooler = PrintSpooler.peekInstance();
if (spooler != null) {
spooler.checkAllPrintJobsHandled();
}
} break;
}
}
}

View File

@ -2047,29 +2047,39 @@ public class NotificationManagerService extends INotificationManager.Stub
* Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
* and none of the {@code mustNotHaveFlags}.
*/
private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags,
int mustNotHaveFlags, boolean sendDelete, int userId) {
EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, tag, userId,
mustHaveFlags, mustNotHaveFlags);
private void cancelNotification(final String pkg, final String tag, final int id,
final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
final int userId) {
// In enqueueNotificationInternal notifications are added by scheduling the
// work on the worker handler. Hence, we also schedule the cancel on this
// handler to avoid a scenario where an add notification call followed by a
// remove notification call ends up in not removing the notification.
mHandler.post(new Runnable() {
@Override
public void run() {
EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, tag, userId,
mustHaveFlags, mustNotHaveFlags);
synchronized (mNotificationList) {
int index = indexOfNotificationLocked(pkg, tag, id, userId);
if (index >= 0) {
NotificationRecord r = mNotificationList.get(index);
synchronized (mNotificationList) {
int index = indexOfNotificationLocked(pkg, tag, id, userId);
if (index >= 0) {
NotificationRecord r = mNotificationList.get(index);
if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
return;
if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
return;
}
if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
return;
}
mNotificationList.remove(index);
cancelNotificationLocked(r, sendDelete);
updateLightsLocked();
}
}
if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
return;
}
mNotificationList.remove(index);
cancelNotificationLocked(r, sendDelete);
updateLightsLocked();
}
}
});
}
/**

View File

@ -68,7 +68,7 @@ public final class PrintManagerService extends IPrintManager.Stub {
synchronized (mLock) {
UserState userState = getCurrentUserStateLocked();
userState.updateIfNeededLocked();
userState.getSpoolerLocked().notifyClientForActivteJobs();
userState.getSpoolerLocked().start();
}
}
});
@ -144,7 +144,7 @@ public final class PrintManagerService extends IPrintManager.Stub {
}
final long identity = Binder.clearCallingIdentity();
try {
PrintJobInfo printJobInfo = getPrintJobInfo(printJobId, resolvedAppId, resolvedUserId);
PrintJobInfo printJobInfo = spooler.getPrintJobInfo(printJobId, resolvedAppId);
if (printJobInfo == null) {
return;
}
@ -152,7 +152,7 @@ public final class PrintManagerService extends IPrintManager.Stub {
ComponentName printServiceName = printJobInfo.getPrinterId().getServiceName();
RemotePrintService printService = null;
synchronized (mLock) {
printService = userState.getActiveServices().get(printServiceName);
printService = userState.getActiveServicesLocked().get(printServiceName);
}
if (printService == null) {
return;
@ -328,7 +328,7 @@ public final class PrintManagerService extends IPrintManager.Stub {
mCurrentUserId = newUserId;
UserState userState = getCurrentUserStateLocked();
userState.updateIfNeededLocked();
userState.getSpoolerLocked().notifyClientForActivteJobs();
userState.getSpoolerLocked().start();
}
}

View File

@ -228,19 +228,19 @@ final class RemotePrintService implements DeathRecipient {
printerIds).sendToTarget();
}
private void handleReqeustUpdatePritners(final List<PrinterId> printerIds) {
private void handleReqeustUpdatePrinters(final List<PrinterId> printerIds) {
throwIfDestroyed();
if (!isBound()) {
ensureBound();
mPendingCommands.add(new Runnable() {
@Override
public void run() {
handleReqeustUpdatePritners(printerIds);
handleReqeustUpdatePrinters(printerIds);
}
});
} else {
if (DEBUG) {
Slog.i(LOG_TAG, "[user: " + mUserId + "] handleReqeustUpdatePritners()");
Slog.i(LOG_TAG, "[user: " + mUserId + "] handleReqeustUpdatePrinters()");
}
try {
mPrintService.onRequestUpdatePrinters(printerIds);
@ -367,7 +367,7 @@ final class RemotePrintService implements DeathRecipient {
case MSG_ON_REQUEST_UPDATE_PRINTERS: {
List<PrinterId> printerIds = (List<PrinterId>) message.obj;
handleReqeustUpdatePritners(printerIds);
handleReqeustUpdatePrinters(printerIds);
} break;
case MSG_ON_STOP_PRINTER_DISCOVERY: {

View File

@ -116,9 +116,6 @@ final class RemotePrintSpooler {
throwIfDestroyedLocked();
mCanUnbind = false;
}
if (DEBUG) {
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] getPrintJobInfos()");
}
try {
return mGetPrintJobInfosCaller.getPrintJobInfos(getRemoteInstanceLazy(),
componentName, state, appId);
@ -127,6 +124,9 @@ final class RemotePrintSpooler {
} catch (TimeoutException te) {
Slog.e(LOG_TAG, "Error getting print jobs.", te);
} finally {
if (DEBUG) {
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] getPrintJobInfos()");
}
synchronized (mLock) {
mCanUnbind = true;
mLock.notifyAll();
@ -142,9 +142,6 @@ final class RemotePrintSpooler {
throwIfDestroyedLocked();
mCanUnbind = false;
}
if (DEBUG) {
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] createPrintJob()");
}
try {
return mCreatePrintJobCaller.createPrintJob(getRemoteInstanceLazy(),
printJobName, client, documentAdapter, attributes, appId);
@ -153,6 +150,9 @@ final class RemotePrintSpooler {
} catch (TimeoutException te) {
Slog.e(LOG_TAG, "Error creating print job.", te);
} finally {
if (DEBUG) {
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] createPrintJob()");
}
synchronized (mLock) {
mCanUnbind = true;
mLock.notifyAll();
@ -167,9 +167,6 @@ final class RemotePrintSpooler {
throwIfDestroyedLocked();
mCanUnbind = false;
}
if (DEBUG) {
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] writePrintJobData()");
}
try {
getRemoteInstanceLazy().writePrintJobData(fd, printJobId);
} catch (RemoteException re) {
@ -177,6 +174,9 @@ final class RemotePrintSpooler {
} catch (TimeoutException te) {
Slog.e(LOG_TAG, "Error writing print job data.", te);
} finally {
if (DEBUG) {
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] writePrintJobData()");
}
// We passed the file descriptor across and now the other
// side is responsible to close it, so close the local copy.
IoUtils.closeQuietly(fd);
@ -193,9 +193,6 @@ final class RemotePrintSpooler {
throwIfDestroyedLocked();
mCanUnbind = false;
}
if (DEBUG) {
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] getPrintJobInfo()");
}
try {
return mGetPrintJobInfoCaller.getPrintJobInfo(getRemoteInstanceLazy(),
printJobId, appId);
@ -204,6 +201,9 @@ final class RemotePrintSpooler {
} catch (TimeoutException te) {
Slog.e(LOG_TAG, "Error getting print job info.", te);
} finally {
if (DEBUG) {
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] getPrintJobInfo()");
}
synchronized (mLock) {
mCanUnbind = true;
mLock.notifyAll();
@ -218,9 +218,6 @@ final class RemotePrintSpooler {
throwIfDestroyedLocked();
mCanUnbind = false;
}
if (DEBUG) {
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setPrintJobState()");
}
try {
return mSetPrintJobStatusCaller.setPrintJobState(getRemoteInstanceLazy(),
printJobId, state, error);
@ -229,6 +226,9 @@ final class RemotePrintSpooler {
} catch (TimeoutException te) {
Slog.e(LOG_TAG, "Error setting print job state.", te);
} finally {
if (DEBUG) {
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setPrintJobState()");
}
synchronized (mLock) {
mCanUnbind = true;
mLock.notifyAll();
@ -243,9 +243,6 @@ final class RemotePrintSpooler {
throwIfDestroyedLocked();
mCanUnbind = false;
}
if (DEBUG) {
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setPrintJobTag()");
}
try {
return mSetPrintJobTagCaller.setPrintJobTag(getRemoteInstanceLazy(),
printJobId, tag);
@ -254,6 +251,9 @@ final class RemotePrintSpooler {
} catch (TimeoutException te) {
Slog.e(LOG_TAG, "Error setting print job tag.", te);
} finally {
if (DEBUG) {
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setPrintJobTag()");
}
synchronized (mLock) {
mCanUnbind = true;
mLock.notifyAll();
@ -262,23 +262,20 @@ final class RemotePrintSpooler {
return false;
}
public final void notifyClientForActivteJobs() {
public final void start() {
throwIfCalledOnMainThread();
synchronized (mLock) {
throwIfDestroyedLocked();
mCanUnbind = false;
}
if (DEBUG) {
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier()
+ "] notifyClientForActivteJobs()");
}
try {
getRemoteInstanceLazy().notifyClientForActivteJobs();
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error asking for active print job notification.", re);
getRemoteInstanceLazy();
} catch (TimeoutException te) {
Slog.e(LOG_TAG, "Error asking for active print job notification.", te);
Slog.e(LOG_TAG, "Error starting the spooler.", te);
} finally {
if (DEBUG) {
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] start()");
}
synchronized (mLock) {
mCanUnbind = true;
mLock.notifyAll();

View File

@ -166,7 +166,7 @@ final class UserState implements PrintSpoolerCallbacks {
return mSpooler;
}
public Map<ComponentName, RemotePrintService> getActiveServices() {
public Map<ComponentName, RemotePrintService> getActiveServicesLocked() {
synchronized(mLock) {
throwIfDestroyedLocked();
return mActiveServices;