blob: c067661fca1f7ff98551c3f874b5c4c6b9e16780 [file] [log] [blame]
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -07001/*
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
17package android.print;
18
19import android.content.Context;
20import android.content.IntentSender;
21import android.content.IntentSender.SendIntentException;
Svetoslav62836082013-07-17 14:52:35 -070022import android.os.Bundle;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070023import android.os.CancellationSignal;
24import android.os.Handler;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070025import android.os.Looper;
26import android.os.Message;
27import android.os.ParcelFileDescriptor;
28import android.os.RemoteException;
Svetoslav Ganova0027152013-06-25 14:59:53 -070029import android.print.PrintDocumentAdapter.LayoutResultCallback;
30import android.print.PrintDocumentAdapter.WriteResultCallback;
Svetoslav Ganov88d19912013-07-22 12:32:03 -070031import android.text.TextUtils;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070032import android.util.Log;
33
34import com.android.internal.os.SomeArgs;
35
36import libcore.io.IoUtils;
37
38import java.io.File;
39import java.io.FileDescriptor;
40import java.lang.ref.WeakReference;
41import java.util.ArrayList;
42import java.util.Collections;
43import java.util.List;
44
45/**
46 * System level service for accessing the printing capabilities of the platform.
47 * <p>
48 * To obtain a handle to the print manager do the following:
49 * </p>
50 * <pre>
51 * PrintManager printManager =
52 * (PrintManager) context.getSystemService(Context.PRINT_SERVICE);
53 * </pre>
54 */
55public final class PrintManager {
56
57 private static final String LOG_TAG = "PrintManager";
58
59 /** @hide */
60 public static final int APP_ID_ANY = -2;
61
62 private final Context mContext;
63
64 private final IPrintManager mService;
65
66 private final int mUserId;
67
68 private final int mAppId;
69
70 private final PrintClient mPrintClient;
71
72 private final Handler mHandler;
73
74 /**
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070075 * Creates a new instance.
76 *
77 * @param context The current context in which to operate.
78 * @param service The backing system service.
79 *
80 * @hide
81 */
82 public PrintManager(Context context, IPrintManager service, int userId, int appId) {
83 mContext = context;
84 mService = service;
85 mUserId = userId;
86 mAppId = appId;
87 mPrintClient = new PrintClient(this);
88 mHandler = new Handler(context.getMainLooper(), null, false) {
89 @Override
90 public void handleMessage(Message message) {
91 SomeArgs args = (SomeArgs) message.obj;
92 Context context = (Context) args.arg1;
93 IntentSender intent = (IntentSender) args.arg2;
94 args.recycle();
95 try {
96 context.startIntentSender(intent, null, 0, 0, 0);
97 } catch (SendIntentException sie) {
98 Log.e(LOG_TAG, "Couldn't start print job config activity.", sie);
99 }
100 }
101 };
102 }
103
104 /**
105 * Creates an instance that can access all print jobs.
106 *
107 * @param userId The user id for which to get all print jobs.
Svetoslav Ganova0027152013-06-25 14:59:53 -0700108 * @return An instance if the caller has the permission to access
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700109 * all print jobs, null otherwise.
110 *
111 * @hide
112 */
113 public PrintManager getGlobalPrintManagerForUser(int userId) {
114 return new PrintManager(mContext, mService, userId, APP_ID_ANY);
115 }
116
Svetoslav Ganova0027152013-06-25 14:59:53 -0700117 PrintJobInfo getPrintJobInfo(int printJobId) {
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700118 try {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700119 return mService.getPrintJobInfo(printJobId, mAppId, mUserId);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700120 } catch (RemoteException re) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700121 Log.e(LOG_TAG, "Error getting a print job info:" + printJobId, re);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700122 }
123 return null;
124 }
125
126 /**
127 * Gets the print jobs for this application.
128 *
129 * @return The print job list.
130 *
131 * @see PrintJob
132 */
133 public List<PrintJob> getPrintJobs() {
134 try {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700135 List<PrintJobInfo> printJobInfos = mService.getPrintJobInfos(mAppId, mUserId);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700136 if (printJobInfos == null) {
137 return Collections.emptyList();
138 }
139 final int printJobCount = printJobInfos.size();
140 List<PrintJob> printJobs = new ArrayList<PrintJob>(printJobCount);
141 for (int i = 0; i < printJobCount; i++) {
142 printJobs.add(new PrintJob(printJobInfos.get(i), this));
143 }
144 return printJobs;
145 } catch (RemoteException re) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700146 Log.e(LOG_TAG, "Error getting print jobs", re);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700147 }
148 return Collections.emptyList();
149 }
150
Svetoslav Ganova0027152013-06-25 14:59:53 -0700151 void cancelPrintJob(int printJobId) {
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700152 try {
153 mService.cancelPrintJob(printJobId, mAppId, mUserId);
154 } catch (RemoteException re) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700155 Log.e(LOG_TAG, "Error cancleing a print job: " + printJobId, re);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700156 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700157 }
158
159 /**
160 * Creates a print job for printing a file with default print attributes.
161 *
162 * @param printJobName A name for the new print job.
163 * @param pdfFile The PDF file to print.
164 * @param attributes The default print job attributes.
165 * @return The created print job.
Svetoslavfd906512013-06-24 09:04:48 -0700166 *
167 * @see PrintJob
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700168 */
169 public PrintJob print(String printJobName, File pdfFile, PrintAttributes attributes) {
Svetoslav17b7f6e2013-06-24 18:29:33 -0700170 FileDocumentAdapter documentAdapter = new FileDocumentAdapter(mContext, pdfFile);
Svetoslav Ganova0027152013-06-25 14:59:53 -0700171 return print(printJobName, documentAdapter, attributes);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700172 }
173
174 /**
Svetoslav Ganova0027152013-06-25 14:59:53 -0700175 * Creates a print job for printing a {@link PrintDocumentAdapter} with default print
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700176 * attributes.
177 *
178 * @param printJobName A name for the new print job.
Svetoslav Ganova0027152013-06-25 14:59:53 -0700179 * @param documentAdapter An adapter that emits the document to print.
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700180 * @param attributes The default print job attributes.
181 * @return The created print job.
Svetoslavfd906512013-06-24 09:04:48 -0700182 *
183 * @see PrintJob
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700184 */
Svetoslav Ganova0027152013-06-25 14:59:53 -0700185 public PrintJob print(String printJobName, PrintDocumentAdapter documentAdapter,
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700186 PrintAttributes attributes) {
Svetoslav Ganov88d19912013-07-22 12:32:03 -0700187 if (TextUtils.isEmpty(printJobName)) {
188 throw new IllegalArgumentException("priintJobName cannot be empty");
189 }
Svetoslav Ganova0027152013-06-25 14:59:53 -0700190 PrintDocumentAdapterDelegate delegate = new PrintDocumentAdapterDelegate(documentAdapter,
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700191 mContext.getMainLooper());
192 try {
193 PrintJobInfo printJob = mService.print(printJobName, mPrintClient, delegate,
194 attributes, mAppId, mUserId);
195 if (printJob != null) {
196 return new PrintJob(printJob, this);
197 }
198 } catch (RemoteException re) {
199 Log.e(LOG_TAG, "Error creating a print job", re);
200 }
201 return null;
202 }
203
204 private static final class PrintClient extends IPrintClient.Stub {
205
206 private final WeakReference<PrintManager> mWeakPrintManager;
207
208 public PrintClient(PrintManager manager) {
209 mWeakPrintManager = new WeakReference<PrintManager>(manager);
210 }
211
212 @Override
213 public void startPrintJobConfigActivity(IntentSender intent) {
214 PrintManager manager = mWeakPrintManager.get();
215 if (manager != null) {
216 SomeArgs args = SomeArgs.obtain();
217 args.arg1 = manager.mContext;
218 args.arg2 = intent;
219 manager.mHandler.obtainMessage(0, args).sendToTarget();
220 }
221 }
222 }
223
Svetoslav Ganova0027152013-06-25 14:59:53 -0700224 private static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub {
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700225
226 private final Object mLock = new Object();
227
228 private CancellationSignal mLayoutOrWriteCancellation;
229
Svetoslav Ganova0027152013-06-25 14:59:53 -0700230 private PrintDocumentAdapter mDocumentAdapter; // Strong reference OK - cleared in finish()
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700231
Svetoslav Ganova0027152013-06-25 14:59:53 -0700232 private Handler mHandler; // Strong reference OK - cleared in finish()
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700233
Svetoslav Ganova0027152013-06-25 14:59:53 -0700234 public PrintDocumentAdapterDelegate(PrintDocumentAdapter documentAdapter, Looper looper) {
235 mDocumentAdapter = documentAdapter;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700236 mHandler = new MyHandler(looper);
237 }
238
239 @Override
240 public void start() {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700241 mHandler.sendEmptyMessage(MyHandler.MSG_START);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700242 }
243
244 @Override
Svetoslav62836082013-07-17 14:52:35 -0700245 public void layout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700246 ILayoutResultCallback callback, Bundle metadata, int sequence) {
247 synchronized (mLock) {
248 if (mLayoutOrWriteCancellation != null) {
249 mLayoutOrWriteCancellation.cancel();
250 }
251 }
Svetoslav Ganova0027152013-06-25 14:59:53 -0700252 SomeArgs args = SomeArgs.obtain();
253 args.arg1 = oldAttributes;
254 args.arg2 = newAttributes;
255 args.arg3 = callback;
Svetoslav62836082013-07-17 14:52:35 -0700256 args.arg4 = metadata;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700257 args.argi1 = sequence;
258 mHandler.removeMessages(MyHandler.MSG_LAYOUT);
Svetoslav Ganova0027152013-06-25 14:59:53 -0700259 mHandler.obtainMessage(MyHandler.MSG_LAYOUT, args).sendToTarget();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700260 }
261
262 @Override
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700263 public void write(PageRange[] pages, ParcelFileDescriptor fd,
264 IWriteResultCallback callback, int sequence) {
265 synchronized (mLock) {
266 if (mLayoutOrWriteCancellation != null) {
267 mLayoutOrWriteCancellation.cancel();
268 }
269 }
Svetoslav Ganova0027152013-06-25 14:59:53 -0700270 SomeArgs args = SomeArgs.obtain();
271 args.arg1 = pages;
272 args.arg2 = fd.getFileDescriptor();
273 args.arg3 = callback;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700274 args.argi1 = sequence;
275 mHandler.removeMessages(MyHandler.MSG_WRITE);
Svetoslav Ganova0027152013-06-25 14:59:53 -0700276 mHandler.obtainMessage(MyHandler.MSG_WRITE, args).sendToTarget();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700277 }
278
279 @Override
280 public void finish() {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700281 mHandler.sendEmptyMessage(MyHandler.MSG_FINISH);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700282 }
283
Svetoslav Ganova0027152013-06-25 14:59:53 -0700284 private boolean isFinished() {
285 return mDocumentAdapter == null;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700286 }
287
Svetoslav Ganova0027152013-06-25 14:59:53 -0700288 private void doFinish() {
289 mDocumentAdapter = null;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700290 mHandler = null;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700291 mLayoutOrWriteCancellation = null;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700292 }
293
294 private final class MyHandler extends Handler {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700295 public static final int MSG_START = 1;
296 public static final int MSG_LAYOUT = 2;
297 public static final int MSG_WRITE = 3;
298 public static final int MSG_FINISH = 4;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700299
300 public MyHandler(Looper looper) {
301 super(looper, null, true);
302 }
303
304 @Override
305 public void handleMessage(Message message) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700306 if (isFinished()) {
307 return;
308 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700309 switch (message.what) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700310 case MSG_START: {
311 mDocumentAdapter.onStart();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700312 } break;
313
Svetoslav Ganova0027152013-06-25 14:59:53 -0700314 case MSG_LAYOUT: {
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700315 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700316 PrintAttributes oldAttributes = (PrintAttributes) args.arg1;
317 PrintAttributes newAttributes = (PrintAttributes) args.arg2;
318 ILayoutResultCallback callback = (ILayoutResultCallback) args.arg3;
319 Bundle metadata = (Bundle) args.arg4;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700320 final int sequence = args.argi1;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700321 args.recycle();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700322
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700323 CancellationSignal cancellation = new CancellationSignal();
324 synchronized (mLock) {
325 mLayoutOrWriteCancellation = cancellation;
Svetoslav Ganova0027152013-06-25 14:59:53 -0700326 }
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700327
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700328 mDocumentAdapter.onLayout(oldAttributes, newAttributes, cancellation,
329 new MyLayoutResultCallback(callback, sequence), metadata);
Svetoslav Ganova0027152013-06-25 14:59:53 -0700330 } break;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700331
Svetoslav Ganova0027152013-06-25 14:59:53 -0700332 case MSG_WRITE: {
333 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700334 PageRange[] pages = (PageRange[]) args.arg1;
335 FileDescriptor fd = (FileDescriptor) args.arg2;
336 IWriteResultCallback callback = (IWriteResultCallback) args.arg3;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700337 final int sequence = args.argi1;
Svetoslav Ganova0027152013-06-25 14:59:53 -0700338 args.recycle();
339
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700340 CancellationSignal cancellation = new CancellationSignal();
341 synchronized (mLock) {
342 mLayoutOrWriteCancellation = cancellation;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700343 }
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700344
345 mDocumentAdapter.onWrite(pages, fd, cancellation,
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700346 new MyWriteResultCallback(callback, fd, sequence));
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700347 } break;
348
Svetoslav Ganova0027152013-06-25 14:59:53 -0700349 case MSG_FINISH: {
350 mDocumentAdapter.onFinish();
351 doFinish();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700352 } break;
353
354 default: {
355 throw new IllegalArgumentException("Unknown message: "
356 + message.what);
357 }
358 }
359 }
360 }
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700361
362 private final class MyLayoutResultCallback extends LayoutResultCallback {
363 private ILayoutResultCallback mCallback;
364 private final int mSequence;
365
366 public MyLayoutResultCallback(ILayoutResultCallback callback,
367 int sequence) {
368 mCallback = callback;
369 mSequence = sequence;
370 }
371
372 @Override
373 public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
374 final ILayoutResultCallback callback;
375 synchronized (mLock) {
376 callback = mCallback;
377 clearLocked();
378 }
379 if (info == null) {
380 throw new IllegalArgumentException("info cannot be null");
381 }
382 if (callback != null) {
383 try {
384 callback.onLayoutFinished(info, changed, mSequence);
385 } catch (RemoteException re) {
386 Log.e(LOG_TAG, "Error calling onLayoutFinished", re);
387 }
388 }
389 }
390
391 @Override
392 public void onLayoutFailed(CharSequence error) {
393 final ILayoutResultCallback callback;
394 synchronized (mLock) {
395 callback = mCallback;
396 clearLocked();
397 }
398 if (callback != null) {
399 try {
400 callback.onLayoutFailed(error, mSequence);
401 } catch (RemoteException re) {
402 Log.e(LOG_TAG, "Error calling onLayoutFailed", re);
403 }
404 }
405 }
406
407 @Override
408 public void onLayoutCancelled() {
409 synchronized (mLock) {
410 clearLocked();
411 }
412 }
413
414 private void clearLocked() {
415 mLayoutOrWriteCancellation = null;
416 mCallback = null;
417 }
418 }
419
420 private final class MyWriteResultCallback extends WriteResultCallback {
421 private FileDescriptor mFd;
422 private int mSequence;
423 private IWriteResultCallback mCallback;
424
425 public MyWriteResultCallback(IWriteResultCallback callback,
426 FileDescriptor fd, int sequence) {
427 mFd = fd;
428 mSequence = sequence;
429 mCallback = callback;
430 }
431
432 @Override
433 public void onWriteFinished(PageRange[] pages) {
434 final IWriteResultCallback callback;
435 synchronized (mLock) {
436 callback = mCallback;
437 clearLocked();
438 }
439 if (pages == null) {
440 throw new IllegalArgumentException("pages cannot be null");
441 }
442 if (pages.length == 0) {
443 throw new IllegalArgumentException("pages cannot be empty");
444 }
445 if (callback != null) {
446 try {
447 callback.onWriteFinished(pages, mSequence);
448 } catch (RemoteException re) {
449 Log.e(LOG_TAG, "Error calling onWriteFinished", re);
450 }
451 }
452 }
453
454 @Override
455 public void onWriteFailed(CharSequence error) {
456 final IWriteResultCallback callback;
457 synchronized (mLock) {
458 callback = mCallback;
459 clearLocked();
460 }
461 if (callback != null) {
462 try {
463 callback.onWriteFailed(error, mSequence);
464 } catch (RemoteException re) {
465 Log.e(LOG_TAG, "Error calling onWriteFailed", re);
466 }
467 }
468 }
469
470 @Override
471 public void onWriteCancelled() {
472 synchronized (mLock) {
473 clearLocked();
474 }
475 }
476
477 private void clearLocked() {
478 mLayoutOrWriteCancellation = null;
479 IoUtils.closeQuietly(mFd);
480 mCallback = null;
481 mFd = null;
482 }
483 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700484 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700485}