blob: d15ae1c07ca774c3f9c0be757b42a66669617b70 [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
Philip P. Moltmann2e301262016-06-16 12:39:54 -070092 private static final String PRINT_JOB_STATE_HISTO = "print_job_state";
93
Svetoslav Ganov835835e2013-08-04 20:17:52 -070094 private static final long CHECK_ALL_PRINTJOBS_HANDLED_DELAY = 5000;
95
Svetoslav Ganovdd68da22013-09-27 10:48:31 -070096 private static final String PRINT_JOB_FILE_PREFIX = "print_job_";
97
Svetoslav269403b2013-08-14 17:31:04 -070098 private static final String PRINT_FILE_EXTENSION = "pdf";
99
100 private static final Object sLock = new Object();
101
102 private final Object mLock = new Object();
103
Svetoslava798c0a2014-05-15 10:47:19 -0700104 private final List<PrintJobInfo> mPrintJobs = new ArrayList<>();
Svetoslav269403b2013-08-14 17:31:04 -0700105
106 private static PrintSpoolerService sInstance;
107
Svetoslav Ganov835835e2013-08-04 20:17:52 -0700108 private IPrintSpoolerClient mClient;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700109
Svetoslav269403b2013-08-14 17:31:04 -0700110 private HandlerCaller mHandlerCaller;
111
112 private PersistenceManager mPersistanceManager;
113
114 private NotificationController mNotificationController;
115
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -0800116 /** Cache for custom printer icons loaded from the print service */
117 private CustomPrinterIconCache mCustomIconCache;
118
Svetoslav269403b2013-08-14 17:31:04 -0700119 public static PrintSpoolerService peekInstance() {
120 synchronized (sLock) {
121 return sInstance;
122 }
123 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700124
125 @Override
126 public void onCreate() {
127 super.onCreate();
Svetoslav269403b2013-08-14 17:31:04 -0700128 mHandlerCaller = new HandlerCaller(this, getMainLooper(),
129 new HandlerCallerCallback(), false);
130
131 mPersistanceManager = new PersistenceManager();
132 mNotificationController = new NotificationController(PrintSpoolerService.this);
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -0800133 mCustomIconCache = new CustomPrinterIconCache(getCacheDir());
Svetoslav269403b2013-08-14 17:31:04 -0700134
135 synchronized (mLock) {
136 mPersistanceManager.readStateLocked();
137 handleReadPrintJobsLocked();
138 }
139
140 synchronized (sLock) {
141 sInstance = this;
142 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700143 }
144
145 @Override
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -0800146 public void onDestroy() {
147 super.onDestroy();
148 }
149
150 @Override
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700151 public IBinder onBind(Intent intent) {
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700152 return new PrintSpooler();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700153 }
154
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700155 @Override
156 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Philip P. Moltmann853a6f52015-11-03 10:38:56 -0800157 String prefix = (args.length > 0) ? args[0] : "";
158 String tab = " ";
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700159
Philip P. Moltmann853a6f52015-11-03 10:38:56 -0800160 synchronized (mLock) {
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700161 pw.append(prefix).append("print jobs:").println();
162 final int printJobCount = mPrintJobs.size();
163 for (int i = 0; i < printJobCount; i++) {
164 PrintJobInfo printJob = mPrintJobs.get(i);
165 pw.append(prefix).append(tab).append(printJob.toString());
166 pw.println();
167 }
168
169 pw.append(prefix).append("print job files:").println();
170 File[] files = getFilesDir().listFiles();
171 if (files != null) {
172 final int fileCount = files.length;
173 for (int i = 0; i < fileCount; i++) {
174 File file = files[i];
175 if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) {
176 pw.append(prefix).append(tab).append(file.getName()).println();
177 }
178 }
179 }
180 }
Philip P. Moltmann853a6f52015-11-03 10:38:56 -0800181
182 pw.append(prefix).append("approved print services:").println();
183 Set<String> approvedPrintServices = (new ApprovedPrintServices(this)).getApprovedServices();
184 if (approvedPrintServices != null) {
185 for (String approvedService : approvedPrintServices) {
186 pw.append(prefix).append(tab).append(approvedService).println();
187 }
188 }
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700189 }
190
Svetoslav269403b2013-08-14 17:31:04 -0700191 private void sendOnPrintJobQueued(PrintJobInfo printJob) {
192 Message message = mHandlerCaller.obtainMessageO(
193 HandlerCallerCallback.MSG_ON_PRINT_JOB_QUEUED, printJob);
194 mHandlerCaller.executeOrSendMessage(message);
195 }
196
197 private void sendOnAllPrintJobsForServiceHandled(ComponentName service) {
198 Message message = mHandlerCaller.obtainMessageO(
199 HandlerCallerCallback.MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED, service);
200 mHandlerCaller.executeOrSendMessage(message);
201 }
202
203 private void sendOnAllPrintJobsHandled() {
204 Message message = mHandlerCaller.obtainMessage(
205 HandlerCallerCallback.MSG_ON_ALL_PRINT_JOBS_HANDLED);
206 mHandlerCaller.executeOrSendMessage(message);
207 }
208
209 private final class HandlerCallerCallback implements HandlerCaller.Callback {
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700210 public static final int MSG_SET_CLIENT = 1;
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700211 public static final int MSG_ON_PRINT_JOB_QUEUED = 2;
212 public static final int MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED = 3;
213 public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 4;
214 public static final int MSG_CHECK_ALL_PRINTJOBS_HANDLED = 5;
215 public static final int MSG_ON_PRINT_JOB_STATE_CHANGED = 6;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700216
217 @Override
Svetoslav269403b2013-08-14 17:31:04 -0700218 public void executeMessage(Message message) {
Svetoslav Ganov835835e2013-08-04 20:17:52 -0700219 switch (message.what) {
220 case MSG_SET_CLIENT: {
Svetoslav269403b2013-08-14 17:31:04 -0700221 synchronized (mLock) {
222 mClient = (IPrintSpoolerClient) message.obj;
223 if (mClient != null) {
224 Message msg = mHandlerCaller.obtainMessage(
225 HandlerCallerCallback.MSG_CHECK_ALL_PRINTJOBS_HANDLED);
226 mHandlerCaller.sendMessageDelayed(msg,
227 CHECK_ALL_PRINTJOBS_HANDLED_DELAY);
228 }
Svetoslav Ganov835835e2013-08-04 20:17:52 -0700229 }
230 } break;
231
Svetoslav Ganov835835e2013-08-04 20:17:52 -0700232 case MSG_ON_PRINT_JOB_QUEUED: {
233 PrintJobInfo printJob = (PrintJobInfo) message.obj;
234 if (mClient != null) {
235 try {
236 mClient.onPrintJobQueued(printJob);
237 } catch (RemoteException re) {
238 Slog.e(LOG_TAG, "Error notify for a queued print job.", re);
239 }
240 }
241 } break;
242
243 case MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED: {
244 ComponentName service = (ComponentName) message.obj;
245 if (mClient != null) {
246 try {
247 mClient.onAllPrintJobsForServiceHandled(service);
248 } catch (RemoteException re) {
249 Slog.e(LOG_TAG, "Error notify for all print jobs per service"
250 + " handled.", re);
251 }
252 }
253 } break;
254
255 case MSG_ON_ALL_PRINT_JOBS_HANDLED: {
256 if (mClient != null) {
257 try {
258 mClient.onAllPrintJobsHandled();
259 } catch (RemoteException re) {
260 Slog.e(LOG_TAG, "Error notify for all print job handled.", re);
261 }
262 }
263 } break;
264
Svetoslav Ganov835835e2013-08-04 20:17:52 -0700265 case MSG_CHECK_ALL_PRINTJOBS_HANDLED: {
Svetoslav269403b2013-08-14 17:31:04 -0700266 checkAllPrintJobsHandled();
267 } break;
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700268
269 case MSG_ON_PRINT_JOB_STATE_CHANGED: {
270 if (mClient != null) {
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700271 PrintJobInfo printJob = (PrintJobInfo) message.obj;
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700272 try {
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700273 mClient.onPrintJobStateChanged(printJob);
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700274 } catch (RemoteException re) {
275 Slog.e(LOG_TAG, "Error notify for print job state change.", re);
276 }
277 }
278 } break;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700279 }
280 }
281 }
Svetoslav269403b2013-08-14 17:31:04 -0700282
283 public List<PrintJobInfo> getPrintJobInfos(ComponentName componentName,
284 int state, int appId) {
285 List<PrintJobInfo> foundPrintJobs = null;
286 synchronized (mLock) {
287 final int printJobCount = mPrintJobs.size();
288 for (int i = 0; i < printJobCount; i++) {
289 PrintJobInfo printJob = mPrintJobs.get(i);
290 PrinterId printerId = printJob.getPrinterId();
291 final boolean sameComponent = (componentName == null
292 || (printerId != null
293 && componentName.equals(printerId.getServiceName())));
294 final boolean sameAppId = appId == PrintManager.APP_ID_ANY
295 || printJob.getAppId() == appId;
296 final boolean sameState = (state == printJob.getState())
297 || (state == PrintJobInfo.STATE_ANY)
298 || (state == PrintJobInfo.STATE_ANY_VISIBLE_TO_CLIENTS
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700299 && isStateVisibleToUser(printJob.getState()))
300 || (state == PrintJobInfo.STATE_ANY_ACTIVE
Svetoslav Ganov9b6d3a12013-10-12 12:35:41 -0700301 && isActiveState(printJob.getState()))
302 || (state == PrintJobInfo.STATE_ANY_SCHEDULED
303 && isScheduledState(printJob.getState()));
Svetoslav269403b2013-08-14 17:31:04 -0700304 if (sameComponent && sameAppId && sameState) {
305 if (foundPrintJobs == null) {
Svetoslava798c0a2014-05-15 10:47:19 -0700306 foundPrintJobs = new ArrayList<>();
Svetoslav269403b2013-08-14 17:31:04 -0700307 }
308 foundPrintJobs.add(printJob);
309 }
310 }
311 }
312 return foundPrintJobs;
313 }
314
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700315 private boolean isStateVisibleToUser(int state) {
316 return (isActiveState(state) && (state == PrintJobInfo.STATE_FAILED
Svetoslav2fbd2a72013-09-16 17:53:51 -0700317 || state == PrintJobInfo.STATE_COMPLETED || state == PrintJobInfo.STATE_CANCELED
318 || state == PrintJobInfo.STATE_BLOCKED));
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700319 }
320
Svetoslav2fbd2a72013-09-16 17:53:51 -0700321 public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId) {
Svetoslav269403b2013-08-14 17:31:04 -0700322 synchronized (mLock) {
323 final int printJobCount = mPrintJobs.size();
324 for (int i = 0; i < printJobCount; i++) {
325 PrintJobInfo printJob = mPrintJobs.get(i);
Svetoslav2fbd2a72013-09-16 17:53:51 -0700326 if (printJob.getId().equals(printJobId)
Svetoslav269403b2013-08-14 17:31:04 -0700327 && (appId == PrintManager.APP_ID_ANY
328 || appId == printJob.getAppId())) {
329 return printJob;
330 }
331 }
332 return null;
333 }
334 }
335
Svetoslav2fbd2a72013-09-16 17:53:51 -0700336 public void createPrintJob(PrintJobInfo printJob) {
Svetoslav269403b2013-08-14 17:31:04 -0700337 synchronized (mLock) {
Svetoslav269403b2013-08-14 17:31:04 -0700338 addPrintJobLocked(printJob);
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700339 setPrintJobState(printJob.getId(), PrintJobInfo.STATE_CREATED, null);
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700340
341 Message message = mHandlerCaller.obtainMessageO(
342 HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
343 printJob);
344 mHandlerCaller.executeOrSendMessage(message);
Svetoslav269403b2013-08-14 17:31:04 -0700345 }
346 }
347
348 private void handleReadPrintJobsLocked() {
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700349 // Make a map with the files for a print job since we may have
350 // to delete some. One example of getting orphan files if the
351 // spooler crashes while constructing a print job. We do not
352 // persist partially populated print jobs under construction to
353 // avoid special handling for various attributes missing.
354 ArrayMap<PrintJobId, File> fileForJobMap = null;
355 File[] files = getFilesDir().listFiles();
356 if (files != null) {
357 final int fileCount = files.length;
358 for (int i = 0; i < fileCount; i++) {
359 File file = files[i];
360 if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) {
361 if (fileForJobMap == null) {
362 fileForJobMap = new ArrayMap<PrintJobId, File>();
363 }
Svetoslav Ganov2b40c832013-10-04 08:49:03 -0700364 String printJobIdString = file.getName().substring(
365 PRINT_JOB_FILE_PREFIX.length(),
366 file.getName().indexOf('.'));
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700367 PrintJobId printJobId = PrintJobId.unflattenFromString(
368 printJobIdString);
369 fileForJobMap.put(printJobId, file);
370 }
371 }
372 }
373
Svetoslav269403b2013-08-14 17:31:04 -0700374 final int printJobCount = mPrintJobs.size();
375 for (int i = 0; i < printJobCount; i++) {
376 PrintJobInfo printJob = mPrintJobs.get(i);
377
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700378 // We want to have only the orphan files at the end.
379 if (fileForJobMap != null) {
380 fileForJobMap.remove(printJob.getId());
381 }
382
Svetoslav269403b2013-08-14 17:31:04 -0700383 switch (printJob.getState()) {
384 case PrintJobInfo.STATE_QUEUED:
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700385 case PrintJobInfo.STATE_STARTED:
386 case PrintJobInfo.STATE_BLOCKED: {
387 // We have a print job that was queued or started or blocked in
388 // the past but the device battery died or a crash occurred. In
389 // this case we assume the print job failed and let the user
390 // decide whether to restart the job or just cancel it.
Svetoslav269403b2013-08-14 17:31:04 -0700391 setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED,
392 getString(R.string.no_connection_to_printer));
Svetoslav2fbd2a72013-09-16 17:53:51 -0700393 } break;
Svetoslav269403b2013-08-14 17:31:04 -0700394 }
395 }
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700396
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700397 if (!mPrintJobs.isEmpty()) {
398 // Update the notification.
399 mNotificationController.onUpdateNotifications(mPrintJobs);
400 }
401
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700402 // Delete the orphan files.
403 if (fileForJobMap != null) {
404 final int orphanFileCount = fileForJobMap.size();
405 for (int i = 0; i < orphanFileCount; i++) {
406 File file = fileForJobMap.valueAt(i);
407 file.delete();
408 }
409 }
Svetoslav269403b2013-08-14 17:31:04 -0700410 }
411
412 public void checkAllPrintJobsHandled() {
413 synchronized (mLock) {
414 if (!hasActivePrintJobsLocked()) {
415 notifyOnAllPrintJobsHandled();
416 }
417 }
418 }
419
Svetoslav2fbd2a72013-09-16 17:53:51 -0700420 public void writePrintJobData(final ParcelFileDescriptor fd, final PrintJobId printJobId) {
Svetoslav269403b2013-08-14 17:31:04 -0700421 final PrintJobInfo printJob;
422 synchronized (mLock) {
423 printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
424 }
425 new AsyncTask<Void, Void, Void>() {
426 @Override
427 protected Void doInBackground(Void... params) {
428 FileInputStream in = null;
429 FileOutputStream out = null;
430 try {
431 if (printJob != null) {
Svetoslava798c0a2014-05-15 10:47:19 -0700432 File file = generateFileForPrintJob(PrintSpoolerService.this, printJobId);
Svetoslav269403b2013-08-14 17:31:04 -0700433 in = new FileInputStream(file);
434 out = new FileOutputStream(fd.getFileDescriptor());
435 }
436 final byte[] buffer = new byte[8192];
437 while (true) {
438 final int readByteCount = in.read(buffer);
439 if (readByteCount < 0) {
440 return null;
441 }
442 out.write(buffer, 0, readByteCount);
443 }
444 } catch (FileNotFoundException fnfe) {
445 Log.e(LOG_TAG, "Error writing print job data!", fnfe);
446 } catch (IOException ioe) {
447 Log.e(LOG_TAG, "Error writing print job data!", ioe);
448 } finally {
449 IoUtils.closeQuietly(in);
450 IoUtils.closeQuietly(out);
451 IoUtils.closeQuietly(fd);
452 }
453 Log.i(LOG_TAG, "[END WRITE]");
454 return null;
455 }
456 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
457 }
458
Svetoslava798c0a2014-05-15 10:47:19 -0700459 public static File generateFileForPrintJob(Context context, PrintJobId printJobId) {
460 return new File(context.getFilesDir(), PRINT_JOB_FILE_PREFIX
Svetoslav2fbd2a72013-09-16 17:53:51 -0700461 + printJobId.flattenToString() + "." + PRINT_FILE_EXTENSION);
Svetoslav269403b2013-08-14 17:31:04 -0700462 }
463
464 private void addPrintJobLocked(PrintJobInfo printJob) {
465 mPrintJobs.add(printJob);
466 if (DEBUG_PRINT_JOB_LIFECYCLE) {
467 Slog.i(LOG_TAG, "[ADD] " + printJob);
468 }
469 }
470
Svetoslav2fbd2a72013-09-16 17:53:51 -0700471 private void removeObsoletePrintJobs() {
472 synchronized (mLock) {
Svetoslav885810d2013-11-14 17:59:14 -0800473 boolean persistState = false;
Svetoslav2fbd2a72013-09-16 17:53:51 -0700474 final int printJobCount = mPrintJobs.size();
475 for (int i = printJobCount - 1; i >= 0; i--) {
476 PrintJobInfo printJob = mPrintJobs.get(i);
477 if (isObsoleteState(printJob.getState())) {
478 mPrintJobs.remove(i);
479 if (DEBUG_PRINT_JOB_LIFECYCLE) {
480 Slog.i(LOG_TAG, "[REMOVE] " + printJob.getId().flattenToString());
481 }
482 removePrintJobFileLocked(printJob.getId());
Svetoslav885810d2013-11-14 17:59:14 -0800483 persistState = true;
Svetoslav2fbd2a72013-09-16 17:53:51 -0700484 }
485 }
Svetoslav885810d2013-11-14 17:59:14 -0800486 if (persistState) {
487 mPersistanceManager.writeStateLocked();
488 }
Svetoslav2fbd2a72013-09-16 17:53:51 -0700489 }
490 }
491
492 private void removePrintJobFileLocked(PrintJobId printJobId) {
Svetoslava798c0a2014-05-15 10:47:19 -0700493 File file = generateFileForPrintJob(PrintSpoolerService.this, printJobId);
Svetoslav2fbd2a72013-09-16 17:53:51 -0700494 if (file.exists()) {
495 file.delete();
496 if (DEBUG_PRINT_JOB_LIFECYCLE) {
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700497 Slog.i(LOG_TAG, "[REMOVE FILE FOR] " + printJobId);
Svetoslav2fbd2a72013-09-16 17:53:51 -0700498 }
499 }
500 }
501
Philip P. Moltmann4bd8fac2016-04-21 15:17:58 -0700502 /**
503 * Notify all interested parties that a print job has been updated.
504 *
505 * @param printJob The updated print job.
506 */
507 private void notifyPrintJobUpdated(PrintJobInfo printJob) {
508 Message message = mHandlerCaller.obtainMessageO(
509 HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
510 printJob);
511 mHandlerCaller.executeOrSendMessage(message);
512
513 mNotificationController.onUpdateNotifications(mPrintJobs);
514 }
515
Svetoslav2fbd2a72013-09-16 17:53:51 -0700516 public boolean setPrintJobState(PrintJobId printJobId, int state, String error) {
Svetoslav269403b2013-08-14 17:31:04 -0700517 boolean success = false;
518
519 synchronized (mLock) {
520 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
521 if (printJob != null) {
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700522 final int oldState = printJob.getState();
523 if (oldState == state) {
524 return false;
525 }
526
Svetoslav269403b2013-08-14 17:31:04 -0700527 success = true;
528
529 printJob.setState(state);
Philip P. Moltmannb3078c22015-11-23 16:12:39 -0800530 printJob.setStatus(error);
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700531 printJob.setCancelling(false);
Svetoslav269403b2013-08-14 17:31:04 -0700532
533 if (DEBUG_PRINT_JOB_LIFECYCLE) {
534 Slog.i(LOG_TAG, "[STATE CHANGED] " + printJob);
535 }
536
Philip P. Moltmann2e301262016-06-16 12:39:54 -0700537 MetricsLogger.histogram(this, PRINT_JOB_STATE_HISTO, state);
Svetoslav269403b2013-08-14 17:31:04 -0700538 switch (state) {
539 case PrintJobInfo.STATE_COMPLETED:
540 case PrintJobInfo.STATE_CANCELED:
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700541 mPrintJobs.remove(printJob);
Svetoslav2fbd2a72013-09-16 17:53:51 -0700542 removePrintJobFileLocked(printJob.getId());
Svetoslav269403b2013-08-14 17:31:04 -0700543 // $fall-through$
544
545 case PrintJobInfo.STATE_FAILED: {
546 PrinterId printerId = printJob.getPrinterId();
547 if (printerId != null) {
548 ComponentName service = printerId.getServiceName();
549 if (!hasActivePrintJobsForServiceLocked(service)) {
550 sendOnAllPrintJobsForServiceHandled(service);
551 }
552 }
553 } break;
554
555 case PrintJobInfo.STATE_QUEUED: {
556 sendOnPrintJobQueued(new PrintJobInfo(printJob));
557 } break;
558 }
559
560 if (shouldPersistPrintJob(printJob)) {
561 mPersistanceManager.writeStateLocked();
562 }
563
564 if (!hasActivePrintJobsLocked()) {
565 notifyOnAllPrintJobsHandled();
566 }
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700567
Philip P. Moltmann4bd8fac2016-04-21 15:17:58 -0700568 notifyPrintJobUpdated(printJob);
Svetoslav269403b2013-08-14 17:31:04 -0700569 }
570 }
571
572 return success;
573 }
574
Philip P. Moltmannb3078c22015-11-23 16:12:39 -0800575 /**
576 * Set the progress for a print job.
577 *
578 * @param printJobId ID of the print job to update
579 * @param progress the new progress
580 */
581 public void setProgress(@NonNull PrintJobId printJobId,
582 @FloatRange(from=0.0, to=1.0) float progress) {
583 synchronized (mLock) {
584 getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY).setProgress(progress);
585
586 mNotificationController.onUpdateNotifications(mPrintJobs);
587 }
588 }
589
Philip P. Moltmannd74d1e52016-03-17 16:37:47 -0700590 /**
591 * Set the status for a print job.
592 *
593 * @param printJobId ID of the print job to update
594 * @param status the new status
595 */
596 public void setStatus(@NonNull PrintJobId printJobId, @Nullable CharSequence status) {
597 synchronized (mLock) {
Philip P. Moltmann4bd8fac2016-04-21 15:17:58 -0700598 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
Philip P. Moltmannb3078c22015-11-23 16:12:39 -0800599
Philip P. Moltmann4bd8fac2016-04-21 15:17:58 -0700600 if (printJob != null) {
601 printJob.setStatus(status);
602 notifyPrintJobUpdated(printJob);
603 }
Philip P. Moltmannd74d1e52016-03-17 16:37:47 -0700604 }
605 }
606
607 /**
608 * Set the status for a print job.
609 *
610 * @param printJobId ID of the print job to update
611 * @param status the new status as a string resource
612 * @param appPackageName app package the resource belongs to
613 */
614 public void setStatus(@NonNull PrintJobId printJobId, @StringRes int status,
615 @Nullable CharSequence appPackageName) {
616 synchronized (mLock) {
Philip P. Moltmann4bd8fac2016-04-21 15:17:58 -0700617 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
Philip P. Moltmannd74d1e52016-03-17 16:37:47 -0700618
Philip P. Moltmann4bd8fac2016-04-21 15:17:58 -0700619 if (printJob != null) {
620 printJob.setStatus(status, appPackageName);
621 notifyPrintJobUpdated(printJob);
622 }
Philip P. Moltmannd74d1e52016-03-17 16:37:47 -0700623 }
624 }
Philip P. Moltmannb3078c22015-11-23 16:12:39 -0800625
Svetoslav269403b2013-08-14 17:31:04 -0700626 public boolean hasActivePrintJobsLocked() {
627 final int printJobCount = mPrintJobs.size();
628 for (int i = 0; i < printJobCount; i++) {
629 PrintJobInfo printJob = mPrintJobs.get(i);
630 if (isActiveState(printJob.getState())) {
631 return true;
632 }
633 }
634 return false;
635 }
636
637 public boolean hasActivePrintJobsForServiceLocked(ComponentName service) {
638 final int printJobCount = mPrintJobs.size();
639 for (int i = 0; i < printJobCount; i++) {
640 PrintJobInfo printJob = mPrintJobs.get(i);
Svetoslav75d28502013-11-04 18:25:05 -0800641 if (isActiveState(printJob.getState()) && printJob.getPrinterId() != null
Svetoslav269403b2013-08-14 17:31:04 -0700642 && printJob.getPrinterId().getServiceName().equals(service)) {
643 return true;
644 }
645 }
646 return false;
647 }
648
Svetoslav2fbd2a72013-09-16 17:53:51 -0700649 private boolean isObsoleteState(int printJobState) {
Svetoslava798c0a2014-05-15 10:47:19 -0700650 return (isTerminalState(printJobState)
Svetoslav2fbd2a72013-09-16 17:53:51 -0700651 || printJobState == PrintJobInfo.STATE_QUEUED);
652 }
653
Svetoslav Ganov9b6d3a12013-10-12 12:35:41 -0700654 private boolean isScheduledState(int printJobState) {
655 return printJobState == PrintJobInfo.STATE_QUEUED
656 || printJobState == PrintJobInfo.STATE_STARTED
657 || printJobState == PrintJobInfo.STATE_BLOCKED;
658 }
659
Svetoslav269403b2013-08-14 17:31:04 -0700660 private boolean isActiveState(int printJobState) {
661 return printJobState == PrintJobInfo.STATE_CREATED
662 || printJobState == PrintJobInfo.STATE_QUEUED
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700663 || printJobState == PrintJobInfo.STATE_STARTED
664 || printJobState == PrintJobInfo.STATE_BLOCKED;
Svetoslav269403b2013-08-14 17:31:04 -0700665 }
666
Svetoslava798c0a2014-05-15 10:47:19 -0700667 private boolean isTerminalState(int printJobState) {
Svetoslav2fbd2a72013-09-16 17:53:51 -0700668 return printJobState == PrintJobInfo.STATE_COMPLETED
669 || printJobState == PrintJobInfo.STATE_CANCELED;
670 }
671
672 public boolean setPrintJobTag(PrintJobId printJobId, String tag) {
Svetoslav269403b2013-08-14 17:31:04 -0700673 synchronized (mLock) {
674 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
675 if (printJob != null) {
676 String printJobTag = printJob.getTag();
677 if (printJobTag == null) {
678 if (tag == null) {
679 return false;
680 }
681 } else if (printJobTag.equals(tag)) {
682 return false;
683 }
684 printJob.setTag(tag);
685 if (shouldPersistPrintJob(printJob)) {
686 mPersistanceManager.writeStateLocked();
687 }
688 return true;
689 }
690 }
691 return false;
692 }
693
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700694 public void setPrintJobCancelling(PrintJobId printJobId, boolean cancelling) {
695 synchronized (mLock) {
696 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
697 if (printJob != null) {
698 printJob.setCancelling(cancelling);
699 if (shouldPersistPrintJob(printJob)) {
700 mPersistanceManager.writeStateLocked();
701 }
702 mNotificationController.onUpdateNotifications(mPrintJobs);
703
704 Message message = mHandlerCaller.obtainMessageO(
705 HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
706 printJob);
707 mHandlerCaller.executeOrSendMessage(message);
708 }
709 }
710 }
711
Svetoslava798c0a2014-05-15 10:47:19 -0700712 public void updatePrintJobUserConfigurableOptionsNoPersistence(PrintJobInfo printJob) {
Svetoslav269403b2013-08-14 17:31:04 -0700713 synchronized (mLock) {
Svetoslava798c0a2014-05-15 10:47:19 -0700714 final int printJobCount = mPrintJobs.size();
715 for (int i = 0; i < printJobCount; i++) {
716 PrintJobInfo cachedPrintJob = mPrintJobs.get(i);
717 if (cachedPrintJob.getId().equals(printJob.getId())) {
718 cachedPrintJob.setPrinterId(printJob.getPrinterId());
719 cachedPrintJob.setPrinterName(printJob.getPrinterName());
720 cachedPrintJob.setCopies(printJob.getCopies());
721 cachedPrintJob.setDocumentInfo(printJob.getDocumentInfo());
722 cachedPrintJob.setPages(printJob.getPages());
723 cachedPrintJob.setAttributes(printJob.getAttributes());
724 cachedPrintJob.setAdvancedOptions(printJob.getAdvancedOptions());
725 return;
726 }
Svetoslav269403b2013-08-14 17:31:04 -0700727 }
Svetoslava798c0a2014-05-15 10:47:19 -0700728 throw new IllegalArgumentException("No print job with id:" + printJob.getId());
Svetoslav269403b2013-08-14 17:31:04 -0700729 }
730 }
731
732 private boolean shouldPersistPrintJob(PrintJobInfo printJob) {
733 return printJob.getState() >= PrintJobInfo.STATE_QUEUED;
734 }
735
736 private void notifyOnAllPrintJobsHandled() {
737 // This has to run on the tread that is persisting the current state
738 // since this call may result in the system unbinding from the spooler
739 // and as a result the spooler process may get killed before the write
740 // completes.
741 new AsyncTask<Void, Void, Void>() {
742 @Override
743 protected Void doInBackground(Void... params) {
744 sendOnAllPrintJobsHandled();
745 return null;
746 }
747 }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
748 }
749
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -0800750 /**
751 * Handle that a custom icon for a printer was loaded.
752 *
753 * @param printerId the id of the printer the icon belongs to
754 * @param icon the icon that was loaded
755 * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon()
756 */
757 public void onCustomPrinterIconLoaded(PrinterId printerId, Icon icon) {
758 mCustomIconCache.onCustomPrinterIconLoaded(printerId, icon);
759 }
760
761 /**
762 * Get the custom icon for a printer. If the icon is not cached, the icon is
763 * requested asynchronously. Once it is available the printer is updated.
764 *
765 * @param printerId the id of the printer the icon should be loaded for
766 * @return the custom icon to be used for the printer or null if the icon is
767 * not yet available
768 * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon()
769 */
770 public Icon getCustomPrinterIcon(PrinterId printerId) {
771 return mCustomIconCache.getIcon(printerId);
772 }
773
774 /**
775 * Clear the custom printer icon cache.
776 */
777 public void clearCustomPrinterIconCache() {
778 mCustomIconCache.clear();
779 }
780
Svetoslav269403b2013-08-14 17:31:04 -0700781 private final class PersistenceManager {
782 private static final String PERSIST_FILE_NAME = "print_spooler_state.xml";
783
784 private static final String TAG_SPOOLER = "spooler";
785 private static final String TAG_JOB = "job";
786
787 private static final String TAG_PRINTER_ID = "printerId";
788 private static final String TAG_PAGE_RANGE = "pageRange";
789 private static final String TAG_ATTRIBUTES = "attributes";
790 private static final String TAG_DOCUMENT_INFO = "documentInfo";
791
792 private static final String ATTR_ID = "id";
793 private static final String ATTR_LABEL = "label";
Svetoslav773f54d2013-09-03 14:01:43 -0700794 private static final String ATTR_LABEL_RES_ID = "labelResId";
795 private static final String ATTR_PACKAGE_NAME = "packageName";
Svetoslav269403b2013-08-14 17:31:04 -0700796 private static final String ATTR_STATE = "state";
797 private static final String ATTR_APP_ID = "appId";
Svetoslav269403b2013-08-14 17:31:04 -0700798 private static final String ATTR_TAG = "tag";
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700799 private static final String ATTR_CREATION_TIME = "creationTime";
Svetoslav269403b2013-08-14 17:31:04 -0700800 private static final String ATTR_COPIES = "copies";
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700801 private static final String ATTR_PRINTER_NAME = "printerName";
802 private static final String ATTR_STATE_REASON = "stateReason";
Philip P. Moltmannb3078c22015-11-23 16:12:39 -0800803 private static final String ATTR_STATUS = "status";
804 private static final String ATTR_PROGRESS = "progress";
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700805 private static final String ATTR_CANCELLING = "cancelling";
Svetoslav269403b2013-08-14 17:31:04 -0700806
Svetoslavb4fda132013-10-25 18:57:43 -0700807 private static final String TAG_ADVANCED_OPTIONS = "advancedOptions";
808 private static final String TAG_ADVANCED_OPTION = "advancedOption";
809 private static final String ATTR_KEY = "key";
810 private static final String ATTR_TYPE = "type";
811 private static final String ATTR_VALUE = "value";
812 private static final String TYPE_STRING = "string";
813 private static final String TYPE_INT = "int";
814
Svetoslav269403b2013-08-14 17:31:04 -0700815 private static final String TAG_MEDIA_SIZE = "mediaSize";
816 private static final String TAG_RESOLUTION = "resolution";
817 private static final String TAG_MARGINS = "margins";
Svetoslav269403b2013-08-14 17:31:04 -0700818
Svetoslav269403b2013-08-14 17:31:04 -0700819 private static final String ATTR_COLOR_MODE = "colorMode";
Svetoslav948c9a62015-02-02 19:47:04 -0800820 private static final String ATTR_DUPLEX_MODE = "duplexMode";
Svetoslav269403b2013-08-14 17:31:04 -0700821
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700822 private static final String ATTR_LOCAL_ID = "localId";
Svetoslav269403b2013-08-14 17:31:04 -0700823 private static final String ATTR_SERVICE_NAME = "serviceName";
824
825 private static final String ATTR_WIDTH_MILS = "widthMils";
826 private static final String ATTR_HEIGHT_MILS = "heightMils";
827
828 private static final String ATTR_HORIZONTAL_DPI = "horizontalDip";
829 private static final String ATTR_VERTICAL_DPI = "verticalDpi";
830
831 private static final String ATTR_LEFT_MILS = "leftMils";
832 private static final String ATTR_TOP_MILS = "topMils";
833 private static final String ATTR_RIGHT_MILS = "rightMils";
834 private static final String ATTR_BOTTOM_MILS = "bottomMils";
835
836 private static final String ATTR_START = "start";
837 private static final String ATTR_END = "end";
838
839 private static final String ATTR_NAME = "name";
840 private static final String ATTR_PAGE_COUNT = "pageCount";
841 private static final String ATTR_CONTENT_TYPE = "contentType";
Svetoslav Ganov7d7888d2013-10-12 13:18:12 -0700842 private static final String ATTR_DATA_SIZE = "dataSize";
Svetoslav269403b2013-08-14 17:31:04 -0700843
844 private final AtomicFile mStatePersistFile;
845
846 private boolean mWriteStateScheduled;
847
848 private PersistenceManager() {
849 mStatePersistFile = new AtomicFile(new File(getFilesDir(),
850 PERSIST_FILE_NAME));
851 }
852
853 public void writeStateLocked() {
Svet Ganov4237c922014-10-24 12:53:23 -0700854 if (!PERSISTENCE_MANAGER_ENABLED) {
Svetoslav269403b2013-08-14 17:31:04 -0700855 return;
856 }
857 if (mWriteStateScheduled) {
858 return;
859 }
860 mWriteStateScheduled = true;
861 new AsyncTask<Void, Void, Void>() {
862 @Override
863 protected Void doInBackground(Void... params) {
864 synchronized (mLock) {
865 mWriteStateScheduled = false;
866 doWriteStateLocked();
867 }
868 return null;
869 }
870 }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
871 }
872
873 private void doWriteStateLocked() {
874 if (DEBUG_PERSISTENCE) {
875 Log.i(LOG_TAG, "[PERSIST START]");
876 }
877 FileOutputStream out = null;
878 try {
879 out = mStatePersistFile.startWrite();
880
881 XmlSerializer serializer = new FastXmlSerializer();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100882 serializer.setOutput(out, StandardCharsets.UTF_8.name());
Svetoslav269403b2013-08-14 17:31:04 -0700883 serializer.startDocument(null, true);
884 serializer.startTag(null, TAG_SPOOLER);
885
886 List<PrintJobInfo> printJobs = mPrintJobs;
887
888 final int printJobCount = printJobs.size();
889 for (int j = 0; j < printJobCount; j++) {
890 PrintJobInfo printJob = printJobs.get(j);
891
Svetoslav885810d2013-11-14 17:59:14 -0800892 if (!shouldPersistPrintJob(printJob)) {
893 continue;
894 }
895
Svetoslav269403b2013-08-14 17:31:04 -0700896 serializer.startTag(null, TAG_JOB);
897
Svetoslav2fbd2a72013-09-16 17:53:51 -0700898 serializer.attribute(null, ATTR_ID, printJob.getId().flattenToString());
Svetoslav269403b2013-08-14 17:31:04 -0700899 serializer.attribute(null, ATTR_LABEL, printJob.getLabel().toString());
900 serializer.attribute(null, ATTR_STATE, String.valueOf(printJob.getState()));
901 serializer.attribute(null, ATTR_APP_ID, String.valueOf(printJob.getAppId()));
Svetoslav269403b2013-08-14 17:31:04 -0700902 String tag = printJob.getTag();
903 if (tag != null) {
904 serializer.attribute(null, ATTR_TAG, tag);
905 }
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700906 serializer.attribute(null, ATTR_CREATION_TIME, String.valueOf(
907 printJob.getCreationTime()));
Svetoslav269403b2013-08-14 17:31:04 -0700908 serializer.attribute(null, ATTR_COPIES, String.valueOf(printJob.getCopies()));
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700909 String printerName = printJob.getPrinterName();
910 if (!TextUtils.isEmpty(printerName)) {
911 serializer.attribute(null, ATTR_PRINTER_NAME, printerName);
912 }
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700913 serializer.attribute(null, ATTR_CANCELLING, String.valueOf(
914 printJob.isCancelling()));
Svetoslav269403b2013-08-14 17:31:04 -0700915
Philip P. Moltmannb3078c22015-11-23 16:12:39 -0800916 float progress = printJob.getProgress();
Ian Rogers310ddc32016-05-27 13:50:05 -0700917 if (!Float.isNaN(progress)) {
Philip P. Moltmannb3078c22015-11-23 16:12:39 -0800918 serializer.attribute(null, ATTR_PROGRESS, String.valueOf(progress));
919 }
920
Philip P. Moltmannd74d1e52016-03-17 16:37:47 -0700921 CharSequence status = printJob.getStatus(getPackageManager());
Philip P. Moltmannb3078c22015-11-23 16:12:39 -0800922 if (!TextUtils.isEmpty(status)) {
923 serializer.attribute(null, ATTR_STATUS, status.toString());
924 }
925
Svetoslav269403b2013-08-14 17:31:04 -0700926 PrinterId printerId = printJob.getPrinterId();
927 if (printerId != null) {
928 serializer.startTag(null, TAG_PRINTER_ID);
929 serializer.attribute(null, ATTR_LOCAL_ID, printerId.getLocalId());
930 serializer.attribute(null, ATTR_SERVICE_NAME, printerId.getServiceName()
931 .flattenToString());
932 serializer.endTag(null, TAG_PRINTER_ID);
933 }
934
935 PageRange[] pages = printJob.getPages();
936 if (pages != null) {
937 for (int i = 0; i < pages.length; i++) {
938 serializer.startTag(null, TAG_PAGE_RANGE);
939 serializer.attribute(null, ATTR_START, String.valueOf(
940 pages[i].getStart()));
941 serializer.attribute(null, ATTR_END, String.valueOf(
942 pages[i].getEnd()));
943 serializer.endTag(null, TAG_PAGE_RANGE);
944 }
945 }
946
947 PrintAttributes attributes = printJob.getAttributes();
948 if (attributes != null) {
949 serializer.startTag(null, TAG_ATTRIBUTES);
950
Svetoslav269403b2013-08-14 17:31:04 -0700951 final int colorMode = attributes.getColorMode();
952 serializer.attribute(null, ATTR_COLOR_MODE,
953 String.valueOf(colorMode));
954
Svetoslav948c9a62015-02-02 19:47:04 -0800955 final int duplexMode = attributes.getDuplexMode();
956 serializer.attribute(null, ATTR_DUPLEX_MODE,
957 String.valueOf(duplexMode));
958
Svetoslav269403b2013-08-14 17:31:04 -0700959 MediaSize mediaSize = attributes.getMediaSize();
960 if (mediaSize != null) {
961 serializer.startTag(null, TAG_MEDIA_SIZE);
962 serializer.attribute(null, ATTR_ID, mediaSize.getId());
Svetoslav269403b2013-08-14 17:31:04 -0700963 serializer.attribute(null, ATTR_WIDTH_MILS, String.valueOf(
964 mediaSize.getWidthMils()));
965 serializer.attribute(null, ATTR_HEIGHT_MILS, String.valueOf(
966 mediaSize.getHeightMils()));
Svetoslava76233a2013-09-05 09:38:02 -0700967 // We prefer to store only the package name and
968 // resource id and fallback to the label.
969 if (!TextUtils.isEmpty(mediaSize.mPackageName)
Svetoslav773f54d2013-09-03 14:01:43 -0700970 && mediaSize.mLabelResId > 0) {
971 serializer.attribute(null, ATTR_PACKAGE_NAME,
972 mediaSize.mPackageName);
973 serializer.attribute(null, ATTR_LABEL_RES_ID,
974 String.valueOf(mediaSize.mLabelResId));
975 } else {
976 serializer.attribute(null, ATTR_LABEL,
977 mediaSize.getLabel(getPackageManager()));
978 }
Svetoslav269403b2013-08-14 17:31:04 -0700979 serializer.endTag(null, TAG_MEDIA_SIZE);
980 }
981
982 Resolution resolution = attributes.getResolution();
983 if (resolution != null) {
984 serializer.startTag(null, TAG_RESOLUTION);
985 serializer.attribute(null, ATTR_ID, resolution.getId());
Svetoslav269403b2013-08-14 17:31:04 -0700986 serializer.attribute(null, ATTR_HORIZONTAL_DPI, String.valueOf(
987 resolution.getHorizontalDpi()));
988 serializer.attribute(null, ATTR_VERTICAL_DPI, String.valueOf(
989 resolution.getVerticalDpi()));
Svetoslavc6066792013-09-10 21:08:32 -0700990 serializer.attribute(null, ATTR_LABEL,
Svetoslav651dd4e2013-09-12 14:37:47 -0700991 resolution.getLabel());
Svetoslav269403b2013-08-14 17:31:04 -0700992 serializer.endTag(null, TAG_RESOLUTION);
993 }
994
Svetoslav651dd4e2013-09-12 14:37:47 -0700995 Margins margins = attributes.getMinMargins();
Svetoslav269403b2013-08-14 17:31:04 -0700996 if (margins != null) {
997 serializer.startTag(null, TAG_MARGINS);
998 serializer.attribute(null, ATTR_LEFT_MILS, String.valueOf(
999 margins.getLeftMils()));
1000 serializer.attribute(null, ATTR_TOP_MILS, String.valueOf(
1001 margins.getTopMils()));
1002 serializer.attribute(null, ATTR_RIGHT_MILS, String.valueOf(
1003 margins.getRightMils()));
1004 serializer.attribute(null, ATTR_BOTTOM_MILS, String.valueOf(
1005 margins.getBottomMils()));
1006 serializer.endTag(null, TAG_MARGINS);
1007 }
1008
Svetoslav269403b2013-08-14 17:31:04 -07001009 serializer.endTag(null, TAG_ATTRIBUTES);
1010 }
1011
1012 PrintDocumentInfo documentInfo = printJob.getDocumentInfo();
1013 if (documentInfo != null) {
1014 serializer.startTag(null, TAG_DOCUMENT_INFO);
1015 serializer.attribute(null, ATTR_NAME, documentInfo.getName());
1016 serializer.attribute(null, ATTR_CONTENT_TYPE, String.valueOf(
1017 documentInfo.getContentType()));
1018 serializer.attribute(null, ATTR_PAGE_COUNT, String.valueOf(
1019 documentInfo.getPageCount()));
Svetoslav Ganov7d7888d2013-10-12 13:18:12 -07001020 serializer.attribute(null, ATTR_DATA_SIZE, String.valueOf(
1021 documentInfo.getDataSize()));
Svetoslav269403b2013-08-14 17:31:04 -07001022 serializer.endTag(null, TAG_DOCUMENT_INFO);
1023 }
1024
Svetoslavb4fda132013-10-25 18:57:43 -07001025 Bundle advancedOptions = printJob.getAdvancedOptions();
1026 if (advancedOptions != null) {
1027 serializer.startTag(null, TAG_ADVANCED_OPTIONS);
1028 for (String key : advancedOptions.keySet()) {
1029 Object value = advancedOptions.get(key);
1030 if (value instanceof String) {
1031 String stringValue = (String) value;
1032 serializer.startTag(null, TAG_ADVANCED_OPTION);
1033 serializer.attribute(null, ATTR_KEY, key);
1034 serializer.attribute(null, ATTR_TYPE, TYPE_STRING);
1035 serializer.attribute(null, ATTR_VALUE, stringValue);
1036 serializer.endTag(null, TAG_ADVANCED_OPTION);
1037 } else if (value instanceof Integer) {
1038 String intValue = Integer.toString((Integer) value);
1039 serializer.startTag(null, TAG_ADVANCED_OPTION);
1040 serializer.attribute(null, ATTR_KEY, key);
1041 serializer.attribute(null, ATTR_TYPE, TYPE_INT);
1042 serializer.attribute(null, ATTR_VALUE, intValue);
1043 serializer.endTag(null, TAG_ADVANCED_OPTION);
1044 }
1045 }
1046 serializer.endTag(null, TAG_ADVANCED_OPTIONS);
1047 }
1048
Svetoslav269403b2013-08-14 17:31:04 -07001049 serializer.endTag(null, TAG_JOB);
1050
1051 if (DEBUG_PERSISTENCE) {
1052 Log.i(LOG_TAG, "[PERSISTED] " + printJob);
1053 }
1054 }
1055
1056 serializer.endTag(null, TAG_SPOOLER);
1057 serializer.endDocument();
1058 mStatePersistFile.finishWrite(out);
1059 if (DEBUG_PERSISTENCE) {
1060 Log.i(LOG_TAG, "[PERSIST END]");
1061 }
1062 } catch (IOException e) {
1063 Slog.w(LOG_TAG, "Failed to write state, restoring backup.", e);
1064 mStatePersistFile.failWrite(out);
1065 } finally {
1066 IoUtils.closeQuietly(out);
1067 }
1068 }
1069
1070 public void readStateLocked() {
Svet Ganov4237c922014-10-24 12:53:23 -07001071 if (!PERSISTENCE_MANAGER_ENABLED) {
Svetoslav269403b2013-08-14 17:31:04 -07001072 return;
1073 }
1074 FileInputStream in = null;
1075 try {
1076 in = mStatePersistFile.openRead();
1077 } catch (FileNotFoundException e) {
Joe Onorato13460a62016-03-18 13:34:13 -07001078 if (DEBUG_PERSISTENCE) {
1079 Log.d(LOG_TAG, "No existing print spooler state.");
1080 }
Svetoslav269403b2013-08-14 17:31:04 -07001081 return;
1082 }
1083 try {
1084 XmlPullParser parser = Xml.newPullParser();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +01001085 parser.setInput(in, StandardCharsets.UTF_8.name());
Svetoslav269403b2013-08-14 17:31:04 -07001086 parseState(parser);
1087 } catch (IllegalStateException ise) {
1088 Slog.w(LOG_TAG, "Failed parsing ", ise);
1089 } catch (NullPointerException npe) {
1090 Slog.w(LOG_TAG, "Failed parsing ", npe);
1091 } catch (NumberFormatException nfe) {
1092 Slog.w(LOG_TAG, "Failed parsing ", nfe);
1093 } catch (XmlPullParserException xppe) {
1094 Slog.w(LOG_TAG, "Failed parsing ", xppe);
1095 } catch (IOException ioe) {
1096 Slog.w(LOG_TAG, "Failed parsing ", ioe);
1097 } catch (IndexOutOfBoundsException iobe) {
1098 Slog.w(LOG_TAG, "Failed parsing ", iobe);
1099 } finally {
1100 IoUtils.closeQuietly(in);
1101 }
1102 }
1103
1104 private void parseState(XmlPullParser parser)
1105 throws IOException, XmlPullParserException {
1106 parser.next();
1107 skipEmptyTextTags(parser);
1108 expect(parser, XmlPullParser.START_TAG, TAG_SPOOLER);
1109 parser.next();
1110
1111 while (parsePrintJob(parser)) {
1112 parser.next();
1113 }
1114
1115 skipEmptyTextTags(parser);
1116 expect(parser, XmlPullParser.END_TAG, TAG_SPOOLER);
1117 }
1118
1119 private boolean parsePrintJob(XmlPullParser parser)
1120 throws IOException, XmlPullParserException {
1121 skipEmptyTextTags(parser);
1122 if (!accept(parser, XmlPullParser.START_TAG, TAG_JOB)) {
1123 return false;
1124 }
1125
1126 PrintJobInfo printJob = new PrintJobInfo();
1127
Svetoslav2fbd2a72013-09-16 17:53:51 -07001128 PrintJobId printJobId = PrintJobId.unflattenFromString(
1129 parser.getAttributeValue(null, ATTR_ID));
Svetoslav269403b2013-08-14 17:31:04 -07001130 printJob.setId(printJobId);
1131 String label = parser.getAttributeValue(null, ATTR_LABEL);
1132 printJob.setLabel(label);
1133 final int state = Integer.parseInt(parser.getAttributeValue(null, ATTR_STATE));
1134 printJob.setState(state);
1135 final int appId = Integer.parseInt(parser.getAttributeValue(null, ATTR_APP_ID));
1136 printJob.setAppId(appId);
Svetoslav269403b2013-08-14 17:31:04 -07001137 String tag = parser.getAttributeValue(null, ATTR_TAG);
1138 printJob.setTag(tag);
Svetoslav Ganov704697b2013-09-21 20:30:24 -07001139 String creationTime = parser.getAttributeValue(null, ATTR_CREATION_TIME);
1140 printJob.setCreationTime(Long.parseLong(creationTime));
Svetoslav269403b2013-08-14 17:31:04 -07001141 String copies = parser.getAttributeValue(null, ATTR_COPIES);
1142 printJob.setCopies(Integer.parseInt(copies));
Svetoslav Ganov704697b2013-09-21 20:30:24 -07001143 String printerName = parser.getAttributeValue(null, ATTR_PRINTER_NAME);
1144 printJob.setPrinterName(printerName);
Philip P. Moltmannb3078c22015-11-23 16:12:39 -08001145
1146 String progressString = parser.getAttributeValue(null, ATTR_PROGRESS);
1147 if (progressString != null) {
1148 float progress = Float.parseFloat(progressString);
1149
Philip P. Moltmanna958fc32015-12-02 15:39:09 -08001150 if (progress != -1) {
Philip P. Moltmannb3078c22015-11-23 16:12:39 -08001151 printJob.setProgress(progress);
1152 }
1153 }
1154
1155 CharSequence status = parser.getAttributeValue(null, ATTR_STATUS);
1156 printJob.setStatus(status);
1157
1158 // stateReason is deprecated, but might be used by old print jobs
Svetoslav Ganov704697b2013-09-21 20:30:24 -07001159 String stateReason = parser.getAttributeValue(null, ATTR_STATE_REASON);
Philip P. Moltmannb3078c22015-11-23 16:12:39 -08001160 if (stateReason != null) {
1161 printJob.setStatus(stateReason);
1162 }
1163
Svetoslav Ganova18661d2013-10-09 22:55:49 -07001164 String cancelling = parser.getAttributeValue(null, ATTR_CANCELLING);
1165 printJob.setCancelling(!TextUtils.isEmpty(cancelling)
1166 ? Boolean.parseBoolean(cancelling) : false);
Svetoslav269403b2013-08-14 17:31:04 -07001167
1168 parser.next();
1169
1170 skipEmptyTextTags(parser);
1171 if (accept(parser, XmlPullParser.START_TAG, TAG_PRINTER_ID)) {
1172 String localId = parser.getAttributeValue(null, ATTR_LOCAL_ID);
1173 ComponentName service = ComponentName.unflattenFromString(parser.getAttributeValue(
1174 null, ATTR_SERVICE_NAME));
1175 printJob.setPrinterId(new PrinterId(service, localId));
1176 parser.next();
1177 skipEmptyTextTags(parser);
1178 expect(parser, XmlPullParser.END_TAG, TAG_PRINTER_ID);
1179 parser.next();
1180 }
1181
1182 skipEmptyTextTags(parser);
1183 List<PageRange> pageRanges = null;
1184 while (accept(parser, XmlPullParser.START_TAG, TAG_PAGE_RANGE)) {
1185 final int start = Integer.parseInt(parser.getAttributeValue(null, ATTR_START));
1186 final int end = Integer.parseInt(parser.getAttributeValue(null, ATTR_END));
1187 PageRange pageRange = new PageRange(start, end);
1188 if (pageRanges == null) {
1189 pageRanges = new ArrayList<PageRange>();
1190 }
1191 pageRanges.add(pageRange);
1192 parser.next();
1193 skipEmptyTextTags(parser);
1194 expect(parser, XmlPullParser.END_TAG, TAG_PAGE_RANGE);
1195 parser.next();
Svetoslavb4fda132013-10-25 18:57:43 -07001196 skipEmptyTextTags(parser);
Svetoslav269403b2013-08-14 17:31:04 -07001197 }
1198 if (pageRanges != null) {
1199 PageRange[] pageRangesArray = new PageRange[pageRanges.size()];
1200 pageRanges.toArray(pageRangesArray);
1201 printJob.setPages(pageRangesArray);
1202 }
1203
1204 skipEmptyTextTags(parser);
1205 if (accept(parser, XmlPullParser.START_TAG, TAG_ATTRIBUTES)) {
1206
1207 PrintAttributes.Builder builder = new PrintAttributes.Builder();
1208
Svetoslav269403b2013-08-14 17:31:04 -07001209 String colorMode = parser.getAttributeValue(null, ATTR_COLOR_MODE);
1210 builder.setColorMode(Integer.parseInt(colorMode));
1211
Svetoslav948c9a62015-02-02 19:47:04 -08001212 String duplexMode = parser.getAttributeValue(null, ATTR_DUPLEX_MODE);
1213 // Duplex mode was added later, so null check is needed.
1214 if (duplexMode != null) {
1215 builder.setDuplexMode(Integer.parseInt(duplexMode));
1216 }
1217
Svetoslav269403b2013-08-14 17:31:04 -07001218 parser.next();
1219
1220 skipEmptyTextTags(parser);
1221 if (accept(parser, XmlPullParser.START_TAG, TAG_MEDIA_SIZE)) {
1222 String id = parser.getAttributeValue(null, ATTR_ID);
1223 label = parser.getAttributeValue(null, ATTR_LABEL);
1224 final int widthMils = Integer.parseInt(parser.getAttributeValue(null,
1225 ATTR_WIDTH_MILS));
1226 final int heightMils = Integer.parseInt(parser.getAttributeValue(null,
1227 ATTR_HEIGHT_MILS));
Svetoslav773f54d2013-09-03 14:01:43 -07001228 String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
Svetoslavb206f122013-09-20 10:43:24 -07001229 String labelResIdString = parser.getAttributeValue(null, ATTR_LABEL_RES_ID);
1230 final int labelResId = (labelResIdString != null)
1231 ? Integer.parseInt(labelResIdString) : 0;
Svetoslav773f54d2013-09-03 14:01:43 -07001232 label = parser.getAttributeValue(null, ATTR_LABEL);
Svetoslavb4fda132013-10-25 18:57:43 -07001233 MediaSize mediaSize = new MediaSize(id, label, packageName,
1234 widthMils, heightMils, labelResId);
Svetoslav269403b2013-08-14 17:31:04 -07001235 builder.setMediaSize(mediaSize);
1236 parser.next();
1237 skipEmptyTextTags(parser);
1238 expect(parser, XmlPullParser.END_TAG, TAG_MEDIA_SIZE);
1239 parser.next();
1240 }
1241
1242 skipEmptyTextTags(parser);
1243 if (accept(parser, XmlPullParser.START_TAG, TAG_RESOLUTION)) {
1244 String id = parser.getAttributeValue(null, ATTR_ID);
1245 label = parser.getAttributeValue(null, ATTR_LABEL);
1246 final int horizontalDpi = Integer.parseInt(parser.getAttributeValue(null,
1247 ATTR_HORIZONTAL_DPI));
1248 final int verticalDpi = Integer.parseInt(parser.getAttributeValue(null,
1249 ATTR_VERTICAL_DPI));
Svetoslavc6066792013-09-10 21:08:32 -07001250 Resolution resolution = new Resolution(id, label, horizontalDpi, verticalDpi);
Svetoslav269403b2013-08-14 17:31:04 -07001251 builder.setResolution(resolution);
1252 parser.next();
1253 skipEmptyTextTags(parser);
1254 expect(parser, XmlPullParser.END_TAG, TAG_RESOLUTION);
1255 parser.next();
1256 }
1257
1258 skipEmptyTextTags(parser);
1259 if (accept(parser, XmlPullParser.START_TAG, TAG_MARGINS)) {
1260 final int leftMils = Integer.parseInt(parser.getAttributeValue(null,
1261 ATTR_LEFT_MILS));
1262 final int topMils = Integer.parseInt(parser.getAttributeValue(null,
1263 ATTR_TOP_MILS));
1264 final int rightMils = Integer.parseInt(parser.getAttributeValue(null,
1265 ATTR_RIGHT_MILS));
1266 final int bottomMils = Integer.parseInt(parser.getAttributeValue(null,
1267 ATTR_BOTTOM_MILS));
1268 Margins margins = new Margins(leftMils, topMils, rightMils, bottomMils);
Svetoslav651dd4e2013-09-12 14:37:47 -07001269 builder.setMinMargins(margins);
Svetoslav269403b2013-08-14 17:31:04 -07001270 parser.next();
1271 skipEmptyTextTags(parser);
1272 expect(parser, XmlPullParser.END_TAG, TAG_MARGINS);
1273 parser.next();
1274 }
1275
Svetoslav651dd4e2013-09-12 14:37:47 -07001276 printJob.setAttributes(builder.build());
Svetoslav269403b2013-08-14 17:31:04 -07001277
1278 skipEmptyTextTags(parser);
1279 expect(parser, XmlPullParser.END_TAG, TAG_ATTRIBUTES);
1280 parser.next();
1281 }
1282
1283 skipEmptyTextTags(parser);
1284 if (accept(parser, XmlPullParser.START_TAG, TAG_DOCUMENT_INFO)) {
1285 String name = parser.getAttributeValue(null, ATTR_NAME);
1286 final int pageCount = Integer.parseInt(parser.getAttributeValue(null,
1287 ATTR_PAGE_COUNT));
1288 final int contentType = Integer.parseInt(parser.getAttributeValue(null,
1289 ATTR_CONTENT_TYPE));
Svetoslav Ganov7d7888d2013-10-12 13:18:12 -07001290 final int dataSize = Integer.parseInt(parser.getAttributeValue(null,
1291 ATTR_DATA_SIZE));
Svetoslav269403b2013-08-14 17:31:04 -07001292 PrintDocumentInfo info = new PrintDocumentInfo.Builder(name)
1293 .setPageCount(pageCount)
Svetoslav651dd4e2013-09-12 14:37:47 -07001294 .setContentType(contentType).build();
Svetoslav269403b2013-08-14 17:31:04 -07001295 printJob.setDocumentInfo(info);
Svetoslav Ganov7d7888d2013-10-12 13:18:12 -07001296 info.setDataSize(dataSize);
Svetoslav269403b2013-08-14 17:31:04 -07001297 parser.next();
1298 skipEmptyTextTags(parser);
1299 expect(parser, XmlPullParser.END_TAG, TAG_DOCUMENT_INFO);
1300 parser.next();
1301 }
1302
Svetoslavb4fda132013-10-25 18:57:43 -07001303 skipEmptyTextTags(parser);
1304 if (accept(parser, XmlPullParser.START_TAG, TAG_ADVANCED_OPTIONS)) {
1305 parser.next();
1306 skipEmptyTextTags(parser);
1307 Bundle advancedOptions = new Bundle();
1308 while (accept(parser, XmlPullParser.START_TAG, TAG_ADVANCED_OPTION)) {
1309 String key = parser.getAttributeValue(null, ATTR_KEY);
1310 String value = parser.getAttributeValue(null, ATTR_VALUE);
1311 String type = parser.getAttributeValue(null, ATTR_TYPE);
1312 if (TYPE_STRING.equals(type)) {
1313 advancedOptions.putString(key, value);
1314 } else if (TYPE_INT.equals(type)) {
Tobias Thierer9bbbf2b2016-04-21 12:02:10 +01001315 advancedOptions.putInt(key, Integer.parseInt(value));
Svetoslavb4fda132013-10-25 18:57:43 -07001316 }
1317 parser.next();
1318 skipEmptyTextTags(parser);
1319 expect(parser, XmlPullParser.END_TAG, TAG_ADVANCED_OPTION);
1320 parser.next();
1321 skipEmptyTextTags(parser);
1322 }
1323 printJob.setAdvancedOptions(advancedOptions);
1324 skipEmptyTextTags(parser);
1325 expect(parser, XmlPullParser.END_TAG, TAG_ADVANCED_OPTIONS);
1326 parser.next();
1327 }
1328
Svetoslav269403b2013-08-14 17:31:04 -07001329 mPrintJobs.add(printJob);
1330
1331 if (DEBUG_PERSISTENCE) {
1332 Log.i(LOG_TAG, "[RESTORED] " + printJob);
1333 }
1334
1335 skipEmptyTextTags(parser);
1336 expect(parser, XmlPullParser.END_TAG, TAG_JOB);
1337
1338 return true;
1339 }
1340
1341 private void expect(XmlPullParser parser, int type, String tag)
Philip P. Moltmannc43639c2015-12-18 13:58:40 -08001342 throws XmlPullParserException {
Svetoslav269403b2013-08-14 17:31:04 -07001343 if (!accept(parser, type, tag)) {
1344 throw new XmlPullParserException("Exepected event: " + type
1345 + " and tag: " + tag + " but got event: " + parser.getEventType()
1346 + " and tag:" + parser.getName());
1347 }
1348 }
1349
1350 private void skipEmptyTextTags(XmlPullParser parser)
1351 throws IOException, XmlPullParserException {
1352 while (accept(parser, XmlPullParser.TEXT, null)
1353 && "\n".equals(parser.getText())) {
1354 parser.next();
1355 }
1356 }
1357
1358 private boolean accept(XmlPullParser parser, int type, String tag)
Philip P. Moltmannc43639c2015-12-18 13:58:40 -08001359 throws XmlPullParserException {
Svetoslav269403b2013-08-14 17:31:04 -07001360 if (parser.getEventType() != type) {
1361 return false;
1362 }
1363 if (tag != null) {
1364 if (!tag.equals(parser.getName())) {
1365 return false;
1366 }
1367 } else if (parser.getName() != null) {
1368 return false;
1369 }
1370 return true;
1371 }
1372 }
Svetoslav7bfbbcb2013-10-10 13:36:23 -07001373
Svetoslava798c0a2014-05-15 10:47:19 -07001374 public final class PrintSpooler extends IPrintSpooler.Stub {
Svetoslav7bfbbcb2013-10-10 13:36:23 -07001375 @Override
1376 public void getPrintJobInfos(IPrintSpoolerCallbacks callback,
1377 ComponentName componentName, int state, int appId, int sequence)
1378 throws RemoteException {
1379 List<PrintJobInfo> printJobs = null;
1380 try {
1381 printJobs = PrintSpoolerService.this.getPrintJobInfos(
1382 componentName, state, appId);
1383 } finally {
1384 callback.onGetPrintJobInfosResult(printJobs, sequence);
1385 }
1386 }
1387
1388 @Override
1389 public void getPrintJobInfo(PrintJobId printJobId, IPrintSpoolerCallbacks callback,
1390 int appId, int sequence) throws RemoteException {
1391 PrintJobInfo printJob = null;
1392 try {
1393 printJob = PrintSpoolerService.this.getPrintJobInfo(printJobId, appId);
1394 } finally {
1395 callback.onGetPrintJobInfoResult(printJob, sequence);
1396 }
1397 }
1398
1399 @Override
1400 public void createPrintJob(PrintJobInfo printJob) {
1401 PrintSpoolerService.this.createPrintJob(printJob);
1402 }
1403
1404 @Override
1405 public void setPrintJobState(PrintJobId printJobId, int state, String error,
1406 IPrintSpoolerCallbacks callback, int sequece) throws RemoteException {
1407 boolean success = false;
1408 try {
1409 success = PrintSpoolerService.this.setPrintJobState(
1410 printJobId, state, error);
1411 } finally {
1412 callback.onSetPrintJobStateResult(success, sequece);
1413 }
1414 }
1415
1416 @Override
1417 public void setPrintJobTag(PrintJobId printJobId, String tag,
1418 IPrintSpoolerCallbacks callback, int sequece) throws RemoteException {
1419 boolean success = false;
1420 try {
1421 success = PrintSpoolerService.this.setPrintJobTag(printJobId, tag);
1422 } finally {
1423 callback.onSetPrintJobTagResult(success, sequece);
1424 }
1425 }
1426
1427 @Override
1428 public void writePrintJobData(ParcelFileDescriptor fd, PrintJobId printJobId) {
1429 PrintSpoolerService.this.writePrintJobData(fd, printJobId);
1430 }
1431
1432 @Override
1433 public void setClient(IPrintSpoolerClient client) {
1434 Message message = mHandlerCaller.obtainMessageO(
1435 HandlerCallerCallback.MSG_SET_CLIENT, client);
1436 mHandlerCaller.executeOrSendMessage(message);
1437 }
1438
1439 @Override
1440 public void removeObsoletePrintJobs() {
1441 PrintSpoolerService.this.removeObsoletePrintJobs();
1442 }
1443
1444 @Override
1445 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
1446 PrintSpoolerService.this.dump(fd, writer, args);
1447 }
1448
1449 @Override
1450 public void setPrintJobCancelling(PrintJobId printJobId, boolean cancelling) {
1451 PrintSpoolerService.this.setPrintJobCancelling(printJobId, cancelling);
1452 }
1453
Philip P. Moltmann853a6f52015-11-03 10:38:56 -08001454 @Override
Philip P. Moltmann8141bdf2015-12-21 17:03:05 -08001455 public void pruneApprovedPrintServices(List<ComponentName> servicesToKeep) {
Philip P. Moltmann853a6f52015-11-03 10:38:56 -08001456 (new ApprovedPrintServices(PrintSpoolerService.this))
Philip P. Moltmann8141bdf2015-12-21 17:03:05 -08001457 .pruneApprovedServices(servicesToKeep);
Philip P. Moltmann853a6f52015-11-03 10:38:56 -08001458 }
1459
Philip P. Moltmannb3078c22015-11-23 16:12:39 -08001460 @Override
1461 public void setProgress(@NonNull PrintJobId printJobId,
1462 @FloatRange(from=0.0, to=1.0) float progress) throws RemoteException {
1463 PrintSpoolerService.this.setProgress(printJobId, progress);
1464 }
1465
1466 @Override
1467 public void setStatus(@NonNull PrintJobId printJobId,
1468 @Nullable CharSequence status) throws RemoteException {
1469 PrintSpoolerService.this.setStatus(printJobId, status);
1470 }
1471
Philip P. Moltmannd74d1e52016-03-17 16:37:47 -07001472 @Override
1473 public void setStatusRes(@NonNull PrintJobId printJobId, @StringRes int status,
1474 @NonNull CharSequence appPackageName) throws RemoteException {
1475 PrintSpoolerService.this.setStatus(printJobId, status, appPackageName);
1476 }
1477
1478
Svetoslav7bfbbcb2013-10-10 13:36:23 -07001479 public PrintSpoolerService getService() {
1480 return PrintSpoolerService.this;
1481 }
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -08001482
1483 @Override
1484 public void onCustomPrinterIconLoaded(PrinterId printerId, Icon icon,
1485 IPrintSpoolerCallbacks callbacks, int sequence)
1486 throws RemoteException {
1487 try {
1488 PrintSpoolerService.this.onCustomPrinterIconLoaded(printerId, icon);
1489 } finally {
1490 callbacks.onCustomPrinterIconCached(sequence);
1491 }
1492 }
1493
1494 @Override
1495 public void getCustomPrinterIcon(PrinterId printerId, IPrintSpoolerCallbacks callbacks,
1496 int sequence) throws RemoteException {
1497 Icon icon = null;
1498 try {
1499 icon = PrintSpoolerService.this.getCustomPrinterIcon(printerId);
1500 } finally {
1501 callbacks.onGetCustomPrinterIconResult(icon, sequence);
1502 }
1503 }
1504
1505 @Override
1506 public void clearCustomPrinterIconCache(IPrintSpoolerCallbacks callbacks,
1507 int sequence) throws RemoteException {
1508 try {
1509 PrintSpoolerService.this.clearCustomPrinterIconCache();
1510 } finally {
1511 callbacks.customPrinterIconCacheCleared(sequence);
1512 }
1513 }
1514
Svetoslav7bfbbcb2013-10-10 13:36:23 -07001515 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -07001516}