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}