1 /* 2 * Copyright (C) 2014 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.annotation.NonNull; 20 import android.content.ContentResolver; 21 import android.content.Context; 22 import android.net.Uri; 23 import android.os.AsyncTask; 24 import android.os.Bundle; 25 import android.os.Handler; 26 import android.os.IBinder.DeathRecipient; 27 import android.os.ICancellationSignal; 28 import android.os.Looper; 29 import android.os.Message; 30 import android.os.ParcelFileDescriptor; 31 import android.os.RemoteException; 32 import android.print.ILayoutResultCallback; 33 import android.print.IPrintDocumentAdapter; 34 import android.print.IPrintDocumentAdapterObserver; 35 import android.print.IWriteResultCallback; 36 import android.print.PageRange; 37 import android.print.PrintAttributes; 38 import android.print.PrintDocumentAdapter; 39 import android.print.PrintDocumentInfo; 40 import android.util.Log; 41 42 import com.android.printspooler.R; 43 import com.android.printspooler.util.PageRangeUtils; 44 45 import libcore.io.IoUtils; 46 47 import java.io.File; 48 import java.io.FileInputStream; 49 import java.io.FileOutputStream; 50 import java.io.IOException; 51 import java.io.InputStream; 52 import java.io.OutputStream; 53 import java.lang.ref.WeakReference; 54 import java.util.Arrays; 55 56 public final class RemotePrintDocument { 57 private static final String LOG_TAG = "RemotePrintDocument"; 58 59 private static final boolean DEBUG = false; 60 61 private static final long FORCE_CANCEL_TIMEOUT = 1000; // ms 62 63 private static final int STATE_INITIAL = 0; 64 private static final int STATE_STARTED = 1; 65 private static final int STATE_UPDATING = 2; 66 private static final int STATE_UPDATED = 3; 67 private static final int STATE_FAILED = 4; 68 private static final int STATE_FINISHED = 5; 69 private static final int STATE_CANCELING = 6; 70 private static final int STATE_CANCELED = 7; 71 private static final int STATE_DESTROYED = 8; 72 73 private final Context mContext; 74 75 private final RemotePrintDocumentInfo mDocumentInfo; 76 private final UpdateSpec mUpdateSpec = new UpdateSpec(); 77 78 private final Looper mLooper; 79 private final IPrintDocumentAdapter mPrintDocumentAdapter; 80 private final RemoteAdapterDeathObserver mAdapterDeathObserver; 81 82 private final UpdateResultCallbacks mUpdateCallbacks; 83 84 private final CommandDoneCallback mCommandResultCallback = 85 new CommandDoneCallback() { 86 @Override 87 public void onDone() { 88 if (mCurrentCommand.isCompleted()) { 89 if (mCurrentCommand instanceof LayoutCommand) { 90 // If there is a next command after a layout is done, then another 91 // update was issued and the next command is another layout, so we 92 // do nothing. However, if there is no next command we may need to 93 // ask for some pages given we do not already have them or we do 94 // but the content has changed. 95 if (mNextCommand == null) { 96 if (mUpdateSpec.pages != null && (mDocumentInfo.changed 97 || (mDocumentInfo.info.getPageCount() 98 != PrintDocumentInfo.PAGE_COUNT_UNKNOWN 99 && !PageRangeUtils.contains(mDocumentInfo.writtenPages, 100 mUpdateSpec.pages, mDocumentInfo.info.getPageCount())))) { 101 mNextCommand = new WriteCommand(mContext, mLooper, 102 mPrintDocumentAdapter, mDocumentInfo, 103 mDocumentInfo.info.getPageCount(), mUpdateSpec.pages, 104 mDocumentInfo.fileProvider, mCommandResultCallback); 105 } else { 106 if (mUpdateSpec.pages != null) { 107 // If we have the requested pages, update which ones to be printed. 108 mDocumentInfo.printedPages = PageRangeUtils.computePrintedPages( 109 mUpdateSpec.pages, mDocumentInfo.writtenPages, 110 mDocumentInfo.info.getPageCount()); 111 } 112 // Notify we are done. 113 mState = STATE_UPDATED; 114 notifyUpdateCompleted(); 115 } 116 } 117 } else { 118 // We always notify after a write. 119 mState = STATE_UPDATED; 120 notifyUpdateCompleted(); 121 } 122 runPendingCommand(); 123 } else if (mCurrentCommand.isFailed()) { 124 mState = STATE_FAILED; 125 CharSequence error = mCurrentCommand.getError(); 126 mCurrentCommand = null; 127 mNextCommand = null; 128 mUpdateSpec.reset(); 129 notifyUpdateFailed(error); 130 } else if (mCurrentCommand.isCanceled()) { 131 if (mState == STATE_CANCELING) { 132 mState = STATE_CANCELED; 133 notifyUpdateCanceled(); 134 } 135 runPendingCommand(); 136 } 137 } 138 }; 139 140 private final DeathRecipient mDeathRecipient = new DeathRecipient() { 141 @Override 142 public void binderDied() { 143 onPrintingAppDied(); 144 } 145 }; 146 147 private int mState = STATE_INITIAL; 148 149 private AsyncCommand mCurrentCommand; 150 private AsyncCommand mNextCommand; 151 152 public interface RemoteAdapterDeathObserver { 153 public void onDied(); 154 } 155 156 public interface UpdateResultCallbacks { 157 public void onUpdateCompleted(RemotePrintDocumentInfo document); 158 public void onUpdateCanceled(); 159 public void onUpdateFailed(CharSequence error); 160 } 161 162 public RemotePrintDocument(Context context, IPrintDocumentAdapter adapter, 163 MutexFileProvider fileProvider, RemoteAdapterDeathObserver deathObserver, 164 UpdateResultCallbacks callbacks) { 165 mPrintDocumentAdapter = adapter; 166 mLooper = context.getMainLooper(); 167 mContext = context; 168 mAdapterDeathObserver = deathObserver; 169 mDocumentInfo = new RemotePrintDocumentInfo(); 170 mDocumentInfo.fileProvider = fileProvider; 171 mUpdateCallbacks = callbacks; 172 connectToRemoteDocument(); 173 } 174 175 public void start() { 176 if (DEBUG) { 177 Log.i(LOG_TAG, "[CALLED] start()"); 178 } 179 if (mState == STATE_FAILED) { 180 Log.w(LOG_TAG, "Failed before start."); 181 } else if (mState == STATE_DESTROYED) { 182 Log.w(LOG_TAG, "Destroyed before start."); 183 } else { 184 if (mState != STATE_INITIAL) { 185 throw new IllegalStateException("Cannot start in state:" + stateToString(mState)); 186 } 187 try { 188 mPrintDocumentAdapter.start(); 189 mState = STATE_STARTED; 190 } catch (RemoteException re) { 191 Log.e(LOG_TAG, "Error calling start()", re); 192 mState = STATE_FAILED; 193 } 194 } 195 } 196 197 public boolean update(PrintAttributes attributes, PageRange[] pages, boolean preview) { 198 boolean willUpdate; 199 200 if (DEBUG) { 201 Log.i(LOG_TAG, "[CALLED] update()"); 202 } 203 204 if (hasUpdateError()) { 205 throw new IllegalStateException("Cannot update without a clearing the failure"); 206 } 207 208 if (mState == STATE_INITIAL || mState == STATE_FINISHED || mState == STATE_DESTROYED) { 209 throw new IllegalStateException("Cannot update in state:" + stateToString(mState)); 210 } 211 212 // We schedule a layout if the constraints changed. 213 if (!mUpdateSpec.hasSameConstraints(attributes, preview)) { 214 willUpdate = true; 215 216 // If there is a current command that is running we ask for a 217 // cancellation and start over. 218 if (mCurrentCommand != null && (mCurrentCommand.isRunning() 219 || mCurrentCommand.isPending())) { 220 mCurrentCommand.cancel(false); 221 } 222 223 // Schedule a layout command. 224 PrintAttributes oldAttributes = mDocumentInfo.attributes != null 225 ? mDocumentInfo.attributes : new PrintAttributes.Builder().build(); 226 AsyncCommand command = new LayoutCommand(mLooper, mPrintDocumentAdapter, 227 mDocumentInfo, oldAttributes, attributes, preview, mCommandResultCallback); 228 scheduleCommand(command); 229 230 mState = STATE_UPDATING; 231 // If no layout in progress and we don't have all pages - schedule a write. 232 } else if ((!(mCurrentCommand instanceof LayoutCommand) 233 || (!mCurrentCommand.isPending() && !mCurrentCommand.isRunning())) 234 && pages != null && !PageRangeUtils.contains(mUpdateSpec.pages, pages, 235 mDocumentInfo.info.getPageCount())) { 236 willUpdate = true; 237 238 // Cancel the current write as a new one is to be scheduled. 239 if (mCurrentCommand instanceof WriteCommand 240 && (mCurrentCommand.isPending() || mCurrentCommand.isRunning())) { 241 mCurrentCommand.cancel(false); 242 } 243 244 // Schedule a write command. 245 AsyncCommand command = new WriteCommand(mContext, mLooper, mPrintDocumentAdapter, 246 mDocumentInfo, mDocumentInfo.info.getPageCount(), pages, 247 mDocumentInfo.fileProvider, mCommandResultCallback); 248 scheduleCommand(command); 249 250 mState = STATE_UPDATING; 251 } else { 252 willUpdate = false; 253 if (DEBUG) { 254 Log.i(LOG_TAG, "[SKIPPING] No update needed"); 255 } 256 } 257 258 // Keep track of what is requested. 259 mUpdateSpec.update(attributes, preview, pages); 260 261 runPendingCommand(); 262 263 return willUpdate; 264 } 265 266 public void finish() { 267 if (DEBUG) { 268 Log.i(LOG_TAG, "[CALLED] finish()"); 269 } 270 if (mState != STATE_STARTED && mState != STATE_UPDATED 271 && mState != STATE_FAILED && mState != STATE_CANCELING 272 && mState != STATE_CANCELED && mState != STATE_DESTROYED) { 273 throw new IllegalStateException("Cannot finish in state:" 274 + stateToString(mState)); 275 } 276 try { 277 mPrintDocumentAdapter.finish(); 278 mState = STATE_FINISHED; 279 } catch (RemoteException re) { 280 Log.e(LOG_TAG, "Error calling finish()"); 281 mState = STATE_FAILED; 282 } 283 } 284 285 public void cancel(boolean force) { 286 if (DEBUG) { 287 Log.i(LOG_TAG, "[CALLED] cancel(" + force + ")"); 288 } 289 290 mNextCommand = null; 291 292 if (mState != STATE_UPDATING) { 293 return; 294 } 295 296 mState = STATE_CANCELING; 297 298 mCurrentCommand.cancel(force); 299 } 300 301 public void destroy() { 302 if (DEBUG) { 303 Log.i(LOG_TAG, "[CALLED] destroy()"); 304 } 305 if (mState == STATE_DESTROYED) { 306 throw new IllegalStateException("Cannot destroy in state:" + stateToString(mState)); 307 } 308 309 mState = STATE_DESTROYED; 310 311 disconnectFromRemoteDocument(); 312 } 313 314 public void kill(String reason) { 315 if (DEBUG) { 316 Log.i(LOG_TAG, "[CALLED] kill()"); 317 } 318 319 try { 320 mPrintDocumentAdapter.kill(reason); 321 } catch (RemoteException re) { 322 Log.e(LOG_TAG, "Error calling kill()", re); 323 } 324 } 325 326 public boolean isUpdating() { 327 return mState == STATE_UPDATING || mState == STATE_CANCELING; 328 } 329 330 public boolean isDestroyed() { 331 return mState == STATE_DESTROYED; 332 } 333 334 public boolean hasUpdateError() { 335 return mState == STATE_FAILED; 336 } 337 338 public boolean hasLaidOutPages() { 339 return mDocumentInfo.info != null 340 && mDocumentInfo.info.getPageCount() > 0; 341 } 342 343 public void clearUpdateError() { 344 if (!hasUpdateError()) { 345 throw new IllegalStateException("No update error to clear"); 346 } 347 mState = STATE_STARTED; 348 } 349 350 public RemotePrintDocumentInfo getDocumentInfo() { 351 return mDocumentInfo; 352 } 353 354 public void writeContent(ContentResolver contentResolver, Uri uri) { 355 File file = null; 356 InputStream in = null; 357 OutputStream out = null; 358 try { 359 file = mDocumentInfo.fileProvider.acquireFile(null); 360 in = new FileInputStream(file); 361 out = contentResolver.openOutputStream(uri); 362 final byte[] buffer = new byte[8192]; 363 while (true) { 364 final int readByteCount = in.read(buffer); 365 if (readByteCount < 0) { 366 break; 367 } 368 out.write(buffer, 0, readByteCount); 369 } 370 } catch (IOException e) { 371 Log.e(LOG_TAG, "Error writing document content.", e); 372 } finally { 373 IoUtils.closeQuietly(in); 374 IoUtils.closeQuietly(out); 375 if (file != null) { 376 mDocumentInfo.fileProvider.releaseFile(); 377 } 378 } 379 } 380 381 private void notifyUpdateCanceled() { 382 if (DEBUG) { 383 Log.i(LOG_TAG, "[CALLING] onUpdateCanceled()"); 384 } 385 mUpdateCallbacks.onUpdateCanceled(); 386 } 387 388 private void notifyUpdateCompleted() { 389 if (DEBUG) { 390 Log.i(LOG_TAG, "[CALLING] onUpdateCompleted()"); 391 } 392 mUpdateCallbacks.onUpdateCompleted(mDocumentInfo); 393 } 394 395 private void notifyUpdateFailed(CharSequence error) { 396 if (DEBUG) { 397 Log.i(LOG_TAG, "[CALLING] onUpdateCompleted()"); 398 } 399 mUpdateCallbacks.onUpdateFailed(error); 400 } 401 402 private void connectToRemoteDocument() { 403 try { 404 mPrintDocumentAdapter.asBinder().linkToDeath(mDeathRecipient, 0); 405 } catch (RemoteException re) { 406 Log.w(LOG_TAG, "The printing process is dead."); 407 destroy(); 408 return; 409 } 410 411 try { 412 mPrintDocumentAdapter.setObserver(new PrintDocumentAdapterObserver(this)); 413 } catch (RemoteException re) { 414 Log.w(LOG_TAG, "Error setting observer to the print adapter."); 415 destroy(); 416 } 417 } 418 419 private void disconnectFromRemoteDocument() { 420 try { 421 mPrintDocumentAdapter.setObserver(null); 422 } catch (RemoteException re) { 423 Log.w(LOG_TAG, "Error setting observer to the print adapter."); 424 // Keep going - best effort... 425 } 426 427 mPrintDocumentAdapter.asBinder().unlinkToDeath(mDeathRecipient, 0); 428 } 429 430 private void scheduleCommand(AsyncCommand command) { 431 if (mCurrentCommand == null) { 432 mCurrentCommand = command; 433 } else { 434 mNextCommand = command; 435 } 436 } 437 438 private void runPendingCommand() { 439 if (mCurrentCommand != null 440 && (mCurrentCommand.isCompleted() 441 || mCurrentCommand.isCanceled())) { 442 mCurrentCommand = mNextCommand; 443 mNextCommand = null; 444 } 445 446 if (mCurrentCommand != null) { 447 if (mCurrentCommand.isPending()) { 448 mCurrentCommand.run(); 449 450 mState = STATE_UPDATING; 451 } 452 } else { 453 mState = STATE_UPDATED; 454 } 455 } 456 457 private static String stateToString(int state) { 458 switch (state) { 459 case STATE_FINISHED: { 460 return "STATE_FINISHED"; 461 } 462 case STATE_FAILED: { 463 return "STATE_FAILED"; 464 } 465 case STATE_STARTED: { 466 return "STATE_STARTED"; 467 } 468 case STATE_UPDATING: { 469 return "STATE_UPDATING"; 470 } 471 case STATE_UPDATED: { 472 return "STATE_UPDATED"; 473 } 474 case STATE_CANCELING: { 475 return "STATE_CANCELING"; 476 } 477 case STATE_CANCELED: { 478 return "STATE_CANCELED"; 479 } 480 case STATE_DESTROYED: { 481 return "STATE_DESTROYED"; 482 } 483 default: { 484 return "STATE_UNKNOWN"; 485 } 486 } 487 } 488 489 static final class UpdateSpec { 490 final PrintAttributes attributes = new PrintAttributes.Builder().build(); 491 boolean preview; 492 PageRange[] pages; 493 494 public void update(PrintAttributes attributes, boolean preview, 495 PageRange[] pages) { 496 this.attributes.copyFrom(attributes); 497 this.preview = preview; 498 this.pages = (pages != null) ? Arrays.copyOf(pages, pages.length) : null; 499 } 500 501 public void reset() { 502 attributes.clear(); 503 preview = false; 504 pages = null; 505 } 506 507 public boolean hasSameConstraints(PrintAttributes attributes, boolean preview) { 508 return this.attributes.equals(attributes) && this.preview == preview; 509 } 510 } 511 512 public static final class RemotePrintDocumentInfo { 513 public PrintAttributes attributes; 514 public Bundle metadata; 515 public PrintDocumentInfo info; 516 public PageRange[] printedPages; 517 public PageRange[] writtenPages; 518 public MutexFileProvider fileProvider; 519 public boolean changed; 520 public boolean updated; 521 public boolean laidout; 522 } 523 524 private interface CommandDoneCallback { 525 public void onDone(); 526 } 527 528 private static abstract class AsyncCommand implements Runnable { 529 private static final int STATE_PENDING = 0; 530 private static final int STATE_RUNNING = 1; 531 private static final int STATE_COMPLETED = 2; 532 private static final int STATE_CANCELED = 3; 533 private static final int STATE_CANCELING = 4; 534 private static final int STATE_FAILED = 5; 535 536 private static int sSequenceCounter; 537 538 protected final int mSequence = sSequenceCounter++; 539 protected final IPrintDocumentAdapter mAdapter; 540 protected final RemotePrintDocumentInfo mDocument; 541 542 protected final CommandDoneCallback mDoneCallback; 543 544 private final Handler mHandler; 545 546 protected ICancellationSignal mCancellation; 547 548 private CharSequence mError; 549 550 private int mState = STATE_PENDING; 551 552 public AsyncCommand(Looper looper, IPrintDocumentAdapter adapter, RemotePrintDocumentInfo document, 553 CommandDoneCallback doneCallback) { 554 mHandler = new AsyncCommandHandler(looper); 555 mAdapter = adapter; 556 mDocument = document; 557 mDoneCallback = doneCallback; 558 } 559 560 protected final boolean isCanceling() { 561 return mState == STATE_CANCELING; 562 } 563 564 public final boolean isCanceled() { 565 return mState == STATE_CANCELED; 566 } 567 568 /** 569 * If a force cancel is pending, remove it. This is usually called when a command returns 570 * and thereby does not need to be canceled anymore. 571 */ 572 protected void removeForceCancel() { 573 if (DEBUG) { 574 if (mHandler.hasMessages(AsyncCommandHandler.MSG_FORCE_CANCEL)) { 575 Log.i(LOG_TAG, "[FORCE CANCEL] Removed"); 576 } 577 } 578 579 mHandler.removeMessages(AsyncCommandHandler.MSG_FORCE_CANCEL); 580 } 581 582 /** 583 * Cancel the current command. 584 * 585 * @param force If set, does not wait for the {@link PrintDocumentAdapter} to cancel. This 586 * should only be used if this is the last command send to the as otherwise the 587 * {@link PrintDocumentAdapter adapter} might get commands while it is still 588 * running the old one. 589 */ 590 public final void cancel(boolean force) { 591 if (isRunning()) { 592 canceling(); 593 if (mCancellation != null) { 594 try { 595 mCancellation.cancel(); 596 } catch (RemoteException re) { 597 Log.w(LOG_TAG, "Error while canceling", re); 598 } 599 } 600 } 601 602 if (isCanceling()) { 603 if (force) { 604 if (DEBUG) { 605 Log.i(LOG_TAG, "[FORCE CANCEL] queued"); 606 } 607 mHandler.sendMessageDelayed( 608 mHandler.obtainMessage(AsyncCommandHandler.MSG_FORCE_CANCEL), 609 FORCE_CANCEL_TIMEOUT); 610 } 611 612 return; 613 } 614 615 canceled(); 616 617 // Done. 618 mDoneCallback.onDone(); 619 } 620 621 protected final void canceling() { 622 if (mState != STATE_PENDING && mState != STATE_RUNNING) { 623 throw new IllegalStateException("Command not pending or running."); 624 } 625 mState = STATE_CANCELING; 626 } 627 628 protected final void canceled() { 629 if (mState != STATE_CANCELING) { 630 throw new IllegalStateException("Not canceling."); 631 } 632 mState = STATE_CANCELED; 633 } 634 635 public final boolean isPending() { 636 return mState == STATE_PENDING; 637 } 638 639 protected final void running() { 640 if (mState != STATE_PENDING) { 641 throw new IllegalStateException("Not pending."); 642 } 643 mState = STATE_RUNNING; 644 } 645 646 public final boolean isRunning() { 647 return mState == STATE_RUNNING; 648 } 649 650 protected final void completed() { 651 if (mState != STATE_RUNNING && mState != STATE_CANCELING) { 652 throw new IllegalStateException("Not running."); 653 } 654 mState = STATE_COMPLETED; 655 } 656 657 public final boolean isCompleted() { 658 return mState == STATE_COMPLETED; 659 } 660 661 protected final void failed(CharSequence error) { 662 if (mState != STATE_RUNNING && mState != STATE_CANCELING) { 663 throw new IllegalStateException("Not running."); 664 } 665 mState = STATE_FAILED; 666 667 mError = error; 668 } 669 670 public final boolean isFailed() { 671 return mState == STATE_FAILED; 672 } 673 674 public CharSequence getError() { 675 return mError; 676 } 677 678 /** 679 * Handler for the async command. 680 */ 681 private class AsyncCommandHandler extends Handler { 682 /** Message indicated the desire for to force cancel a command */ 683 final static int MSG_FORCE_CANCEL = 0; 684 685 AsyncCommandHandler(@NonNull Looper looper) { 686 super(looper); 687 } 688 689 @Override 690 public void handleMessage(Message msg) { 691 switch (msg.what) { 692 case MSG_FORCE_CANCEL: 693 if (isCanceling()) { 694 if (DEBUG) { 695 Log.i(LOG_TAG, "[FORCE CANCEL] executed"); 696 } 697 failed("Command did not respond to cancellation in " 698 + FORCE_CANCEL_TIMEOUT + " ms"); 699 700 mDoneCallback.onDone(); 701 } 702 break; 703 default: 704 // not reached; 705 } 706 } 707 } 708 } 709 710 private static final class LayoutCommand extends AsyncCommand { 711 private final PrintAttributes mOldAttributes = new PrintAttributes.Builder().build(); 712 private final PrintAttributes mNewAttributes = new PrintAttributes.Builder().build(); 713 private final Bundle mMetadata = new Bundle(); 714 715 private final ILayoutResultCallback mRemoteResultCallback; 716 717 private final Handler mHandler; 718 719 public LayoutCommand(Looper looper, IPrintDocumentAdapter adapter, 720 RemotePrintDocumentInfo document, PrintAttributes oldAttributes, 721 PrintAttributes newAttributes, boolean preview, CommandDoneCallback callback) { 722 super(looper, adapter, document, callback); 723 mHandler = new LayoutHandler(looper); 724 mRemoteResultCallback = new LayoutResultCallback(mHandler); 725 mOldAttributes.copyFrom(oldAttributes); 726 mNewAttributes.copyFrom(newAttributes); 727 mMetadata.putBoolean(PrintDocumentAdapter.EXTRA_PRINT_PREVIEW, preview); 728 } 729 730 @Override 731 public void run() { 732 running(); 733 734 try { 735 if (DEBUG) { 736 Log.i(LOG_TAG, "[PERFORMING] layout"); 737 } 738 mDocument.changed = false; 739 mAdapter.layout(mOldAttributes, mNewAttributes, mRemoteResultCallback, 740 mMetadata, mSequence); 741 } catch (RemoteException re) { 742 Log.e(LOG_TAG, "Error calling layout", re); 743 handleOnLayoutFailed(null, mSequence); 744 } 745 } 746 747 private void handleOnLayoutStarted(ICancellationSignal cancellation, int sequence) { 748 if (sequence != mSequence) { 749 return; 750 } 751 752 if (DEBUG) { 753 Log.i(LOG_TAG, "[CALLBACK] onLayoutStarted"); 754 } 755 756 if (isCanceling()) { 757 try { 758 cancellation.cancel(); 759 } catch (RemoteException re) { 760 Log.e(LOG_TAG, "Error cancelling", re); 761 handleOnLayoutFailed(null, mSequence); 762 } 763 } else { 764 mCancellation = cancellation; 765 } 766 } 767 768 private void handleOnLayoutFinished(PrintDocumentInfo info, 769 boolean changed, int sequence) { 770 if (sequence != mSequence) { 771 return; 772 } 773 774 if (DEBUG) { 775 Log.i(LOG_TAG, "[CALLBACK] onLayoutFinished"); 776 } 777 778 completed(); 779 780 // If the document description changed or the content in the 781 // document changed, the we need to invalidate the pages. 782 if (changed || !equalsIgnoreSize(mDocument.info, info)) { 783 // If the content changed we throw away all pages as 784 // we will request them again with the new content. 785 mDocument.writtenPages = null; 786 mDocument.printedPages = null; 787 mDocument.changed = true; 788 } 789 790 // Update the document with data from the layout pass. 791 mDocument.attributes = mNewAttributes; 792 mDocument.metadata = mMetadata; 793 mDocument.laidout = true; 794 mDocument.info = info; 795 796 // Release the remote cancellation interface. 797 mCancellation = null; 798 799 // Done. 800 mDoneCallback.onDone(); 801 } 802 803 private void handleOnLayoutFailed(CharSequence error, int sequence) { 804 if (sequence != mSequence) { 805 return; 806 } 807 808 if (DEBUG) { 809 Log.i(LOG_TAG, "[CALLBACK] onLayoutFailed"); 810 } 811 812 mDocument.laidout = false; 813 814 failed(error); 815 816 // Release the remote cancellation interface. 817 mCancellation = null; 818 819 // Failed. 820 mDoneCallback.onDone(); 821 } 822 823 private void handleOnLayoutCanceled(int sequence) { 824 if (sequence != mSequence) { 825 return; 826 } 827 828 if (DEBUG) { 829 Log.i(LOG_TAG, "[CALLBACK] onLayoutCanceled"); 830 } 831 832 canceled(); 833 834 // Release the remote cancellation interface. 835 mCancellation = null; 836 837 // Done. 838 mDoneCallback.onDone(); 839 } 840 841 private boolean equalsIgnoreSize(PrintDocumentInfo lhs, PrintDocumentInfo rhs) { 842 if (lhs == rhs) { 843 return true; 844 } 845 if (lhs == null) { 846 return false; 847 } else { 848 if (rhs == null) { 849 return false; 850 } 851 if (lhs.getContentType() != rhs.getContentType() 852 || lhs.getPageCount() != rhs.getPageCount()) { 853 return false; 854 } 855 } 856 return true; 857 } 858 859 private final class LayoutHandler extends Handler { 860 public static final int MSG_ON_LAYOUT_STARTED = 1; 861 public static final int MSG_ON_LAYOUT_FINISHED = 2; 862 public static final int MSG_ON_LAYOUT_FAILED = 3; 863 public static final int MSG_ON_LAYOUT_CANCELED = 4; 864 865 public LayoutHandler(Looper looper) { 866 super(looper, null, false); 867 } 868 869 @Override 870 public void handleMessage(Message message) { 871 // The command might have been force canceled, see 872 // AsyncCommand.AsyncCommandHandler#handleMessage 873 if (isFailed()) { 874 if (DEBUG) { 875 Log.i(LOG_TAG, "[CALLBACK] on failed layout command"); 876 } 877 878 return; 879 } 880 881 int sequence; 882 int what = message.what; 883 switch (what) { 884 case MSG_ON_LAYOUT_FINISHED: 885 removeForceCancel(); 886 sequence = message.arg2; 887 break; 888 case MSG_ON_LAYOUT_FAILED: 889 case MSG_ON_LAYOUT_CANCELED: 890 removeForceCancel(); 891 // $FALL-THROUGH - message uses the same format as "started" 892 case MSG_ON_LAYOUT_STARTED: 893 // Don't remote force-cancel as command is still running and might need to 894 // be canceled later 895 sequence = message.arg1; 896 break; 897 default: 898 // not reached 899 sequence = -1; 900 } 901 902 // If we are canceling any result is treated as a cancel 903 if (isCanceling() && what != MSG_ON_LAYOUT_STARTED) { 904 what = MSG_ON_LAYOUT_CANCELED; 905 } 906 907 switch (what) { 908 case MSG_ON_LAYOUT_STARTED: { 909 ICancellationSignal cancellation = (ICancellationSignal) message.obj; 910 handleOnLayoutStarted(cancellation, sequence); 911 } break; 912 913 case MSG_ON_LAYOUT_FINISHED: { 914 PrintDocumentInfo info = (PrintDocumentInfo) message.obj; 915 final boolean changed = (message.arg1 == 1); 916 handleOnLayoutFinished(info, changed, sequence); 917 } break; 918 919 case MSG_ON_LAYOUT_FAILED: { 920 CharSequence error = (CharSequence) message.obj; 921 handleOnLayoutFailed(error, sequence); 922 } break; 923 924 case MSG_ON_LAYOUT_CANCELED: { 925 handleOnLayoutCanceled(sequence); 926 } break; 927 } 928 } 929 } 930 931 private static final class LayoutResultCallback extends ILayoutResultCallback.Stub { 932 private final WeakReference<Handler> mWeakHandler; 933 934 public LayoutResultCallback(Handler handler) { 935 mWeakHandler = new WeakReference<>(handler); 936 } 937 938 @Override 939 public void onLayoutStarted(ICancellationSignal cancellation, int sequence) { 940 Handler handler = mWeakHandler.get(); 941 if (handler != null) { 942 handler.obtainMessage(LayoutHandler.MSG_ON_LAYOUT_STARTED, 943 sequence, 0, cancellation).sendToTarget(); 944 } 945 } 946 947 @Override 948 public void onLayoutFinished(PrintDocumentInfo info, boolean changed, int sequence) { 949 Handler handler = mWeakHandler.get(); 950 if (handler != null) { 951 handler.obtainMessage(LayoutHandler.MSG_ON_LAYOUT_FINISHED, 952 changed ? 1 : 0, sequence, info).sendToTarget(); 953 } 954 } 955 956 @Override 957 public void onLayoutFailed(CharSequence error, int sequence) { 958 Handler handler = mWeakHandler.get(); 959 if (handler != null) { 960 handler.obtainMessage(LayoutHandler.MSG_ON_LAYOUT_FAILED, 961 sequence, 0, error).sendToTarget(); 962 } 963 } 964 965 @Override 966 public void onLayoutCanceled(int sequence) { 967 Handler handler = mWeakHandler.get(); 968 if (handler != null) { 969 handler.obtainMessage(LayoutHandler.MSG_ON_LAYOUT_CANCELED, 970 sequence, 0).sendToTarget(); 971 } 972 } 973 } 974 } 975 976 private static final class WriteCommand extends AsyncCommand { 977 private final int mPageCount; 978 private final PageRange[] mPages; 979 private final MutexFileProvider mFileProvider; 980 981 private final IWriteResultCallback mRemoteResultCallback; 982 private final CommandDoneCallback mWriteDoneCallback; 983 984 private final Context mContext; 985 private final Handler mHandler; 986 987 public WriteCommand(Context context, Looper looper, IPrintDocumentAdapter adapter, 988 RemotePrintDocumentInfo document, int pageCount, PageRange[] pages, 989 MutexFileProvider fileProvider, CommandDoneCallback callback) { 990 super(looper, adapter, document, callback); 991 mContext = context; 992 mHandler = new WriteHandler(looper); 993 mRemoteResultCallback = new WriteResultCallback(mHandler); 994 mPageCount = pageCount; 995 mPages = Arrays.copyOf(pages, pages.length); 996 mFileProvider = fileProvider; 997 mWriteDoneCallback = callback; 998 } 999 1000 @Override 1001 public void run() { 1002 running(); 1003 1004 // This is a long running operation as we will be reading fully 1005 // the written data. In case of a cancellation, we ask the client 1006 // to stop writing data and close the file descriptor after 1007 // which we will reach the end of the stream, thus stop reading. 1008 new AsyncTask<Void, Void, Void>() { 1009 @Override 1010 protected Void doInBackground(Void... params) { 1011 File file = null; 1012 InputStream in = null; 1013 OutputStream out = null; 1014 ParcelFileDescriptor source = null; 1015 ParcelFileDescriptor sink = null; 1016 try { 1017 file = mFileProvider.acquireFile(null); 1018 ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); 1019 source = pipe[0]; 1020 sink = pipe[1]; 1021 1022 in = new FileInputStream(source.getFileDescriptor()); 1023 out = new FileOutputStream(file); 1024 1025 // Async call to initiate the other process writing the data. 1026 if (DEBUG) { 1027 Log.i(LOG_TAG, "[PERFORMING] write"); 1028 } 1029 mAdapter.write(mPages, sink, mRemoteResultCallback, mSequence); 1030 1031 // Close the source. It is now held by the client. 1032 sink.close(); 1033 sink = null; 1034 1035 // Read the data. 1036 final byte[] buffer = new byte[8192]; 1037 while (true) { 1038 final int readByteCount = in.read(buffer); 1039 if (readByteCount < 0) { 1040 break; 1041 } 1042 out.write(buffer, 0, readByteCount); 1043 } 1044 } catch (RemoteException | IOException e) { 1045 Log.e(LOG_TAG, "Error calling write()", e); 1046 } finally { 1047 IoUtils.closeQuietly(in); 1048 IoUtils.closeQuietly(out); 1049 IoUtils.closeQuietly(sink); 1050 IoUtils.closeQuietly(source); 1051 if (file != null) { 1052 mFileProvider.releaseFile(); 1053 } 1054 } 1055 return null; 1056 } 1057 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); 1058 } 1059 1060 private void handleOnWriteStarted(ICancellationSignal cancellation, int sequence) { 1061 if (sequence != mSequence) { 1062 return; 1063 } 1064 1065 if (DEBUG) { 1066 Log.i(LOG_TAG, "[CALLBACK] onWriteStarted"); 1067 } 1068 1069 if (isCanceling()) { 1070 try { 1071 cancellation.cancel(); 1072 } catch (RemoteException re) { 1073 Log.e(LOG_TAG, "Error cancelling", re); 1074 handleOnWriteFailed(null, sequence); 1075 } 1076 } else { 1077 mCancellation = cancellation; 1078 } 1079 } 1080 1081 private void handleOnWriteFinished(PageRange[] pages, int sequence) { 1082 if (sequence != mSequence) { 1083 return; 1084 } 1085 1086 if (DEBUG) { 1087 Log.i(LOG_TAG, "[CALLBACK] onWriteFinished"); 1088 } 1089 1090 PageRange[] writtenPages = PageRangeUtils.normalize(pages); 1091 PageRange[] printedPages = PageRangeUtils.computePrintedPages( 1092 mPages, writtenPages, mPageCount); 1093 1094 // Handle if we got invalid pages 1095 if (printedPages != null) { 1096 mDocument.writtenPages = writtenPages; 1097 mDocument.printedPages = printedPages; 1098 completed(); 1099 } else { 1100 mDocument.writtenPages = null; 1101 mDocument.printedPages = null; 1102 failed(mContext.getString(R.string.print_error_default_message)); 1103 } 1104 1105 // Release the remote cancellation interface. 1106 mCancellation = null; 1107 1108 // Done. 1109 mWriteDoneCallback.onDone(); 1110 } 1111 1112 private void handleOnWriteFailed(CharSequence error, int sequence) { 1113 if (sequence != mSequence) { 1114 return; 1115 } 1116 1117 if (DEBUG) { 1118 Log.i(LOG_TAG, "[CALLBACK] onWriteFailed"); 1119 } 1120 1121 failed(error); 1122 1123 // Release the remote cancellation interface. 1124 mCancellation = null; 1125 1126 // Done. 1127 mWriteDoneCallback.onDone(); 1128 } 1129 1130 private void handleOnWriteCanceled(int sequence) { 1131 if (sequence != mSequence) { 1132 return; 1133 } 1134 1135 if (DEBUG) { 1136 Log.i(LOG_TAG, "[CALLBACK] onWriteCanceled"); 1137 } 1138 1139 canceled(); 1140 1141 // Release the remote cancellation interface. 1142 mCancellation = null; 1143 1144 // Done. 1145 mWriteDoneCallback.onDone(); 1146 } 1147 1148 private final class WriteHandler extends Handler { 1149 public static final int MSG_ON_WRITE_STARTED = 1; 1150 public static final int MSG_ON_WRITE_FINISHED = 2; 1151 public static final int MSG_ON_WRITE_FAILED = 3; 1152 public static final int MSG_ON_WRITE_CANCELED = 4; 1153 1154 public WriteHandler(Looper looper) { 1155 super(looper, null, false); 1156 } 1157 1158 @Override 1159 public void handleMessage(Message message) { 1160 // The command might have been force canceled, see 1161 // AsyncCommand.AsyncCommandHandler#handleMessage 1162 if (isFailed()) { 1163 if (DEBUG) { 1164 Log.i(LOG_TAG, "[CALLBACK] on failed write command"); 1165 } 1166 1167 return; 1168 } 1169 1170 int what = message.what; 1171 switch (what) { 1172 case MSG_ON_WRITE_FINISHED: 1173 case MSG_ON_WRITE_FAILED: 1174 case MSG_ON_WRITE_CANCELED: 1175 removeForceCancel(); 1176 case MSG_ON_WRITE_STARTED: 1177 // Don't remote force-cancel as command is still running and might need to 1178 // be canceled later 1179 break; 1180 } 1181 1182 // If we are canceling any result is treated as a cancel 1183 if (isCanceling() && what != MSG_ON_WRITE_STARTED) { 1184 what = MSG_ON_WRITE_CANCELED; 1185 } 1186 1187 switch (what) { 1188 case MSG_ON_WRITE_STARTED: { 1189 ICancellationSignal cancellation = (ICancellationSignal) message.obj; 1190 final int sequence = message.arg1; 1191 handleOnWriteStarted(cancellation, sequence); 1192 } break; 1193 1194 case MSG_ON_WRITE_FINISHED: { 1195 PageRange[] pages = (PageRange[]) message.obj; 1196 final int sequence = message.arg1; 1197 handleOnWriteFinished(pages, sequence); 1198 } break; 1199 1200 case MSG_ON_WRITE_FAILED: { 1201 CharSequence error = (CharSequence) message.obj; 1202 final int sequence = message.arg1; 1203 handleOnWriteFailed(error, sequence); 1204 } break; 1205 1206 case MSG_ON_WRITE_CANCELED: { 1207 final int sequence = message.arg1; 1208 handleOnWriteCanceled(sequence); 1209 } break; 1210 } 1211 } 1212 } 1213 1214 private static final class WriteResultCallback extends IWriteResultCallback.Stub { 1215 private final WeakReference<Handler> mWeakHandler; 1216 1217 public WriteResultCallback(Handler handler) { 1218 mWeakHandler = new WeakReference<>(handler); 1219 } 1220 1221 @Override 1222 public void onWriteStarted(ICancellationSignal cancellation, int sequence) { 1223 Handler handler = mWeakHandler.get(); 1224 if (handler != null) { 1225 handler.obtainMessage(WriteHandler.MSG_ON_WRITE_STARTED, 1226 sequence, 0, cancellation).sendToTarget(); 1227 } 1228 } 1229 1230 @Override 1231 public void onWriteFinished(PageRange[] pages, int sequence) { 1232 Handler handler = mWeakHandler.get(); 1233 if (handler != null) { 1234 handler.obtainMessage(WriteHandler.MSG_ON_WRITE_FINISHED, 1235 sequence, 0, pages).sendToTarget(); 1236 } 1237 } 1238 1239 @Override 1240 public void onWriteFailed(CharSequence error, int sequence) { 1241 Handler handler = mWeakHandler.get(); 1242 if (handler != null) { 1243 handler.obtainMessage(WriteHandler.MSG_ON_WRITE_FAILED, 1244 sequence, 0, error).sendToTarget(); 1245 } 1246 } 1247 1248 @Override 1249 public void onWriteCanceled(int sequence) { 1250 Handler handler = mWeakHandler.get(); 1251 if (handler != null) { 1252 handler.obtainMessage(WriteHandler.MSG_ON_WRITE_CANCELED, 1253 sequence, 0).sendToTarget(); 1254 } 1255 } 1256 } 1257 } 1258 1259 private void onPrintingAppDied() { 1260 mState = STATE_FAILED; 1261 new Handler(mLooper).post(new Runnable() { 1262 @Override 1263 public void run() { 1264 mAdapterDeathObserver.onDied(); 1265 } 1266 }); 1267 } 1268 1269 private static final class PrintDocumentAdapterObserver 1270 extends IPrintDocumentAdapterObserver.Stub { 1271 private final WeakReference<RemotePrintDocument> mWeakDocument; 1272 1273 public PrintDocumentAdapterObserver(RemotePrintDocument document) { 1274 mWeakDocument = new WeakReference<>(document); 1275 } 1276 1277 @Override 1278 public void onDestroy() { 1279 final RemotePrintDocument document = mWeakDocument.get(); 1280 if (document != null) { 1281 document.onPrintingAppDied(); 1282 } 1283 } 1284 } 1285 } 1286