1 /* 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 17 package com.android.printspooler; 18 19 import android.app.Service; 20 import android.content.ComponentName; 21 import android.content.Intent; 22 import android.os.AsyncTask; 23 import android.os.Bundle; 24 import android.os.IBinder; 25 import android.os.Message; 26 import android.os.ParcelFileDescriptor; 27 import android.os.RemoteException; 28 import android.print.IPrintSpooler; 29 import android.print.IPrintSpoolerCallbacks; 30 import android.print.IPrintSpoolerClient; 31 import android.print.PageRange; 32 import android.print.PrintAttributes; 33 import android.print.PrintAttributes.Margins; 34 import android.print.PrintAttributes.MediaSize; 35 import android.print.PrintAttributes.Resolution; 36 import android.print.PrintDocumentInfo; 37 import android.print.PrintJobId; 38 import android.print.PrintJobInfo; 39 import android.print.PrintManager; 40 import android.print.PrinterId; 41 import android.print.PrinterInfo; 42 import android.text.TextUtils; 43 import android.util.ArrayMap; 44 import android.util.AtomicFile; 45 import android.util.Log; 46 import android.util.Slog; 47 import android.util.Xml; 48 49 import com.android.internal.os.HandlerCaller; 50 import com.android.internal.util.FastXmlSerializer; 51 52 import libcore.io.IoUtils; 53 54 import org.xmlpull.v1.XmlPullParser; 55 import org.xmlpull.v1.XmlPullParserException; 56 import org.xmlpull.v1.XmlSerializer; 57 58 import java.io.File; 59 import java.io.FileDescriptor; 60 import java.io.FileInputStream; 61 import java.io.FileNotFoundException; 62 import java.io.FileOutputStream; 63 import java.io.IOException; 64 import java.io.PrintWriter; 65 import java.util.ArrayList; 66 import java.util.List; 67 68 /** 69 * Service for exposing some of the {@link PrintSpooler} functionality to 70 * another process. 71 */ 72 public final class PrintSpoolerService extends Service { 73 74 private static final String LOG_TAG = "PrintSpoolerService"; 75 76 private static final boolean DEBUG_PRINT_JOB_LIFECYCLE = false; 77 78 private static final boolean DEBUG_PERSISTENCE = false; 79 80 private static final boolean PERSISTNECE_MANAGER_ENABLED = true; 81 82 private static final long CHECK_ALL_PRINTJOBS_HANDLED_DELAY = 5000; 83 84 private static final String PRINT_JOB_FILE_PREFIX = "print_job_"; 85 86 private static final String PRINT_FILE_EXTENSION = "pdf"; 87 88 private static final Object sLock = new Object(); 89 90 private final Object mLock = new Object(); 91 92 private final List<PrintJobInfo> mPrintJobs = new ArrayList<PrintJobInfo>(); 93 94 private static PrintSpoolerService sInstance; 95 96 private IPrintSpoolerClient mClient; 97 98 private HandlerCaller mHandlerCaller; 99 100 private PersistenceManager mPersistanceManager; 101 102 private NotificationController mNotificationController; 103 104 public static PrintSpoolerService peekInstance() { 105 synchronized (sLock) { 106 return sInstance; 107 } 108 } 109 110 @Override 111 public void onCreate() { 112 super.onCreate(); 113 mHandlerCaller = new HandlerCaller(this, getMainLooper(), 114 new HandlerCallerCallback(), false); 115 116 mPersistanceManager = new PersistenceManager(); 117 mNotificationController = new NotificationController(PrintSpoolerService.this); 118 119 synchronized (mLock) { 120 mPersistanceManager.readStateLocked(); 121 handleReadPrintJobsLocked(); 122 } 123 124 synchronized (sLock) { 125 sInstance = this; 126 } 127 } 128 129 @Override 130 public IBinder onBind(Intent intent) { 131 return new PrintSpooler(); 132 } 133 134 @Override 135 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 136 synchronized (mLock) { 137 String prefix = (args.length > 0) ? args[0] : ""; 138 String tab = " "; 139 140 pw.append(prefix).append("print jobs:").println(); 141 final int printJobCount = mPrintJobs.size(); 142 for (int i = 0; i < printJobCount; i++) { 143 PrintJobInfo printJob = mPrintJobs.get(i); 144 pw.append(prefix).append(tab).append(printJob.toString()); 145 pw.println(); 146 } 147 148 pw.append(prefix).append("print job files:").println(); 149 File[] files = getFilesDir().listFiles(); 150 if (files != null) { 151 final int fileCount = files.length; 152 for (int i = 0; i < fileCount; i++) { 153 File file = files[i]; 154 if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) { 155 pw.append(prefix).append(tab).append(file.getName()).println(); 156 } 157 } 158 } 159 } 160 } 161 162 private void sendOnPrintJobQueued(PrintJobInfo printJob) { 163 Message message = mHandlerCaller.obtainMessageO( 164 HandlerCallerCallback.MSG_ON_PRINT_JOB_QUEUED, printJob); 165 mHandlerCaller.executeOrSendMessage(message); 166 } 167 168 private void sendOnAllPrintJobsForServiceHandled(ComponentName service) { 169 Message message = mHandlerCaller.obtainMessageO( 170 HandlerCallerCallback.MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED, service); 171 mHandlerCaller.executeOrSendMessage(message); 172 } 173 174 private void sendOnAllPrintJobsHandled() { 175 Message message = mHandlerCaller.obtainMessage( 176 HandlerCallerCallback.MSG_ON_ALL_PRINT_JOBS_HANDLED); 177 mHandlerCaller.executeOrSendMessage(message); 178 } 179 180 private final class HandlerCallerCallback implements HandlerCaller.Callback { 181 public static final int MSG_SET_CLIENT = 1; 182 public static final int MSG_ON_PRINT_JOB_QUEUED = 2; 183 public static final int MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED = 3; 184 public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 4; 185 public static final int MSG_CHECK_ALL_PRINTJOBS_HANDLED = 5; 186 public static final int MSG_ON_PRINT_JOB_STATE_CHANGED = 6; 187 188 @Override 189 public void executeMessage(Message message) { 190 switch (message.what) { 191 case MSG_SET_CLIENT: { 192 synchronized (mLock) { 193 mClient = (IPrintSpoolerClient) message.obj; 194 if (mClient != null) { 195 Message msg = mHandlerCaller.obtainMessage( 196 HandlerCallerCallback.MSG_CHECK_ALL_PRINTJOBS_HANDLED); 197 mHandlerCaller.sendMessageDelayed(msg, 198 CHECK_ALL_PRINTJOBS_HANDLED_DELAY); 199 } 200 } 201 } break; 202 203 case MSG_ON_PRINT_JOB_QUEUED: { 204 PrintJobInfo printJob = (PrintJobInfo) message.obj; 205 if (mClient != null) { 206 try { 207 mClient.onPrintJobQueued(printJob); 208 } catch (RemoteException re) { 209 Slog.e(LOG_TAG, "Error notify for a queued print job.", re); 210 } 211 } 212 } break; 213 214 case MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED: { 215 ComponentName service = (ComponentName) message.obj; 216 if (mClient != null) { 217 try { 218 mClient.onAllPrintJobsForServiceHandled(service); 219 } catch (RemoteException re) { 220 Slog.e(LOG_TAG, "Error notify for all print jobs per service" 221 + " handled.", re); 222 } 223 } 224 } break; 225 226 case MSG_ON_ALL_PRINT_JOBS_HANDLED: { 227 if (mClient != null) { 228 try { 229 mClient.onAllPrintJobsHandled(); 230 } catch (RemoteException re) { 231 Slog.e(LOG_TAG, "Error notify for all print job handled.", re); 232 } 233 } 234 } break; 235 236 case MSG_CHECK_ALL_PRINTJOBS_HANDLED: { 237 checkAllPrintJobsHandled(); 238 } break; 239 240 case MSG_ON_PRINT_JOB_STATE_CHANGED: { 241 if (mClient != null) { 242 PrintJobInfo printJob = (PrintJobInfo) message.obj; 243 try { 244 mClient.onPrintJobStateChanged(printJob); 245 } catch (RemoteException re) { 246 Slog.e(LOG_TAG, "Error notify for print job state change.", re); 247 } 248 } 249 } break; 250 } 251 } 252 } 253 254 public List<PrintJobInfo> getPrintJobInfos(ComponentName componentName, 255 int state, int appId) { 256 List<PrintJobInfo> foundPrintJobs = null; 257 synchronized (mLock) { 258 final int printJobCount = mPrintJobs.size(); 259 for (int i = 0; i < printJobCount; i++) { 260 PrintJobInfo printJob = mPrintJobs.get(i); 261 PrinterId printerId = printJob.getPrinterId(); 262 final boolean sameComponent = (componentName == null 263 || (printerId != null 264 && componentName.equals(printerId.getServiceName()))); 265 final boolean sameAppId = appId == PrintManager.APP_ID_ANY 266 || printJob.getAppId() == appId; 267 final boolean sameState = (state == printJob.getState()) 268 || (state == PrintJobInfo.STATE_ANY) 269 || (state == PrintJobInfo.STATE_ANY_VISIBLE_TO_CLIENTS 270 && isStateVisibleToUser(printJob.getState())) 271 || (state == PrintJobInfo.STATE_ANY_ACTIVE 272 && isActiveState(printJob.getState())) 273 || (state == PrintJobInfo.STATE_ANY_SCHEDULED 274 && isScheduledState(printJob.getState())); 275 if (sameComponent && sameAppId && sameState) { 276 if (foundPrintJobs == null) { 277 foundPrintJobs = new ArrayList<PrintJobInfo>(); 278 } 279 foundPrintJobs.add(printJob); 280 } 281 } 282 } 283 return foundPrintJobs; 284 } 285 286 private boolean isStateVisibleToUser(int state) { 287 return (isActiveState(state) && (state == PrintJobInfo.STATE_FAILED 288 || state == PrintJobInfo.STATE_COMPLETED || state == PrintJobInfo.STATE_CANCELED 289 || state == PrintJobInfo.STATE_BLOCKED)); 290 } 291 292 public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId) { 293 synchronized (mLock) { 294 final int printJobCount = mPrintJobs.size(); 295 for (int i = 0; i < printJobCount; i++) { 296 PrintJobInfo printJob = mPrintJobs.get(i); 297 if (printJob.getId().equals(printJobId) 298 && (appId == PrintManager.APP_ID_ANY 299 || appId == printJob.getAppId())) { 300 return printJob; 301 } 302 } 303 return null; 304 } 305 } 306 307 public void createPrintJob(PrintJobInfo printJob) { 308 synchronized (mLock) { 309 addPrintJobLocked(printJob); 310 setPrintJobState(printJob.getId(), PrintJobInfo.STATE_CREATED, null); 311 312 Message message = mHandlerCaller.obtainMessageO( 313 HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED, 314 printJob); 315 mHandlerCaller.executeOrSendMessage(message); 316 } 317 } 318 319 private void handleReadPrintJobsLocked() { 320 // Make a map with the files for a print job since we may have 321 // to delete some. One example of getting orphan files if the 322 // spooler crashes while constructing a print job. We do not 323 // persist partially populated print jobs under construction to 324 // avoid special handling for various attributes missing. 325 ArrayMap<PrintJobId, File> fileForJobMap = null; 326 File[] files = getFilesDir().listFiles(); 327 if (files != null) { 328 final int fileCount = files.length; 329 for (int i = 0; i < fileCount; i++) { 330 File file = files[i]; 331 if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) { 332 if (fileForJobMap == null) { 333 fileForJobMap = new ArrayMap<PrintJobId, File>(); 334 } 335 String printJobIdString = file.getName().substring( 336 PRINT_JOB_FILE_PREFIX.length(), 337 file.getName().indexOf('.')); 338 PrintJobId printJobId = PrintJobId.unflattenFromString( 339 printJobIdString); 340 fileForJobMap.put(printJobId, file); 341 } 342 } 343 } 344 345 final int printJobCount = mPrintJobs.size(); 346 for (int i = 0; i < printJobCount; i++) { 347 PrintJobInfo printJob = mPrintJobs.get(i); 348 349 // We want to have only the orphan files at the end. 350 if (fileForJobMap != null) { 351 fileForJobMap.remove(printJob.getId()); 352 } 353 354 switch (printJob.getState()) { 355 case PrintJobInfo.STATE_QUEUED: 356 case PrintJobInfo.STATE_STARTED: 357 case PrintJobInfo.STATE_BLOCKED: { 358 // We have a print job that was queued or started or blocked in 359 // the past but the device battery died or a crash occurred. In 360 // this case we assume the print job failed and let the user 361 // decide whether to restart the job or just cancel it. 362 setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED, 363 getString(R.string.no_connection_to_printer)); 364 } break; 365 } 366 } 367 368 if (!mPrintJobs.isEmpty()) { 369 // Update the notification. 370 mNotificationController.onUpdateNotifications(mPrintJobs); 371 } 372 373 // Delete the orphan files. 374 if (fileForJobMap != null) { 375 final int orphanFileCount = fileForJobMap.size(); 376 for (int i = 0; i < orphanFileCount; i++) { 377 File file = fileForJobMap.valueAt(i); 378 file.delete(); 379 } 380 } 381 } 382 383 public void checkAllPrintJobsHandled() { 384 synchronized (mLock) { 385 if (!hasActivePrintJobsLocked()) { 386 notifyOnAllPrintJobsHandled(); 387 } 388 } 389 } 390 391 public void writePrintJobData(final ParcelFileDescriptor fd, final PrintJobId printJobId) { 392 final PrintJobInfo printJob; 393 synchronized (mLock) { 394 printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY); 395 } 396 new AsyncTask<Void, Void, Void>() { 397 @Override 398 protected Void doInBackground(Void... params) { 399 FileInputStream in = null; 400 FileOutputStream out = null; 401 try { 402 if (printJob != null) { 403 File file = generateFileForPrintJob(printJobId); 404 in = new FileInputStream(file); 405 out = new FileOutputStream(fd.getFileDescriptor()); 406 } 407 final byte[] buffer = new byte[8192]; 408 while (true) { 409 final int readByteCount = in.read(buffer); 410 if (readByteCount < 0) { 411 return null; 412 } 413 out.write(buffer, 0, readByteCount); 414 } 415 } catch (FileNotFoundException fnfe) { 416 Log.e(LOG_TAG, "Error writing print job data!", fnfe); 417 } catch (IOException ioe) { 418 Log.e(LOG_TAG, "Error writing print job data!", ioe); 419 } finally { 420 IoUtils.closeQuietly(in); 421 IoUtils.closeQuietly(out); 422 IoUtils.closeQuietly(fd); 423 } 424 Log.i(LOG_TAG, "[END WRITE]"); 425 return null; 426 } 427 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); 428 } 429 430 public File generateFileForPrintJob(PrintJobId printJobId) { 431 return new File(getFilesDir(), PRINT_JOB_FILE_PREFIX 432 + printJobId.flattenToString() + "." + PRINT_FILE_EXTENSION); 433 } 434 435 private void addPrintJobLocked(PrintJobInfo printJob) { 436 mPrintJobs.add(printJob); 437 if (DEBUG_PRINT_JOB_LIFECYCLE) { 438 Slog.i(LOG_TAG, "[ADD] " + printJob); 439 } 440 } 441 442 private void removeObsoletePrintJobs() { 443 synchronized (mLock) { 444 boolean persistState = false; 445 final int printJobCount = mPrintJobs.size(); 446 for (int i = printJobCount - 1; i >= 0; i--) { 447 PrintJobInfo printJob = mPrintJobs.get(i); 448 if (isObsoleteState(printJob.getState())) { 449 mPrintJobs.remove(i); 450 if (DEBUG_PRINT_JOB_LIFECYCLE) { 451 Slog.i(LOG_TAG, "[REMOVE] " + printJob.getId().flattenToString()); 452 } 453 removePrintJobFileLocked(printJob.getId()); 454 persistState = true; 455 } 456 } 457 if (persistState) { 458 mPersistanceManager.writeStateLocked(); 459 } 460 } 461 } 462 463 private void removePrintJobFileLocked(PrintJobId printJobId) { 464 File file = generateFileForPrintJob(printJobId); 465 if (file.exists()) { 466 file.delete(); 467 if (DEBUG_PRINT_JOB_LIFECYCLE) { 468 Slog.i(LOG_TAG, "[REMOVE FILE FOR] " + printJobId); 469 } 470 } 471 } 472 473 public boolean setPrintJobState(PrintJobId printJobId, int state, String error) { 474 boolean success = false; 475 476 synchronized (mLock) { 477 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY); 478 if (printJob != null) { 479 final int oldState = printJob.getState(); 480 if (oldState == state) { 481 return false; 482 } 483 484 success = true; 485 486 printJob.setState(state); 487 printJob.setStateReason(error); 488 printJob.setCancelling(false); 489 490 if (DEBUG_PRINT_JOB_LIFECYCLE) { 491 Slog.i(LOG_TAG, "[STATE CHANGED] " + printJob); 492 } 493 494 switch (state) { 495 case PrintJobInfo.STATE_COMPLETED: 496 case PrintJobInfo.STATE_CANCELED: 497 mPrintJobs.remove(printJob); 498 removePrintJobFileLocked(printJob.getId()); 499 // $fall-through$ 500 501 case PrintJobInfo.STATE_FAILED: { 502 PrinterId printerId = printJob.getPrinterId(); 503 if (printerId != null) { 504 ComponentName service = printerId.getServiceName(); 505 if (!hasActivePrintJobsForServiceLocked(service)) { 506 sendOnAllPrintJobsForServiceHandled(service); 507 } 508 } 509 } break; 510 511 case PrintJobInfo.STATE_QUEUED: { 512 sendOnPrintJobQueued(new PrintJobInfo(printJob)); 513 } break; 514 } 515 516 if (shouldPersistPrintJob(printJob)) { 517 mPersistanceManager.writeStateLocked(); 518 } 519 520 if (!hasActivePrintJobsLocked()) { 521 notifyOnAllPrintJobsHandled(); 522 } 523 524 Message message = mHandlerCaller.obtainMessageO( 525 HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED, 526 printJob); 527 mHandlerCaller.executeOrSendMessage(message); 528 529 mNotificationController.onUpdateNotifications(mPrintJobs); 530 } 531 } 532 533 return success; 534 } 535 536 public boolean hasActivePrintJobsLocked() { 537 final int printJobCount = mPrintJobs.size(); 538 for (int i = 0; i < printJobCount; i++) { 539 PrintJobInfo printJob = mPrintJobs.get(i); 540 if (isActiveState(printJob.getState())) { 541 return true; 542 } 543 } 544 return false; 545 } 546 547 public boolean hasActivePrintJobsForServiceLocked(ComponentName service) { 548 final int printJobCount = mPrintJobs.size(); 549 for (int i = 0; i < printJobCount; i++) { 550 PrintJobInfo printJob = mPrintJobs.get(i); 551 if (isActiveState(printJob.getState()) && printJob.getPrinterId() != null 552 && printJob.getPrinterId().getServiceName().equals(service)) { 553 return true; 554 } 555 } 556 return false; 557 } 558 559 private boolean isObsoleteState(int printJobState) { 560 return (isTeminalState(printJobState) 561 || printJobState == PrintJobInfo.STATE_QUEUED); 562 } 563 564 private boolean isScheduledState(int printJobState) { 565 return printJobState == PrintJobInfo.STATE_QUEUED 566 || printJobState == PrintJobInfo.STATE_STARTED 567 || printJobState == PrintJobInfo.STATE_BLOCKED; 568 } 569 570 private boolean isActiveState(int printJobState) { 571 return printJobState == PrintJobInfo.STATE_CREATED 572 || printJobState == PrintJobInfo.STATE_QUEUED 573 || printJobState == PrintJobInfo.STATE_STARTED 574 || printJobState == PrintJobInfo.STATE_BLOCKED; 575 } 576 577 private boolean isTeminalState(int printJobState) { 578 return printJobState == PrintJobInfo.STATE_COMPLETED 579 || printJobState == PrintJobInfo.STATE_CANCELED; 580 } 581 582 public boolean setPrintJobTag(PrintJobId printJobId, String tag) { 583 synchronized (mLock) { 584 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY); 585 if (printJob != null) { 586 String printJobTag = printJob.getTag(); 587 if (printJobTag == null) { 588 if (tag == null) { 589 return false; 590 } 591 } else if (printJobTag.equals(tag)) { 592 return false; 593 } 594 printJob.setTag(tag); 595 if (shouldPersistPrintJob(printJob)) { 596 mPersistanceManager.writeStateLocked(); 597 } 598 return true; 599 } 600 } 601 return false; 602 } 603 604 public void setPrintJobCancelling(PrintJobId printJobId, boolean cancelling) { 605 synchronized (mLock) { 606 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY); 607 if (printJob != null) { 608 printJob.setCancelling(cancelling); 609 if (shouldPersistPrintJob(printJob)) { 610 mPersistanceManager.writeStateLocked(); 611 } 612 mNotificationController.onUpdateNotifications(mPrintJobs); 613 614 Message message = mHandlerCaller.obtainMessageO( 615 HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED, 616 printJob); 617 mHandlerCaller.executeOrSendMessage(message); 618 } 619 } 620 } 621 622 public void setPrintJobCopiesNoPersistence(PrintJobId printJobId, int copies) { 623 synchronized (mLock) { 624 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY); 625 if (printJob != null) { 626 printJob.setCopies(copies); 627 } 628 } 629 } 630 631 public void setPrintJobAdvancedOptionsNoPersistence(PrintJobId printJobId, 632 Bundle advancedOptions) { 633 synchronized (mLock) { 634 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY); 635 if (printJob != null) { 636 printJob.setAdvancedOptions(advancedOptions); 637 } 638 } 639 } 640 641 public void setPrintJobPrintDocumentInfoNoPersistence(PrintJobId printJobId, 642 PrintDocumentInfo info) { 643 synchronized (mLock) { 644 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY); 645 if (printJob != null) { 646 printJob.setDocumentInfo(info); 647 } 648 } 649 } 650 651 public void setPrintJobAttributesNoPersistence(PrintJobId printJobId, 652 PrintAttributes attributes) { 653 synchronized (mLock) { 654 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY); 655 if (printJob != null) { 656 printJob.setAttributes(attributes); 657 } 658 } 659 } 660 661 public void setPrintJobPrinterNoPersistence(PrintJobId printJobId, PrinterInfo printer) { 662 synchronized (mLock) { 663 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY); 664 if (printJob != null) { 665 printJob.setPrinterId(printer.getId()); 666 printJob.setPrinterName(printer.getName()); 667 } 668 } 669 } 670 671 public void setPrintJobPagesNoPersistence(PrintJobId printJobId, PageRange[] pages) { 672 synchronized (mLock) { 673 PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY); 674 if (printJob != null) { 675 printJob.setPages(pages); 676 } 677 } 678 } 679 680 private boolean shouldPersistPrintJob(PrintJobInfo printJob) { 681 return printJob.getState() >= PrintJobInfo.STATE_QUEUED; 682 } 683 684 private void notifyOnAllPrintJobsHandled() { 685 // This has to run on the tread that is persisting the current state 686 // since this call may result in the system unbinding from the spooler 687 // and as a result the spooler process may get killed before the write 688 // completes. 689 new AsyncTask<Void, Void, Void>() { 690 @Override 691 protected Void doInBackground(Void... params) { 692 sendOnAllPrintJobsHandled(); 693 return null; 694 } 695 }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null); 696 } 697 698 private final class PersistenceManager { 699 private static final String PERSIST_FILE_NAME = "print_spooler_state.xml"; 700 701 private static final String TAG_SPOOLER = "spooler"; 702 private static final String TAG_JOB = "job"; 703 704 private static final String TAG_PRINTER_ID = "printerId"; 705 private static final String TAG_PAGE_RANGE = "pageRange"; 706 private static final String TAG_ATTRIBUTES = "attributes"; 707 private static final String TAG_DOCUMENT_INFO = "documentInfo"; 708 709 private static final String ATTR_ID = "id"; 710 private static final String ATTR_LABEL = "label"; 711 private static final String ATTR_LABEL_RES_ID = "labelResId"; 712 private static final String ATTR_PACKAGE_NAME = "packageName"; 713 private static final String ATTR_STATE = "state"; 714 private static final String ATTR_APP_ID = "appId"; 715 private static final String ATTR_TAG = "tag"; 716 private static final String ATTR_CREATION_TIME = "creationTime"; 717 private static final String ATTR_COPIES = "copies"; 718 private static final String ATTR_PRINTER_NAME = "printerName"; 719 private static final String ATTR_STATE_REASON = "stateReason"; 720 private static final String ATTR_CANCELLING = "cancelling"; 721 722 private static final String TAG_ADVANCED_OPTIONS = "advancedOptions"; 723 private static final String TAG_ADVANCED_OPTION = "advancedOption"; 724 private static final String ATTR_KEY = "key"; 725 private static final String ATTR_TYPE = "type"; 726 private static final String ATTR_VALUE = "value"; 727 private static final String TYPE_STRING = "string"; 728 private static final String TYPE_INT = "int"; 729 730 private static final String TAG_MEDIA_SIZE = "mediaSize"; 731 private static final String TAG_RESOLUTION = "resolution"; 732 private static final String TAG_MARGINS = "margins"; 733 734 private static final String ATTR_COLOR_MODE = "colorMode"; 735 736 private static final String ATTR_LOCAL_ID = "localId"; 737 private static final String ATTR_SERVICE_NAME = "serviceName"; 738 739 private static final String ATTR_WIDTH_MILS = "widthMils"; 740 private static final String ATTR_HEIGHT_MILS = "heightMils"; 741 742 private static final String ATTR_HORIZONTAL_DPI = "horizontalDip"; 743 private static final String ATTR_VERTICAL_DPI = "verticalDpi"; 744 745 private static final String ATTR_LEFT_MILS = "leftMils"; 746 private static final String ATTR_TOP_MILS = "topMils"; 747 private static final String ATTR_RIGHT_MILS = "rightMils"; 748 private static final String ATTR_BOTTOM_MILS = "bottomMils"; 749 750 private static final String ATTR_START = "start"; 751 private static final String ATTR_END = "end"; 752 753 private static final String ATTR_NAME = "name"; 754 private static final String ATTR_PAGE_COUNT = "pageCount"; 755 private static final String ATTR_CONTENT_TYPE = "contentType"; 756 private static final String ATTR_DATA_SIZE = "dataSize"; 757 758 private final AtomicFile mStatePersistFile; 759 760 private boolean mWriteStateScheduled; 761 762 private PersistenceManager() { 763 mStatePersistFile = new AtomicFile(new File(getFilesDir(), 764 PERSIST_FILE_NAME)); 765 } 766 767 public void writeStateLocked() { 768 if (!PERSISTNECE_MANAGER_ENABLED) { 769 return; 770 } 771 if (mWriteStateScheduled) { 772 return; 773 } 774 mWriteStateScheduled = true; 775 new AsyncTask<Void, Void, Void>() { 776 @Override 777 protected Void doInBackground(Void... params) { 778 synchronized (mLock) { 779 mWriteStateScheduled = false; 780 doWriteStateLocked(); 781 } 782 return null; 783 } 784 }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null); 785 } 786 787 private void doWriteStateLocked() { 788 if (DEBUG_PERSISTENCE) { 789 Log.i(LOG_TAG, "[PERSIST START]"); 790 } 791 FileOutputStream out = null; 792 try { 793 out = mStatePersistFile.startWrite(); 794 795 XmlSerializer serializer = new FastXmlSerializer(); 796 serializer.setOutput(out, "utf-8"); 797 serializer.startDocument(null, true); 798 serializer.startTag(null, TAG_SPOOLER); 799 800 List<PrintJobInfo> printJobs = mPrintJobs; 801 802 final int printJobCount = printJobs.size(); 803 for (int j = 0; j < printJobCount; j++) { 804 PrintJobInfo printJob = printJobs.get(j); 805 806 if (!shouldPersistPrintJob(printJob)) { 807 continue; 808 } 809 810 serializer.startTag(null, TAG_JOB); 811 812 serializer.attribute(null, ATTR_ID, printJob.getId().flattenToString()); 813 serializer.attribute(null, ATTR_LABEL, printJob.getLabel().toString()); 814 serializer.attribute(null, ATTR_STATE, String.valueOf(printJob.getState())); 815 serializer.attribute(null, ATTR_APP_ID, String.valueOf(printJob.getAppId())); 816 String tag = printJob.getTag(); 817 if (tag != null) { 818 serializer.attribute(null, ATTR_TAG, tag); 819 } 820 serializer.attribute(null, ATTR_CREATION_TIME, String.valueOf( 821 printJob.getCreationTime())); 822 serializer.attribute(null, ATTR_COPIES, String.valueOf(printJob.getCopies())); 823 String printerName = printJob.getPrinterName(); 824 if (!TextUtils.isEmpty(printerName)) { 825 serializer.attribute(null, ATTR_PRINTER_NAME, printerName); 826 } 827 String stateReason = printJob.getStateReason(); 828 if (!TextUtils.isEmpty(stateReason)) { 829 serializer.attribute(null, ATTR_STATE_REASON, stateReason); 830 } 831 serializer.attribute(null, ATTR_CANCELLING, String.valueOf( 832 printJob.isCancelling())); 833 834 PrinterId printerId = printJob.getPrinterId(); 835 if (printerId != null) { 836 serializer.startTag(null, TAG_PRINTER_ID); 837 serializer.attribute(null, ATTR_LOCAL_ID, printerId.getLocalId()); 838 serializer.attribute(null, ATTR_SERVICE_NAME, printerId.getServiceName() 839 .flattenToString()); 840 serializer.endTag(null, TAG_PRINTER_ID); 841 } 842 843 PageRange[] pages = printJob.getPages(); 844 if (pages != null) { 845 for (int i = 0; i < pages.length; i++) { 846 serializer.startTag(null, TAG_PAGE_RANGE); 847 serializer.attribute(null, ATTR_START, String.valueOf( 848 pages[i].getStart())); 849 serializer.attribute(null, ATTR_END, String.valueOf( 850 pages[i].getEnd())); 851 serializer.endTag(null, TAG_PAGE_RANGE); 852 } 853 } 854 855 PrintAttributes attributes = printJob.getAttributes(); 856 if (attributes != null) { 857 serializer.startTag(null, TAG_ATTRIBUTES); 858 859 final int colorMode = attributes.getColorMode(); 860 serializer.attribute(null, ATTR_COLOR_MODE, 861 String.valueOf(colorMode)); 862 863 MediaSize mediaSize = attributes.getMediaSize(); 864 if (mediaSize != null) { 865 serializer.startTag(null, TAG_MEDIA_SIZE); 866 serializer.attribute(null, ATTR_ID, mediaSize.getId()); 867 serializer.attribute(null, ATTR_WIDTH_MILS, String.valueOf( 868 mediaSize.getWidthMils())); 869 serializer.attribute(null, ATTR_HEIGHT_MILS, String.valueOf( 870 mediaSize.getHeightMils())); 871 // We prefer to store only the package name and 872 // resource id and fallback to the label. 873 if (!TextUtils.isEmpty(mediaSize.mPackageName) 874 && mediaSize.mLabelResId > 0) { 875 serializer.attribute(null, ATTR_PACKAGE_NAME, 876 mediaSize.mPackageName); 877 serializer.attribute(null, ATTR_LABEL_RES_ID, 878 String.valueOf(mediaSize.mLabelResId)); 879 } else { 880 serializer.attribute(null, ATTR_LABEL, 881 mediaSize.getLabel(getPackageManager())); 882 } 883 serializer.endTag(null, TAG_MEDIA_SIZE); 884 } 885 886 Resolution resolution = attributes.getResolution(); 887 if (resolution != null) { 888 serializer.startTag(null, TAG_RESOLUTION); 889 serializer.attribute(null, ATTR_ID, resolution.getId()); 890 serializer.attribute(null, ATTR_HORIZONTAL_DPI, String.valueOf( 891 resolution.getHorizontalDpi())); 892 serializer.attribute(null, ATTR_VERTICAL_DPI, String.valueOf( 893 resolution.getVerticalDpi())); 894 serializer.attribute(null, ATTR_LABEL, 895 resolution.getLabel()); 896 serializer.endTag(null, TAG_RESOLUTION); 897 } 898 899 Margins margins = attributes.getMinMargins(); 900 if (margins != null) { 901 serializer.startTag(null, TAG_MARGINS); 902 serializer.attribute(null, ATTR_LEFT_MILS, String.valueOf( 903 margins.getLeftMils())); 904 serializer.attribute(null, ATTR_TOP_MILS, String.valueOf( 905 margins.getTopMils())); 906 serializer.attribute(null, ATTR_RIGHT_MILS, String.valueOf( 907 margins.getRightMils())); 908 serializer.attribute(null, ATTR_BOTTOM_MILS, String.valueOf( 909 margins.getBottomMils())); 910 serializer.endTag(null, TAG_MARGINS); 911 } 912 913 serializer.endTag(null, TAG_ATTRIBUTES); 914 } 915 916 PrintDocumentInfo documentInfo = printJob.getDocumentInfo(); 917 if (documentInfo != null) { 918 serializer.startTag(null, TAG_DOCUMENT_INFO); 919 serializer.attribute(null, ATTR_NAME, documentInfo.getName()); 920 serializer.attribute(null, ATTR_CONTENT_TYPE, String.valueOf( 921 documentInfo.getContentType())); 922 serializer.attribute(null, ATTR_PAGE_COUNT, String.valueOf( 923 documentInfo.getPageCount())); 924 serializer.attribute(null, ATTR_DATA_SIZE, String.valueOf( 925 documentInfo.getDataSize())); 926 serializer.endTag(null, TAG_DOCUMENT_INFO); 927 } 928 929 Bundle advancedOptions = printJob.getAdvancedOptions(); 930 if (advancedOptions != null) { 931 serializer.startTag(null, TAG_ADVANCED_OPTIONS); 932 for (String key : advancedOptions.keySet()) { 933 Object value = advancedOptions.get(key); 934 if (value instanceof String) { 935 String stringValue = (String) value; 936 serializer.startTag(null, TAG_ADVANCED_OPTION); 937 serializer.attribute(null, ATTR_KEY, key); 938 serializer.attribute(null, ATTR_TYPE, TYPE_STRING); 939 serializer.attribute(null, ATTR_VALUE, stringValue); 940 serializer.endTag(null, TAG_ADVANCED_OPTION); 941 } else if (value instanceof Integer) { 942 String intValue = Integer.toString((Integer) value); 943 serializer.startTag(null, TAG_ADVANCED_OPTION); 944 serializer.attribute(null, ATTR_KEY, key); 945 serializer.attribute(null, ATTR_TYPE, TYPE_INT); 946 serializer.attribute(null, ATTR_VALUE, intValue); 947 serializer.endTag(null, TAG_ADVANCED_OPTION); 948 } 949 } 950 serializer.endTag(null, TAG_ADVANCED_OPTIONS); 951 } 952 953 serializer.endTag(null, TAG_JOB); 954 955 if (DEBUG_PERSISTENCE) { 956 Log.i(LOG_TAG, "[PERSISTED] " + printJob); 957 } 958 } 959 960 serializer.endTag(null, TAG_SPOOLER); 961 serializer.endDocument(); 962 mStatePersistFile.finishWrite(out); 963 if (DEBUG_PERSISTENCE) { 964 Log.i(LOG_TAG, "[PERSIST END]"); 965 } 966 } catch (IOException e) { 967 Slog.w(LOG_TAG, "Failed to write state, restoring backup.", e); 968 mStatePersistFile.failWrite(out); 969 } finally { 970 IoUtils.closeQuietly(out); 971 } 972 } 973 974 public void readStateLocked() { 975 if (!PERSISTNECE_MANAGER_ENABLED) { 976 return; 977 } 978 FileInputStream in = null; 979 try { 980 in = mStatePersistFile.openRead(); 981 } catch (FileNotFoundException e) { 982 Log.i(LOG_TAG, "No existing print spooler state."); 983 return; 984 } 985 try { 986 XmlPullParser parser = Xml.newPullParser(); 987 parser.setInput(in, null); 988 parseState(parser); 989 } catch (IllegalStateException ise) { 990 Slog.w(LOG_TAG, "Failed parsing ", ise); 991 } catch (NullPointerException npe) { 992 Slog.w(LOG_TAG, "Failed parsing ", npe); 993 } catch (NumberFormatException nfe) { 994 Slog.w(LOG_TAG, "Failed parsing ", nfe); 995 } catch (XmlPullParserException xppe) { 996 Slog.w(LOG_TAG, "Failed parsing ", xppe); 997 } catch (IOException ioe) { 998 Slog.w(LOG_TAG, "Failed parsing ", ioe); 999 } catch (IndexOutOfBoundsException iobe) { 1000 Slog.w(LOG_TAG, "Failed parsing ", iobe); 1001 } finally { 1002 IoUtils.closeQuietly(in); 1003 } 1004 } 1005 1006 private void parseState(XmlPullParser parser) 1007 throws IOException, XmlPullParserException { 1008 parser.next(); 1009 skipEmptyTextTags(parser); 1010 expect(parser, XmlPullParser.START_TAG, TAG_SPOOLER); 1011 parser.next(); 1012 1013 while (parsePrintJob(parser)) { 1014 parser.next(); 1015 } 1016 1017 skipEmptyTextTags(parser); 1018 expect(parser, XmlPullParser.END_TAG, TAG_SPOOLER); 1019 } 1020 1021 private boolean parsePrintJob(XmlPullParser parser) 1022 throws IOException, XmlPullParserException { 1023 skipEmptyTextTags(parser); 1024 if (!accept(parser, XmlPullParser.START_TAG, TAG_JOB)) { 1025 return false; 1026 } 1027 1028 PrintJobInfo printJob = new PrintJobInfo(); 1029 1030 PrintJobId printJobId = PrintJobId.unflattenFromString( 1031 parser.getAttributeValue(null, ATTR_ID)); 1032 printJob.setId(printJobId); 1033 String label = parser.getAttributeValue(null, ATTR_LABEL); 1034 printJob.setLabel(label); 1035 final int state = Integer.parseInt(parser.getAttributeValue(null, ATTR_STATE)); 1036 printJob.setState(state); 1037 final int appId = Integer.parseInt(parser.getAttributeValue(null, ATTR_APP_ID)); 1038 printJob.setAppId(appId); 1039 String tag = parser.getAttributeValue(null, ATTR_TAG); 1040 printJob.setTag(tag); 1041 String creationTime = parser.getAttributeValue(null, ATTR_CREATION_TIME); 1042 printJob.setCreationTime(Long.parseLong(creationTime)); 1043 String copies = parser.getAttributeValue(null, ATTR_COPIES); 1044 printJob.setCopies(Integer.parseInt(copies)); 1045 String printerName = parser.getAttributeValue(null, ATTR_PRINTER_NAME); 1046 printJob.setPrinterName(printerName); 1047 String stateReason = parser.getAttributeValue(null, ATTR_STATE_REASON); 1048 printJob.setStateReason(stateReason); 1049 String cancelling = parser.getAttributeValue(null, ATTR_CANCELLING); 1050 printJob.setCancelling(!TextUtils.isEmpty(cancelling) 1051 ? Boolean.parseBoolean(cancelling) : false); 1052 1053 parser.next(); 1054 1055 skipEmptyTextTags(parser); 1056 if (accept(parser, XmlPullParser.START_TAG, TAG_PRINTER_ID)) { 1057 String localId = parser.getAttributeValue(null, ATTR_LOCAL_ID); 1058 ComponentName service = ComponentName.unflattenFromString(parser.getAttributeValue( 1059 null, ATTR_SERVICE_NAME)); 1060 printJob.setPrinterId(new PrinterId(service, localId)); 1061 parser.next(); 1062 skipEmptyTextTags(parser); 1063 expect(parser, XmlPullParser.END_TAG, TAG_PRINTER_ID); 1064 parser.next(); 1065 } 1066 1067 skipEmptyTextTags(parser); 1068 List<PageRange> pageRanges = null; 1069 while (accept(parser, XmlPullParser.START_TAG, TAG_PAGE_RANGE)) { 1070 final int start = Integer.parseInt(parser.getAttributeValue(null, ATTR_START)); 1071 final int end = Integer.parseInt(parser.getAttributeValue(null, ATTR_END)); 1072 PageRange pageRange = new PageRange(start, end); 1073 if (pageRanges == null) { 1074 pageRanges = new ArrayList<PageRange>(); 1075 } 1076 pageRanges.add(pageRange); 1077 parser.next(); 1078 skipEmptyTextTags(parser); 1079 expect(parser, XmlPullParser.END_TAG, TAG_PAGE_RANGE); 1080 parser.next(); 1081 skipEmptyTextTags(parser); 1082 } 1083 if (pageRanges != null) { 1084 PageRange[] pageRangesArray = new PageRange[pageRanges.size()]; 1085 pageRanges.toArray(pageRangesArray); 1086 printJob.setPages(pageRangesArray); 1087 } 1088 1089 skipEmptyTextTags(parser); 1090 if (accept(parser, XmlPullParser.START_TAG, TAG_ATTRIBUTES)) { 1091 1092 PrintAttributes.Builder builder = new PrintAttributes.Builder(); 1093 1094 String colorMode = parser.getAttributeValue(null, ATTR_COLOR_MODE); 1095 builder.setColorMode(Integer.parseInt(colorMode)); 1096 1097 parser.next(); 1098 1099 skipEmptyTextTags(parser); 1100 if (accept(parser, XmlPullParser.START_TAG, TAG_MEDIA_SIZE)) { 1101 String id = parser.getAttributeValue(null, ATTR_ID); 1102 label = parser.getAttributeValue(null, ATTR_LABEL); 1103 final int widthMils = Integer.parseInt(parser.getAttributeValue(null, 1104 ATTR_WIDTH_MILS)); 1105 final int heightMils = Integer.parseInt(parser.getAttributeValue(null, 1106 ATTR_HEIGHT_MILS)); 1107 String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME); 1108 String labelResIdString = parser.getAttributeValue(null, ATTR_LABEL_RES_ID); 1109 final int labelResId = (labelResIdString != null) 1110 ? Integer.parseInt(labelResIdString) : 0; 1111 label = parser.getAttributeValue(null, ATTR_LABEL); 1112 MediaSize mediaSize = new MediaSize(id, label, packageName, 1113 widthMils, heightMils, labelResId); 1114 builder.setMediaSize(mediaSize); 1115 parser.next(); 1116 skipEmptyTextTags(parser); 1117 expect(parser, XmlPullParser.END_TAG, TAG_MEDIA_SIZE); 1118 parser.next(); 1119 } 1120 1121 skipEmptyTextTags(parser); 1122 if (accept(parser, XmlPullParser.START_TAG, TAG_RESOLUTION)) { 1123 String id = parser.getAttributeValue(null, ATTR_ID); 1124 label = parser.getAttributeValue(null, ATTR_LABEL); 1125 final int horizontalDpi = Integer.parseInt(parser.getAttributeValue(null, 1126 ATTR_HORIZONTAL_DPI)); 1127 final int verticalDpi = Integer.parseInt(parser.getAttributeValue(null, 1128 ATTR_VERTICAL_DPI)); 1129 Resolution resolution = new Resolution(id, label, horizontalDpi, verticalDpi); 1130 builder.setResolution(resolution); 1131 parser.next(); 1132 skipEmptyTextTags(parser); 1133 expect(parser, XmlPullParser.END_TAG, TAG_RESOLUTION); 1134 parser.next(); 1135 } 1136 1137 skipEmptyTextTags(parser); 1138 if (accept(parser, XmlPullParser.START_TAG, TAG_MARGINS)) { 1139 final int leftMils = Integer.parseInt(parser.getAttributeValue(null, 1140 ATTR_LEFT_MILS)); 1141 final int topMils = Integer.parseInt(parser.getAttributeValue(null, 1142 ATTR_TOP_MILS)); 1143 final int rightMils = Integer.parseInt(parser.getAttributeValue(null, 1144 ATTR_RIGHT_MILS)); 1145 final int bottomMils = Integer.parseInt(parser.getAttributeValue(null, 1146 ATTR_BOTTOM_MILS)); 1147 Margins margins = new Margins(leftMils, topMils, rightMils, bottomMils); 1148 builder.setMinMargins(margins); 1149 parser.next(); 1150 skipEmptyTextTags(parser); 1151 expect(parser, XmlPullParser.END_TAG, TAG_MARGINS); 1152 parser.next(); 1153 } 1154 1155 printJob.setAttributes(builder.build()); 1156 1157 skipEmptyTextTags(parser); 1158 expect(parser, XmlPullParser.END_TAG, TAG_ATTRIBUTES); 1159 parser.next(); 1160 } 1161 1162 skipEmptyTextTags(parser); 1163 if (accept(parser, XmlPullParser.START_TAG, TAG_DOCUMENT_INFO)) { 1164 String name = parser.getAttributeValue(null, ATTR_NAME); 1165 final int pageCount = Integer.parseInt(parser.getAttributeValue(null, 1166 ATTR_PAGE_COUNT)); 1167 final int contentType = Integer.parseInt(parser.getAttributeValue(null, 1168 ATTR_CONTENT_TYPE)); 1169 final int dataSize = Integer.parseInt(parser.getAttributeValue(null, 1170 ATTR_DATA_SIZE)); 1171 PrintDocumentInfo info = new PrintDocumentInfo.Builder(name) 1172 .setPageCount(pageCount) 1173 .setContentType(contentType).build(); 1174 printJob.setDocumentInfo(info); 1175 info.setDataSize(dataSize); 1176 parser.next(); 1177 skipEmptyTextTags(parser); 1178 expect(parser, XmlPullParser.END_TAG, TAG_DOCUMENT_INFO); 1179 parser.next(); 1180 } 1181 1182 skipEmptyTextTags(parser); 1183 if (accept(parser, XmlPullParser.START_TAG, TAG_ADVANCED_OPTIONS)) { 1184 parser.next(); 1185 skipEmptyTextTags(parser); 1186 Bundle advancedOptions = new Bundle(); 1187 while (accept(parser, XmlPullParser.START_TAG, TAG_ADVANCED_OPTION)) { 1188 String key = parser.getAttributeValue(null, ATTR_KEY); 1189 String value = parser.getAttributeValue(null, ATTR_VALUE); 1190 String type = parser.getAttributeValue(null, ATTR_TYPE); 1191 if (TYPE_STRING.equals(type)) { 1192 advancedOptions.putString(key, value); 1193 } else if (TYPE_INT.equals(type)) { 1194 advancedOptions.putInt(key, Integer.valueOf(value)); 1195 } 1196 parser.next(); 1197 skipEmptyTextTags(parser); 1198 expect(parser, XmlPullParser.END_TAG, TAG_ADVANCED_OPTION); 1199 parser.next(); 1200 skipEmptyTextTags(parser); 1201 } 1202 printJob.setAdvancedOptions(advancedOptions); 1203 skipEmptyTextTags(parser); 1204 expect(parser, XmlPullParser.END_TAG, TAG_ADVANCED_OPTIONS); 1205 parser.next(); 1206 } 1207 1208 mPrintJobs.add(printJob); 1209 1210 if (DEBUG_PERSISTENCE) { 1211 Log.i(LOG_TAG, "[RESTORED] " + printJob); 1212 } 1213 1214 skipEmptyTextTags(parser); 1215 expect(parser, XmlPullParser.END_TAG, TAG_JOB); 1216 1217 return true; 1218 } 1219 1220 private void expect(XmlPullParser parser, int type, String tag) 1221 throws IOException, XmlPullParserException { 1222 if (!accept(parser, type, tag)) { 1223 throw new XmlPullParserException("Exepected event: " + type 1224 + " and tag: " + tag + " but got event: " + parser.getEventType() 1225 + " and tag:" + parser.getName()); 1226 } 1227 } 1228 1229 private void skipEmptyTextTags(XmlPullParser parser) 1230 throws IOException, XmlPullParserException { 1231 while (accept(parser, XmlPullParser.TEXT, null) 1232 && "\n".equals(parser.getText())) { 1233 parser.next(); 1234 } 1235 } 1236 1237 private boolean accept(XmlPullParser parser, int type, String tag) 1238 throws IOException, XmlPullParserException { 1239 if (parser.getEventType() != type) { 1240 return false; 1241 } 1242 if (tag != null) { 1243 if (!tag.equals(parser.getName())) { 1244 return false; 1245 } 1246 } else if (parser.getName() != null) { 1247 return false; 1248 } 1249 return true; 1250 } 1251 } 1252 1253 final class PrintSpooler extends IPrintSpooler.Stub { 1254 @Override 1255 public void getPrintJobInfos(IPrintSpoolerCallbacks callback, 1256 ComponentName componentName, int state, int appId, int sequence) 1257 throws RemoteException { 1258 List<PrintJobInfo> printJobs = null; 1259 try { 1260 printJobs = PrintSpoolerService.this.getPrintJobInfos( 1261 componentName, state, appId); 1262 } finally { 1263 callback.onGetPrintJobInfosResult(printJobs, sequence); 1264 } 1265 } 1266 1267 @Override 1268 public void getPrintJobInfo(PrintJobId printJobId, IPrintSpoolerCallbacks callback, 1269 int appId, int sequence) throws RemoteException { 1270 PrintJobInfo printJob = null; 1271 try { 1272 printJob = PrintSpoolerService.this.getPrintJobInfo(printJobId, appId); 1273 } finally { 1274 callback.onGetPrintJobInfoResult(printJob, sequence); 1275 } 1276 } 1277 1278 @Override 1279 public void createPrintJob(PrintJobInfo printJob) { 1280 PrintSpoolerService.this.createPrintJob(printJob); 1281 } 1282 1283 @Override 1284 public void setPrintJobState(PrintJobId printJobId, int state, String error, 1285 IPrintSpoolerCallbacks callback, int sequece) throws RemoteException { 1286 boolean success = false; 1287 try { 1288 success = PrintSpoolerService.this.setPrintJobState( 1289 printJobId, state, error); 1290 } finally { 1291 callback.onSetPrintJobStateResult(success, sequece); 1292 } 1293 } 1294 1295 @Override 1296 public void setPrintJobTag(PrintJobId printJobId, String tag, 1297 IPrintSpoolerCallbacks callback, int sequece) throws RemoteException { 1298 boolean success = false; 1299 try { 1300 success = PrintSpoolerService.this.setPrintJobTag(printJobId, tag); 1301 } finally { 1302 callback.onSetPrintJobTagResult(success, sequece); 1303 } 1304 } 1305 1306 @Override 1307 public void writePrintJobData(ParcelFileDescriptor fd, PrintJobId printJobId) { 1308 PrintSpoolerService.this.writePrintJobData(fd, printJobId); 1309 } 1310 1311 @Override 1312 public void setClient(IPrintSpoolerClient client) { 1313 Message message = mHandlerCaller.obtainMessageO( 1314 HandlerCallerCallback.MSG_SET_CLIENT, client); 1315 mHandlerCaller.executeOrSendMessage(message); 1316 } 1317 1318 @Override 1319 public void removeObsoletePrintJobs() { 1320 PrintSpoolerService.this.removeObsoletePrintJobs(); 1321 } 1322 1323 @Override 1324 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 1325 PrintSpoolerService.this.dump(fd, writer, args); 1326 } 1327 1328 @Override 1329 public void setPrintJobCancelling(PrintJobId printJobId, boolean cancelling) { 1330 PrintSpoolerService.this.setPrintJobCancelling(printJobId, cancelling); 1331 } 1332 1333 public PrintSpoolerService getService() { 1334 return PrintSpoolerService.this; 1335 } 1336 } 1337 } 1338