blob: 87181f74ed0e00dcd2af33977fd7547239cb6849 [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.app.PendingIntent;
20import android.app.Service;
21import android.content.ComponentName;
22import android.content.Intent;
23import android.content.IntentSender;
Svetoslav269403b2013-08-14 17:31:04 -070024import android.os.AsyncTask;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070025import android.os.IBinder;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070026import android.os.Message;
27import android.os.ParcelFileDescriptor;
28import android.os.RemoteException;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070029import android.print.IPrintClient;
Svetoslav Ganov835835e2013-08-04 20:17:52 -070030import android.print.IPrintDocumentAdapter;
Svetoslav Ganova0027152013-06-25 14:59:53 -070031import android.print.IPrintSpooler;
32import android.print.IPrintSpoolerCallbacks;
Svetoslav Ganov835835e2013-08-04 20:17:52 -070033import android.print.IPrintSpoolerClient;
Svetoslav269403b2013-08-14 17:31:04 -070034import android.print.PageRange;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070035import android.print.PrintAttributes;
Svetoslav269403b2013-08-14 17:31:04 -070036import android.print.PrintAttributes.Margins;
37import android.print.PrintAttributes.MediaSize;
38import android.print.PrintAttributes.Resolution;
Svetoslav269403b2013-08-14 17:31:04 -070039import android.print.PrintDocumentInfo;
Svetoslav2fbd2a72013-09-16 17:53:51 -070040import android.print.PrintJobId;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070041import android.print.PrintJobInfo;
Svetoslav269403b2013-08-14 17:31:04 -070042import android.print.PrintManager;
43import android.print.PrinterId;
44import android.print.PrinterInfo;
Svetoslava76233a2013-09-05 09:38:02 -070045import android.text.TextUtils;
Svetoslav Ganovdd68da22013-09-27 10:48:31 -070046import android.util.ArrayMap;
Svetoslav269403b2013-08-14 17:31:04 -070047import android.util.AtomicFile;
Svetoslav Ganov835835e2013-08-04 20:17:52 -070048import android.util.Log;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070049import android.util.Slog;
Svetoslav269403b2013-08-14 17:31:04 -070050import android.util.Xml;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070051
Svetoslav269403b2013-08-14 17:31:04 -070052import com.android.internal.os.HandlerCaller;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070053import com.android.internal.os.SomeArgs;
Svetoslav269403b2013-08-14 17:31:04 -070054import com.android.internal.util.FastXmlSerializer;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070055
Svetoslav2fbd2a72013-09-16 17:53:51 -070056import libcore.io.IoUtils;
57
Svetoslav269403b2013-08-14 17:31:04 -070058import org.xmlpull.v1.XmlPullParser;
59import org.xmlpull.v1.XmlPullParserException;
60import org.xmlpull.v1.XmlSerializer;
61
62import java.io.File;
Svetoslav Ganovdd68da22013-09-27 10:48:31 -070063import java.io.FileDescriptor;
Svetoslav269403b2013-08-14 17:31:04 -070064import java.io.FileInputStream;
65import java.io.FileNotFoundException;
66import java.io.FileOutputStream;
67import java.io.IOException;
Svetoslav Ganovdd68da22013-09-27 10:48:31 -070068import java.io.PrintWriter;
Svetoslav269403b2013-08-14 17:31:04 -070069import java.util.ArrayList;
Svetoslav Ganov835835e2013-08-04 20:17:52 -070070import java.util.List;
71
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070072/**
73 * Service for exposing some of the {@link PrintSpooler} functionality to
74 * another process.
75 */
76public final class PrintSpoolerService extends Service {
77
Svetoslav269403b2013-08-14 17:31:04 -070078 private static final String LOG_TAG = "PrintSpoolerService";
79
Svetoslavb5f18062013-09-23 18:48:34 -070080 private static final boolean DEBUG_PRINT_JOB_LIFECYCLE = false;
Svetoslav269403b2013-08-14 17:31:04 -070081
Svetoslavc6066792013-09-10 21:08:32 -070082 private static final boolean DEBUG_PERSISTENCE = false;
Svetoslav269403b2013-08-14 17:31:04 -070083
84 private static final boolean PERSISTNECE_MANAGER_ENABLED = true;
85
Svetoslav Ganov835835e2013-08-04 20:17:52 -070086 private static final long CHECK_ALL_PRINTJOBS_HANDLED_DELAY = 5000;
87
Svetoslav Ganovdd68da22013-09-27 10:48:31 -070088 private static final String PRINT_JOB_FILE_PREFIX = "print_job_";
89
Svetoslav269403b2013-08-14 17:31:04 -070090 private static final String PRINT_FILE_EXTENSION = "pdf";
91
92 private static final Object sLock = new Object();
93
94 private final Object mLock = new Object();
95
96 private final List<PrintJobInfo> mPrintJobs = new ArrayList<PrintJobInfo>();
97
98 private static PrintSpoolerService sInstance;
99
Svetoslav Ganov835835e2013-08-04 20:17:52 -0700100 private IPrintSpoolerClient mClient;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700101
Svetoslav269403b2013-08-14 17:31:04 -0700102 private HandlerCaller mHandlerCaller;
103
104 private PersistenceManager mPersistanceManager;
105
106 private NotificationController mNotificationController;
107
Svetoslav269403b2013-08-14 17:31:04 -0700108 public static PrintSpoolerService peekInstance() {
109 synchronized (sLock) {
110 return sInstance;
111 }
112 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700113
114 @Override
115 public void onCreate() {
116 super.onCreate();
Svetoslav269403b2013-08-14 17:31:04 -0700117 mHandlerCaller = new HandlerCaller(this, getMainLooper(),
118 new HandlerCallerCallback(), false);
119
120 mPersistanceManager = new PersistenceManager();
121 mNotificationController = new NotificationController(PrintSpoolerService.this);
122
123 synchronized (mLock) {
124 mPersistanceManager.readStateLocked();
125 handleReadPrintJobsLocked();
126 }
127
128 synchronized (sLock) {
129 sInstance = this;
130 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700131 }
132
133 @Override
134 public IBinder onBind(Intent intent) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700135 return new IPrintSpooler.Stub() {
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700136 @Override
Svetoslav Ganova0027152013-06-25 14:59:53 -0700137 public void getPrintJobInfos(IPrintSpoolerCallbacks callback,
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700138 ComponentName componentName, int state, int appId, int sequence)
Svetoslav269403b2013-08-14 17:31:04 -0700139 throws RemoteException {
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700140 List<PrintJobInfo> printJobs = null;
141 try {
Svetoslav269403b2013-08-14 17:31:04 -0700142 printJobs = PrintSpoolerService.this.getPrintJobInfos(
Svetoslav Ganov835835e2013-08-04 20:17:52 -0700143 componentName, state, appId);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700144 } finally {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700145 callback.onGetPrintJobInfosResult(printJobs, sequence);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700146 }
147 }
148
149 @Override
Svetoslav2fbd2a72013-09-16 17:53:51 -0700150 public void getPrintJobInfo(PrintJobId printJobId, IPrintSpoolerCallbacks callback,
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700151 int appId, int sequence) throws RemoteException {
152 PrintJobInfo printJob = null;
153 try {
Svetoslav269403b2013-08-14 17:31:04 -0700154 printJob = PrintSpoolerService.this.getPrintJobInfo(printJobId, appId);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700155 } finally {
156 callback.onGetPrintJobInfoResult(printJob, sequence);
157 }
158 }
159
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700160 @SuppressWarnings("deprecation")
161 @Override
Svetoslav2fbd2a72013-09-16 17:53:51 -0700162 public void createPrintJob(PrintJobInfo printJob, IPrintClient client,
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700163 IPrintDocumentAdapter printAdapter) throws RemoteException {
164 PrintSpoolerService.this.createPrintJob(printJob);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700165
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700166 Intent intent = new Intent(printJob.getId().flattenToString());
167 intent.setClass(PrintSpoolerService.this, PrintJobConfigActivity.class);
168 intent.putExtra(PrintJobConfigActivity.EXTRA_PRINT_DOCUMENT_ADAPTER,
169 printAdapter.asBinder());
170 intent.putExtra(PrintJobConfigActivity.EXTRA_PRINT_JOB, printJob);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700171
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700172 IntentSender sender = PendingIntent.getActivity(
173 PrintSpoolerService.this, 0, intent, PendingIntent.FLAG_ONE_SHOT
174 | PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender();
Svetoslav2fbd2a72013-09-16 17:53:51 -0700175
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700176 Message message = mHandlerCaller.obtainMessageO(
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700177 HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700178 printJob);
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700179 mHandlerCaller.executeOrSendMessage(message);
180
181 message = mHandlerCaller.obtainMessageOO(
182 HandlerCallerCallback.MSG_START_PRINT_JOB_CONFIG_ACTIVITY,
183 client, sender);
184 mHandlerCaller.executeOrSendMessage(message);
185
186 printJob.setCreationTime(System.currentTimeMillis());
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700187 }
188
189 @Override
Svetoslav2fbd2a72013-09-16 17:53:51 -0700190 public void setPrintJobState(PrintJobId printJobId, int state, String error,
Svetoslav Ganov8c433762013-08-02 14:22:19 -0700191 IPrintSpoolerCallbacks callback, int sequece) throws RemoteException {
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700192 boolean success = false;
193 try {
Svetoslav269403b2013-08-14 17:31:04 -0700194 success = PrintSpoolerService.this.setPrintJobState(
Svetoslav Ganov835835e2013-08-04 20:17:52 -0700195 printJobId, state, error);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700196 } finally {
197 callback.onSetPrintJobStateResult(success, sequece);
198 }
199 }
200
201 @Override
Svetoslav2fbd2a72013-09-16 17:53:51 -0700202 public void setPrintJobTag(PrintJobId printJobId, String tag,
Svetoslav Ganov835835e2013-08-04 20:17:52 -0700203 IPrintSpoolerCallbacks callback, int sequece) throws RemoteException {
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700204 boolean success = false;
205 try {
Svetoslav269403b2013-08-14 17:31:04 -0700206 success = PrintSpoolerService.this.setPrintJobTag(printJobId, tag);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700207 } finally {
208 callback.onSetPrintJobTagResult(success, sequece);
209 }
210 }
211
212 @Override
Svetoslav2fbd2a72013-09-16 17:53:51 -0700213 public void writePrintJobData(ParcelFileDescriptor fd, PrintJobId printJobId) {
Svetoslav269403b2013-08-14 17:31:04 -0700214 PrintSpoolerService.this.writePrintJobData(fd, printJobId);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700215 }
Svetoslav Ganova0027152013-06-25 14:59:53 -0700216
217 @Override
Svetoslav Ganov835835e2013-08-04 20:17:52 -0700218 public void setClient(IPrintSpoolerClient client) {
Svetoslav269403b2013-08-14 17:31:04 -0700219 Message message = mHandlerCaller.obtainMessageO(
220 HandlerCallerCallback.MSG_SET_CLIENT, client);
221 mHandlerCaller.executeOrSendMessage(message);
222 }
Svetoslav2fbd2a72013-09-16 17:53:51 -0700223
224 @Override
225 public void removeObsoletePrintJobs() {
226 PrintSpoolerService.this.removeObsoletePrintJobs();
227 }
228
229 @Override
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700230 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
231 PrintSpoolerService.this.dump(fd, writer, args);
Svetoslav2fbd2a72013-09-16 17:53:51 -0700232 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700233 };
234 }
235
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700236 @Override
237 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
238 synchronized (mLock) {
239 String prefix = args[0];
240 String tab = " ";
241
242 pw.append(prefix).append("print jobs:").println();
243 final int printJobCount = mPrintJobs.size();
244 for (int i = 0; i < printJobCount; i++) {
245 PrintJobInfo printJob = mPrintJobs.get(i);
246 pw.append(prefix).append(tab).append(printJob.toString());
247 pw.println();
248 }
249
250 pw.append(prefix).append("print job files:").println();
251 File[] files = getFilesDir().listFiles();
252 if (files != null) {
253 final int fileCount = files.length;
254 for (int i = 0; i < fileCount; i++) {
255 File file = files[i];
256 if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) {
257 pw.append(prefix).append(tab).append(file.getName()).println();
258 }
259 }
260 }
261 }
262 }
263
Svetoslav269403b2013-08-14 17:31:04 -0700264 private void sendOnPrintJobQueued(PrintJobInfo printJob) {
265 Message message = mHandlerCaller.obtainMessageO(
266 HandlerCallerCallback.MSG_ON_PRINT_JOB_QUEUED, printJob);
267 mHandlerCaller.executeOrSendMessage(message);
268 }
269
270 private void sendOnAllPrintJobsForServiceHandled(ComponentName service) {
271 Message message = mHandlerCaller.obtainMessageO(
272 HandlerCallerCallback.MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED, service);
273 mHandlerCaller.executeOrSendMessage(message);
274 }
275
276 private void sendOnAllPrintJobsHandled() {
277 Message message = mHandlerCaller.obtainMessage(
278 HandlerCallerCallback.MSG_ON_ALL_PRINT_JOBS_HANDLED);
279 mHandlerCaller.executeOrSendMessage(message);
280 }
281
282 private final class HandlerCallerCallback implements HandlerCaller.Callback {
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700283 public static final int MSG_SET_CLIENT = 1;
284 public static final int MSG_START_PRINT_JOB_CONFIG_ACTIVITY = 2;
285 public static final int MSG_ON_PRINT_JOB_QUEUED = 3;
286 public static final int MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED = 4;
287 public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 5;
288 public static final int MSG_CHECK_ALL_PRINTJOBS_HANDLED = 6;
289 public static final int MSG_ON_PRINT_JOB_STATE_CHANGED = 7;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700290
291 @Override
Svetoslav269403b2013-08-14 17:31:04 -0700292 public void executeMessage(Message message) {
Svetoslav Ganov835835e2013-08-04 20:17:52 -0700293 switch (message.what) {
294 case MSG_SET_CLIENT: {
Svetoslav269403b2013-08-14 17:31:04 -0700295 synchronized (mLock) {
296 mClient = (IPrintSpoolerClient) message.obj;
297 if (mClient != null) {
298 Message msg = mHandlerCaller.obtainMessage(
299 HandlerCallerCallback.MSG_CHECK_ALL_PRINTJOBS_HANDLED);
300 mHandlerCaller.sendMessageDelayed(msg,
301 CHECK_ALL_PRINTJOBS_HANDLED_DELAY);
302 }
Svetoslav Ganov835835e2013-08-04 20:17:52 -0700303 }
304 } break;
305
306 case MSG_START_PRINT_JOB_CONFIG_ACTIVITY: {
307 SomeArgs args = (SomeArgs) message.obj;
308 IPrintClient client = (IPrintClient) args.arg1;
309 IntentSender sender = (IntentSender) args.arg2;
310 args.recycle();
311 try {
312 client.startPrintJobConfigActivity(sender);
313 } catch (RemoteException re) {
314 Slog.i(LOG_TAG, "Error starting print job config activity!", re);
315 }
316 } break;
317
Svetoslav Ganov835835e2013-08-04 20:17:52 -0700318 case MSG_ON_PRINT_JOB_QUEUED: {
319 PrintJobInfo printJob = (PrintJobInfo) message.obj;
320 if (mClient != null) {
321 try {
322 mClient.onPrintJobQueued(printJob);
323 } catch (RemoteException re) {
324 Slog.e(LOG_TAG, "Error notify for a queued print job.", re);
325 }
326 }
327 } break;
328
329 case MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED: {
330 ComponentName service = (ComponentName) message.obj;
331 if (mClient != null) {
332 try {
333 mClient.onAllPrintJobsForServiceHandled(service);
334 } catch (RemoteException re) {
335 Slog.e(LOG_TAG, "Error notify for all print jobs per service"
336 + " handled.", re);
337 }
338 }
339 } break;
340
341 case MSG_ON_ALL_PRINT_JOBS_HANDLED: {
342 if (mClient != null) {
343 try {
344 mClient.onAllPrintJobsHandled();
345 } catch (RemoteException re) {
346 Slog.e(LOG_TAG, "Error notify for all print job handled.", re);
347 }
348 }
349 } break;
350
Svetoslav Ganov835835e2013-08-04 20:17:52 -0700351 case MSG_CHECK_ALL_PRINTJOBS_HANDLED: {
Svetoslav269403b2013-08-14 17:31:04 -0700352 checkAllPrintJobsHandled();
353 } break;
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700354
355 case MSG_ON_PRINT_JOB_STATE_CHANGED: {
356 if (mClient != null) {
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700357 PrintJobInfo printJob = (PrintJobInfo) message.obj;
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700358 try {
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700359 mClient.onPrintJobStateChanged(printJob);
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700360 } catch (RemoteException re) {
361 Slog.e(LOG_TAG, "Error notify for print job state change.", re);
362 }
363 }
364 } break;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700365 }
366 }
367 }
Svetoslav269403b2013-08-14 17:31:04 -0700368
369 public List<PrintJobInfo> getPrintJobInfos(ComponentName componentName,
370 int state, int appId) {
371 List<PrintJobInfo> foundPrintJobs = null;
372 synchronized (mLock) {
373 final int printJobCount = mPrintJobs.size();
374 for (int i = 0; i < printJobCount; i++) {
375 PrintJobInfo printJob = mPrintJobs.get(i);
376 PrinterId printerId = printJob.getPrinterId();
377 final boolean sameComponent = (componentName == null
378 || (printerId != null
379 && componentName.equals(printerId.getServiceName())));
380 final boolean sameAppId = appId == PrintManager.APP_ID_ANY
381 || printJob.getAppId() == appId;
382 final boolean sameState = (state == printJob.getState())
383 || (state == PrintJobInfo.STATE_ANY)
384 || (state == PrintJobInfo.STATE_ANY_VISIBLE_TO_CLIENTS
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700385 && isStateVisibleToUser(printJob.getState()))
386 || (state == PrintJobInfo.STATE_ANY_ACTIVE
387 && isActiveState(printJob.getState()));
Svetoslav269403b2013-08-14 17:31:04 -0700388 if (sameComponent && sameAppId && sameState) {
389 if (foundPrintJobs == null) {
390 foundPrintJobs = new ArrayList<PrintJobInfo>();
391 }
392 foundPrintJobs.add(printJob);
393 }
394 }
395 }
396 return foundPrintJobs;
397 }
398
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700399 private boolean isStateVisibleToUser(int state) {
400 return (isActiveState(state) && (state == PrintJobInfo.STATE_FAILED
Svetoslav2fbd2a72013-09-16 17:53:51 -0700401 || state == PrintJobInfo.STATE_COMPLETED || state == PrintJobInfo.STATE_CANCELED
402 || state == PrintJobInfo.STATE_BLOCKED));
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700403 }
404
Svetoslav2fbd2a72013-09-16 17:53:51 -0700405 public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId) {
Svetoslav269403b2013-08-14 17:31:04 -0700406 synchronized (mLock) {
407 final int printJobCount = mPrintJobs.size();
408 for (int i = 0; i < printJobCount; i++) {
409 PrintJobInfo printJob = mPrintJobs.get(i);
Svetoslav2fbd2a72013-09-16 17:53:51 -0700410 if (printJob.getId().equals(printJobId)
Svetoslav269403b2013-08-14 17:31:04 -0700411 && (appId == PrintManager.APP_ID_ANY
412 || appId == printJob.getAppId())) {
413 return printJob;
414 }
415 }
416 return null;
417 }
418 }
419
Svetoslav2fbd2a72013-09-16 17:53:51 -0700420 public void createPrintJob(PrintJobInfo printJob) {
Svetoslav269403b2013-08-14 17:31:04 -0700421 synchronized (mLock) {
Svetoslav269403b2013-08-14 17:31:04 -0700422 addPrintJobLocked(printJob);
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700423 setPrintJobState(printJob.getId(), PrintJobInfo.STATE_CREATED, null);
Svetoslav269403b2013-08-14 17:31:04 -0700424 }
425 }
426
427 private void handleReadPrintJobsLocked() {
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700428 // Make a map with the files for a print job since we may have
429 // to delete some. One example of getting orphan files if the
430 // spooler crashes while constructing a print job. We do not
431 // persist partially populated print jobs under construction to
432 // avoid special handling for various attributes missing.
433 ArrayMap<PrintJobId, File> fileForJobMap = null;
434 File[] files = getFilesDir().listFiles();
435 if (files != null) {
436 final int fileCount = files.length;
437 for (int i = 0; i < fileCount; i++) {
438 File file = files[i];
439 if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) {
440 if (fileForJobMap == null) {
441 fileForJobMap = new ArrayMap<PrintJobId, File>();
442 }
443 String printJobIdString = file.getName().substring(0,
444 PRINT_JOB_FILE_PREFIX.length());
445 PrintJobId printJobId = PrintJobId.unflattenFromString(
446 printJobIdString);
447 fileForJobMap.put(printJobId, file);
448 }
449 }
450 }
451
Svetoslav269403b2013-08-14 17:31:04 -0700452 final int printJobCount = mPrintJobs.size();
453 for (int i = 0; i < printJobCount; i++) {
454 PrintJobInfo printJob = mPrintJobs.get(i);
455
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700456 // We want to have only the orphan files at the end.
457 if (fileForJobMap != null) {
458 fileForJobMap.remove(printJob.getId());
459 }
460
Svetoslav269403b2013-08-14 17:31:04 -0700461 // Update the notification.
462 mNotificationController.onPrintJobStateChanged(printJob);
Svetoslav269403b2013-08-14 17:31:04 -0700463 switch (printJob.getState()) {
464 case PrintJobInfo.STATE_QUEUED:
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700465 case PrintJobInfo.STATE_STARTED:
466 case PrintJobInfo.STATE_BLOCKED: {
467 // We have a print job that was queued or started or blocked in
468 // the past but the device battery died or a crash occurred. In
469 // this case we assume the print job failed and let the user
470 // decide whether to restart the job or just cancel it.
Svetoslav269403b2013-08-14 17:31:04 -0700471 setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED,
472 getString(R.string.no_connection_to_printer));
Svetoslav2fbd2a72013-09-16 17:53:51 -0700473 } break;
Svetoslav269403b2013-08-14 17:31:04 -0700474 }
475 }
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700476
477 // Delete the orphan files.
478 if (fileForJobMap != null) {
479 final int orphanFileCount = fileForJobMap.size();
480 for (int i = 0; i < orphanFileCount; i++) {
481 File file = fileForJobMap.valueAt(i);
482 file.delete();
483 }
484 }
Svetoslav269403b2013-08-14 17:31:04 -0700485 }
486
487 public void checkAllPrintJobsHandled() {
488 synchronized (mLock) {
489 if (!hasActivePrintJobsLocked()) {
490 notifyOnAllPrintJobsHandled();
491 }
492 }
493 }
494
Svetoslav2fbd2a72013-09-16 17:53:51 -0700495 public void writePrintJobData(final ParcelFileDescriptor fd, final PrintJobId printJobId) {
Svetoslav269403b2013-08-14 17:31:04 -0700496 final PrintJobInfo printJob;
497 synchronized (mLock) {
498 printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
499 }
500 new AsyncTask<Void, Void, Void>() {
501 @Override
502 protected Void doInBackground(Void... params) {
503 FileInputStream in = null;
504 FileOutputStream out = null;
505 try {
506 if (printJob != null) {
507 File file = generateFileForPrintJob(printJobId);
508 in = new FileInputStream(file);
509 out = new FileOutputStream(fd.getFileDescriptor());
510 }
511 final byte[] buffer = new byte[8192];
512 while (true) {
513 final int readByteCount = in.read(buffer);
514 if (readByteCount < 0) {
515 return null;
516 }
517 out.write(buffer, 0, readByteCount);
518 }
519 } catch (FileNotFoundException fnfe) {
520 Log.e(LOG_TAG, "Error writing print job data!", fnfe);
521 } catch (IOException ioe) {
522 Log.e(LOG_TAG, "Error writing print job data!", ioe);
523 } finally {
524 IoUtils.closeQuietly(in);
525 IoUtils.closeQuietly(out);
526 IoUtils.closeQuietly(fd);
527 }
528 Log.i(LOG_TAG, "[END WRITE]");
529 return null;
530 }
531 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
532 }
533
Svetoslav2fbd2a72013-09-16 17:53:51 -0700534 public File generateFileForPrintJob(PrintJobId printJobId) {
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700535 return new File(getFilesDir(), PRINT_JOB_FILE_PREFIX
Svetoslav2fbd2a72013-09-16 17:53:51 -0700536 + printJobId.flattenToString() + "." + PRINT_FILE_EXTENSION);
Svetoslav269403b2013-08-14 17:31:04 -0700537 }
538
539 private void addPrintJobLocked(PrintJobInfo printJob) {
540 mPrintJobs.add(printJob);
541 if (DEBUG_PRINT_JOB_LIFECYCLE) {
542 Slog.i(LOG_TAG, "[ADD] " + printJob);
543 }
544 }
545
Svetoslav2fbd2a72013-09-16 17:53:51 -0700546 private void removeObsoletePrintJobs() {
547 synchronized (mLock) {
548 final int printJobCount = mPrintJobs.size();
549 for (int i = printJobCount - 1; i >= 0; i--) {
550 PrintJobInfo printJob = mPrintJobs.get(i);
551 if (isObsoleteState(printJob.getState())) {
552 mPrintJobs.remove(i);
553 if (DEBUG_PRINT_JOB_LIFECYCLE) {
554 Slog.i(LOG_TAG, "[REMOVE] " + printJob.getId().flattenToString());
555 }
556 removePrintJobFileLocked(printJob.getId());
557 }
558 }
559 mPersistanceManager.writeStateLocked();
560 }
561 }
562
563 private void removePrintJobFileLocked(PrintJobId printJobId) {
564 File file = generateFileForPrintJob(printJobId);
565 if (file.exists()) {
566 file.delete();
567 if (DEBUG_PRINT_JOB_LIFECYCLE) {
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700568 Slog.i(LOG_TAG, "[REMOVE FILE FOR] " + printJobId);
Svetoslav2fbd2a72013-09-16 17:53:51 -0700569 }
570 }
571 }
572
573 public boolean setPrintJobState(PrintJobId printJobId, int state, String error) {
Svetoslav269403b2013-08-14 17:31:04 -0700574 boolean success = false;
575
576 synchronized (mLock) {
577 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
578 if (printJob != null) {
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700579 final int oldState = printJob.getState();
580 if (oldState == state) {
581 return false;
582 }
583
Svetoslav269403b2013-08-14 17:31:04 -0700584 success = true;
585
586 printJob.setState(state);
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700587 printJob.setStateReason(error);
Svetoslav269403b2013-08-14 17:31:04 -0700588 mNotificationController.onPrintJobStateChanged(printJob);
589
590 if (DEBUG_PRINT_JOB_LIFECYCLE) {
591 Slog.i(LOG_TAG, "[STATE CHANGED] " + printJob);
592 }
593
594 switch (state) {
595 case PrintJobInfo.STATE_COMPLETED:
596 case PrintJobInfo.STATE_CANCELED:
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700597 mPrintJobs.remove(printJob);
Svetoslav2fbd2a72013-09-16 17:53:51 -0700598 removePrintJobFileLocked(printJob.getId());
Svetoslav269403b2013-08-14 17:31:04 -0700599 // $fall-through$
600
601 case PrintJobInfo.STATE_FAILED: {
602 PrinterId printerId = printJob.getPrinterId();
603 if (printerId != null) {
604 ComponentName service = printerId.getServiceName();
605 if (!hasActivePrintJobsForServiceLocked(service)) {
606 sendOnAllPrintJobsForServiceHandled(service);
607 }
608 }
609 } break;
610
611 case PrintJobInfo.STATE_QUEUED: {
612 sendOnPrintJobQueued(new PrintJobInfo(printJob));
613 } break;
614 }
615
616 if (shouldPersistPrintJob(printJob)) {
617 mPersistanceManager.writeStateLocked();
618 }
619
620 if (!hasActivePrintJobsLocked()) {
621 notifyOnAllPrintJobsHandled();
622 }
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700623
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700624 Message message = mHandlerCaller.obtainMessageO(
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700625 HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
Svetoslav Ganovdd68da22013-09-27 10:48:31 -0700626 printJob);
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700627 mHandlerCaller.executeOrSendMessage(message);
Svetoslav269403b2013-08-14 17:31:04 -0700628 }
629 }
630
631 return success;
632 }
633
634 public boolean hasActivePrintJobsLocked() {
635 final int printJobCount = mPrintJobs.size();
636 for (int i = 0; i < printJobCount; i++) {
637 PrintJobInfo printJob = mPrintJobs.get(i);
638 if (isActiveState(printJob.getState())) {
639 return true;
640 }
641 }
642 return false;
643 }
644
645 public boolean hasActivePrintJobsForServiceLocked(ComponentName service) {
646 final int printJobCount = mPrintJobs.size();
647 for (int i = 0; i < printJobCount; i++) {
648 PrintJobInfo printJob = mPrintJobs.get(i);
649 if (isActiveState(printJob.getState())
650 && printJob.getPrinterId().getServiceName().equals(service)) {
651 return true;
652 }
653 }
654 return false;
655 }
656
Svetoslav2fbd2a72013-09-16 17:53:51 -0700657 private boolean isObsoleteState(int printJobState) {
658 return (isTeminalState(printJobState)
659 || printJobState == PrintJobInfo.STATE_QUEUED);
660 }
661
Svetoslav269403b2013-08-14 17:31:04 -0700662 private boolean isActiveState(int printJobState) {
663 return printJobState == PrintJobInfo.STATE_CREATED
664 || printJobState == PrintJobInfo.STATE_QUEUED
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700665 || printJobState == PrintJobInfo.STATE_STARTED
666 || printJobState == PrintJobInfo.STATE_BLOCKED;
Svetoslav269403b2013-08-14 17:31:04 -0700667 }
668
Svetoslav2fbd2a72013-09-16 17:53:51 -0700669 private boolean isTeminalState(int printJobState) {
670 return printJobState == PrintJobInfo.STATE_COMPLETED
671 || printJobState == PrintJobInfo.STATE_CANCELED;
672 }
673
674 public boolean setPrintJobTag(PrintJobId printJobId, String tag) {
Svetoslav269403b2013-08-14 17:31:04 -0700675 synchronized (mLock) {
676 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
677 if (printJob != null) {
678 String printJobTag = printJob.getTag();
679 if (printJobTag == null) {
680 if (tag == null) {
681 return false;
682 }
683 } else if (printJobTag.equals(tag)) {
684 return false;
685 }
686 printJob.setTag(tag);
687 if (shouldPersistPrintJob(printJob)) {
688 mPersistanceManager.writeStateLocked();
689 }
690 return true;
691 }
692 }
693 return false;
694 }
695
Svetoslav2fbd2a72013-09-16 17:53:51 -0700696 public void setPrintJobCopiesNoPersistence(PrintJobId printJobId, int copies) {
Svetoslav269403b2013-08-14 17:31:04 -0700697 synchronized (mLock) {
698 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
699 if (printJob != null) {
700 printJob.setCopies(copies);
701 }
702 }
703 }
704
Svetoslav2fbd2a72013-09-16 17:53:51 -0700705 public void setPrintJobPrintDocumentInfoNoPersistence(PrintJobId printJobId,
706 PrintDocumentInfo info) {
Svetoslav269403b2013-08-14 17:31:04 -0700707 synchronized (mLock) {
708 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
709 if (printJob != null) {
710 printJob.setDocumentInfo(info);
711 }
712 }
713 }
714
Svetoslav2fbd2a72013-09-16 17:53:51 -0700715 public void setPrintJobAttributesNoPersistence(PrintJobId printJobId,
716 PrintAttributes attributes) {
Svetoslav269403b2013-08-14 17:31:04 -0700717 synchronized (mLock) {
718 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
719 if (printJob != null) {
720 printJob.setAttributes(attributes);
721 }
722 }
723 }
724
Svetoslav2fbd2a72013-09-16 17:53:51 -0700725 public void setPrintJobPrinterNoPersistence(PrintJobId printJobId, PrinterInfo printer) {
Svetoslav269403b2013-08-14 17:31:04 -0700726 synchronized (mLock) {
727 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
728 if (printJob != null) {
729 printJob.setPrinterId(printer.getId());
730 printJob.setPrinterName(printer.getName());
731 }
732 }
733 }
734
Svetoslav2fbd2a72013-09-16 17:53:51 -0700735 public void setPrintJobPagesNoPersistence(PrintJobId printJobId, PageRange[] pages) {
Svetoslav269403b2013-08-14 17:31:04 -0700736 synchronized (mLock) {
737 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
738 if (printJob != null) {
739 printJob.setPages(pages);
740 }
741 }
742 }
743
744 private boolean shouldPersistPrintJob(PrintJobInfo printJob) {
745 return printJob.getState() >= PrintJobInfo.STATE_QUEUED;
746 }
747
748 private void notifyOnAllPrintJobsHandled() {
749 // This has to run on the tread that is persisting the current state
750 // since this call may result in the system unbinding from the spooler
751 // and as a result the spooler process may get killed before the write
752 // completes.
753 new AsyncTask<Void, Void, Void>() {
754 @Override
755 protected Void doInBackground(Void... params) {
756 sendOnAllPrintJobsHandled();
757 return null;
758 }
759 }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
760 }
761
762 private final class PersistenceManager {
763 private static final String PERSIST_FILE_NAME = "print_spooler_state.xml";
764
765 private static final String TAG_SPOOLER = "spooler";
766 private static final String TAG_JOB = "job";
767
768 private static final String TAG_PRINTER_ID = "printerId";
769 private static final String TAG_PAGE_RANGE = "pageRange";
770 private static final String TAG_ATTRIBUTES = "attributes";
771 private static final String TAG_DOCUMENT_INFO = "documentInfo";
772
773 private static final String ATTR_ID = "id";
774 private static final String ATTR_LABEL = "label";
Svetoslav773f54d2013-09-03 14:01:43 -0700775 private static final String ATTR_LABEL_RES_ID = "labelResId";
776 private static final String ATTR_PACKAGE_NAME = "packageName";
Svetoslav269403b2013-08-14 17:31:04 -0700777 private static final String ATTR_STATE = "state";
778 private static final String ATTR_APP_ID = "appId";
779 private static final String ATTR_USER_ID = "userId";
780 private static final String ATTR_TAG = "tag";
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700781 private static final String ATTR_CREATION_TIME = "creationTime";
Svetoslav269403b2013-08-14 17:31:04 -0700782 private static final String ATTR_COPIES = "copies";
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700783 private static final String ATTR_PRINTER_NAME = "printerName";
784 private static final String ATTR_STATE_REASON = "stateReason";
Svetoslav269403b2013-08-14 17:31:04 -0700785
786 private static final String TAG_MEDIA_SIZE = "mediaSize";
787 private static final String TAG_RESOLUTION = "resolution";
788 private static final String TAG_MARGINS = "margins";
Svetoslav269403b2013-08-14 17:31:04 -0700789
Svetoslav269403b2013-08-14 17:31:04 -0700790 private static final String ATTR_COLOR_MODE = "colorMode";
Svetoslav269403b2013-08-14 17:31:04 -0700791
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700792 private static final String ATTR_LOCAL_ID = "localId";
Svetoslav269403b2013-08-14 17:31:04 -0700793 private static final String ATTR_SERVICE_NAME = "serviceName";
794
795 private static final String ATTR_WIDTH_MILS = "widthMils";
796 private static final String ATTR_HEIGHT_MILS = "heightMils";
797
798 private static final String ATTR_HORIZONTAL_DPI = "horizontalDip";
799 private static final String ATTR_VERTICAL_DPI = "verticalDpi";
800
801 private static final String ATTR_LEFT_MILS = "leftMils";
802 private static final String ATTR_TOP_MILS = "topMils";
803 private static final String ATTR_RIGHT_MILS = "rightMils";
804 private static final String ATTR_BOTTOM_MILS = "bottomMils";
805
806 private static final String ATTR_START = "start";
807 private static final String ATTR_END = "end";
808
809 private static final String ATTR_NAME = "name";
810 private static final String ATTR_PAGE_COUNT = "pageCount";
811 private static final String ATTR_CONTENT_TYPE = "contentType";
812
813 private final AtomicFile mStatePersistFile;
814
815 private boolean mWriteStateScheduled;
816
817 private PersistenceManager() {
818 mStatePersistFile = new AtomicFile(new File(getFilesDir(),
819 PERSIST_FILE_NAME));
820 }
821
822 public void writeStateLocked() {
823 if (!PERSISTNECE_MANAGER_ENABLED) {
824 return;
825 }
826 if (mWriteStateScheduled) {
827 return;
828 }
829 mWriteStateScheduled = true;
830 new AsyncTask<Void, Void, Void>() {
831 @Override
832 protected Void doInBackground(Void... params) {
833 synchronized (mLock) {
834 mWriteStateScheduled = false;
835 doWriteStateLocked();
836 }
837 return null;
838 }
839 }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
840 }
841
842 private void doWriteStateLocked() {
843 if (DEBUG_PERSISTENCE) {
844 Log.i(LOG_TAG, "[PERSIST START]");
845 }
846 FileOutputStream out = null;
847 try {
848 out = mStatePersistFile.startWrite();
849
850 XmlSerializer serializer = new FastXmlSerializer();
851 serializer.setOutput(out, "utf-8");
852 serializer.startDocument(null, true);
853 serializer.startTag(null, TAG_SPOOLER);
854
855 List<PrintJobInfo> printJobs = mPrintJobs;
856
857 final int printJobCount = printJobs.size();
858 for (int j = 0; j < printJobCount; j++) {
859 PrintJobInfo printJob = printJobs.get(j);
860
Svetoslav269403b2013-08-14 17:31:04 -0700861 serializer.startTag(null, TAG_JOB);
862
Svetoslav2fbd2a72013-09-16 17:53:51 -0700863 serializer.attribute(null, ATTR_ID, printJob.getId().flattenToString());
Svetoslav269403b2013-08-14 17:31:04 -0700864 serializer.attribute(null, ATTR_LABEL, printJob.getLabel().toString());
865 serializer.attribute(null, ATTR_STATE, String.valueOf(printJob.getState()));
866 serializer.attribute(null, ATTR_APP_ID, String.valueOf(printJob.getAppId()));
867 serializer.attribute(null, ATTR_USER_ID, String.valueOf(printJob.getUserId()));
868 String tag = printJob.getTag();
869 if (tag != null) {
870 serializer.attribute(null, ATTR_TAG, tag);
871 }
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700872 serializer.attribute(null, ATTR_CREATION_TIME, String.valueOf(
873 printJob.getCreationTime()));
Svetoslav269403b2013-08-14 17:31:04 -0700874 serializer.attribute(null, ATTR_COPIES, String.valueOf(printJob.getCopies()));
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700875 String printerName = printJob.getPrinterName();
876 if (!TextUtils.isEmpty(printerName)) {
877 serializer.attribute(null, ATTR_PRINTER_NAME, printerName);
878 }
879 String stateReason = printJob.getStateReason();
880 if (!TextUtils.isEmpty(stateReason)) {
881 serializer.attribute(null, ATTR_STATE_REASON, stateReason);
882 }
Svetoslav269403b2013-08-14 17:31:04 -0700883
884 PrinterId printerId = printJob.getPrinterId();
885 if (printerId != null) {
886 serializer.startTag(null, TAG_PRINTER_ID);
887 serializer.attribute(null, ATTR_LOCAL_ID, printerId.getLocalId());
888 serializer.attribute(null, ATTR_SERVICE_NAME, printerId.getServiceName()
889 .flattenToString());
890 serializer.endTag(null, TAG_PRINTER_ID);
891 }
892
893 PageRange[] pages = printJob.getPages();
894 if (pages != null) {
895 for (int i = 0; i < pages.length; i++) {
896 serializer.startTag(null, TAG_PAGE_RANGE);
897 serializer.attribute(null, ATTR_START, String.valueOf(
898 pages[i].getStart()));
899 serializer.attribute(null, ATTR_END, String.valueOf(
900 pages[i].getEnd()));
901 serializer.endTag(null, TAG_PAGE_RANGE);
902 }
903 }
904
905 PrintAttributes attributes = printJob.getAttributes();
906 if (attributes != null) {
907 serializer.startTag(null, TAG_ATTRIBUTES);
908
Svetoslav269403b2013-08-14 17:31:04 -0700909 final int colorMode = attributes.getColorMode();
910 serializer.attribute(null, ATTR_COLOR_MODE,
911 String.valueOf(colorMode));
912
Svetoslav269403b2013-08-14 17:31:04 -0700913 MediaSize mediaSize = attributes.getMediaSize();
914 if (mediaSize != null) {
915 serializer.startTag(null, TAG_MEDIA_SIZE);
916 serializer.attribute(null, ATTR_ID, mediaSize.getId());
Svetoslav269403b2013-08-14 17:31:04 -0700917 serializer.attribute(null, ATTR_WIDTH_MILS, String.valueOf(
918 mediaSize.getWidthMils()));
919 serializer.attribute(null, ATTR_HEIGHT_MILS, String.valueOf(
920 mediaSize.getHeightMils()));
Svetoslava76233a2013-09-05 09:38:02 -0700921 // We prefer to store only the package name and
922 // resource id and fallback to the label.
923 if (!TextUtils.isEmpty(mediaSize.mPackageName)
Svetoslav773f54d2013-09-03 14:01:43 -0700924 && mediaSize.mLabelResId > 0) {
925 serializer.attribute(null, ATTR_PACKAGE_NAME,
926 mediaSize.mPackageName);
927 serializer.attribute(null, ATTR_LABEL_RES_ID,
928 String.valueOf(mediaSize.mLabelResId));
929 } else {
930 serializer.attribute(null, ATTR_LABEL,
931 mediaSize.getLabel(getPackageManager()));
932 }
Svetoslav269403b2013-08-14 17:31:04 -0700933 serializer.endTag(null, TAG_MEDIA_SIZE);
934 }
935
936 Resolution resolution = attributes.getResolution();
937 if (resolution != null) {
938 serializer.startTag(null, TAG_RESOLUTION);
939 serializer.attribute(null, ATTR_ID, resolution.getId());
Svetoslav269403b2013-08-14 17:31:04 -0700940 serializer.attribute(null, ATTR_HORIZONTAL_DPI, String.valueOf(
941 resolution.getHorizontalDpi()));
942 serializer.attribute(null, ATTR_VERTICAL_DPI, String.valueOf(
943 resolution.getVerticalDpi()));
Svetoslavc6066792013-09-10 21:08:32 -0700944 serializer.attribute(null, ATTR_LABEL,
Svetoslav651dd4e2013-09-12 14:37:47 -0700945 resolution.getLabel());
Svetoslav269403b2013-08-14 17:31:04 -0700946 serializer.endTag(null, TAG_RESOLUTION);
947 }
948
Svetoslav651dd4e2013-09-12 14:37:47 -0700949 Margins margins = attributes.getMinMargins();
Svetoslav269403b2013-08-14 17:31:04 -0700950 if (margins != null) {
951 serializer.startTag(null, TAG_MARGINS);
952 serializer.attribute(null, ATTR_LEFT_MILS, String.valueOf(
953 margins.getLeftMils()));
954 serializer.attribute(null, ATTR_TOP_MILS, String.valueOf(
955 margins.getTopMils()));
956 serializer.attribute(null, ATTR_RIGHT_MILS, String.valueOf(
957 margins.getRightMils()));
958 serializer.attribute(null, ATTR_BOTTOM_MILS, String.valueOf(
959 margins.getBottomMils()));
960 serializer.endTag(null, TAG_MARGINS);
961 }
962
Svetoslav269403b2013-08-14 17:31:04 -0700963 serializer.endTag(null, TAG_ATTRIBUTES);
964 }
965
966 PrintDocumentInfo documentInfo = printJob.getDocumentInfo();
967 if (documentInfo != null) {
968 serializer.startTag(null, TAG_DOCUMENT_INFO);
969 serializer.attribute(null, ATTR_NAME, documentInfo.getName());
970 serializer.attribute(null, ATTR_CONTENT_TYPE, String.valueOf(
971 documentInfo.getContentType()));
972 serializer.attribute(null, ATTR_PAGE_COUNT, String.valueOf(
973 documentInfo.getPageCount()));
974 serializer.endTag(null, TAG_DOCUMENT_INFO);
975 }
976
977 serializer.endTag(null, TAG_JOB);
978
979 if (DEBUG_PERSISTENCE) {
980 Log.i(LOG_TAG, "[PERSISTED] " + printJob);
981 }
982 }
983
984 serializer.endTag(null, TAG_SPOOLER);
985 serializer.endDocument();
986 mStatePersistFile.finishWrite(out);
987 if (DEBUG_PERSISTENCE) {
988 Log.i(LOG_TAG, "[PERSIST END]");
989 }
990 } catch (IOException e) {
991 Slog.w(LOG_TAG, "Failed to write state, restoring backup.", e);
992 mStatePersistFile.failWrite(out);
993 } finally {
994 IoUtils.closeQuietly(out);
995 }
996 }
997
998 public void readStateLocked() {
999 if (!PERSISTNECE_MANAGER_ENABLED) {
1000 return;
1001 }
1002 FileInputStream in = null;
1003 try {
1004 in = mStatePersistFile.openRead();
1005 } catch (FileNotFoundException e) {
1006 Log.i(LOG_TAG, "No existing print spooler state.");
1007 return;
1008 }
1009 try {
1010 XmlPullParser parser = Xml.newPullParser();
1011 parser.setInput(in, null);
1012 parseState(parser);
1013 } catch (IllegalStateException ise) {
1014 Slog.w(LOG_TAG, "Failed parsing ", ise);
1015 } catch (NullPointerException npe) {
1016 Slog.w(LOG_TAG, "Failed parsing ", npe);
1017 } catch (NumberFormatException nfe) {
1018 Slog.w(LOG_TAG, "Failed parsing ", nfe);
1019 } catch (XmlPullParserException xppe) {
1020 Slog.w(LOG_TAG, "Failed parsing ", xppe);
1021 } catch (IOException ioe) {
1022 Slog.w(LOG_TAG, "Failed parsing ", ioe);
1023 } catch (IndexOutOfBoundsException iobe) {
1024 Slog.w(LOG_TAG, "Failed parsing ", iobe);
1025 } finally {
1026 IoUtils.closeQuietly(in);
1027 }
1028 }
1029
1030 private void parseState(XmlPullParser parser)
1031 throws IOException, XmlPullParserException {
1032 parser.next();
1033 skipEmptyTextTags(parser);
1034 expect(parser, XmlPullParser.START_TAG, TAG_SPOOLER);
1035 parser.next();
1036
1037 while (parsePrintJob(parser)) {
1038 parser.next();
1039 }
1040
1041 skipEmptyTextTags(parser);
1042 expect(parser, XmlPullParser.END_TAG, TAG_SPOOLER);
1043 }
1044
1045 private boolean parsePrintJob(XmlPullParser parser)
1046 throws IOException, XmlPullParserException {
1047 skipEmptyTextTags(parser);
1048 if (!accept(parser, XmlPullParser.START_TAG, TAG_JOB)) {
1049 return false;
1050 }
1051
1052 PrintJobInfo printJob = new PrintJobInfo();
1053
Svetoslav2fbd2a72013-09-16 17:53:51 -07001054 PrintJobId printJobId = PrintJobId.unflattenFromString(
1055 parser.getAttributeValue(null, ATTR_ID));
Svetoslav269403b2013-08-14 17:31:04 -07001056 printJob.setId(printJobId);
1057 String label = parser.getAttributeValue(null, ATTR_LABEL);
1058 printJob.setLabel(label);
1059 final int state = Integer.parseInt(parser.getAttributeValue(null, ATTR_STATE));
1060 printJob.setState(state);
1061 final int appId = Integer.parseInt(parser.getAttributeValue(null, ATTR_APP_ID));
1062 printJob.setAppId(appId);
1063 final int userId = Integer.parseInt(parser.getAttributeValue(null, ATTR_USER_ID));
1064 printJob.setUserId(userId);
1065 String tag = parser.getAttributeValue(null, ATTR_TAG);
1066 printJob.setTag(tag);
Svetoslav Ganov704697b2013-09-21 20:30:24 -07001067 String creationTime = parser.getAttributeValue(null, ATTR_CREATION_TIME);
1068 printJob.setCreationTime(Long.parseLong(creationTime));
Svetoslav269403b2013-08-14 17:31:04 -07001069 String copies = parser.getAttributeValue(null, ATTR_COPIES);
1070 printJob.setCopies(Integer.parseInt(copies));
Svetoslav Ganov704697b2013-09-21 20:30:24 -07001071 String printerName = parser.getAttributeValue(null, ATTR_PRINTER_NAME);
1072 printJob.setPrinterName(printerName);
1073 String stateReason = parser.getAttributeValue(null, ATTR_STATE_REASON);
1074 printJob.setStateReason(stateReason);
Svetoslav269403b2013-08-14 17:31:04 -07001075
1076 parser.next();
1077
1078 skipEmptyTextTags(parser);
1079 if (accept(parser, XmlPullParser.START_TAG, TAG_PRINTER_ID)) {
1080 String localId = parser.getAttributeValue(null, ATTR_LOCAL_ID);
1081 ComponentName service = ComponentName.unflattenFromString(parser.getAttributeValue(
1082 null, ATTR_SERVICE_NAME));
1083 printJob.setPrinterId(new PrinterId(service, localId));
1084 parser.next();
1085 skipEmptyTextTags(parser);
1086 expect(parser, XmlPullParser.END_TAG, TAG_PRINTER_ID);
1087 parser.next();
1088 }
1089
1090 skipEmptyTextTags(parser);
1091 List<PageRange> pageRanges = null;
1092 while (accept(parser, XmlPullParser.START_TAG, TAG_PAGE_RANGE)) {
1093 final int start = Integer.parseInt(parser.getAttributeValue(null, ATTR_START));
1094 final int end = Integer.parseInt(parser.getAttributeValue(null, ATTR_END));
1095 PageRange pageRange = new PageRange(start, end);
1096 if (pageRanges == null) {
1097 pageRanges = new ArrayList<PageRange>();
1098 }
1099 pageRanges.add(pageRange);
1100 parser.next();
1101 skipEmptyTextTags(parser);
1102 expect(parser, XmlPullParser.END_TAG, TAG_PAGE_RANGE);
1103 parser.next();
1104 }
1105 if (pageRanges != null) {
1106 PageRange[] pageRangesArray = new PageRange[pageRanges.size()];
1107 pageRanges.toArray(pageRangesArray);
1108 printJob.setPages(pageRangesArray);
1109 }
1110
1111 skipEmptyTextTags(parser);
1112 if (accept(parser, XmlPullParser.START_TAG, TAG_ATTRIBUTES)) {
1113
1114 PrintAttributes.Builder builder = new PrintAttributes.Builder();
1115
Svetoslav269403b2013-08-14 17:31:04 -07001116 String colorMode = parser.getAttributeValue(null, ATTR_COLOR_MODE);
1117 builder.setColorMode(Integer.parseInt(colorMode));
1118
Svetoslav269403b2013-08-14 17:31:04 -07001119 parser.next();
1120
1121 skipEmptyTextTags(parser);
1122 if (accept(parser, XmlPullParser.START_TAG, TAG_MEDIA_SIZE)) {
1123 String id = parser.getAttributeValue(null, ATTR_ID);
1124 label = parser.getAttributeValue(null, ATTR_LABEL);
1125 final int widthMils = Integer.parseInt(parser.getAttributeValue(null,
1126 ATTR_WIDTH_MILS));
1127 final int heightMils = Integer.parseInt(parser.getAttributeValue(null,
1128 ATTR_HEIGHT_MILS));
Svetoslav773f54d2013-09-03 14:01:43 -07001129 String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
Svetoslavb206f122013-09-20 10:43:24 -07001130 String labelResIdString = parser.getAttributeValue(null, ATTR_LABEL_RES_ID);
1131 final int labelResId = (labelResIdString != null)
1132 ? Integer.parseInt(labelResIdString) : 0;
Svetoslav773f54d2013-09-03 14:01:43 -07001133 label = parser.getAttributeValue(null, ATTR_LABEL);
1134 MediaSize mediaSize = new MediaSize(id, label, packageName, labelResId,
1135 widthMils, heightMils);
Svetoslav269403b2013-08-14 17:31:04 -07001136 builder.setMediaSize(mediaSize);
1137 parser.next();
1138 skipEmptyTextTags(parser);
1139 expect(parser, XmlPullParser.END_TAG, TAG_MEDIA_SIZE);
1140 parser.next();
1141 }
1142
1143 skipEmptyTextTags(parser);
1144 if (accept(parser, XmlPullParser.START_TAG, TAG_RESOLUTION)) {
1145 String id = parser.getAttributeValue(null, ATTR_ID);
1146 label = parser.getAttributeValue(null, ATTR_LABEL);
1147 final int horizontalDpi = Integer.parseInt(parser.getAttributeValue(null,
1148 ATTR_HORIZONTAL_DPI));
1149 final int verticalDpi = Integer.parseInt(parser.getAttributeValue(null,
1150 ATTR_VERTICAL_DPI));
Svetoslavc6066792013-09-10 21:08:32 -07001151 Resolution resolution = new Resolution(id, label, horizontalDpi, verticalDpi);
Svetoslav269403b2013-08-14 17:31:04 -07001152 builder.setResolution(resolution);
1153 parser.next();
1154 skipEmptyTextTags(parser);
1155 expect(parser, XmlPullParser.END_TAG, TAG_RESOLUTION);
1156 parser.next();
1157 }
1158
1159 skipEmptyTextTags(parser);
1160 if (accept(parser, XmlPullParser.START_TAG, TAG_MARGINS)) {
1161 final int leftMils = Integer.parseInt(parser.getAttributeValue(null,
1162 ATTR_LEFT_MILS));
1163 final int topMils = Integer.parseInt(parser.getAttributeValue(null,
1164 ATTR_TOP_MILS));
1165 final int rightMils = Integer.parseInt(parser.getAttributeValue(null,
1166 ATTR_RIGHT_MILS));
1167 final int bottomMils = Integer.parseInt(parser.getAttributeValue(null,
1168 ATTR_BOTTOM_MILS));
1169 Margins margins = new Margins(leftMils, topMils, rightMils, bottomMils);
Svetoslav651dd4e2013-09-12 14:37:47 -07001170 builder.setMinMargins(margins);
Svetoslav269403b2013-08-14 17:31:04 -07001171 parser.next();
1172 skipEmptyTextTags(parser);
1173 expect(parser, XmlPullParser.END_TAG, TAG_MARGINS);
1174 parser.next();
1175 }
1176
Svetoslav651dd4e2013-09-12 14:37:47 -07001177 printJob.setAttributes(builder.build());
Svetoslav269403b2013-08-14 17:31:04 -07001178
1179 skipEmptyTextTags(parser);
1180 expect(parser, XmlPullParser.END_TAG, TAG_ATTRIBUTES);
1181 parser.next();
1182 }
1183
1184 skipEmptyTextTags(parser);
1185 if (accept(parser, XmlPullParser.START_TAG, TAG_DOCUMENT_INFO)) {
1186 String name = parser.getAttributeValue(null, ATTR_NAME);
1187 final int pageCount = Integer.parseInt(parser.getAttributeValue(null,
1188 ATTR_PAGE_COUNT));
1189 final int contentType = Integer.parseInt(parser.getAttributeValue(null,
1190 ATTR_CONTENT_TYPE));
1191 PrintDocumentInfo info = new PrintDocumentInfo.Builder(name)
1192 .setPageCount(pageCount)
Svetoslav651dd4e2013-09-12 14:37:47 -07001193 .setContentType(contentType).build();
Svetoslav269403b2013-08-14 17:31:04 -07001194 printJob.setDocumentInfo(info);
1195 parser.next();
1196 skipEmptyTextTags(parser);
1197 expect(parser, XmlPullParser.END_TAG, TAG_DOCUMENT_INFO);
1198 parser.next();
1199 }
1200
1201 mPrintJobs.add(printJob);
1202
1203 if (DEBUG_PERSISTENCE) {
1204 Log.i(LOG_TAG, "[RESTORED] " + printJob);
1205 }
1206
1207 skipEmptyTextTags(parser);
1208 expect(parser, XmlPullParser.END_TAG, TAG_JOB);
1209
1210 return true;
1211 }
1212
1213 private void expect(XmlPullParser parser, int type, String tag)
1214 throws IOException, XmlPullParserException {
1215 if (!accept(parser, type, tag)) {
1216 throw new XmlPullParserException("Exepected event: " + type
1217 + " and tag: " + tag + " but got event: " + parser.getEventType()
1218 + " and tag:" + parser.getName());
1219 }
1220 }
1221
1222 private void skipEmptyTextTags(XmlPullParser parser)
1223 throws IOException, XmlPullParserException {
1224 while (accept(parser, XmlPullParser.TEXT, null)
1225 && "\n".equals(parser.getText())) {
1226 parser.next();
1227 }
1228 }
1229
1230 private boolean accept(XmlPullParser parser, int type, String tag)
1231 throws IOException, XmlPullParserException {
1232 if (parser.getEventType() != type) {
1233 return false;
1234 }
1235 if (tag != null) {
1236 if (!tag.equals(parser.getName())) {
1237 return false;
1238 }
1239 } else if (parser.getName() != null) {
1240 return false;
1241 }
1242 return true;
1243 }
1244 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -07001245}