1. The UI of a printing app was freezing a little when calling the print method since the print manager service was waiting for it to bind to the print spooler which generated the print job id (and the initial print job info really). Now the print manager service is responsible for job id generation and does not not wait for the print spooler to spin. Hence, the app UI is not blocked at all. Note that the print manager initiates the binding to the spooler and as soon as it completes the spooler shows the print UI which is hosted in its process. It is not possible to show the print UI before the system is bound to the spooler since during this binding the system passes a callback to the spooler so the latter can talk to the system. 2. Changed the print job id to be an opaque class allowing us to vary the way we generate print job ids in the future. 3. The queued print job state was hidden but the print job returned by the print method of the print manager is in that state. Now now hidden. 4. We were incorrecly removing print job infos if they are completed or cancelled. Doing that is problematic since the print job returned by the print method allows the app to query for the job info after the job has been say completed. Hence, an app can initiate printing and get a print job whose state is "created" and hold onto it until after the job is completed, now if the app asks for the print job info it will get an info in "created" state even though the job is "completed" since the spooler was not retaining the completed jobs. Now the spooler removes the PDF files for the completed and cancelled print jobs but keeps around the infos (also persisting them to disc) so it can answer questions about them. On first boot or switch to a user we purge the persisted print jobs in completed/cancelled state since they are obsolete - no app can have a handle to them. 5. Removed the print method that takes a file since we have a public PrintDocumentAdapter implementation for printing files. Once can instantiate a PrintFileDocumentAdapter and pass it to the print method. This class also allows overriding of the finish method to know when the data is spooled and deleted the file if desired, etc. 6. Replaced the wrong code to slice a large list of parcelables to use ParceledListSlice class. bug:10748093 Change-Id: I1ebeeb47576e88fce550851cdd3e401fcede6e2b
623 lines
21 KiB
Java
623 lines
21 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.ServiceConnection;
|
|
import android.os.Binder;
|
|
import android.os.IBinder;
|
|
import android.os.ParcelFileDescriptor;
|
|
import android.os.RemoteException;
|
|
import android.os.SystemClock;
|
|
import android.os.UserHandle;
|
|
import android.print.IPrintClient;
|
|
import android.print.IPrintDocumentAdapter;
|
|
import android.print.IPrintSpooler;
|
|
import android.print.IPrintSpoolerCallbacks;
|
|
import android.print.IPrintSpoolerClient;
|
|
import android.print.PrintJobId;
|
|
import android.print.PrintJobInfo;
|
|
import android.print.PrintManager;
|
|
import android.util.Slog;
|
|
import android.util.TimedRemoteCaller;
|
|
|
|
import java.io.FileDescriptor;
|
|
import java.io.PrintWriter;
|
|
import java.lang.ref.WeakReference;
|
|
import java.util.List;
|
|
import java.util.concurrent.TimeoutException;
|
|
|
|
import libcore.io.IoUtils;
|
|
|
|
/**
|
|
* This represents the remote print spooler as a local object to the
|
|
* PrintManagerSerivce. It is responsible to connecting to the remote
|
|
* spooler if needed, to make the timed remote calls, to handle
|
|
* remote exceptions, and to bind/unbind to the remote instance as
|
|
* needed.
|
|
*/
|
|
final class RemotePrintSpooler {
|
|
|
|
private static final String LOG_TAG = "RemotePrintSpooler";
|
|
|
|
private static final boolean DEBUG = false;
|
|
|
|
private static final long BIND_SPOOLER_SERVICE_TIMEOUT = 10000;
|
|
|
|
private final Object mLock = new Object();
|
|
|
|
private final GetPrintJobInfosCaller mGetPrintJobInfosCaller = new GetPrintJobInfosCaller();
|
|
|
|
private final GetPrintJobInfoCaller mGetPrintJobInfoCaller = new GetPrintJobInfoCaller();
|
|
|
|
private final SetPrintJobStateCaller mSetPrintJobStatusCaller = new SetPrintJobStateCaller();
|
|
|
|
private final SetPrintJobTagCaller mSetPrintJobTagCaller = new SetPrintJobTagCaller();
|
|
|
|
private final ServiceConnection mServiceConnection = new MyServiceConnection();
|
|
|
|
private final Context mContext;
|
|
|
|
private final UserHandle mUserHandle;
|
|
|
|
private final PrintSpoolerClient mClient;
|
|
|
|
private final Intent mIntent;
|
|
|
|
private final PrintSpoolerCallbacks mCallbacks;
|
|
|
|
private IPrintSpooler mRemoteInstance;
|
|
|
|
private boolean mDestroyed;
|
|
|
|
private boolean mCanUnbind;
|
|
|
|
public static interface PrintSpoolerCallbacks {
|
|
public void onPrintJobQueued(PrintJobInfo printJob);
|
|
public void onAllPrintJobsForServiceHandled(ComponentName printService);
|
|
}
|
|
|
|
public RemotePrintSpooler(Context context, int userId,
|
|
PrintSpoolerCallbacks callbacks) {
|
|
mContext = context;
|
|
mUserHandle = new UserHandle(userId);
|
|
mCallbacks = callbacks;
|
|
mClient = new PrintSpoolerClient(this);
|
|
mIntent = new Intent();
|
|
mIntent.setComponent(new ComponentName("com.android.printspooler",
|
|
"com.android.printspooler.PrintSpoolerService"));
|
|
}
|
|
|
|
public final List<PrintJobInfo> getPrintJobInfos(ComponentName componentName, int state,
|
|
int appId) {
|
|
throwIfCalledOnMainThread();
|
|
synchronized (mLock) {
|
|
throwIfDestroyedLocked();
|
|
mCanUnbind = false;
|
|
}
|
|
try {
|
|
return mGetPrintJobInfosCaller.getPrintJobInfos(getRemoteInstanceLazy(),
|
|
componentName, state, appId);
|
|
} catch (RemoteException re) {
|
|
Slog.e(LOG_TAG, "Error getting print jobs.", re);
|
|
} 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();
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public final void createPrintJob(PrintJobInfo printJob, IPrintClient client,
|
|
IPrintDocumentAdapter documentAdapter) {
|
|
throwIfCalledOnMainThread();
|
|
synchronized (mLock) {
|
|
throwIfDestroyedLocked();
|
|
mCanUnbind = false;
|
|
}
|
|
try {
|
|
getRemoteInstanceLazy().createPrintJob(printJob, client, documentAdapter);
|
|
} catch (RemoteException re) {
|
|
Slog.e(LOG_TAG, "Error creating print job.", re);
|
|
} 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();
|
|
}
|
|
}
|
|
}
|
|
|
|
public final void writePrintJobData(ParcelFileDescriptor fd, PrintJobId printJobId) {
|
|
throwIfCalledOnMainThread();
|
|
synchronized (mLock) {
|
|
throwIfDestroyedLocked();
|
|
mCanUnbind = false;
|
|
}
|
|
try {
|
|
getRemoteInstanceLazy().writePrintJobData(fd, printJobId);
|
|
} catch (RemoteException re) {
|
|
Slog.e(LOG_TAG, "Error writing print job data.", re);
|
|
} 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);
|
|
synchronized (mLock) {
|
|
mCanUnbind = true;
|
|
mLock.notifyAll();
|
|
}
|
|
}
|
|
}
|
|
|
|
public final PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId) {
|
|
throwIfCalledOnMainThread();
|
|
synchronized (mLock) {
|
|
throwIfDestroyedLocked();
|
|
mCanUnbind = false;
|
|
}
|
|
try {
|
|
return mGetPrintJobInfoCaller.getPrintJobInfo(getRemoteInstanceLazy(),
|
|
printJobId, appId);
|
|
} catch (RemoteException re) {
|
|
Slog.e(LOG_TAG, "Error getting print job info.", re);
|
|
} 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();
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public final boolean setPrintJobState(PrintJobId printJobId, int state, String error) {
|
|
throwIfCalledOnMainThread();
|
|
synchronized (mLock) {
|
|
throwIfDestroyedLocked();
|
|
mCanUnbind = false;
|
|
}
|
|
try {
|
|
return mSetPrintJobStatusCaller.setPrintJobState(getRemoteInstanceLazy(),
|
|
printJobId, state, error);
|
|
} catch (RemoteException re) {
|
|
Slog.e(LOG_TAG, "Error setting print job state.", re);
|
|
} 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();
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public final boolean setPrintJobTag(PrintJobId printJobId, String tag) {
|
|
throwIfCalledOnMainThread();
|
|
synchronized (mLock) {
|
|
throwIfDestroyedLocked();
|
|
mCanUnbind = false;
|
|
}
|
|
try {
|
|
return mSetPrintJobTagCaller.setPrintJobTag(getRemoteInstanceLazy(),
|
|
printJobId, tag);
|
|
} catch (RemoteException re) {
|
|
Slog.e(LOG_TAG, "Error setting print job tag.", re);
|
|
} 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();
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public final void removeObsoletePrintJobs() {
|
|
throwIfCalledOnMainThread();
|
|
synchronized (mLock) {
|
|
throwIfDestroyedLocked();
|
|
mCanUnbind = false;
|
|
}
|
|
try {
|
|
getRemoteInstanceLazy().removeObsoletePrintJobs();
|
|
} catch (RemoteException re) {
|
|
Slog.e(LOG_TAG, "Error removing obsolete print jobs .", re);
|
|
} catch (TimeoutException te) {
|
|
Slog.e(LOG_TAG, "Error removing obsolete print jobs .", te);
|
|
} finally {
|
|
if (DEBUG) {
|
|
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier()
|
|
+ "] removeObsoletePrintJobs()");
|
|
}
|
|
synchronized (mLock) {
|
|
mCanUnbind = true;
|
|
mLock.notifyAll();
|
|
}
|
|
}
|
|
}
|
|
|
|
public final void forgetPrintJobs(List<PrintJobId> printJobIds) {
|
|
throwIfCalledOnMainThread();
|
|
synchronized (mLock) {
|
|
throwIfDestroyedLocked();
|
|
mCanUnbind = false;
|
|
}
|
|
try {
|
|
getRemoteInstanceLazy().forgetPrintJobs(printJobIds);
|
|
} catch (RemoteException re) {
|
|
Slog.e(LOG_TAG, "Error forgeting print jobs", re);
|
|
} catch (TimeoutException te) {
|
|
Slog.e(LOG_TAG, "Error forgeting print jobs", te);
|
|
} finally {
|
|
if (DEBUG) {
|
|
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier()
|
|
+ "] forgetPrintJobs()");
|
|
}
|
|
synchronized (mLock) {
|
|
mCanUnbind = true;
|
|
mLock.notifyAll();
|
|
}
|
|
}
|
|
}
|
|
|
|
public final void destroy() {
|
|
throwIfCalledOnMainThread();
|
|
if (DEBUG) {
|
|
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] destroy()");
|
|
}
|
|
synchronized (mLock) {
|
|
throwIfDestroyedLocked();
|
|
unbindLocked();
|
|
mDestroyed = true;
|
|
mCanUnbind = false;
|
|
}
|
|
}
|
|
|
|
public void dump(FileDescriptor fd, PrintWriter pw, String prefix) {
|
|
synchronized (mLock) {
|
|
pw.append(prefix).append("destroyed=")
|
|
.append(String.valueOf(mDestroyed)).println();
|
|
pw.append(prefix).append("bound=")
|
|
.append((mRemoteInstance != null) ? "true" : "false").println();
|
|
pw.append(prefix).append("print jobs:").println();
|
|
if (mRemoteInstance != null) {
|
|
List<PrintJobInfo> printJobs = getPrintJobInfos(null,
|
|
PrintJobInfo.STATE_ANY, PrintManager.APP_ID_ANY);
|
|
if (printJobs != null) {
|
|
final int printJobCount = printJobs.size();
|
|
for (int i = 0; i < printJobCount; i++) {
|
|
PrintJobInfo printJob = printJobs.get(i);
|
|
pw.append(prefix).append(prefix).append(printJob.toString());
|
|
pw.println();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void onAllPrintJobsHandled() {
|
|
synchronized (mLock) {
|
|
throwIfDestroyedLocked();
|
|
unbindLocked();
|
|
}
|
|
}
|
|
|
|
private IPrintSpooler getRemoteInstanceLazy() throws TimeoutException {
|
|
synchronized (mLock) {
|
|
if (mRemoteInstance != null) {
|
|
return mRemoteInstance;
|
|
}
|
|
bindLocked();
|
|
return mRemoteInstance;
|
|
}
|
|
}
|
|
|
|
private void bindLocked() throws TimeoutException {
|
|
if (mRemoteInstance != null) {
|
|
return;
|
|
}
|
|
if (DEBUG) {
|
|
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] bindLocked()");
|
|
}
|
|
|
|
mContext.bindServiceAsUser(mIntent, mServiceConnection,
|
|
Context.BIND_AUTO_CREATE, mUserHandle);
|
|
|
|
final long startMillis = SystemClock.uptimeMillis();
|
|
while (true) {
|
|
if (mRemoteInstance != null) {
|
|
break;
|
|
}
|
|
final long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
|
|
final long remainingMillis = BIND_SPOOLER_SERVICE_TIMEOUT - elapsedMillis;
|
|
if (remainingMillis <= 0) {
|
|
throw new TimeoutException("Cannot get spooler!");
|
|
}
|
|
try {
|
|
mLock.wait(remainingMillis);
|
|
} catch (InterruptedException ie) {
|
|
/* ignore */
|
|
}
|
|
}
|
|
|
|
mCanUnbind = true;
|
|
mLock.notifyAll();
|
|
}
|
|
|
|
private void unbindLocked() {
|
|
if (mRemoteInstance == null) {
|
|
return;
|
|
}
|
|
while (true) {
|
|
if (mCanUnbind) {
|
|
if (DEBUG) {
|
|
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] unbindLocked()");
|
|
}
|
|
clearClientLocked();
|
|
mRemoteInstance = null;
|
|
mContext.unbindService(mServiceConnection);
|
|
return;
|
|
}
|
|
try {
|
|
mLock.wait();
|
|
} catch (InterruptedException ie) {
|
|
/* ignore */
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private void setClientLocked() {
|
|
try {
|
|
mRemoteInstance.setClient(mClient);
|
|
} catch (RemoteException re) {
|
|
Slog.d(LOG_TAG, "Error setting print spooler client", re);
|
|
}
|
|
}
|
|
|
|
private void clearClientLocked() {
|
|
try {
|
|
mRemoteInstance.setClient(null);
|
|
} catch (RemoteException re) {
|
|
Slog.d(LOG_TAG, "Error clearing print spooler client", re);
|
|
}
|
|
|
|
}
|
|
|
|
private void throwIfDestroyedLocked() {
|
|
if (mDestroyed) {
|
|
throw new IllegalStateException("Cannot interact with a destroyed instance.");
|
|
}
|
|
}
|
|
|
|
private void throwIfCalledOnMainThread() {
|
|
if (Thread.currentThread() == mContext.getMainLooper().getThread()) {
|
|
throw new RuntimeException("Cannot invoke on the main thread");
|
|
}
|
|
}
|
|
|
|
private final class MyServiceConnection implements ServiceConnection {
|
|
@Override
|
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
|
synchronized (mLock) {
|
|
mRemoteInstance = IPrintSpooler.Stub.asInterface(service);
|
|
setClientLocked();
|
|
mLock.notifyAll();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onServiceDisconnected(ComponentName name) {
|
|
synchronized (mLock) {
|
|
clearClientLocked();
|
|
mRemoteInstance = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static final class GetPrintJobInfosCaller
|
|
extends TimedRemoteCaller<List<PrintJobInfo>> {
|
|
private final IPrintSpoolerCallbacks mCallback;
|
|
|
|
public GetPrintJobInfosCaller() {
|
|
super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
|
|
mCallback = new BasePrintSpoolerServiceCallbacks() {
|
|
@Override
|
|
public void onGetPrintJobInfosResult(List<PrintJobInfo> printJobs, int sequence) {
|
|
onRemoteMethodResult(printJobs, sequence);
|
|
}
|
|
};
|
|
}
|
|
|
|
public List<PrintJobInfo> getPrintJobInfos(IPrintSpooler target,
|
|
ComponentName componentName, int state, int appId)
|
|
throws RemoteException, TimeoutException {
|
|
final int sequence = onBeforeRemoteCall();
|
|
target.getPrintJobInfos(mCallback, componentName, state, appId, sequence);
|
|
return getResultTimed(sequence);
|
|
}
|
|
}
|
|
|
|
private static final class GetPrintJobInfoCaller extends TimedRemoteCaller<PrintJobInfo> {
|
|
private final IPrintSpoolerCallbacks mCallback;
|
|
|
|
public GetPrintJobInfoCaller() {
|
|
super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
|
|
mCallback = new BasePrintSpoolerServiceCallbacks() {
|
|
@Override
|
|
public void onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence) {
|
|
onRemoteMethodResult(printJob, sequence);
|
|
}
|
|
};
|
|
}
|
|
|
|
public PrintJobInfo getPrintJobInfo(IPrintSpooler target, PrintJobId printJobId,
|
|
int appId) throws RemoteException, TimeoutException {
|
|
final int sequence = onBeforeRemoteCall();
|
|
target.getPrintJobInfo(printJobId, mCallback, appId, sequence);
|
|
return getResultTimed(sequence);
|
|
}
|
|
}
|
|
|
|
private static final class SetPrintJobStateCaller extends TimedRemoteCaller<Boolean> {
|
|
private final IPrintSpoolerCallbacks mCallback;
|
|
|
|
public SetPrintJobStateCaller() {
|
|
super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
|
|
mCallback = new BasePrintSpoolerServiceCallbacks() {
|
|
@Override
|
|
public void onSetPrintJobStateResult(boolean success, int sequence) {
|
|
onRemoteMethodResult(success, sequence);
|
|
}
|
|
};
|
|
}
|
|
|
|
public boolean setPrintJobState(IPrintSpooler target, PrintJobId printJobId,
|
|
int status, String error) throws RemoteException, TimeoutException {
|
|
final int sequence = onBeforeRemoteCall();
|
|
target.setPrintJobState(printJobId, status, error, mCallback, sequence);
|
|
return getResultTimed(sequence);
|
|
}
|
|
}
|
|
|
|
private static final class SetPrintJobTagCaller extends TimedRemoteCaller<Boolean> {
|
|
private final IPrintSpoolerCallbacks mCallback;
|
|
|
|
public SetPrintJobTagCaller() {
|
|
super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
|
|
mCallback = new BasePrintSpoolerServiceCallbacks() {
|
|
@Override
|
|
public void onSetPrintJobTagResult(boolean success, int sequence) {
|
|
onRemoteMethodResult(success, sequence);
|
|
}
|
|
};
|
|
}
|
|
|
|
public boolean setPrintJobTag(IPrintSpooler target, PrintJobId printJobId,
|
|
String tag) throws RemoteException, TimeoutException {
|
|
final int sequence = onBeforeRemoteCall();
|
|
target.setPrintJobTag(printJobId, tag, mCallback, sequence);
|
|
return getResultTimed(sequence);
|
|
}
|
|
}
|
|
|
|
private static abstract class BasePrintSpoolerServiceCallbacks
|
|
extends IPrintSpoolerCallbacks.Stub {
|
|
@Override
|
|
public void onGetPrintJobInfosResult(List<PrintJobInfo> printJobIds, int sequence) {
|
|
/* do nothing */
|
|
}
|
|
|
|
@Override
|
|
public void onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence) {
|
|
/* do nothing */
|
|
}
|
|
|
|
@Override
|
|
public void onCancelPrintJobResult(boolean canceled, int sequence) {
|
|
/* do nothing */
|
|
}
|
|
|
|
@Override
|
|
public void onSetPrintJobStateResult(boolean success, int sequece) {
|
|
/* do nothing */
|
|
}
|
|
|
|
@Override
|
|
public void onSetPrintJobTagResult(boolean success, int sequence) {
|
|
/* do nothing */
|
|
}
|
|
}
|
|
|
|
private static final class PrintSpoolerClient extends IPrintSpoolerClient.Stub {
|
|
|
|
private final WeakReference<RemotePrintSpooler> mWeakSpooler;
|
|
|
|
public PrintSpoolerClient(RemotePrintSpooler spooler) {
|
|
mWeakSpooler = new WeakReference<RemotePrintSpooler>(spooler);
|
|
}
|
|
|
|
@Override
|
|
public void onPrintJobQueued(PrintJobInfo printJob) {
|
|
RemotePrintSpooler spooler = mWeakSpooler.get();
|
|
if (spooler != null) {
|
|
final long identity = Binder.clearCallingIdentity();
|
|
try {
|
|
spooler.mCallbacks.onPrintJobQueued(printJob);
|
|
} finally {
|
|
Binder.restoreCallingIdentity(identity);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onAllPrintJobsForServiceHandled(ComponentName printService) {
|
|
RemotePrintSpooler spooler = mWeakSpooler.get();
|
|
if (spooler != null) {
|
|
final long identity = Binder.clearCallingIdentity();
|
|
try {
|
|
spooler.mCallbacks.onAllPrintJobsForServiceHandled(printService);
|
|
} finally {
|
|
Binder.restoreCallingIdentity(identity);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onAllPrintJobsHandled() {
|
|
RemotePrintSpooler spooler = mWeakSpooler.get();
|
|
if (spooler != null) {
|
|
final long identity = Binder.clearCallingIdentity();
|
|
try {
|
|
spooler.onAllPrintJobsHandled();
|
|
} finally {
|
|
Binder.restoreCallingIdentity(identity);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|