blob: 0546a43166197174c3f30ec07ffeb4b05beb8f8c [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
17package com.android.printspooler;
18
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070019import android.content.ComponentName;
20import android.content.Context;
21import android.os.AsyncTask;
22import android.os.ParcelFileDescriptor;
23import android.os.RemoteException;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070024import android.print.IPrintClient;
Svetoslav Ganova0027152013-06-25 14:59:53 -070025import android.print.IPrintSpoolerClient;
26import android.print.IPrinterDiscoveryObserver;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070027import android.print.PrintAttributes;
28import android.print.PrintJobInfo;
29import android.print.PrintManager;
Svetoslav Ganova0027152013-06-25 14:59:53 -070030import android.print.PrintDocumentInfo;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070031import android.print.PrinterId;
32import android.util.AtomicFile;
33import android.util.Log;
34import android.util.Slog;
35import android.util.Xml;
36
37import com.android.internal.util.FastXmlSerializer;
38
Svetoslav Ganova0027152013-06-25 14:59:53 -070039import libcore.io.IoUtils;
40
41import org.xmlpull.v1.XmlPullParser;
42import org.xmlpull.v1.XmlPullParserException;
43import org.xmlpull.v1.XmlSerializer;
44
45import java.io.File;
46import java.io.FileInputStream;
47import java.io.FileNotFoundException;
48import java.io.FileOutputStream;
49import java.io.IOException;
50import java.util.ArrayList;
51import java.util.HashMap;
52import java.util.List;
53import java.util.Map;
54
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070055public class PrintSpooler {
56
57 private static final String LOG_TAG = PrintSpooler.class.getSimpleName();
58
59 private static final boolean DEBUG_PRINT_JOB_LIFECYCLE = false;
60
61 private static final boolean DEBUG_PERSISTENCE = false;
62
63 private static final boolean PERSISTNECE_MANAGER_ENABLED = false;
64
65 private static final String PRINT_FILE_EXTENSION = "pdf";
66
67 private static int sPrintJobIdCounter;
68
69 private static final Object sLock = new Object();
70
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070071 private static PrintSpooler sInstance;
72
Svetoslav Ganova0027152013-06-25 14:59:53 -070073 private final Object mLock = new Object();
74
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070075 private final List<PrintJobInfo> mPrintJobs = new ArrayList<PrintJobInfo>();
76
77 private final PersistenceManager mPersistanceManager;
78
79 private final Context mContext;
80
Svetoslav Ganova0027152013-06-25 14:59:53 -070081 public IPrintSpoolerClient mClient;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070082
83 public static PrintSpooler getInstance(Context context) {
84 synchronized (sLock) {
85 if (sInstance == null) {
86 sInstance = new PrintSpooler(context);
87 }
88 return sInstance;
89 }
90 }
91
92 private PrintSpooler(Context context) {
93 mContext = context;
94 mPersistanceManager = new PersistenceManager();
95 mPersistanceManager.readStateLocked();
Svetoslav Ganova0027152013-06-25 14:59:53 -070096 }
97
98 public void setCleint(IPrintSpoolerClient client) {
99 synchronized (mLock) {
100 mClient = client;
101 }
102 }
103
104 public void startPrinterDiscovery(IPrinterDiscoveryObserver observer) {
105 IPrintSpoolerClient client = null;
106 synchronized (mLock) {
107 client = mClient;
108 }
109 if (client != null) {
110 try {
111 client.onStartPrinterDiscovery(observer);
112 } catch (RemoteException re) {
113 Log.e(LOG_TAG, "Error notifying start printer discovery.", re);
114 }
115 }
116 }
117
118 public void stopPrinterDiscovery() {
119 IPrintSpoolerClient client = null;
120 synchronized (mLock) {
121 client = mClient;
122 }
123 if (client != null) {
124 try {
125 client.onStopPrinterDiscovery();
126 } catch (RemoteException re) {
127 Log.e(LOG_TAG, "Error notifying stop printer discovery.", re);
128 }
129 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700130 }
131
132 public List<PrintJobInfo> getPrintJobs(ComponentName componentName, int state, int appId) {
133 synchronized (mLock) {
134 List<PrintJobInfo> foundPrintJobs = null;
135 final int printJobCount = mPrintJobs.size();
136 for (int i = 0; i < printJobCount; i++) {
137 PrintJobInfo printJob = mPrintJobs.get(i);
138 PrinterId printerId = printJob.getPrinterId();
139 final boolean sameComponent = (componentName == null
140 || (printerId != null
Svetoslav Ganova0027152013-06-25 14:59:53 -0700141 && componentName.equals(printerId.getService())));
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700142 final boolean sameAppId = appId == PrintManager.APP_ID_ANY
143 || printJob.getAppId() == appId;
144 final boolean sameState = state == PrintJobInfo.STATE_ANY
145 || state == printJob.getState();
146 if (sameComponent && sameAppId && sameState) {
147 if (foundPrintJobs == null) {
148 foundPrintJobs = new ArrayList<PrintJobInfo>();
149 }
150 foundPrintJobs.add(printJob);
151 }
152 }
153 return foundPrintJobs;
154 }
155 }
156
157 public PrintJobInfo getPrintJob(int printJobId, int appId) {
158 synchronized (mLock) {
159 final int printJobCount = mPrintJobs.size();
160 for (int i = 0; i < printJobCount; i++) {
161 PrintJobInfo printJob = mPrintJobs.get(i);
162 if (printJob.getId() == printJobId
163 && (appId == PrintManager.APP_ID_ANY || appId == printJob.getAppId())) {
164 return printJob;
165 }
166 }
167 return null;
168 }
169 }
170
171 public boolean cancelPrintJob(int printJobId, int appId) {
172 synchronized (mLock) {
173 PrintJobInfo printJob = getPrintJob(printJobId, appId);
174 if (printJob != null) {
175 switch (printJob.getState()) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700176 case PrintJobInfo.STATE_CREATED:
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700177 case PrintJobInfo.STATE_QUEUED: {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700178 setPrintJobState(printJobId, PrintJobInfo.STATE_CANCELED);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700179 } return true;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700180 }
181 }
182 return false;
183 }
184 }
185
186 public PrintJobInfo createPrintJob(CharSequence label, IPrintClient client,
187 PrintAttributes attributes, int appId) {
188 synchronized (mLock) {
189 final int printJobId = generatePrintJobIdLocked();
190 PrintJobInfo printJob = new PrintJobInfo();
191 printJob.setId(printJobId);
192 printJob.setAppId(appId);
193 printJob.setLabel(label);
194 printJob.setAttributes(attributes);
195
196 addPrintJobLocked(printJob);
197 setPrintJobState(printJobId, PrintJobInfo.STATE_CREATED);
198
199 return printJob;
200 }
201 }
202
Svetoslav Ganova0027152013-06-25 14:59:53 -0700203 public void notifyClientForActivteJobs() {
204 IPrintSpoolerClient client = null;
205 Map<ComponentName, List<PrintJobInfo>> activeJobsPerServiceMap =
206 new HashMap<ComponentName, List<PrintJobInfo>>();
207
208 synchronized(mLock) {
209 if (mClient == null) {
210 throw new IllegalStateException("Client cannot be null.");
211 }
212 client = mClient;
213
214 final int printJobCount = mPrintJobs.size();
215 for (int i = 0; i < printJobCount; i++) {
216 PrintJobInfo printJob = mPrintJobs.get(i);
217 switch (printJob.getState()) {
218 case PrintJobInfo.STATE_CREATED: {
219 /* skip - not ready to be handled by a service */
220 } break;
221
222 case PrintJobInfo.STATE_QUEUED:
223 case PrintJobInfo.STATE_STARTED: {
224 ComponentName service = printJob.getPrinterId().getService();
225 List<PrintJobInfo> jobsPerService = activeJobsPerServiceMap.get(service);
226 if (jobsPerService == null) {
227 jobsPerService = new ArrayList<PrintJobInfo>();
228 activeJobsPerServiceMap.put(service, jobsPerService);
229 }
230 jobsPerService.add(printJob);
231 } break;
232
233 default: {
234 ComponentName service = printJob.getPrinterId().getService();
235 if (!activeJobsPerServiceMap.containsKey(service)) {
236 activeJobsPerServiceMap.put(service, null);
237 }
238 }
239 }
240 }
241 }
242
243 boolean allPrintJobsHandled = true;
244
245 for (Map.Entry<ComponentName, List<PrintJobInfo>> entry
246 : activeJobsPerServiceMap.entrySet()) {
247 ComponentName service = entry.getKey();
248 List<PrintJobInfo> printJobs = entry.getValue();
249
250 if (printJobs != null) {
251 allPrintJobsHandled = false;
252 final int printJobCount = printJobs.size();
253 for (int i = 0; i < printJobCount; i++) {
254 PrintJobInfo printJob = printJobs.get(i);
255 if (printJob.getState() == PrintJobInfo.STATE_QUEUED) {
256 callOnPrintJobQueuedQuietly(client, printJob);
257 }
258 }
259 } else {
260 callOnAllPrintJobsForServiceHandledQuietly(client, service);
261 }
262 }
263
264 if (allPrintJobsHandled) {
265 callOnAllPrintJobsHandledQuietly(client);
266 }
267 }
268
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700269 private int generatePrintJobIdLocked() {
270 int printJobId = sPrintJobIdCounter++;
271 while (isDuplicatePrintJobId(printJobId)) {
272 printJobId = sPrintJobIdCounter++;
273 }
274 return printJobId;
275 }
276
277 private boolean isDuplicatePrintJobId(int printJobId) {
278 final int printJobCount = mPrintJobs.size();
279 for (int j = 0; j < printJobCount; j++) {
280 PrintJobInfo printJob = mPrintJobs.get(j);
281 if (printJob.getId() == printJobId) {
282 return true;
283 }
284 }
285 return false;
286 }
287
288 @SuppressWarnings("resource")
289 public boolean writePrintJobData(ParcelFileDescriptor fd, int printJobId) {
290 synchronized (mLock) {
291 FileInputStream in = null;
292 FileOutputStream out = null;
293 try {
294 PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
295 if (printJob != null) {
296 File file = generateFileForPrintJob(printJobId);
297 in = new FileInputStream(file);
298 out = new FileOutputStream(fd.getFileDescriptor());
299 final byte[] buffer = new byte[8192];
300 while (true) {
301 final int readByteCount = in.read(buffer);
302 if (readByteCount < 0) {
303 return true;
304 }
305 out.write(buffer, 0, readByteCount);
306 }
307 }
308 } catch (FileNotFoundException fnfe) {
309 Log.e(LOG_TAG, "Error writing print job data!", fnfe);
310 } catch (IOException ioe) {
311 Log.e(LOG_TAG, "Error writing print job data!", ioe);
312 } finally {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700313 IoUtils.closeQuietly(in);
314 IoUtils.closeQuietly(out);
315 IoUtils.closeQuietly(fd);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700316 }
317 }
318 return false;
319 }
320
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700321 public File generateFileForPrintJob(int printJobId) {
322 return new File(mContext.getFilesDir(), "print_job_"
323 + printJobId + "." + PRINT_FILE_EXTENSION);
324 }
325
326 private void addPrintJobLocked(PrintJobInfo printJob) {
327 mPrintJobs.add(printJob);
328 if (DEBUG_PRINT_JOB_LIFECYCLE) {
329 Slog.i(LOG_TAG, "[ADD] " + printJob);
330 }
331 }
332
333 private void removePrintJobLocked(PrintJobInfo printJob) {
334 if (mPrintJobs.remove(printJob)) {
335 generateFileForPrintJob(printJob.getId()).delete();
336 if (DEBUG_PRINT_JOB_LIFECYCLE) {
337 Slog.i(LOG_TAG, "[REMOVE] " + printJob);
338 }
339 }
340 }
341
342 public boolean setPrintJobState(int printJobId, int state) {
343 boolean success = false;
Svetoslav Ganova0027152013-06-25 14:59:53 -0700344
345 boolean allPrintJobsHandled = false;
346 boolean allPrintJobsForServiceHandled = false;
347
348 IPrintSpoolerClient client = null;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700349 PrintJobInfo queuedPrintJob = null;
Svetoslav Ganova0027152013-06-25 14:59:53 -0700350 PrintJobInfo removedPrintJob = null;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700351
352 synchronized (mLock) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700353 if (mClient == null) {
354 throw new IllegalStateException("Client cannot be null.");
355 }
356 client = mClient;
357
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700358 PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
359 if (printJob != null && printJob.getState() < state) {
360 success = true;
361 printJob.setState(state);
362 // TODO: Update notifications.
363 switch (state) {
364 case PrintJobInfo.STATE_COMPLETED:
365 case PrintJobInfo.STATE_CANCELED: {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700366 removedPrintJob = printJob;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700367 removePrintJobLocked(printJob);
Svetoslav Ganova0027152013-06-25 14:59:53 -0700368
369 // No printer means creation of a print job was cancelled,
370 // therefore the state of the spooler did not change and no
371 // notifications are needed. We also do not need to persist
372 // the state.
373 PrinterId printerId = printJob.getPrinterId();
374 if (printerId == null) {
375 return true;
376 }
377
378 allPrintJobsHandled = !hasActivePrintJobsLocked();
379 allPrintJobsForServiceHandled = !hasActivePrintJobsForServiceLocked(
380 printerId.getService());
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700381 } break;
Svetoslav Ganova0027152013-06-25 14:59:53 -0700382
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700383 case PrintJobInfo.STATE_QUEUED: {
384 queuedPrintJob = new PrintJobInfo(printJob);
385 } break;
386 }
387 if (DEBUG_PRINT_JOB_LIFECYCLE) {
388 Slog.i(LOG_TAG, "[STATUS CHANGED] " + printJob);
389 }
390 mPersistanceManager.writeStateLocked();
391 }
392 }
393
394 if (queuedPrintJob != null) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700395 callOnPrintJobQueuedQuietly(client, queuedPrintJob);
396 }
397
398 if (allPrintJobsForServiceHandled) {
399 callOnAllPrintJobsForServiceHandledQuietly(client,
400 removedPrintJob.getPrinterId().getService());
401 }
402
403 if (allPrintJobsHandled) {
404 callOnAllPrintJobsHandledQuietly(client);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700405 }
406
407 return success;
408 }
409
Svetoslav Ganova0027152013-06-25 14:59:53 -0700410 private void callOnPrintJobQueuedQuietly(IPrintSpoolerClient client,
411 PrintJobInfo printJob) {
412 try {
413 client.onPrintJobQueued(printJob);
414 } catch (RemoteException re) {
415 Slog.e(LOG_TAG, "Error notify for a queued print job.", re);
416 }
417 }
418
419 private void callOnAllPrintJobsForServiceHandledQuietly(IPrintSpoolerClient client,
420 ComponentName service) {
421 try {
422 client.onAllPrintJobsForServiceHandled(service);
423 } catch (RemoteException re) {
424 Slog.e(LOG_TAG, "Error notify for all print jobs per service handled.", re);
425 }
426 }
427
428 private void callOnAllPrintJobsHandledQuietly(IPrintSpoolerClient client) {
429 try {
430 client.onAllPrintJobsHandled();
431 } catch (RemoteException re) {
432 Slog.e(LOG_TAG, "Error notify for all print job handled.", re);
433 }
434 }
435
436 private boolean hasActivePrintJobsLocked() {
437 final int printJobCount = mPrintJobs.size();
438 for (int i = 0; i < printJobCount; i++) {
439 PrintJobInfo printJob = mPrintJobs.get(i);
440 switch (printJob.getState()) {
441 case PrintJobInfo.STATE_QUEUED:
442 case PrintJobInfo.STATE_STARTED: {
443 return true;
444 }
445 }
446 }
447 return false;
448 }
449
450 private boolean hasActivePrintJobsForServiceLocked(ComponentName service) {
451 final int printJobCount = mPrintJobs.size();
452 for (int i = 0; i < printJobCount; i++) {
453 PrintJobInfo printJob = mPrintJobs.get(i);
454 switch (printJob.getState()) {
455 case PrintJobInfo.STATE_QUEUED:
456 case PrintJobInfo.STATE_STARTED: {
457 if (printJob.getPrinterId().getService().equals(service)) {
458 return true;
459 }
460 } break;
461 }
462 }
463 return false;
464 }
465
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700466 public boolean setPrintJobTag(int printJobId, String tag) {
467 synchronized (mLock) {
468 PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
469 if (printJob != null) {
470 printJob.setTag(tag);
471 mPersistanceManager.writeStateLocked();
472 return true;
473 }
474 }
475 return false;
476 }
477
Svetoslav Ganova0027152013-06-25 14:59:53 -0700478 public final boolean setPrintJobPrintDocumentInfo(int printJobId, PrintDocumentInfo info) {
479 synchronized (mLock) {
480 PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
481 if (printJob != null) {
482 printJob.setDocumentInfo(info);
483 mPersistanceManager.writeStateLocked();
484 return true;
485 }
486 }
487 return false;
488 }
489
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700490 public void setPrintJobAttributes(int printJobId, PrintAttributes attributes) {
491 synchronized (mLock) {
492 PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
493 if (printJob != null) {
494 printJob.setAttributes(attributes);
495 mPersistanceManager.writeStateLocked();
496 }
497 }
498 }
499
500 public void setPrintJobPrinterId(int printJobId, PrinterId printerId) {
501 synchronized (mLock) {
502 PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
503 if (printJob != null) {
504 printJob.setPrinterId(printerId);
505 mPersistanceManager.writeStateLocked();
506 }
507 }
508 }
509
510 private final class PersistenceManager {
511 private static final String PERSIST_FILE_NAME = "print_spooler_state.xml";
512
513 private static final String TAG_SPOOLER = "spooler";
514 private static final String TAG_JOB = "job";
515 private static final String TAG_ID = "id";
516 private static final String TAG_TAG = "tag";
517 private static final String TAG_APP_ID = "app-id";
518 private static final String TAG_STATE = "state";
519 private static final String TAG_ATTRIBUTES = "attributes";
520 private static final String TAG_LABEL = "label";
521 private static final String TAG_PRINTER = "printer";
522
523 private static final String ATTRIBUTE_MEDIA_SIZE = "mediaSize";
524 private static final String ATTRIBUTE_RESOLUTION = "resolution";
525 private static final String ATTRIBUTE_MARGINS = "margins";
526 private static final String ATTRIBUTE_INPUT_TRAY = "inputTray";
527 private static final String ATTRIBUTE_OUTPUT_TRAY = "outputTray";
528 private static final String ATTRIBUTE_DUPLEX_MODE = "duplexMode";
529 private static final String ATTRIBUTE_COLOR_MODE = "colorMode";
530 private static final String ATTRIBUTE_FITTING_MODE = "fittingMode";
531 private static final String ATTRIBUTE_ORIENTATION = "orientation";
532
533 private final AtomicFile mStatePersistFile;
534
535 private boolean mWriteStateScheduled;
536
537 private PersistenceManager() {
538 mStatePersistFile = new AtomicFile(new File(mContext.getFilesDir(),
539 PERSIST_FILE_NAME));
540 }
541
542 public void writeStateLocked() {
543 // TODO: Implement persistence of PrintableInfo
544 if (!PERSISTNECE_MANAGER_ENABLED) {
545 return;
546 }
547 if (mWriteStateScheduled) {
548 return;
549 }
550 mWriteStateScheduled = true;
551 new AsyncTask<Void, Void, Void>() {
552 @Override
553 protected Void doInBackground(Void... params) {
554 synchronized (mLock) {
555 mWriteStateScheduled = false;
556 doWriteStateLocked();
557 }
558 return null;
559 }
560 }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
561 }
562
563 private void doWriteStateLocked() {
564 FileOutputStream out = null;
565 try {
566 out = mStatePersistFile.startWrite();
567
568 XmlSerializer serializer = new FastXmlSerializer();
569 serializer.setOutput(out, "utf-8");
570 serializer.startDocument(null, true);
571 serializer.startTag(null, TAG_SPOOLER);
572
573 List<PrintJobInfo> printJobs = mPrintJobs;
574
575 final int printJobCount = printJobs.size();
576 for (int j = 0; j < printJobCount; j++) {
577 PrintJobInfo printJob = printJobs.get(j);
578
579 final int state = printJob.getState();
580 if (state < PrintJobInfo.STATE_QUEUED
581 || state > PrintJobInfo.STATE_FAILED) {
582 continue;
583 }
584
585 serializer.startTag(null, TAG_JOB);
586
587 serializer.startTag(null, TAG_ID);
588 serializer.text(String.valueOf(printJob.getId()));
589 serializer.endTag(null, TAG_ID);
590
591 serializer.startTag(null, TAG_TAG);
592 serializer.text(printJob.getTag());
593 serializer.endTag(null, TAG_TAG);
594
595 serializer.startTag(null, TAG_APP_ID);
596 serializer.text(String.valueOf(printJob.getAppId()));
597 serializer.endTag(null, TAG_APP_ID);
598
599 serializer.startTag(null, TAG_LABEL);
600 serializer.text(printJob.getLabel().toString());
601 serializer.endTag(null, TAG_LABEL);
602
603 serializer.startTag(null, TAG_STATE);
604 serializer.text(String.valueOf(printJob.getState()));
605 serializer.endTag(null, TAG_STATE);
606
607 serializer.startTag(null, TAG_PRINTER);
608 serializer.text(printJob.getPrinterId().flattenToString());
609 serializer.endTag(null, TAG_PRINTER);
610
611 PrintAttributes attributes = printJob.getAttributes();
612 if (attributes != null) {
613 serializer.startTag(null, TAG_ATTRIBUTES);
614
615 //TODO: Implement persistence of the attributes below.
616
617// MediaSize mediaSize = attributes.getMediaSize();
618// if (mediaSize != null) {
619// serializer.attribute(null, ATTRIBUTE_MEDIA_SIZE,
620// mediaSize.flattenToString());
621// }
622//
623// Resolution resolution = attributes.getResolution();
624// if (resolution != null) {
625// serializer.attribute(null, ATTRIBUTE_RESOLUTION,
626// resolution.flattenToString());
627// }
628//
629// Margins margins = attributes.getMargins();
630// if (margins != null) {
631// serializer.attribute(null, ATTRIBUTE_MARGINS,
632// margins.flattenToString());
633// }
634//
635// Tray inputTray = attributes.getInputTray();
636// if (inputTray != null) {
637// serializer.attribute(null, ATTRIBUTE_INPUT_TRAY,
638// inputTray.flattenToString());
639// }
640//
641// Tray outputTray = attributes.getOutputTray();
642// if (outputTray != null) {
643// serializer.attribute(null, ATTRIBUTE_OUTPUT_TRAY,
644// outputTray.flattenToString());
645// }
646
647 final int duplexMode = attributes.getDuplexMode();
648 if (duplexMode > 0) {
649 serializer.attribute(null, ATTRIBUTE_DUPLEX_MODE,
650 String.valueOf(duplexMode));
651 }
652
653 final int colorMode = attributes.getColorMode();
654 if (colorMode > 0) {
655 serializer.attribute(null, ATTRIBUTE_COLOR_MODE,
656 String.valueOf(colorMode));
657 }
658
659 final int fittingMode = attributes.getFittingMode();
660 if (fittingMode > 0) {
661 serializer.attribute(null, ATTRIBUTE_FITTING_MODE,
662 String.valueOf(fittingMode));
663 }
664
665 final int orientation = attributes.getOrientation();
666 if (orientation > 0) {
667 serializer.attribute(null, ATTRIBUTE_ORIENTATION,
668 String.valueOf(orientation));
669 }
670
671 serializer.endTag(null, TAG_ATTRIBUTES);
672 }
673
674 serializer.endTag(null, TAG_JOB);
675
676 if (DEBUG_PERSISTENCE) {
677 Log.i(LOG_TAG, "[PERSISTED] " + printJob);
678 }
679 }
680
681 serializer.endTag(null, TAG_SPOOLER);
682 serializer.endDocument();
683 mStatePersistFile.finishWrite(out);
684 } catch (IOException e) {
685 Slog.w(LOG_TAG, "Failed to write state, restoring backup.", e);
686 mStatePersistFile.failWrite(out);
687 } finally {
688 if (out != null) {
689 try {
690 out.close();
691 } catch (IOException ioe) {
692 /* ignore */
693 }
694 }
695 }
696 }
697
698 public void readStateLocked() {
699 if (!PERSISTNECE_MANAGER_ENABLED) {
700 return;
701 }
702 FileInputStream in = null;
703 try {
704 in = mStatePersistFile.openRead();
705 } catch (FileNotFoundException e) {
706 Log.i(LOG_TAG, "No existing print spooler state.");
707 return;
708 }
709 try {
710 XmlPullParser parser = Xml.newPullParser();
711 parser.setInput(in, null);
712 parseState(parser);
713 } catch (IllegalStateException ise) {
714 Slog.w(LOG_TAG, "Failed parsing " + ise);
715 } catch (NullPointerException npe) {
716 Slog.w(LOG_TAG, "Failed parsing " + npe);
717 } catch (NumberFormatException nfe) {
718 Slog.w(LOG_TAG, "Failed parsing " + nfe);
719 } catch (XmlPullParserException xppe) {
720 Slog.w(LOG_TAG, "Failed parsing " + xppe);
721 } catch (IOException ioe) {
722 Slog.w(LOG_TAG, "Failed parsing " + ioe);
723 } catch (IndexOutOfBoundsException iobe) {
724 Slog.w(LOG_TAG, "Failed parsing " + iobe);
725 } finally {
726 try {
727 in.close();
728 } catch (IOException ioe) {
729 /* ignore */
730 }
731 }
732 }
733
734 private void parseState(XmlPullParser parser)
735 throws IOException, XmlPullParserException {
736 parser.next();
737 skipEmptyTextTags(parser);
738 expect(parser, XmlPullParser.START_TAG, TAG_SPOOLER);
739 parser.next();
740
741 while (parsePrintJob(parser)) {
742 parser.next();
743 }
744
745 skipEmptyTextTags(parser);
746 expect(parser, XmlPullParser.END_TAG, TAG_SPOOLER);
747 }
748
749 private boolean parsePrintJob(XmlPullParser parser)
750 throws IOException, XmlPullParserException {
751 skipEmptyTextTags(parser);
752 if (!accept(parser, XmlPullParser.START_TAG, TAG_JOB)) {
753 return false;
754 }
755 parser.next();
756
757 skipEmptyTextTags(parser);
758 expect(parser, XmlPullParser.START_TAG, TAG_ID);
759 parser.next();
760 final int printJobId = Integer.parseInt(parser.getText());
761 parser.next();
762 skipEmptyTextTags(parser);
763 expect(parser, XmlPullParser.END_TAG, TAG_ID);
764 parser.next();
765
766 skipEmptyTextTags(parser);
767 expect(parser, XmlPullParser.START_TAG, TAG_TAG);
768 parser.next();
769 String tag = parser.getText();
770 parser.next();
771 skipEmptyTextTags(parser);
772 expect(parser, XmlPullParser.END_TAG, TAG_TAG);
773 parser.next();
774
775 skipEmptyTextTags(parser);
776 expect(parser, XmlPullParser.START_TAG, TAG_APP_ID);
777 parser.next();
778 final int appId = Integer.parseInt(parser.getText());
779 parser.next();
780 skipEmptyTextTags(parser);
781 expect(parser, XmlPullParser.END_TAG, TAG_APP_ID);
782 parser.next();
783
784 skipEmptyTextTags(parser);
785 expect(parser, XmlPullParser.START_TAG, TAG_LABEL);
786 parser.next();
787 String label = parser.getText();
788 parser.next();
789 skipEmptyTextTags(parser);
790 expect(parser, XmlPullParser.END_TAG, TAG_LABEL);
791 parser.next();
792
793 skipEmptyTextTags(parser);
794 expect(parser, XmlPullParser.START_TAG, TAG_STATE);
795 parser.next();
796 final int state = Integer.parseInt(parser.getText());
797 parser.next();
798 skipEmptyTextTags(parser);
799 expect(parser, XmlPullParser.END_TAG, TAG_STATE);
800 parser.next();
801
802 skipEmptyTextTags(parser);
803 expect(parser, XmlPullParser.START_TAG, TAG_PRINTER);
804 parser.next();
805 PrinterId printerId = PrinterId.unflattenFromString(parser.getText());
806 parser.next();
807 skipEmptyTextTags(parser);
808 expect(parser, XmlPullParser.END_TAG, TAG_PRINTER);
809 parser.next();
810
811 skipEmptyTextTags(parser);
812 expect(parser, XmlPullParser.START_TAG, TAG_ATTRIBUTES);
813
814 final int attributeCount = parser.getAttributeCount();
815 PrintAttributes attributes = null;
816 if (attributeCount > 0) {
817 PrintAttributes.Builder builder = new PrintAttributes.Builder();
818
819 // TODO: Implement reading of the attributes below.
820
821// String mediaSize = parser.getAttributeValue(null, ATTRIBUTE_MEDIA_SIZE);
822// if (mediaSize != null) {
823// builder.setMediaSize(MediaSize.unflattenFromString(mediaSize));
824// }
825//
826// String resolution = parser.getAttributeValue(null, ATTRIBUTE_RESOLUTION);
827// if (resolution != null) {
828// builder.setMediaSize(Resolution.unflattenFromString(resolution));
829// }
830//
831// String margins = parser.getAttributeValue(null, ATTRIBUTE_MARGINS);
832// if (margins != null) {
833// builder.setMediaSize(Margins.unflattenFromString(margins));
834// }
835//
836// String inputTray = parser.getAttributeValue(null, ATTRIBUTE_INPUT_TRAY);
837// if (inputTray != null) {
838// builder.setMediaSize(Tray.unflattenFromString(inputTray));
839// }
840//
841// String outputTray = parser.getAttributeValue(null, ATTRIBUTE_OUTPUT_TRAY);
842// if (outputTray != null) {
843// builder.setMediaSize(Tray.unflattenFromString(outputTray));
844// }
845//
846// String duplexMode = parser.getAttributeValue(null, ATTRIBUTE_DUPLEX_MODE);
847// if (duplexMode != null) {
848// builder.setDuplexMode(Integer.parseInt(duplexMode));
849// }
850
851 String colorMode = parser.getAttributeValue(null, ATTRIBUTE_COLOR_MODE);
852 if (colorMode != null) {
853 builder.setColorMode(Integer.parseInt(colorMode));
854 }
855
856 String fittingMode = parser.getAttributeValue(null, ATTRIBUTE_COLOR_MODE);
857 if (fittingMode != null) {
858 builder.setFittingMode(Integer.parseInt(fittingMode));
859 }
860 }
861 parser.next();
862 skipEmptyTextTags(parser);
863 expect(parser, XmlPullParser.END_TAG, TAG_ATTRIBUTES);
864 parser.next();
865
866 PrintJobInfo printJob = new PrintJobInfo();
867 printJob.setId(printJobId);
868 printJob.setTag(tag);
869 printJob.setAppId(appId);
870 printJob.setLabel(label);
871 printJob.setState(state);
872 printJob.setAttributes(attributes);
873 printJob.setPrinterId(printerId);
874
875 mPrintJobs.add(printJob);
876
877 if (DEBUG_PERSISTENCE) {
878 Log.i(LOG_TAG, "[RESTORED] " + printJob);
879 }
880
881 skipEmptyTextTags(parser);
882 expect(parser, XmlPullParser.END_TAG, TAG_JOB);
883
884 return true;
885 }
886
887 private void expect(XmlPullParser parser, int type, String tag)
888 throws IOException, XmlPullParserException {
889 if (!accept(parser, type, tag)) {
890 throw new XmlPullParserException("Exepected event: " + type
891 + " and tag: " + tag + " but got event: " + parser.getEventType()
892 + " and tag:" + parser.getName());
893 }
894 }
895
896 private void skipEmptyTextTags(XmlPullParser parser)
897 throws IOException, XmlPullParserException {
898 while (accept(parser, XmlPullParser.TEXT, null)
899 && "\n".equals(parser.getText())) {
900 parser.next();
901 }
902 }
903
904 private boolean accept(XmlPullParser parser, int type, String tag)
905 throws IOException, XmlPullParserException {
906 if (parser.getEventType() != type) {
907 return false;
908 }
909 if (tag != null) {
910 if (!tag.equals(parser.getName())) {
911 return false;
912 }
913 } else if (parser.getName() != null) {
914 return false;
915 }
916 return true;
917 }
918 }
919}