Related changes:
Skia (inlcude PDF APIs): https://googleplex-android-review.googlesource.com/#/c/305814/
Canvas to PDF: https://googleplex-android-review.googlesource.com/#/c/319367/
Settings (initial version): https://googleplex-android-review.googlesource.com/#/c/306077/
Build: https://googleplex-android-review.googlesource.com/#/c/292437/
Sample print services: https://googleplex-android-review.googlesource.com/#/c/281785/
Change-Id: I104d12efd12577f05c7b9b2a5e5e49125c0f09da
416 lines
15 KiB
Java
416 lines
15 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.os.IBinder.DeathRecipient;
|
|
import android.print.IPrintAdapter;
|
|
import android.print.IPrintClient;
|
|
import android.print.IPrintSpoolerService;
|
|
import android.print.IPrintSpoolerServiceCallbacks;
|
|
import android.print.PrintAttributes;
|
|
import android.print.PrintJobInfo;
|
|
import android.util.Slog;
|
|
import android.util.TimedRemoteCaller;
|
|
|
|
import java.io.IOException;
|
|
import java.util.List;
|
|
import java.util.concurrent.TimeoutException;
|
|
|
|
/**
|
|
* This represents the remote print spooler as a local object to the
|
|
* PrintManagerSerivce. It is responsible to connecting to the remove
|
|
* spooler if needed, to make the timed out remote calls, and to handle
|
|
* remove exceptions.
|
|
*/
|
|
final class RemoteSpooler implements ServiceConnection, DeathRecipient {
|
|
|
|
private static final String LOG_TAG = "Spooler";
|
|
|
|
private static final long BIND_SPOOLER_SERVICE_TIMEOUT = 10000;
|
|
|
|
private final Object mLock = new Object();
|
|
|
|
private final Context mContext;
|
|
|
|
private final Intent mIntent;
|
|
|
|
private final GetPrintJobsCaller mGetPrintJobsCaller = new GetPrintJobsCaller();
|
|
|
|
private final CreatePrintJobCaller mCreatePrintJobCaller = new CreatePrintJobCaller();
|
|
|
|
private final CancelPrintJobCaller mCancelPrintJobCaller = new CancelPrintJobCaller();
|
|
|
|
private final GetPrintJobCaller mGetPrintJobCaller = new GetPrintJobCaller();
|
|
|
|
private final SetPrintJobStateCaller mSetPrintJobStatusCaller = new SetPrintJobStateCaller();
|
|
|
|
private final SetPrintJobTagCaller mSetPrintJobTagCaller = new SetPrintJobTagCaller();
|
|
|
|
private IPrintSpoolerService mRemoteInterface;
|
|
|
|
private int mUserId = UserHandle.USER_NULL;
|
|
|
|
public RemoteSpooler(Context context) {
|
|
mContext = context;
|
|
mIntent = new Intent();
|
|
mIntent.setComponent(new ComponentName("com.android.printspooler",
|
|
"com.android.printspooler.PrintSpoolerService"));
|
|
}
|
|
|
|
public List<PrintJobInfo> getPrintJobs(ComponentName componentName, int state, int appId,
|
|
int userId) {
|
|
try {
|
|
return mGetPrintJobsCaller.getPrintJobs(getRemoteInstance(userId),
|
|
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);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public PrintJobInfo createPrintJob(String printJobName, IPrintClient client,
|
|
IPrintAdapter printAdapter, PrintAttributes attributes, int appId, int userId) {
|
|
try {
|
|
return mCreatePrintJobCaller.createPrintJob(getRemoteInstance(userId),
|
|
printJobName, client, printAdapter, attributes, appId);
|
|
} catch (RemoteException re) {
|
|
Slog.e(LOG_TAG, "Error creating print job!", re);
|
|
} catch (TimeoutException te) {
|
|
Slog.e(LOG_TAG, "Error creating print job!", te);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public boolean cancelPrintJob(int printJobId, int appId, int userId) {
|
|
try {
|
|
return mCancelPrintJobCaller.cancelPrintJob(getRemoteInstance(userId),
|
|
printJobId, appId);
|
|
} catch (RemoteException re) {
|
|
Slog.e(LOG_TAG, "Error canceling print job!", re);
|
|
} catch (TimeoutException te) {
|
|
Slog.e(LOG_TAG, "Error canceling print job!", te);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public void writePrintJobData(ParcelFileDescriptor fd, int printJobId, int userId) {
|
|
try {
|
|
getRemoteInstance(userId).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 {
|
|
// We passed the file descriptor across and now the other
|
|
// side is responsible to close it, so close the local copy.
|
|
try {
|
|
fd.close();
|
|
} catch (IOException ioe) {
|
|
/* ignore */
|
|
}
|
|
}
|
|
}
|
|
|
|
public PrintJobInfo getPrintJobInfo(int printJobId, int appId, int userId) {
|
|
try {
|
|
return mGetPrintJobCaller.getPrintJobInfo(getRemoteInstance(userId),
|
|
printJobId, appId);
|
|
} catch (RemoteException re) {
|
|
Slog.e(LOG_TAG, "Error getting print job!", re);
|
|
} catch (TimeoutException te) {
|
|
Slog.e(LOG_TAG, "Error getting print job!", te);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public boolean setPrintJobState(int printJobId, int state, int userId) {
|
|
try {
|
|
return mSetPrintJobStatusCaller.setPrintJobState(getRemoteInstance(userId),
|
|
printJobId, state);
|
|
} catch (RemoteException re) {
|
|
Slog.e(LOG_TAG, "Error setting print job status!", re);
|
|
} catch (TimeoutException te) {
|
|
Slog.e(LOG_TAG, "Error setting print job status!", te);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public boolean setPrintJobTag(int printJobId, String tag, int userId) {
|
|
try {
|
|
return mSetPrintJobTagCaller.setPrintJobTag(getRemoteInstance(userId),
|
|
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);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public void onServiceDisconnected(ComponentName name) {
|
|
binderDied();
|
|
}
|
|
|
|
@Override
|
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
|
synchronized (mLock) {
|
|
try {
|
|
service.linkToDeath(this, 0);
|
|
mRemoteInterface = IPrintSpoolerService.Stub.asInterface(service);
|
|
} catch (RemoteException re) {
|
|
/* ignore */
|
|
}
|
|
}
|
|
}
|
|
|
|
private IPrintSpoolerService getRemoteInstance(int userId) throws TimeoutException {
|
|
synchronized (mLock) {
|
|
if (mRemoteInterface != null && mUserId == userId) {
|
|
return mRemoteInterface;
|
|
}
|
|
|
|
final long identity = Binder.clearCallingIdentity();
|
|
try {
|
|
if (mUserId != UserHandle.USER_NULL && mUserId != userId) {
|
|
unbind();
|
|
}
|
|
|
|
mContext.bindServiceAsUser(mIntent, this,
|
|
Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_OOM_MANAGEMENT,
|
|
UserHandle.CURRENT);
|
|
} finally {
|
|
Binder.restoreCallingIdentity(identity);
|
|
}
|
|
|
|
final long startMillis = SystemClock.uptimeMillis();
|
|
while (true) {
|
|
if (mRemoteInterface != 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 */
|
|
}
|
|
}
|
|
|
|
mUserId = userId;
|
|
|
|
return mRemoteInterface;
|
|
}
|
|
}
|
|
|
|
public void unbind() {
|
|
synchronized (mLock) {
|
|
if (mRemoteInterface != null) {
|
|
mContext.unbindService(this);
|
|
mRemoteInterface = null;
|
|
mUserId = UserHandle.USER_NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void binderDied() {
|
|
synchronized (mLock) {
|
|
if (mRemoteInterface != null) {
|
|
mRemoteInterface.asBinder().unlinkToDeath(this, 0);
|
|
mRemoteInterface = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
private final class GetPrintJobsCaller extends TimedRemoteCaller<List<PrintJobInfo>> {
|
|
private final IPrintSpoolerServiceCallbacks mCallback;
|
|
|
|
public GetPrintJobsCaller() {
|
|
super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
|
|
mCallback = new BasePrintSpoolerServiceCallbacks() {
|
|
@Override
|
|
public void onGetPrintJobsResult(List<PrintJobInfo> printJobs, int sequence) {
|
|
onRemoteMethodResult(printJobs, sequence);
|
|
}
|
|
};
|
|
}
|
|
|
|
public List<PrintJobInfo> getPrintJobs(IPrintSpoolerService target,
|
|
ComponentName componentName, int state, int appId)
|
|
throws RemoteException, TimeoutException {
|
|
final int sequence = onBeforeRemoteCall();
|
|
target.getPrintJobs(mCallback, componentName, state, appId, sequence);
|
|
return getResultTimed(sequence);
|
|
}
|
|
}
|
|
|
|
private final class CreatePrintJobCaller extends TimedRemoteCaller<PrintJobInfo> {
|
|
private final IPrintSpoolerServiceCallbacks mCallback;
|
|
|
|
public CreatePrintJobCaller() {
|
|
super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
|
|
mCallback = new BasePrintSpoolerServiceCallbacks() {
|
|
@Override
|
|
public void onCreatePrintJobResult(PrintJobInfo printJob, int sequence) {
|
|
onRemoteMethodResult(printJob, sequence);
|
|
}
|
|
};
|
|
}
|
|
|
|
public PrintJobInfo createPrintJob(IPrintSpoolerService target, String printJobName,
|
|
IPrintClient client, IPrintAdapter printAdapter, PrintAttributes attributes,
|
|
int appId) throws RemoteException, TimeoutException {
|
|
final int sequence = onBeforeRemoteCall();
|
|
target.createPrintJob(printJobName, client, printAdapter, attributes,
|
|
mCallback, appId, sequence);
|
|
return getResultTimed(sequence);
|
|
}
|
|
}
|
|
|
|
private final class CancelPrintJobCaller extends TimedRemoteCaller<Boolean> {
|
|
private final IPrintSpoolerServiceCallbacks mCallback;
|
|
|
|
public CancelPrintJobCaller() {
|
|
super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
|
|
mCallback = new BasePrintSpoolerServiceCallbacks() {
|
|
@Override
|
|
public void onCancelPrintJobResult(boolean canceled, int sequence) {
|
|
onRemoteMethodResult(canceled, sequence);
|
|
}
|
|
};
|
|
}
|
|
|
|
public boolean cancelPrintJob(IPrintSpoolerService target, int printJobId,
|
|
int appId) throws RemoteException, TimeoutException {
|
|
final int sequence = onBeforeRemoteCall();
|
|
target.cancelPrintJob(printJobId, mCallback, appId, sequence);
|
|
return getResultTimed(sequence);
|
|
}
|
|
}
|
|
|
|
private final class GetPrintJobCaller extends TimedRemoteCaller<PrintJobInfo> {
|
|
private final IPrintSpoolerServiceCallbacks mCallback;
|
|
|
|
public GetPrintJobCaller() {
|
|
super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
|
|
mCallback = new BasePrintSpoolerServiceCallbacks() {
|
|
@Override
|
|
public void onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence) {
|
|
onRemoteMethodResult(printJob, sequence);
|
|
}
|
|
};
|
|
}
|
|
|
|
public PrintJobInfo getPrintJobInfo(IPrintSpoolerService target, int printJobId,
|
|
int appId) throws RemoteException, TimeoutException {
|
|
final int sequence = onBeforeRemoteCall();
|
|
target.getPrintJob(printJobId, mCallback, appId, sequence);
|
|
return getResultTimed(sequence);
|
|
}
|
|
}
|
|
|
|
private final class SetPrintJobStateCaller extends TimedRemoteCaller<Boolean> {
|
|
private final IPrintSpoolerServiceCallbacks 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(IPrintSpoolerService target, int printJobId,
|
|
int status) throws RemoteException, TimeoutException {
|
|
final int sequence = onBeforeRemoteCall();
|
|
target.setPrintJobState(printJobId, status, mCallback, sequence);
|
|
return getResultTimed(sequence);
|
|
}
|
|
}
|
|
|
|
private final class SetPrintJobTagCaller extends TimedRemoteCaller<Boolean> {
|
|
private final IPrintSpoolerServiceCallbacks 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(IPrintSpoolerService target, int printJobId,
|
|
String tag) throws RemoteException, TimeoutException {
|
|
final int sequence = onBeforeRemoteCall();
|
|
target.setPrintJobTag(printJobId, tag, mCallback, sequence);
|
|
return getResultTimed(sequence);
|
|
}
|
|
}
|
|
|
|
private abstract class BasePrintSpoolerServiceCallbacks
|
|
extends IPrintSpoolerServiceCallbacks.Stub {
|
|
@Override
|
|
public void onGetPrintJobsResult(List<PrintJobInfo> printJobIds, int sequence) {
|
|
/** do nothing */
|
|
}
|
|
|
|
@Override
|
|
public void onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence) {
|
|
/** do nothing */
|
|
}
|
|
|
|
@Override
|
|
public void onCreatePrintJobResult(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 */
|
|
}
|
|
}
|
|
} |