blob: 309887a0fe9f06ca8c8ba3db744f597a5b2e4df7 [file] [log] [blame]
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -07001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Svetoslava798c0a2014-05-15 10:47:19 -070017package com.android.printspooler.model;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070018
Philip P. Moltmannb3078c22015-11-23 16:12:39 -080019import android.annotation.FloatRange;
20import android.annotation.NonNull;
21import android.annotation.Nullable;
Philip P. Moltmannd74d1e52016-03-17 16:37:47 -070022import android.annotation.StringRes;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070023import android.app.Service;
24import android.content.ComponentName;
Svetoslava798c0a2014-05-15 10:47:19 -070025import android.content.Context;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070026import android.content.Intent;
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -080027import android.graphics.drawable.Icon;
Svetoslav269403b2013-08-14 17:31:04 -070028import android.os.AsyncTask;
Svetoslavb4fda132013-10-25 18:57:43 -070029import android.os.Bundle;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070030import android.os.IBinder;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070031import android.os.Message;
32import android.os.ParcelFileDescriptor;
33import android.os.RemoteException;
Svetoslav Ganova0027152013-06-25 14:59:53 -070034import android.print.IPrintSpooler;
35import android.print.IPrintSpoolerCallbacks;
Svetoslav Ganov835835e2013-08-04 20:17:52 -070036import android.print.IPrintSpoolerClient;
Svetoslav269403b2013-08-14 17:31:04 -070037import android.print.PageRange;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070038import android.print.PrintAttributes;
Svetoslav269403b2013-08-14 17:31:04 -070039import android.print.PrintAttributes.Margins;
40import android.print.PrintAttributes.MediaSize;
41import android.print.PrintAttributes.Resolution;
Svetoslav269403b2013-08-14 17:31:04 -070042import android.print.PrintDocumentInfo;
Svetoslav2fbd2a72013-09-16 17:53:51 -070043import android.print.PrintJobId;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070044import android.print.PrintJobInfo;
Svetoslav269403b2013-08-14 17:31:04 -070045import android.print.PrintManager;
46import android.print.PrinterId;
Svetoslava76233a2013-09-05 09:38:02 -070047import android.text.TextUtils;
Svetoslav Ganovdd68da22013-09-27 10:48:31 -070048import android.util.ArrayMap;
Svetoslav269403b2013-08-14 17:31:04 -070049import android.util.AtomicFile;
Svetoslav Ganov835835e2013-08-04 20:17:52 -070050import android.util.Log;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070051import android.util.Slog;
Svetoslav269403b2013-08-14 17:31:04 -070052import android.util.Xml;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070053
Chris Wrendcc34fd2015-07-30 14:27:02 -040054import com.android.internal.logging.MetricsLogger;
Svetoslav269403b2013-08-14 17:31:04 -070055import com.android.internal.os.HandlerCaller;
Svetoslav269403b2013-08-14 17:31:04 -070056import com.android.internal.util.FastXmlSerializer;
Svetoslava798c0a2014-05-15 10:47:19 -070057import com.android.printspooler.R;
Philip P. Moltmann853a6f52015-11-03 10:38:56 -080058import com.android.printspooler.util.ApprovedPrintServices;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070059
Svetoslav2fbd2a72013-09-16 17:53:51 -070060import libcore.io.IoUtils;
61
Svetoslav269403b2013-08-14 17:31:04 -070062import org.xmlpull.v1.XmlPullParser;
63import org.xmlpull.v1.XmlPullParserException;
64import org.xmlpull.v1.XmlSerializer;
65
66import java.io.File;
Svetoslav Ganovdd68da22013-09-27 10:48:31 -070067import java.io.FileDescriptor;
Svetoslav269403b2013-08-14 17:31:04 -070068import java.io.FileInputStream;
69import java.io.FileNotFoundException;
70import java.io.FileOutputStream;
71import java.io.IOException;
Svetoslav Ganovdd68da22013-09-27 10:48:31 -070072import java.io.PrintWriter;
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +010073import java.nio.charset.StandardCharsets;
Svetoslav269403b2013-08-14 17:31:04 -070074import java.util.ArrayList;
Svetoslav Ganov835835e2013-08-04 20:17:52 -070075import java.util.List;
Philip P. Moltmann853a6f52015-11-03 10:38:56 -080076import java.util.Set;
Svetoslav Ganov835835e2013-08-04 20:17:52 -070077
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070078/**
79 * Service for exposing some of the {@link PrintSpooler} functionality to
80 * another process.
81 */
82public final class PrintSpoolerService extends Service {
83
Svetoslav269403b2013-08-14 17:31:04 -070084 private static final String LOG_TAG = "PrintSpoolerService";
85
Svetoslavb5f18062013-09-23 18:48:34 -070086 private static final boolean DEBUG_PRINT_JOB_LIFECYCLE = false;
Svetoslav269403b2013-08-14 17:31:04 -070087
Svetoslavc6066792013-09-10 21:08:32 -070088 private static final boolean DEBUG_PERSISTENCE = false;
Svetoslav269403b2013-08-14 17:31:04 -070089
Svet Ganov4237c922014-10-24 12:53:23 -070090 private static final boolean PERSISTENCE_MANAGER_ENABLED = true;
Svetoslav269403b2013-08-14 17:31:04 -070091
Svetoslav Ganov835835e2013-08-04 20:17:52 -070092 private static final long CHECK_ALL_PRINTJOBS_HANDLED_DELAY = 5000;
93
Svetoslav Ganovdd68da22013-09-27 10:48:31 -070094 private static final String PRINT_JOB_FILE_PREFIX = "print_job_";
95
Svetoslav269403b2013-08-14 17:31:04 -070096 private static final String PRINT_FILE_EXTENSION = "pdf";
97
98 private static final Object sLock = new Object();
99
100 private final Object mLock = new Object();
101
Svetoslava798c0a2014-05-15 10:47:19 -0700102 private final List<PrintJobInfo> mPrintJobs = new ArrayList<>();
Svetoslav269403b2013-08-14 17:31:04 -0700103
104 private static PrintSpoolerService sInstance;
105
Svetoslav Ganov835835e2013-08-04 20:17:52 -0700106 private IPrintSpoolerClient mClient;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700107
Svetoslav269403b2013-08-14 17:31:04 -0700108 private HandlerCaller mHandlerCaller;
109
110 private PersistenceManager mPersistanceManager;
111
112 private NotificationController mNotificationController;
113
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -0800114 /** Cache for custom printer icons loaded from the print service */
115 private CustomPrinterIconCache mCustomIconCache;
116
Svetoslav269403b2013-08-14 17:31:04 -0700117 public static PrintSpoolerService peekInstance() {
118 synchronized (sLock) {
119 return sInstance;
120 }
121 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700122
123 @Override
124 public void onCreate() {
125 super.onCreate();
Svetoslav269403b2013-08-14 17:31:04 -0700126 mHandlerCaller = new HandlerCaller(this, getMainLooper(),
127 new HandlerCallerCallback(), false);
128
129 mPersistanceManager = new PersistenceManager();
130 mNotificationController = new NotificationController(PrintSpoolerService.this);
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -0800131 mCustomIconCache = new CustomPrinterIconCache(getCacheDir());
Svetoslav269403b2013-08-14 17:31:04 -0700132
133 synchronized (mLock) {
134 mPersistanceManager.readStateLocked();
135 handleReadPrintJobsLocked();
136 }
137
138 synchronized (sLock) {
139 sInstance = this;
140 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700141 }
142
143 @Override
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -0800144 public void onDestroy() {
145 super.onDestroy();
146 }
147
148 @Override
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700149 public IBinder onBind(Intent intent) {
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700150 return new PrintSpooler();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700151 }
152
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700153 @Override
154 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Philip P. Moltmann853a6f52015-11-03 10:38:56 -0800155 String prefix = (args.length > 0) ? args[0] : "";
156 String tab = " ";
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700157
Philip P. Moltmann853a6f52015-11-03 10:38:56 -0800158 synchronized (mLock) {
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700159 pw.append(prefix).append("print jobs:").println();
160 final int printJobCount = mPrintJobs.size();
161 for (int i = 0; i < printJobCount; i++) {
162 PrintJobInfo printJob = mPrintJobs.get(i);
163 pw.append(prefix).append(tab).append(printJob.toString());
164 pw.println();
165 }
166
167 pw.append(prefix).append("print job files:").println();
168 File[] files = getFilesDir().listFiles();
169 if (files != null) {
170 final int fileCount = files.length;
171 for (int i = 0; i < fileCount; i++) {
172 File file = files[i];
173 if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) {
174 pw.append(prefix).append(tab).append(file.getName()).println();
175 }
176 }
177 }
178 }
Philip P. Moltmann853a6f52015-11-03 10:38:56 -0800179
180 pw.append(prefix).append("approved print services:").println();
181 Set<String> approvedPrintServices = (new ApprovedPrintServices(this)).getApprovedServices();
182 if (approvedPrintServices != null) {
183 for (String approvedService : approvedPrintServices) {
184 pw.append(prefix).append(tab).append(approvedService).println();
185 }
186 }
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700187 }
188
Svetoslav269403b2013-08-14 17:31:04 -0700189 private void sendOnPrintJobQueued(PrintJobInfo printJob) {
190 Message message = mHandlerCaller.obtainMessageO(
191 HandlerCallerCallback.MSG_ON_PRINT_JOB_QUEUED, printJob);
192 mHandlerCaller.executeOrSendMessage(message);
193 }
194
195 private void sendOnAllPrintJobsForServiceHandled(ComponentName service) {
196 Message message = mHandlerCaller.obtainMessageO(
197 HandlerCallerCallback.MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED, service);
198 mHandlerCaller.executeOrSendMessage(message);
199 }
200
201 private void sendOnAllPrintJobsHandled() {
202 Message message = mHandlerCaller.obtainMessage(
203 HandlerCallerCallback.MSG_ON_ALL_PRINT_JOBS_HANDLED);
204 mHandlerCaller.executeOrSendMessage(message);
205 }
206
207 private final class HandlerCallerCallback implements HandlerCaller.Callback {
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700208 public static final int MSG_SET_CLIENT = 1;
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700209 public static final int MSG_ON_PRINT_JOB_QUEUED = 2;
210 public static final int MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED = 3;
211 public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 4;
212 public static final int MSG_CHECK_ALL_PRINTJOBS_HANDLED = 5;
213 public static final int MSG_ON_PRINT_JOB_STATE_CHANGED = 6;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700214
215 @Override
Svetoslav269403b2013-08-14 17:31:04 -0700216 public void executeMessage(Message message) {
Svetoslav Ganov835835e2013-08-04 20:17:52 -0700217 switch (message.what) {
218 case MSG_SET_CLIENT: {
Svetoslav269403b2013-08-14 17:31:04 -0700219 synchronized (mLock) {
220 mClient = (IPrintSpoolerClient) message.obj;
221 if (mClient != null) {
222 Message msg = mHandlerCaller.obtainMessage(
223 HandlerCallerCallback.MSG_CHECK_ALL_PRINTJOBS_HANDLED);
224 mHandlerCaller.sendMessageDelayed(msg,
225 CHECK_ALL_PRINTJOBS_HANDLED_DELAY);
226 }
Svetoslav Ganov835835e2013-08-04 20:17:52 -0700227 }
228 } break;
229
Svetoslav Ganov835835e2013-08-04 20:17:52 -0700230 case MSG_ON_PRINT_JOB_QUEUED: {
231 PrintJobInfo printJob = (PrintJobInfo) message.obj;
232 if (mClient != null) {
233 try {
234 mClient.onPrintJobQueued(printJob);
235 } catch (RemoteException re) {
236 Slog.e(LOG_TAG, "Error notify for a queued print job.", re);
237 }
238 }
239 } break;
240
241 case MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED: {
242 ComponentName service = (ComponentName) message.obj;
243 if (mClient != null) {
244 try {
245 mClient.onAllPrintJobsForServiceHandled(service);
246 } catch (RemoteException re) {
247 Slog.e(LOG_TAG, "Error notify for all print jobs per service"
248 + " handled.", re);
249 }
250 }
251 } break;
252
253 case MSG_ON_ALL_PRINT_JOBS_HANDLED: {
254 if (mClient != null) {
255 try {
256 mClient.onAllPrintJobsHandled();
257 } catch (RemoteException re) {
258 Slog.e(LOG_TAG, "Error notify for all print job handled.", re);
259 }
260 }
261 } break;
262
Svetoslav Ganov835835e2013-08-04 20:17:52 -0700263 case MSG_CHECK_ALL_PRINTJOBS_HANDLED: {
Svetoslav269403b2013-08-14 17:31:04 -0700264 checkAllPrintJobsHandled();
265 } break;
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700266
267 case MSG_ON_PRINT_JOB_STATE_CHANGED: {
268 if (mClient != null) {
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700269 PrintJobInfo printJob = (PrintJobInfo) message.obj;
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700270 try {
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700271 mClient.onPrintJobStateChanged(printJob);
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700272 } catch (RemoteException re) {
273 Slog.e(LOG_TAG, "Error notify for print job state change.", re);
274 }
275 }
276 } break;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700277 }
278 }
279 }
Svetoslav269403b2013-08-14 17:31:04 -0700280
281 public List<PrintJobInfo> getPrintJobInfos(ComponentName componentName,
282 int state, int appId) {
283 List<PrintJobInfo> foundPrintJobs = null;
284 synchronized (mLock) {
285 final int printJobCount = mPrintJobs.size();
286 for (int i = 0; i < printJobCount; i++) {
287 PrintJobInfo printJob = mPrintJobs.get(i);
288 PrinterId printerId = printJob.getPrinterId();
289 final boolean sameComponent = (componentName == null
290 || (printerId != null
291 && componentName.equals(printerId.getServiceName())));
292 final boolean sameAppId = appId == PrintManager.APP_ID_ANY
293 || printJob.getAppId() == appId;
294 final boolean sameState = (state == printJob.getState())
295 || (state == PrintJobInfo.STATE_ANY)
296 || (state == PrintJobInfo.STATE_ANY_VISIBLE_TO_CLIENTS
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700297 && isStateVisibleToUser(printJob.getState()))
298 || (state == PrintJobInfo.STATE_ANY_ACTIVE
Svetoslav Ganov9b6d3a12013-10-12 12:35:41 -0700299 && isActiveState(printJob.getState()))
300 || (state == PrintJobInfo.STATE_ANY_SCHEDULED
301 && isScheduledState(printJob.getState()));
Svetoslav269403b2013-08-14 17:31:04 -0700302 if (sameComponent && sameAppId && sameState) {
303 if (foundPrintJobs == null) {
Svetoslava798c0a2014-05-15 10:47:19 -0700304 foundPrintJobs = new ArrayList<>();
Svetoslav269403b2013-08-14 17:31:04 -0700305 }
306 foundPrintJobs.add(printJob);
307 }
308 }
309 }
310 return foundPrintJobs;
311 }
312
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700313 private boolean isStateVisibleToUser(int state) {
314 return (isActiveState(state) && (state == PrintJobInfo.STATE_FAILED
Svetoslav2fbd2a72013-09-16 17:53:51 -0700315 || state == PrintJobInfo.STATE_COMPLETED || state == PrintJobInfo.STATE_CANCELED
316 || state == PrintJobInfo.STATE_BLOCKED));
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700317 }
318
Svetoslav2fbd2a72013-09-16 17:53:51 -0700319 public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId) {
Svetoslav269403b2013-08-14 17:31:04 -0700320 synchronized (mLock) {
321 final int printJobCount = mPrintJobs.size();
322 for (int i = 0; i < printJobCount; i++) {
323 PrintJobInfo printJob = mPrintJobs.get(i);
Svetoslav2fbd2a72013-09-16 17:53:51 -0700324 if (printJob.getId().equals(printJobId)
Svetoslav269403b2013-08-14 17:31:04 -0700325 && (appId == PrintManager.APP_ID_ANY
326 || appId == printJob.getAppId())) {
327 return printJob;
328 }
329 }
330 return null;
331 }
332 }
333
Svetoslav2fbd2a72013-09-16 17:53:51 -0700334 public void createPrintJob(PrintJobInfo printJob) {
Svetoslav269403b2013-08-14 17:31:04 -0700335 synchronized (mLock) {
Svetoslav269403b2013-08-14 17:31:04 -0700336 addPrintJobLocked(printJob);
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700337 setPrintJobState(printJob.getId(), PrintJobInfo.STATE_CREATED, null);
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700338
339 Message message = mHandlerCaller.obtainMessageO(
340 HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
341 printJob);
342 mHandlerCaller.executeOrSendMessage(message);
Svetoslav269403b2013-08-14 17:31:04 -0700343 }
344 }
345
346 private void handleReadPrintJobsLocked() {
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700347 // Make a map with the files for a print job since we may have
348 // to delete some. One example of getting orphan files if the
349 // spooler crashes while constructing a print job. We do not
350 // persist partially populated print jobs under construction to
351 // avoid special handling for various attributes missing.
352 ArrayMap<PrintJobId, File> fileForJobMap = null;
353 File[] files = getFilesDir().listFiles();
354 if (files != null) {
355 final int fileCount = files.length;
356 for (int i = 0; i < fileCount; i++) {
357 File file = files[i];
358 if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) {
359 if (fileForJobMap == null) {
360 fileForJobMap = new ArrayMap<PrintJobId, File>();
361 }
Svetoslav Ganov2b40c832013-10-04 08:49:03 -0700362 String printJobIdString = file.getName().substring(
363 PRINT_JOB_FILE_PREFIX.length(),
364 file.getName().indexOf('.'));
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700365 PrintJobId printJobId = PrintJobId.unflattenFromString(
366 printJobIdString);
367 fileForJobMap.put(printJobId, file);
368 }
369 }
370 }
371
Svetoslav269403b2013-08-14 17:31:04 -0700372 final int printJobCount = mPrintJobs.size();
373 for (int i = 0; i < printJobCount; i++) {
374 PrintJobInfo printJob = mPrintJobs.get(i);
375
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700376 // We want to have only the orphan files at the end.
377 if (fileForJobMap != null) {
378 fileForJobMap.remove(printJob.getId());
379 }
380
Svetoslav269403b2013-08-14 17:31:04 -0700381 switch (printJob.getState()) {
382 case PrintJobInfo.STATE_QUEUED:
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700383 case PrintJobInfo.STATE_STARTED:
384 case PrintJobInfo.STATE_BLOCKED: {
385 // We have a print job that was queued or started or blocked in
386 // the past but the device battery died or a crash occurred. In
387 // this case we assume the print job failed and let the user
388 // decide whether to restart the job or just cancel it.
Svetoslav269403b2013-08-14 17:31:04 -0700389 setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED,
390 getString(R.string.no_connection_to_printer));
Svetoslav2fbd2a72013-09-16 17:53:51 -0700391 } break;
Svetoslav269403b2013-08-14 17:31:04 -0700392 }
393 }
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700394
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700395 if (!mPrintJobs.isEmpty()) {
396 // Update the notification.
397 mNotificationController.onUpdateNotifications(mPrintJobs);
398 }
399
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700400 // Delete the orphan files.
401 if (fileForJobMap != null) {
402 final int orphanFileCount = fileForJobMap.size();
403 for (int i = 0; i < orphanFileCount; i++) {
404 File file = fileForJobMap.valueAt(i);
405 file.delete();
406 }
407 }
Svetoslav269403b2013-08-14 17:31:04 -0700408 }
409
410 public void checkAllPrintJobsHandled() {
411 synchronized (mLock) {
412 if (!hasActivePrintJobsLocked()) {
413 notifyOnAllPrintJobsHandled();
414 }
415 }
416 }
417
Svetoslav2fbd2a72013-09-16 17:53:51 -0700418 public void writePrintJobData(final ParcelFileDescriptor fd, final PrintJobId printJobId) {
Svetoslav269403b2013-08-14 17:31:04 -0700419 final PrintJobInfo printJob;
420 synchronized (mLock) {
421 printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
422 }
423 new AsyncTask<Void, Void, Void>() {
424 @Override
425 protected Void doInBackground(Void... params) {
426 FileInputStream in = null;
427 FileOutputStream out = null;
428 try {
429 if (printJob != null) {
Svetoslava798c0a2014-05-15 10:47:19 -0700430 File file = generateFileForPrintJob(PrintSpoolerService.this, printJobId);
Svetoslav269403b2013-08-14 17:31:04 -0700431 in = new FileInputStream(file);
432 out = new FileOutputStream(fd.getFileDescriptor());
433 }
434 final byte[] buffer = new byte[8192];
435 while (true) {
436 final int readByteCount = in.read(buffer);
437 if (readByteCount < 0) {
438 return null;
439 }
440 out.write(buffer, 0, readByteCount);
441 }
442 } catch (FileNotFoundException fnfe) {
443 Log.e(LOG_TAG, "Error writing print job data!", fnfe);
444 } catch (IOException ioe) {
445 Log.e(LOG_TAG, "Error writing print job data!", ioe);
446 } finally {
447 IoUtils.closeQuietly(in);
448 IoUtils.closeQuietly(out);
449 IoUtils.closeQuietly(fd);
450 }
451 Log.i(LOG_TAG, "[END WRITE]");
452 return null;
453 }
454 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
455 }
456
Svetoslava798c0a2014-05-15 10:47:19 -0700457 public static File generateFileForPrintJob(Context context, PrintJobId printJobId) {
458 return new File(context.getFilesDir(), PRINT_JOB_FILE_PREFIX
Svetoslav2fbd2a72013-09-16 17:53:51 -0700459 + printJobId.flattenToString() + "." + PRINT_FILE_EXTENSION);
Svetoslav269403b2013-08-14 17:31:04 -0700460 }
461
462 private void addPrintJobLocked(PrintJobInfo printJob) {
463 mPrintJobs.add(printJob);
464 if (DEBUG_PRINT_JOB_LIFECYCLE) {
465 Slog.i(LOG_TAG, "[ADD] " + printJob);
466 }
467 }
468
Svetoslav2fbd2a72013-09-16 17:53:51 -0700469 private void removeObsoletePrintJobs() {
470 synchronized (mLock) {
Svetoslav885810d2013-11-14 17:59:14 -0800471 boolean persistState = false;
Svetoslav2fbd2a72013-09-16 17:53:51 -0700472 final int printJobCount = mPrintJobs.size();
473 for (int i = printJobCount - 1; i >= 0; i--) {
474 PrintJobInfo printJob = mPrintJobs.get(i);
475 if (isObsoleteState(printJob.getState())) {
476 mPrintJobs.remove(i);
477 if (DEBUG_PRINT_JOB_LIFECYCLE) {
478 Slog.i(LOG_TAG, "[REMOVE] " + printJob.getId().flattenToString());
479 }
480 removePrintJobFileLocked(printJob.getId());
Svetoslav885810d2013-11-14 17:59:14 -0800481 persistState = true;
Svetoslav2fbd2a72013-09-16 17:53:51 -0700482 }
483 }
Svetoslav885810d2013-11-14 17:59:14 -0800484 if (persistState) {
485 mPersistanceManager.writeStateLocked();
486 }
Svetoslav2fbd2a72013-09-16 17:53:51 -0700487 }
488 }
489
490 private void removePrintJobFileLocked(PrintJobId printJobId) {
Svetoslava798c0a2014-05-15 10:47:19 -0700491 File file = generateFileForPrintJob(PrintSpoolerService.this, printJobId);
Svetoslav2fbd2a72013-09-16 17:53:51 -0700492 if (file.exists()) {
493 file.delete();
494 if (DEBUG_PRINT_JOB_LIFECYCLE) {
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700495 Slog.i(LOG_TAG, "[REMOVE FILE FOR] " + printJobId);
Svetoslav2fbd2a72013-09-16 17:53:51 -0700496 }
497 }
498 }
499
Philip P. Moltmann4bd8fac2016-04-21 15:17:58 -0700500 /**
501 * Notify all interested parties that a print job has been updated.
502 *
503 * @param printJob The updated print job.
504 */
505 private void notifyPrintJobUpdated(PrintJobInfo printJob) {
506 Message message = mHandlerCaller.obtainMessageO(
507 HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
508 printJob);
509 mHandlerCaller.executeOrSendMessage(message);
510
511 mNotificationController.onUpdateNotifications(mPrintJobs);
512 }
513
Svetoslav2fbd2a72013-09-16 17:53:51 -0700514 public boolean setPrintJobState(PrintJobId printJobId, int state, String error) {
Svetoslav269403b2013-08-14 17:31:04 -0700515 boolean success = false;
516
517 synchronized (mLock) {
518 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
519 if (printJob != null) {
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700520 final int oldState = printJob.getState();
521 if (oldState == state) {
522 return false;
523 }
524
Svetoslav269403b2013-08-14 17:31:04 -0700525 success = true;
526
527 printJob.setState(state);
Philip P. Moltmannb3078c22015-11-23 16:12:39 -0800528 printJob.setStatus(error);
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700529 printJob.setCancelling(false);
Svetoslav269403b2013-08-14 17:31:04 -0700530
531 if (DEBUG_PRINT_JOB_LIFECYCLE) {
532 Slog.i(LOG_TAG, "[STATE CHANGED] " + printJob);
533 }
534
Chris Wrendcc34fd2015-07-30 14:27:02 -0400535 MetricsLogger.histogram(this, "print_job_state", state);
Svetoslav269403b2013-08-14 17:31:04 -0700536 switch (state) {
537 case PrintJobInfo.STATE_COMPLETED:
538 case PrintJobInfo.STATE_CANCELED:
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700539 mPrintJobs.remove(printJob);
Svetoslav2fbd2a72013-09-16 17:53:51 -0700540 removePrintJobFileLocked(printJob.getId());
Svetoslav269403b2013-08-14 17:31:04 -0700541 // $fall-through$
542
543 case PrintJobInfo.STATE_FAILED: {
544 PrinterId printerId = printJob.getPrinterId();
545 if (printerId != null) {
546 ComponentName service = printerId.getServiceName();
547 if (!hasActivePrintJobsForServiceLocked(service)) {
548 sendOnAllPrintJobsForServiceHandled(service);
549 }
550 }
551 } break;
552
553 case PrintJobInfo.STATE_QUEUED: {
554 sendOnPrintJobQueued(new PrintJobInfo(printJob));
555 } break;
556 }
557
558 if (shouldPersistPrintJob(printJob)) {
559 mPersistanceManager.writeStateLocked();
560 }
561
562 if (!hasActivePrintJobsLocked()) {
563 notifyOnAllPrintJobsHandled();
564 }
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700565
Philip P. Moltmann4bd8fac2016-04-21 15:17:58 -0700566 notifyPrintJobUpdated(printJob);
Svetoslav269403b2013-08-14 17:31:04 -0700567 }
568 }
569
570 return success;
571 }
572
Philip P. Moltmannb3078c22015-11-23 16:12:39 -0800573 /**
574 * Set the progress for a print job.
575 *
576 * @param printJobId ID of the print job to update
577 * @param progress the new progress
578 */
579 public void setProgress(@NonNull PrintJobId printJobId,
580 @FloatRange(from=0.0, to=1.0) float progress) {
581 synchronized (mLock) {
582 getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY).setProgress(progress);
583
584 mNotificationController.onUpdateNotifications(mPrintJobs);
585 }
586 }
587
Philip P. Moltmannd74d1e52016-03-17 16:37:47 -0700588 /**
589 * Set the status for a print job.
590 *
591 * @param printJobId ID of the print job to update
592 * @param status the new status
593 */
594 public void setStatus(@NonNull PrintJobId printJobId, @Nullable CharSequence status) {
595 synchronized (mLock) {
Philip P. Moltmann4bd8fac2016-04-21 15:17:58 -0700596 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
Philip P. Moltmannb3078c22015-11-23 16:12:39 -0800597
Philip P. Moltmann4bd8fac2016-04-21 15:17:58 -0700598 if (printJob != null) {
599 printJob.setStatus(status);
600 notifyPrintJobUpdated(printJob);
601 }
Philip P. Moltmannd74d1e52016-03-17 16:37:47 -0700602 }
603 }
604
605 /**
606 * Set the status for a print job.
607 *
608 * @param printJobId ID of the print job to update
609 * @param status the new status as a string resource
610 * @param appPackageName app package the resource belongs to
611 */
612 public void setStatus(@NonNull PrintJobId printJobId, @StringRes int status,
613 @Nullable CharSequence appPackageName) {
614 synchronized (mLock) {
Philip P. Moltmann4bd8fac2016-04-21 15:17:58 -0700615 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
Philip P. Moltmannd74d1e52016-03-17 16:37:47 -0700616
Philip P. Moltmann4bd8fac2016-04-21 15:17:58 -0700617 if (printJob != null) {
618 printJob.setStatus(status, appPackageName);
619 notifyPrintJobUpdated(printJob);
620 }
Philip P. Moltmannd74d1e52016-03-17 16:37:47 -0700621 }
622 }
Philip P. Moltmannb3078c22015-11-23 16:12:39 -0800623
Svetoslav269403b2013-08-14 17:31:04 -0700624 public boolean hasActivePrintJobsLocked() {
625 final int printJobCount = mPrintJobs.size();
626 for (int i = 0; i < printJobCount; i++) {
627 PrintJobInfo printJob = mPrintJobs.get(i);
628 if (isActiveState(printJob.getState())) {
629 return true;
630 }
631 }
632 return false;
633 }
634
635 public boolean hasActivePrintJobsForServiceLocked(ComponentName service) {
636 final int printJobCount = mPrintJobs.size();
637 for (int i = 0; i < printJobCount; i++) {
638 PrintJobInfo printJob = mPrintJobs.get(i);
Svetoslav75d28502013-11-04 18:25:05 -0800639 if (isActiveState(printJob.getState()) && printJob.getPrinterId() != null
Svetoslav269403b2013-08-14 17:31:04 -0700640 && printJob.getPrinterId().getServiceName().equals(service)) {
641 return true;
642 }
643 }
644 return false;
645 }
646
Svetoslav2fbd2a72013-09-16 17:53:51 -0700647 private boolean isObsoleteState(int printJobState) {
Svetoslava798c0a2014-05-15 10:47:19 -0700648 return (isTerminalState(printJobState)
Svetoslav2fbd2a72013-09-16 17:53:51 -0700649 || printJobState == PrintJobInfo.STATE_QUEUED);
650 }
651
Svetoslav Ganov9b6d3a12013-10-12 12:35:41 -0700652 private boolean isScheduledState(int printJobState) {
653 return printJobState == PrintJobInfo.STATE_QUEUED
654 || printJobState == PrintJobInfo.STATE_STARTED
655 || printJobState == PrintJobInfo.STATE_BLOCKED;
656 }
657
Svetoslav269403b2013-08-14 17:31:04 -0700658 private boolean isActiveState(int printJobState) {
659 return printJobState == PrintJobInfo.STATE_CREATED
660 || printJobState == PrintJobInfo.STATE_QUEUED
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700661 || printJobState == PrintJobInfo.STATE_STARTED
662 || printJobState == PrintJobInfo.STATE_BLOCKED;
Svetoslav269403b2013-08-14 17:31:04 -0700663 }
664
Svetoslava798c0a2014-05-15 10:47:19 -0700665 private boolean isTerminalState(int printJobState) {
Svetoslav2fbd2a72013-09-16 17:53:51 -0700666 return printJobState == PrintJobInfo.STATE_COMPLETED
667 || printJobState == PrintJobInfo.STATE_CANCELED;
668 }
669
670 public boolean setPrintJobTag(PrintJobId printJobId, String tag) {
Svetoslav269403b2013-08-14 17:31:04 -0700671 synchronized (mLock) {
672 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
673 if (printJob != null) {
674 String printJobTag = printJob.getTag();
675 if (printJobTag == null) {
676 if (tag == null) {
677 return false;
678 }
679 } else if (printJobTag.equals(tag)) {
680 return false;
681 }
682 printJob.setTag(tag);
683 if (shouldPersistPrintJob(printJob)) {
684 mPersistanceManager.writeStateLocked();
685 }
686 return true;
687 }
688 }
689 return false;
690 }
691
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700692 public void setPrintJobCancelling(PrintJobId printJobId, boolean cancelling) {
693 synchronized (mLock) {
694 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
695 if (printJob != null) {
696 printJob.setCancelling(cancelling);
697 if (shouldPersistPrintJob(printJob)) {
698 mPersistanceManager.writeStateLocked();
699 }
700 mNotificationController.onUpdateNotifications(mPrintJobs);
701
702 Message message = mHandlerCaller.obtainMessageO(
703 HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
704 printJob);
705 mHandlerCaller.executeOrSendMessage(message);
706 }
707 }
708 }
709
Svetoslava798c0a2014-05-15 10:47:19 -0700710 public void updatePrintJobUserConfigurableOptionsNoPersistence(PrintJobInfo printJob) {
Svetoslav269403b2013-08-14 17:31:04 -0700711 synchronized (mLock) {
Svetoslava798c0a2014-05-15 10:47:19 -0700712 final int printJobCount = mPrintJobs.size();
713 for (int i = 0; i < printJobCount; i++) {
714 PrintJobInfo cachedPrintJob = mPrintJobs.get(i);
715 if (cachedPrintJob.getId().equals(printJob.getId())) {
716 cachedPrintJob.setPrinterId(printJob.getPrinterId());
717 cachedPrintJob.setPrinterName(printJob.getPrinterName());
718 cachedPrintJob.setCopies(printJob.getCopies());
719 cachedPrintJob.setDocumentInfo(printJob.getDocumentInfo());
720 cachedPrintJob.setPages(printJob.getPages());
721 cachedPrintJob.setAttributes(printJob.getAttributes());
722 cachedPrintJob.setAdvancedOptions(printJob.getAdvancedOptions());
723 return;
724 }
Svetoslav269403b2013-08-14 17:31:04 -0700725 }
Svetoslava798c0a2014-05-15 10:47:19 -0700726 throw new IllegalArgumentException("No print job with id:" + printJob.getId());
Svetoslav269403b2013-08-14 17:31:04 -0700727 }
728 }
729
730 private boolean shouldPersistPrintJob(PrintJobInfo printJob) {
731 return printJob.getState() >= PrintJobInfo.STATE_QUEUED;
732 }
733
734 private void notifyOnAllPrintJobsHandled() {
735 // This has to run on the tread that is persisting the current state
736 // since this call may result in the system unbinding from the spooler
737 // and as a result the spooler process may get killed before the write
738 // completes.
739 new AsyncTask<Void, Void, Void>() {
740 @Override
741 protected Void doInBackground(Void... params) {
742 sendOnAllPrintJobsHandled();
743 return null;
744 }
745 }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
746 }
747
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -0800748 /**
749 * Handle that a custom icon for a printer was loaded.
750 *
751 * @param printerId the id of the printer the icon belongs to
752 * @param icon the icon that was loaded
753 * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon()
754 */
755 public void onCustomPrinterIconLoaded(PrinterId printerId, Icon icon) {
756 mCustomIconCache.onCustomPrinterIconLoaded(printerId, icon);
757 }
758
759 /**
760 * Get the custom icon for a printer. If the icon is not cached, the icon is
761 * requested asynchronously. Once it is available the printer is updated.
762 *
763 * @param printerId the id of the printer the icon should be loaded for
764 * @return the custom icon to be used for the printer or null if the icon is
765 * not yet available
766 * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon()
767 */
768 public Icon getCustomPrinterIcon(PrinterId printerId) {
769 return mCustomIconCache.getIcon(printerId);
770 }
771
772 /**
773 * Clear the custom printer icon cache.
774 */
775 public void clearCustomPrinterIconCache() {
776 mCustomIconCache.clear();
777 }
778
Svetoslav269403b2013-08-14 17:31:04 -0700779 private final class PersistenceManager {
780 private static final String PERSIST_FILE_NAME = "print_spooler_state.xml";
781
782 private static final String TAG_SPOOLER = "spooler";
783 private static final String TAG_JOB = "job";
784
785 private static final String TAG_PRINTER_ID = "printerId";
786 private static final String TAG_PAGE_RANGE = "pageRange";
787 private static final String TAG_ATTRIBUTES = "attributes";
788 private static final String TAG_DOCUMENT_INFO = "documentInfo";
789
790 private static final String ATTR_ID = "id";
791 private static final String ATTR_LABEL = "label";
Svetoslav773f54d2013-09-03 14:01:43 -0700792 private static final String ATTR_LABEL_RES_ID = "labelResId";
793 private static final String ATTR_PACKAGE_NAME = "packageName";
Svetoslav269403b2013-08-14 17:31:04 -0700794 private static final String ATTR_STATE = "state";
795 private static final String ATTR_APP_ID = "appId";
Svetoslav269403b2013-08-14 17:31:04 -0700796 private static final String ATTR_TAG = "tag";
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700797 private static final String ATTR_CREATION_TIME = "creationTime";
Svetoslav269403b2013-08-14 17:31:04 -0700798 private static final String ATTR_COPIES = "copies";
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700799 private static final String ATTR_PRINTER_NAME = "printerName";
800 private static final String ATTR_STATE_REASON = "stateReason";
Philip P. Moltmannb3078c22015-11-23 16:12:39 -0800801 private static final String ATTR_STATUS = "status";
802 private static final String ATTR_PROGRESS = "progress";
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700803 private static final String ATTR_CANCELLING = "cancelling";
Svetoslav269403b2013-08-14 17:31:04 -0700804
Svetoslavb4fda132013-10-25 18:57:43 -0700805 private static final String TAG_ADVANCED_OPTIONS = "advancedOptions";
806 private static final String TAG_ADVANCED_OPTION = "advancedOption";
807 private static final String ATTR_KEY = "key";
808 private static final String ATTR_TYPE = "type";
809 private static final String ATTR_VALUE = "value";
810 private static final String TYPE_STRING = "string";
811 private static final String TYPE_INT = "int";
812
Svetoslav269403b2013-08-14 17:31:04 -0700813 private static final String TAG_MEDIA_SIZE = "mediaSize";
814 private static final String TAG_RESOLUTION = "resolution";
815 private static final String TAG_MARGINS = "margins";
Svetoslav269403b2013-08-14 17:31:04 -0700816
Svetoslav269403b2013-08-14 17:31:04 -0700817 private static final String ATTR_COLOR_MODE = "colorMode";
Svetoslav948c9a62015-02-02 19:47:04 -0800818 private static final String ATTR_DUPLEX_MODE = "duplexMode";
Svetoslav269403b2013-08-14 17:31:04 -0700819
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700820 private static final String ATTR_LOCAL_ID = "localId";
Svetoslav269403b2013-08-14 17:31:04 -0700821 private static final String ATTR_SERVICE_NAME = "serviceName";
822
823 private static final String ATTR_WIDTH_MILS = "widthMils";
824 private static final String ATTR_HEIGHT_MILS = "heightMils";
825
826 private static final String ATTR_HORIZONTAL_DPI = "horizontalDip";
827 private static final String ATTR_VERTICAL_DPI = "verticalDpi";
828
829 private static final String ATTR_LEFT_MILS = "leftMils";
830 private static final String ATTR_TOP_MILS = "topMils";
831 private static final String ATTR_RIGHT_MILS = "rightMils";
832 private static final String ATTR_BOTTOM_MILS = "bottomMils";
833
834 private static final String ATTR_START = "start";
835 private static final String ATTR_END = "end";
836
837 private static final String ATTR_NAME = "name";
838 private static final String ATTR_PAGE_COUNT = "pageCount";
839 private static final String ATTR_CONTENT_TYPE = "contentType";
Svetoslav Ganov7d7888d2013-10-12 13:18:12 -0700840 private static final String ATTR_DATA_SIZE = "dataSize";
Svetoslav269403b2013-08-14 17:31:04 -0700841
842 private final AtomicFile mStatePersistFile;
843
844 private boolean mWriteStateScheduled;
845
846 private PersistenceManager() {
847 mStatePersistFile = new AtomicFile(new File(getFilesDir(),
848 PERSIST_FILE_NAME));
849 }
850
851 public void writeStateLocked() {
Svet Ganov4237c922014-10-24 12:53:23 -0700852 if (!PERSISTENCE_MANAGER_ENABLED) {
Svetoslav269403b2013-08-14 17:31:04 -0700853 return;
854 }
855 if (mWriteStateScheduled) {
856 return;
857 }
858 mWriteStateScheduled = true;
859 new AsyncTask<Void, Void, Void>() {
860 @Override
861 protected Void doInBackground(Void... params) {
862 synchronized (mLock) {
863 mWriteStateScheduled = false;
864 doWriteStateLocked();
865 }
866 return null;
867 }
868 }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
869 }
870
871 private void doWriteStateLocked() {
872 if (DEBUG_PERSISTENCE) {
873 Log.i(LOG_TAG, "[PERSIST START]");
874 }
875 FileOutputStream out = null;
876 try {
877 out = mStatePersistFile.startWrite();
878
879 XmlSerializer serializer = new FastXmlSerializer();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100880 serializer.setOutput(out, StandardCharsets.UTF_8.name());
Svetoslav269403b2013-08-14 17:31:04 -0700881 serializer.startDocument(null, true);
882 serializer.startTag(null, TAG_SPOOLER);
883
884 List<PrintJobInfo> printJobs = mPrintJobs;
885
886 final int printJobCount = printJobs.size();
887 for (int j = 0; j < printJobCount; j++) {
888 PrintJobInfo printJob = printJobs.get(j);
889
Svetoslav885810d2013-11-14 17:59:14 -0800890 if (!shouldPersistPrintJob(printJob)) {
891 continue;
892 }
893
Svetoslav269403b2013-08-14 17:31:04 -0700894 serializer.startTag(null, TAG_JOB);
895
Svetoslav2fbd2a72013-09-16 17:53:51 -0700896 serializer.attribute(null, ATTR_ID, printJob.getId().flattenToString());
Svetoslav269403b2013-08-14 17:31:04 -0700897 serializer.attribute(null, ATTR_LABEL, printJob.getLabel().toString());
898 serializer.attribute(null, ATTR_STATE, String.valueOf(printJob.getState()));
899 serializer.attribute(null, ATTR_APP_ID, String.valueOf(printJob.getAppId()));
Svetoslav269403b2013-08-14 17:31:04 -0700900 String tag = printJob.getTag();
901 if (tag != null) {
902 serializer.attribute(null, ATTR_TAG, tag);
903 }
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700904 serializer.attribute(null, ATTR_CREATION_TIME, String.valueOf(
905 printJob.getCreationTime()));
Svetoslav269403b2013-08-14 17:31:04 -0700906 serializer.attribute(null, ATTR_COPIES, String.valueOf(printJob.getCopies()));
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700907 String printerName = printJob.getPrinterName();
908 if (!TextUtils.isEmpty(printerName)) {
909 serializer.attribute(null, ATTR_PRINTER_NAME, printerName);
910 }
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700911 serializer.attribute(null, ATTR_CANCELLING, String.valueOf(
912 printJob.isCancelling()));
Svetoslav269403b2013-08-14 17:31:04 -0700913
Philip P. Moltmannb3078c22015-11-23 16:12:39 -0800914 float progress = printJob.getProgress();
915 if (progress != Float.NaN) {
916 serializer.attribute(null, ATTR_PROGRESS, String.valueOf(progress));
917 }
918
Philip P. Moltmannd74d1e52016-03-17 16:37:47 -0700919 CharSequence status = printJob.getStatus(getPackageManager());
Philip P. Moltmannb3078c22015-11-23 16:12:39 -0800920 if (!TextUtils.isEmpty(status)) {
921 serializer.attribute(null, ATTR_STATUS, status.toString());
922 }
923
Svetoslav269403b2013-08-14 17:31:04 -0700924 PrinterId printerId = printJob.getPrinterId();
925 if (printerId != null) {
926 serializer.startTag(null, TAG_PRINTER_ID);
927 serializer.attribute(null, ATTR_LOCAL_ID, printerId.getLocalId());
928 serializer.attribute(null, ATTR_SERVICE_NAME, printerId.getServiceName()
929 .flattenToString());
930 serializer.endTag(null, TAG_PRINTER_ID);
931 }
932
933 PageRange[] pages = printJob.getPages();
934 if (pages != null) {
935 for (int i = 0; i < pages.length; i++) {
936 serializer.startTag(null, TAG_PAGE_RANGE);
937 serializer.attribute(null, ATTR_START, String.valueOf(
938 pages[i].getStart()));
939 serializer.attribute(null, ATTR_END, String.valueOf(
940 pages[i].getEnd()));
941 serializer.endTag(null, TAG_PAGE_RANGE);
942 }
943 }
944
945 PrintAttributes attributes = printJob.getAttributes();
946 if (attributes != null) {
947 serializer.startTag(null, TAG_ATTRIBUTES);
948
Svetoslav269403b2013-08-14 17:31:04 -0700949 final int colorMode = attributes.getColorMode();
950 serializer.attribute(null, ATTR_COLOR_MODE,
951 String.valueOf(colorMode));
952
Svetoslav948c9a62015-02-02 19:47:04 -0800953 final int duplexMode = attributes.getDuplexMode();
954 serializer.attribute(null, ATTR_DUPLEX_MODE,
955 String.valueOf(duplexMode));
956
Svetoslav269403b2013-08-14 17:31:04 -0700957 MediaSize mediaSize = attributes.getMediaSize();
958 if (mediaSize != null) {
959 serializer.startTag(null, TAG_MEDIA_SIZE);
960 serializer.attribute(null, ATTR_ID, mediaSize.getId());
Svetoslav269403b2013-08-14 17:31:04 -0700961 serializer.attribute(null, ATTR_WIDTH_MILS, String.valueOf(
962 mediaSize.getWidthMils()));
963 serializer.attribute(null, ATTR_HEIGHT_MILS, String.valueOf(
964 mediaSize.getHeightMils()));
Svetoslava76233a2013-09-05 09:38:02 -0700965 // We prefer to store only the package name and
966 // resource id and fallback to the label.
967 if (!TextUtils.isEmpty(mediaSize.mPackageName)
Svetoslav773f54d2013-09-03 14:01:43 -0700968 && mediaSize.mLabelResId > 0) {
969 serializer.attribute(null, ATTR_PACKAGE_NAME,
970 mediaSize.mPackageName);
971 serializer.attribute(null, ATTR_LABEL_RES_ID,
972 String.valueOf(mediaSize.mLabelResId));
973 } else {
974 serializer.attribute(null, ATTR_LABEL,
975 mediaSize.getLabel(getPackageManager()));
976 }
Svetoslav269403b2013-08-14 17:31:04 -0700977 serializer.endTag(null, TAG_MEDIA_SIZE);
978 }
979
980 Resolution resolution = attributes.getResolution();
981 if (resolution != null) {
982 serializer.startTag(null, TAG_RESOLUTION);
983 serializer.attribute(null, ATTR_ID, resolution.getId());
Svetoslav269403b2013-08-14 17:31:04 -0700984 serializer.attribute(null, ATTR_HORIZONTAL_DPI, String.valueOf(
985 resolution.getHorizontalDpi()));
986 serializer.attribute(null, ATTR_VERTICAL_DPI, String.valueOf(
987 resolution.getVerticalDpi()));
Svetoslavc6066792013-09-10 21:08:32 -0700988 serializer.attribute(null, ATTR_LABEL,
Svetoslav651dd4e2013-09-12 14:37:47 -0700989 resolution.getLabel());
Svetoslav269403b2013-08-14 17:31:04 -0700990 serializer.endTag(null, TAG_RESOLUTION);
991 }
992
Svetoslav651dd4e2013-09-12 14:37:47 -0700993 Margins margins = attributes.getMinMargins();
Svetoslav269403b2013-08-14 17:31:04 -0700994 if (margins != null) {
995 serializer.startTag(null, TAG_MARGINS);
996 serializer.attribute(null, ATTR_LEFT_MILS, String.valueOf(
997 margins.getLeftMils()));
998 serializer.attribute(null, ATTR_TOP_MILS, String.valueOf(
999 margins.getTopMils()));
1000 serializer.attribute(null, ATTR_RIGHT_MILS, String.valueOf(
1001 margins.getRightMils()));
1002 serializer.attribute(null, ATTR_BOTTOM_MILS, String.valueOf(
1003 margins.getBottomMils()));
1004 serializer.endTag(null, TAG_MARGINS);
1005 }
1006
Svetoslav269403b2013-08-14 17:31:04 -07001007 serializer.endTag(null, TAG_ATTRIBUTES);
1008 }
1009
1010 PrintDocumentInfo documentInfo = printJob.getDocumentInfo();
1011 if (documentInfo != null) {
1012 serializer.startTag(null, TAG_DOCUMENT_INFO);
1013 serializer.attribute(null, ATTR_NAME, documentInfo.getName());
1014 serializer.attribute(null, ATTR_CONTENT_TYPE, String.valueOf(
1015 documentInfo.getContentType()));
1016 serializer.attribute(null, ATTR_PAGE_COUNT, String.valueOf(
1017 documentInfo.getPageCount()));
Svetoslav Ganov7d7888d2013-10-12 13:18:12 -07001018 serializer.attribute(null, ATTR_DATA_SIZE, String.valueOf(
1019 documentInfo.getDataSize()));
Svetoslav269403b2013-08-14 17:31:04 -07001020 serializer.endTag(null, TAG_DOCUMENT_INFO);
1021 }
1022
Svetoslavb4fda132013-10-25 18:57:43 -07001023 Bundle advancedOptions = printJob.getAdvancedOptions();
1024 if (advancedOptions != null) {
1025 serializer.startTag(null, TAG_ADVANCED_OPTIONS);
1026 for (String key : advancedOptions.keySet()) {
1027 Object value = advancedOptions.get(key);
1028 if (value instanceof String) {
1029 String stringValue = (String) value;
1030 serializer.startTag(null, TAG_ADVANCED_OPTION);
1031 serializer.attribute(null, ATTR_KEY, key);
1032 serializer.attribute(null, ATTR_TYPE, TYPE_STRING);
1033 serializer.attribute(null, ATTR_VALUE, stringValue);
1034 serializer.endTag(null, TAG_ADVANCED_OPTION);
1035 } else if (value instanceof Integer) {
1036 String intValue = Integer.toString((Integer) value);
1037 serializer.startTag(null, TAG_ADVANCED_OPTION);
1038 serializer.attribute(null, ATTR_KEY, key);
1039 serializer.attribute(null, ATTR_TYPE, TYPE_INT);
1040 serializer.attribute(null, ATTR_VALUE, intValue);
1041 serializer.endTag(null, TAG_ADVANCED_OPTION);
1042 }
1043 }
1044 serializer.endTag(null, TAG_ADVANCED_OPTIONS);
1045 }
1046
Svetoslav269403b2013-08-14 17:31:04 -07001047 serializer.endTag(null, TAG_JOB);
1048
1049 if (DEBUG_PERSISTENCE) {
1050 Log.i(LOG_TAG, "[PERSISTED] " + printJob);
1051 }
1052 }
1053
1054 serializer.endTag(null, TAG_SPOOLER);
1055 serializer.endDocument();
1056 mStatePersistFile.finishWrite(out);
1057 if (DEBUG_PERSISTENCE) {
1058 Log.i(LOG_TAG, "[PERSIST END]");
1059 }
1060 } catch (IOException e) {
1061 Slog.w(LOG_TAG, "Failed to write state, restoring backup.", e);
1062 mStatePersistFile.failWrite(out);
1063 } finally {
1064 IoUtils.closeQuietly(out);
1065 }
1066 }
1067
1068 public void readStateLocked() {
Svet Ganov4237c922014-10-24 12:53:23 -07001069 if (!PERSISTENCE_MANAGER_ENABLED) {
Svetoslav269403b2013-08-14 17:31:04 -07001070 return;
1071 }
1072 FileInputStream in = null;
1073 try {
1074 in = mStatePersistFile.openRead();
1075 } catch (FileNotFoundException e) {
Joe Onorato13460a62016-03-18 13:34:13 -07001076 if (DEBUG_PERSISTENCE) {
1077 Log.d(LOG_TAG, "No existing print spooler state.");
1078 }
Svetoslav269403b2013-08-14 17:31:04 -07001079 return;
1080 }
1081 try {
1082 XmlPullParser parser = Xml.newPullParser();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +01001083 parser.setInput(in, StandardCharsets.UTF_8.name());
Svetoslav269403b2013-08-14 17:31:04 -07001084 parseState(parser);
1085 } catch (IllegalStateException ise) {
1086 Slog.w(LOG_TAG, "Failed parsing ", ise);
1087 } catch (NullPointerException npe) {
1088 Slog.w(LOG_TAG, "Failed parsing ", npe);
1089 } catch (NumberFormatException nfe) {
1090 Slog.w(LOG_TAG, "Failed parsing ", nfe);
1091 } catch (XmlPullParserException xppe) {
1092 Slog.w(LOG_TAG, "Failed parsing ", xppe);
1093 } catch (IOException ioe) {
1094 Slog.w(LOG_TAG, "Failed parsing ", ioe);
1095 } catch (IndexOutOfBoundsException iobe) {
1096 Slog.w(LOG_TAG, "Failed parsing ", iobe);
1097 } finally {
1098 IoUtils.closeQuietly(in);
1099 }
1100 }
1101
1102 private void parseState(XmlPullParser parser)
1103 throws IOException, XmlPullParserException {
1104 parser.next();
1105 skipEmptyTextTags(parser);
1106 expect(parser, XmlPullParser.START_TAG, TAG_SPOOLER);
1107 parser.next();
1108
1109 while (parsePrintJob(parser)) {
1110 parser.next();
1111 }
1112
1113 skipEmptyTextTags(parser);
1114 expect(parser, XmlPullParser.END_TAG, TAG_SPOOLER);
1115 }
1116
1117 private boolean parsePrintJob(XmlPullParser parser)
1118 throws IOException, XmlPullParserException {
1119 skipEmptyTextTags(parser);
1120 if (!accept(parser, XmlPullParser.START_TAG, TAG_JOB)) {
1121 return false;
1122 }
1123
1124 PrintJobInfo printJob = new PrintJobInfo();
1125
Svetoslav2fbd2a72013-09-16 17:53:51 -07001126 PrintJobId printJobId = PrintJobId.unflattenFromString(
1127 parser.getAttributeValue(null, ATTR_ID));
Svetoslav269403b2013-08-14 17:31:04 -07001128 printJob.setId(printJobId);
1129 String label = parser.getAttributeValue(null, ATTR_LABEL);
1130 printJob.setLabel(label);
1131 final int state = Integer.parseInt(parser.getAttributeValue(null, ATTR_STATE));
1132 printJob.setState(state);
1133 final int appId = Integer.parseInt(parser.getAttributeValue(null, ATTR_APP_ID));
1134 printJob.setAppId(appId);
Svetoslav269403b2013-08-14 17:31:04 -07001135 String tag = parser.getAttributeValue(null, ATTR_TAG);
1136 printJob.setTag(tag);
Svetoslav Ganov704697b2013-09-21 20:30:24 -07001137 String creationTime = parser.getAttributeValue(null, ATTR_CREATION_TIME);
1138 printJob.setCreationTime(Long.parseLong(creationTime));
Svetoslav269403b2013-08-14 17:31:04 -07001139 String copies = parser.getAttributeValue(null, ATTR_COPIES);
1140 printJob.setCopies(Integer.parseInt(copies));
Svetoslav Ganov704697b2013-09-21 20:30:24 -07001141 String printerName = parser.getAttributeValue(null, ATTR_PRINTER_NAME);
1142 printJob.setPrinterName(printerName);
Philip P. Moltmannb3078c22015-11-23 16:12:39 -08001143
1144 String progressString = parser.getAttributeValue(null, ATTR_PROGRESS);
1145 if (progressString != null) {
1146 float progress = Float.parseFloat(progressString);
1147
Philip P. Moltmanna958fc32015-12-02 15:39:09 -08001148 if (progress != -1) {
Philip P. Moltmannb3078c22015-11-23 16:12:39 -08001149 printJob.setProgress(progress);
1150 }
1151 }
1152
1153 CharSequence status = parser.getAttributeValue(null, ATTR_STATUS);
1154 printJob.setStatus(status);
1155
1156 // stateReason is deprecated, but might be used by old print jobs
Svetoslav Ganov704697b2013-09-21 20:30:24 -07001157 String stateReason = parser.getAttributeValue(null, ATTR_STATE_REASON);
Philip P. Moltmannb3078c22015-11-23 16:12:39 -08001158 if (stateReason != null) {
1159 printJob.setStatus(stateReason);
1160 }
1161
Svetoslav Ganova18661d2013-10-09 22:55:49 -07001162 String cancelling = parser.getAttributeValue(null, ATTR_CANCELLING);
1163 printJob.setCancelling(!TextUtils.isEmpty(cancelling)
1164 ? Boolean.parseBoolean(cancelling) : false);
Svetoslav269403b2013-08-14 17:31:04 -07001165
1166 parser.next();
1167
1168 skipEmptyTextTags(parser);
1169 if (accept(parser, XmlPullParser.START_TAG, TAG_PRINTER_ID)) {
1170 String localId = parser.getAttributeValue(null, ATTR_LOCAL_ID);
1171 ComponentName service = ComponentName.unflattenFromString(parser.getAttributeValue(
1172 null, ATTR_SERVICE_NAME));
1173 printJob.setPrinterId(new PrinterId(service, localId));
1174 parser.next();
1175 skipEmptyTextTags(parser);
1176 expect(parser, XmlPullParser.END_TAG, TAG_PRINTER_ID);
1177 parser.next();
1178 }
1179
1180 skipEmptyTextTags(parser);
1181 List<PageRange> pageRanges = null;
1182 while (accept(parser, XmlPullParser.START_TAG, TAG_PAGE_RANGE)) {
1183 final int start = Integer.parseInt(parser.getAttributeValue(null, ATTR_START));
1184 final int end = Integer.parseInt(parser.getAttributeValue(null, ATTR_END));
1185 PageRange pageRange = new PageRange(start, end);
1186 if (pageRanges == null) {
1187 pageRanges = new ArrayList<PageRange>();
1188 }
1189 pageRanges.add(pageRange);
1190 parser.next();
1191 skipEmptyTextTags(parser);
1192 expect(parser, XmlPullParser.END_TAG, TAG_PAGE_RANGE);
1193 parser.next();
Svetoslavb4fda132013-10-25 18:57:43 -07001194 skipEmptyTextTags(parser);
Svetoslav269403b2013-08-14 17:31:04 -07001195 }
1196 if (pageRanges != null) {
1197 PageRange[] pageRangesArray = new PageRange[pageRanges.size()];
1198 pageRanges.toArray(pageRangesArray);
1199 printJob.setPages(pageRangesArray);
1200 }
1201
1202 skipEmptyTextTags(parser);
1203 if (accept(parser, XmlPullParser.START_TAG, TAG_ATTRIBUTES)) {
1204
1205 PrintAttributes.Builder builder = new PrintAttributes.Builder();
1206
Svetoslav269403b2013-08-14 17:31:04 -07001207 String colorMode = parser.getAttributeValue(null, ATTR_COLOR_MODE);
1208 builder.setColorMode(Integer.parseInt(colorMode));
1209
Svetoslav948c9a62015-02-02 19:47:04 -08001210 String duplexMode = parser.getAttributeValue(null, ATTR_DUPLEX_MODE);
1211 // Duplex mode was added later, so null check is needed.
1212 if (duplexMode != null) {
1213 builder.setDuplexMode(Integer.parseInt(duplexMode));
1214 }
1215
Svetoslav269403b2013-08-14 17:31:04 -07001216 parser.next();
1217
1218 skipEmptyTextTags(parser);
1219 if (accept(parser, XmlPullParser.START_TAG, TAG_MEDIA_SIZE)) {
1220 String id = parser.getAttributeValue(null, ATTR_ID);
1221 label = parser.getAttributeValue(null, ATTR_LABEL);
1222 final int widthMils = Integer.parseInt(parser.getAttributeValue(null,
1223 ATTR_WIDTH_MILS));
1224 final int heightMils = Integer.parseInt(parser.getAttributeValue(null,
1225 ATTR_HEIGHT_MILS));
Svetoslav773f54d2013-09-03 14:01:43 -07001226 String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
Svetoslavb206f122013-09-20 10:43:24 -07001227 String labelResIdString = parser.getAttributeValue(null, ATTR_LABEL_RES_ID);
1228 final int labelResId = (labelResIdString != null)
1229 ? Integer.parseInt(labelResIdString) : 0;
Svetoslav773f54d2013-09-03 14:01:43 -07001230 label = parser.getAttributeValue(null, ATTR_LABEL);
Svetoslavb4fda132013-10-25 18:57:43 -07001231 MediaSize mediaSize = new MediaSize(id, label, packageName,
1232 widthMils, heightMils, labelResId);
Svetoslav269403b2013-08-14 17:31:04 -07001233 builder.setMediaSize(mediaSize);
1234 parser.next();
1235 skipEmptyTextTags(parser);
1236 expect(parser, XmlPullParser.END_TAG, TAG_MEDIA_SIZE);
1237 parser.next();
1238 }
1239
1240 skipEmptyTextTags(parser);
1241 if (accept(parser, XmlPullParser.START_TAG, TAG_RESOLUTION)) {
1242 String id = parser.getAttributeValue(null, ATTR_ID);
1243 label = parser.getAttributeValue(null, ATTR_LABEL);
1244 final int horizontalDpi = Integer.parseInt(parser.getAttributeValue(null,
1245 ATTR_HORIZONTAL_DPI));
1246 final int verticalDpi = Integer.parseInt(parser.getAttributeValue(null,
1247 ATTR_VERTICAL_DPI));
Svetoslavc6066792013-09-10 21:08:32 -07001248 Resolution resolution = new Resolution(id, label, horizontalDpi, verticalDpi);
Svetoslav269403b2013-08-14 17:31:04 -07001249 builder.setResolution(resolution);
1250 parser.next();
1251 skipEmptyTextTags(parser);
1252 expect(parser, XmlPullParser.END_TAG, TAG_RESOLUTION);
1253 parser.next();
1254 }
1255
1256 skipEmptyTextTags(parser);
1257 if (accept(parser, XmlPullParser.START_TAG, TAG_MARGINS)) {
1258 final int leftMils = Integer.parseInt(parser.getAttributeValue(null,
1259 ATTR_LEFT_MILS));
1260 final int topMils = Integer.parseInt(parser.getAttributeValue(null,
1261 ATTR_TOP_MILS));
1262 final int rightMils = Integer.parseInt(parser.getAttributeValue(null,
1263 ATTR_RIGHT_MILS));
1264 final int bottomMils = Integer.parseInt(parser.getAttributeValue(null,
1265 ATTR_BOTTOM_MILS));
1266 Margins margins = new Margins(leftMils, topMils, rightMils, bottomMils);
Svetoslav651dd4e2013-09-12 14:37:47 -07001267 builder.setMinMargins(margins);
Svetoslav269403b2013-08-14 17:31:04 -07001268 parser.next();
1269 skipEmptyTextTags(parser);
1270 expect(parser, XmlPullParser.END_TAG, TAG_MARGINS);
1271 parser.next();
1272 }
1273
Svetoslav651dd4e2013-09-12 14:37:47 -07001274 printJob.setAttributes(builder.build());
Svetoslav269403b2013-08-14 17:31:04 -07001275
1276 skipEmptyTextTags(parser);
1277 expect(parser, XmlPullParser.END_TAG, TAG_ATTRIBUTES);
1278 parser.next();
1279 }
1280
1281 skipEmptyTextTags(parser);
1282 if (accept(parser, XmlPullParser.START_TAG, TAG_DOCUMENT_INFO)) {
1283 String name = parser.getAttributeValue(null, ATTR_NAME);
1284 final int pageCount = Integer.parseInt(parser.getAttributeValue(null,
1285 ATTR_PAGE_COUNT));
1286 final int contentType = Integer.parseInt(parser.getAttributeValue(null,
1287 ATTR_CONTENT_TYPE));
Svetoslav Ganov7d7888d2013-10-12 13:18:12 -07001288 final int dataSize = Integer.parseInt(parser.getAttributeValue(null,
1289 ATTR_DATA_SIZE));
Svetoslav269403b2013-08-14 17:31:04 -07001290 PrintDocumentInfo info = new PrintDocumentInfo.Builder(name)
1291 .setPageCount(pageCount)
Svetoslav651dd4e2013-09-12 14:37:47 -07001292 .setContentType(contentType).build();
Svetoslav269403b2013-08-14 17:31:04 -07001293 printJob.setDocumentInfo(info);
Svetoslav Ganov7d7888d2013-10-12 13:18:12 -07001294 info.setDataSize(dataSize);
Svetoslav269403b2013-08-14 17:31:04 -07001295 parser.next();
1296 skipEmptyTextTags(parser);
1297 expect(parser, XmlPullParser.END_TAG, TAG_DOCUMENT_INFO);
1298 parser.next();
1299 }
1300
Svetoslavb4fda132013-10-25 18:57:43 -07001301 skipEmptyTextTags(parser);
1302 if (accept(parser, XmlPullParser.START_TAG, TAG_ADVANCED_OPTIONS)) {
1303 parser.next();
1304 skipEmptyTextTags(parser);
1305 Bundle advancedOptions = new Bundle();
1306 while (accept(parser, XmlPullParser.START_TAG, TAG_ADVANCED_OPTION)) {
1307 String key = parser.getAttributeValue(null, ATTR_KEY);
1308 String value = parser.getAttributeValue(null, ATTR_VALUE);
1309 String type = parser.getAttributeValue(null, ATTR_TYPE);
1310 if (TYPE_STRING.equals(type)) {
1311 advancedOptions.putString(key, value);
1312 } else if (TYPE_INT.equals(type)) {
Tobias Thierer9bbbf2b2016-04-21 12:02:10 +01001313 advancedOptions.putInt(key, Integer.parseInt(value));
Svetoslavb4fda132013-10-25 18:57:43 -07001314 }
1315 parser.next();
1316 skipEmptyTextTags(parser);
1317 expect(parser, XmlPullParser.END_TAG, TAG_ADVANCED_OPTION);
1318 parser.next();
1319 skipEmptyTextTags(parser);
1320 }
1321 printJob.setAdvancedOptions(advancedOptions);
1322 skipEmptyTextTags(parser);
1323 expect(parser, XmlPullParser.END_TAG, TAG_ADVANCED_OPTIONS);
1324 parser.next();
1325 }
1326
Svetoslav269403b2013-08-14 17:31:04 -07001327 mPrintJobs.add(printJob);
1328
1329 if (DEBUG_PERSISTENCE) {
1330 Log.i(LOG_TAG, "[RESTORED] " + printJob);
1331 }
1332
1333 skipEmptyTextTags(parser);
1334 expect(parser, XmlPullParser.END_TAG, TAG_JOB);
1335
1336 return true;
1337 }
1338
1339 private void expect(XmlPullParser parser, int type, String tag)
Philip P. Moltmannc43639c2015-12-18 13:58:40 -08001340 throws XmlPullParserException {
Svetoslav269403b2013-08-14 17:31:04 -07001341 if (!accept(parser, type, tag)) {
1342 throw new XmlPullParserException("Exepected event: " + type
1343 + " and tag: " + tag + " but got event: " + parser.getEventType()
1344 + " and tag:" + parser.getName());
1345 }
1346 }
1347
1348 private void skipEmptyTextTags(XmlPullParser parser)
1349 throws IOException, XmlPullParserException {
1350 while (accept(parser, XmlPullParser.TEXT, null)
1351 && "\n".equals(parser.getText())) {
1352 parser.next();
1353 }
1354 }
1355
1356 private boolean accept(XmlPullParser parser, int type, String tag)
Philip P. Moltmannc43639c2015-12-18 13:58:40 -08001357 throws XmlPullParserException {
Svetoslav269403b2013-08-14 17:31:04 -07001358 if (parser.getEventType() != type) {
1359 return false;
1360 }
1361 if (tag != null) {
1362 if (!tag.equals(parser.getName())) {
1363 return false;
1364 }
1365 } else if (parser.getName() != null) {
1366 return false;
1367 }
1368 return true;
1369 }
1370 }
Svetoslav7bfbbcb2013-10-10 13:36:23 -07001371
Svetoslava798c0a2014-05-15 10:47:19 -07001372 public final class PrintSpooler extends IPrintSpooler.Stub {
Svetoslav7bfbbcb2013-10-10 13:36:23 -07001373 @Override
1374 public void getPrintJobInfos(IPrintSpoolerCallbacks callback,
1375 ComponentName componentName, int state, int appId, int sequence)
1376 throws RemoteException {
1377 List<PrintJobInfo> printJobs = null;
1378 try {
1379 printJobs = PrintSpoolerService.this.getPrintJobInfos(
1380 componentName, state, appId);
1381 } finally {
1382 callback.onGetPrintJobInfosResult(printJobs, sequence);
1383 }
1384 }
1385
1386 @Override
1387 public void getPrintJobInfo(PrintJobId printJobId, IPrintSpoolerCallbacks callback,
1388 int appId, int sequence) throws RemoteException {
1389 PrintJobInfo printJob = null;
1390 try {
1391 printJob = PrintSpoolerService.this.getPrintJobInfo(printJobId, appId);
1392 } finally {
1393 callback.onGetPrintJobInfoResult(printJob, sequence);
1394 }
1395 }
1396
1397 @Override
1398 public void createPrintJob(PrintJobInfo printJob) {
1399 PrintSpoolerService.this.createPrintJob(printJob);
1400 }
1401
1402 @Override
1403 public void setPrintJobState(PrintJobId printJobId, int state, String error,
1404 IPrintSpoolerCallbacks callback, int sequece) throws RemoteException {
1405 boolean success = false;
1406 try {
1407 success = PrintSpoolerService.this.setPrintJobState(
1408 printJobId, state, error);
1409 } finally {
1410 callback.onSetPrintJobStateResult(success, sequece);
1411 }
1412 }
1413
1414 @Override
1415 public void setPrintJobTag(PrintJobId printJobId, String tag,
1416 IPrintSpoolerCallbacks callback, int sequece) throws RemoteException {
1417 boolean success = false;
1418 try {
1419 success = PrintSpoolerService.this.setPrintJobTag(printJobId, tag);
1420 } finally {
1421 callback.onSetPrintJobTagResult(success, sequece);
1422 }
1423 }
1424
1425 @Override
1426 public void writePrintJobData(ParcelFileDescriptor fd, PrintJobId printJobId) {
1427 PrintSpoolerService.this.writePrintJobData(fd, printJobId);
1428 }
1429
1430 @Override
1431 public void setClient(IPrintSpoolerClient client) {
1432 Message message = mHandlerCaller.obtainMessageO(
1433 HandlerCallerCallback.MSG_SET_CLIENT, client);
1434 mHandlerCaller.executeOrSendMessage(message);
1435 }
1436
1437 @Override
1438 public void removeObsoletePrintJobs() {
1439 PrintSpoolerService.this.removeObsoletePrintJobs();
1440 }
1441
1442 @Override
1443 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
1444 PrintSpoolerService.this.dump(fd, writer, args);
1445 }
1446
1447 @Override
1448 public void setPrintJobCancelling(PrintJobId printJobId, boolean cancelling) {
1449 PrintSpoolerService.this.setPrintJobCancelling(printJobId, cancelling);
1450 }
1451
Philip P. Moltmann853a6f52015-11-03 10:38:56 -08001452 @Override
Philip P. Moltmann8141bdf2015-12-21 17:03:05 -08001453 public void pruneApprovedPrintServices(List<ComponentName> servicesToKeep) {
Philip P. Moltmann853a6f52015-11-03 10:38:56 -08001454 (new ApprovedPrintServices(PrintSpoolerService.this))
Philip P. Moltmann8141bdf2015-12-21 17:03:05 -08001455 .pruneApprovedServices(servicesToKeep);
Philip P. Moltmann853a6f52015-11-03 10:38:56 -08001456 }
1457
Philip P. Moltmannb3078c22015-11-23 16:12:39 -08001458 @Override
1459 public void setProgress(@NonNull PrintJobId printJobId,
1460 @FloatRange(from=0.0, to=1.0) float progress) throws RemoteException {
1461 PrintSpoolerService.this.setProgress(printJobId, progress);
1462 }
1463
1464 @Override
1465 public void setStatus(@NonNull PrintJobId printJobId,
1466 @Nullable CharSequence status) throws RemoteException {
1467 PrintSpoolerService.this.setStatus(printJobId, status);
1468 }
1469
Philip P. Moltmannd74d1e52016-03-17 16:37:47 -07001470 @Override
1471 public void setStatusRes(@NonNull PrintJobId printJobId, @StringRes int status,
1472 @NonNull CharSequence appPackageName) throws RemoteException {
1473 PrintSpoolerService.this.setStatus(printJobId, status, appPackageName);
1474 }
1475
1476
Svetoslav7bfbbcb2013-10-10 13:36:23 -07001477 public PrintSpoolerService getService() {
1478 return PrintSpoolerService.this;
1479 }
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -08001480
1481 @Override
1482 public void onCustomPrinterIconLoaded(PrinterId printerId, Icon icon,
1483 IPrintSpoolerCallbacks callbacks, int sequence)
1484 throws RemoteException {
1485 try {
1486 PrintSpoolerService.this.onCustomPrinterIconLoaded(printerId, icon);
1487 } finally {
1488 callbacks.onCustomPrinterIconCached(sequence);
1489 }
1490 }
1491
1492 @Override
1493 public void getCustomPrinterIcon(PrinterId printerId, IPrintSpoolerCallbacks callbacks,
1494 int sequence) throws RemoteException {
1495 Icon icon = null;
1496 try {
1497 icon = PrintSpoolerService.this.getCustomPrinterIcon(printerId);
1498 } finally {
1499 callbacks.onGetCustomPrinterIconResult(icon, sequence);
1500 }
1501 }
1502
1503 @Override
1504 public void clearCustomPrinterIconCache(IPrintSpoolerCallbacks callbacks,
1505 int sequence) throws RemoteException {
1506 try {
1507 PrintSpoolerService.this.clearCustomPrinterIconCache();
1508 } finally {
1509 callbacks.customPrinterIconCacheCleared(sequence);
1510 }
1511 }
1512
Svetoslav7bfbbcb2013-10-10 13:36:23 -07001513 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -07001514}