13bf66744d61d18c66d46f2608de0467ad3df0268Mopria/* 23bf66744d61d18c66d46f2608de0467ad3df0268Mopria * Copyright (C) 2016 The Android Open Source Project 33bf66744d61d18c66d46f2608de0467ad3df0268Mopria * Copyright (C) 2016 Mopria Alliance, Inc. 43bf66744d61d18c66d46f2608de0467ad3df0268Mopria * 53bf66744d61d18c66d46f2608de0467ad3df0268Mopria * Licensed under the Apache License, Version 2.0 (the "License"); 63bf66744d61d18c66d46f2608de0467ad3df0268Mopria * you may not use this file except in compliance with the License. 73bf66744d61d18c66d46f2608de0467ad3df0268Mopria * You may obtain a copy of the License at 83bf66744d61d18c66d46f2608de0467ad3df0268Mopria * 93bf66744d61d18c66d46f2608de0467ad3df0268Mopria * http://www.apache.org/licenses/LICENSE-2.0 103bf66744d61d18c66d46f2608de0467ad3df0268Mopria * 113bf66744d61d18c66d46f2608de0467ad3df0268Mopria * Unless required by applicable law or agreed to in writing, software 123bf66744d61d18c66d46f2608de0467ad3df0268Mopria * distributed under the License is distributed on an "AS IS" BASIS, 133bf66744d61d18c66d46f2608de0467ad3df0268Mopria * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 143bf66744d61d18c66d46f2608de0467ad3df0268Mopria * See the License for the specific language governing permissions and 153bf66744d61d18c66d46f2608de0467ad3df0268Mopria * limitations under the License. 163bf66744d61d18c66d46f2608de0467ad3df0268Mopria */ 173bf66744d61d18c66d46f2608de0467ad3df0268Mopria 183bf66744d61d18c66d46f2608de0467ad3df0268Mopriapackage com.android.bips; 193bf66744d61d18c66d46f2608de0467ad3df0268Mopria 203bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport android.net.Uri; 213bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport android.os.Handler; 223bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport android.print.PrintJobId; 233bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport android.printservice.PrintJob; 243bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport android.util.Log; 253bf66744d61d18c66d46f2608de0467ad3df0268Mopria 263bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport com.android.bips.discovery.DiscoveredPrinter; 273bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport com.android.bips.discovery.MdnsDiscovery; 283bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport com.android.bips.ipp.Backend; 293bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport com.android.bips.ipp.JobStatus; 303bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport com.android.bips.jni.BackendConstants; 313bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport com.android.bips.jni.LocalPrinterCapabilities; 323bf66744d61d18c66d46f2608de0467ad3df0268Mopria 333bf66744d61d18c66d46f2608de0467ad3df0268Mopriaimport java.util.function.Consumer; 343bf66744d61d18c66d46f2608de0467ad3df0268Mopria 353bf66744d61d18c66d46f2608de0467ad3df0268Mopria/** 363bf66744d61d18c66d46f2608de0467ad3df0268Mopria * Manage the process of delivering a print job 373bf66744d61d18c66d46f2608de0467ad3df0268Mopria */ 383bf66744d61d18c66d46f2608de0467ad3df0268Mopriaclass LocalPrintJob implements MdnsDiscovery.Listener { 393bf66744d61d18c66d46f2608de0467ad3df0268Mopria private static final String TAG = LocalPrintJob.class.getSimpleName(); 403bf66744d61d18c66d46f2608de0467ad3df0268Mopria private static final boolean DEBUG = false; 413bf66744d61d18c66d46f2608de0467ad3df0268Mopria 423bf66744d61d18c66d46f2608de0467ad3df0268Mopria /** Maximum time to wait to find a printer before failing the job */ 433bf66744d61d18c66d46f2608de0467ad3df0268Mopria private static final int DISCOVERY_TIMEOUT = 30 * 1000; 443bf66744d61d18c66d46f2608de0467ad3df0268Mopria 453bf66744d61d18c66d46f2608de0467ad3df0268Mopria // Internal job states 463bf66744d61d18c66d46f2608de0467ad3df0268Mopria private static final int STATE_INIT = 0; 473bf66744d61d18c66d46f2608de0467ad3df0268Mopria private static final int STATE_DISCOVERY = 1; 483bf66744d61d18c66d46f2608de0467ad3df0268Mopria private static final int STATE_DELIVERING = 2; 493bf66744d61d18c66d46f2608de0467ad3df0268Mopria private static final int STATE_CANCEL = 3; 503bf66744d61d18c66d46f2608de0467ad3df0268Mopria private static final int STATE_DONE = 4; 513bf66744d61d18c66d46f2608de0467ad3df0268Mopria 523bf66744d61d18c66d46f2608de0467ad3df0268Mopria private final BuiltInPrintService mPrintService; 533bf66744d61d18c66d46f2608de0467ad3df0268Mopria private final PrintJob mPrintJob; 543bf66744d61d18c66d46f2608de0467ad3df0268Mopria private final Backend mBackend; 553bf66744d61d18c66d46f2608de0467ad3df0268Mopria private final Handler mMainHandler; 563bf66744d61d18c66d46f2608de0467ad3df0268Mopria 573bf66744d61d18c66d46f2608de0467ad3df0268Mopria private int mState; 583bf66744d61d18c66d46f2608de0467ad3df0268Mopria private Consumer<LocalPrintJob> mCompleteConsumer; 593bf66744d61d18c66d46f2608de0467ad3df0268Mopria private Uri mPath; 603bf66744d61d18c66d46f2608de0467ad3df0268Mopria 613bf66744d61d18c66d46f2608de0467ad3df0268Mopria /** 623bf66744d61d18c66d46f2608de0467ad3df0268Mopria * Construct the object; use {@link #start(Consumer)} to begin job processing. 633bf66744d61d18c66d46f2608de0467ad3df0268Mopria */ 643bf66744d61d18c66d46f2608de0467ad3df0268Mopria LocalPrintJob(BuiltInPrintService printService, Backend backend, PrintJob printJob) { 653bf66744d61d18c66d46f2608de0467ad3df0268Mopria mPrintService = printService; 663bf66744d61d18c66d46f2608de0467ad3df0268Mopria mBackend = backend; 673bf66744d61d18c66d46f2608de0467ad3df0268Mopria mPrintJob = printJob; 683bf66744d61d18c66d46f2608de0467ad3df0268Mopria mMainHandler = new Handler(printService.getMainLooper()); 693bf66744d61d18c66d46f2608de0467ad3df0268Mopria mState = STATE_INIT; 703bf66744d61d18c66d46f2608de0467ad3df0268Mopria 713bf66744d61d18c66d46f2608de0467ad3df0268Mopria // Tell the job it is blocked (until start()) 723bf66744d61d18c66d46f2608de0467ad3df0268Mopria mPrintJob.start(); 733bf66744d61d18c66d46f2608de0467ad3df0268Mopria mPrintJob.block(printService.getString(R.string.waiting_to_send)); 743bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 753bf66744d61d18c66d46f2608de0467ad3df0268Mopria 763bf66744d61d18c66d46f2608de0467ad3df0268Mopria /** 773bf66744d61d18c66d46f2608de0467ad3df0268Mopria * Begin the process of delivering the job. Internally, discovers the target printer, 783bf66744d61d18c66d46f2608de0467ad3df0268Mopria * obtains its capabilities, delivers the job to the printer, and waits for job completion. 793bf66744d61d18c66d46f2608de0467ad3df0268Mopria * 803bf66744d61d18c66d46f2608de0467ad3df0268Mopria * @param callback Callback to be issued when job processing is complete 813bf66744d61d18c66d46f2608de0467ad3df0268Mopria */ 823bf66744d61d18c66d46f2608de0467ad3df0268Mopria void start(Consumer<LocalPrintJob> callback) { 833bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (DEBUG) Log.d(TAG, "start() " + mPrintJob); 843bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (mState != STATE_INIT) { 853bf66744d61d18c66d46f2608de0467ad3df0268Mopria Log.w(TAG, "Invalid start state " + mState); 863bf66744d61d18c66d46f2608de0467ad3df0268Mopria return; 873bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 883bf66744d61d18c66d46f2608de0467ad3df0268Mopria mPrintJob.start(); 893bf66744d61d18c66d46f2608de0467ad3df0268Mopria 903bf66744d61d18c66d46f2608de0467ad3df0268Mopria // Acquire a lock so that WiFi isn't put to sleep while we send the job 913bf66744d61d18c66d46f2608de0467ad3df0268Mopria mPrintService.lockWifi(); 923bf66744d61d18c66d46f2608de0467ad3df0268Mopria 933bf66744d61d18c66d46f2608de0467ad3df0268Mopria mState = STATE_DISCOVERY; 943bf66744d61d18c66d46f2608de0467ad3df0268Mopria mCompleteConsumer = callback; 953bf66744d61d18c66d46f2608de0467ad3df0268Mopria mPrintService.getDiscovery().start(this); 963bf66744d61d18c66d46f2608de0467ad3df0268Mopria 973bf66744d61d18c66d46f2608de0467ad3df0268Mopria mMainHandler.postDelayed(() -> { 983bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (DEBUG) Log.d(TAG, "Discovery timeout"); 993bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (mState == STATE_DISCOVERY) { 1003bf66744d61d18c66d46f2608de0467ad3df0268Mopria mPrintService.getDiscovery().stop(LocalPrintJob.this); 1013bf66744d61d18c66d46f2608de0467ad3df0268Mopria finish(false, mPrintService.getString(R.string.printer_offline)); 1023bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 1033bf66744d61d18c66d46f2608de0467ad3df0268Mopria }, DISCOVERY_TIMEOUT); 1043bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 1053bf66744d61d18c66d46f2608de0467ad3df0268Mopria 1063bf66744d61d18c66d46f2608de0467ad3df0268Mopria void cancel() { 1073bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (DEBUG) Log.d(TAG, "cancel() " + mPrintJob + " in state " + mState); 1083bf66744d61d18c66d46f2608de0467ad3df0268Mopria 1093bf66744d61d18c66d46f2608de0467ad3df0268Mopria switch (mState) { 1103bf66744d61d18c66d46f2608de0467ad3df0268Mopria case STATE_DISCOVERY: 1113bf66744d61d18c66d46f2608de0467ad3df0268Mopria // Cancel immediately 1123bf66744d61d18c66d46f2608de0467ad3df0268Mopria mPrintService.getDiscovery().stop(this); 1133bf66744d61d18c66d46f2608de0467ad3df0268Mopria mState = STATE_CANCEL; 1143bf66744d61d18c66d46f2608de0467ad3df0268Mopria finish(false, null); 1153bf66744d61d18c66d46f2608de0467ad3df0268Mopria break; 1163bf66744d61d18c66d46f2608de0467ad3df0268Mopria 1173bf66744d61d18c66d46f2608de0467ad3df0268Mopria case STATE_DELIVERING: 1183bf66744d61d18c66d46f2608de0467ad3df0268Mopria // Request cancel and wait for completion 1193bf66744d61d18c66d46f2608de0467ad3df0268Mopria mState = STATE_CANCEL; 1203bf66744d61d18c66d46f2608de0467ad3df0268Mopria mBackend.cancel(); 1213bf66744d61d18c66d46f2608de0467ad3df0268Mopria break; 1223bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 1233bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 1243bf66744d61d18c66d46f2608de0467ad3df0268Mopria 1253bf66744d61d18c66d46f2608de0467ad3df0268Mopria PrintJobId getPrintJobId() { 1263bf66744d61d18c66d46f2608de0467ad3df0268Mopria return mPrintJob.getId(); 1273bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 1283bf66744d61d18c66d46f2608de0467ad3df0268Mopria 1293bf66744d61d18c66d46f2608de0467ad3df0268Mopria @Override 1303bf66744d61d18c66d46f2608de0467ad3df0268Mopria public void onPrinterFound(DiscoveredPrinter printer) { 1313bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (mState != STATE_DISCOVERY) return; 1323bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (printer.getId(mPrintService).equals(mPrintJob.getInfo().getPrinterId())) { 1333bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (DEBUG) Log.d(TAG, "onPrinterFound() " + printer.name + " state=" + mState); 1343bf66744d61d18c66d46f2608de0467ad3df0268Mopria mPath = printer.path; 1353bf66744d61d18c66d46f2608de0467ad3df0268Mopria mPrintService.getCapabilitiesCache().request(printer, true, 1363bf66744d61d18c66d46f2608de0467ad3df0268Mopria this::handleCapabilities); 1373bf66744d61d18c66d46f2608de0467ad3df0268Mopria mPrintService.getDiscovery().stop(this); 1383bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 1393bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 1403bf66744d61d18c66d46f2608de0467ad3df0268Mopria 1413bf66744d61d18c66d46f2608de0467ad3df0268Mopria @Override 1423bf66744d61d18c66d46f2608de0467ad3df0268Mopria public void onPrinterLost(DiscoveredPrinter printer) { 1433bf66744d61d18c66d46f2608de0467ad3df0268Mopria // Ignore (the capability request, if any, will fail) 1443bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 1453bf66744d61d18c66d46f2608de0467ad3df0268Mopria 1463bf66744d61d18c66d46f2608de0467ad3df0268Mopria PrintJob getPrintJob() { 1473bf66744d61d18c66d46f2608de0467ad3df0268Mopria return mPrintJob; 1483bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 1493bf66744d61d18c66d46f2608de0467ad3df0268Mopria 150e604a9fafd50bca9fbc86ca525196efb6f1b70b1Glade Diviney private void handleCapabilities(DiscoveredPrinter printer, LocalPrinterCapabilities capabilities) { 1513bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (DEBUG) Log.d(TAG, "Capabilities for " + mPath + " are " + capabilities); 1523bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (mState != STATE_DISCOVERY) return; 1533bf66744d61d18c66d46f2608de0467ad3df0268Mopria 1543bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (capabilities == null) { 1553bf66744d61d18c66d46f2608de0467ad3df0268Mopria finish(false, mPrintService.getString(R.string.printer_offline)); 1563bf66744d61d18c66d46f2608de0467ad3df0268Mopria } else { 1573bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (DEBUG) Log.d(TAG, "Starting backend print of " + mPrintJob); 1583bf66744d61d18c66d46f2608de0467ad3df0268Mopria mMainHandler.removeCallbacksAndMessages(null); 1593bf66744d61d18c66d46f2608de0467ad3df0268Mopria mState = STATE_DELIVERING; 1603bf66744d61d18c66d46f2608de0467ad3df0268Mopria mBackend.print(mPath, mPrintJob, capabilities, this::handleJobStatus); 1613bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 1623bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 1633bf66744d61d18c66d46f2608de0467ad3df0268Mopria 1643bf66744d61d18c66d46f2608de0467ad3df0268Mopria private void handleJobStatus(JobStatus jobStatus) { 1653bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (DEBUG) Log.d(TAG, "onJobStatus() " + jobStatus); 1663bf66744d61d18c66d46f2608de0467ad3df0268Mopria switch (jobStatus.getJobState()) { 1673bf66744d61d18c66d46f2608de0467ad3df0268Mopria case BackendConstants.JOB_STATE_DONE: 1683bf66744d61d18c66d46f2608de0467ad3df0268Mopria switch (jobStatus.getJobResult()) { 1693bf66744d61d18c66d46f2608de0467ad3df0268Mopria case BackendConstants.JOB_DONE_OK: 1703bf66744d61d18c66d46f2608de0467ad3df0268Mopria finish(true, null); 1713bf66744d61d18c66d46f2608de0467ad3df0268Mopria break; 1723bf66744d61d18c66d46f2608de0467ad3df0268Mopria case BackendConstants.JOB_DONE_CANCELLED: 1733bf66744d61d18c66d46f2608de0467ad3df0268Mopria mState = STATE_CANCEL; 1743bf66744d61d18c66d46f2608de0467ad3df0268Mopria finish(false, null); 1753bf66744d61d18c66d46f2608de0467ad3df0268Mopria break; 1763bf66744d61d18c66d46f2608de0467ad3df0268Mopria case BackendConstants.JOB_DONE_CORRUPT: 1773bf66744d61d18c66d46f2608de0467ad3df0268Mopria finish(false, mPrintService.getString(R.string.unreadable_input)); 1783bf66744d61d18c66d46f2608de0467ad3df0268Mopria break; 1793bf66744d61d18c66d46f2608de0467ad3df0268Mopria default: 1803bf66744d61d18c66d46f2608de0467ad3df0268Mopria // Job failed 1813bf66744d61d18c66d46f2608de0467ad3df0268Mopria finish(false, null); 1823bf66744d61d18c66d46f2608de0467ad3df0268Mopria break; 1833bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 1843bf66744d61d18c66d46f2608de0467ad3df0268Mopria break; 1853bf66744d61d18c66d46f2608de0467ad3df0268Mopria 1863bf66744d61d18c66d46f2608de0467ad3df0268Mopria case BackendConstants.JOB_STATE_BLOCKED: 1873bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (mState == STATE_CANCEL) return; 1883bf66744d61d18c66d46f2608de0467ad3df0268Mopria int blockedId = jobStatus.getBlockedReasonId(); 1893bf66744d61d18c66d46f2608de0467ad3df0268Mopria blockedId = (blockedId == 0) ? R.string.printer_check : blockedId; 1903bf66744d61d18c66d46f2608de0467ad3df0268Mopria String blockedReason = mPrintService.getString(blockedId); 1913bf66744d61d18c66d46f2608de0467ad3df0268Mopria mPrintJob.block(blockedReason); 1923bf66744d61d18c66d46f2608de0467ad3df0268Mopria break; 1933bf66744d61d18c66d46f2608de0467ad3df0268Mopria 1943bf66744d61d18c66d46f2608de0467ad3df0268Mopria case BackendConstants.JOB_STATE_RUNNING: 1953bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (mState == STATE_CANCEL) return; 1963bf66744d61d18c66d46f2608de0467ad3df0268Mopria mPrintJob.start(); 1973bf66744d61d18c66d46f2608de0467ad3df0268Mopria break; 1983bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 1993bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 2003bf66744d61d18c66d46f2608de0467ad3df0268Mopria 2013bf66744d61d18c66d46f2608de0467ad3df0268Mopria /** 2023bf66744d61d18c66d46f2608de0467ad3df0268Mopria * Terminate the job, issuing appropriate notifications. 2033bf66744d61d18c66d46f2608de0467ad3df0268Mopria * 2043bf66744d61d18c66d46f2608de0467ad3df0268Mopria * @param success true if the printer reported successful job completion 2053bf66744d61d18c66d46f2608de0467ad3df0268Mopria * @param error reason for job failure if known 2063bf66744d61d18c66d46f2608de0467ad3df0268Mopria */ 2073bf66744d61d18c66d46f2608de0467ad3df0268Mopria private void finish(boolean success, String error) { 2083bf66744d61d18c66d46f2608de0467ad3df0268Mopria mPrintService.unlockWifi(); 2093bf66744d61d18c66d46f2608de0467ad3df0268Mopria mBackend.closeDocument(); 2103bf66744d61d18c66d46f2608de0467ad3df0268Mopria mMainHandler.removeCallbacksAndMessages(null); 2113bf66744d61d18c66d46f2608de0467ad3df0268Mopria if (success) { 2123bf66744d61d18c66d46f2608de0467ad3df0268Mopria // Job must not be blocked before completion 2133bf66744d61d18c66d46f2608de0467ad3df0268Mopria mPrintJob.start(); 2143bf66744d61d18c66d46f2608de0467ad3df0268Mopria mPrintJob.complete(); 2153bf66744d61d18c66d46f2608de0467ad3df0268Mopria } else if (mState == STATE_CANCEL) { 2163bf66744d61d18c66d46f2608de0467ad3df0268Mopria mPrintJob.cancel(); 2173bf66744d61d18c66d46f2608de0467ad3df0268Mopria } else { 2183bf66744d61d18c66d46f2608de0467ad3df0268Mopria mPrintJob.fail(error); 2193bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 2203bf66744d61d18c66d46f2608de0467ad3df0268Mopria mState = STATE_DONE; 2213bf66744d61d18c66d46f2608de0467ad3df0268Mopria mCompleteConsumer.accept(LocalPrintJob.this); 2223bf66744d61d18c66d46f2608de0467ad3df0268Mopria } 2233bf66744d61d18c66d46f2608de0467ad3df0268Mopria}