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