summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk10
-rw-r--r--api/current.txt76
-rw-r--r--core/java/android/print/FileDocumentAdapter.java (renamed from core/java/android/print/PrintFileAdapter.java)33
-rw-r--r--core/java/android/print/ILayoutResultCallback.aidl31
-rw-r--r--core/java/android/print/IPrintDocumentAdapter.aidl (renamed from core/java/android/print/IPrintAdapter.aidl)12
-rw-r--r--core/java/android/print/IPrintManager.aidl18
-rw-r--r--core/java/android/print/IPrintSpooler.aidl (renamed from core/java/android/print/IPrintSpoolerService.aidl)29
-rw-r--r--core/java/android/print/IPrintSpoolerCallbacks.aidl (renamed from core/java/android/print/IPrintSpoolerServiceCallbacks.aidl)4
-rw-r--r--core/java/android/print/IPrintSpoolerClient.aidl35
-rw-r--r--core/java/android/print/IPrintSpoolerObserver.aidl31
-rw-r--r--core/java/android/print/IWriteResultCallback.aidl (renamed from core/java/android/print/IPrintResultCallback.aidl)12
-rw-r--r--core/java/android/print/PrintAdapter.java164
-rw-r--r--core/java/android/print/PrintAdapterInfo.java136
-rw-r--r--core/java/android/print/PrintDocumentAdapter.java200
-rw-r--r--core/java/android/print/PrintDocumentInfo.aidl (renamed from core/java/android/print/PrintAdapterInfo.aidl)2
-rw-r--r--core/java/android/print/PrintDocumentInfo.java171
-rw-r--r--core/java/android/print/PrintJob.java2
-rw-r--r--core/java/android/print/PrintJobInfo.java94
-rw-r--r--core/java/android/print/PrintManager.java239
-rw-r--r--core/java/android/print/PrinterId.java2
-rw-r--r--core/java/android/print/PrinterInfo.java115
-rw-r--r--core/java/android/printservice/IPrintService.aidl7
-rw-r--r--core/java/android/printservice/IPrintServiceClient.aidl6
-rw-r--r--core/java/android/printservice/PrintDocument.java91
-rw-r--r--core/java/android/printservice/PrintJob.java91
-rw-r--r--core/java/android/printservice/PrintService.java182
-rw-r--r--core/java/android/printservice/PrintServiceInfo.java15
-rw-r--r--core/res/res/values/strings.xml78
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java229
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java279
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java35
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/RemotePrintAdapter.java219
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java448
-rw-r--r--services/java/com/android/server/AppWidgetService.java2
-rw-r--r--services/java/com/android/server/AssetAtlasService.java2
-rw-r--r--services/java/com/android/server/CommonTimeManagementService.java2
-rw-r--r--services/java/com/android/server/CountryDetectorService.java2
-rw-r--r--services/java/com/android/server/InputMethodManagerService.java2
-rw-r--r--services/java/com/android/server/LocationManagerService.java2
-rw-r--r--services/java/com/android/server/NetworkTimeUpdateService.java2
-rw-r--r--services/java/com/android/server/SystemServer.java61
-rw-r--r--services/java/com/android/server/TelephonyRegistry.java2
-rw-r--r--services/java/com/android/server/TextServicesManagerService.java2
-rw-r--r--services/java/com/android/server/WallpaperManagerService.java2
-rw-r--r--services/java/com/android/server/dreams/DreamManagerService.java2
-rw-r--r--services/java/com/android/server/input/InputManagerService.java2
-rw-r--r--services/java/com/android/server/print/PrintManagerService.java720
-rw-r--r--services/java/com/android/server/print/RemotePrintService.java466
-rw-r--r--services/java/com/android/server/print/RemotePrintSpooler.java613
-rw-r--r--services/java/com/android/server/print/RemoteSpooler.java416
-rw-r--r--services/java/com/android/server/print/UserState.java274
51 files changed, 3442 insertions, 2228 deletions
diff --git a/Android.mk b/Android.mk
index db6a7049f6a7..d81e58118db0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -159,13 +159,15 @@ LOCAL_SRC_FILES += \
core/java/android/os/IUserManager.aidl \
core/java/android/os/IVibratorService.aidl \
core/java/android/service/notification/INotificationListener.aidl \
+ core/java/android/print/ILayoutResultCallback.aidl \
+ core/java/android/print/IPrintDocumentAdapter.aidl \
core/java/android/print/IPrinterDiscoveryObserver.aidl \
- core/java/android/print/IPrintAdapter.aidl \
core/java/android/print/IPrintClient.aidl \
- core/java/android/print/IPrintResultCallback.aidl \
core/java/android/print/IPrintManager.aidl \
- core/java/android/print/IPrintSpoolerService.aidl \
- core/java/android/print/IPrintSpoolerServiceCallbacks.aidl \
+ core/java/android/print/IPrintSpooler.aidl \
+ core/java/android/print/IPrintSpoolerCallbacks.aidl \
+ core/java/android/print/IPrintSpoolerClient.aidl \
+ core/java/android/print/IWriteResultCallback.aidl \
core/java/android/printservice/IPrintService.aidl \
core/java/android/printservice/IPrintServiceClient.aidl \
core/java/android/service/dreams/IDreamManager.aidl \
diff --git a/api/current.txt b/api/current.txt
index 7196161174b2..ca6dff97f84a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -18448,36 +18448,6 @@ package android.print {
field public static final android.os.Parcelable.Creator CREATOR;
}
- public abstract class PrintAdapter {
- ctor public PrintAdapter();
- method public abstract android.print.PrintAdapterInfo getInfo();
- method public void onFinish();
- method public abstract void onPrint(java.util.List<android.print.PageRange>, java.io.FileDescriptor, android.os.CancellationSignal, android.print.PrintAdapter.PrintResultCallback);
- method public boolean onPrintAttributesChanged(android.print.PrintAttributes);
- method public void onStart();
- }
-
- public static abstract class PrintAdapter.PrintResultCallback {
- method public void onPrintFailed(java.lang.CharSequence);
- method public void onPrintFinished(java.util.List<android.print.PageRange>);
- }
-
- public final class PrintAdapterInfo implements android.os.Parcelable {
- method public int describeContents();
- method public int getFlags();
- method public int getPageCount();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator CREATOR;
- field public static final int PAGE_COUNT_UNKNOWN = -1; // 0xffffffff
- }
-
- public static final class PrintAdapterInfo.Builder {
- ctor public PrintAdapterInfo.Builder();
- method public android.print.PrintAdapterInfo create();
- method public android.print.PrintAdapterInfo.Builder setFlags(int);
- method public android.print.PrintAdapterInfo.Builder setPageCount(int);
- }
-
public final class PrintAttributes implements android.os.Parcelable {
method public void clear();
method public int describeContents();
@@ -18588,6 +18558,43 @@ package android.print {
method public java.lang.CharSequence getLabel(android.content.pm.PackageManager);
}
+ public abstract class PrintDocumentAdapter {
+ ctor public PrintDocumentAdapter();
+ method public void onFinish();
+ method public abstract void onLayout(android.print.PrintAttributes, android.print.PrintAttributes, android.os.CancellationSignal, android.print.PrintDocumentAdapter.LayoutResultCallback);
+ method public void onStart();
+ method public abstract void onWrite(java.util.List<android.print.PageRange>, java.io.FileDescriptor, android.os.CancellationSignal, android.print.PrintDocumentAdapter.WriteResultCallback);
+ }
+
+ public static abstract class PrintDocumentAdapter.LayoutResultCallback {
+ method public void onLayoutFailed(java.lang.CharSequence);
+ method public void onLayoutFinished(android.print.PrintDocumentInfo, boolean);
+ }
+
+ public static abstract class PrintDocumentAdapter.WriteResultCallback {
+ method public void onWriteFailed(java.lang.CharSequence);
+ method public void onWriteFinished(java.util.List<android.print.PageRange>);
+ }
+
+ public final class PrintDocumentInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getContentType();
+ method public int getPageCount();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CONTENT_TYPE_DOCUMENT = 0; // 0x0
+ field public static final int CONTENT_TYPE_PHOTO = 1; // 0x1
+ field public static final int CONTENT_TYPE_UNKNOWN = -1; // 0xffffffff
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public static final int PAGE_COUNT_UNKNOWN = -1; // 0xffffffff
+ }
+
+ public static final class PrintDocumentInfo.Builder {
+ ctor public PrintDocumentInfo.Builder();
+ method public android.print.PrintDocumentInfo create();
+ method public android.print.PrintDocumentInfo.Builder setContentType(int);
+ method public android.print.PrintDocumentInfo.Builder setPageCount(int);
+ }
+
public final class PrintJob {
method public void cancel();
method public int getId();
@@ -18617,7 +18624,7 @@ package android.print {
public final class PrintManager {
method public java.util.List<android.print.PrintJob> getPrintJobs();
method public android.print.PrintJob print(java.lang.String, java.io.File, android.print.PrintAttributes);
- method public android.print.PrintJob print(java.lang.String, android.print.PrintAdapter, android.print.PrintAttributes);
+ method public android.print.PrintJob print(java.lang.String, android.print.PrintDocumentAdapter, android.print.PrintAttributes);
}
public final class PrinterId implements android.os.Parcelable {
@@ -18699,11 +18706,16 @@ package android.print.pdf {
package android.printservice {
+ public final class PrintDocument {
+ method public java.io.FileDescriptor getData();
+ method public android.print.PrintDocumentInfo getInfo();
+ }
+
public final class PrintJob {
method public boolean cancel();
method public boolean complete();
method public boolean fail(java.lang.CharSequence);
- method public final java.io.FileDescriptor getData();
+ method public android.printservice.PrintDocument getDocument();
method public int getId();
method public android.print.PrintJobInfo getInfo();
method public boolean isQueued();
diff --git a/core/java/android/print/PrintFileAdapter.java b/core/java/android/print/FileDocumentAdapter.java
index dab964846552..d162c19fdb37 100644
--- a/core/java/android/print/PrintFileAdapter.java
+++ b/core/java/android/print/FileDocumentAdapter.java
@@ -36,15 +36,15 @@ import java.util.List;
/**
* Adapter for printing files.
*/
-class PrintFileAdapter extends PrintAdapter {
+final class FileDocumentAdapter extends PrintDocumentAdapter {
- private static final String LOG_TAG = "PrintFileAdapter";
+ private static final String LOG_TAG = "FileDocumentAdapter";
private final File mFile;
private WriteFileAsyncTask mWriteFileAsyncTask;
- public PrintFileAdapter(File file) {
+ public FileDocumentAdapter(File file) {
if (file == null) {
throw new IllegalArgumentException("File cannot be null!");
}
@@ -52,8 +52,17 @@ class PrintFileAdapter extends PrintAdapter {
}
@Override
- public void onPrint(List<PageRange> pages, FileDescriptor destination,
- CancellationSignal cancellationSignal, PrintResultCallback callback) {
+ public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
+ CancellationSignal cancellationSignal, LayoutResultCallback callback) {
+ // TODO: When we have a PDF rendering library we should query the page count.
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder()
+ .setPageCount(PrintDocumentInfo.PAGE_COUNT_UNKNOWN).create();
+ callback.onLayoutFinished(info, false);
+ }
+
+ @Override
+ public void onWrite(List<PageRange> pages, FileDescriptor destination,
+ CancellationSignal cancellationSignal, WriteResultCallback callback) {
mWriteFileAsyncTask = new WriteFileAsyncTask(mFile, destination, cancellationSignal,
callback);
mWriteFileAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
@@ -61,24 +70,18 @@ class PrintFileAdapter extends PrintAdapter {
}
- @Override
- public PrintAdapterInfo getInfo() {
- // TODO: When we have PDF render library we should query the page count.
- return new PrintAdapterInfo.Builder().create();
- }
-
private static final class WriteFileAsyncTask extends AsyncTask<Void, Void, Void> {
private final File mSource;
private final FileDescriptor mDestination;
- private final PrintResultCallback mResultCallback;
+ private final WriteResultCallback mResultCallback;
private final CancellationSignal mCancellationSignal;
public WriteFileAsyncTask(File source, FileDescriptor destination,
- CancellationSignal cancellationSignal, PrintResultCallback callback) {
+ CancellationSignal cancellationSignal, WriteResultCallback callback) {
mSource = source;
mDestination = destination;
mResultCallback = callback;
@@ -113,9 +116,9 @@ class PrintFileAdapter extends PrintAdapter {
if (!isCancelled()) {
List<PageRange> pages = new ArrayList<PageRange>();
pages.add(PageRange.ALL_PAGES);
- mResultCallback.onPrintFinished(pages);
+ mResultCallback.onWriteFinished(pages);
} else {
- mResultCallback.onPrintFailed("Cancelled");
+ mResultCallback.onWriteFailed("Cancelled");
}
}
return null;
diff --git a/core/java/android/print/ILayoutResultCallback.aidl b/core/java/android/print/ILayoutResultCallback.aidl
new file mode 100644
index 000000000000..e4d79f3057ba
--- /dev/null
+++ b/core/java/android/print/ILayoutResultCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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 android.print;
+
+import android.os.ICancellationSignal;
+import android.print.PrintDocumentInfo;
+
+/**
+ * Callback for observing the result of android.print.PrintAdapter#onLayout.
+ *
+ * @hide
+ */
+oneway interface ILayoutResultCallback {
+ void onLayoutStarted(ICancellationSignal cancellationSignal);
+ void onLayoutFinished(in PrintDocumentInfo info, boolean changed);
+ void onLayoutFailed(CharSequence error);
+}
diff --git a/core/java/android/print/IPrintAdapter.aidl b/core/java/android/print/IPrintDocumentAdapter.aidl
index f3ff8c4d0ce8..36938e3592cc 100644
--- a/core/java/android/print/IPrintAdapter.aidl
+++ b/core/java/android/print/IPrintDocumentAdapter.aidl
@@ -17,7 +17,8 @@
package android.print;
import android.os.ParcelFileDescriptor;
-import android.print.IPrintResultCallback;
+import android.print.ILayoutResultCallback;
+import android.print.IWriteResultCallback;
import android.print.PageRange;
import android.print.PrintAttributes;
@@ -26,10 +27,11 @@ import android.print.PrintAttributes;
*
* @hide
*/
-oneway interface IPrintAdapter {
+oneway interface IPrintDocumentAdapter {
void start();
- void printAttributesChanged(in PrintAttributes attributes);
- void print(in List<PageRange> pages, in ParcelFileDescriptor fd,
- IPrintResultCallback callback);
+ void layout(in PrintAttributes oldAttributes, in PrintAttributes newAttributes,
+ ILayoutResultCallback callback);
+ void write(in List<PageRange> pages, in ParcelFileDescriptor fd,
+ IWriteResultCallback callback);
void finish();
}
diff --git a/core/java/android/print/IPrintManager.aidl b/core/java/android/print/IPrintManager.aidl
index ff9877e04980..a466e741bc90 100644
--- a/core/java/android/print/IPrintManager.aidl
+++ b/core/java/android/print/IPrintManager.aidl
@@ -16,11 +16,8 @@
package android.print;
-import android.os.ICancellationSignal;
-import android.print.IPrintAdapter;
+import android.print.IPrintDocumentAdapter;
import android.print.IPrintClient;
-import android.print.IPrinterDiscoveryObserver;
-import android.print.PrinterId;
import android.print.PrintJobInfo;
import android.print.PrintAttributes;
@@ -30,12 +27,11 @@ import android.print.PrintAttributes;
* @hide
*/
interface IPrintManager {
- List<PrintJobInfo> getPrintJobs(int appId, int userId);
- PrintJobInfo getPrintJob(int printJobId, int appId, int userId);
- PrintJobInfo print(String printJobName, in IPrintClient client, in IPrintAdapter printAdapter,
- in PrintAttributes attributes, int appId, int userId);
+ List<PrintJobInfo> getPrintJobInfos(int appId, int userId);
+ PrintJobInfo getPrintJobInfo(int printJobId, int appId, int userId);
+ PrintJobInfo print(String printJobName, in IPrintClient client,
+ in IPrintDocumentAdapter printAdapter, in PrintAttributes attributes,
+ int appId, int userId);
void cancelPrintJob(int printJobId, int appId, int userId);
- void onPrintJobQueued(in PrinterId printerId, in PrintJobInfo printJob);
- void startDiscoverPrinters(IPrinterDiscoveryObserver observer);
- void stopDiscoverPrinters();
+
}
diff --git a/core/java/android/print/IPrintSpoolerService.aidl b/core/java/android/print/IPrintSpooler.aidl
index e84d5927fe0c..c55205d0f1ff 100644
--- a/core/java/android/print/IPrintSpoolerService.aidl
+++ b/core/java/android/print/IPrintSpooler.aidl
@@ -18,32 +18,35 @@ package android.print;
import android.content.ComponentName;
import android.os.ParcelFileDescriptor;
-import android.print.IPrintAdapter;
+import android.print.IPrintDocumentAdapter;
import android.print.IPrintClient;
-import android.print.IPrintSpoolerServiceCallbacks;
+import android.print.IPrintSpoolerClient;
+import android.print.IPrintSpoolerCallbacks;
import android.print.PrinterInfo;
import android.print.PrintAttributes;
/**
* Interface for communication with the print spooler service.
*
- * @see android.print.IPrintSpoolerServiceCallbacks
+ * @see android.print.IPrintSpoolerCallbacks
*
* @hide
*/
-oneway interface IPrintSpoolerService {
- void getPrintJobs(IPrintSpoolerServiceCallbacks callback, in ComponentName componentName,
+oneway interface IPrintSpooler {
+ void getPrintJobInfos(IPrintSpoolerCallbacks callback, in ComponentName componentName,
int state, int appId, int sequence);
- void getPrintJob(int printJobId, IPrintSpoolerServiceCallbacks callback,
+ void getPrintJobInfo(int printJobId, IPrintSpoolerCallbacks callback,
int appId, int sequence);
- void createPrintJob(String printJobName, in IPrintClient client, in IPrintAdapter printAdapter,
- in PrintAttributes attributes, IPrintSpoolerServiceCallbacks callback, int appId,
- int sequence);
- void cancelPrintJob(int printJobId, IPrintSpoolerServiceCallbacks callback,
+ void createPrintJob(String printJobName, in IPrintClient client,
+ in IPrintDocumentAdapter printAdapter, in PrintAttributes attributes,
+ IPrintSpoolerCallbacks callback, int appId, int sequence);
+ void cancelPrintJob(int printJobId, IPrintSpoolerCallbacks callback,
int appId, int sequence);
- void setPrintJobState(int printJobId, int status, IPrintSpoolerServiceCallbacks callback,
+ void setPrintJobState(int printJobId, int status, IPrintSpoolerCallbacks callback,
int sequence);
- void setPrintJobTag(int printJobId, String tag, IPrintSpoolerServiceCallbacks callback,
+ void setPrintJobTag(int printJobId, String tag, IPrintSpoolerCallbacks callback,
int sequence);
void writePrintJobData(in ParcelFileDescriptor fd, int printJobId);
-} \ No newline at end of file
+ void setClient(IPrintSpoolerClient client);
+ void notifyClientForActivteJobs();
+}
diff --git a/core/java/android/print/IPrintSpoolerServiceCallbacks.aidl b/core/java/android/print/IPrintSpoolerCallbacks.aidl
index 0c519136bb83..7912964efecc 100644
--- a/core/java/android/print/IPrintSpoolerServiceCallbacks.aidl
+++ b/core/java/android/print/IPrintSpoolerCallbacks.aidl
@@ -26,8 +26,8 @@ import java.util.List;
*
* @hide
*/
-oneway interface IPrintSpoolerServiceCallbacks {
- void onGetPrintJobsResult(in List<PrintJobInfo> printJob, int sequence);
+oneway interface IPrintSpoolerCallbacks {
+ void onGetPrintJobInfosResult(in List<PrintJobInfo> printJob, int sequence);
void onGetPrintJobInfoResult(in PrintJobInfo printJob, int sequence);
void onCreatePrintJobResult(in PrintJobInfo printJob, int sequence);
void onCancelPrintJobResult(boolean canceled, int sequence);
diff --git a/core/java/android/print/IPrintSpoolerClient.aidl b/core/java/android/print/IPrintSpoolerClient.aidl
new file mode 100644
index 000000000000..47975e1eafa9
--- /dev/null
+++ b/core/java/android/print/IPrintSpoolerClient.aidl
@@ -0,0 +1,35 @@
+/*
+ * 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 android.print;
+
+import android.content.ComponentName;
+import android.print.IPrinterDiscoveryObserver;
+import android.print.PrintJobInfo;
+
+
+/**
+ * Interface for receiving interesting state updates from the print spooler.
+ *
+ * @hide
+ */
+oneway interface IPrintSpoolerClient {
+ void onPrintJobQueued(in PrintJobInfo printJob);
+ void onStartPrinterDiscovery(IPrinterDiscoveryObserver observer);
+ void onStopPrinterDiscovery();
+ void onAllPrintJobsForServiceHandled(in ComponentName printService);
+ void onAllPrintJobsHandled();
+}
diff --git a/core/java/android/print/IPrintSpoolerObserver.aidl b/core/java/android/print/IPrintSpoolerObserver.aidl
new file mode 100644
index 000000000000..7b8f40e4ef10
--- /dev/null
+++ b/core/java/android/print/IPrintSpoolerObserver.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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 android.print;
+
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+
+/**
+ * Interface for observing the state of the print spooler.
+ *
+ * @hide
+ */
+oneway interface IPrinterDiscoveryObserver {
+ void onPrintJobQueued(in PrinterId printerId, in PrintJobInfo printJob);
+ void onAllPrintJobsHandled(in ComponentName printService);
+ void onAllPrintJobsHandled();
+}
diff --git a/core/java/android/print/IPrintResultCallback.aidl b/core/java/android/print/IWriteResultCallback.aidl
index 838377ea4a96..d5428b145aff 100644
--- a/core/java/android/print/IPrintResultCallback.aidl
+++ b/core/java/android/print/IWriteResultCallback.aidl
@@ -18,16 +18,14 @@ package android.print;
import android.os.ICancellationSignal;
import android.print.PageRange;
-import android.print.PrintAdapterInfo;
/**
- * Callbacks for observing the print progress (writing of printed content)
- * of a PrintAdapter.
+ * Callback for observing the result of android.print.DocuemntAdapter#onWrite.
*
* @hide
*/
-oneway interface IPrintResultCallback {
- void onPrintStarted(in PrintAdapterInfo info, ICancellationSignal cancellationSignal);
- void onPrintFinished(in List<PageRange> pages);
- void onPrintFailed(CharSequence error);
+oneway interface IWriteResultCallback {
+ void onWriteStarted(ICancellationSignal cancellationSignal);
+ void onWriteFinished(in List<PageRange> pages);
+ void onWriteFailed(CharSequence error);
}
diff --git a/core/java/android/print/PrintAdapter.java b/core/java/android/print/PrintAdapter.java
deleted file mode 100644
index 6547c55e1d1a..000000000000
--- a/core/java/android/print/PrintAdapter.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * 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 android.print;
-
-import android.os.CancellationSignal;
-
-import java.io.FileDescriptor;
-import java.util.List;
-
-/**
- * Base class that provides data to be printed.
- *
- * <h3>Lifecycle</h3>
- * <p>
- * <ul>
- * <li>
- * You will receive a call on {@link #onStart()} when printing starts.
- * This callback can be used to allocate resources.
- * </li>
- * <li>
- * Next you will get one or more calls to {@link #onPrintAttributesChanged(
- * PrintAttributes) to informs you that the print attributes (page size, density,
- * etc) changed giving you an opportunity to re-layout the content.
- * </li>
- * <li>
- * After every {@link #onPrintAttributesChanged(PrintAttributes) you will receive
- * one or more calls to {@link #onPrint(List, FileDescriptor, CancellationSignal,
- * PrintResultCallback)} asking you to write a PDF file with the content for
- * specific pages.
- * </li>
- * <li>
- * Finally, you will receive a call on {@link #onFinish()} right after printing.
- * You can use this callback to release resources.
- * </li>
- * <li>
- * You can receive calls to {@link #getInfo()} at any point after a call to
- * {@link #onPrintAttributesChanged(PrintAttributes)} which should return
- * a {@link PrintAdapterInfo} describing your {@link PrintAdapter}.
- * </li>
- * </ul>
- * </p>
- * <p>
- */
-public abstract class PrintAdapter {
-
- /**
- * Called when printing started. You can use this callback to
- * allocate resources.
- * <p>
- * <strong>Note:</strong> Invoked on the main thread.
- * </p>
- */
- public void onStart() {
- /* do nothing - stub */
- }
-
- /**
- * Called when the print job attributes (page size, density, etc)
- * changed giving you a chance to re-layout the content such that
- * it matches the new constraints.
- * <p>
- * <strong>Note:</strong> Invoked on the main thread.
- * </p>
- *
- * @param attributes The print job attributes.
- * @return Whether the content changed based on the provided attributes.
- */
- public boolean onPrintAttributesChanged(PrintAttributes attributes) {
- return false;
- }
-
- /**
- * Called when specific pages of the content have to be printed in the from of
- * a PDF file to the given file descriptor. You should <strong>not</strong>
- * close the file descriptor instead you have to invoke {@link PrintResultCallback
- * #onPrintFinished()} or {@link PrintResultCallback#onPrintFailed(CharSequence)}.
- * <p>
- * <strong>Note:</strong> If the printed content is large, it is a good
- * practice to schedule writing it on a dedicated thread and register a
- * callback in the provided {@link CancellationSignal} upon invocation of
- * which you should stop writing data. The cancellation callback will not
- * be made on the main thread.
- * </p>
- * <p>
- * <strong>Note:</strong> Invoked on the main thread.
- * </p>
- *
- * @param pages The pages whose content to print.
- * @param destination The destination file descriptor to which to start writing.
- * @param cancellationSignal Signal for observing cancel print requests.
- * @param progressListener Callback to inform the system with the write progress.
- *
- * @see CancellationSignal
- */
- public abstract void onPrint(List<PageRange> pages, FileDescriptor destination,
- CancellationSignal cancellationSignal, PrintResultCallback progressListener);
-
- /**
- * Called when printing finished. You can use this callback to release
- * resources.
- * <p>
- * <strong>Note:</strong> Invoked on the main thread.
- * </p>
- */
- public void onFinish() {
- /* do nothing - stub */
- }
-
- /**
- * Gets a {@link PrinterInfo} object that contains metadata about the
- * printed content.
- * <p>
- * <strong>Note:</strong> Invoked on the main thread.
- * </p>
- *
- * @return The info object for this {@link PrintAdapter}.
- *
- * @see PrintAdapterInfo
- */
- public abstract PrintAdapterInfo getInfo();
-
- /**
- * Base class for implementing a listener for the print result
- * of a {@link PrintAdapter}.
- */
- public static abstract class PrintResultCallback {
-
- PrintResultCallback() {
- /* do nothing - hide constructor */
- }
-
- /**
- * Notifies that all the data was printed.
- *
- * @param pages The pages that were printed.
- */
- public void onPrintFinished(List<PageRange> pages) {
- /* do nothing - stub */
- }
-
- /**
- * Notifies that an error occurred while printing the data.
- *
- * @param error Error message. May be null if error is unknown.
- */
- public void onPrintFailed(CharSequence error) {
- /* do nothing - stub */
- }
- }
-}
diff --git a/core/java/android/print/PrintAdapterInfo.java b/core/java/android/print/PrintAdapterInfo.java
deleted file mode 100644
index 06e6b10f3b48..000000000000
--- a/core/java/android/print/PrintAdapterInfo.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * 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 android.print;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * This class encapsulates information about a {@link PrintAdapter} object.
- */
-public final class PrintAdapterInfo implements Parcelable {
-
- /**
- * Constant for unknown page count.
- */
- public static final int PAGE_COUNT_UNKNOWN = -1;
-
- private int mPageCount;
- private int mFlags;
-
- /**
- * Creates a new instance.
- */
- private PrintAdapterInfo() {
- /* do nothing */
- }
-
- /**
- * Creates a new instance.
- *
- * @param parcel Data from which to initialize.
- */
- private PrintAdapterInfo(Parcel parcel) {
- mPageCount = parcel.readInt();
- mFlags = parcel.readInt();
- }
-
- /**
- * Gets the total number of pages.
- *
- * @return The number of pages.
- */
- public int getPageCount() {
- return mPageCount;
- }
-
- /**
- * @return The flags of this printable info.
- *
- * @see #FLAG_NOTIFY_FOR_ATTRIBUTES_CHANGE
- */
- public int getFlags() {
- return mFlags;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeInt(mPageCount);
- parcel.writeInt(mFlags);
- }
-
- /**
- * Builder for creating an {@link PrintAdapterInfo}.
- */
- public static final class Builder {
- private final PrintAdapterInfo mPrintableInfo = new PrintAdapterInfo();
-
- /**
- * Sets the total number of pages.
- *
- * @param pageCount The number of pages. Must be
- * greater than zero.
- */
- public Builder setPageCount(int pageCount) {
- if (pageCount < 0) {
- throw new IllegalArgumentException("pageCount"
- + " must be greater than or euqal to zero!");
- }
- mPrintableInfo.mPageCount = pageCount;
- return this;
- }
-
- /**
- * Sets the flags of this printable info.
- *
- * @param flags The flags.
- *
- * @see #FLAG_NOTIFY_FOR_ATTRIBUTES_CHANGE
- */
- public Builder setFlags(int flags) {
- mPrintableInfo.mFlags = flags;
- return this;
- }
-
- /**
- * Creates a new {@link PrintAdapterInfo} instance.
- *
- * @return The new instance.
- */
- public PrintAdapterInfo create() {
- return mPrintableInfo;
- }
- }
-
- public static final Parcelable.Creator<PrintAdapterInfo> CREATOR =
- new Creator<PrintAdapterInfo>() {
- @Override
- public PrintAdapterInfo createFromParcel(Parcel parcel) {
- return new PrintAdapterInfo(parcel);
- }
-
- @Override
- public PrintAdapterInfo[] newArray(int size) {
- return new PrintAdapterInfo[size];
- }
- };
-}
diff --git a/core/java/android/print/PrintDocumentAdapter.java b/core/java/android/print/PrintDocumentAdapter.java
new file mode 100644
index 000000000000..ef69400c3628
--- /dev/null
+++ b/core/java/android/print/PrintDocumentAdapter.java
@@ -0,0 +1,200 @@
+/*
+ * 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 android.print;
+
+import android.os.CancellationSignal;
+
+import java.io.FileDescriptor;
+import java.util.List;
+
+/**
+ * Base class that provides the content of a document to be printed.
+ *
+ * <h3>Lifecycle</h3>
+ * <p>
+ * <ul>
+ * <li>
+ * Initially, you will receive a call to {@link #onStart()}. This callback
+ * can be used to allocate resources.
+ * </li>
+ * <li>
+ * Next, you will get one or more calls to {@link #onLayout(PrintAttributes,
+ * PrintAttributes, CancellationSignal, LayoutResultCallback)} to inform you
+ * that the print attributes (page size, density, etc) changed giving you an
+ * opportunity to layout the content to match the new constraints.
+ * </li>
+ * <li>
+ * After every call to {@link #onLayout(PrintAttributes, PrintAttributes,
+ * CancellationSignal, LayoutResultCallback)}, you may get a call to {@link
+ * #onWrite(List, FileDescriptor, CancellationSignal, WriteResultCallback)}
+ * asking you to write a PDF file with the content for specific pages.
+ * </li>
+ * <li>
+ * Finally, you will receive a call to {@link #onFinish()}. You can use this
+ * callback to release resources allocated in {@link #onStart()}.
+ * </li>
+ * </ul>
+ * </p>
+ */
+public abstract class PrintDocumentAdapter {
+
+ /**
+ * Called when printing starts. You can use this callback to allocate
+ * resources. This method is invoked on the main thread.
+ */
+ public void onStart() {
+ /* do nothing - stub */
+ }
+
+ /**
+ * Called when the print attributes (page size, density, etc) changed
+ * giving you a chance to layout the content such that it matches the
+ * new constraints. This method is invoked on the main thread.
+ * <p>
+ * After you are done laying out, you must invoke: {@link LayoutResultCallback
+ * #onLayoutFinished(PrintDocumentInfo, boolean)} with the last argument <code>true
+ * </code> or <code>false</code> depending on whether the layout changed the
+ * content or not, respectively; and {@link LayoutResultCallback#onLayoutFailed(
+ * CharSequence), if an error occurred.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> If the content is large and a layout will be
+ * performed, it is a good practice to schedule the work on a dedicated
+ * thread and register an observer in the provided {@link
+ * CancellationSignal} upon invocation of which you should stop the
+ * layout. The cancellation callback will not be made on the main
+ * thread.
+ * </p>
+ *
+ * @param oldAttributes The old print attributes.
+ * @param newAttributes The new print attributes.
+ * @param cancellationSignal Signal for observing cancel layout requests.
+ * @param callback Callback to inform the system for the layout result.
+ *
+ * @see LayoutResultCallback
+ * @see CancellationSignal
+ */
+ public abstract void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
+ CancellationSignal cancellationSignal, LayoutResultCallback callback);
+
+ /**
+ * Called when specific pages of the content should be written in the
+ * from of a PDF file to the given file descriptor. This method is invoked
+ * on the main thread.
+ *<p>
+ * After you are done writing, you should <strong>not</strong> close the
+ * file descriptor, rather you must invoke: {@link WriteResultCallback
+ * #onWriteFinished()}, if writing completed successfully; or {@link
+ * WriteResultCallback#onWriteFailed(CharSequence)}, if an error occurred.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> If the printed content is large, it is a good
+ * practice to schedule writing it on a dedicated thread and register an
+ * observer in the provided {@link CancellationSignal} upon invocation of
+ * which you should stop writing. The cancellation callback will not be
+ * made on the main thread.
+ * </p>
+ *
+ * @param pages The pages whose content to print.
+ * @param destination The destination file descriptor to which to write.
+ * @param cancellationSignal Signal for observing cancel writing requests.
+ * @param callback Callback to inform the system for the write result.
+ *
+ * @see WriteResultCallback
+ * @see CancellationSignal
+ */
+ public abstract void onWrite(List<PageRange> pages, FileDescriptor destination,
+ CancellationSignal cancellationSignal, WriteResultCallback callback);
+
+ /**
+ * Called when printing finishes. You can use this callback to release
+ * resources acquired in {@link #onStart()}. This method is invoked on
+ * the main thread.
+ */
+ public void onFinish() {
+ /* do nothing - stub */
+ }
+
+ /**
+ * Base class for implementing a callback for the result of {@link
+ * PrintDocumentAdapter#onWrite(List, FileDescriptor, CancellationSignal,
+ * WriteResultCallback)}.
+ */
+ public static abstract class WriteResultCallback {
+
+ /**
+ * @hide
+ */
+ public WriteResultCallback() {
+ /* do nothing - hide constructor */
+ }
+
+ /**
+ * Notifies that all the data was written.
+ *
+ * @param pages The pages that were written.
+ */
+ public void onWriteFinished(List<PageRange> pages) {
+ /* do nothing - stub */
+ }
+
+ /**
+ * Notifies that an error occurred while writing the data.
+ *
+ * @param error Error message. May be null if error is unknown.
+ */
+ public void onWriteFailed(CharSequence error) {
+ /* do nothing - stub */
+ }
+ }
+
+ /**
+ * Base class for implementing a callback for the result of {@link
+ * PrintDocumentAdapter#onLayout(PrintAttributes, PrintAttributes,
+ * CancellationSignal, LayoutResultCallback)}.
+ */
+ public static abstract class LayoutResultCallback {
+
+ /**
+ * @hide
+ */
+ public LayoutResultCallback() {
+ /* do nothing - hide constructor */
+ }
+
+ /**
+ * Notifies that the layout finished and whether the content changed.
+ *
+ * @param info An info object describing the document.
+ * @param changed Whether the layout changed.
+ *
+ * @see PrintDocumentInfo
+ */
+ public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
+ /* do nothing - stub */
+ }
+
+ /**
+ * Notifies that an error occurred while laying out the document.
+ *
+ * @param error Error message. May be null if error is unknown.
+ */
+ public void onLayoutFailed(CharSequence error) {
+ /* do nothing - stub */
+ }
+ }
+}
diff --git a/core/java/android/print/PrintAdapterInfo.aidl b/core/java/android/print/PrintDocumentInfo.aidl
index 27bf717f6d20..831dcb7ba1ae 100644
--- a/core/java/android/print/PrintAdapterInfo.aidl
+++ b/core/java/android/print/PrintDocumentInfo.aidl
@@ -16,4 +16,4 @@
package android.print;
-parcelable PrintAdapterInfo;
+parcelable PrintDocumentInfo;
diff --git a/core/java/android/print/PrintDocumentInfo.java b/core/java/android/print/PrintDocumentInfo.java
new file mode 100644
index 000000000000..7731debfa7f2
--- /dev/null
+++ b/core/java/android/print/PrintDocumentInfo.java
@@ -0,0 +1,171 @@
+/*
+ * 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 android.print;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class encapsulates information about a printed document.
+ */
+public final class PrintDocumentInfo implements Parcelable {
+
+ /**
+ * Constant for unknown page count (default).
+ */
+ public static final int PAGE_COUNT_UNKNOWN = -1;
+
+ /**
+ * Content type: unknown (default).
+ */
+ public static final int CONTENT_TYPE_UNKNOWN = -1;
+
+ /**
+ * Content type: document.
+ */
+ public static final int CONTENT_TYPE_DOCUMENT = 0;
+
+ /**
+ * Content type: photo.
+ */
+ public static final int CONTENT_TYPE_PHOTO = 1;
+
+ private int mPageCount;
+ private int mContentType;
+
+ /**
+ * Creates a new instance.
+ */
+ private PrintDocumentInfo() {
+ mPageCount = PAGE_COUNT_UNKNOWN;
+ mContentType = CONTENT_TYPE_UNKNOWN;
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param Prototype from which to clone.
+ */
+ private PrintDocumentInfo(PrintDocumentInfo prototype) {
+ mPageCount = prototype.mPageCount;
+ mContentType = prototype.mContentType;
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param parcel Data from which to initialize.
+ */
+ private PrintDocumentInfo(Parcel parcel) {
+ mPageCount = parcel.readInt();
+ mContentType = parcel.readInt();
+ }
+
+ /**
+ * Gets the total number of pages.
+ *
+ * @return The number of pages.
+ *
+ * @see #PAGE_COUNT_UNKNOWN
+ */
+ public int getPageCount() {
+ return mPageCount;
+ }
+
+ /**
+ * Gets the content type.
+ *
+ * @return The content type.
+ *
+ * @see #CONTENT_TYPE_UNKNOWN
+ * @see #CONTENT_TYPE_DOCUMENT
+ * @see #CONTENT_TYPE_PHOTO
+ */
+ public int getContentType() {
+ return mContentType;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mPageCount);
+ parcel.writeInt(mContentType);
+ }
+
+ /**
+ * Builder for creating an {@link PrintDocumentInfo}.
+ */
+ public static final class Builder {
+ private final PrintDocumentInfo mPrototype = new PrintDocumentInfo();
+
+ /**
+ * Sets the total number of pages.
+ *
+ * @param pageCount The number of pages. Must be greater than
+ * or equal to zero or {@link PrintDocumentInfo#PAGE_COUNT_UNKNOWN}.
+ */
+ public Builder setPageCount(int pageCount) {
+ if (pageCount < 0 && pageCount != PAGE_COUNT_UNKNOWN) {
+ throw new IllegalArgumentException("pageCount"
+ + " must be greater than or euqal to zero or"
+ + " DocumentInfo#PAGE_COUNT_UNKNOWN");
+ }
+ mPrototype.mPageCount = pageCount;
+ return this;
+ }
+
+ /**
+ * Sets the content type.
+ *
+ * @param type The content type.
+ *
+ * @see #CONTENT_TYPE_UNKNOWN
+ * @see #CONTENT_TYPE_DOCUMENT
+ * @see #CONTENT_TYPE_PHOTO
+ */
+ public Builder setContentType(int type) {
+ mPrototype.mContentType = type;
+ return this;
+ }
+
+ /**
+ * Creates a new {@link PrintDocumentInfo} instance.
+ *
+ * @return The new instance.
+ */
+ public PrintDocumentInfo create() {
+ return new PrintDocumentInfo(mPrototype);
+ }
+ }
+
+ public static final Parcelable.Creator<PrintDocumentInfo> CREATOR =
+ new Creator<PrintDocumentInfo>() {
+ @Override
+ public PrintDocumentInfo createFromParcel(Parcel parcel) {
+ return new PrintDocumentInfo(parcel);
+ }
+
+ @Override
+ public PrintDocumentInfo[] newArray(int size) {
+ return new PrintDocumentInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/print/PrintJob.java b/core/java/android/print/PrintJob.java
index f7cca875a2c9..a5e0b796802a 100644
--- a/core/java/android/print/PrintJob.java
+++ b/core/java/android/print/PrintJob.java
@@ -55,7 +55,7 @@ public final class PrintJob {
* @return The print job info.
*/
public PrintJobInfo getInfo() {
- PrintJobInfo info = mPrintManager.getPrintJob(mId);
+ PrintJobInfo info = mPrintManager.getPrintJobInfo(mId);
if (info != null) {
mCachedInfo = info;
}
diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java
index 72d6057e5323..6e613bcd981f 100644
--- a/core/java/android/print/PrintJobInfo.java
+++ b/core/java/android/print/PrintJobInfo.java
@@ -116,6 +116,44 @@ public final class PrintJobInfo implements Parcelable {
/** The print job attributes size. */
private PrintAttributes mAttributes;
+ /** Information about the printed document. */
+ private PrintDocumentInfo mDocumentInfo;
+
+ /** @hide*/
+ public PrintJobInfo() {
+ /* do nothing */
+ }
+
+ /** @hide */
+ public PrintJobInfo(PrintJobInfo other) {
+ mId = other.mId;
+ mLabel = other.mLabel;
+ mPrinterId = other.mPrinterId;
+ mState = other.mState;
+ mAppId = other.mAppId;
+ mUserId = other.mUserId;
+ mAttributes = other.mAttributes;
+ mDocumentInfo = other.mDocumentInfo;
+ }
+
+ private PrintJobInfo(Parcel parcel) {
+ mId = parcel.readInt();
+ mLabel = parcel.readCharSequence();
+ mPrinterId = parcel.readParcelable(null);
+ mState = parcel.readInt();
+ mAppId = parcel.readInt();
+ mUserId = parcel.readInt();
+ if (parcel.readInt() == 1) {
+ mPageRanges = (PageRange[]) parcel.readParcelableArray(null);
+ }
+ if (parcel.readInt() == 1) {
+ mAttributes = PrintAttributes.CREATOR.createFromParcel(parcel);
+ }
+ if (parcel.readInt() == 1) {
+ mDocumentInfo = PrintDocumentInfo.CREATOR.createFromParcel(parcel);
+ }
+ }
+
/**
* Gets the unique print job id.
*
@@ -300,35 +338,26 @@ public final class PrintJobInfo implements Parcelable {
mAttributes = attributes;
}
- /** @hide*/
- public PrintJobInfo() {
- /* do nothing */
- }
-
- /** @hide */
- public PrintJobInfo(PrintJobInfo other) {
- mId = other.mId;
- mLabel = other.mLabel;
- mPrinterId = other.mPrinterId;
- mState = other.mState;
- mAppId = other.mAppId;
- mUserId = other.mUserId;
- mAttributes = other.mAttributes;
+ /**
+ * Gets the info describing the printed document.
+ *
+ * @return The document info.
+ *
+ * @hide
+ */
+ public PrintDocumentInfo getDocumentInfo() {
+ return mDocumentInfo;
}
- private PrintJobInfo(Parcel parcel) {
- mId = parcel.readInt();
- mLabel = parcel.readCharSequence();
- mPrinterId = parcel.readParcelable(null);
- mState = parcel.readInt();
- mAppId = parcel.readInt();
- mUserId = parcel.readInt();
- if (parcel.readInt() == 1) {
- mPageRanges = (PageRange[]) parcel.readParcelableArray(null);
- }
- if (parcel.readInt() == 1) {
- mAttributes = PrintAttributes.CREATOR.createFromParcel(parcel);
- }
+ /**
+ * Sets the info describing the printed document.
+ *
+ * @param info The document info.
+ *
+ * @hide
+ */
+ public void setDocumentInfo(PrintDocumentInfo info) {
+ mDocumentInfo = info;
}
@Override
@@ -356,6 +385,12 @@ public final class PrintJobInfo implements Parcelable {
} else {
parcel.writeInt(0);
}
+ if (mDocumentInfo != null) {
+ parcel.writeInt(1);
+ mDocumentInfo.writeToParcel(parcel, flags);
+ } else {
+ parcel.writeInt(0);
+ }
}
@Override
@@ -366,7 +401,10 @@ public final class PrintJobInfo implements Parcelable {
builder.append(", id: ").append(mId);
builder.append(", status: ").append(stateToString(mState));
builder.append(", printer: " + mPrinterId);
- builder.append(", attributes: " + (mAttributes != null ? mAttributes.toString() : null));
+ builder.append(", attributes: " + (mAttributes != null
+ ? mAttributes.toString() : null));
+ builder.append(", documentInfo: " + (mDocumentInfo != null
+ ? mDocumentInfo.toString() : null));
builder.append("}");
return builder.toString();
}
diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java
index be9b59658c7b..8913daa9650b 100644
--- a/core/java/android/print/PrintManager.java
+++ b/core/java/android/print/PrintManager.java
@@ -26,7 +26,8 @@ import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
-import android.print.PrintAdapter.PrintResultCallback;
+import android.print.PrintDocumentAdapter.LayoutResultCallback;
+import android.print.PrintDocumentAdapter.WriteResultCallback;
import android.util.Log;
import com.android.internal.os.SomeArgs;
@@ -103,7 +104,7 @@ public final class PrintManager {
* Creates an instance that can access all print jobs.
*
* @param userId The user id for which to get all print jobs.
- * @return An instance of the caller has the permission to access
+ * @return An instance if the caller has the permission to access
* all print jobs, null otherwise.
*
* @hide
@@ -112,11 +113,11 @@ public final class PrintManager {
return new PrintManager(mContext, mService, userId, APP_ID_ANY);
}
- PrintJobInfo getPrintJob(int printJobId) {
+ PrintJobInfo getPrintJobInfo(int printJobId) {
try {
- return mService.getPrintJob(printJobId, mAppId, mUserId);
+ return mService.getPrintJobInfo(printJobId, mAppId, mUserId);
} catch (RemoteException re) {
- Log.e(LOG_TAG, "Error getting print job:" + printJobId, re);
+ Log.e(LOG_TAG, "Error getting a print job info:" + printJobId, re);
}
return null;
}
@@ -130,7 +131,7 @@ public final class PrintManager {
*/
public List<PrintJob> getPrintJobs() {
try {
- List<PrintJobInfo> printJobInfos = mService.getPrintJobs(mAppId, mUserId);
+ List<PrintJobInfo> printJobInfos = mService.getPrintJobInfos(mAppId, mUserId);
if (printJobInfos == null) {
return Collections.emptyList();
}
@@ -141,18 +142,17 @@ public final class PrintManager {
}
return printJobs;
} catch (RemoteException re) {
- Log.e(LOG_TAG, "Error getting print jobs!", re);
+ Log.e(LOG_TAG, "Error getting print jobs", re);
}
return Collections.emptyList();
}
- ICancellationSignal cancelPrintJob(int printJobId) {
+ void cancelPrintJob(int printJobId) {
try {
mService.cancelPrintJob(printJobId, mAppId, mUserId);
} catch (RemoteException re) {
- Log.e(LOG_TAG, "Error cancleing a print job:" + printJobId, re);
+ Log.e(LOG_TAG, "Error cancleing a print job: " + printJobId, re);
}
- return null;
}
/**
@@ -166,24 +166,24 @@ public final class PrintManager {
* @see PrintJob
*/
public PrintJob print(String printJobName, File pdfFile, PrintAttributes attributes) {
- PrintFileAdapter printable = new PrintFileAdapter(pdfFile);
- return print(printJobName, printable, attributes);
+ FileDocumentAdapter documentAdapter = new FileDocumentAdapter(pdfFile);
+ return print(printJobName, documentAdapter, attributes);
}
/**
- * Creates a print job for printing a {@link PrintAdapter} with default print
+ * Creates a print job for printing a {@link PrintDocumentAdapter} with default print
* attributes.
*
* @param printJobName A name for the new print job.
- * @param printAdapter The printable adapter to print.
+ * @param documentAdapter An adapter that emits the document to print.
* @param attributes The default print job attributes.
* @return The created print job.
*
* @see PrintJob
*/
- public PrintJob print(String printJobName, PrintAdapter printAdapter,
+ public PrintJob print(String printJobName, PrintDocumentAdapter documentAdapter,
PrintAttributes attributes) {
- PrintAdapterDelegate delegate = new PrintAdapterDelegate(printAdapter,
+ PrintDocumentAdapterDelegate delegate = new PrintDocumentAdapterDelegate(documentAdapter,
mContext.getMainLooper());
try {
PrintJobInfo printJob = mService.print(printJobName, mPrintClient, delegate,
@@ -217,145 +217,118 @@ public final class PrintManager {
}
}
- private static final class PrintAdapterDelegate extends IPrintAdapter.Stub {
- private final Object mLock = new Object();
-
- private PrintAdapter mPrintAdapter;
+ private static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub {
+ private PrintDocumentAdapter mDocumentAdapter; // Strong reference OK - cleared in finish()
- private Handler mHandler;
+ private Handler mHandler; // Strong reference OK - cleared in finish()
- public PrintAdapterDelegate(PrintAdapter printAdapter, Looper looper) {
- mPrintAdapter = printAdapter;
+ public PrintDocumentAdapterDelegate(PrintDocumentAdapter documentAdapter, Looper looper) {
+ mDocumentAdapter = documentAdapter;
mHandler = new MyHandler(looper);
}
@Override
public void start() {
- synchronized (mLock) {
- if (isFinishedLocked()) {
- return;
- }
- mHandler.obtainMessage(MyHandler.MESSAGE_START,
- mPrintAdapter).sendToTarget();
- }
+ mHandler.sendEmptyMessage(MyHandler.MSG_START);
}
@Override
- public void printAttributesChanged(PrintAttributes attributes) {
- synchronized (mLock) {
- if (isFinishedLocked()) {
- return;
- }
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = mPrintAdapter;
- args.arg2 = attributes;
- mHandler.obtainMessage(MyHandler.MESSAGE_PRINT_ATTRIBUTES_CHANGED,
- args).sendToTarget();
- }
+ public void layout(PrintAttributes oldAttributes,
+ PrintAttributes newAttributes, ILayoutResultCallback callback) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = oldAttributes;
+ args.arg2 = newAttributes;
+ args.arg3 = callback;
+ mHandler.obtainMessage(MyHandler.MSG_LAYOUT, args).sendToTarget();
}
@Override
- public void print(List<PageRange> pages, ParcelFileDescriptor fd,
- IPrintResultCallback callback) {
- synchronized (mLock) {
- if (isFinishedLocked()) {
- return;
- }
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = mPrintAdapter;
- args.arg2 = pages;
- args.arg3 = fd.getFileDescriptor();
- args.arg4 = callback;
- mHandler.obtainMessage(MyHandler.MESSAGE_PRINT, args).sendToTarget();
- }
+ public void write(List<PageRange> pages, ParcelFileDescriptor fd,
+ IWriteResultCallback callback) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = pages;
+ args.arg2 = fd.getFileDescriptor();
+ args.arg3 = callback;
+ mHandler.obtainMessage(MyHandler.MSG_WRITE, args).sendToTarget();
}
@Override
public void finish() {
- synchronized (mLock) {
- if (isFinishedLocked()) {
- return;
- }
- mHandler.obtainMessage(MyHandler.MESSAGE_FINIS,
- mPrintAdapter).sendToTarget();
- }
+ mHandler.sendEmptyMessage(MyHandler.MSG_FINISH);
}
- private boolean isFinishedLocked() {
- return mPrintAdapter == null;
+ private boolean isFinished() {
+ return mDocumentAdapter == null;
}
- private void finishLocked() {
- mPrintAdapter = null;
+ private void doFinish() {
+ mDocumentAdapter = null;
mHandler = null;
}
private final class MyHandler extends Handler {
- public static final int MESSAGE_START = 1;
- public static final int MESSAGE_PRINT_ATTRIBUTES_CHANGED = 2;
- public static final int MESSAGE_PRINT = 3;
- public static final int MESSAGE_FINIS = 4;
+ public static final int MSG_START = 1;
+ public static final int MSG_LAYOUT = 2;
+ public static final int MSG_WRITE = 3;
+ public static final int MSG_FINISH = 4;
public MyHandler(Looper looper) {
super(looper, null, true);
}
@Override
+ @SuppressWarnings("unchecked")
public void handleMessage(Message message) {
+ if (isFinished()) {
+ return;
+ }
switch (message.what) {
- case MESSAGE_START: {
- PrintAdapter adapter = (PrintAdapter) message.obj;
- adapter.onStart();
+ case MSG_START: {
+ mDocumentAdapter.onStart();
} break;
- case MESSAGE_PRINT_ATTRIBUTES_CHANGED: {
+ case MSG_LAYOUT: {
SomeArgs args = (SomeArgs) message.obj;
- PrintAdapter adapter = (PrintAdapter) args.arg1;
- PrintAttributes attributes = (PrintAttributes) args.arg2;
+ PrintAttributes oldAttributes = (PrintAttributes) args.arg1;
+ PrintAttributes newAttributes = (PrintAttributes) args.arg2;
+ ILayoutResultCallback callback = (ILayoutResultCallback) args.arg3;
args.recycle();
- adapter.onPrintAttributesChanged(attributes);
+
+ try {
+ ICancellationSignal remoteSignal = CancellationSignal.createTransport();
+ callback.onLayoutStarted(remoteSignal);
+
+ mDocumentAdapter.onLayout(oldAttributes, newAttributes,
+ CancellationSignal.fromTransport(remoteSignal),
+ new LayoutResultCallbackWrapper(callback));
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error printing", re);
+ }
} break;
- case MESSAGE_PRINT: {
+ case MSG_WRITE: {
SomeArgs args = (SomeArgs) message.obj;
- PrintAdapter adapter = (PrintAdapter) args.arg1;
- @SuppressWarnings("unchecked")
- List<PageRange> pages = (List<PageRange>) args.arg2;
- final FileDescriptor fd = (FileDescriptor) args.arg3;
- IPrintResultCallback callback = (IPrintResultCallback) args.arg4;
+ List<PageRange> pages = (List<PageRange>) args.arg1;
+ FileDescriptor fd = (FileDescriptor) args.arg2;
+ IWriteResultCallback callback = (IWriteResultCallback) args.arg3;
args.recycle();
+
try {
ICancellationSignal remoteSignal = CancellationSignal.createTransport();
- callback.onPrintStarted(adapter.getInfo(), remoteSignal);
-
- CancellationSignal localSignal = CancellationSignal.fromTransport(
- remoteSignal);
- adapter.onPrint(pages, fd, localSignal,
- new PrintResultCallbackWrapper(callback) {
- @Override
- public void onPrintFinished(List<PageRange> pages) {
- IoUtils.closeQuietly(fd);
- super.onPrintFinished(pages);
- }
-
- @Override
- public void onPrintFailed(CharSequence error) {
- IoUtils.closeQuietly(fd);
- super.onPrintFailed(error);
- }
- });
+ callback.onWriteStarted(remoteSignal);
+
+ mDocumentAdapter.onWrite(pages, fd,
+ CancellationSignal.fromTransport(remoteSignal),
+ new WriteResultCallbackWrapper(callback, fd));
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error printing", re);
IoUtils.closeQuietly(fd);
}
} break;
- case MESSAGE_FINIS: {
- PrintAdapter adapter = (PrintAdapter) message.obj;
- adapter.onFinish();
- synchronized (mLock) {
- finishLocked();
- }
+ case MSG_FINISH: {
+ mDocumentAdapter.onFinish();
+ doFinish();
} break;
default: {
@@ -367,29 +340,65 @@ public final class PrintManager {
}
}
- private static abstract class PrintResultCallbackWrapper extends PrintResultCallback {
+ private static final class WriteResultCallbackWrapper extends WriteResultCallback {
+
+ private final IWriteResultCallback mWrappedCallback;
+ private final FileDescriptor mFd;
+
+ public WriteResultCallbackWrapper(IWriteResultCallback callback,
+ FileDescriptor fd) {
+ mWrappedCallback = callback;
+ mFd = fd;
+ }
+
+ @Override
+ public void onWriteFinished(List<PageRange> pages) {
+ try {
+ // Close before notifying the other end. We want
+ // to be ready by the time we announce it.
+ IoUtils.closeQuietly(mFd);
+ mWrappedCallback.onWriteFinished(pages);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error calling onWriteFinished", re);
+ }
+ }
+
+ @Override
+ public void onWriteFailed(CharSequence error) {
+ try {
+ // Close before notifying the other end. We want
+ // to be ready by the time we announce it.
+ IoUtils.closeQuietly(mFd);
+ mWrappedCallback.onWriteFailed(error);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error calling onWriteFailed", re);
+ }
+ }
+ }
+
+ private static final class LayoutResultCallbackWrapper extends LayoutResultCallback {
- private final IPrintResultCallback mWrappedCallback;
+ private final ILayoutResultCallback mWrappedCallback;
- public PrintResultCallbackWrapper(IPrintResultCallback callback) {
+ public LayoutResultCallbackWrapper(ILayoutResultCallback callback) {
mWrappedCallback = callback;
}
@Override
- public void onPrintFinished(List<PageRange> pages) {
+ public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
try {
- mWrappedCallback.onPrintFinished(pages);
+ mWrappedCallback.onLayoutFinished(info, changed);
} catch (RemoteException re) {
- Log.e(LOG_TAG, "Error calling onPrintFinished", re);
+ Log.e(LOG_TAG, "Error calling onLayoutFinished", re);
}
}
@Override
- public void onPrintFailed(CharSequence error) {
+ public void onLayoutFailed(CharSequence error) {
try {
- mWrappedCallback.onPrintFailed(error);
+ mWrappedCallback.onLayoutFailed(error);
} catch (RemoteException re) {
- Log.e(LOG_TAG, "Error calling onPrintFailed", re);
+ Log.e(LOG_TAG, "Error calling onLayoutFailed", re);
}
}
}
diff --git a/core/java/android/print/PrinterId.java b/core/java/android/print/PrinterId.java
index b853eb0f8e8a..8a3148c35c8a 100644
--- a/core/java/android/print/PrinterId.java
+++ b/core/java/android/print/PrinterId.java
@@ -54,7 +54,7 @@ public final class PrinterId implements Parcelable {
*
* @hide
*/
- public ComponentName getServiceComponentName() {
+ public ComponentName getService() {
return mServiceComponentName;
}
diff --git a/core/java/android/print/PrinterInfo.java b/core/java/android/print/PrinterInfo.java
index 9283472ee5fc..da3b6bc27a16 100644
--- a/core/java/android/print/PrinterInfo.java
+++ b/core/java/android/print/PrinterInfo.java
@@ -86,6 +86,31 @@ public final class PrinterInfo implements Parcelable {
mDefaults.put(PROPERTY_ORIENTATION, DEFAULT_UNDEFINED);
}
+ private PrinterInfo(PrinterInfo prototype) {
+ mId = prototype.mId;
+ mLabel = prototype.mLabel;
+ mStatus = prototype.mStatus;
+
+ mMinMargins = prototype.mMinMargins;
+ mMediaSizes.addAll(prototype.mMediaSizes);
+ mResolutions.addAll(prototype.mResolutions);
+ mInputTrays = (prototype.mInputTrays != null)
+ ? new ArrayList<Tray>(prototype.mInputTrays) : null;
+ mOutputTrays = (prototype.mOutputTrays != null)
+ ? new ArrayList<Tray>(prototype.mOutputTrays) : null;
+
+ mDuplexModes = prototype.mDuplexModes;
+ mColorModes = prototype.mColorModes;
+ mFittingModes = prototype.mFittingModes;
+ mOrientations = prototype.mOrientations;
+
+ final int defaultCount = prototype.mDefaults.size();
+ for (int i = 0; i < defaultCount; i++) {
+ mDefaults.put(prototype.mDefaults.keyAt(i), prototype.mDefaults.valueAt(i));
+ }
+ mDefaultMargins = prototype.mDefaultMargins;
+ }
+
/**
* Get the globally unique printer id.
*
@@ -437,7 +462,7 @@ public final class PrinterInfo implements Parcelable {
* </p>
*/
public static final class Builder {
- private final PrinterInfo mPrinterInfo;
+ private final PrinterInfo mPrototype;
/**
* Creates a new instance.
@@ -455,9 +480,9 @@ public final class PrinterInfo implements Parcelable {
if (TextUtils.isEmpty(label)) {
throw new IllegalArgumentException("label cannot be empty.");
}
- mPrinterInfo = new PrinterInfo();
- mPrinterInfo.mLabel = label;
- mPrinterInfo.mId = printerId;
+ mPrototype = new PrinterInfo();
+ mPrototype.mLabel = label;
+ mPrototype.mId = printerId;
}
/**
@@ -470,7 +495,7 @@ public final class PrinterInfo implements Parcelable {
* @return This builder.
*/
public Builder setStatus(int status) {
- mPrinterInfo.mStatus = status;
+ mPrototype.mStatus = status;
return this;
}
@@ -489,11 +514,11 @@ public final class PrinterInfo implements Parcelable {
* @see PrintAttributes.MediaSize
*/
public Builder addMediaSize(MediaSize mediaSize, boolean isDefault) {
- final int insertionIndex = mPrinterInfo.mMediaSizes.size();
- mPrinterInfo.mMediaSizes.add(mediaSize);
+ final int insertionIndex = mPrototype.mMediaSizes.size();
+ mPrototype.mMediaSizes.add(mediaSize);
if (isDefault) {
throwIfDefaultAlreadySpecified(PROPERTY_MEDIA_SIZE);
- mPrinterInfo.mDefaults.put(PROPERTY_MEDIA_SIZE, insertionIndex);
+ mPrototype.mDefaults.put(PROPERTY_MEDIA_SIZE, insertionIndex);
}
return this;
}
@@ -514,11 +539,11 @@ public final class PrinterInfo implements Parcelable {
* @see PrintAttributes.Resolution
*/
public Builder addResolution(Resolution resolution, boolean isDefault) {
- final int insertionIndex = mPrinterInfo.mResolutions.size();
- mPrinterInfo.mResolutions.add(resolution);
+ final int insertionIndex = mPrototype.mResolutions.size();
+ mPrototype.mResolutions.add(resolution);
if (isDefault) {
throwIfDefaultAlreadySpecified(PROPERTY_RESOLUTION);
- mPrinterInfo.mDefaults.put(PROPERTY_RESOLUTION, insertionIndex);
+ mPrototype.mDefaults.put(PROPERTY_RESOLUTION, insertionIndex);
}
return this;
}
@@ -543,8 +568,8 @@ public final class PrinterInfo implements Parcelable {
throw new IllegalArgumentException("Default margins"
+ " cannot be outside of the min margins.");
}
- mPrinterInfo.mMinMargins = margins;
- mPrinterInfo.mDefaultMargins = defaultMargins;
+ mPrototype.mMinMargins = margins;
+ mPrototype.mDefaultMargins = defaultMargins;
return this;
}
@@ -564,14 +589,14 @@ public final class PrinterInfo implements Parcelable {
* @see PrintAttributes.Tray
*/
public Builder addInputTray(Tray inputTray, boolean isDefault) {
- if (mPrinterInfo.mInputTrays == null) {
- mPrinterInfo.mInputTrays = new ArrayList<Tray>();
+ if (mPrototype.mInputTrays == null) {
+ mPrototype.mInputTrays = new ArrayList<Tray>();
}
- final int insertionIndex = mPrinterInfo.mInputTrays.size();
- mPrinterInfo.mInputTrays.add(inputTray);
+ final int insertionIndex = mPrototype.mInputTrays.size();
+ mPrototype.mInputTrays.add(inputTray);
if (isDefault) {
throwIfDefaultAlreadySpecified(PROPERTY_INPUT_TRAY);
- mPrinterInfo.mDefaults.put(PROPERTY_INPUT_TRAY, insertionIndex);
+ mPrototype.mDefaults.put(PROPERTY_INPUT_TRAY, insertionIndex);
}
return this;
}
@@ -592,14 +617,14 @@ public final class PrinterInfo implements Parcelable {
* @see PrintAttributes.Tray
*/
public Builder addOutputTray(Tray outputTray, boolean isDefault) {
- if (mPrinterInfo.mOutputTrays == null) {
- mPrinterInfo.mOutputTrays = new ArrayList<Tray>();
+ if (mPrototype.mOutputTrays == null) {
+ mPrototype.mOutputTrays = new ArrayList<Tray>();
}
- final int insertionIndex = mPrinterInfo.mOutputTrays.size();
- mPrinterInfo.mOutputTrays.add(outputTray);
+ final int insertionIndex = mPrototype.mOutputTrays.size();
+ mPrototype.mOutputTrays.add(outputTray);
if (isDefault) {
throwIfDefaultAlreadySpecified(PROPERTY_OUTPUT_TRAY);
- mPrinterInfo.mDefaults.put(PROPERTY_OUTPUT_TRAY, insertionIndex);
+ mPrototype.mDefaults.put(PROPERTY_OUTPUT_TRAY, insertionIndex);
}
return this;
}
@@ -631,8 +656,8 @@ public final class PrinterInfo implements Parcelable {
throw new IllegalArgumentException("Default color mode not in color modes.");
}
PrintAttributes.enforceValidColorMode(colorModes);
- mPrinterInfo.mColorModes = colorModes;
- mPrinterInfo.mDefaults.put(PROPERTY_COLOR_MODE, defaultColorMode);
+ mPrototype.mColorModes = colorModes;
+ mPrototype.mDefaults.put(PROPERTY_COLOR_MODE, defaultColorMode);
return this;
}
@@ -664,8 +689,8 @@ public final class PrinterInfo implements Parcelable {
throw new IllegalArgumentException("Default duplex mode not in duplex modes.");
}
PrintAttributes.enforceValidDuplexMode(defaultDuplexMode);
- mPrinterInfo.mDuplexModes = duplexModes;
- mPrinterInfo.mDefaults.put(PROPERTY_DUPLEX_MODE, defaultDuplexMode);
+ mPrototype.mDuplexModes = duplexModes;
+ mPrototype.mDefaults.put(PROPERTY_DUPLEX_MODE, defaultDuplexMode);
return this;
}
@@ -696,8 +721,8 @@ public final class PrinterInfo implements Parcelable {
throw new IllegalArgumentException("Default fitting mode not in fiting modes.");
}
PrintAttributes.enfoceValidFittingMode(defaultFittingMode);
- mPrinterInfo.mFittingModes = fittingModes;
- mPrinterInfo.mDefaults.put(PROPERTY_FITTING_MODE, defaultFittingMode);
+ mPrototype.mFittingModes = fittingModes;
+ mPrototype.mDefaults.put(PROPERTY_FITTING_MODE, defaultFittingMode);
return this;
}
@@ -728,8 +753,8 @@ public final class PrinterInfo implements Parcelable {
throw new IllegalArgumentException("Default orientation not in orientations.");
}
PrintAttributes.enforceValidOrientation(defaultOrientation);
- mPrinterInfo.mOrientations = orientations;
- mPrinterInfo.mDefaults.put(PROPERTY_ORIENTATION, defaultOrientation);
+ mPrototype.mOrientations = orientations;
+ mPrototype.mDefaults.put(PROPERTY_ORIENTATION, defaultOrientation);
return this;
}
@@ -743,41 +768,41 @@ public final class PrinterInfo implements Parcelable {
* @throws IllegalStateException If a required attribute was not specified.
*/
public PrinterInfo create() {
- if (mPrinterInfo.mMediaSizes == null || mPrinterInfo.mMediaSizes.isEmpty()) {
+ if (mPrototype.mMediaSizes == null || mPrototype.mMediaSizes.isEmpty()) {
throw new IllegalStateException("No media size specified.");
}
- if (mPrinterInfo.mDefaults.valueAt(PROPERTY_MEDIA_SIZE) == DEFAULT_UNDEFINED) {
+ if (mPrototype.mDefaults.valueAt(PROPERTY_MEDIA_SIZE) == DEFAULT_UNDEFINED) {
throw new IllegalStateException("No default media size specified.");
}
- if (mPrinterInfo.mResolutions == null || mPrinterInfo.mResolutions.isEmpty()) {
+ if (mPrototype.mResolutions == null || mPrototype.mResolutions.isEmpty()) {
throw new IllegalStateException("No resolution specified.");
}
- if (mPrinterInfo.mDefaults.valueAt(PROPERTY_RESOLUTION) == DEFAULT_UNDEFINED) {
+ if (mPrototype.mDefaults.valueAt(PROPERTY_RESOLUTION) == DEFAULT_UNDEFINED) {
throw new IllegalStateException("No default resolution specified.");
}
- if (mPrinterInfo.mColorModes == 0) {
+ if (mPrototype.mColorModes == 0) {
throw new IllegalStateException("No color mode specified.");
}
- if (mPrinterInfo.mDefaults.valueAt(PROPERTY_COLOR_MODE) == DEFAULT_UNDEFINED) {
+ if (mPrototype.mDefaults.valueAt(PROPERTY_COLOR_MODE) == DEFAULT_UNDEFINED) {
throw new IllegalStateException("No default color mode specified.");
}
- if (mPrinterInfo.mOrientations == 0) {
+ if (mPrototype.mOrientations == 0) {
throw new IllegalStateException("No oprientation specified.");
}
- if (mPrinterInfo.mDefaults.valueAt(PROPERTY_ORIENTATION) == DEFAULT_UNDEFINED) {
+ if (mPrototype.mDefaults.valueAt(PROPERTY_ORIENTATION) == DEFAULT_UNDEFINED) {
throw new IllegalStateException("No default orientation specified.");
}
- if (mPrinterInfo.mMinMargins == null) {
- mPrinterInfo.mMinMargins = new Margins(0, 0, 0, 0);
+ if (mPrototype.mMinMargins == null) {
+ mPrototype.mMinMargins = new Margins(0, 0, 0, 0);
}
- if (mPrinterInfo.mDefaultMargins == null) {
- mPrinterInfo.mDefaultMargins = mPrinterInfo.mMinMargins;
+ if (mPrototype.mDefaultMargins == null) {
+ mPrototype.mDefaultMargins = mPrototype.mMinMargins;
}
- return mPrinterInfo;
+ return new PrinterInfo(mPrototype);
}
private void throwIfDefaultAlreadySpecified(int propertyIndex) {
- if (mPrinterInfo.mDefaults.get(propertyIndex) != DEFAULT_UNDEFINED) {
+ if (mPrototype.mDefaults.get(propertyIndex) != DEFAULT_UNDEFINED) {
throw new IllegalArgumentException("Default already specified.");
}
}
diff --git a/core/java/android/printservice/IPrintService.aidl b/core/java/android/printservice/IPrintService.aidl
index eabd96dc0118..c72385adda3f 100644
--- a/core/java/android/printservice/IPrintService.aidl
+++ b/core/java/android/printservice/IPrintService.aidl
@@ -17,6 +17,7 @@
package android.printservice;
import android.os.ICancellationSignal;
+import android.print.IPrinterDiscoveryObserver;
import android.print.PrintJobInfo;
import android.print.PrinterId;
import android.printservice.IPrintServiceClient;
@@ -28,8 +29,8 @@ import android.printservice.IPrintServiceClient;
*/
oneway interface IPrintService {
void setClient(IPrintServiceClient client);
- void requestCancelPrintJob(in PrintJobInfo printJob);
- void onPrintJobQueued(in PrintJobInfo printJob);
- void startPrinterDiscovery();
+ void requestCancelPrintJob(in PrintJobInfo printJobInfo);
+ void onPrintJobQueued(in PrintJobInfo printJobInfo);
+ void startPrinterDiscovery(IPrinterDiscoveryObserver observer);
void stopPrinterDiscovery();
}
diff --git a/core/java/android/printservice/IPrintServiceClient.aidl b/core/java/android/printservice/IPrintServiceClient.aidl
index cff8c028b93b..cdde4d865162 100644
--- a/core/java/android/printservice/IPrintServiceClient.aidl
+++ b/core/java/android/printservice/IPrintServiceClient.aidl
@@ -27,11 +27,9 @@ import android.print.PrinterInfo;
* @hide
*/
interface IPrintServiceClient {
- List<PrintJobInfo> getPrintJobs();
- PrintJobInfo getPrintJob(int printJobId);
+ List<PrintJobInfo> getPrintJobInfos();
+ PrintJobInfo getPrintJobInfo(int printJobId);
boolean setPrintJobState(int printJobId, int status);
boolean setPrintJobTag(int printJobId, String tag);
oneway void writePrintJobData(in ParcelFileDescriptor fd, int printJobId);
- oneway void addDiscoveredPrinters(in List<PrinterInfo> printers);
- oneway void removeDiscoveredPrinters(in List<PrinterId> printers);
}
diff --git a/core/java/android/printservice/PrintDocument.java b/core/java/android/printservice/PrintDocument.java
new file mode 100644
index 000000000000..2a1581a602d5
--- /dev/null
+++ b/core/java/android/printservice/PrintDocument.java
@@ -0,0 +1,91 @@
+/*
+ * 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 android.printservice;
+
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.print.PrintDocumentInfo;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+/**
+ * This class represents a printed document from the perspective of a print
+ * service. It exposes APIs to query the document and obtain its data.
+ */
+public final class PrintDocument {
+
+ private static final String LOG_TAG = "PrintDocument";
+
+ private final int mPrintJobId;
+
+ private final IPrintServiceClient mPrintServiceClient;
+
+ private final PrintDocumentInfo mInfo;
+
+ PrintDocument(int printJobId, IPrintServiceClient printServiceClient,
+ PrintDocumentInfo info) {
+ mPrintJobId = printJobId;
+ mPrintServiceClient = printServiceClient;
+ mInfo = info;
+ }
+
+ /**
+ * Gets the {@link PrintDocumentInfo} that describes this document.
+ *
+ * @return The document info.
+ */
+ public PrintDocumentInfo getInfo() {
+ return mInfo;
+ }
+
+ /**
+ * Gets the data associated with this document. It is a responsibility of the
+ * client to open a stream to the returned file descriptor and fully read the
+ * data.
+ * <p>
+ * <strong>Note:</strong> It is your responsibility to close the file descriptor.
+ * </p>
+ *
+ * @return A file descriptor for reading the data or <code>null</code>.
+ */
+ public FileDescriptor getData() {
+ ParcelFileDescriptor source = null;
+ ParcelFileDescriptor sink = null;
+ try {
+ ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
+ source = fds[0];
+ sink = fds[1];
+ mPrintServiceClient.writePrintJobData(sink, mPrintJobId);
+ return source.getFileDescriptor();
+ } catch (IOException ioe) {
+ Log.e(LOG_TAG, "Error calling getting print job data!", ioe);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error calling getting print job data!", re);
+ } finally {
+ if (sink != null) {
+ try {
+ sink.close();
+ } catch (IOException ioe) {
+ /* ignore */
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java
index f490f911936f..80530a7cb8d6 100644
--- a/core/java/android/printservice/PrintJob.java
+++ b/core/java/android/printservice/PrintJob.java
@@ -16,10 +16,6 @@
package android.printservice;
-import java.io.FileDescriptor;
-import java.io.IOException;
-
-import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.print.PrintJobInfo;
import android.util.Log;
@@ -33,19 +29,16 @@ public final class PrintJob {
private static final String LOG_TAG = "PrintJob";
- private final int mId;
-
private final IPrintServiceClient mPrintServiceClient;
+ private final PrintDocument mDocument;
+
private PrintJobInfo mCachedInfo;
- PrintJob(PrintJobInfo info, IPrintServiceClient client) {
- if (client == null) {
- throw new IllegalStateException("Print serivice not connected!");
- }
- mCachedInfo = info;
- mId = info.getId();
+ PrintJob(PrintJobInfo jobInfo, IPrintServiceClient client) {
+ mCachedInfo = jobInfo;
mPrintServiceClient = client;
+ mDocument = new PrintDocument(mCachedInfo.getId(), client, jobInfo.getDocumentInfo());
}
/**
@@ -54,7 +47,7 @@ public final class PrintJob {
* @return The id.
*/
public int getId() {
- return mId;
+ return mCachedInfo.getId();
}
/**
@@ -70,9 +63,9 @@ public final class PrintJob {
public PrintJobInfo getInfo() {
PrintJobInfo info = null;
try {
- info = mPrintServiceClient.getPrintJob(mId);
+ info = mPrintServiceClient.getPrintJobInfo(mCachedInfo.getId());
} catch (RemoteException re) {
- Log.e(LOG_TAG, "Couldn't get info for job: " + mId, re);
+ Log.e(LOG_TAG, "Couldn't get info for job: " + mCachedInfo.getId(), re);
}
if (info != null) {
mCachedInfo = info;
@@ -81,6 +74,15 @@ public final class PrintJob {
}
/**
+ * Gets the document of this print job.
+ *
+ * @return The document.
+ */
+ public PrintDocument getDocument() {
+ return mDocument;
+ }
+
+ /**
* Gets whether this print job is queued. Such a print job is
* ready to be printed and can be started.
*
@@ -103,7 +105,7 @@ public final class PrintJob {
* @see #fail(CharSequence)
*/
public boolean isStarted() {
- return getInfo().getState() == PrintJobInfo.STATE_STARTED;
+ return getInfo().getState() == PrintJobInfo.STATE_STARTED;
}
/**
@@ -181,48 +183,13 @@ public final class PrintJob {
*/
public boolean setTag(String tag) {
try {
- return mPrintServiceClient.setPrintJobTag(mId, tag);
+ return mPrintServiceClient.setPrintJobTag(mCachedInfo.getId(), tag);
} catch (RemoteException re) {
- Log.e(LOG_TAG, "Error setting tag for job:" + mId, re);
+ Log.e(LOG_TAG, "Error setting tag for job: " + mCachedInfo.getId(), re);
}
return false;
}
- /**
- * Gets the data associated with this print job. It is a responsibility of
- * the print service to open a stream to the returned file descriptor
- * and fully read the content.
- * <p>
- * <strong>Note:</strong> It is your responsibility to close the file descriptor.
- * </p>
- *
- * @return A file descriptor for reading the data or <code>null</code>.
- */
- public final FileDescriptor getData() {
- ParcelFileDescriptor source = null;
- ParcelFileDescriptor sink = null;
- try {
- ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
- source = fds[0];
- sink = fds[1];
- mPrintServiceClient.writePrintJobData(sink, mId);
- return source.getFileDescriptor();
- } catch (IOException ioe) {
- Log.e(LOG_TAG, "Error calling getting print job data!", ioe);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error calling getting print job data!", re);
- } finally {
- if (sink != null) {
- try {
- sink.close();
- } catch (IOException ioe) {
- /* ignore */
- }
- }
- }
- return null;
- }
-
@Override
public boolean equals(Object obj) {
if (this == obj) {
@@ -235,23 +202,25 @@ public final class PrintJob {
return false;
}
PrintJob other = (PrintJob) obj;
- return (mId == other.mId);
+ return (mCachedInfo.getId() == other.mCachedInfo.getId());
}
@Override
public int hashCode() {
- return mId;
+ return mCachedInfo.getId();
}
private boolean setState(int state) {
- // Best effort - update the state of the cached info since
- // we may not be able to re-fetch it later if the job gets
- // removed from the spooler.
- mCachedInfo.setState(state);
try {
- return mPrintServiceClient.setPrintJobState(mId, state);
+ if (mPrintServiceClient.setPrintJobState(mCachedInfo.getId(), state)) {
+ // Best effort - update the state of the cached info since
+ // we may not be able to re-fetch it later if the job gets
+ // removed from the spooler as a result of the state change.
+ mCachedInfo.setState(state);
+ return true;
+ }
} catch (RemoteException re) {
- Log.e(LOG_TAG, "Error setting the state of job:" + mId, re);
+ Log.e(LOG_TAG, "Error setting the state of job: " + mCachedInfo.getId(), re);
}
return false;
}
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
index 92569661138d..820c2d8ff4ef 100644
--- a/core/java/android/printservice/PrintService.java
+++ b/core/java/android/printservice/PrintService.java
@@ -25,6 +25,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.print.IPrinterDiscoveryObserver;
import android.print.PrintJobInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
@@ -47,7 +48,7 @@ import java.util.List;
* {@link #onStartPrinterDiscovery()} and ends with a call to
* {@link #onStopPrinterDiscovery()}. During a printer discovery
* period the print service reports newly discovered printers by
- * calling {@link #addDiscoveredPrinters(List)} and added printers
+ * calling {@link #addDiscoveredPrinters(List)} and reports added printers
* that disappeared by calling {@link #removeDiscoveredPrinters(List)}.
* Calls to {@link #addDiscoveredPrinters(List)} and
* {@link #removeDiscoveredPrinters(List)} before a call to
@@ -67,26 +68,30 @@ import java.util.List;
* a call to {@link #onPrintJobQueued(PrintJob)} is made and the print
* service may handle it immediately or schedule that for an appropriate
* time in the future. The list of all print jobs for this service
- * are be available by calling {@link #getPrintJobs()}. A queued print
- * job is one whose {@link PrintJob#isQueued()} return true.
+ * are be available by calling {@link #getPrintJobs()}.
* </p>
* <p>
* A print service is responsible for setting the print job state as
* appropriate while processing it. Initially, a print job is in a
* {@link PrintJobInfo#STATE_QUEUED} state which means that the data to
* be printed is spooled by the system and the print service can obtain
- * that data by calling {@link PrintJob#getData()}. After the print
- * service starts printing the data it should set the print job state
- * to {@link PrintJobInfo#STATE_STARTED}. Upon successful completion, the
- * print job state has to be set to {@link PrintJobInfo#STATE_COMPLETED}.
- * In a case of a failure, the print job state should be set to
- * {@link PrintJobInfo#STATE_FAILED}. If a print job is in a
- * {@link PrintJobInfo#STATE_STARTED} state and the user requests to
- * cancel it, the print service will receive a call to
- * {@link #onRequestCancelPrintJob(PrintJob)} which requests from the
- * service to do a best effort in canceling the job. In case the job
- * is successfully canceled, its state has to be set to
- * {@link PrintJobInfo#STATE_CANCELED}.
+ * that data by calling {@link PrintJob#getDocument()}. A queued print
+ * job's {@link PrintJob#isQueued()} method returns true.
+ * </p>
+ * <p>
+ * After the print service starts printing the data it should set the
+ * print job state to {@link PrintJobInfo#STATE_STARTED} by calling
+ * {@link PrintJob#start()}. Upon successful completion, the print job
+ * state has to be set to {@link PrintJobInfo#STATE_COMPLETED} by calling
+ * {@link PrintJob#complete()}. In case of a failure, the print job
+ * state should be set to {@link PrintJobInfo#STATE_FAILED} by calling
+ * {@link PrintJob#fail(CharSequence)}. If a print job is in a
+ * {@link PrintJobInfo#STATE_STARTED} state, i.e. {@link PrintJob#isStarted()}
+ * return true, and the user requests to cancel it, the print service will
+ * receive a call to {@link #onRequestCancelPrintJob(PrintJob)} which
+ * requests from the service to do a best effort in canceling the job. In
+ * case the job is successfully canceled, its state has to be set to
+ * {@link PrintJobInfo#STATE_CANCELED}. by calling {@link PrintJob#cancel()}.
* </p>
* <h3>Lifecycle</h3>
* <p>
@@ -124,9 +129,9 @@ import java.util.List;
* <p>
* A print service can be configured by specifying an optional settings
* activity which exposes service specific options, an optional add
- * prints activity which is used for manual addition of printers, etc.
- * It is a responsibility of the system to launch the settings and add
- * printers activities when appropriate.
+ * prints activity which is used for manual addition of printers, vendor
+ * name ,etc. It is a responsibility of the system to launch the settings
+ * and add printers activities when appropriate.
* </p>
* <p>
* A print service is configured by providing a
@@ -148,7 +153,7 @@ import java.util.List;
*/
public abstract class PrintService extends Service {
- private static final String LOG_TAG = PrintService.class.getSimpleName();
+ private static final String LOG_TAG = "PrintService";
/**
* The {@link Intent} action that must be declared as handled by a service
@@ -162,6 +167,7 @@ public abstract class PrintService extends Service {
* <code>&lt;{@link android.R.styleable#PrintService print-service}&gt;</code>
* tag. This is a a sample XML file configuring a print service:
* <pre> &lt;print-service
+ * android:vendor="SomeVendor"
* android:settingsActivity="foo.bar.MySettingsActivity"
* andorid:addPrintersActivity="foo.bar.MyAddPrintersActivity."
* . . .
@@ -175,7 +181,7 @@ public abstract class PrintService extends Service {
private IPrintServiceClient mClient;
- private boolean mDiscoveringPrinters;
+ private IPrinterDiscoveryObserver mDiscoveryObserver;
@Override
protected void attachBaseContext(Context base) {
@@ -230,29 +236,29 @@ public abstract class PrintService extends Service {
* printers have to be added. You can call this method as many times as
* necessary during the discovery period but should not pass in already
* added printers. If a printer is already added in the same printer
- * discovery period, it will be ignored.
+ * discovery period, it will be ignored. If you want to update an already
+ * added printer, you should removed it and then re-add it.
* </p>
*
* @param printers A list with discovered printers.
*
- * @throws IllegalStateException If this service is not connected.
- *
* @see #removeDiscoveredPrinters(List)
* @see #onStartPrinterDiscovery()
* @see #onStopPrinterDiscovery()
+ *
+ * @throws IllegalStateException If this service is not connected.
*/
public final void addDiscoveredPrinters(List<PrinterInfo> printers) {
+ final IPrinterDiscoveryObserver observer;
synchronized (mLock) {
- if (mClient == null) {
- throw new IllegalStateException("Print serivice not connected!");
- }
- if (mDiscoveringPrinters) {
- try {
- // Calling with a lock into the system is fine.
- mClient.addDiscoveredPrinters(printers);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error adding discovered printers!", re);
- }
+ throwIfNotConnectedLocked();
+ observer = mDiscoveryObserver;
+ }
+ if (observer != null) {
+ try {
+ observer.addDiscoveredPrinters(printers);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error adding discovered printers", re);
}
}
}
@@ -269,37 +275,38 @@ public abstract class PrintService extends Service {
* this method as many times as necessary during the discovery period
* but should not pass in already removed printer ids. If a printer with
* a given id is already removed in the same discovery period, it will
- * be ignored.
+ * be ignored. If you want to update an already added printer, you should
+ * removed it and then re-add it.
* </p>
*
* @param printerIds A list with disappeared printer ids.
*
- * @throws IllegalStateException If this service is not connected.
- *
* @see #addDiscoveredPrinters(List)
* @see #onStartPrinterDiscovery()
* @see #onStopPrinterDiscovery()
+ *
+ * @throws IllegalStateException If this service is not connected.
*/
public final void removeDiscoveredPrinters(List<PrinterId> printerIds) {
+ final IPrinterDiscoveryObserver observer;
synchronized (mLock) {
- if (mClient == null) {
- throw new IllegalStateException("Print serivice not connected!");
- }
- if (mDiscoveringPrinters) {
- try {
- // Calling with a lock into the system is fine.
- mClient.removeDiscoveredPrinters(printerIds);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error removing discovered printers!", re);
- }
+ throwIfNotConnectedLocked();
+ observer = mDiscoveryObserver;
+ }
+ if (observer != null) {
+ try {
+ observer.removeDiscoveredPrinters(printerIds);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error removing discovered printers", re);
}
}
}
/**
* Called when canceling of a print job is requested. The service
- * should do best effort to fulfill the request. After the print
- * job is canceled by calling {@link PrintJob#cancel()}.
+ * should do best effort to fulfill the request. After the cancellation
+ * is performed, the print job should be set to a cancelled state by
+ * calling {@link PrintJob#cancel()}.
*
* @param printJob The print job to be canceled.
*/
@@ -310,11 +317,12 @@ public abstract class PrintService extends Service {
* Called when there is a queued print job for one of the printers
* managed by this print service. A queued print job is ready for
* processing by a print service which can get the data to be printed
- * by calling {@link PrintJob#getData()}. This service may start
+ * by calling {@link PrintJob#getDocument()}. This service may start
* processing the passed in print job or schedule handling of queued
* print jobs at a convenient time. The service can get the print
* jobs by a call to {@link #getPrintJobs()} and examine their state
- * to find the ones with state {@link PrintJobInfo#STATE_QUEUED}.
+ * to find the ones with state {@link PrintJobInfo#STATE_QUEUED} by
+ * calling {@link PrintJob#isQueued()}.
*
* @param printJob The new queued print job.
*
@@ -330,28 +338,31 @@ public abstract class PrintService extends Service {
* @throws IllegalStateException If this service is not connected.
*/
public final List<PrintJob> getPrintJobs() {
+ final IPrintServiceClient client;
synchronized (mLock) {
- if (mClient == null) {
- throw new IllegalStateException("Print serivice not connected!");
- }
- try {
- List<PrintJob> printJobs = null;
- List<PrintJobInfo> printJobInfos = mClient.getPrintJobs();
- if (printJobInfos != null) {
- final int printJobInfoCount = printJobInfos.size();
- printJobs = new ArrayList<PrintJob>(printJobInfoCount);
- for (int i = 0; i < printJobInfoCount; i++) {
- printJobs.add(new PrintJob(printJobInfos.get(i), mClient));
- }
- }
- if (printJobs != null) {
- return printJobs;
+ throwIfNotConnectedLocked();
+ client = mClient;
+ }
+ if (client == null) {
+ return Collections.emptyList();
+ }
+ try {
+ List<PrintJob> printJobs = null;
+ List<PrintJobInfo> printJobInfos = client.getPrintJobInfos();
+ if (printJobInfos != null) {
+ final int printJobInfoCount = printJobInfos.size();
+ printJobs = new ArrayList<PrintJob>(printJobInfoCount);
+ for (int i = 0; i < printJobInfoCount; i++) {
+ printJobs.add(new PrintJob(printJobInfos.get(i), client));
}
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error calling getPrintJobs()", re);
}
- return Collections.emptyList();
+ if (printJobs != null) {
+ return printJobs;
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error calling getPrintJobs()", re);
}
+ return Collections.emptyList();
}
/**
@@ -375,8 +386,9 @@ public abstract class PrintService extends Service {
}
@Override
- public void startPrinterDiscovery() {
- mHandler.sendEmptyMessage(MyHandler.MESSAGE_START_PRINTER_DISCOVERY);
+ public void startPrinterDiscovery(IPrinterDiscoveryObserver observer) {
+ mHandler.obtainMessage(MyHandler.MESSAGE_START_PRINTER_DISCOVERY,
+ observer).sendToTarget();
}
@Override
@@ -385,18 +397,25 @@ public abstract class PrintService extends Service {
}
@Override
- public void requestCancelPrintJob(PrintJobInfo printJob) {
- mHandler.obtainMessage(MyHandler.MESSAGE_CANCEL_PRINTJOB, printJob).sendToTarget();
+ public void requestCancelPrintJob(PrintJobInfo printJobInfo) {
+ mHandler.obtainMessage(MyHandler.MESSAGE_CANCEL_PRINTJOB,
+ printJobInfo).sendToTarget();
}
@Override
- public void onPrintJobQueued(PrintJobInfo printJob) {
+ public void onPrintJobQueued(PrintJobInfo printJobInfo) {
mHandler.obtainMessage(MyHandler.MESSAGE_ON_PRINTJOB_QUEUED,
- printJob).sendToTarget();
+ printJobInfo).sendToTarget();
}
};
}
+ private void throwIfNotConnectedLocked() {
+ if (mClient == null) {
+ throw new IllegalStateException("Print serivice not connected");
+ }
+ }
+
private final class MyHandler extends Handler {
public static final int MESSAGE_START_PRINTER_DISCOVERY = 1;
public static final int MESSAGE_STOP_PRINTER_DISCOVERY = 2;
@@ -414,26 +433,26 @@ public abstract class PrintService extends Service {
switch (action) {
case MESSAGE_START_PRINTER_DISCOVERY: {
synchronized (mLock) {
- mDiscoveringPrinters = true;
+ mDiscoveryObserver = (IPrinterDiscoveryObserver) message.obj;
}
onStartPrinterDiscovery();
} break;
case MESSAGE_STOP_PRINTER_DISCOVERY: {
synchronized (mLock) {
- mDiscoveringPrinters = false;
+ mDiscoveryObserver = null;
}
onStopPrinterDiscovery();
} break;
case MESSAGE_CANCEL_PRINTJOB: {
- PrintJobInfo printJob = (PrintJobInfo) message.obj;
- onRequestCancelPrintJob(new PrintJob(printJob, mClient));
+ PrintJobInfo printJobInfo = (PrintJobInfo) message.obj;
+ onRequestCancelPrintJob(new PrintJob(printJobInfo, mClient));
} break;
case MESSAGE_ON_PRINTJOB_QUEUED: {
- PrintJobInfo printJob = (PrintJobInfo) message.obj;
- onPrintJobQueued(new PrintJob(printJob, mClient));
+ PrintJobInfo printJobInfo = (PrintJobInfo) message.obj;
+ onPrintJobQueued(new PrintJob(printJobInfo, mClient));
} break;
case MESSAGE_SET_CLEINT: {
@@ -441,13 +460,12 @@ public abstract class PrintService extends Service {
synchronized (mLock) {
mClient = client;
if (client == null) {
- mDiscoveringPrinters = false;
+ mDiscoveryObserver = null;
}
}
if (client != null) {
onConnected();
} else {
- onStopPrinterDiscovery();
onDisconnected();
}
} break;
diff --git a/core/java/android/printservice/PrintServiceInfo.java b/core/java/android/printservice/PrintServiceInfo.java
index 0370a252531b..43dd1b67de66 100644
--- a/core/java/android/printservice/PrintServiceInfo.java
+++ b/core/java/android/printservice/PrintServiceInfo.java
@@ -48,8 +48,6 @@ import java.io.IOException;
*/
public final class PrintServiceInfo implements Parcelable {
- private static final boolean DEBUG = false;
-
private static final String LOG_TAG = PrintServiceInfo.class.getSimpleName();
private static final String TAG_PRINT_SERVICE = "print-service";
@@ -97,7 +95,6 @@ public final class PrintServiceInfo implements Parcelable {
* @param context Context for accessing resources.
* @throws XmlPullParserException If a XML parsing error occurs.
* @throws IOException If a I/O error occurs.
- * @hide
*/
public static PrintServiceInfo create(ResolveInfo resolveInfo, Context context) {
String settingsActivityName = null;
@@ -117,7 +114,7 @@ public final class PrintServiceInfo implements Parcelable {
String nodeName = parser.getName();
if (!TAG_PRINT_SERVICE.equals(nodeName)) {
throw new XmlPullParserException(
- "Meta-data does not start with" + TAG_PRINT_SERVICE + " tag");
+ "Meta-data does not start with " + TAG_PRINT_SERVICE + " tag");
}
Resources resources = packageManager.getResourcesForApplication(
@@ -213,7 +210,7 @@ public final class PrintServiceInfo implements Parcelable {
@Override
public int hashCode() {
- return 31 * 1 + ((mId == null) ? 0 : mId.hashCode());
+ return 31 + ((mId == null) ? 0 : mId.hashCode());
}
@Override
@@ -244,12 +241,8 @@ public final class PrintServiceInfo implements Parcelable {
builder.append("PrintServiceInfo{");
builder.append("id:").append(mId).append(", ");
builder.append("resolveInfo:").append(mResolveInfo).append(", ");
- if (DEBUG) {
- builder.append("settingsActivityName:").append(mSettingsActivityName);
- builder.append("addPrintersActivityName:").append(mAddPrintersActivityName);
- } else if (mSettingsActivityName != null || mAddPrintersActivityName != null) {
- builder.append("<has meta-data>");
- }
+ builder.append("settingsActivityName:").append(mSettingsActivityName);
+ builder.append("addPrintersActivityName:").append(mAddPrintersActivityName);
builder.append("}");
return builder.toString();
}
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 37c5d48ee28c..900f28a0ddec 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4169,86 +4169,86 @@
<!-- Printing -->
- <!-- ISO A0 media size: 33.11" × 46.81" -->
+ <!-- ISO (European standard) A0 media (paper) size: 33.11" × 46.81" -->
<string name="mediaSize_iso_a0">ISO A0</string>
- <!-- ISO A1 media size: 23.39" × 33.11" -->
+ <!-- ISO (European standard) A1 media (paper) size: 23.39" × 33.11" -->
<string name="mediaSize_iso_a1">ISO A1</string>
- <!-- ISO A2 media size: 16.54" x 23.39" -->
+ <!-- ISO (European standard) A2 media (paper) size: 16.54" x 23.39" -->
<string name="mediaSize_iso_a2">ISO A2</string>
- <!-- ISO A3 media size: 11.69" x 16.54" -->
+ <!-- ISO (European standard) A3 media (paper) size: 11.69" x 16.54" -->
<string name="mediaSize_iso_a3">ISO A3</string>
- <!-- ISO A4 media size: 8.27" x 11.69" -->
+ <!-- ISO (European standard) A4 media (paper) size: 8.27" x 11.69" -->
<string name="mediaSize_iso_a4">ISO A4</string>
- <!-- ISO A5 media size: 5.83" x 8.27" -->
+ <!-- ISO (European standard) A5 media (paper) size: 5.83" x 8.27" -->
<string name="mediaSize_iso_a5">ISO A5</string>
- <!-- ISO A6 media size: 4.13" x 5.83" -->
+ <!-- ISO (European standard) A6 media (paper) size: 4.13" x 5.83" -->
<string name="mediaSize_iso_a6">ISO A6</string>
- <!-- ISO A7 media size: 2.91" x 4.13" -->
+ <!-- ISO (European standard) A7 media (paper) size: 2.91" x 4.13" -->
<string name="mediaSize_iso_a7">ISO A7</string>
- <!-- ISO A8 media size: 2.05" x 2.91" -->
+ <!-- ISO (European standard) A8 media (paper) size: 2.05" x 2.91" -->
<string name="mediaSize_iso_a8">ISO A8</string>
- <!-- ISO A9 media size: 1.46" x 2.05" -->
+ <!-- ISO (European standard) A9 media (paper) size: 1.46" x 2.05" -->
<string name="mediaSize_iso_a9">ISO A9</string>
- <!-- ISO A10 media size: 1.02" x 1.46" -->
+ <!-- ISO (European standard) A10 media (paper) size: 1.02" x 1.46" -->
<string name="mediaSize_iso_a10">ISO A10</string>
- <!-- ISO B0 media size: 39.37" x 55.67" -->
+ <!-- ISO (European standard) B0 media (paper) size: 39.37" x 55.67" -->
<string name="mediaSize_iso_b0">ISO B0</string>
- <!-- ISO B1 media size: 27.83" x 39.37" -->
+ <!-- ISO (European standard) B1 media (paper) size: 27.83" x 39.37" -->
<string name="mediaSize_iso_b1">ISO B1</string>
- <!-- ISO B2 media size - 19.69" x 27.83" -->
+ <!-- ISO (European standard) B2 media (paper) size - 19.69" x 27.83" -->
<string name="mediaSize_iso_b2">ISO B2</string>
- <!-- ISO B3 media size: 13.90" x 19.69" -->
+ <!-- ISO (European standard) B3 media (paper) size: 13.90" x 19.69" -->
<string name="mediaSize_iso_b3">ISO B3</string>
- <!-- ISO B4 media size: 9.84" x 13.90" -->
+ <!-- ISO (European standard) B4 media (paper) size: 9.84" x 13.90" -->
<string name="mediaSize_iso_b4">ISO B4</string>
- <!-- ISO B5 media size: 6.93" x 9.84" -->
+ <!-- ISO (European standard) B5 media (paper) size: 6.93" x 9.84" -->
<string name="mediaSize_iso_b5">ISO B5</string>
- <!-- ISO B6 media size: 4.92" x 6.93" -->
+ <!-- ISO (European standard) B6 media (paper) size: 4.92" x 6.93" -->
<string name="mediaSize_iso_b6">ISO B6</string>
- <!-- ISO B7 media size: 3.46" x 4.92" -->
+ <!-- ISO (European standard) B7 media (paper) size: 3.46" x 4.92" -->
<string name="mediaSize_iso_b7">ISO B7</string>
- <!-- ISO B8 media size: 2.44" x 3.46" -->
+ <!-- ISO (European standard) B8 media (paper) size: 2.44" x 3.46" -->
<string name="mediaSize_iso_b8">ISO B8</string>
- <!-- ISO B9 media size: 1.73" x 2.44" -->
+ <!-- ISO (European standard) B9 media (paper) size: 1.73" x 2.44" -->
<string name="mediaSize_iso_b9">ISO B9</string>
- <!-- ISO B10 media size: 1.22" x 1.73" -->
+ <!-- ISO (European standard) B10 media (paper) size: 1.22" x 1.73" -->
<string name="mediaSize_iso_b10">ISO B10</string>
- <!-- ISO C0 media size: 36.10" x 51.06" -->
+ <!-- ISO (European standard) C0 media (paper) size: 36.10" x 51.06" -->
<string name="mediaSize_iso_c0">ISO C0</string>
- <!-- ISO C1 media size: 25.51" x 36.10" -->
+ <!-- ISO (European standard) C1 media (paper) size: 25.51" x 36.10" -->
<string name="mediaSize_iso_c1">ISO C1</string>
- <!-- ISO C2 media size: 18.03" x 25.51" -->
+ <!-- ISO (European standard) C2 media (paper) size: 18.03" x 25.51" -->
<string name="mediaSize_iso_c2">ISO C2</string>
- <!-- ISO C3 media size: 12.76" x 18.03" -->
+ <!-- ISO (European standard) C3 media (paper) size: 12.76" x 18.03" -->
<string name="mediaSize_iso_c3">ISO C3</string>
- <!-- ISO C4 media size: 9.02" x 12.76" -->
+ <!-- ISO (European standard) C4 media (paper) size: 9.02" x 12.76" -->
<string name="mediaSize_iso_c4">ISO C4</string>
- <!-- ISO C5 media size: 6.38" x 9.02" -->
+ <!-- ISO (European standard) C5 media (paper) size: 6.38" x 9.02" -->
<string name="mediaSize_iso_c5">ISO C5</string>
- <!-- ISO C6 media size: 4.49" x 6.38" -->
+ <!-- ISO (European standard) C6 media (paper) size: 4.49" x 6.38" -->
<string name="mediaSize_iso_c6">ISO C6</string>
- <!-- ISO C7 media size: 3.19" x 4.49" -->
+ <!-- ISO (European standard) C7 media (paper) size: 3.19" x 4.49" -->
<string name="mediaSize_iso_c7">ISO C7</string>
- <!-- ISO ISO C8 media size: 2.24" x 3.19" -->
+ <!-- ISO ISO C8 media (paper) size: 2.24" x 3.19" -->
<string name="mediaSize_iso_c8">ISO C8</string>
- <!-- ISO ISO C9 media size: 1.57" x 2.24" -->
+ <!-- ISO ISO C9 media (paper) size: 1.57" x 2.24" -->
<string name="mediaSize_iso_c9">ISO C9</string>
- <!-- ISO C10 media size: 1.10" x 1.57" -->
+ <!-- ISO (European standard) C10 media (paper) size: 1.10" x 1.57" -->
<string name="mediaSize_iso_c10">ISO C10</string>
- <!-- North America Letter media size: 8.5" × 11" -->
+ <!-- North America Letter media (paper) size: 8.5" × 11" -->
<string name="mediaSize_na_letter">Letter</string>
- <!-- North America Government Letter media size: 8.0" × 10.5" -->
+ <!-- North America Government Letter media (paper) size: 8.0" × 10.5" -->
<string name="mediaSize_na_gvrnmt_letter">Government Letter</string>
- <!-- North America Legal media size: 8.5" × 14" -->
+ <!-- North America Legal media (paper) size: 8.5" × 14" -->
<string name="mediaSize_na_legal">Legal</string>
- <!-- North America Junior Legal media size: 8.0" × 5.0" -->
+ <!-- North America Junior Legal media (paper) size: 8.0" × 5.0" -->
<string name="mediaSize_na_junior_legal">Junior Legal</string>
- <!-- North America Ledger media size: 17" × 11" -->
+ <!-- North America Ledger media (paper) size: 17" × 11" -->
<string name="mediaSize_na_ledger">Ledger</string>
- <!-- North America Tabloid media size: 11" × 17" -->
+ <!-- North America Tabloid media (paper) size: 11" × 17" -->
<string name="mediaSize_na_tabloid">Tabloid</string>
<!-- PIN creation dialog message [CHAR LIMIT=none] -->
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
index ae2fe5c43c52..7d07c055dbcb 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
@@ -24,20 +24,21 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
-import android.os.IBinder.DeathRecipient;
-import android.print.IPrintAdapter;
-import android.print.IPrintManager;
+import android.print.IPrintDocumentAdapter;
import android.print.IPrinterDiscoveryObserver;
import android.print.PageRange;
import android.print.PrintAttributes;
import android.print.PrintAttributes.MediaSize;
import android.print.PrintAttributes.Resolution;
import android.print.PrintAttributes.Tray;
+import android.print.PrintDocumentAdapter.LayoutResultCallback;
+import android.print.PrintDocumentAdapter.WriteResultCallback;
+import android.print.PrintDocumentInfo;
import android.print.PrintJobInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
@@ -47,6 +48,7 @@ import android.text.Spanned;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
+import android.view.Choreographer;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -58,7 +60,6 @@ import android.widget.EditText;
import android.widget.Spinner;
import java.io.File;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -78,9 +79,7 @@ public class PrintJobConfigActivity extends Activity {
private static final int MIN_COPIES = 1;
- private final List<QueuedAsyncTask<?>> mTaskQueue = new ArrayList<QueuedAsyncTask<?>>();
-
- private IPrintManager mPrintManager;
+ private final PrintSpooler mPrintSpooler = PrintSpooler.getInstance(this);
private IPrinterDiscoveryObserver mPrinterDiscoveryObserver;
@@ -89,9 +88,7 @@ public class PrintJobConfigActivity extends Activity {
private PrintAttributes mPrintAttributes;
- private final PrintSpooler mPrintSpooler = PrintSpooler.getInstance(this);
-
- private RemotePrintAdapter mRemotePrintAdapter;
+ private RemotePrintDocumentAdapter mRemotePrintAdapter;
// UI elements
@@ -124,11 +121,11 @@ public class PrintJobConfigActivity extends Activity {
private Spinner mOrientationSpinner;
public ArrayAdapter<SpinnerItem<Integer>> mOrientationSpinnerAdapter;
- private boolean mPrintStarted;
-
private boolean mPrintConfirmed;
- private IBinder mPrinable;
+ private boolean mStarted;
+
+ private IBinder mIPrintDocumentAdapter;
// TODO: Implement store/restore state.
@@ -231,9 +228,6 @@ public class PrintJobConfigActivity extends Activity {
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN
| WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
- mPrintManager = (IPrintManager) IPrintManager.Stub.asInterface(
- ServiceManager.getService(PRINT_SERVICE));
-
Bundle extras = getIntent().getExtras();
mPrintJobId = extras.getInt(EXTRA_PRINT_JOB_ID, -1);
@@ -251,15 +245,16 @@ public class PrintJobConfigActivity extends Activity {
mPrintAttributes = new PrintAttributes.Builder().create();
}
- mPrinable = extras.getBinder(EXTRA_PRINTABLE);
- if (mPrinable == null) {
+ mIPrintDocumentAdapter = extras.getBinder(EXTRA_PRINTABLE);
+ if (mIPrintDocumentAdapter == null) {
throw new IllegalArgumentException("Printable cannot be null");
}
- mRemotePrintAdapter = new RemotePrintAdapter(IPrintAdapter.Stub.asInterface(mPrinable),
+ mRemotePrintAdapter = new RemotePrintDocumentAdapter(
+ IPrintDocumentAdapter.Stub.asInterface(mIPrintDocumentAdapter),
mPrintSpooler.generateFileForPrintJob(mPrintJobId));
try {
- mPrinable.linkToDeath(mDeathRecipient, 0);
+ mIPrintDocumentAdapter.linkToDeath(mDeathRecipient, 0);
} catch (RemoteException re) {
finish();
}
@@ -271,7 +266,7 @@ public class PrintJobConfigActivity extends Activity {
@Override
protected void onDestroy() {
- mPrinable.unlinkToDeath(mDeathRecipient, 0);
+ mIPrintDocumentAdapter.unlinkToDeath(mDeathRecipient, 0);
super.onDestroy();
}
@@ -367,7 +362,6 @@ public class PrintJobConfigActivity extends Activity {
mPrintAttributes.getMediaSize());
mMediaSizeSpinner.setOnItemSelectedListener(null);
mMediaSizeSpinner.setSelection(selectedMediaSizeIndex);
- mMediaSizeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
// Resolution.
mResolutionSpinnerAdapter.clear();
@@ -382,7 +376,19 @@ public class PrintJobConfigActivity extends Activity {
mPrintAttributes.getResolution());
mResolutionSpinner.setOnItemSelectedListener(null);
mResolutionSpinner.setSelection(selectedResolutionIndex);
- mResolutionSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+
+ // AdapterView has the weird behavior to notify the selection listener for a
+ // selection event that occurred *before* the listener was registered because
+ // it does the real selection change on the next layout pass. To avoid this
+ // behavior we re-attach the listener in the next traversal window - fun!
+ Choreographer.getInstance().postCallback(
+ Choreographer.CALLBACK_TRAVERSAL, new Runnable() {
+ @Override
+ public void run() {
+ mMediaSizeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+ mResolutionSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+ }
+ }, null);
// Input tray.
mInputTraySpinnerAdapter.clear();
@@ -482,22 +488,14 @@ public class PrintJobConfigActivity extends Activity {
@Override
protected void onResume() {
super.onResume();
- try {
- mPrintManager.startDiscoverPrinters(mPrinterDiscoveryObserver);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error starting printer discovery!", re);
- }
+ mPrintSpooler.startPrinterDiscovery(mPrinterDiscoveryObserver);
notifyPrintableStartIfNeeded();
}
@Override
protected void onPause() {
super.onPause();
- try {
- mPrintManager.stopDiscoverPrinters();
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error starting printer discovery!", re);
- }
+ mPrintSpooler.stopPrinterDiscovery();
notifyPrintableFinishIfNeeded();
}
@@ -518,119 +516,83 @@ public class PrintJobConfigActivity extends Activity {
private void notifyPrintableStartIfNeeded() {
if (mDestinationSpinner.getSelectedItemPosition() < 0
- || mPrintStarted) {
+ || mStarted) {
return;
}
- mPrintStarted = true;
- new QueuedAsyncTask<Void>(mTaskQueue) {
- @Override
- protected Void doInBackground(Void... params) {
- try {
- mRemotePrintAdapter.start();
- } catch (IOException ioe) {
- Log.e(LOG_TAG, "Error reading printed data!", ioe);
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- super.onPostExecute(result);
- updatePrintableContentIfNeeded();
- }
- }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+ mStarted = true;
+ mRemotePrintAdapter.start();
+ updatePrintableContentIfNeeded();
}
private void updatePrintableContentIfNeeded() {
- if (!mPrintStarted) {
+ if (!mStarted) {
return;
}
+ // TODO: Implement old attributes tracking
mPrintSpooler.setPrintJobAttributes(mPrintJobId, mPrintAttributes);
- // TODO: Implement page selector.
- final List<PageRange> pages = new ArrayList<PageRange>();
- pages.add(PageRange.ALL_PAGES);
-
- new QueuedAsyncTask<File>(mTaskQueue) {
+ mRemotePrintAdapter.layout(new PrintAttributes.Builder().create(),
+ mPrintAttributes, new LayoutResultCallback() {
@Override
- protected File doInBackground(Void... params) {
- try {
- mRemotePrintAdapter.printAttributesChanged(mPrintAttributes);
- mRemotePrintAdapter.cancelPrint();
- mRemotePrintAdapter.print(pages);
- return mRemotePrintAdapter.getFile();
- } catch (IOException ioe) {
- Log.e(LOG_TAG, "Error reading printed data!", ioe);
- }
- return null;
+ public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
+ // TODO: Handle the case of unchanged content
+ mPrintSpooler.setPrintJobPrintDocumentInfo(mPrintJobId, info);
+
+ // TODO: Implement page selector.
+ final List<PageRange> pages = new ArrayList<PageRange>();
+ pages.add(PageRange.ALL_PAGES);
+
+ mRemotePrintAdapter.write(pages, new WriteResultCallback() {
+ @Override
+ public void onWriteFinished(List<PageRange> pages) {
+ updatePrintPreview(mRemotePrintAdapter.getFile());
+ }
+
+ @Override
+ public void onWriteFailed(CharSequence error) {
+ Log.e(LOG_TAG, "Error write layout: " + error);
+ finishActivity(Activity.RESULT_CANCELED);
+ }
+ });
}
@Override
- protected void onPostExecute(File file) {
- super.onPostExecute(file);
- updatePrintPreview(file);
+ public void onLayoutFailed(CharSequence error) {
+ Log.e(LOG_TAG, "Error during layout: " + error);
+ finishActivity(Activity.RESULT_CANCELED);
}
- }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+ });
}
private void notifyPrintableFinishIfNeeded() {
- if (!mPrintStarted) {
+ if (!mStarted) {
return;
}
- mPrintStarted = false;
- // Cancel all pending async tasks if the activity was canceled.
if (!mPrintConfirmed) {
- final int taskCount = mTaskQueue.size();
- for (int i = taskCount - 1; i >= 0; i--) {
- mTaskQueue.remove(i).cancel();
- }
+ mRemotePrintAdapter.cancel();
}
+ mRemotePrintAdapter.finish();
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- // Notify the app that printing completed.
- try {
- mRemotePrintAdapter.finish();
- } catch (IOException ioe) {
- Log.e(LOG_TAG, "Error reading printed data!", ioe);
- }
-
- // If canceled, nothing to do.
- if (!mPrintConfirmed) {
- mPrintSpooler.setPrintJobState(mPrintJobId,
- PrintJobInfo.STATE_CANCELED);
- return null;
- }
-
- // No printer, nothing to do.
- final int selectedIndex = mDestinationSpinner.getSelectedItemPosition();
- if (selectedIndex < 0) {
- // Update the print job's status.
- mPrintSpooler.setPrintJobState(mPrintJobId,
- PrintJobInfo.STATE_CANCELED);
- return null;
- }
-
- // Update the print job's printer.
- SpinnerItem<PrinterInfo> printerItem =
- mDestinationSpinnerAdapter.getItem(selectedIndex);
- PrinterId printerId = printerItem.value.getId();
- mPrintSpooler.setPrintJobPrinterId(mPrintJobId, printerId);
+ // If canceled or no printer, nothing to do.
+ final int selectedIndex = mDestinationSpinner.getSelectedItemPosition();
+ if (!mPrintConfirmed || selectedIndex < 0) {
+ // Update the print job's status.
+ mPrintSpooler.setPrintJobState(mPrintJobId,
+ PrintJobInfo.STATE_CANCELED);
+ return;
+ }
- // Update the print job's status.
- mPrintSpooler.setPrintJobState(mPrintJobId,
- PrintJobInfo.STATE_QUEUED);
- return null;
- }
+ // Update the print job's printer.
+ SpinnerItem<PrinterInfo> printerItem =
+ mDestinationSpinnerAdapter.getItem(selectedIndex);
+ PrinterId printerId = printerItem.value.getId();
+ mPrintSpooler.setPrintJobPrinterId(mPrintJobId, printerId);
- // Important: If we are canceling, then we do not wait for the write
- // to complete since the result will be discarded anyway, we simply
- // execute the finish immediately which will interrupt the write.
- }.executeOnExecutor(mPrintConfirmed ? AsyncTask.SERIAL_EXECUTOR
- : AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
+ // Update the print job's status.
+ mPrintSpooler.setPrintJobState(mPrintJobId,
+ PrintJobInfo.STATE_QUEUED);
if (DEBUG) {
if (mPrintConfirmed) {
@@ -689,30 +651,7 @@ public class PrintJobConfigActivity extends Activity {
}
}
- private abstract class QueuedAsyncTask<T> extends AsyncTask<Void, Void, T> {
-
- private final List<QueuedAsyncTask<?>> mPendingOrRunningTasks;
-
- public QueuedAsyncTask(List<QueuedAsyncTask<?>> pendingOrRunningTasks) {
- mPendingOrRunningTasks = pendingOrRunningTasks;
- }
-
- @Override
- protected void onPreExecute() {
- mPendingOrRunningTasks.add(this);
- }
-
- @Override
- protected void onPostExecute(T result) {
- mPendingOrRunningTasks.remove(this);
- }
-
- public void cancel() {
- super.cancel(true);
- mPendingOrRunningTasks.remove(this);
- }
- }
-
+ // Caution: Use this only for debugging
private final class ViewSpooledFileAsyncTask extends AsyncTask<Void, Void, Void> {
private final File mFile;
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java
index 2b27b694f1ac..0546a4316619 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java
@@ -16,30 +16,18 @@
package com.android.printspooler;
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
import android.content.ComponentName;
import android.content.Context;
import android.os.AsyncTask;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.print.IPrintClient;
-import android.print.IPrintManager;
+import android.print.IPrintSpoolerClient;
+import android.print.IPrinterDiscoveryObserver;
import android.print.PrintAttributes;
import android.print.PrintJobInfo;
import android.print.PrintManager;
+import android.print.PrintDocumentInfo;
import android.print.PrinterId;
import android.util.AtomicFile;
import android.util.Log;
@@ -48,6 +36,22 @@ import android.util.Xml;
import com.android.internal.util.FastXmlSerializer;
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+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 {
private static final String LOG_TAG = PrintSpooler.class.getSimpleName();
@@ -64,17 +68,17 @@ public class PrintSpooler {
private static final Object sLock = new Object();
- private final Object mLock = new Object();
-
private static PrintSpooler sInstance;
+ private final Object mLock = new Object();
+
private final List<PrintJobInfo> mPrintJobs = new ArrayList<PrintJobInfo>();
private final PersistenceManager mPersistanceManager;
private final Context mContext;
- private final IPrintManager mPrintManager;
+ public IPrintSpoolerClient mClient;
public static PrintSpooler getInstance(Context context) {
synchronized (sLock) {
@@ -89,8 +93,40 @@ public class PrintSpooler {
mContext = context;
mPersistanceManager = new PersistenceManager();
mPersistanceManager.readStateLocked();
- mPrintManager = IPrintManager.Stub.asInterface(
- ServiceManager.getService("print"));
+ }
+
+ public void setCleint(IPrintSpoolerClient client) {
+ synchronized (mLock) {
+ mClient = client;
+ }
+ }
+
+ public void startPrinterDiscovery(IPrinterDiscoveryObserver observer) {
+ IPrintSpoolerClient client = null;
+ synchronized (mLock) {
+ client = mClient;
+ }
+ if (client != null) {
+ try {
+ client.onStartPrinterDiscovery(observer);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error notifying start printer discovery.", re);
+ }
+ }
+ }
+
+ public void stopPrinterDiscovery() {
+ IPrintSpoolerClient client = null;
+ synchronized (mLock) {
+ client = mClient;
+ }
+ if (client != null) {
+ try {
+ client.onStopPrinterDiscovery();
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error notifying stop printer discovery.", re);
+ }
+ }
}
public List<PrintJobInfo> getPrintJobs(ComponentName componentName, int state, int appId) {
@@ -102,7 +138,7 @@ public class PrintSpooler {
PrinterId printerId = printJob.getPrinterId();
final boolean sameComponent = (componentName == null
|| (printerId != null
- && componentName.equals(printerId.getServiceComponentName())));
+ && componentName.equals(printerId.getService())));
final boolean sameAppId = appId == PrintManager.APP_ID_ANY
|| printJob.getAppId() == appId;
final boolean sameState = state == PrintJobInfo.STATE_ANY
@@ -137,15 +173,10 @@ public class PrintSpooler {
PrintJobInfo printJob = getPrintJob(printJobId, appId);
if (printJob != null) {
switch (printJob.getState()) {
- case PrintJobInfo.STATE_CREATED: {
- removePrintJobLocked(printJob);
- } return true;
+ case PrintJobInfo.STATE_CREATED:
case PrintJobInfo.STATE_QUEUED: {
- removePrintJobLocked(printJob);
+ setPrintJobState(printJobId, PrintJobInfo.STATE_CANCELED);
} return true;
- default: {
- return false;
- }
}
}
return false;
@@ -169,6 +200,72 @@ public class PrintSpooler {
}
}
+ public void notifyClientForActivteJobs() {
+ IPrintSpoolerClient client = null;
+ Map<ComponentName, List<PrintJobInfo>> activeJobsPerServiceMap =
+ new HashMap<ComponentName, List<PrintJobInfo>>();
+
+ 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().getService();
+ 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().getService();
+ if (!activeJobsPerServiceMap.containsKey(service)) {
+ activeJobsPerServiceMap.put(service, null);
+ }
+ }
+ }
+ }
+ }
+
+ boolean allPrintJobsHandled = true;
+
+ for (Map.Entry<ComponentName, List<PrintJobInfo>> entry
+ : activeJobsPerServiceMap.entrySet()) {
+ ComponentName service = entry.getKey();
+ List<PrintJobInfo> printJobs = entry.getValue();
+
+ 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) {
+ callOnPrintJobQueuedQuietly(client, printJob);
+ }
+ }
+ } else {
+ callOnAllPrintJobsForServiceHandledQuietly(client, service);
+ }
+ }
+
+ if (allPrintJobsHandled) {
+ callOnAllPrintJobsHandledQuietly(client);
+ }
+ }
+
private int generatePrintJobIdLocked() {
int printJobId = sPrintJobIdCounter++;
while (isDuplicatePrintJobId(printJobId)) {
@@ -213,24 +310,14 @@ public class PrintSpooler {
} catch (IOException ioe) {
Log.e(LOG_TAG, "Error writing print job data!", ioe);
} finally {
- closeIfNotNullNoException(in);
- closeIfNotNullNoException(out);
- closeIfNotNullNoException(fd);
+ IoUtils.closeQuietly(in);
+ IoUtils.closeQuietly(out);
+ IoUtils.closeQuietly(fd);
}
}
return false;
}
- private void closeIfNotNullNoException(Closeable closeable) {
- if (closeable != null) {
- try {
- closeable.close();
- } catch (IOException ioe) {
- /* ignore */;
- }
- }
- }
-
public File generateFileForPrintJob(int printJobId) {
return new File(mContext.getFilesDir(), "print_job_"
+ printJobId + "." + PRINT_FILE_EXTENSION);
@@ -254,9 +341,20 @@ public class PrintSpooler {
public boolean setPrintJobState(int printJobId, int state) {
boolean success = false;
+
+ boolean allPrintJobsHandled = false;
+ boolean allPrintJobsForServiceHandled = false;
+
+ IPrintSpoolerClient client = null;
PrintJobInfo queuedPrintJob = null;
+ PrintJobInfo removedPrintJob = null;
synchronized (mLock) {
+ if (mClient == null) {
+ throw new IllegalStateException("Client cannot be null.");
+ }
+ client = mClient;
+
PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
if (printJob != null && printJob.getState() < state) {
success = true;
@@ -265,8 +363,23 @@ public class PrintSpooler {
switch (state) {
case PrintJobInfo.STATE_COMPLETED:
case PrintJobInfo.STATE_CANCELED: {
+ removedPrintJob = printJob;
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.
+ PrinterId printerId = printJob.getPrinterId();
+ if (printerId == null) {
+ return true;
+ }
+
+ allPrintJobsHandled = !hasActivePrintJobsLocked();
+ allPrintJobsForServiceHandled = !hasActivePrintJobsForServiceLocked(
+ printerId.getService());
} break;
+
case PrintJobInfo.STATE_QUEUED: {
queuedPrintJob = new PrintJobInfo(printJob);
} break;
@@ -279,17 +392,77 @@ public class PrintSpooler {
}
if (queuedPrintJob != null) {
- try {
- mPrintManager.onPrintJobQueued(queuedPrintJob.getPrinterId(),
- queuedPrintJob);
- } catch (RemoteException re) {
- /* ignore */
- }
+ callOnPrintJobQueuedQuietly(client, queuedPrintJob);
+ }
+
+ if (allPrintJobsForServiceHandled) {
+ callOnAllPrintJobsForServiceHandledQuietly(client,
+ removedPrintJob.getPrinterId().getService());
+ }
+
+ if (allPrintJobsHandled) {
+ callOnAllPrintJobsHandledQuietly(client);
}
return success;
}
+ private void callOnPrintJobQueuedQuietly(IPrintSpoolerClient client,
+ PrintJobInfo printJob) {
+ try {
+ client.onPrintJobQueued(printJob);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error notify for a queued print job.", re);
+ }
+ }
+
+ private void callOnAllPrintJobsForServiceHandledQuietly(IPrintSpoolerClient client,
+ ComponentName service) {
+ try {
+ client.onAllPrintJobsForServiceHandled(service);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error notify for all print jobs per service handled.", re);
+ }
+ }
+
+ private void callOnAllPrintJobsHandledQuietly(IPrintSpoolerClient client) {
+ try {
+ client.onAllPrintJobsHandled();
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error notify for all print job handled.", re);
+ }
+ }
+
+ private boolean hasActivePrintJobsLocked() {
+ final int printJobCount = mPrintJobs.size();
+ for (int i = 0; i < printJobCount; i++) {
+ PrintJobInfo printJob = mPrintJobs.get(i);
+ switch (printJob.getState()) {
+ case PrintJobInfo.STATE_QUEUED:
+ case PrintJobInfo.STATE_STARTED: {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean hasActivePrintJobsForServiceLocked(ComponentName service) {
+ final int printJobCount = mPrintJobs.size();
+ for (int i = 0; i < printJobCount; i++) {
+ PrintJobInfo printJob = mPrintJobs.get(i);
+ switch (printJob.getState()) {
+ case PrintJobInfo.STATE_QUEUED:
+ case PrintJobInfo.STATE_STARTED: {
+ if (printJob.getPrinterId().getService().equals(service)) {
+ return true;
+ }
+ } break;
+ }
+ }
+ return false;
+ }
+
public boolean setPrintJobTag(int printJobId, String tag) {
synchronized (mLock) {
PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
@@ -302,6 +475,18 @@ public class PrintSpooler {
return false;
}
+ public final boolean setPrintJobPrintDocumentInfo(int printJobId, PrintDocumentInfo info) {
+ synchronized (mLock) {
+ PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
+ if (printJob != null) {
+ printJob.setDocumentInfo(info);
+ mPersistanceManager.writeStateLocked();
+ return true;
+ }
+ }
+ return false;
+ }
+
public void setPrintJobAttributes(int printJobId, PrintAttributes attributes) {
synchronized (mLock) {
PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
index 57c45577f8b9..050332cc0dc0 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
@@ -29,10 +29,11 @@ import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
-import android.print.IPrintAdapter;
+import android.print.IPrintDocumentAdapter;
import android.print.IPrintClient;
-import android.print.IPrintSpoolerService;
-import android.print.IPrintSpoolerServiceCallbacks;
+import android.print.IPrintSpoolerClient;
+import android.print.IPrintSpooler;
+import android.print.IPrintSpoolerCallbacks;
import android.print.PrintAttributes;
import android.print.PrintJobInfo;
import android.util.Slog;
@@ -64,21 +65,21 @@ public final class PrintSpoolerService extends Service {
@Override
public IBinder onBind(Intent intent) {
- return new IPrintSpoolerService.Stub() {
+ return new IPrintSpooler.Stub() {
@Override
- public void getPrintJobs(IPrintSpoolerServiceCallbacks callback,
+ public void getPrintJobInfos(IPrintSpoolerCallbacks callback,
ComponentName componentName, int state, int appId, int sequence)
throws RemoteException {
List<PrintJobInfo> printJobs = null;
try {
printJobs = mSpooler.getPrintJobs(componentName, state, appId);
} finally {
- callback.onGetPrintJobsResult(printJobs, sequence);
+ callback.onGetPrintJobInfosResult(printJobs, sequence);
}
}
@Override
- public void getPrintJob(int printJobId, IPrintSpoolerServiceCallbacks callback,
+ public void getPrintJobInfo(int printJobId, IPrintSpoolerCallbacks callback,
int appId, int sequence) throws RemoteException {
PrintJobInfo printJob = null;
try {
@@ -89,7 +90,7 @@ public final class PrintSpoolerService extends Service {
}
@Override
- public void cancelPrintJob(int printJobId, IPrintSpoolerServiceCallbacks callback,
+ public void cancelPrintJob(int printJobId, IPrintSpoolerCallbacks callback,
int appId, int sequence) throws RemoteException {
boolean success = false;
try {
@@ -102,8 +103,8 @@ public final class PrintSpoolerService extends Service {
@SuppressWarnings("deprecation")
@Override
public void createPrintJob(String printJobName, IPrintClient client,
- IPrintAdapter printAdapter, PrintAttributes attributes,
- IPrintSpoolerServiceCallbacks callback, int appId, int sequence)
+ IPrintDocumentAdapter printAdapter, PrintAttributes attributes,
+ IPrintSpoolerCallbacks callback, int appId, int sequence)
throws RemoteException {
PrintJobInfo printJob = null;
try {
@@ -134,7 +135,7 @@ public final class PrintSpoolerService extends Service {
@Override
public void setPrintJobState(int printJobId, int state,
- IPrintSpoolerServiceCallbacks callback, int sequece)
+ IPrintSpoolerCallbacks callback, int sequece)
throws RemoteException {
boolean success = false;
try {
@@ -148,7 +149,7 @@ public final class PrintSpoolerService extends Service {
@Override
public void setPrintJobTag(int printJobId, String tag,
- IPrintSpoolerServiceCallbacks callback, int sequece)
+ IPrintSpoolerCallbacks callback, int sequece)
throws RemoteException {
boolean success = false;
try {
@@ -162,6 +163,16 @@ public final class PrintSpoolerService extends Service {
public void writePrintJobData(ParcelFileDescriptor fd, int printJobId) {
mSpooler.writePrintJobData(fd, printJobId);
}
+
+ @Override
+ public void setClient(IPrintSpoolerClient client) {
+ mSpooler.setCleint(client);
+ }
+
+ @Override
+ public void notifyClientForActivteJobs() {
+ mSpooler.notifyClientForActivteJobs();
+ }
};
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/RemotePrintAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/RemotePrintAdapter.java
deleted file mode 100644
index c81b00c6b167..000000000000
--- a/packages/PrintSpooler/src/com/android/printspooler/RemotePrintAdapter.java
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * 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.printspooler;
-
-import android.os.ICancellationSignal;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.print.IPrintAdapter;
-import android.print.IPrintResultCallback;
-import android.print.PageRange;
-import android.print.PrintAdapterInfo;
-import android.print.PrintAttributes;
-import android.util.Log;
-
-import libcore.io.IoUtils;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.List;
-
-/**
- * This class represents a remote print adapter instance.
- */
-final class RemotePrintAdapter {
- private static final String LOG_TAG = "RemotePrintAdapter";
-
- private static final boolean DEBUG = true;
-
- private final Object mLock = new Object();
-
- private final IPrintAdapter mRemoteInterface;
-
- private final File mFile;
-
- private final IPrintResultCallback mIPrintProgressListener;
-
- private PrintAdapterInfo mInfo;
-
- private ICancellationSignal mCancellationSignal;
-
- private Thread mWriteThread;
-
- public RemotePrintAdapter(IPrintAdapter printAdatper, File file) {
- mRemoteInterface = printAdatper;
- mFile = file;
- mIPrintProgressListener = new IPrintResultCallback.Stub() {
- @Override
- public void onPrintStarted(PrintAdapterInfo info,
- ICancellationSignal cancellationSignal) {
- if (DEBUG) {
- Log.i(LOG_TAG, "IPrintProgressListener#onPrintStarted()");
- }
- synchronized (mLock) {
- mInfo = info;
- mCancellationSignal = cancellationSignal;
- }
- }
-
- @Override
- public void onPrintFinished(List<PageRange> pages) {
- if (DEBUG) {
- Log.i(LOG_TAG, "IPrintProgressListener#onPrintFinished(" + pages + ")");
- }
- synchronized (mLock) {
- if (isPrintingLocked()) {
- mWriteThread.interrupt();
- mCancellationSignal = null;
- }
- }
- }
-
- @Override
- public void onPrintFailed(CharSequence error) {
- if (DEBUG) {
- Log.i(LOG_TAG, "IPrintProgressListener#onPrintFailed(" + error + ")");
- }
- synchronized (mLock) {
- if (isPrintingLocked()) {
- mWriteThread.interrupt();
- mCancellationSignal = null;
- }
- }
- }
- };
- }
-
- public File getFile() {
- if (DEBUG) {
- Log.i(LOG_TAG, "getFile()");
- }
- return mFile;
- }
-
- public void start() throws IOException {
- if (DEBUG) {
- Log.i(LOG_TAG, "start()");
- }
- try {
- mRemoteInterface.start();
- } catch (RemoteException re) {
- throw new IOException("Error reading file", re);
- }
- }
-
- public void printAttributesChanged(PrintAttributes attributes) throws IOException {
- if (DEBUG) {
- Log.i(LOG_TAG, "printAttributesChanged(" + attributes +")");
- }
- try {
- mRemoteInterface.printAttributesChanged(attributes);
- } catch (RemoteException re) {
- throw new IOException("Error reading file", re);
- }
- }
-
- public void print(List<PageRange> pages) throws IOException {
- if (DEBUG) {
- Log.i(LOG_TAG, "print(" + pages +")");
- }
- InputStream in = null;
- OutputStream out = null;
- ParcelFileDescriptor source = null;
- ParcelFileDescriptor sink = null;
- synchronized (mLock) {
- mWriteThread = Thread.currentThread();
- }
- try {
- ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
- source = pipe[0];
- sink = pipe[1];
-
- in = new FileInputStream(source.getFileDescriptor());
- out = new FileOutputStream(mFile);
-
- // Async call to initiate the other process writing the data.
- mRemoteInterface.print(pages, sink, mIPrintProgressListener);
-
- // Close the source. It is now held by the client.
- sink.close();
- sink = null;
-
- final byte[] buffer = new byte[8192];
- while (true) {
- if (Thread.currentThread().isInterrupted()) {
- Thread.currentThread().interrupt();
- break;
- }
- final int readByteCount = in.read(buffer);
- if (readByteCount < 0) {
- break;
- }
- out.write(buffer, 0, readByteCount);
- }
- } catch (RemoteException re) {
- throw new IOException("Error reading file", re);
- } catch (IOException ioe) {
- throw new IOException("Error reading file", ioe);
- } finally {
- IoUtils.closeQuietly(in);
- IoUtils.closeQuietly(out);
- IoUtils.closeQuietly(sink);
- IoUtils.closeQuietly(source);
- }
- }
-
- public void cancelPrint() throws IOException {
- if (DEBUG) {
- Log.i(LOG_TAG, "cancelPrint()");
- }
- synchronized (mLock) {
- if (isPrintingLocked()) {
- try {
- mCancellationSignal.cancel();
- } catch (RemoteException re) {
- throw new IOException("Error cancelling print", re);
- }
- }
- }
- }
-
- public void finish() throws IOException {
- if (DEBUG) {
- Log.i(LOG_TAG, "finish()");
- }
- try {
- mRemoteInterface.finish();
- } catch (RemoteException re) {
- throw new IOException("Error reading file", re);
- }
- }
-
- public PrintAdapterInfo getInfo() {
- synchronized (mLock) {
- return mInfo;
- }
- }
-
- private boolean isPrintingLocked() {
- return mCancellationSignal != null;
- }
-}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java
new file mode 100644
index 000000000000..912dd95b1663
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java
@@ -0,0 +1,448 @@
+/*
+ * 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.printspooler;
+
+import android.os.AsyncTask;
+import android.os.ICancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.print.ILayoutResultCallback;
+import android.print.IPrintDocumentAdapter;
+import android.print.IWriteResultCallback;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintDocumentAdapter.LayoutResultCallback;
+import android.print.PrintDocumentAdapter.WriteResultCallback;
+import android.print.PrintDocumentInfo;
+import android.util.Log;
+import android.util.Slog;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class represents a remote print document adapter instance.
+ */
+final class RemotePrintDocumentAdapter {
+ private static final String LOG_TAG = "RemotePrintDocumentAdapter";
+
+ private static final boolean DEBUG = true;
+
+ public static final int STATE_INITIALIZED = 0;
+ public static final int STATE_START_COMPLETED = 1;
+ public static final int STATE_LAYOUT_COMPLETED = 2;
+ public static final int STATE_WRITE_COMPLETED = 3;
+ public static final int STATE_FINISH_COMPLETED = 4;
+
+ private final Object mLock = new Object();
+
+ private final List<QueuedAsyncTask> mTaskQueue = new ArrayList<QueuedAsyncTask>();
+
+ private final IPrintDocumentAdapter mRemoteInterface;
+
+ private final File mFile;
+
+ private int mState = STATE_INITIALIZED;
+
+ public RemotePrintDocumentAdapter(IPrintDocumentAdapter printAdatper, File file) {
+ mRemoteInterface = printAdatper;
+ mFile = file;
+ }
+
+ public File getFile() {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "getFile()");
+ }
+ synchronized (mLock) {
+ if (mState < STATE_WRITE_COMPLETED) {
+ throw new IllegalStateException("Write not completed");
+ }
+ return mFile;
+ }
+ }
+
+ public void cancel() {
+ synchronized (mLock) {
+ final int taskCount = mTaskQueue.size();
+ for (int i = 0; i < taskCount; i++) {
+ mTaskQueue.remove(i).cancel();
+ }
+ }
+ }
+
+ public void start() {
+ QueuedAsyncTask task = new QueuedAsyncTask() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "start()");
+ }
+ synchronized (mLock) {
+ if (mState != STATE_INITIALIZED) {
+ throw new IllegalStateException("Invalid state: " + mState);
+ }
+ }
+ try {
+ mRemoteInterface.start();
+ synchronized (mLock) {
+ mState = STATE_START_COMPLETED;
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error reading file", re);
+ }
+ return null;
+ }
+ };
+ synchronized (mLock) {
+ mTaskQueue.add(task);
+ task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+ }
+ }
+
+ public void layout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
+ LayoutResultCallback callback) {
+ LayoutAsyncTask task = new LayoutAsyncTask(oldAttributes, newAttributes, callback);
+ synchronized (mLock) {
+ mTaskQueue.add(task);
+ task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+ }
+ }
+
+ public void write(List<PageRange> pages, WriteResultCallback callback) {
+ WriteAsyncTask task = new WriteAsyncTask(pages, callback);
+ mTaskQueue.add(task);
+ task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+ }
+
+ public void finish() {
+ QueuedAsyncTask task = new QueuedAsyncTask() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "finish");
+ }
+ synchronized (mLock) {
+ if (mState != STATE_LAYOUT_COMPLETED
+ && mState != STATE_WRITE_COMPLETED) {
+ throw new IllegalStateException("Invalid state: " + mState);
+ }
+ }
+ try {
+ mRemoteInterface.finish();
+ synchronized (mLock) {
+ mState = STATE_FINISH_COMPLETED;
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error reading file", re);
+ mState = STATE_INITIALIZED;
+ }
+ return null;
+ }
+ };
+ synchronized (mLock) {
+ mTaskQueue.add(task);
+ task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+ }
+ }
+
+ private abstract class QueuedAsyncTask extends AsyncTask<Void, Void, Void> {
+ public void cancel() {
+ super.cancel(true);
+ }
+ }
+
+ private final class LayoutAsyncTask extends QueuedAsyncTask {
+
+ private final PrintAttributes mOldAttributes;
+
+ private final PrintAttributes mNewAttributes;
+
+ private final LayoutResultCallback mCallback;
+
+ private final ILayoutResultCallback mILayoutResultCallback =
+ new ILayoutResultCallback.Stub() {
+ @Override
+ public void onLayoutStarted(ICancellationSignal cancellationSignal) {
+ synchronized (mLock) {
+ mCancellationSignal = cancellationSignal;
+ if (isCancelled()) {
+ cancelSignalQuietlyLocked();
+ }
+ }
+ }
+
+ @Override
+ public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
+ synchronized (mLock) {
+ mCancellationSignal = null;
+ mCompleted = true;
+ mLock.notifyAll();
+ }
+ mCallback.onLayoutFinished(info, changed);
+ }
+
+ @Override
+ public void onLayoutFailed(CharSequence error) {
+ synchronized (mLock) {
+ mCancellationSignal = null;
+ mCompleted = true;
+ mLock.notifyAll();
+ }
+ Slog.e(LOG_TAG, "Error laying out print document: " + error);
+ mCallback.onLayoutFailed(error);
+ }
+ };
+
+ private ICancellationSignal mCancellationSignal;
+
+ private boolean mCompleted;
+
+ public LayoutAsyncTask(PrintAttributes oldAttributes,
+ PrintAttributes newAttributes, LayoutResultCallback callback) {
+ mOldAttributes = oldAttributes;
+ mNewAttributes = newAttributes;
+ mCallback = callback;
+ }
+
+ @Override
+ public void cancel() {
+ synchronized (mLock) {
+ throwIfCancelledLocked();
+ cancelSignalQuietlyLocked();
+ }
+ super.cancel();
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ synchronized (mLock) {
+ if (mState != STATE_START_COMPLETED
+ && mState != STATE_LAYOUT_COMPLETED
+ && mState != STATE_WRITE_COMPLETED) {
+ throw new IllegalStateException("Invalid state: " + mState);
+ }
+ }
+ try {
+ mRemoteInterface.layout(mOldAttributes, mNewAttributes,
+ mILayoutResultCallback);
+ synchronized (mLock) {
+ while (true) {
+ if (isCancelled()) {
+ mState = STATE_INITIALIZED;
+ mTaskQueue.remove(this);
+ break;
+ }
+ if (mCompleted) {
+ mState = STATE_LAYOUT_COMPLETED;
+ mTaskQueue.remove(this);
+ break;
+ }
+ try {
+ mLock.wait();
+ } catch (InterruptedException ie) {
+ /* ignore */
+ }
+ }
+ }
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error calling layout", re);
+ mState = STATE_INITIALIZED;
+ }
+ return null;
+ }
+
+ private void cancelSignalQuietlyLocked() {
+ if (mCancellationSignal != null) {
+ try {
+ mCancellationSignal.cancel();
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error cancelling layout", re);
+ }
+ }
+ }
+
+ private void throwIfCancelledLocked() {
+ if (isCancelled()) {
+ throw new IllegalStateException("Already cancelled");
+ }
+ }
+ }
+
+ private final class WriteAsyncTask extends QueuedAsyncTask {
+
+ private final List<PageRange> mPages;
+
+ private final WriteResultCallback mCallback;
+
+ private final IWriteResultCallback mIWriteResultCallback =
+ new IWriteResultCallback.Stub() {
+ @Override
+ public void onWriteStarted(ICancellationSignal cancellationSignal) {
+ synchronized (mLock) {
+ mCancellationSignal = cancellationSignal;
+ if (isCancelled()) {
+ cancelSignalQuietlyLocked();
+ }
+ }
+ }
+
+ @Override
+ public void onWriteFinished(List<PageRange> pages) {
+ synchronized (mLock) {
+ mCancellationSignal = null;
+ mCompleted = true;
+ mLock.notifyAll();
+ }
+ mCallback.onWriteFinished(pages);
+ }
+
+ @Override
+ public void onWriteFailed(CharSequence error) {
+ synchronized (mLock) {
+ mCancellationSignal = null;
+ mCompleted = true;
+ mLock.notifyAll();
+ }
+ Slog.e(LOG_TAG, "Error writing print document: " + error);
+ mCallback.onWriteFailed(error);
+ }
+ };
+
+ private ICancellationSignal mCancellationSignal;
+
+ private boolean mCompleted;
+
+ private Thread mWriteThread;
+
+ public WriteAsyncTask(List<PageRange> pages, WriteResultCallback callback) {
+ mPages = pages;
+ mCallback = callback;
+ }
+
+ @Override
+ public void cancel() {
+ synchronized (mLock) {
+ throwIfCancelledLocked();
+ cancelSignalQuietlyLocked();
+ mWriteThread.interrupt();
+ }
+ super.cancel();
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "print()");
+ }
+ synchronized (mLock) {
+ if (mState != STATE_LAYOUT_COMPLETED) {
+ throw new IllegalStateException("Invalid state: " + mState);
+ }
+ }
+ InputStream in = null;
+ OutputStream out = null;
+ ParcelFileDescriptor source = null;
+ ParcelFileDescriptor sink = null;
+ synchronized (mLock) {
+ mWriteThread = Thread.currentThread();
+ }
+ try {
+ ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
+ source = pipe[0];
+ sink = pipe[1];
+
+ in = new FileInputStream(source.getFileDescriptor());
+ out = new FileOutputStream(mFile);
+
+ // Async call to initiate the other process writing the data.
+ mRemoteInterface.write(mPages, sink, mIWriteResultCallback);
+
+ // Close the source. It is now held by the client.
+ sink.close();
+ sink = null;
+
+ final byte[] buffer = new byte[8192];
+ while (true) {
+ if (Thread.currentThread().isInterrupted()) {
+ Thread.currentThread().interrupt();
+ break;
+ }
+ final int readByteCount = in.read(buffer);
+ if (readByteCount < 0) {
+ break;
+ }
+ out.write(buffer, 0, readByteCount);
+ }
+ synchronized (mLock) {
+ while (true) {
+ if (isCancelled()) {
+ mState = STATE_INITIALIZED;
+ mTaskQueue.remove(this);
+ break;
+ }
+ if (mCompleted) {
+ mState = STATE_WRITE_COMPLETED;
+ mTaskQueue.remove(this);
+ break;
+ }
+ try {
+ mLock.wait();
+ } catch (InterruptedException ie) {
+ /* ignore */
+ }
+ }
+ }
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error writing print document", re);
+ mState = STATE_INITIALIZED;
+ } catch (IOException ioe) {
+ Slog.e(LOG_TAG, "Error writing print document", ioe);
+ mState = STATE_INITIALIZED;
+ } finally {
+ IoUtils.closeQuietly(in);
+ IoUtils.closeQuietly(out);
+ IoUtils.closeQuietly(sink);
+ IoUtils.closeQuietly(source);
+ }
+ return null;
+ }
+
+ private void cancelSignalQuietlyLocked() {
+ if (mCancellationSignal != null) {
+ try {
+ mCancellationSignal.cancel();
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error cancelling layout", re);
+ }
+ }
+ }
+
+ private void throwIfCancelledLocked() {
+ if (isCancelled()) {
+ throw new IllegalStateException("Already cancelled");
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 5b76f3954d97..203cca692df0 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -70,7 +70,7 @@ class AppWidgetService extends IAppWidgetService.Stub
mAppWidgetServices.append(0, primary);
}
- public void systemReady(boolean safeMode) {
+ public void systemRunning(boolean safeMode) {
mSafeMode = safeMode;
mAppWidgetServices.get(0).systemReady(safeMode);
diff --git a/services/java/com/android/server/AssetAtlasService.java b/services/java/com/android/server/AssetAtlasService.java
index 33f082c00464..26b4652f4c7d 100644
--- a/services/java/com/android/server/AssetAtlasService.java
+++ b/services/java/com/android/server/AssetAtlasService.java
@@ -186,7 +186,7 @@ public class AssetAtlasService extends IAssetAtlas.Stub {
* Callback invoked by the server thread to indicate we can now run
* 3rd party code.
*/
- public void systemReady() {
+ public void systemRunning() {
}
/**
diff --git a/services/java/com/android/server/CommonTimeManagementService.java b/services/java/com/android/server/CommonTimeManagementService.java
index c316733fe058..aa2c8b88a77e 100644
--- a/services/java/com/android/server/CommonTimeManagementService.java
+++ b/services/java/com/android/server/CommonTimeManagementService.java
@@ -153,7 +153,7 @@ class CommonTimeManagementService extends Binder {
mContext = context;
}
- void systemReady() {
+ void systemRunning() {
if (ServiceManager.checkService(CommonTimeConfig.SERVICE_NAME) == null) {
Log.i(TAG, "No common time service detected on this platform. " +
"Common time services will be unavailable.");
diff --git a/services/java/com/android/server/CountryDetectorService.java b/services/java/com/android/server/CountryDetectorService.java
index 8407fa44648f..4956dd5bd80a 100644
--- a/services/java/com/android/server/CountryDetectorService.java
+++ b/services/java/com/android/server/CountryDetectorService.java
@@ -166,7 +166,7 @@ public class CountryDetectorService extends ICountryDetector.Stub implements Run
}
}
- void systemReady() {
+ void systemRunning() {
// Shall we wait for the initialization finish.
BackgroundThread.getHandler().post(this);
}
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index c0a521480528..35656f8367c2 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -823,7 +823,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
- public void systemReady(StatusBarManagerService statusBar) {
+ public void systemRunning(StatusBarManagerService statusBar) {
synchronized (mMethodMap) {
if (DEBUG) {
Slog.d(TAG, "--- systemReady");
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index c6c9845979ad..bde9e1c95150 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -198,7 +198,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// most startup is deferred until systemReady()
}
- public void systemReady() {
+ public void systemRunning() {
synchronized (mLock) {
if (D) Log.d(TAG, "systemReady()");
diff --git a/services/java/com/android/server/NetworkTimeUpdateService.java b/services/java/com/android/server/NetworkTimeUpdateService.java
index 02b42b81de67..cbddf6797797 100644
--- a/services/java/com/android/server/NetworkTimeUpdateService.java
+++ b/services/java/com/android/server/NetworkTimeUpdateService.java
@@ -108,7 +108,7 @@ public class NetworkTimeUpdateService {
}
/** Initialize the receivers and initiate the first NTP request */
- public void systemReady() {
+ public void systemRunning() {
registerForTelephonyIntents();
registerForAlarms();
registerForConnectivityIntents();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 888064ccc7ee..0bbdcfb230e3 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -351,6 +351,7 @@ class ServerThread {
LockSettingsService lockSettings = null;
DreamManagerService dreamy = null;
AssetAtlasService atlas = null;
+ PrintManagerService printManager = null;
// Bring up services needed for UI.
if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
@@ -793,8 +794,8 @@ class ServerThread {
try {
Slog.i(TAG, "Print Service");
- ServiceManager.addService(Context.PRINT_SERVICE,
- new PrintManagerService(context));
+ printManager = new PrintManagerService(context);
+ ServiceManager.addService(Context.PRINT_SERVICE, printManager);
} catch (Throwable e) {
reportWtf("starting Print Service", e);
}
@@ -909,6 +910,7 @@ class ServerThread {
final AssetAtlasService atlasF = atlas;
final InputManagerService inputManagerF = inputManager;
final TelephonyRegistry telephonyRegistryF = telephonyRegistry;
+ final PrintManagerService printManagerF = printManager;
// We now tell the activity manager it is okay to run third party
// code. It will call back into us once it has gotten to the state
@@ -988,66 +990,73 @@ class ServerThread {
// third party code...
try {
- if (appWidgetF != null) appWidgetF.systemReady(safeMode);
+ if (appWidgetF != null) appWidgetF.systemRunning(safeMode);
} catch (Throwable e) {
- reportWtf("making App Widget Service ready", e);
+ reportWtf("Notifying AppWidgetService running", e);
}
try {
- if (wallpaperF != null) wallpaperF.systemReady();
+ if (wallpaperF != null) wallpaperF.systemRunning();
} catch (Throwable e) {
- reportWtf("making Wallpaper Service ready", e);
+ reportWtf("Notifying WallpaperService running", e);
}
try {
- if (immF != null) immF.systemReady(statusBarF);
+ if (immF != null) immF.systemRunning(statusBarF);
} catch (Throwable e) {
- reportWtf("making Input Method Service ready", e);
+ reportWtf("Notifying InputMethodService running", e);
}
try {
- if (locationF != null) locationF.systemReady();
+ if (locationF != null) locationF.systemRunning();
} catch (Throwable e) {
- reportWtf("making Location Service ready", e);
+ reportWtf("Notifying Location Service running", e);
}
try {
- if (countryDetectorF != null) countryDetectorF.systemReady();
+ if (countryDetectorF != null) countryDetectorF.systemRunning();
} catch (Throwable e) {
- reportWtf("making Country Detector Service ready", e);
+ reportWtf("Notifying CountryDetectorService running", e);
}
try {
- if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemReady();
+ if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemRunning();
} catch (Throwable e) {
- reportWtf("making Network Time Service ready", e);
+ reportWtf("Notifying NetworkTimeService running", e);
}
try {
- if (commonTimeMgmtServiceF != null) commonTimeMgmtServiceF.systemReady();
+ if (commonTimeMgmtServiceF != null) commonTimeMgmtServiceF.systemRunning();
} catch (Throwable e) {
- reportWtf("making Common time management service ready", e);
+ reportWtf("Notifying CommonTimeManagementService running", e);
}
try {
- if (textServiceManagerServiceF != null) textServiceManagerServiceF.systemReady();
+ if (textServiceManagerServiceF != null)
+ textServiceManagerServiceF.systemRunning();
} catch (Throwable e) {
- reportWtf("making Text Services Manager Service ready", e);
+ reportWtf("Notifying TextServicesManagerService running", e);
}
try {
- if (dreamyF != null) dreamyF.systemReady();
+ if (dreamyF != null) dreamyF.systemRunning();
} catch (Throwable e) {
- reportWtf("making DreamManagerService ready", e);
+ reportWtf("Notifying DreamManagerService running", e);
}
try {
- if (atlasF != null) atlasF.systemReady();
+ if (atlasF != null) atlasF.systemRunning();
} catch (Throwable e) {
- reportWtf("making AssetAtlasService ready", e);
+ reportWtf("Notifying AssetAtlasService running", e);
}
try {
// TODO(BT) Pass parameter to input manager
- if (inputManagerF != null) inputManagerF.systemReady();
+ if (inputManagerF != null) inputManagerF.systemRunning();
} catch (Throwable e) {
- reportWtf("making InputManagerService ready", e);
+ reportWtf("Notifying InputManagerService running", e);
}
try {
- if (telephonyRegistryF != null) telephonyRegistryF.systemReady();
+ if (telephonyRegistryF != null) telephonyRegistryF.systemRunning();
} catch (Throwable e) {
- reportWtf("making TelephonyRegistry ready", e);
+ reportWtf("Notifying TelephonyRegistry running", e);
+ }
+
+ try {
+ if (printManagerF != null) printManagerF.systemRuning();
+ } catch (Throwable e) {
+ reportWtf("Notifying PrintManagerService running", e);
}
}
});
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index 17260d505fdb..699d79eb0953 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -178,7 +178,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
mConnectedApns = new ArrayList<String>();
}
- public void systemReady() {
+ public void systemRunning() {
// Watch for interesting updates
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
diff --git a/services/java/com/android/server/TextServicesManagerService.java b/services/java/com/android/server/TextServicesManagerService.java
index 7dd9988b93af..6587c416230d 100644
--- a/services/java/com/android/server/TextServicesManagerService.java
+++ b/services/java/com/android/server/TextServicesManagerService.java
@@ -76,7 +76,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
new HashMap<String, SpellCheckerBindGroup>();
private final TextServicesSettings mSettings;
- public void systemReady() {
+ public void systemRunning() {
if (!mSystemReady) {
mSystemReady = true;
}
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index 9a7390980405..d677f2497cbd 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -449,7 +449,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
}
}
- public void systemReady() {
+ public void systemRunning() {
if (DEBUG) Slog.v(TAG, "systemReady");
WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_OWNER);
switchWallpaper(wallpaper, null);
diff --git a/services/java/com/android/server/dreams/DreamManagerService.java b/services/java/com/android/server/dreams/DreamManagerService.java
index c9e0da5b8f54..21e54fec863b 100644
--- a/services/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/java/com/android/server/dreams/DreamManagerService.java
@@ -73,7 +73,7 @@ public final class DreamManagerService extends IDreamManager.Stub {
mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
}
- public void systemReady() {
+ public void systemRunning() {
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java
index 5e4907e6820d..7b4c0775e47e 100644
--- a/services/java/com/android/server/input/InputManagerService.java
+++ b/services/java/com/android/server/input/InputManagerService.java
@@ -283,7 +283,7 @@ public class InputManagerService extends IInputManager.Stub
}
// TODO(BT) Pass in paramter for bluetooth system
- public void systemReady() {
+ public void systemRunning() {
if (DEBUG) {
Slog.d(TAG, "System ready.");
}
diff --git a/services/java/com/android/server/print/PrintManagerService.java b/services/java/com/android/server/print/PrintManagerService.java
index 51739984c613..86e76852b4d5 100644
--- a/services/java/com/android/server/print/PrintManagerService.java
+++ b/services/java/com/android/server/print/PrintManagerService.java
@@ -22,118 +22,111 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.ServiceConnection;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
import android.os.Process;
-import android.os.RemoteException;
import android.os.UserHandle;
-import android.print.IPrintAdapter;
import android.print.IPrintClient;
+import android.print.IPrintDocumentAdapter;
import android.print.IPrintManager;
-import android.print.IPrinterDiscoveryObserver;
import android.print.PrintAttributes;
import android.print.PrintJobInfo;
-import android.print.PrintManager;
-import android.print.PrinterId;
-import android.print.PrinterInfo;
-import android.printservice.IPrintService;
-import android.printservice.IPrintServiceClient;
-import android.printservice.PrintServiceInfo;
import android.provider.Settings;
-import android.text.TextUtils;
-import android.text.TextUtils.SimpleStringSplitter;
-import android.util.Slog;
+import android.util.SparseArray;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.BackgroundThread;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
import java.util.Set;
public final class PrintManagerService extends IPrintManager.Stub {
- private static final String LOG_TAG = PrintManagerService.class.getSimpleName();
-
private static final char COMPONENT_NAME_SEPARATOR = ':';
private final Object mLock = new Object();
- private final SimpleStringSplitter mStringColonSplitter =
- new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
-
- private final Map<ComponentName, PrintServiceClient> mServices =
- new HashMap<ComponentName, PrintServiceClient>();
-
- private final List<PrintServiceInfo> mInstalledServices = new ArrayList<PrintServiceInfo>();
-
- private final Set<ComponentName> mEnabledServiceNames = new HashSet<ComponentName>();
-
private final Context mContext;
- private final RemoteSpooler mSpooler;
-
- private final int mMyUid;
+ private final SparseArray<UserState> mUserStates = new SparseArray<UserState>();
private int mCurrentUserId = UserHandle.USER_OWNER;
- private IPrinterDiscoveryObserver mPrinterDiscoveryObserver;
-
public PrintManagerService(Context context) {
mContext = context;
- mSpooler = new RemoteSpooler(context);
- mMyUid = android.os.Process.myUid();
registerContentObservers();
- registerBoradcastreceivers();
+ registerBoradcastReceivers();
+ }
+
+ public void systemRuning() {
+ BackgroundThread.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mLock) {
+ UserState userState = getCurrentUserStateLocked();
+ userState.updateIfNeededLocked();
+ userState.getSpoolerLocked().notifyClientForActivteJobs();
+ }
+ }
+ });
}
@Override
- public PrintJobInfo print(String printJobName, IPrintClient client, IPrintAdapter printAdapter,
- PrintAttributes attributes, int appId, int userId) {
- final int resolvedAppId = resolveCallingAppEnforcingPermissionsLocked(appId);
- final int resolvedUserId = resolveCallingUserEnforcingPermissionsIdLocked(userId);
+ public PrintJobInfo print(String printJobName, IPrintClient client,
+ IPrintDocumentAdapter documentAdapter, PrintAttributes attributes, int appId,
+ int userId) {
+ final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
+ final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+ final UserState userState;
+ final RemotePrintSpooler spooler;
+ synchronized (mLock) {
+ userState = getOrCreateUserStateLocked(resolvedUserId);
+ spooler = userState.getSpoolerLocked();
+ }
final long identity = Binder.clearCallingIdentity();
try {
- return mSpooler.createPrintJob(printJobName, client, printAdapter,
- attributes, resolvedAppId, resolvedUserId);
+ return spooler.createPrintJob(printJobName, client, documentAdapter,
+ attributes, resolvedAppId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
- public List<PrintJobInfo> getPrintJobs(int appId, int userId) {
- final int resolvedAppId = resolveCallingAppEnforcingPermissionsLocked(appId);
- final int resolvedUserId = resolveCallingUserEnforcingPermissionsIdLocked(userId);
- // TODO: Do we want to return jobs in STATE_CREATED? We should probably
- // have additional argument for the types to get
+ public List<PrintJobInfo> getPrintJobInfos(int appId, int userId) {
+ final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
+ final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+ final UserState userState;
+ final RemotePrintSpooler spooler;
+ synchronized (mLock) {
+ userState = getOrCreateUserStateLocked(resolvedUserId);
+ spooler = userState.getSpoolerLocked();
+ }
final long identity = Binder.clearCallingIdentity();
try {
- return mSpooler.getPrintJobs(null, PrintJobInfo.STATE_ANY,
- resolvedAppId, resolvedUserId);
+ return spooler.getPrintJobInfos(null, PrintJobInfo.STATE_ANY,
+ resolvedAppId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
- public PrintJobInfo getPrintJob(int printJobId, int appId, int userId) {
- final int resolvedAppId = resolveCallingAppEnforcingPermissionsLocked(appId);
- final int resolvedUserId = resolveCallingUserEnforcingPermissionsIdLocked(userId);
+ public PrintJobInfo getPrintJobInfo(int printJobId, int appId, int userId) {
+ final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
+ final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+ final UserState userState;
+ final RemotePrintSpooler spooler;
+ synchronized (mLock) {
+ userState = getOrCreateUserStateLocked(resolvedUserId);
+ spooler = userState.getSpoolerLocked();
+ }
final long identity = Binder.clearCallingIdentity();
try {
- return mSpooler.getPrintJobInfo(printJobId, resolvedAppId, resolvedUserId);
+ return spooler.getPrintJobInfo(printJobId, resolvedAppId);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -141,93 +134,32 @@ public final class PrintManagerService extends IPrintManager.Stub {
@Override
public void cancelPrintJob(int printJobId, int appId, int userId) {
- final int resolvedAppId = resolveCallingAppEnforcingPermissionsLocked(appId);
- final int resolvedUserId = resolveCallingUserEnforcingPermissionsIdLocked(userId);
+ final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
+ final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+ final UserState userState;
+ final RemotePrintSpooler spooler;
+ synchronized (mLock) {
+ userState = getOrCreateUserStateLocked(resolvedUserId);
+ spooler = userState.getSpoolerLocked();
+ }
final long identity = Binder.clearCallingIdentity();
try {
- if (mSpooler.cancelPrintJob(printJobId, resolvedAppId, resolvedUserId)) {
+ if (spooler.cancelPrintJob(printJobId, resolvedAppId)) {
return;
}
- PrintJobInfo printJob = getPrintJob(printJobId, resolvedAppId, resolvedUserId);
- if (printJob == null) {
+ PrintJobInfo printJobInfo = getPrintJobInfo(printJobId, resolvedAppId, resolvedUserId);
+ if (printJobInfo == null) {
return;
}
- ComponentName printServiceName = printJob.getPrinterId().getServiceComponentName();
- PrintServiceClient printService = null;
+ ComponentName printServiceName = printJobInfo.getPrinterId().getService();
+ RemotePrintService printService = null;
synchronized (mLock) {
- printService = mServices.get(printServiceName);
+ printService = userState.getActiveServices().get(printServiceName);
}
if (printService == null) {
return;
}
- printService.requestCancelPrintJob(printJob);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- // Called only from the spooler.
- @Override
- public void onPrintJobQueued(PrinterId printerId, PrintJobInfo printJob) {
- throwIfCallerNotSignedWithSystemKey();
- PrintServiceClient printService = null;
- synchronized (mLock) {
- ComponentName printServiceName = printerId.getServiceComponentName();
- printService = mServices.get(printServiceName);
- }
- if (printService != null) {
- final long identity = Binder.clearCallingIdentity();
- try {
- printService.notifyPrintJobQueued(printJob);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
- }
-
- // Called only from the spooler.
- @Override
- public void startDiscoverPrinters(IPrinterDiscoveryObserver observer) {
- throwIfCallerNotSignedWithSystemKey();
- List<PrintServiceClient> services = new ArrayList<PrintServiceClient>();
- synchronized (mLock) {
- mPrinterDiscoveryObserver = observer;
- services.addAll(mServices.values());
- }
- final int serviceCount = services.size();
- if (serviceCount <= 0) {
- return;
- }
- final long identity = Binder.clearCallingIdentity();
- try {
- for (int i = 0; i < serviceCount; i++) {
- PrintServiceClient service = services.get(i);
- service.startPrinterDiscovery();
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- // Called only from the spooler.
- @Override
- public void stopDiscoverPrinters() {
- throwIfCallerNotSignedWithSystemKey();
- List<PrintServiceClient> services = new ArrayList<PrintServiceClient>();
- synchronized (mLock) {
- mPrinterDiscoveryObserver = null;
- services.addAll(mServices.values());
- }
- final int serviceCount = services.size();
- if (serviceCount <= 0) {
- return;
- }
- final long identity = Binder.clearCallingIdentity();
- try {
- for (int i = 0; i < serviceCount; i++) {
- PrintServiceClient service = services.get(i);
- service.stopPrintersDiscovery();
- }
+ printService.onRequestCancelPrintJob(printJobInfo);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -237,14 +169,13 @@ public final class PrintManagerService extends IPrintManager.Stub {
final Uri enabledPrintServicesUri = Settings.Secure.getUriFor(
Settings.Secure.ENABLED_PRINT_SERVICES);
- ContentObserver observer = new ContentObserver(new Handler(mContext.getMainLooper())) {
+ ContentObserver observer = new ContentObserver(BackgroundThread.getHandler()) {
@Override
public void onChange(boolean selfChange, Uri uri) {
if (enabledPrintServicesUri.equals(uri)) {
synchronized (mLock) {
- if (readEnabledPrintServicesChangedLocked()) {
- onUserStateChangedLocked();
- }
+ UserState userState = getCurrentUserStateLocked();
+ userState.updateIfNeededLocked();
}
}
}
@@ -254,32 +185,37 @@ public final class PrintManagerService extends IPrintManager.Stub {
false, observer, UserHandle.USER_ALL);
}
- private void registerBoradcastreceivers() {
+ private void registerBoradcastReceivers() {
PackageMonitor monitor = new PackageMonitor() {
@Override
- public void onSomePackagesChanged() {
+ public boolean onPackageChanged(String packageName, int uid, String[] components) {
synchronized (mLock) {
- if (getChangingUserId() != mCurrentUserId) {
- return;
- }
- if (readConfigurationForUserStateLocked()) {
- onUserStateChangedLocked();
+ UserState userState = getOrCreateUserStateLocked(getChangingUserId());
+ Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
+ while (iterator.hasNext()) {
+ ComponentName componentName = iterator.next();
+ if (packageName.equals(componentName.getPackageName())) {
+ userState.updateIfNeededLocked();
+ return true;
+ }
}
}
+ return false;
}
@Override
public void onPackageRemoved(String packageName, int uid) {
synchronized (mLock) {
- if (getChangingUserId() != mCurrentUserId) {
- return;
- }
- Iterator<ComponentName> iterator = mEnabledServiceNames.iterator();
+ UserState userState = getOrCreateUserStateLocked(getChangingUserId());
+ Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
while (iterator.hasNext()) {
ComponentName componentName = iterator.next();
if (packageName.equals(componentName.getPackageName())) {
iterator.remove();
- onEnabledServiceNamesChangedLocked();
+ persistComponentNamesToSettingLocked(
+ Settings.Secure.ENABLED_PRINT_SERVICES,
+ userState.getEnabledServices(), getChangingUserId());
+ userState.updateIfNeededLocked();
return;
}
}
@@ -290,10 +226,9 @@ public final class PrintManagerService extends IPrintManager.Stub {
public boolean onHandleForceStop(Intent intent, String[] stoppedPackages,
int uid, boolean doit) {
synchronized (mLock) {
- if (getChangingUserId() != mCurrentUserId) {
- return false;
- }
- Iterator<ComponentName> iterator = mEnabledServiceNames.iterator();
+ UserState userState = getOrCreateUserStateLocked(getChangingUserId());
+ boolean stoppedSomePackages = false;
+ Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
while (iterator.hasNext()) {
ComponentName componentName = iterator.next();
String componentPackage = componentName.getPackageName();
@@ -302,27 +237,35 @@ public final class PrintManagerService extends IPrintManager.Stub {
if (!doit) {
return true;
}
- iterator.remove();
- onEnabledServiceNamesChangedLocked();
+ stoppedSomePackages = true;
+ break;
}
}
}
+ if (stoppedSomePackages) {
+ userState.updateIfNeededLocked();
+ }
return false;
}
}
- private void onEnabledServiceNamesChangedLocked() {
- // Update the enabled services setting.
- persistComponentNamesToSettingLocked(
- Settings.Secure.ENABLED_PRINT_SERVICES,
- mEnabledServiceNames, mCurrentUserId);
- // Update the current user state.
- onUserStateChangedLocked();
+ private void persistComponentNamesToSettingLocked(String settingName,
+ Set<ComponentName> componentNames, int userId) {
+ StringBuilder builder = new StringBuilder();
+ for (ComponentName componentName : componentNames) {
+ if (builder.length() > 0) {
+ builder.append(COMPONENT_NAME_SEPARATOR);
+ }
+ builder.append(componentName.flattenToShortString());
+ }
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ settingName, builder.toString(), userId);
}
};
// package changes
- monitor.register(mContext, null, UserHandle.ALL, true);
+ monitor.register(mContext, BackgroundThread.getHandler().getLooper(),
+ UserHandle.ALL, true);
// user changes
IntentFilter intentFilter = new IntentFilter();
@@ -334,179 +277,67 @@ public final class PrintManagerService extends IPrintManager.Stub {
String action = intent.getAction();
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+ } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
+ removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
}
}
- }, UserHandle.ALL, intentFilter, null, null);
- }
-
- private void throwIfCallerNotSignedWithSystemKey() {
- if (mContext.getPackageManager().checkSignatures(
- mMyUid, Binder.getCallingUid()) != PackageManager.SIGNATURE_MATCH) {
- throw new SecurityException("Caller must be signed with the system key!");
- }
- }
-
- private void onUserStateChangedLocked() {
- manageServicesLocked();
- }
-
- private void manageServicesLocked() {
- 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 (mEnabledServiceNames.contains(serviceName)) {
- if (!mServices.containsKey(serviceName)) {
- new PrintServiceClient(serviceName, mCurrentUserId).ensureBoundLocked();
- }
- } else {
- PrintServiceClient service = mServices.get(serviceName);
- if (service != null) {
- service.ensureUnboundLocked();
- }
- }
- }
+ }, UserHandle.ALL, intentFilter, null, BackgroundThread.getHandler());
}
- private boolean readConfigurationForUserStateLocked() {
- boolean somethingChanged = false;
- somethingChanged |= readInstalledPrintServiceLocked();
- somethingChanged |= readEnabledPrintServicesChangedLocked();
- return somethingChanged;
- }
-
- private boolean readEnabledPrintServicesChangedLocked() {
- Set<ComponentName> tempEnabledServiceNameSet = new HashSet<ComponentName>();
- readComponentNamesFromSettingLocked(Settings.Secure.ENABLED_PRINT_SERVICES,
- mCurrentUserId, tempEnabledServiceNameSet);
- if (!tempEnabledServiceNameSet.equals(mEnabledServiceNames)) {
- mEnabledServiceNames.clear();
- mEnabledServiceNames.addAll(tempEnabledServiceNameSet);
- return true;
- }
- return false;
+ private UserState getCurrentUserStateLocked() {
+ return getOrCreateUserStateLocked(mCurrentUserId);
}
- private boolean readInstalledPrintServiceLocked() {
- Set<PrintServiceInfo> tempPrintServices = new HashSet<PrintServiceInfo>();
-
- List<ResolveInfo> installedServices = mContext.getPackageManager()
- .queryIntentServicesAsUser(
- new Intent(android.printservice.PrintService.SERVICE_INTERFACE),
- PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
- mCurrentUserId);
-
- 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;
+ private UserState getOrCreateUserStateLocked(int userId) {
+ UserState userState = mUserStates.get(userId);
+ if (userState == null) {
+ userState = new UserState(mContext, userId, mLock);
+ mUserStates.put(userId, userState);
}
- return false;
- }
-
- private void readComponentNamesFromSettingLocked(String settingName, int userId,
- Set<ComponentName> outComponentNames) {
- String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
- settingName, userId);
- outComponentNames.clear();
- 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) {
- outComponentNames.add(componentName);
- }
- }
- }
- }
-
- private void persistComponentNamesToSettingLocked(String settingName,
- Set<ComponentName> componentNames, int userId) {
- StringBuilder builder = new StringBuilder();
- for (ComponentName componentName : componentNames) {
- if (builder.length() > 0) {
- builder.append(COMPONENT_NAME_SEPARATOR);
- }
- builder.append(componentName.flattenToShortString());
- }
- Settings.Secure.putStringForUser(mContext.getContentResolver(),
- settingName, builder.toString(), userId);
+ return userState;
}
private void switchUser(int newUserId) {
synchronized (mLock) {
- // Disconnect services for the old user.
- mEnabledServiceNames.clear();
- onUserStateChangedLocked();
-
- // The user changed.
+ if (newUserId == mCurrentUserId) {
+ return;
+ }
mCurrentUserId = newUserId;
-
- // Update the user state based on current settings.
- readConfigurationForUserStateLocked();
- onUserStateChangedLocked();
- }
-
- // Unbind the spooler for the old user).
- mSpooler.unbind();
-
- // If we have queued jobs, advertise it, or we do
- // not need the spooler for now.
- if (notifyQueuedPrintJobs()) {
- mSpooler.unbind();
+ UserState userState = getCurrentUserStateLocked();
+ userState.updateIfNeededLocked();
+ userState.getSpoolerLocked().notifyClientForActivteJobs();
}
}
- private boolean notifyQueuedPrintJobs() {
- Map<PrintServiceClient, List<PrintJobInfo>> notifications =
- new HashMap<PrintServiceClient, List<PrintJobInfo>>();
+ private void removeUser(int removedUserId) {
synchronized (mLock) {
- for (PrintServiceClient service : mServices.values()) {
- List<PrintJobInfo> printJobs = mSpooler.getPrintJobs(
- service.mComponentName, PrintJobInfo.STATE_QUEUED,
- PrintManager.APP_ID_ANY, service.mUserId);
- notifications.put(service, printJobs);
+ UserState userState = mUserStates.get(removedUserId);
+ if (userState != null) {
+ userState.destroyLocked();
+ mUserStates.remove(removedUserId);
}
}
- if (notifications.isEmpty()) {
- return false;
+ }
+
+ private int resolveCallingAppEnforcingPermissions(int appId) {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid == 0 || callingUid == Process.SYSTEM_UID
+ || callingUid == Process.SHELL_UID) {
+ return appId;
}
- for (Map.Entry<PrintServiceClient, List<PrintJobInfo>> notification
- : notifications.entrySet()) {
- PrintServiceClient service = notification.getKey();
- List<PrintJobInfo> printJobs = notification.getValue();
- final int printJobIdCount = printJobs.size();
- for (int i = 0; i < printJobIdCount; i++) {
- service.notifyPrintJobQueued(printJobs.get(i));
- }
+ final int callingAppId = UserHandle.getAppId(callingUid);
+ if (appId == callingAppId) {
+ return appId;
+ }
+ if (mContext.checkCallingPermission(Manifest.permission.ACCESS_ALL_PRINT_JOBS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Call from app " + callingAppId + " as app "
+ + appId + " without permission ACCESS_ALL_PRINT_JOBS");
}
- return true;
+ return appId;
}
- private int resolveCallingUserEnforcingPermissionsIdLocked(int userId) {
+ private int resolveCallingUserEnforcingPermissions(int userId) {
final int callingUid = Binder.getCallingUid();
if (callingUid == 0 || callingUid == Process.SYSTEM_UID
|| callingUid == Process.SHELL_UID) {
@@ -524,8 +355,8 @@ public final class PrintManagerService extends IPrintManager.Stub {
return callingUserId;
}
throw new SecurityException("Call from user " + callingUserId + " as user "
- + userId + " without permission INTERACT_ACROSS_USERS or "
- + "INTERACT_ACROSS_USERS_FULL not allowed.");
+ + userId + " without permission INTERACT_ACROSS_USERS or "
+ + "INTERACT_ACROSS_USERS_FULL not allowed.");
}
if (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF) {
return mCurrentUserId;
@@ -533,257 +364,4 @@ public final class PrintManagerService extends IPrintManager.Stub {
throw new IllegalArgumentException("Calling user can be changed to only "
+ "UserHandle.USER_CURRENT or UserHandle.USER_CURRENT_OR_SELF.");
}
-
- private int resolveCallingAppEnforcingPermissionsLocked(int appId) {
- final int callingUid = Binder.getCallingUid();
- if (callingUid == 0 || callingUid == Process.SYSTEM_UID
- || callingUid == Process.SHELL_UID) {
- return appId;
- }
- final int callingAppId = UserHandle.getAppId(callingUid);
- if (appId == callingAppId) {
- return appId;
- }
- if (mContext.checkCallingPermission(Manifest.permission.ACCESS_ALL_PRINT_JOBS)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Call from app " + callingAppId + " as app "
- + appId + " without permission INTERACT_ACROSS_APPS");
- }
- return appId;
- }
-
- private final class PrintServiceClient extends IPrintServiceClient.Stub
- implements ServiceConnection, DeathRecipient {
-
- private final ComponentName mComponentName;
-
- private final Intent mIntent;
-
- private final int mUserId;
-
- private IPrintService mInterface;
-
- private boolean mBinding;
-
- private boolean mWasConnectedAndDied;
-
- public PrintServiceClient(ComponentName componentName, int userId) {
- mComponentName = componentName;
- mIntent = new Intent().setComponent(mComponentName);
- mUserId = userId;
- }
-
- @Override
- public List<PrintJobInfo> getPrintJobs() {
- return mSpooler.getPrintJobs(mComponentName, PrintJobInfo.STATE_ANY,
- PrintManager.APP_ID_ANY, mUserId);
- }
-
- @Override
- public PrintJobInfo getPrintJob(int printJobId) {
- return mSpooler.getPrintJobInfo(printJobId,
- PrintManager.APP_ID_ANY, mUserId);
- }
-
- @Override
- public boolean setPrintJobState(int printJobId, int state) {
- return mSpooler.setPrintJobState(printJobId, state, mUserId);
- }
-
- @Override
- public boolean setPrintJobTag(int printJobId, String tag) {
- return mSpooler.setPrintJobTag(printJobId, tag, mUserId);
- }
-
- @Override
- public void writePrintJobData(ParcelFileDescriptor fd, int printJobId) {
- mSpooler.writePrintJobData(fd, printJobId, mUserId);
- }
-
- @Override
- public void addDiscoveredPrinters(List<PrinterInfo> printers) {
- throwIfPrinterIdsForPrinterInfoTampered(printers);
- synchronized (mLock) {
- if (mPrinterDiscoveryObserver != null) {
- try {
- mPrinterDiscoveryObserver.addDiscoveredPrinters(printers);
- } catch (RemoteException re) {
- /* ignore */
- }
- }
- }
- }
-
- @Override
- public void removeDiscoveredPrinters(List<PrinterId> printerIds) {
- throwIfPrinterIdsTampered(printerIds);
- synchronized (mLock) {
- if (mPrinterDiscoveryObserver != null) {
- try {
- mPrinterDiscoveryObserver.removeDiscoveredPrinters(printerIds);
- } catch (RemoteException re) {
- /* ignore */
- }
- }
- }
- }
-
- public void requestCancelPrintJob(PrintJobInfo printJob) {
- synchronized (mLock) {
- try {
- mInterface.requestCancelPrintJob(printJob);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Error canceling pring job!", re);
- }
- }
- }
-
- public void notifyPrintJobQueued(PrintJobInfo printJob) {
- IPrintService service = mInterface;
- if (service != null) {
- try {
- service.onPrintJobQueued(printJob);
- } catch (RemoteException re) {
- /* ignore */
- }
- }
- }
-
- public void startPrinterDiscovery() {
- IPrintService service = mInterface;
- if (service != null) {
- try {
- service.startPrinterDiscovery();
- } catch (RemoteException re) {
- /* ignore */
- }
- }
- }
-
- public void stopPrintersDiscovery() {
- IPrintService service = mInterface;
- if (service != null) {
- try {
- service.stopPrinterDiscovery();
- } catch (RemoteException re) {
- /* ignore */
- }
- }
- }
-
- public void ensureBoundLocked() {
- if (mBinding) {
- return;
- }
- if (mInterface == null) {
- mBinding = true;
- mContext.bindServiceAsUser(mIntent, this,
- Context.BIND_AUTO_CREATE, new UserHandle(mUserId));
- }
- }
-
- public void ensureUnboundLocked() {
- if (mBinding) {
- mBinding = false;
- return;
- }
- if (mInterface != null) {
- mContext.unbindService(this);
- destroyLocked();
- }
- }
-
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- synchronized (mLock) {
- mInterface = IPrintService.Stub.asInterface(service);
- mServices.put(mComponentName, this);
- try {
- mInterface.asBinder().linkToDeath(this, 0);
- } catch (RemoteException re) {
- destroyLocked();
- return;
- }
- if (mUserId != mCurrentUserId) {
- destroyLocked();
- return;
- }
- if (mBinding || mWasConnectedAndDied) {
- mBinding = false;
- mWasConnectedAndDied = false;
- onUserStateChangedLocked();
- try {
- mInterface.setClient(this);
- } catch (RemoteException re) {
- Slog.w(LOG_TAG, "Error while setting client for service: "
- + service, re);
- }
- } else {
- destroyLocked();
- }
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- /* do nothing - #binderDied takes care */
- }
-
- @Override
- public void binderDied() {
- synchronized (mLock) {
- if (isConnectedLocked()) {
- mWasConnectedAndDied = true;
- }
- destroyLocked();
- }
- }
-
- private void destroyLocked() {
- if (mServices.remove(mComponentName) == null) {
- return;
- }
- if (isConnectedLocked()) {
- try {
- mInterface.asBinder().unlinkToDeath(this, 0);
- } catch (NoSuchElementException nse) {
- /* ignore */
- }
- try {
- mInterface.setClient(null);
- } catch (RemoteException re) {
- /* ignore */
- }
- mInterface = null;
- }
- mBinding = false;
- }
-
- private boolean isConnectedLocked() {
- return (mInterface != null);
- }
-
- private void throwIfPrinterIdsForPrinterInfoTampered(List<PrinterInfo> printerInfos) {
- final int printerInfoCount = printerInfos.size();
- for (int i = 0; i < printerInfoCount; i++) {
- PrinterId printerId = printerInfos.get(i).getId();
- throwIfPrinterIdTampered(printerId);
- }
- }
-
- private void throwIfPrinterIdsTampered(List<PrinterId> printerIds) {
- final int printerIdCount = printerIds.size();
- for (int i = 0; i < printerIdCount; i++) {
- PrinterId printerId = printerIds.get(i);
- throwIfPrinterIdTampered(printerId);
- }
- }
-
- private void throwIfPrinterIdTampered(PrinterId printerId) {
- if (printerId == null || printerId.getServiceComponentName() == null
- || !printerId.getServiceComponentName().equals(mComponentName)) {
- throw new IllegalArgumentException("Invalid printer id: " + printerId);
- }
- }
- }
}
diff --git a/services/java/com/android/server/print/RemotePrintService.java b/services/java/com/android/server/print/RemotePrintService.java
new file mode 100644
index 000000000000..b9e0280bed82
--- /dev/null
+++ b/services/java/com/android/server/print/RemotePrintService.java
@@ -0,0 +1,466 @@
+/*
+ * 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.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.print.IPrinterDiscoveryObserver;
+import android.print.PrintJobInfo;
+import android.print.PrintManager;
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+import android.printservice.IPrintService;
+import android.printservice.IPrintServiceClient;
+import android.util.Slog;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class represents a remote print service. It abstracts away the binding
+ * and unbinding from the remote implementation. Clients can call methods of
+ * this class without worrying about when and how to bind/unbind.
+ */
+final class RemotePrintService {
+
+ private static final String LOG_TAG = "RemotePrintService";
+
+ private static final boolean DEBUG = true;
+
+ private final Context mContext;
+
+ private final ComponentName mComponentName;
+
+ private final Intent mIntent;
+
+ private final RemotePrintSpooler mSpooler;
+
+ private final int mUserId;
+
+ private final List<Runnable> mPendingCommands = new ArrayList<Runnable>();
+
+ private final ServiceConnection mServiceConnection = new RemoteServiceConneciton();
+
+ private final RemotePrintServiceClient mPrintServiceClient;
+
+ private final Handler mHandler;
+
+ private IPrintService mPrintService;
+
+ private boolean mBinding;
+
+ private boolean mDestroyed;
+
+ public RemotePrintService(Context context, ComponentName componentName, int userId,
+ RemotePrintSpooler spooler) {
+ mContext = context;
+ mComponentName = componentName;
+ mIntent = new Intent().setComponent(mComponentName);
+ mUserId = userId;
+ mSpooler = spooler;
+ mHandler = new MyHandler(context.getMainLooper());
+ mPrintServiceClient = new RemotePrintServiceClient(this);
+ }
+
+ public void destroy() {
+ mHandler.sendEmptyMessage(MyHandler.MSG_DESTROY);
+ }
+
+ private void handleDestroy() {
+ throwIfDestroyed();
+ ensureUnbound();
+ mDestroyed = true;
+ }
+
+ public void onAllPrintJobsHandled() {
+ mHandler.sendEmptyMessage(MyHandler.MSG_ALL_PRINT_JOBS_HANDLED);
+ }
+
+ private void handleOnAllPrintJobsHandled() {
+ throwIfDestroyed();
+ if (isBound()) {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserId + "] handleOnAllPrintJobsHandled");
+ }
+ // If bound and all the work is completed, then unbind.
+ ensureUnbound();
+ }
+ }
+
+ public void onRequestCancelPrintJob(PrintJobInfo printJob) {
+ mHandler.obtainMessage(MyHandler.MSG_REQUEST_CANCEL_PRINT_JOB,
+ printJob).sendToTarget();
+ }
+
+ private void handleOnRequestCancelPrintJob(final PrintJobInfo printJob) {
+ throwIfDestroyed();
+ // If we are not bound, then we have no print jobs to handle
+ // which means that there are no print jobs to be cancelled.
+ if (isBound()) {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserId + "] handleOnRequestCancelPrintJob()");
+ }
+ try {
+ mPrintService.requestCancelPrintJob(printJob);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error canceling pring job.", re);
+ }
+ }
+ }
+
+ public void onPrintJobQueued(PrintJobInfo printJob) {
+ mHandler.obtainMessage(MyHandler.MSG_PRINT_JOB_QUEUED,
+ printJob).sendToTarget();
+ }
+
+ private void handleOnPrintJobQueued(final PrintJobInfo printJob) {
+ throwIfDestroyed();
+ if (!isBound()) {
+ ensureBound();
+ mPendingCommands.add(new Runnable() {
+ @Override
+ public void run() {
+ handleOnPrintJobQueued(printJob);
+ }
+ });
+ } else {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserId + "] handleOnPrintJobQueued()");
+ }
+ try {
+ mPrintService.onPrintJobQueued(printJob);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error announcing queued pring job.", re);
+ }
+ }
+ }
+
+ public void onStartPrinterDiscovery(IPrinterDiscoveryObserver observer) {
+ mHandler.obtainMessage(MyHandler.MSG_START_PRINTER_DISCOVERY, observer).sendToTarget();
+ }
+
+ private void handleOnStartPrinterDiscovery(final IPrinterDiscoveryObserver observer) {
+ throwIfDestroyed();
+ if (!isBound()) {
+ ensureBound();
+ mPendingCommands.add(new Runnable() {
+ @Override
+ public void run() {
+ handleOnStartPrinterDiscovery(observer);
+ }
+ });
+ } else {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserId + "] onStartPrinterDiscovery()");
+ }
+ try {
+ mPrintService.startPrinterDiscovery(observer);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error announcing start printer dicovery.", re);
+ }
+ }
+ }
+
+ public void onStopPrinterDiscovery() {
+ mHandler.sendEmptyMessage(MyHandler.MSG_STOP_PRINTER_DISCOVERY);
+ }
+
+ private void handleStopPrinterDiscovery() {
+ throwIfDestroyed();
+ if (!isBound()) {
+ ensureBound();
+ mPendingCommands.add(new Runnable() {
+ @Override
+ public void run() {
+ handleStopPrinterDiscovery();
+ }
+ });
+ } else {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserId + "] onStopPrinterDiscovery()");
+ }
+ try {
+ mPrintService.stopPrinterDiscovery();
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error announcing stop printer dicovery.", re);
+ }
+ }
+ }
+
+ private boolean isBound() {
+ return mPrintService != null;
+ }
+
+ private void ensureBound() {
+ if (isBound() || mBinding) {
+ return;
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserId + "] ensureBound()");
+ }
+ mBinding = true;
+ mContext.bindServiceAsUser(mIntent, mServiceConnection,
+ Context.BIND_AUTO_CREATE, new UserHandle(mUserId));
+ }
+
+ private void ensureUnbound() {
+ if (!isBound() && !mBinding) {
+ return;
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserId + "] ensureUnbound()");
+ }
+ mBinding = false;
+ mPendingCommands.clear();
+ if (isBound()) {
+ try {
+ mPrintService.setClient(null);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ mPrintService = null;
+ mContext.unbindService(mServiceConnection);
+ }
+ }
+
+ private void throwIfDestroyed() {
+ if (mDestroyed) {
+ throw new IllegalStateException("Cannot interact with a destroyed service");
+ }
+ }
+
+ private class RemoteServiceConneciton implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (mDestroyed || !mBinding) {
+ return;
+ }
+ mBinding = false;
+ mPrintService = IPrintService.Stub.asInterface(service);
+ try {
+ mPrintService.setClient(mPrintServiceClient);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error setting client for: " + service, re);
+ handleDestroy();
+ return;
+ }
+ final int pendingCommandCount = mPendingCommands.size();
+ for (int i = 0; i < pendingCommandCount; i++) {
+ Runnable pendingCommand = mPendingCommands.get(i);
+ pendingCommand.run();
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mBinding = true;
+ }
+ }
+
+ private final class MyHandler extends Handler {
+ public static final int MSG_ALL_PRINT_JOBS_HANDLED = 1;
+ public static final int MSG_REQUEST_CANCEL_PRINT_JOB = 2;
+ public static final int MSG_PRINT_JOB_QUEUED = 3;
+ public static final int MSG_START_PRINTER_DISCOVERY = 4;
+ public static final int MSG_STOP_PRINTER_DISCOVERY = 5;
+ public static final int MSG_DESTROY = 6;
+
+ public MyHandler(Looper looper) {
+ super(looper, null, false);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MSG_ALL_PRINT_JOBS_HANDLED: {
+ handleOnAllPrintJobsHandled();
+ } break;
+
+ case MSG_REQUEST_CANCEL_PRINT_JOB: {
+ PrintJobInfo printJob = (PrintJobInfo) message.obj;
+ handleOnRequestCancelPrintJob(printJob);
+ } break;
+
+ case MSG_PRINT_JOB_QUEUED: {
+ PrintJobInfo printJob = (PrintJobInfo) message.obj;
+ handleOnPrintJobQueued(printJob);
+ } break;
+
+ case MSG_START_PRINTER_DISCOVERY: {
+ IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) message.obj;
+ handleOnStartPrinterDiscovery(new SecurePrinterDiscoveryObserver(
+ mComponentName, observer));
+ } break;
+
+ case MSG_STOP_PRINTER_DISCOVERY: {
+ handleStopPrinterDiscovery();
+ } break;
+
+ case MSG_DESTROY: {
+ handleDestroy();
+ } break;
+ }
+ }
+ }
+
+ private static final class RemotePrintServiceClient extends IPrintServiceClient.Stub {
+ private final WeakReference<RemotePrintService> mWeakService;
+
+ public RemotePrintServiceClient(RemotePrintService service) {
+ mWeakService = new WeakReference<RemotePrintService>(service);
+ }
+
+ @Override
+ public List<PrintJobInfo> getPrintJobInfos() {
+ RemotePrintService service = mWeakService.get();
+ if (service != null) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return service.mSpooler.getPrintJobInfos(service.mComponentName,
+ PrintJobInfo.STATE_ANY, PrintManager.APP_ID_ANY);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public PrintJobInfo getPrintJobInfo(int printJobId) {
+ RemotePrintService service = mWeakService.get();
+ if (service != null) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return service.mSpooler.getPrintJobInfo(printJobId,
+ PrintManager.APP_ID_ANY);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean setPrintJobState(int printJobId, int state) {
+ RemotePrintService service = mWeakService.get();
+ if (service != null) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return service.mSpooler.setPrintJobState(printJobId, state);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean setPrintJobTag(int printJobId, String tag) {
+ RemotePrintService service = mWeakService.get();
+ if (service != null) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return service.mSpooler.setPrintJobTag(printJobId, tag);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void writePrintJobData(ParcelFileDescriptor fd, int printJobId) {
+ RemotePrintService service = mWeakService.get();
+ if (service != null) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ service.mSpooler.writePrintJobData(fd, printJobId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+ }
+
+ private static final class SecurePrinterDiscoveryObserver
+ extends IPrinterDiscoveryObserver.Stub {
+ private final ComponentName mComponentName;
+
+ private final IPrinterDiscoveryObserver mDecoratedObsever;
+
+ public SecurePrinterDiscoveryObserver(ComponentName componentName,
+ IPrinterDiscoveryObserver observer) {
+ mComponentName = componentName;
+ mDecoratedObsever = observer;
+ }
+
+ @Override
+ public void addDiscoveredPrinters(List<PrinterInfo> printers) {
+ throwIfPrinterIdsForPrinterInfoTampered(printers);
+ try {
+ mDecoratedObsever.addDiscoveredPrinters(printers);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error delegating to addDiscoveredPrinters", re);
+ }
+ }
+
+ @Override
+ public void removeDiscoveredPrinters(List<PrinterId> printerIds) {
+ throwIfPrinterIdsTampered(printerIds);
+ try {
+ mDecoratedObsever.removeDiscoveredPrinters(printerIds);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error delegating to removeDiscoveredPrinters", re);
+ }
+ }
+
+ private void throwIfPrinterIdsForPrinterInfoTampered(
+ List<PrinterInfo> printerInfos) {
+ final int printerInfoCount = printerInfos.size();
+ for (int i = 0; i < printerInfoCount; i++) {
+ PrinterId printerId = printerInfos.get(i).getId();
+ throwIfPrinterIdTampered(printerId);
+ }
+ }
+
+ private void throwIfPrinterIdsTampered(List<PrinterId> printerIds) {
+ final int printerIdCount = printerIds.size();
+ for (int i = 0; i < printerIdCount; i++) {
+ PrinterId printerId = printerIds.get(i);
+ throwIfPrinterIdTampered(printerId);
+ }
+ }
+
+ private void throwIfPrinterIdTampered(PrinterId printerId) {
+ if (printerId == null || printerId.getService() == null
+ || !printerId.getService().equals(mComponentName)) {
+ throw new IllegalArgumentException("Invalid printer id: " + printerId);
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/print/RemotePrintSpooler.java b/services/java/com/android/server/print/RemotePrintSpooler.java
new file mode 100644
index 000000000000..bf2c8e73af6c
--- /dev/null
+++ b/services/java/com/android/server/print/RemotePrintSpooler.java
@@ -0,0 +1,613 @@
+/*
+ * 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.IPrinterDiscoveryObserver;
+import android.print.PrintAttributes;
+import android.print.PrintJobInfo;
+import android.util.Slog;
+import android.util.TimedRemoteCaller;
+
+import libcore.io.IoUtils;
+
+import java.lang.ref.WeakReference;
+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 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 = true;
+
+ private static final long BIND_SPOOLER_SERVICE_TIMEOUT = 10000;
+
+ private final Object mLock = new Object();
+
+ private final GetPrintJobInfosCaller mGetPrintJobInfosCaller = new GetPrintJobInfosCaller();
+
+ private final CreatePrintJobCaller mCreatePrintJobCaller = new CreatePrintJobCaller();
+
+ private final CancelPrintJobCaller mCancelPrintJobCaller = new CancelPrintJobCaller();
+
+ 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;
+
+ public static interface PrintSpoolerCallbacks {
+ public void onPrintJobQueued(PrintJobInfo printJob);
+ public void onStartPrinterDiscovery(IPrinterDiscoveryObserver observer);
+ public void onStopPrinterDiscovery();
+ 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();
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] getPrintJobInfos()");
+ }
+ 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);
+ }
+ return null;
+ }
+
+ public final PrintJobInfo createPrintJob(String printJobName, IPrintClient client,
+ IPrintDocumentAdapter documentAdapter, PrintAttributes attributes, int appId) {
+ throwIfCalledOnMainThread();
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] createPrintJob()");
+ }
+ try {
+ return mCreatePrintJobCaller.createPrintJob(getRemoteInstanceLazy(),
+ printJobName, client, documentAdapter, 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 final boolean cancelPrintJob(int printJobId, int appId) {
+ throwIfCalledOnMainThread();
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] cancelPrintJob()");
+ }
+ try {
+ return mCancelPrintJobCaller.cancelPrintJob(getRemoteInstanceLazy(),
+ 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 final void writePrintJobData(ParcelFileDescriptor fd, int printJobId) {
+ throwIfCalledOnMainThread();
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] writePrintJobData()");
+ }
+ 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 {
+ // We passed the file descriptor across and now the other
+ // side is responsible to close it, so close the local copy.
+ IoUtils.closeQuietly(fd);
+ }
+ }
+
+ public final PrintJobInfo getPrintJobInfo(int printJobId, int appId) {
+ throwIfCalledOnMainThread();
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] getPrintJobInfo()");
+ }
+ 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);
+ }
+ return null;
+ }
+
+ public final boolean setPrintJobState(int printJobId, int state) {
+ throwIfCalledOnMainThread();
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setPrintJobState()");
+ }
+ try {
+ return mSetPrintJobStatusCaller.setPrintJobState(getRemoteInstanceLazy(),
+ printJobId, state);
+ } 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);
+ }
+ return false;
+ }
+
+ public final boolean setPrintJobTag(int printJobId, String tag) {
+ throwIfCalledOnMainThread();
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setPrintJobTag()");
+ }
+ 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);
+ }
+ return false;
+ }
+
+ public final void notifyClientForActivteJobs() {
+ throwIfCalledOnMainThread();
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ }
+ 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);
+ } catch (TimeoutException te) {
+ Slog.e(LOG_TAG, "Error asking for active print job notification.", te);
+ }
+ }
+
+ public final void destroy() {
+ throwIfCalledOnMainThread();
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] destroy()");
+ }
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ unbindLocked();
+ mDestroyed = true;
+ }
+ }
+
+ 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 (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] bindLocked()");
+ }
+
+ mContext.bindServiceAsUser(mIntent, mServiceConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_OOM_MANAGEMENT,
+ 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 */
+ }
+ }
+ }
+
+ private void unbindLocked() {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] unbindLocked()");
+ }
+ clearClientLocked();
+ mRemoteInstance = null;
+ mContext.unbindService(mServiceConnection);
+ }
+
+ 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 CreatePrintJobCaller extends TimedRemoteCaller<PrintJobInfo> {
+ private final IPrintSpoolerCallbacks 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(IPrintSpooler target, String printJobName,
+ IPrintClient client, IPrintDocumentAdapter documentAdapter,
+ PrintAttributes attributes, int appId) throws RemoteException, TimeoutException {
+ final int sequence = onBeforeRemoteCall();
+ target.createPrintJob(printJobName, client, documentAdapter, attributes,
+ mCallback, appId, sequence);
+ return getResultTimed(sequence);
+ }
+ }
+
+ private static final class CancelPrintJobCaller extends TimedRemoteCaller<Boolean> {
+ private final IPrintSpoolerCallbacks 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(IPrintSpooler target, int printJobId,
+ int appId) throws RemoteException, TimeoutException {
+ final int sequence = onBeforeRemoteCall();
+ target.cancelPrintJob(printJobId, mCallback, 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, int 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, int printJobId,
+ int status) throws RemoteException, TimeoutException {
+ final int sequence = onBeforeRemoteCall();
+ target.setPrintJobState(printJobId, status, 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, int 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 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 */
+ }
+ }
+
+ 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);
+ }
+ }
+ }
+
+ @Override
+ public void onStartPrinterDiscovery(IPrinterDiscoveryObserver observer) {
+ RemotePrintSpooler spooler = mWeakSpooler.get();
+ if (spooler != null) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ spooler.mCallbacks.onStartPrinterDiscovery(observer);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
+ @Override
+ public void onStopPrinterDiscovery() throws RemoteException {
+ RemotePrintSpooler spooler = mWeakSpooler.get();
+ if (spooler != null) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ spooler.mCallbacks.onStopPrinterDiscovery();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/print/RemoteSpooler.java b/services/java/com/android/server/print/RemoteSpooler.java
deleted file mode 100644
index fef581818183..000000000000
--- a/services/java/com/android/server/print/RemoteSpooler.java
+++ /dev/null
@@ -1,416 +0,0 @@
-/*
- * 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 */
- }
- }
-} \ No newline at end of file
diff --git a/services/java/com/android/server/print/UserState.java b/services/java/com/android/server/print/UserState.java
new file mode 100644
index 000000000000..5cef4d3ceae7
--- /dev/null
+++ b/services/java/com/android/server/print/UserState.java
@@ -0,0 +1,274 @@
+/*
+ * 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.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.print.IPrinterDiscoveryObserver;
+import android.print.PrintJobInfo;
+import android.printservice.PrintServiceInfo;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.text.TextUtils.SimpleStringSplitter;
+import android.util.Slog;
+
+import com.android.server.print.RemotePrintSpooler.PrintSpoolerCallbacks;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Represents the print state for a user.
+ */
+final class UserState implements PrintSpoolerCallbacks {
+
+ private static final String LOG_TAG = "UserState";
+
+ 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 Map<ComponentName, RemotePrintService> mActiveServices =
+ new HashMap<ComponentName, RemotePrintService>();
+
+ private final List<PrintServiceInfo> mInstalledServices =
+ new ArrayList<PrintServiceInfo>();
+
+ private final Set<ComponentName> mEnabledServices =
+ new HashSet<ComponentName>();
+
+ private final Object mLock;
+
+ private final Context mContext;
+
+ private final int mUserId;
+
+ private final RemotePrintSpooler mSpooler;
+
+ private boolean mDestroyed;
+
+ public UserState(Context context, int userId, Object lock) {
+ mContext = context;
+ mUserId = userId;
+ mLock = lock;
+ mSpooler = new RemotePrintSpooler(context, userId, this);
+ }
+
+ @Override
+ public void onPrintJobQueued(PrintJobInfo printJob) {
+ final RemotePrintService service;
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ ComponentName printServiceName = printJob.getPrinterId().getService();
+ service = mActiveServices.get(printServiceName);
+ }
+ if (service != null) {
+ service.onPrintJobQueued(printJob);
+ }
+ }
+
+ @Override
+ public void onAllPrintJobsForServiceHandled(ComponentName printService) {
+ final RemotePrintService service;
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ service = mActiveServices.get(printService);
+ }
+ if (service != null) {
+ service.onAllPrintJobsHandled();
+ }
+ }
+
+ @Override
+ public void onStartPrinterDiscovery(IPrinterDiscoveryObserver observer) {
+ final List<RemotePrintService> services;
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ if (mActiveServices.isEmpty()) {
+ return;
+ }
+ services = new ArrayList<RemotePrintService>(mActiveServices.values());
+ }
+ final int serviceCount = services.size();
+ for (int i = 0; i < serviceCount; i++) {
+ RemotePrintService service = services.get(i);
+ service.onStartPrinterDiscovery(observer);
+ }
+ }
+
+ @Override
+ public void onStopPrinterDiscovery() {
+ final List<RemotePrintService> services;
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ if (mActiveServices.isEmpty()) {
+ return;
+ }
+ services = new ArrayList<RemotePrintService>(mActiveServices.values());
+ }
+ final int serviceCount = services.size();
+ for (int i = 0; i < serviceCount; i++) {
+ RemotePrintService service = services.get(i);
+ service.onStopPrinterDiscovery();
+ }
+ }
+
+ public void updateIfNeededLocked() {
+ throwIfDestroyedLocked();
+ if (readConfigurationLocked()) {
+ onConfigurationChangedLocked();
+ }
+ }
+
+ public RemotePrintSpooler getSpoolerLocked() {
+ throwIfDestroyedLocked();
+ return mSpooler;
+ }
+
+ public Map<ComponentName, RemotePrintService> getActiveServices() {
+ synchronized(mLock) {
+ throwIfDestroyedLocked();
+ return mActiveServices;
+ }
+ }
+
+ 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();
+ mDestroyed = true;
+ }
+
+ 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>();
+
+ String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+ Settings.Secure.ENABLED_PRINT_SERVICES, 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) {
+ tempEnabledServiceNameSet.add(componentName);
+ }
+ }
+ }
+
+ if (!tempEnabledServiceNameSet.equals(mEnabledServices)) {
+ mEnabledServices.clear();
+ mEnabledServices.addAll(tempEnabledServiceNameSet);
+ return true;
+ }
+
+ return false;
+ }
+
+ 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)) {
+ mActiveServices.put(serviceName, new RemotePrintService(
+ mContext, serviceName, mUserId, mSpooler));
+ }
+ } else {
+ RemotePrintService service = mActiveServices.remove(serviceName);
+ if (service != null) {
+ service.destroy();
+ }
+ }
+ }
+ }
+
+ private void throwIfDestroyedLocked() {
+ if (mDestroyed) {
+ throw new IllegalStateException("Cannot interact with a destroyed instance.");
+ }
+ }
+}
+