blob: e0a3f6cb31cac61bc256b63ddc492fc55ffcd1a0 [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. Moltmannc0a128d2017-06-19 10:55:09 -070019import static com.android.internal.print.DumpUtils.writeComponentName;
20import static com.android.internal.print.DumpUtils.writePrintJobInfo;
21
Philip P. Moltmannb3078c22015-11-23 16:12:39 -080022import android.annotation.FloatRange;
23import android.annotation.NonNull;
24import android.annotation.Nullable;
Philip P. Moltmannd74d1e52016-03-17 16:37:47 -070025import android.annotation.StringRes;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070026import android.app.Service;
27import android.content.ComponentName;
Svetoslava798c0a2014-05-15 10:47:19 -070028import android.content.Context;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070029import android.content.Intent;
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -080030import android.graphics.drawable.Icon;
Svetoslav269403b2013-08-14 17:31:04 -070031import android.os.AsyncTask;
Philip P. Moltmannc0a128d2017-06-19 10:55:09 -070032import android.os.Binder;
Svetoslavb4fda132013-10-25 18:57:43 -070033import android.os.Bundle;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070034import android.os.IBinder;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070035import android.os.Message;
36import android.os.ParcelFileDescriptor;
37import android.os.RemoteException;
Svetoslav Ganova0027152013-06-25 14:59:53 -070038import android.print.IPrintSpooler;
39import android.print.IPrintSpoolerCallbacks;
Svetoslav Ganov835835e2013-08-04 20:17:52 -070040import android.print.IPrintSpoolerClient;
Svetoslav269403b2013-08-14 17:31:04 -070041import android.print.PageRange;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070042import android.print.PrintAttributes;
Svetoslav269403b2013-08-14 17:31:04 -070043import android.print.PrintAttributes.Margins;
44import android.print.PrintAttributes.MediaSize;
45import android.print.PrintAttributes.Resolution;
Svetoslav269403b2013-08-14 17:31:04 -070046import android.print.PrintDocumentInfo;
Svetoslav2fbd2a72013-09-16 17:53:51 -070047import android.print.PrintJobId;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070048import android.print.PrintJobInfo;
Svetoslav269403b2013-08-14 17:31:04 -070049import android.print.PrintManager;
50import android.print.PrinterId;
Philip P. Moltmannc0a128d2017-06-19 10:55:09 -070051import android.service.print.PrintSpoolerInternalStateProto;
Svetoslava76233a2013-09-05 09:38:02 -070052import android.text.TextUtils;
Svetoslav Ganovdd68da22013-09-27 10:48:31 -070053import android.util.ArrayMap;
Svetoslav269403b2013-08-14 17:31:04 -070054import android.util.AtomicFile;
Svetoslav Ganov835835e2013-08-04 20:17:52 -070055import android.util.Log;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070056import android.util.Slog;
Svetoslav269403b2013-08-14 17:31:04 -070057import android.util.Xml;
Philip P. Moltmannc0a128d2017-06-19 10:55:09 -070058import android.util.proto.ProtoOutputStream;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070059
Chris Wrendcc34fd2015-07-30 14:27:02 -040060import com.android.internal.logging.MetricsLogger;
Svetoslav269403b2013-08-14 17:31:04 -070061import com.android.internal.os.HandlerCaller;
Svetoslav269403b2013-08-14 17:31:04 -070062import com.android.internal.util.FastXmlSerializer;
Philip P. Moltmannc0a128d2017-06-19 10:55:09 -070063import com.android.internal.util.Preconditions;
Svetoslava798c0a2014-05-15 10:47:19 -070064import com.android.printspooler.R;
Philip P. Moltmann853a6f52015-11-03 10:38:56 -080065import com.android.printspooler.util.ApprovedPrintServices;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070066
Svetoslav2fbd2a72013-09-16 17:53:51 -070067import libcore.io.IoUtils;
68
Svetoslav269403b2013-08-14 17:31:04 -070069import org.xmlpull.v1.XmlPullParser;
70import org.xmlpull.v1.XmlPullParserException;
71import org.xmlpull.v1.XmlSerializer;
72
73import java.io.File;
Svetoslav Ganovdd68da22013-09-27 10:48:31 -070074import java.io.FileDescriptor;
Svetoslav269403b2013-08-14 17:31:04 -070075import java.io.FileInputStream;
76import java.io.FileNotFoundException;
77import java.io.FileOutputStream;
78import java.io.IOException;
Svetoslav Ganovdd68da22013-09-27 10:48:31 -070079import java.io.PrintWriter;
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +010080import java.nio.charset.StandardCharsets;
Svetoslav269403b2013-08-14 17:31:04 -070081import java.util.ArrayList;
Svetoslav Ganov835835e2013-08-04 20:17:52 -070082import java.util.List;
Philip P. Moltmann853a6f52015-11-03 10:38:56 -080083import java.util.Set;
Svetoslav Ganov835835e2013-08-04 20:17:52 -070084
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070085/**
86 * Service for exposing some of the {@link PrintSpooler} functionality to
87 * another process.
88 */
89public final class PrintSpoolerService extends Service {
90
Svetoslav269403b2013-08-14 17:31:04 -070091 private static final String LOG_TAG = "PrintSpoolerService";
92
Svetoslavb5f18062013-09-23 18:48:34 -070093 private static final boolean DEBUG_PRINT_JOB_LIFECYCLE = false;
Svetoslav269403b2013-08-14 17:31:04 -070094
Svetoslavc6066792013-09-10 21:08:32 -070095 private static final boolean DEBUG_PERSISTENCE = false;
Svetoslav269403b2013-08-14 17:31:04 -070096
Svet Ganov4237c922014-10-24 12:53:23 -070097 private static final boolean PERSISTENCE_MANAGER_ENABLED = true;
Svetoslav269403b2013-08-14 17:31:04 -070098
Philip P. Moltmann2e301262016-06-16 12:39:54 -070099 private static final String PRINT_JOB_STATE_HISTO = "print_job_state";
100
Svetoslav Ganov835835e2013-08-04 20:17:52 -0700101 private static final long CHECK_ALL_PRINTJOBS_HANDLED_DELAY = 5000;
102
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700103 private static final String PRINT_JOB_FILE_PREFIX = "print_job_";
104
Svetoslav269403b2013-08-14 17:31:04 -0700105 private static final String PRINT_FILE_EXTENSION = "pdf";
106
107 private static final Object sLock = new Object();
108
109 private final Object mLock = new Object();
110
Svetoslava798c0a2014-05-15 10:47:19 -0700111 private final List<PrintJobInfo> mPrintJobs = new ArrayList<>();
Svetoslav269403b2013-08-14 17:31:04 -0700112
113 private static PrintSpoolerService sInstance;
114
Svetoslav Ganov835835e2013-08-04 20:17:52 -0700115 private IPrintSpoolerClient mClient;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700116
Svetoslav269403b2013-08-14 17:31:04 -0700117 private HandlerCaller mHandlerCaller;
118
119 private PersistenceManager mPersistanceManager;
120
121 private NotificationController mNotificationController;
122
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -0800123 /** Cache for custom printer icons loaded from the print service */
124 private CustomPrinterIconCache mCustomIconCache;
125
Svetoslav269403b2013-08-14 17:31:04 -0700126 public static PrintSpoolerService peekInstance() {
127 synchronized (sLock) {
128 return sInstance;
129 }
130 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700131
132 @Override
133 public void onCreate() {
134 super.onCreate();
Svetoslav269403b2013-08-14 17:31:04 -0700135 mHandlerCaller = new HandlerCaller(this, getMainLooper(),
136 new HandlerCallerCallback(), false);
137
138 mPersistanceManager = new PersistenceManager();
139 mNotificationController = new NotificationController(PrintSpoolerService.this);
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -0800140 mCustomIconCache = new CustomPrinterIconCache(getCacheDir());
Svetoslav269403b2013-08-14 17:31:04 -0700141
142 synchronized (mLock) {
143 mPersistanceManager.readStateLocked();
144 handleReadPrintJobsLocked();
145 }
146
147 synchronized (sLock) {
148 sInstance = this;
149 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700150 }
151
152 @Override
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -0800153 public void onDestroy() {
154 super.onDestroy();
155 }
156
157 @Override
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700158 public IBinder onBind(Intent intent) {
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700159 return new PrintSpooler();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700160 }
161
Philip P. Moltmannc0a128d2017-06-19 10:55:09 -0700162 private void dumpLocked(PrintWriter pw, String[] args) {
Philip P. Moltmann853a6f52015-11-03 10:38:56 -0800163 String prefix = (args.length > 0) ? args[0] : "";
164 String tab = " ";
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700165
Philip P. Moltmannc0a128d2017-06-19 10:55:09 -0700166 pw.append(prefix).append("print jobs:").println();
167 final int printJobCount = mPrintJobs.size();
168 for (int i = 0; i < printJobCount; i++) {
169 PrintJobInfo printJob = mPrintJobs.get(i);
170 pw.append(prefix).append(tab).append(printJob.toString());
171 pw.println();
172 }
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700173
Philip P. Moltmannc0a128d2017-06-19 10:55:09 -0700174 pw.append(prefix).append("print job files:").println();
175 File[] files = getFilesDir().listFiles();
176 if (files != null) {
177 final int fileCount = files.length;
178 for (int i = 0; i < fileCount; i++) {
179 File file = files[i];
180 if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) {
181 pw.append(prefix).append(tab).append(file.getName()).println();
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700182 }
183 }
184 }
Philip P. Moltmann853a6f52015-11-03 10:38:56 -0800185
186 pw.append(prefix).append("approved print services:").println();
187 Set<String> approvedPrintServices = (new ApprovedPrintServices(this)).getApprovedServices();
188 if (approvedPrintServices != null) {
189 for (String approvedService : approvedPrintServices) {
190 pw.append(prefix).append(tab).append(approvedService).println();
191 }
192 }
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700193 }
194
Philip P. Moltmannc0a128d2017-06-19 10:55:09 -0700195 private void dumpLocked(@NonNull ProtoOutputStream proto) {
196 int numPrintJobs = mPrintJobs.size();
197 for (int i = 0; i < numPrintJobs; i++) {
198 writePrintJobInfo(this, proto, PrintSpoolerInternalStateProto.PRINT_JOBS,
199 mPrintJobs.get(i));
200 }
201
202 File[] files = getFilesDir().listFiles();
203 if (files != null) {
204 for (int i = 0; i < files.length; i++) {
205 File file = files[i];
206 if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) {
207 proto.write(PrintSpoolerInternalStateProto.PRINT_JOB_FILES, file.getName());
208 }
209 }
210 }
211
212 Set<String> approvedPrintServices = (new ApprovedPrintServices(this)).getApprovedServices();
213 if (approvedPrintServices != null) {
214 for (String approvedService : approvedPrintServices) {
215 ComponentName componentName = ComponentName.unflattenFromString(approvedService);
216 if (componentName != null) {
217 writeComponentName(proto, PrintSpoolerInternalStateProto.APPROVED_SERVICES,
218 componentName);
219 }
220 }
221 }
222
223 proto.flush();
224 }
225
226 @Override
227 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
228 fd = Preconditions.checkNotNull(fd);
229
230 int opti = 0;
231 boolean dumpAsProto = false;
232 while (opti < args.length) {
233 String opt = args[opti];
234 if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') {
235 break;
236 }
237 opti++;
238 if ("--proto".equals(opt)) {
239 dumpAsProto = true;
240 }
241 }
242
243 final long identity = Binder.clearCallingIdentity();
244 try {
245 synchronized (mLock) {
246 if (dumpAsProto) {
247 dumpLocked(new ProtoOutputStream(fd));
248 } else {
249 dumpLocked(pw, args);
250 }
251 }
252 } finally {
253 Binder.restoreCallingIdentity(identity);
254 }
255 }
256
Svetoslav269403b2013-08-14 17:31:04 -0700257 private void sendOnPrintJobQueued(PrintJobInfo printJob) {
258 Message message = mHandlerCaller.obtainMessageO(
259 HandlerCallerCallback.MSG_ON_PRINT_JOB_QUEUED, printJob);
260 mHandlerCaller.executeOrSendMessage(message);
261 }
262
263 private void sendOnAllPrintJobsForServiceHandled(ComponentName service) {
264 Message message = mHandlerCaller.obtainMessageO(
265 HandlerCallerCallback.MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED, service);
266 mHandlerCaller.executeOrSendMessage(message);
267 }
268
269 private void sendOnAllPrintJobsHandled() {
270 Message message = mHandlerCaller.obtainMessage(
271 HandlerCallerCallback.MSG_ON_ALL_PRINT_JOBS_HANDLED);
272 mHandlerCaller.executeOrSendMessage(message);
273 }
274
275 private final class HandlerCallerCallback implements HandlerCaller.Callback {
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700276 public static final int MSG_SET_CLIENT = 1;
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700277 public static final int MSG_ON_PRINT_JOB_QUEUED = 2;
278 public static final int MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED = 3;
279 public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 4;
280 public static final int MSG_CHECK_ALL_PRINTJOBS_HANDLED = 5;
281 public static final int MSG_ON_PRINT_JOB_STATE_CHANGED = 6;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700282
283 @Override
Svetoslav269403b2013-08-14 17:31:04 -0700284 public void executeMessage(Message message) {
Svetoslav Ganov835835e2013-08-04 20:17:52 -0700285 switch (message.what) {
286 case MSG_SET_CLIENT: {
Svetoslav269403b2013-08-14 17:31:04 -0700287 synchronized (mLock) {
288 mClient = (IPrintSpoolerClient) message.obj;
289 if (mClient != null) {
290 Message msg = mHandlerCaller.obtainMessage(
291 HandlerCallerCallback.MSG_CHECK_ALL_PRINTJOBS_HANDLED);
292 mHandlerCaller.sendMessageDelayed(msg,
293 CHECK_ALL_PRINTJOBS_HANDLED_DELAY);
294 }
Svetoslav Ganov835835e2013-08-04 20:17:52 -0700295 }
296 } break;
297
Svetoslav Ganov835835e2013-08-04 20:17:52 -0700298 case MSG_ON_PRINT_JOB_QUEUED: {
299 PrintJobInfo printJob = (PrintJobInfo) message.obj;
300 if (mClient != null) {
301 try {
302 mClient.onPrintJobQueued(printJob);
303 } catch (RemoteException re) {
304 Slog.e(LOG_TAG, "Error notify for a queued print job.", re);
305 }
306 }
307 } break;
308
309 case MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED: {
310 ComponentName service = (ComponentName) message.obj;
311 if (mClient != null) {
312 try {
313 mClient.onAllPrintJobsForServiceHandled(service);
314 } catch (RemoteException re) {
315 Slog.e(LOG_TAG, "Error notify for all print jobs per service"
316 + " handled.", re);
317 }
318 }
319 } break;
320
321 case MSG_ON_ALL_PRINT_JOBS_HANDLED: {
322 if (mClient != null) {
323 try {
324 mClient.onAllPrintJobsHandled();
325 } catch (RemoteException re) {
326 Slog.e(LOG_TAG, "Error notify for all print job handled.", re);
327 }
328 }
329 } break;
330
Svetoslav Ganov835835e2013-08-04 20:17:52 -0700331 case MSG_CHECK_ALL_PRINTJOBS_HANDLED: {
Svetoslav269403b2013-08-14 17:31:04 -0700332 checkAllPrintJobsHandled();
333 } break;
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700334
335 case MSG_ON_PRINT_JOB_STATE_CHANGED: {
336 if (mClient != null) {
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700337 PrintJobInfo printJob = (PrintJobInfo) message.obj;
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700338 try {
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700339 mClient.onPrintJobStateChanged(printJob);
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700340 } catch (RemoteException re) {
341 Slog.e(LOG_TAG, "Error notify for print job state change.", re);
342 }
343 }
344 } break;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700345 }
346 }
347 }
Svetoslav269403b2013-08-14 17:31:04 -0700348
349 public List<PrintJobInfo> getPrintJobInfos(ComponentName componentName,
350 int state, int appId) {
351 List<PrintJobInfo> foundPrintJobs = null;
352 synchronized (mLock) {
353 final int printJobCount = mPrintJobs.size();
354 for (int i = 0; i < printJobCount; i++) {
355 PrintJobInfo printJob = mPrintJobs.get(i);
356 PrinterId printerId = printJob.getPrinterId();
357 final boolean sameComponent = (componentName == null
358 || (printerId != null
359 && componentName.equals(printerId.getServiceName())));
360 final boolean sameAppId = appId == PrintManager.APP_ID_ANY
361 || printJob.getAppId() == appId;
362 final boolean sameState = (state == printJob.getState())
363 || (state == PrintJobInfo.STATE_ANY)
364 || (state == PrintJobInfo.STATE_ANY_VISIBLE_TO_CLIENTS
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700365 && isStateVisibleToUser(printJob.getState()))
366 || (state == PrintJobInfo.STATE_ANY_ACTIVE
Svetoslav Ganov9b6d3a12013-10-12 12:35:41 -0700367 && isActiveState(printJob.getState()))
368 || (state == PrintJobInfo.STATE_ANY_SCHEDULED
369 && isScheduledState(printJob.getState()));
Svetoslav269403b2013-08-14 17:31:04 -0700370 if (sameComponent && sameAppId && sameState) {
371 if (foundPrintJobs == null) {
Svetoslava798c0a2014-05-15 10:47:19 -0700372 foundPrintJobs = new ArrayList<>();
Svetoslav269403b2013-08-14 17:31:04 -0700373 }
374 foundPrintJobs.add(printJob);
375 }
376 }
377 }
378 return foundPrintJobs;
379 }
380
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700381 private boolean isStateVisibleToUser(int state) {
382 return (isActiveState(state) && (state == PrintJobInfo.STATE_FAILED
Svetoslav2fbd2a72013-09-16 17:53:51 -0700383 || state == PrintJobInfo.STATE_COMPLETED || state == PrintJobInfo.STATE_CANCELED
384 || state == PrintJobInfo.STATE_BLOCKED));
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700385 }
386
Svetoslav2fbd2a72013-09-16 17:53:51 -0700387 public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId) {
Svetoslav269403b2013-08-14 17:31:04 -0700388 synchronized (mLock) {
389 final int printJobCount = mPrintJobs.size();
390 for (int i = 0; i < printJobCount; i++) {
391 PrintJobInfo printJob = mPrintJobs.get(i);
Svetoslav2fbd2a72013-09-16 17:53:51 -0700392 if (printJob.getId().equals(printJobId)
Svetoslav269403b2013-08-14 17:31:04 -0700393 && (appId == PrintManager.APP_ID_ANY
394 || appId == printJob.getAppId())) {
395 return printJob;
396 }
397 }
398 return null;
399 }
400 }
401
Svetoslav2fbd2a72013-09-16 17:53:51 -0700402 public void createPrintJob(PrintJobInfo printJob) {
Svetoslav269403b2013-08-14 17:31:04 -0700403 synchronized (mLock) {
Svetoslav269403b2013-08-14 17:31:04 -0700404 addPrintJobLocked(printJob);
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700405 setPrintJobState(printJob.getId(), PrintJobInfo.STATE_CREATED, null);
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700406
407 Message message = mHandlerCaller.obtainMessageO(
408 HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
409 printJob);
410 mHandlerCaller.executeOrSendMessage(message);
Svetoslav269403b2013-08-14 17:31:04 -0700411 }
412 }
413
414 private void handleReadPrintJobsLocked() {
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700415 // Make a map with the files for a print job since we may have
416 // to delete some. One example of getting orphan files if the
417 // spooler crashes while constructing a print job. We do not
418 // persist partially populated print jobs under construction to
419 // avoid special handling for various attributes missing.
420 ArrayMap<PrintJobId, File> fileForJobMap = null;
421 File[] files = getFilesDir().listFiles();
422 if (files != null) {
423 final int fileCount = files.length;
424 for (int i = 0; i < fileCount; i++) {
425 File file = files[i];
426 if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) {
427 if (fileForJobMap == null) {
428 fileForJobMap = new ArrayMap<PrintJobId, File>();
429 }
Svetoslav Ganov2b40c832013-10-04 08:49:03 -0700430 String printJobIdString = file.getName().substring(
431 PRINT_JOB_FILE_PREFIX.length(),
432 file.getName().indexOf('.'));
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700433 PrintJobId printJobId = PrintJobId.unflattenFromString(
434 printJobIdString);
435 fileForJobMap.put(printJobId, file);
436 }
437 }
438 }
439
Svetoslav269403b2013-08-14 17:31:04 -0700440 final int printJobCount = mPrintJobs.size();
441 for (int i = 0; i < printJobCount; i++) {
442 PrintJobInfo printJob = mPrintJobs.get(i);
443
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700444 // We want to have only the orphan files at the end.
445 if (fileForJobMap != null) {
446 fileForJobMap.remove(printJob.getId());
447 }
448
Svetoslav269403b2013-08-14 17:31:04 -0700449 switch (printJob.getState()) {
450 case PrintJobInfo.STATE_QUEUED:
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700451 case PrintJobInfo.STATE_STARTED:
452 case PrintJobInfo.STATE_BLOCKED: {
453 // We have a print job that was queued or started or blocked in
454 // the past but the device battery died or a crash occurred. In
455 // this case we assume the print job failed and let the user
456 // decide whether to restart the job or just cancel it.
Svetoslav269403b2013-08-14 17:31:04 -0700457 setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED,
458 getString(R.string.no_connection_to_printer));
Svetoslav2fbd2a72013-09-16 17:53:51 -0700459 } break;
Svetoslav269403b2013-08-14 17:31:04 -0700460 }
461 }
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700462
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700463 if (!mPrintJobs.isEmpty()) {
464 // Update the notification.
465 mNotificationController.onUpdateNotifications(mPrintJobs);
466 }
467
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700468 // Delete the orphan files.
469 if (fileForJobMap != null) {
470 final int orphanFileCount = fileForJobMap.size();
471 for (int i = 0; i < orphanFileCount; i++) {
472 File file = fileForJobMap.valueAt(i);
473 file.delete();
474 }
475 }
Svetoslav269403b2013-08-14 17:31:04 -0700476 }
477
478 public void checkAllPrintJobsHandled() {
479 synchronized (mLock) {
480 if (!hasActivePrintJobsLocked()) {
481 notifyOnAllPrintJobsHandled();
482 }
483 }
484 }
485
Svetoslav2fbd2a72013-09-16 17:53:51 -0700486 public void writePrintJobData(final ParcelFileDescriptor fd, final PrintJobId printJobId) {
Svetoslav269403b2013-08-14 17:31:04 -0700487 final PrintJobInfo printJob;
488 synchronized (mLock) {
489 printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
490 }
491 new AsyncTask<Void, Void, Void>() {
492 @Override
493 protected Void doInBackground(Void... params) {
494 FileInputStream in = null;
495 FileOutputStream out = null;
496 try {
497 if (printJob != null) {
Svetoslava798c0a2014-05-15 10:47:19 -0700498 File file = generateFileForPrintJob(PrintSpoolerService.this, printJobId);
Svetoslav269403b2013-08-14 17:31:04 -0700499 in = new FileInputStream(file);
500 out = new FileOutputStream(fd.getFileDescriptor());
501 }
502 final byte[] buffer = new byte[8192];
503 while (true) {
504 final int readByteCount = in.read(buffer);
505 if (readByteCount < 0) {
506 return null;
507 }
508 out.write(buffer, 0, readByteCount);
509 }
510 } catch (FileNotFoundException fnfe) {
511 Log.e(LOG_TAG, "Error writing print job data!", fnfe);
512 } catch (IOException ioe) {
513 Log.e(LOG_TAG, "Error writing print job data!", ioe);
514 } finally {
515 IoUtils.closeQuietly(in);
516 IoUtils.closeQuietly(out);
517 IoUtils.closeQuietly(fd);
518 }
519 Log.i(LOG_TAG, "[END WRITE]");
520 return null;
521 }
522 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
523 }
524
Svetoslava798c0a2014-05-15 10:47:19 -0700525 public static File generateFileForPrintJob(Context context, PrintJobId printJobId) {
526 return new File(context.getFilesDir(), PRINT_JOB_FILE_PREFIX
Svetoslav2fbd2a72013-09-16 17:53:51 -0700527 + printJobId.flattenToString() + "." + PRINT_FILE_EXTENSION);
Svetoslav269403b2013-08-14 17:31:04 -0700528 }
529
530 private void addPrintJobLocked(PrintJobInfo printJob) {
531 mPrintJobs.add(printJob);
532 if (DEBUG_PRINT_JOB_LIFECYCLE) {
533 Slog.i(LOG_TAG, "[ADD] " + printJob);
534 }
535 }
536
Svetoslav2fbd2a72013-09-16 17:53:51 -0700537 private void removeObsoletePrintJobs() {
538 synchronized (mLock) {
Svetoslav885810d2013-11-14 17:59:14 -0800539 boolean persistState = false;
Svetoslav2fbd2a72013-09-16 17:53:51 -0700540 final int printJobCount = mPrintJobs.size();
541 for (int i = printJobCount - 1; i >= 0; i--) {
542 PrintJobInfo printJob = mPrintJobs.get(i);
543 if (isObsoleteState(printJob.getState())) {
544 mPrintJobs.remove(i);
545 if (DEBUG_PRINT_JOB_LIFECYCLE) {
546 Slog.i(LOG_TAG, "[REMOVE] " + printJob.getId().flattenToString());
547 }
548 removePrintJobFileLocked(printJob.getId());
Svetoslav885810d2013-11-14 17:59:14 -0800549 persistState = true;
Svetoslav2fbd2a72013-09-16 17:53:51 -0700550 }
551 }
Svetoslav885810d2013-11-14 17:59:14 -0800552 if (persistState) {
553 mPersistanceManager.writeStateLocked();
554 }
Svetoslav2fbd2a72013-09-16 17:53:51 -0700555 }
556 }
557
558 private void removePrintJobFileLocked(PrintJobId printJobId) {
Svetoslava798c0a2014-05-15 10:47:19 -0700559 File file = generateFileForPrintJob(PrintSpoolerService.this, printJobId);
Svetoslav2fbd2a72013-09-16 17:53:51 -0700560 if (file.exists()) {
561 file.delete();
562 if (DEBUG_PRINT_JOB_LIFECYCLE) {
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700563 Slog.i(LOG_TAG, "[REMOVE FILE FOR] " + printJobId);
Svetoslav2fbd2a72013-09-16 17:53:51 -0700564 }
565 }
566 }
567
Philip P. Moltmann4bd8fac2016-04-21 15:17:58 -0700568 /**
569 * Notify all interested parties that a print job has been updated.
570 *
571 * @param printJob The updated print job.
572 */
573 private void notifyPrintJobUpdated(PrintJobInfo printJob) {
574 Message message = mHandlerCaller.obtainMessageO(
575 HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
576 printJob);
577 mHandlerCaller.executeOrSendMessage(message);
578
579 mNotificationController.onUpdateNotifications(mPrintJobs);
580 }
581
Svetoslav2fbd2a72013-09-16 17:53:51 -0700582 public boolean setPrintJobState(PrintJobId printJobId, int state, String error) {
Svetoslav269403b2013-08-14 17:31:04 -0700583 boolean success = false;
584
585 synchronized (mLock) {
586 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
587 if (printJob != null) {
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700588 final int oldState = printJob.getState();
589 if (oldState == state) {
590 return false;
591 }
592
Svetoslav269403b2013-08-14 17:31:04 -0700593 success = true;
594
595 printJob.setState(state);
Philip P. Moltmannb3078c22015-11-23 16:12:39 -0800596 printJob.setStatus(error);
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700597 printJob.setCancelling(false);
Svetoslav269403b2013-08-14 17:31:04 -0700598
599 if (DEBUG_PRINT_JOB_LIFECYCLE) {
600 Slog.i(LOG_TAG, "[STATE CHANGED] " + printJob);
601 }
602
Philip P. Moltmann2e301262016-06-16 12:39:54 -0700603 MetricsLogger.histogram(this, PRINT_JOB_STATE_HISTO, state);
Svetoslav269403b2013-08-14 17:31:04 -0700604 switch (state) {
605 case PrintJobInfo.STATE_COMPLETED:
606 case PrintJobInfo.STATE_CANCELED:
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700607 mPrintJobs.remove(printJob);
Svetoslav2fbd2a72013-09-16 17:53:51 -0700608 removePrintJobFileLocked(printJob.getId());
Svetoslav269403b2013-08-14 17:31:04 -0700609 // $fall-through$
610
611 case PrintJobInfo.STATE_FAILED: {
612 PrinterId printerId = printJob.getPrinterId();
613 if (printerId != null) {
614 ComponentName service = printerId.getServiceName();
615 if (!hasActivePrintJobsForServiceLocked(service)) {
616 sendOnAllPrintJobsForServiceHandled(service);
617 }
618 }
619 } break;
620
621 case PrintJobInfo.STATE_QUEUED: {
622 sendOnPrintJobQueued(new PrintJobInfo(printJob));
623 } break;
624 }
625
626 if (shouldPersistPrintJob(printJob)) {
627 mPersistanceManager.writeStateLocked();
628 }
629
630 if (!hasActivePrintJobsLocked()) {
631 notifyOnAllPrintJobsHandled();
632 }
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700633
Philip P. Moltmann4bd8fac2016-04-21 15:17:58 -0700634 notifyPrintJobUpdated(printJob);
Svetoslav269403b2013-08-14 17:31:04 -0700635 }
636 }
637
638 return success;
639 }
640
Philip P. Moltmannb3078c22015-11-23 16:12:39 -0800641 /**
642 * Set the progress for a print job.
643 *
644 * @param printJobId ID of the print job to update
645 * @param progress the new progress
646 */
647 public void setProgress(@NonNull PrintJobId printJobId,
648 @FloatRange(from=0.0, to=1.0) float progress) {
649 synchronized (mLock) {
650 getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY).setProgress(progress);
651
652 mNotificationController.onUpdateNotifications(mPrintJobs);
653 }
654 }
655
Philip P. Moltmannd74d1e52016-03-17 16:37:47 -0700656 /**
657 * Set the status for a print job.
658 *
659 * @param printJobId ID of the print job to update
660 * @param status the new status
661 */
662 public void setStatus(@NonNull PrintJobId printJobId, @Nullable CharSequence status) {
663 synchronized (mLock) {
Philip P. Moltmann4bd8fac2016-04-21 15:17:58 -0700664 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
Philip P. Moltmannb3078c22015-11-23 16:12:39 -0800665
Philip P. Moltmann4bd8fac2016-04-21 15:17:58 -0700666 if (printJob != null) {
667 printJob.setStatus(status);
668 notifyPrintJobUpdated(printJob);
669 }
Philip P. Moltmannd74d1e52016-03-17 16:37:47 -0700670 }
671 }
672
673 /**
674 * Set the status for a print job.
675 *
676 * @param printJobId ID of the print job to update
677 * @param status the new status as a string resource
678 * @param appPackageName app package the resource belongs to
679 */
680 public void setStatus(@NonNull PrintJobId printJobId, @StringRes int status,
681 @Nullable CharSequence appPackageName) {
682 synchronized (mLock) {
Philip P. Moltmann4bd8fac2016-04-21 15:17:58 -0700683 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
Philip P. Moltmannd74d1e52016-03-17 16:37:47 -0700684
Philip P. Moltmann4bd8fac2016-04-21 15:17:58 -0700685 if (printJob != null) {
686 printJob.setStatus(status, appPackageName);
687 notifyPrintJobUpdated(printJob);
688 }
Philip P. Moltmannd74d1e52016-03-17 16:37:47 -0700689 }
690 }
Philip P. Moltmannb3078c22015-11-23 16:12:39 -0800691
Svetoslav269403b2013-08-14 17:31:04 -0700692 public boolean hasActivePrintJobsLocked() {
693 final int printJobCount = mPrintJobs.size();
694 for (int i = 0; i < printJobCount; i++) {
695 PrintJobInfo printJob = mPrintJobs.get(i);
696 if (isActiveState(printJob.getState())) {
697 return true;
698 }
699 }
700 return false;
701 }
702
703 public boolean hasActivePrintJobsForServiceLocked(ComponentName service) {
704 final int printJobCount = mPrintJobs.size();
705 for (int i = 0; i < printJobCount; i++) {
706 PrintJobInfo printJob = mPrintJobs.get(i);
Svetoslav75d28502013-11-04 18:25:05 -0800707 if (isActiveState(printJob.getState()) && printJob.getPrinterId() != null
Svetoslav269403b2013-08-14 17:31:04 -0700708 && printJob.getPrinterId().getServiceName().equals(service)) {
709 return true;
710 }
711 }
712 return false;
713 }
714
Svetoslav2fbd2a72013-09-16 17:53:51 -0700715 private boolean isObsoleteState(int printJobState) {
Svetoslava798c0a2014-05-15 10:47:19 -0700716 return (isTerminalState(printJobState)
Svetoslav2fbd2a72013-09-16 17:53:51 -0700717 || printJobState == PrintJobInfo.STATE_QUEUED);
718 }
719
Svetoslav Ganov9b6d3a12013-10-12 12:35:41 -0700720 private boolean isScheduledState(int printJobState) {
721 return printJobState == PrintJobInfo.STATE_QUEUED
722 || printJobState == PrintJobInfo.STATE_STARTED
723 || printJobState == PrintJobInfo.STATE_BLOCKED;
724 }
725
Svetoslav269403b2013-08-14 17:31:04 -0700726 private boolean isActiveState(int printJobState) {
727 return printJobState == PrintJobInfo.STATE_CREATED
728 || printJobState == PrintJobInfo.STATE_QUEUED
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700729 || printJobState == PrintJobInfo.STATE_STARTED
730 || printJobState == PrintJobInfo.STATE_BLOCKED;
Svetoslav269403b2013-08-14 17:31:04 -0700731 }
732
Svetoslava798c0a2014-05-15 10:47:19 -0700733 private boolean isTerminalState(int printJobState) {
Svetoslav2fbd2a72013-09-16 17:53:51 -0700734 return printJobState == PrintJobInfo.STATE_COMPLETED
735 || printJobState == PrintJobInfo.STATE_CANCELED;
736 }
737
738 public boolean setPrintJobTag(PrintJobId printJobId, String tag) {
Svetoslav269403b2013-08-14 17:31:04 -0700739 synchronized (mLock) {
740 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
741 if (printJob != null) {
742 String printJobTag = printJob.getTag();
743 if (printJobTag == null) {
744 if (tag == null) {
745 return false;
746 }
747 } else if (printJobTag.equals(tag)) {
748 return false;
749 }
750 printJob.setTag(tag);
751 if (shouldPersistPrintJob(printJob)) {
752 mPersistanceManager.writeStateLocked();
753 }
754 return true;
755 }
756 }
757 return false;
758 }
759
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700760 public void setPrintJobCancelling(PrintJobId printJobId, boolean cancelling) {
761 synchronized (mLock) {
762 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
763 if (printJob != null) {
764 printJob.setCancelling(cancelling);
765 if (shouldPersistPrintJob(printJob)) {
766 mPersistanceManager.writeStateLocked();
767 }
768 mNotificationController.onUpdateNotifications(mPrintJobs);
769
770 Message message = mHandlerCaller.obtainMessageO(
771 HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
772 printJob);
773 mHandlerCaller.executeOrSendMessage(message);
774 }
775 }
776 }
777
Svetoslava798c0a2014-05-15 10:47:19 -0700778 public void updatePrintJobUserConfigurableOptionsNoPersistence(PrintJobInfo printJob) {
Svetoslav269403b2013-08-14 17:31:04 -0700779 synchronized (mLock) {
Svetoslava798c0a2014-05-15 10:47:19 -0700780 final int printJobCount = mPrintJobs.size();
781 for (int i = 0; i < printJobCount; i++) {
782 PrintJobInfo cachedPrintJob = mPrintJobs.get(i);
783 if (cachedPrintJob.getId().equals(printJob.getId())) {
784 cachedPrintJob.setPrinterId(printJob.getPrinterId());
785 cachedPrintJob.setPrinterName(printJob.getPrinterName());
786 cachedPrintJob.setCopies(printJob.getCopies());
787 cachedPrintJob.setDocumentInfo(printJob.getDocumentInfo());
788 cachedPrintJob.setPages(printJob.getPages());
789 cachedPrintJob.setAttributes(printJob.getAttributes());
790 cachedPrintJob.setAdvancedOptions(printJob.getAdvancedOptions());
791 return;
792 }
Svetoslav269403b2013-08-14 17:31:04 -0700793 }
Svetoslava798c0a2014-05-15 10:47:19 -0700794 throw new IllegalArgumentException("No print job with id:" + printJob.getId());
Svetoslav269403b2013-08-14 17:31:04 -0700795 }
796 }
797
798 private boolean shouldPersistPrintJob(PrintJobInfo printJob) {
799 return printJob.getState() >= PrintJobInfo.STATE_QUEUED;
800 }
801
802 private void notifyOnAllPrintJobsHandled() {
803 // This has to run on the tread that is persisting the current state
804 // since this call may result in the system unbinding from the spooler
805 // and as a result the spooler process may get killed before the write
806 // completes.
807 new AsyncTask<Void, Void, Void>() {
808 @Override
809 protected Void doInBackground(Void... params) {
810 sendOnAllPrintJobsHandled();
811 return null;
812 }
813 }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
814 }
815
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -0800816 /**
817 * Handle that a custom icon for a printer was loaded.
818 *
819 * @param printerId the id of the printer the icon belongs to
820 * @param icon the icon that was loaded
Philip P. Moltmannc0a128d2017-06-19 10:55:09 -0700821 * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -0800822 */
823 public void onCustomPrinterIconLoaded(PrinterId printerId, Icon icon) {
824 mCustomIconCache.onCustomPrinterIconLoaded(printerId, icon);
825 }
826
827 /**
828 * Get the custom icon for a printer. If the icon is not cached, the icon is
829 * requested asynchronously. Once it is available the printer is updated.
830 *
831 * @param printerId the id of the printer the icon should be loaded for
832 * @return the custom icon to be used for the printer or null if the icon is
833 * not yet available
Philip P. Moltmannc0a128d2017-06-19 10:55:09 -0700834 * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -0800835 */
836 public Icon getCustomPrinterIcon(PrinterId printerId) {
837 return mCustomIconCache.getIcon(printerId);
838 }
839
840 /**
841 * Clear the custom printer icon cache.
842 */
843 public void clearCustomPrinterIconCache() {
844 mCustomIconCache.clear();
845 }
846
Svetoslav269403b2013-08-14 17:31:04 -0700847 private final class PersistenceManager {
848 private static final String PERSIST_FILE_NAME = "print_spooler_state.xml";
849
850 private static final String TAG_SPOOLER = "spooler";
851 private static final String TAG_JOB = "job";
852
853 private static final String TAG_PRINTER_ID = "printerId";
854 private static final String TAG_PAGE_RANGE = "pageRange";
855 private static final String TAG_ATTRIBUTES = "attributes";
856 private static final String TAG_DOCUMENT_INFO = "documentInfo";
857
858 private static final String ATTR_ID = "id";
859 private static final String ATTR_LABEL = "label";
Svetoslav773f54d2013-09-03 14:01:43 -0700860 private static final String ATTR_LABEL_RES_ID = "labelResId";
861 private static final String ATTR_PACKAGE_NAME = "packageName";
Svetoslav269403b2013-08-14 17:31:04 -0700862 private static final String ATTR_STATE = "state";
863 private static final String ATTR_APP_ID = "appId";
Svetoslav269403b2013-08-14 17:31:04 -0700864 private static final String ATTR_TAG = "tag";
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700865 private static final String ATTR_CREATION_TIME = "creationTime";
Svetoslav269403b2013-08-14 17:31:04 -0700866 private static final String ATTR_COPIES = "copies";
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700867 private static final String ATTR_PRINTER_NAME = "printerName";
868 private static final String ATTR_STATE_REASON = "stateReason";
Philip P. Moltmannb3078c22015-11-23 16:12:39 -0800869 private static final String ATTR_STATUS = "status";
870 private static final String ATTR_PROGRESS = "progress";
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700871 private static final String ATTR_CANCELLING = "cancelling";
Svetoslav269403b2013-08-14 17:31:04 -0700872
Svetoslavb4fda132013-10-25 18:57:43 -0700873 private static final String TAG_ADVANCED_OPTIONS = "advancedOptions";
874 private static final String TAG_ADVANCED_OPTION = "advancedOption";
875 private static final String ATTR_KEY = "key";
876 private static final String ATTR_TYPE = "type";
877 private static final String ATTR_VALUE = "value";
878 private static final String TYPE_STRING = "string";
879 private static final String TYPE_INT = "int";
880
Svetoslav269403b2013-08-14 17:31:04 -0700881 private static final String TAG_MEDIA_SIZE = "mediaSize";
882 private static final String TAG_RESOLUTION = "resolution";
883 private static final String TAG_MARGINS = "margins";
Svetoslav269403b2013-08-14 17:31:04 -0700884
Svetoslav269403b2013-08-14 17:31:04 -0700885 private static final String ATTR_COLOR_MODE = "colorMode";
Svetoslav948c9a62015-02-02 19:47:04 -0800886 private static final String ATTR_DUPLEX_MODE = "duplexMode";
Svetoslav269403b2013-08-14 17:31:04 -0700887
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700888 private static final String ATTR_LOCAL_ID = "localId";
Svetoslav269403b2013-08-14 17:31:04 -0700889 private static final String ATTR_SERVICE_NAME = "serviceName";
890
891 private static final String ATTR_WIDTH_MILS = "widthMils";
892 private static final String ATTR_HEIGHT_MILS = "heightMils";
893
894 private static final String ATTR_HORIZONTAL_DPI = "horizontalDip";
895 private static final String ATTR_VERTICAL_DPI = "verticalDpi";
896
897 private static final String ATTR_LEFT_MILS = "leftMils";
898 private static final String ATTR_TOP_MILS = "topMils";
899 private static final String ATTR_RIGHT_MILS = "rightMils";
900 private static final String ATTR_BOTTOM_MILS = "bottomMils";
901
902 private static final String ATTR_START = "start";
903 private static final String ATTR_END = "end";
904
905 private static final String ATTR_NAME = "name";
906 private static final String ATTR_PAGE_COUNT = "pageCount";
907 private static final String ATTR_CONTENT_TYPE = "contentType";
Svetoslav Ganov7d7888d2013-10-12 13:18:12 -0700908 private static final String ATTR_DATA_SIZE = "dataSize";
Svetoslav269403b2013-08-14 17:31:04 -0700909
910 private final AtomicFile mStatePersistFile;
911
912 private boolean mWriteStateScheduled;
913
914 private PersistenceManager() {
915 mStatePersistFile = new AtomicFile(new File(getFilesDir(),
916 PERSIST_FILE_NAME));
917 }
918
919 public void writeStateLocked() {
Svet Ganov4237c922014-10-24 12:53:23 -0700920 if (!PERSISTENCE_MANAGER_ENABLED) {
Svetoslav269403b2013-08-14 17:31:04 -0700921 return;
922 }
923 if (mWriteStateScheduled) {
924 return;
925 }
926 mWriteStateScheduled = true;
927 new AsyncTask<Void, Void, Void>() {
928 @Override
929 protected Void doInBackground(Void... params) {
930 synchronized (mLock) {
931 mWriteStateScheduled = false;
932 doWriteStateLocked();
933 }
934 return null;
935 }
936 }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
937 }
938
939 private void doWriteStateLocked() {
940 if (DEBUG_PERSISTENCE) {
941 Log.i(LOG_TAG, "[PERSIST START]");
942 }
943 FileOutputStream out = null;
944 try {
945 out = mStatePersistFile.startWrite();
946
947 XmlSerializer serializer = new FastXmlSerializer();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100948 serializer.setOutput(out, StandardCharsets.UTF_8.name());
Svetoslav269403b2013-08-14 17:31:04 -0700949 serializer.startDocument(null, true);
950 serializer.startTag(null, TAG_SPOOLER);
951
952 List<PrintJobInfo> printJobs = mPrintJobs;
953
954 final int printJobCount = printJobs.size();
955 for (int j = 0; j < printJobCount; j++) {
956 PrintJobInfo printJob = printJobs.get(j);
957
Svetoslav885810d2013-11-14 17:59:14 -0800958 if (!shouldPersistPrintJob(printJob)) {
959 continue;
960 }
961
Svetoslav269403b2013-08-14 17:31:04 -0700962 serializer.startTag(null, TAG_JOB);
963
Svetoslav2fbd2a72013-09-16 17:53:51 -0700964 serializer.attribute(null, ATTR_ID, printJob.getId().flattenToString());
Svetoslav269403b2013-08-14 17:31:04 -0700965 serializer.attribute(null, ATTR_LABEL, printJob.getLabel().toString());
966 serializer.attribute(null, ATTR_STATE, String.valueOf(printJob.getState()));
967 serializer.attribute(null, ATTR_APP_ID, String.valueOf(printJob.getAppId()));
Svetoslav269403b2013-08-14 17:31:04 -0700968 String tag = printJob.getTag();
969 if (tag != null) {
970 serializer.attribute(null, ATTR_TAG, tag);
971 }
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700972 serializer.attribute(null, ATTR_CREATION_TIME, String.valueOf(
973 printJob.getCreationTime()));
Svetoslav269403b2013-08-14 17:31:04 -0700974 serializer.attribute(null, ATTR_COPIES, String.valueOf(printJob.getCopies()));
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700975 String printerName = printJob.getPrinterName();
976 if (!TextUtils.isEmpty(printerName)) {
977 serializer.attribute(null, ATTR_PRINTER_NAME, printerName);
978 }
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700979 serializer.attribute(null, ATTR_CANCELLING, String.valueOf(
980 printJob.isCancelling()));
Svetoslav269403b2013-08-14 17:31:04 -0700981
Philip P. Moltmannb3078c22015-11-23 16:12:39 -0800982 float progress = printJob.getProgress();
Ian Rogers310ddc32016-05-27 13:50:05 -0700983 if (!Float.isNaN(progress)) {
Philip P. Moltmannb3078c22015-11-23 16:12:39 -0800984 serializer.attribute(null, ATTR_PROGRESS, String.valueOf(progress));
985 }
986
Philip P. Moltmannd74d1e52016-03-17 16:37:47 -0700987 CharSequence status = printJob.getStatus(getPackageManager());
Philip P. Moltmannb3078c22015-11-23 16:12:39 -0800988 if (!TextUtils.isEmpty(status)) {
989 serializer.attribute(null, ATTR_STATUS, status.toString());
990 }
991
Svetoslav269403b2013-08-14 17:31:04 -0700992 PrinterId printerId = printJob.getPrinterId();
993 if (printerId != null) {
994 serializer.startTag(null, TAG_PRINTER_ID);
995 serializer.attribute(null, ATTR_LOCAL_ID, printerId.getLocalId());
996 serializer.attribute(null, ATTR_SERVICE_NAME, printerId.getServiceName()
997 .flattenToString());
998 serializer.endTag(null, TAG_PRINTER_ID);
999 }
1000
1001 PageRange[] pages = printJob.getPages();
1002 if (pages != null) {
1003 for (int i = 0; i < pages.length; i++) {
1004 serializer.startTag(null, TAG_PAGE_RANGE);
1005 serializer.attribute(null, ATTR_START, String.valueOf(
1006 pages[i].getStart()));
1007 serializer.attribute(null, ATTR_END, String.valueOf(
1008 pages[i].getEnd()));
1009 serializer.endTag(null, TAG_PAGE_RANGE);
1010 }
1011 }
1012
1013 PrintAttributes attributes = printJob.getAttributes();
1014 if (attributes != null) {
1015 serializer.startTag(null, TAG_ATTRIBUTES);
1016
Svetoslav269403b2013-08-14 17:31:04 -07001017 final int colorMode = attributes.getColorMode();
1018 serializer.attribute(null, ATTR_COLOR_MODE,
1019 String.valueOf(colorMode));
1020
Svetoslav948c9a62015-02-02 19:47:04 -08001021 final int duplexMode = attributes.getDuplexMode();
1022 serializer.attribute(null, ATTR_DUPLEX_MODE,
1023 String.valueOf(duplexMode));
1024
Svetoslav269403b2013-08-14 17:31:04 -07001025 MediaSize mediaSize = attributes.getMediaSize();
1026 if (mediaSize != null) {
1027 serializer.startTag(null, TAG_MEDIA_SIZE);
1028 serializer.attribute(null, ATTR_ID, mediaSize.getId());
Svetoslav269403b2013-08-14 17:31:04 -07001029 serializer.attribute(null, ATTR_WIDTH_MILS, String.valueOf(
1030 mediaSize.getWidthMils()));
1031 serializer.attribute(null, ATTR_HEIGHT_MILS, String.valueOf(
1032 mediaSize.getHeightMils()));
Svetoslava76233a2013-09-05 09:38:02 -07001033 // We prefer to store only the package name and
1034 // resource id and fallback to the label.
1035 if (!TextUtils.isEmpty(mediaSize.mPackageName)
Svetoslav773f54d2013-09-03 14:01:43 -07001036 && mediaSize.mLabelResId > 0) {
1037 serializer.attribute(null, ATTR_PACKAGE_NAME,
1038 mediaSize.mPackageName);
1039 serializer.attribute(null, ATTR_LABEL_RES_ID,
1040 String.valueOf(mediaSize.mLabelResId));
1041 } else {
1042 serializer.attribute(null, ATTR_LABEL,
1043 mediaSize.getLabel(getPackageManager()));
1044 }
Svetoslav269403b2013-08-14 17:31:04 -07001045 serializer.endTag(null, TAG_MEDIA_SIZE);
1046 }
1047
1048 Resolution resolution = attributes.getResolution();
1049 if (resolution != null) {
1050 serializer.startTag(null, TAG_RESOLUTION);
1051 serializer.attribute(null, ATTR_ID, resolution.getId());
Svetoslav269403b2013-08-14 17:31:04 -07001052 serializer.attribute(null, ATTR_HORIZONTAL_DPI, String.valueOf(
1053 resolution.getHorizontalDpi()));
1054 serializer.attribute(null, ATTR_VERTICAL_DPI, String.valueOf(
1055 resolution.getVerticalDpi()));
Svetoslavc6066792013-09-10 21:08:32 -07001056 serializer.attribute(null, ATTR_LABEL,
Svetoslav651dd4e2013-09-12 14:37:47 -07001057 resolution.getLabel());
Svetoslav269403b2013-08-14 17:31:04 -07001058 serializer.endTag(null, TAG_RESOLUTION);
1059 }
1060
Svetoslav651dd4e2013-09-12 14:37:47 -07001061 Margins margins = attributes.getMinMargins();
Svetoslav269403b2013-08-14 17:31:04 -07001062 if (margins != null) {
1063 serializer.startTag(null, TAG_MARGINS);
1064 serializer.attribute(null, ATTR_LEFT_MILS, String.valueOf(
1065 margins.getLeftMils()));
1066 serializer.attribute(null, ATTR_TOP_MILS, String.valueOf(
1067 margins.getTopMils()));
1068 serializer.attribute(null, ATTR_RIGHT_MILS, String.valueOf(
1069 margins.getRightMils()));
1070 serializer.attribute(null, ATTR_BOTTOM_MILS, String.valueOf(
1071 margins.getBottomMils()));
1072 serializer.endTag(null, TAG_MARGINS);
1073 }
1074
Svetoslav269403b2013-08-14 17:31:04 -07001075 serializer.endTag(null, TAG_ATTRIBUTES);
1076 }
1077
1078 PrintDocumentInfo documentInfo = printJob.getDocumentInfo();
1079 if (documentInfo != null) {
1080 serializer.startTag(null, TAG_DOCUMENT_INFO);
1081 serializer.attribute(null, ATTR_NAME, documentInfo.getName());
1082 serializer.attribute(null, ATTR_CONTENT_TYPE, String.valueOf(
1083 documentInfo.getContentType()));
1084 serializer.attribute(null, ATTR_PAGE_COUNT, String.valueOf(
1085 documentInfo.getPageCount()));
Svetoslav Ganov7d7888d2013-10-12 13:18:12 -07001086 serializer.attribute(null, ATTR_DATA_SIZE, String.valueOf(
1087 documentInfo.getDataSize()));
Svetoslav269403b2013-08-14 17:31:04 -07001088 serializer.endTag(null, TAG_DOCUMENT_INFO);
1089 }
1090
Svetoslavb4fda132013-10-25 18:57:43 -07001091 Bundle advancedOptions = printJob.getAdvancedOptions();
1092 if (advancedOptions != null) {
1093 serializer.startTag(null, TAG_ADVANCED_OPTIONS);
1094 for (String key : advancedOptions.keySet()) {
1095 Object value = advancedOptions.get(key);
1096 if (value instanceof String) {
1097 String stringValue = (String) value;
1098 serializer.startTag(null, TAG_ADVANCED_OPTION);
1099 serializer.attribute(null, ATTR_KEY, key);
1100 serializer.attribute(null, ATTR_TYPE, TYPE_STRING);
1101 serializer.attribute(null, ATTR_VALUE, stringValue);
1102 serializer.endTag(null, TAG_ADVANCED_OPTION);
1103 } else if (value instanceof Integer) {
1104 String intValue = Integer.toString((Integer) value);
1105 serializer.startTag(null, TAG_ADVANCED_OPTION);
1106 serializer.attribute(null, ATTR_KEY, key);
1107 serializer.attribute(null, ATTR_TYPE, TYPE_INT);
1108 serializer.attribute(null, ATTR_VALUE, intValue);
1109 serializer.endTag(null, TAG_ADVANCED_OPTION);
1110 }
1111 }
1112 serializer.endTag(null, TAG_ADVANCED_OPTIONS);
1113 }
1114
Svetoslav269403b2013-08-14 17:31:04 -07001115 serializer.endTag(null, TAG_JOB);
1116
1117 if (DEBUG_PERSISTENCE) {
1118 Log.i(LOG_TAG, "[PERSISTED] " + printJob);
1119 }
1120 }
1121
1122 serializer.endTag(null, TAG_SPOOLER);
1123 serializer.endDocument();
1124 mStatePersistFile.finishWrite(out);
1125 if (DEBUG_PERSISTENCE) {
1126 Log.i(LOG_TAG, "[PERSIST END]");
1127 }
1128 } catch (IOException e) {
1129 Slog.w(LOG_TAG, "Failed to write state, restoring backup.", e);
1130 mStatePersistFile.failWrite(out);
1131 } finally {
1132 IoUtils.closeQuietly(out);
1133 }
1134 }
1135
1136 public void readStateLocked() {
Svet Ganov4237c922014-10-24 12:53:23 -07001137 if (!PERSISTENCE_MANAGER_ENABLED) {
Svetoslav269403b2013-08-14 17:31:04 -07001138 return;
1139 }
1140 FileInputStream in = null;
1141 try {
1142 in = mStatePersistFile.openRead();
1143 } catch (FileNotFoundException e) {
Joe Onorato13460a62016-03-18 13:34:13 -07001144 if (DEBUG_PERSISTENCE) {
1145 Log.d(LOG_TAG, "No existing print spooler state.");
1146 }
Svetoslav269403b2013-08-14 17:31:04 -07001147 return;
1148 }
1149 try {
1150 XmlPullParser parser = Xml.newPullParser();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +01001151 parser.setInput(in, StandardCharsets.UTF_8.name());
Svetoslav269403b2013-08-14 17:31:04 -07001152 parseState(parser);
1153 } catch (IllegalStateException ise) {
1154 Slog.w(LOG_TAG, "Failed parsing ", ise);
1155 } catch (NullPointerException npe) {
1156 Slog.w(LOG_TAG, "Failed parsing ", npe);
1157 } catch (NumberFormatException nfe) {
1158 Slog.w(LOG_TAG, "Failed parsing ", nfe);
1159 } catch (XmlPullParserException xppe) {
1160 Slog.w(LOG_TAG, "Failed parsing ", xppe);
1161 } catch (IOException ioe) {
1162 Slog.w(LOG_TAG, "Failed parsing ", ioe);
1163 } catch (IndexOutOfBoundsException iobe) {
1164 Slog.w(LOG_TAG, "Failed parsing ", iobe);
1165 } finally {
1166 IoUtils.closeQuietly(in);
1167 }
1168 }
1169
1170 private void parseState(XmlPullParser parser)
1171 throws IOException, XmlPullParserException {
1172 parser.next();
1173 skipEmptyTextTags(parser);
1174 expect(parser, XmlPullParser.START_TAG, TAG_SPOOLER);
1175 parser.next();
1176
1177 while (parsePrintJob(parser)) {
1178 parser.next();
1179 }
1180
1181 skipEmptyTextTags(parser);
1182 expect(parser, XmlPullParser.END_TAG, TAG_SPOOLER);
1183 }
1184
1185 private boolean parsePrintJob(XmlPullParser parser)
1186 throws IOException, XmlPullParserException {
1187 skipEmptyTextTags(parser);
1188 if (!accept(parser, XmlPullParser.START_TAG, TAG_JOB)) {
1189 return false;
1190 }
1191
1192 PrintJobInfo printJob = new PrintJobInfo();
1193
Svetoslav2fbd2a72013-09-16 17:53:51 -07001194 PrintJobId printJobId = PrintJobId.unflattenFromString(
1195 parser.getAttributeValue(null, ATTR_ID));
Svetoslav269403b2013-08-14 17:31:04 -07001196 printJob.setId(printJobId);
1197 String label = parser.getAttributeValue(null, ATTR_LABEL);
1198 printJob.setLabel(label);
1199 final int state = Integer.parseInt(parser.getAttributeValue(null, ATTR_STATE));
1200 printJob.setState(state);
1201 final int appId = Integer.parseInt(parser.getAttributeValue(null, ATTR_APP_ID));
1202 printJob.setAppId(appId);
Svetoslav269403b2013-08-14 17:31:04 -07001203 String tag = parser.getAttributeValue(null, ATTR_TAG);
1204 printJob.setTag(tag);
Svetoslav Ganov704697b2013-09-21 20:30:24 -07001205 String creationTime = parser.getAttributeValue(null, ATTR_CREATION_TIME);
1206 printJob.setCreationTime(Long.parseLong(creationTime));
Svetoslav269403b2013-08-14 17:31:04 -07001207 String copies = parser.getAttributeValue(null, ATTR_COPIES);
1208 printJob.setCopies(Integer.parseInt(copies));
Svetoslav Ganov704697b2013-09-21 20:30:24 -07001209 String printerName = parser.getAttributeValue(null, ATTR_PRINTER_NAME);
1210 printJob.setPrinterName(printerName);
Philip P. Moltmannb3078c22015-11-23 16:12:39 -08001211
1212 String progressString = parser.getAttributeValue(null, ATTR_PROGRESS);
1213 if (progressString != null) {
1214 float progress = Float.parseFloat(progressString);
1215
Philip P. Moltmanna958fc32015-12-02 15:39:09 -08001216 if (progress != -1) {
Philip P. Moltmannb3078c22015-11-23 16:12:39 -08001217 printJob.setProgress(progress);
1218 }
1219 }
1220
1221 CharSequence status = parser.getAttributeValue(null, ATTR_STATUS);
1222 printJob.setStatus(status);
1223
1224 // stateReason is deprecated, but might be used by old print jobs
Svetoslav Ganov704697b2013-09-21 20:30:24 -07001225 String stateReason = parser.getAttributeValue(null, ATTR_STATE_REASON);
Philip P. Moltmannb3078c22015-11-23 16:12:39 -08001226 if (stateReason != null) {
1227 printJob.setStatus(stateReason);
1228 }
1229
Svetoslav Ganova18661d2013-10-09 22:55:49 -07001230 String cancelling = parser.getAttributeValue(null, ATTR_CANCELLING);
1231 printJob.setCancelling(!TextUtils.isEmpty(cancelling)
1232 ? Boolean.parseBoolean(cancelling) : false);
Svetoslav269403b2013-08-14 17:31:04 -07001233
1234 parser.next();
1235
1236 skipEmptyTextTags(parser);
1237 if (accept(parser, XmlPullParser.START_TAG, TAG_PRINTER_ID)) {
1238 String localId = parser.getAttributeValue(null, ATTR_LOCAL_ID);
1239 ComponentName service = ComponentName.unflattenFromString(parser.getAttributeValue(
1240 null, ATTR_SERVICE_NAME));
1241 printJob.setPrinterId(new PrinterId(service, localId));
1242 parser.next();
1243 skipEmptyTextTags(parser);
1244 expect(parser, XmlPullParser.END_TAG, TAG_PRINTER_ID);
1245 parser.next();
1246 }
1247
1248 skipEmptyTextTags(parser);
1249 List<PageRange> pageRanges = null;
1250 while (accept(parser, XmlPullParser.START_TAG, TAG_PAGE_RANGE)) {
1251 final int start = Integer.parseInt(parser.getAttributeValue(null, ATTR_START));
1252 final int end = Integer.parseInt(parser.getAttributeValue(null, ATTR_END));
1253 PageRange pageRange = new PageRange(start, end);
1254 if (pageRanges == null) {
1255 pageRanges = new ArrayList<PageRange>();
1256 }
1257 pageRanges.add(pageRange);
1258 parser.next();
1259 skipEmptyTextTags(parser);
1260 expect(parser, XmlPullParser.END_TAG, TAG_PAGE_RANGE);
1261 parser.next();
Svetoslavb4fda132013-10-25 18:57:43 -07001262 skipEmptyTextTags(parser);
Svetoslav269403b2013-08-14 17:31:04 -07001263 }
1264 if (pageRanges != null) {
1265 PageRange[] pageRangesArray = new PageRange[pageRanges.size()];
1266 pageRanges.toArray(pageRangesArray);
1267 printJob.setPages(pageRangesArray);
1268 }
1269
1270 skipEmptyTextTags(parser);
1271 if (accept(parser, XmlPullParser.START_TAG, TAG_ATTRIBUTES)) {
1272
1273 PrintAttributes.Builder builder = new PrintAttributes.Builder();
1274
Svetoslav269403b2013-08-14 17:31:04 -07001275 String colorMode = parser.getAttributeValue(null, ATTR_COLOR_MODE);
1276 builder.setColorMode(Integer.parseInt(colorMode));
1277
Svetoslav948c9a62015-02-02 19:47:04 -08001278 String duplexMode = parser.getAttributeValue(null, ATTR_DUPLEX_MODE);
1279 // Duplex mode was added later, so null check is needed.
1280 if (duplexMode != null) {
1281 builder.setDuplexMode(Integer.parseInt(duplexMode));
1282 }
1283
Svetoslav269403b2013-08-14 17:31:04 -07001284 parser.next();
1285
1286 skipEmptyTextTags(parser);
1287 if (accept(parser, XmlPullParser.START_TAG, TAG_MEDIA_SIZE)) {
1288 String id = parser.getAttributeValue(null, ATTR_ID);
1289 label = parser.getAttributeValue(null, ATTR_LABEL);
1290 final int widthMils = Integer.parseInt(parser.getAttributeValue(null,
1291 ATTR_WIDTH_MILS));
1292 final int heightMils = Integer.parseInt(parser.getAttributeValue(null,
1293 ATTR_HEIGHT_MILS));
Svetoslav773f54d2013-09-03 14:01:43 -07001294 String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
Svetoslavb206f122013-09-20 10:43:24 -07001295 String labelResIdString = parser.getAttributeValue(null, ATTR_LABEL_RES_ID);
1296 final int labelResId = (labelResIdString != null)
1297 ? Integer.parseInt(labelResIdString) : 0;
Svetoslav773f54d2013-09-03 14:01:43 -07001298 label = parser.getAttributeValue(null, ATTR_LABEL);
Svetoslavb4fda132013-10-25 18:57:43 -07001299 MediaSize mediaSize = new MediaSize(id, label, packageName,
1300 widthMils, heightMils, labelResId);
Svetoslav269403b2013-08-14 17:31:04 -07001301 builder.setMediaSize(mediaSize);
1302 parser.next();
1303 skipEmptyTextTags(parser);
1304 expect(parser, XmlPullParser.END_TAG, TAG_MEDIA_SIZE);
1305 parser.next();
1306 }
1307
1308 skipEmptyTextTags(parser);
1309 if (accept(parser, XmlPullParser.START_TAG, TAG_RESOLUTION)) {
1310 String id = parser.getAttributeValue(null, ATTR_ID);
1311 label = parser.getAttributeValue(null, ATTR_LABEL);
1312 final int horizontalDpi = Integer.parseInt(parser.getAttributeValue(null,
1313 ATTR_HORIZONTAL_DPI));
1314 final int verticalDpi = Integer.parseInt(parser.getAttributeValue(null,
1315 ATTR_VERTICAL_DPI));
Svetoslavc6066792013-09-10 21:08:32 -07001316 Resolution resolution = new Resolution(id, label, horizontalDpi, verticalDpi);
Svetoslav269403b2013-08-14 17:31:04 -07001317 builder.setResolution(resolution);
1318 parser.next();
1319 skipEmptyTextTags(parser);
1320 expect(parser, XmlPullParser.END_TAG, TAG_RESOLUTION);
1321 parser.next();
1322 }
1323
1324 skipEmptyTextTags(parser);
1325 if (accept(parser, XmlPullParser.START_TAG, TAG_MARGINS)) {
1326 final int leftMils = Integer.parseInt(parser.getAttributeValue(null,
1327 ATTR_LEFT_MILS));
1328 final int topMils = Integer.parseInt(parser.getAttributeValue(null,
1329 ATTR_TOP_MILS));
1330 final int rightMils = Integer.parseInt(parser.getAttributeValue(null,
1331 ATTR_RIGHT_MILS));
1332 final int bottomMils = Integer.parseInt(parser.getAttributeValue(null,
1333 ATTR_BOTTOM_MILS));
1334 Margins margins = new Margins(leftMils, topMils, rightMils, bottomMils);
Svetoslav651dd4e2013-09-12 14:37:47 -07001335 builder.setMinMargins(margins);
Svetoslav269403b2013-08-14 17:31:04 -07001336 parser.next();
1337 skipEmptyTextTags(parser);
1338 expect(parser, XmlPullParser.END_TAG, TAG_MARGINS);
1339 parser.next();
1340 }
1341
Svetoslav651dd4e2013-09-12 14:37:47 -07001342 printJob.setAttributes(builder.build());
Svetoslav269403b2013-08-14 17:31:04 -07001343
1344 skipEmptyTextTags(parser);
1345 expect(parser, XmlPullParser.END_TAG, TAG_ATTRIBUTES);
1346 parser.next();
1347 }
1348
1349 skipEmptyTextTags(parser);
1350 if (accept(parser, XmlPullParser.START_TAG, TAG_DOCUMENT_INFO)) {
1351 String name = parser.getAttributeValue(null, ATTR_NAME);
1352 final int pageCount = Integer.parseInt(parser.getAttributeValue(null,
1353 ATTR_PAGE_COUNT));
1354 final int contentType = Integer.parseInt(parser.getAttributeValue(null,
1355 ATTR_CONTENT_TYPE));
Svetoslav Ganov7d7888d2013-10-12 13:18:12 -07001356 final int dataSize = Integer.parseInt(parser.getAttributeValue(null,
1357 ATTR_DATA_SIZE));
Svetoslav269403b2013-08-14 17:31:04 -07001358 PrintDocumentInfo info = new PrintDocumentInfo.Builder(name)
1359 .setPageCount(pageCount)
Svetoslav651dd4e2013-09-12 14:37:47 -07001360 .setContentType(contentType).build();
Svetoslav269403b2013-08-14 17:31:04 -07001361 printJob.setDocumentInfo(info);
Svetoslav Ganov7d7888d2013-10-12 13:18:12 -07001362 info.setDataSize(dataSize);
Svetoslav269403b2013-08-14 17:31:04 -07001363 parser.next();
1364 skipEmptyTextTags(parser);
1365 expect(parser, XmlPullParser.END_TAG, TAG_DOCUMENT_INFO);
1366 parser.next();
1367 }
1368
Svetoslavb4fda132013-10-25 18:57:43 -07001369 skipEmptyTextTags(parser);
1370 if (accept(parser, XmlPullParser.START_TAG, TAG_ADVANCED_OPTIONS)) {
1371 parser.next();
1372 skipEmptyTextTags(parser);
1373 Bundle advancedOptions = new Bundle();
1374 while (accept(parser, XmlPullParser.START_TAG, TAG_ADVANCED_OPTION)) {
1375 String key = parser.getAttributeValue(null, ATTR_KEY);
1376 String value = parser.getAttributeValue(null, ATTR_VALUE);
1377 String type = parser.getAttributeValue(null, ATTR_TYPE);
1378 if (TYPE_STRING.equals(type)) {
1379 advancedOptions.putString(key, value);
1380 } else if (TYPE_INT.equals(type)) {
Tobias Thierer9bbbf2b2016-04-21 12:02:10 +01001381 advancedOptions.putInt(key, Integer.parseInt(value));
Svetoslavb4fda132013-10-25 18:57:43 -07001382 }
1383 parser.next();
1384 skipEmptyTextTags(parser);
1385 expect(parser, XmlPullParser.END_TAG, TAG_ADVANCED_OPTION);
1386 parser.next();
1387 skipEmptyTextTags(parser);
1388 }
1389 printJob.setAdvancedOptions(advancedOptions);
1390 skipEmptyTextTags(parser);
1391 expect(parser, XmlPullParser.END_TAG, TAG_ADVANCED_OPTIONS);
1392 parser.next();
1393 }
1394
Svetoslav269403b2013-08-14 17:31:04 -07001395 mPrintJobs.add(printJob);
1396
1397 if (DEBUG_PERSISTENCE) {
1398 Log.i(LOG_TAG, "[RESTORED] " + printJob);
1399 }
1400
1401 skipEmptyTextTags(parser);
1402 expect(parser, XmlPullParser.END_TAG, TAG_JOB);
1403
1404 return true;
1405 }
1406
1407 private void expect(XmlPullParser parser, int type, String tag)
Philip P. Moltmannc43639c2015-12-18 13:58:40 -08001408 throws XmlPullParserException {
Svetoslav269403b2013-08-14 17:31:04 -07001409 if (!accept(parser, type, tag)) {
1410 throw new XmlPullParserException("Exepected event: " + type
1411 + " and tag: " + tag + " but got event: " + parser.getEventType()
1412 + " and tag:" + parser.getName());
1413 }
1414 }
1415
1416 private void skipEmptyTextTags(XmlPullParser parser)
1417 throws IOException, XmlPullParserException {
1418 while (accept(parser, XmlPullParser.TEXT, null)
1419 && "\n".equals(parser.getText())) {
1420 parser.next();
1421 }
1422 }
1423
1424 private boolean accept(XmlPullParser parser, int type, String tag)
Philip P. Moltmannc43639c2015-12-18 13:58:40 -08001425 throws XmlPullParserException {
Svetoslav269403b2013-08-14 17:31:04 -07001426 if (parser.getEventType() != type) {
1427 return false;
1428 }
1429 if (tag != null) {
1430 if (!tag.equals(parser.getName())) {
1431 return false;
1432 }
1433 } else if (parser.getName() != null) {
1434 return false;
1435 }
1436 return true;
1437 }
1438 }
Svetoslav7bfbbcb2013-10-10 13:36:23 -07001439
Svetoslava798c0a2014-05-15 10:47:19 -07001440 public final class PrintSpooler extends IPrintSpooler.Stub {
Svetoslav7bfbbcb2013-10-10 13:36:23 -07001441 @Override
1442 public void getPrintJobInfos(IPrintSpoolerCallbacks callback,
1443 ComponentName componentName, int state, int appId, int sequence)
1444 throws RemoteException {
1445 List<PrintJobInfo> printJobs = null;
1446 try {
1447 printJobs = PrintSpoolerService.this.getPrintJobInfos(
1448 componentName, state, appId);
1449 } finally {
1450 callback.onGetPrintJobInfosResult(printJobs, sequence);
1451 }
1452 }
1453
1454 @Override
1455 public void getPrintJobInfo(PrintJobId printJobId, IPrintSpoolerCallbacks callback,
1456 int appId, int sequence) throws RemoteException {
1457 PrintJobInfo printJob = null;
1458 try {
1459 printJob = PrintSpoolerService.this.getPrintJobInfo(printJobId, appId);
1460 } finally {
1461 callback.onGetPrintJobInfoResult(printJob, sequence);
1462 }
1463 }
1464
1465 @Override
1466 public void createPrintJob(PrintJobInfo printJob) {
1467 PrintSpoolerService.this.createPrintJob(printJob);
1468 }
1469
1470 @Override
1471 public void setPrintJobState(PrintJobId printJobId, int state, String error,
1472 IPrintSpoolerCallbacks callback, int sequece) throws RemoteException {
1473 boolean success = false;
1474 try {
1475 success = PrintSpoolerService.this.setPrintJobState(
1476 printJobId, state, error);
1477 } finally {
1478 callback.onSetPrintJobStateResult(success, sequece);
1479 }
1480 }
1481
1482 @Override
1483 public void setPrintJobTag(PrintJobId printJobId, String tag,
1484 IPrintSpoolerCallbacks callback, int sequece) throws RemoteException {
1485 boolean success = false;
1486 try {
1487 success = PrintSpoolerService.this.setPrintJobTag(printJobId, tag);
1488 } finally {
1489 callback.onSetPrintJobTagResult(success, sequece);
1490 }
1491 }
1492
1493 @Override
1494 public void writePrintJobData(ParcelFileDescriptor fd, PrintJobId printJobId) {
1495 PrintSpoolerService.this.writePrintJobData(fd, printJobId);
1496 }
1497
1498 @Override
1499 public void setClient(IPrintSpoolerClient client) {
1500 Message message = mHandlerCaller.obtainMessageO(
1501 HandlerCallerCallback.MSG_SET_CLIENT, client);
1502 mHandlerCaller.executeOrSendMessage(message);
1503 }
1504
1505 @Override
1506 public void removeObsoletePrintJobs() {
1507 PrintSpoolerService.this.removeObsoletePrintJobs();
1508 }
1509
1510 @Override
1511 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
1512 PrintSpoolerService.this.dump(fd, writer, args);
1513 }
1514
1515 @Override
1516 public void setPrintJobCancelling(PrintJobId printJobId, boolean cancelling) {
1517 PrintSpoolerService.this.setPrintJobCancelling(printJobId, cancelling);
1518 }
1519
Philip P. Moltmann853a6f52015-11-03 10:38:56 -08001520 @Override
Philip P. Moltmann8141bdf2015-12-21 17:03:05 -08001521 public void pruneApprovedPrintServices(List<ComponentName> servicesToKeep) {
Philip P. Moltmann853a6f52015-11-03 10:38:56 -08001522 (new ApprovedPrintServices(PrintSpoolerService.this))
Philip P. Moltmann8141bdf2015-12-21 17:03:05 -08001523 .pruneApprovedServices(servicesToKeep);
Philip P. Moltmann853a6f52015-11-03 10:38:56 -08001524 }
1525
Philip P. Moltmannb3078c22015-11-23 16:12:39 -08001526 @Override
1527 public void setProgress(@NonNull PrintJobId printJobId,
1528 @FloatRange(from=0.0, to=1.0) float progress) throws RemoteException {
1529 PrintSpoolerService.this.setProgress(printJobId, progress);
1530 }
1531
1532 @Override
1533 public void setStatus(@NonNull PrintJobId printJobId,
1534 @Nullable CharSequence status) throws RemoteException {
1535 PrintSpoolerService.this.setStatus(printJobId, status);
1536 }
1537
Philip P. Moltmannd74d1e52016-03-17 16:37:47 -07001538 @Override
1539 public void setStatusRes(@NonNull PrintJobId printJobId, @StringRes int status,
1540 @NonNull CharSequence appPackageName) throws RemoteException {
1541 PrintSpoolerService.this.setStatus(printJobId, status, appPackageName);
1542 }
1543
1544
Svetoslav7bfbbcb2013-10-10 13:36:23 -07001545 public PrintSpoolerService getService() {
1546 return PrintSpoolerService.this;
1547 }
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -08001548
1549 @Override
1550 public void onCustomPrinterIconLoaded(PrinterId printerId, Icon icon,
1551 IPrintSpoolerCallbacks callbacks, int sequence)
1552 throws RemoteException {
1553 try {
1554 PrintSpoolerService.this.onCustomPrinterIconLoaded(printerId, icon);
1555 } finally {
1556 callbacks.onCustomPrinterIconCached(sequence);
1557 }
1558 }
1559
1560 @Override
1561 public void getCustomPrinterIcon(PrinterId printerId, IPrintSpoolerCallbacks callbacks,
1562 int sequence) throws RemoteException {
1563 Icon icon = null;
1564 try {
1565 icon = PrintSpoolerService.this.getCustomPrinterIcon(printerId);
1566 } finally {
1567 callbacks.onGetCustomPrinterIconResult(icon, sequence);
1568 }
1569 }
1570
1571 @Override
1572 public void clearCustomPrinterIconCache(IPrintSpoolerCallbacks callbacks,
1573 int sequence) throws RemoteException {
1574 try {
1575 PrintSpoolerService.this.clearCustomPrinterIconCache();
1576 } finally {
1577 callbacks.customPrinterIconCacheCleared(sequence);
1578 }
1579 }
1580
Svetoslav7bfbbcb2013-10-10 13:36:23 -07001581 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -07001582}