• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  * Copyright (C) 2016 Mopria Alliance, Inc.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package com.android.bips.ipp;
19 
20 import android.content.Context;
21 import android.net.Uri;
22 import android.os.AsyncTask;
23 import android.os.Build;
24 import android.os.ParcelFileDescriptor;
25 import android.print.PrintAttributes;
26 import android.print.PrintDocumentInfo;
27 import android.print.PrintJobInfo;
28 import android.printservice.PrintJob;
29 import android.util.Log;
30 import android.view.Gravity;
31 
32 import com.android.bips.jni.BackendConstants;
33 import com.android.bips.jni.LocalJobParams;
34 import com.android.bips.jni.LocalPrinterCapabilities;
35 import com.android.bips.jni.MediaSizes;
36 import com.android.bips.util.FileUtils;
37 
38 import java.io.BufferedOutputStream;
39 import java.io.File;
40 import java.io.FileOutputStream;
41 import java.io.IOException;
42 
43 /**
44  * A background task that starts sending a print job. The result of this task is an integer
45  * defined by {@link Backend} ERROR_* codes or a non-negative code for success.
46  */
47 class StartJobTask extends AsyncTask<Void, Void, Integer> {
48     private static final String TAG = StartJobTask.class.getSimpleName();
49     private static final boolean DEBUG = false;
50 
51     private static final String MIME_TYPE_PDF = "application/pdf";
52 
53     // see wprint_df_types.h for enum values
54     private static final int MEDIA_TYPE_PLAIN = 0;
55     private static final int MEDIA_TYPE_AUTO = 98;
56     // Unused but present
57     //    private static final int MEDIA_TYPE_PHOTO = 1;
58     //    private static final int MEDIA_TYPE_PHOTO_GLOSSY = 2;
59 
60     private static final int SIDES_SIMPLEX = 0;
61     private static final int SIDES_DUPLEX_LONG_EDGE = 1;
62     private static final int SIDES_DUPLEX_SHORT_EDGE = 2;
63 
64     private static final int RESOLUTION_300_DPI = 300;
65 
66     private static final int COLOR_SPACE_MONOCHROME = 0;
67     private static final int COLOR_SPACE_COLOR = 1;
68 
69     private static final int BORDERLESS_OFF = 0;
70     private static final int BORDERLESS_ON = 1;
71 
72     private final Context mContext;
73     private final Backend mBackend;
74     private final Uri mDestination;
75     private final LocalPrinterCapabilities mCapabilities;
76     private final LocalJobParams mJobParams;
77     private final ParcelFileDescriptor mSourceFileDescriptor;
78     private final String mJobId;
79     private final PrintJobInfo mJobInfo;
80     private final PrintDocumentInfo mDocInfo;
81     private final MediaSizes mMediaSizes;
82 
StartJobTask(Context context, Backend backend, Uri destination, PrintJob printJob, LocalPrinterCapabilities capabilities)83     StartJobTask(Context context, Backend backend, Uri destination, PrintJob printJob,
84             LocalPrinterCapabilities capabilities) {
85         mContext = context;
86         mBackend = backend;
87         mDestination = destination;
88         mCapabilities = capabilities;
89         mJobParams = new LocalJobParams();
90         mJobId = printJob.getId().toString();
91         mJobInfo = printJob.getInfo();
92         mDocInfo = printJob.getDocument().getInfo();
93         mSourceFileDescriptor = printJob.getDocument().getData();
94         mMediaSizes = MediaSizes.getInstance(mContext);
95     }
96 
populateJobParams()97     private void populateJobParams() {
98         PrintAttributes.MediaSize mediaSize = mJobInfo.getAttributes().getMediaSize();
99 
100         mJobParams.borderless = isBorderless() ? BORDERLESS_ON : BORDERLESS_OFF;
101         mJobParams.duplex = getSides();
102         mJobParams.num_copies = mJobInfo.getCopies();
103         mJobParams.pdf_render_resolution = RESOLUTION_300_DPI;
104         mJobParams.fit_to_page = !getFillPage();
105         mJobParams.fill_page = getFillPage();
106         mJobParams.job_name = mJobInfo.getLabel();
107         mJobParams.job_originating_user_name = Build.MODEL;
108         mJobParams.auto_rotate = false;
109         mJobParams.portrait_mode = mediaSize == null || mediaSize.isPortrait();
110         mJobParams.landscape_mode = !mJobParams.portrait_mode;
111         mJobParams.media_size = mMediaSizes.toMediaCode(mediaSize);
112         mJobParams.media_type = getMediaType();
113         mJobParams.color_space = getColorSpace();
114         mJobParams.document_category = getDocumentCategory();
115 
116         mJobParams.job_margin_top = Math.max(mJobParams.job_margin_top, 0.0f);
117         mJobParams.job_margin_left = Math.max(mJobParams.job_margin_left, 0.0f);
118         mJobParams.job_margin_right = Math.max(mJobParams.job_margin_right, 0.0f);
119         mJobParams.job_margin_bottom = Math.max(mJobParams.job_margin_bottom, 0.0f);
120 
121         mJobParams.alignment = Gravity.CENTER;
122     }
123 
124     @Override
doInBackground(Void... voids)125     protected Integer doInBackground(Void... voids) {
126         if (DEBUG) Log.d(TAG, "doInBackground() job=" + mJobParams + ", cap=" + mCapabilities);
127         File tempFolder = new File(mContext.getFilesDir(), Backend.TEMP_JOB_FOLDER);
128         if (!FileUtils.makeDirectory(tempFolder)) {
129             Log.w(TAG, "makeDirectory failure");
130             return Backend.ERROR_FILE;
131         }
132 
133         File pdfFile = new File(tempFolder, mJobId + ".pdf");
134         try {
135             try {
136                 FileUtils.copy(new ParcelFileDescriptor.AutoCloseInputStream(mSourceFileDescriptor),
137                         new BufferedOutputStream(new FileOutputStream(pdfFile)));
138             } catch (IOException e) {
139                 Log.w(TAG, "Error while copying to " + pdfFile, e);
140                 return Backend.ERROR_FILE;
141             }
142             String[] files = new String[]{pdfFile.toString()};
143 
144             // Address, without port.
145             String address = mDestination.getHost() + mDestination.getPath();
146 
147             if (isCancelled()) {
148                 return Backend.ERROR_CANCEL;
149             }
150 
151             // Get default job parameters
152             int result = mBackend.nativeGetDefaultJobParameters(mJobParams);
153             if (result != 0) {
154                 if (DEBUG) Log.w(TAG, "nativeGetDefaultJobParameters failure: " + result);
155                 return Backend.ERROR_UNKNOWN;
156             }
157 
158             if (isCancelled()) {
159                 return Backend.ERROR_CANCEL;
160             }
161 
162             // Fill in job parameters from capabilities and print job info.
163             populateJobParams();
164 
165             // Finalize job parameters
166             mBackend.nativeGetFinalJobParameters(mJobParams, mCapabilities);
167 
168             if (isCancelled()) {
169                 return Backend.ERROR_CANCEL;
170             }
171             if (DEBUG) {
172                 Log.d(TAG, "nativeStartJob address=" + address
173                         + " port=" + mDestination.getPort() + " mime=" + MIME_TYPE_PDF
174                         + " files=" + files[0] + " job=" + mJobParams);
175             }
176             // Initiate job
177             result = mBackend.nativeStartJob(Backend.getIp(address), mDestination.getPort(),
178                     MIME_TYPE_PDF, mJobParams, mCapabilities, files, null,
179                     mDestination.getScheme());
180             if (result < 0) {
181                 Log.w(TAG, "nativeStartJob failure: " + result);
182                 return Backend.ERROR_UNKNOWN;
183             }
184 
185             pdfFile = null;
186             return result;
187         } finally {
188             if (pdfFile != null) {
189                 pdfFile.delete();
190             }
191         }
192     }
193 
isBorderless()194     private boolean isBorderless() {
195         return mCapabilities.borderless
196                 && mDocInfo.getContentType() == PrintDocumentInfo.CONTENT_TYPE_PHOTO;
197     }
198 
getSides()199     private int getSides() {
200         // Never duplex photo media; may damage printers
201         if (mDocInfo.getContentType() == PrintDocumentInfo.CONTENT_TYPE_PHOTO) {
202             return SIDES_SIMPLEX;
203         }
204 
205         switch (mJobInfo.getAttributes().getDuplexMode()) {
206             case PrintAttributes.DUPLEX_MODE_LONG_EDGE:
207                 return SIDES_DUPLEX_LONG_EDGE;
208             case PrintAttributes.DUPLEX_MODE_SHORT_EDGE:
209                 return SIDES_DUPLEX_SHORT_EDGE;
210             case PrintAttributes.DUPLEX_MODE_NONE:
211             default:
212                 return SIDES_SIMPLEX;
213         }
214     }
215 
getFillPage()216     private boolean getFillPage() {
217         switch (mDocInfo.getContentType()) {
218             case PrintDocumentInfo.CONTENT_TYPE_PHOTO:
219                 return true;
220             case PrintDocumentInfo.CONTENT_TYPE_UNKNOWN:
221             case PrintDocumentInfo.CONTENT_TYPE_DOCUMENT:
222             default:
223                 return false;
224         }
225     }
226 
getMediaType()227     private int getMediaType() {
228         int mediaType = MEDIA_TYPE_PLAIN;
229         for (int supportedType : mCapabilities.supportedMediaTypes) {
230             if (supportedType == MEDIA_TYPE_AUTO) {
231                 // if auto media is supported, use that and break out of the loop
232                 mediaType = MEDIA_TYPE_AUTO;
233                 break;
234             } else if (mDocInfo.getContentType() == PrintDocumentInfo.CONTENT_TYPE_PHOTO
235                     && supportedType > mediaType) {
236                 // Select the best (highest #) supported type for photos
237                 mediaType = supportedType;
238             }
239         }
240         return mediaType;
241     }
242 
getColorSpace()243     private int getColorSpace() {
244         switch (mJobInfo.getAttributes().getColorMode()) {
245             case PrintAttributes.COLOR_MODE_COLOR:
246                 return COLOR_SPACE_COLOR;
247             case PrintAttributes.COLOR_MODE_MONOCHROME:
248             default:
249                 return COLOR_SPACE_MONOCHROME;
250         }
251     }
252 
getDocumentCategory()253     private String getDocumentCategory() {
254         switch (mDocInfo.getContentType()) {
255             case PrintDocumentInfo.CONTENT_TYPE_PHOTO:
256                 return BackendConstants.PRINT_DOCUMENT_CATEGORY__PHOTO;
257 
258             case PrintDocumentInfo.CONTENT_TYPE_DOCUMENT:
259             default:
260                 return BackendConstants.PRINT_DOCUMENT_CATEGORY__DOCUMENT;
261         }
262     }
263 }
264