blob: 6e32c05fc24cdee93cb57abc3eeb3618e1e86c3c [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;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070039import java.lang.ref.WeakReference;
40import java.util.ArrayList;
41import java.util.Collections;
42import java.util.List;
43
44/**
45 * System level service for accessing the printing capabilities of the platform.
46 * <p>
47 * To obtain a handle to the print manager do the following:
48 * </p>
49 * <pre>
50 * PrintManager printManager =
51 * (PrintManager) context.getSystemService(Context.PRINT_SERVICE);
52 * </pre>
53 */
54public final class PrintManager {
55
56 private static final String LOG_TAG = "PrintManager";
57
58 /** @hide */
59 public static final int APP_ID_ANY = -2;
60
61 private final Context mContext;
62
63 private final IPrintManager mService;
64
65 private final int mUserId;
66
67 private final int mAppId;
68
69 private final PrintClient mPrintClient;
70
71 private final Handler mHandler;
72
73 /**
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070074 * Creates a new instance.
75 *
76 * @param context The current context in which to operate.
77 * @param service The backing system service.
78 *
79 * @hide
80 */
81 public PrintManager(Context context, IPrintManager service, int userId, int appId) {
82 mContext = context;
83 mService = service;
84 mUserId = userId;
85 mAppId = appId;
86 mPrintClient = new PrintClient(this);
87 mHandler = new Handler(context.getMainLooper(), null, false) {
88 @Override
89 public void handleMessage(Message message) {
90 SomeArgs args = (SomeArgs) message.obj;
91 Context context = (Context) args.arg1;
92 IntentSender intent = (IntentSender) args.arg2;
93 args.recycle();
94 try {
95 context.startIntentSender(intent, null, 0, 0, 0);
96 } catch (SendIntentException sie) {
97 Log.e(LOG_TAG, "Couldn't start print job config activity.", sie);
98 }
99 }
100 };
101 }
102
103 /**
104 * Creates an instance that can access all print jobs.
105 *
106 * @param userId The user id for which to get all print jobs.
Svetoslav Ganova0027152013-06-25 14:59:53 -0700107 * @return An instance if the caller has the permission to access
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700108 * all print jobs, null otherwise.
109 *
110 * @hide
111 */
112 public PrintManager getGlobalPrintManagerForUser(int userId) {
113 return new PrintManager(mContext, mService, userId, APP_ID_ANY);
114 }
115
Svetoslav Ganova0027152013-06-25 14:59:53 -0700116 PrintJobInfo getPrintJobInfo(int printJobId) {
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700117 try {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700118 return mService.getPrintJobInfo(printJobId, mAppId, mUserId);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700119 } catch (RemoteException re) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700120 Log.e(LOG_TAG, "Error getting a print job info:" + printJobId, re);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700121 }
122 return null;
123 }
124
125 /**
126 * Gets the print jobs for this application.
127 *
128 * @return The print job list.
129 *
130 * @see PrintJob
131 */
132 public List<PrintJob> getPrintJobs() {
133 try {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700134 List<PrintJobInfo> printJobInfos = mService.getPrintJobInfos(mAppId, mUserId);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700135 if (printJobInfos == null) {
136 return Collections.emptyList();
137 }
138 final int printJobCount = printJobInfos.size();
139 List<PrintJob> printJobs = new ArrayList<PrintJob>(printJobCount);
140 for (int i = 0; i < printJobCount; i++) {
141 printJobs.add(new PrintJob(printJobInfos.get(i), this));
142 }
143 return printJobs;
144 } catch (RemoteException re) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700145 Log.e(LOG_TAG, "Error getting print jobs", re);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700146 }
147 return Collections.emptyList();
148 }
149
Svetoslav Ganova0027152013-06-25 14:59:53 -0700150 void cancelPrintJob(int printJobId) {
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700151 try {
152 mService.cancelPrintJob(printJobId, mAppId, mUserId);
153 } catch (RemoteException re) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700154 Log.e(LOG_TAG, "Error cancleing a print job: " + printJobId, re);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700155 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700156 }
157
158 /**
159 * Creates a print job for printing a file with default print attributes.
160 *
161 * @param printJobName A name for the new print job.
162 * @param pdfFile The PDF file to print.
Svetoslav Ganov798bed62013-08-11 12:29:39 -0700163 * @param documentInfo Information about the printed document.
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700164 * @param attributes The default print job attributes.
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700165 * @return The created print job on success or null on failure.
Svetoslavfd906512013-06-24 09:04:48 -0700166 *
167 * @see PrintJob
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700168 */
Svetoslav Ganov798bed62013-08-11 12:29:39 -0700169 public PrintJob print(String printJobName, File pdfFile, PrintDocumentInfo documentInfo,
170 PrintAttributes attributes) {
171 PrintFileDocumentAdapter documentAdapter = new PrintFileDocumentAdapter(
172 mContext, pdfFile, documentInfo);
Svetoslav Ganova0027152013-06-25 14:59:53 -0700173 return print(printJobName, documentAdapter, attributes);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700174 }
175
176 /**
Svetoslav Ganova0027152013-06-25 14:59:53 -0700177 * Creates a print job for printing a {@link PrintDocumentAdapter} with default print
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700178 * attributes.
179 *
180 * @param printJobName A name for the new print job.
Svetoslav Ganova0027152013-06-25 14:59:53 -0700181 * @param documentAdapter An adapter that emits the document to print.
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700182 * @param attributes The default print job attributes.
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700183 * @return The created print job on success or null on failure.
Svetoslavfd906512013-06-24 09:04:48 -0700184 *
185 * @see PrintJob
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700186 */
Svetoslav Ganova0027152013-06-25 14:59:53 -0700187 public PrintJob print(String printJobName, PrintDocumentAdapter documentAdapter,
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700188 PrintAttributes attributes) {
Svetoslav Ganov88d19912013-07-22 12:32:03 -0700189 if (TextUtils.isEmpty(printJobName)) {
190 throw new IllegalArgumentException("priintJobName cannot be empty");
191 }
Svetoslav Ganova0027152013-06-25 14:59:53 -0700192 PrintDocumentAdapterDelegate delegate = new PrintDocumentAdapterDelegate(documentAdapter,
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700193 mContext.getMainLooper());
194 try {
195 PrintJobInfo printJob = mService.print(printJobName, mPrintClient, delegate,
196 attributes, mAppId, mUserId);
197 if (printJob != null) {
198 return new PrintJob(printJob, this);
199 }
200 } catch (RemoteException re) {
201 Log.e(LOG_TAG, "Error creating a print job", re);
202 }
203 return null;
204 }
205
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700206 /**
207 * @hide
208 */
209 public PrinterDiscoverySession createPrinterDiscoverySession() {
210 return new PrinterDiscoverySession(mService, mContext, mUserId);
211 }
212
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700213 private static final class PrintClient extends IPrintClient.Stub {
214
215 private final WeakReference<PrintManager> mWeakPrintManager;
216
217 public PrintClient(PrintManager manager) {
218 mWeakPrintManager = new WeakReference<PrintManager>(manager);
219 }
220
221 @Override
222 public void startPrintJobConfigActivity(IntentSender intent) {
223 PrintManager manager = mWeakPrintManager.get();
224 if (manager != null) {
225 SomeArgs args = SomeArgs.obtain();
226 args.arg1 = manager.mContext;
227 args.arg2 = intent;
228 manager.mHandler.obtainMessage(0, args).sendToTarget();
229 }
230 }
231 }
232
Svetoslav Ganova0027152013-06-25 14:59:53 -0700233 private static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub {
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700234
235 private final Object mLock = new Object();
236
237 private CancellationSignal mLayoutOrWriteCancellation;
238
Svetoslav Ganova0027152013-06-25 14:59:53 -0700239 private PrintDocumentAdapter mDocumentAdapter; // Strong reference OK - cleared in finish()
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700240
Svetoslav Ganova0027152013-06-25 14:59:53 -0700241 private Handler mHandler; // Strong reference OK - cleared in finish()
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700242
Svetoslav Ganova0027152013-06-25 14:59:53 -0700243 public PrintDocumentAdapterDelegate(PrintDocumentAdapter documentAdapter, Looper looper) {
244 mDocumentAdapter = documentAdapter;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700245 mHandler = new MyHandler(looper);
246 }
247
248 @Override
249 public void start() {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700250 mHandler.sendEmptyMessage(MyHandler.MSG_START);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700251 }
252
253 @Override
Svetoslav62836082013-07-17 14:52:35 -0700254 public void layout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700255 ILayoutResultCallback callback, Bundle metadata, int sequence) {
256 synchronized (mLock) {
257 if (mLayoutOrWriteCancellation != null) {
258 mLayoutOrWriteCancellation.cancel();
259 }
260 }
Svetoslav Ganova0027152013-06-25 14:59:53 -0700261 SomeArgs args = SomeArgs.obtain();
262 args.arg1 = oldAttributes;
263 args.arg2 = newAttributes;
264 args.arg3 = callback;
Svetoslav62836082013-07-17 14:52:35 -0700265 args.arg4 = metadata;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700266 args.argi1 = sequence;
267 mHandler.removeMessages(MyHandler.MSG_LAYOUT);
Svetoslav Ganova0027152013-06-25 14:59:53 -0700268 mHandler.obtainMessage(MyHandler.MSG_LAYOUT, args).sendToTarget();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700269 }
270
271 @Override
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700272 public void write(PageRange[] pages, ParcelFileDescriptor fd,
273 IWriteResultCallback callback, int sequence) {
274 synchronized (mLock) {
275 if (mLayoutOrWriteCancellation != null) {
276 mLayoutOrWriteCancellation.cancel();
277 }
278 }
Svetoslav Ganova0027152013-06-25 14:59:53 -0700279 SomeArgs args = SomeArgs.obtain();
280 args.arg1 = pages;
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700281 args.arg2 = fd;
Svetoslav Ganova0027152013-06-25 14:59:53 -0700282 args.arg3 = callback;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700283 args.argi1 = sequence;
284 mHandler.removeMessages(MyHandler.MSG_WRITE);
Svetoslav Ganova0027152013-06-25 14:59:53 -0700285 mHandler.obtainMessage(MyHandler.MSG_WRITE, args).sendToTarget();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700286 }
287
288 @Override
289 public void finish() {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700290 mHandler.sendEmptyMessage(MyHandler.MSG_FINISH);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700291 }
292
Svetoslav Ganova0027152013-06-25 14:59:53 -0700293 private boolean isFinished() {
294 return mDocumentAdapter == null;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700295 }
296
Svetoslav Ganova0027152013-06-25 14:59:53 -0700297 private void doFinish() {
298 mDocumentAdapter = null;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700299 mHandler = null;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700300 mLayoutOrWriteCancellation = null;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700301 }
302
303 private final class MyHandler extends Handler {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700304 public static final int MSG_START = 1;
305 public static final int MSG_LAYOUT = 2;
306 public static final int MSG_WRITE = 3;
307 public static final int MSG_FINISH = 4;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700308
309 public MyHandler(Looper looper) {
310 super(looper, null, true);
311 }
312
313 @Override
314 public void handleMessage(Message message) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700315 if (isFinished()) {
316 return;
317 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700318 switch (message.what) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700319 case MSG_START: {
320 mDocumentAdapter.onStart();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700321 } break;
322
Svetoslav Ganova0027152013-06-25 14:59:53 -0700323 case MSG_LAYOUT: {
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700324 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700325 PrintAttributes oldAttributes = (PrintAttributes) args.arg1;
326 PrintAttributes newAttributes = (PrintAttributes) args.arg2;
327 ILayoutResultCallback callback = (ILayoutResultCallback) args.arg3;
328 Bundle metadata = (Bundle) args.arg4;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700329 final int sequence = args.argi1;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700330 args.recycle();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700331
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700332 CancellationSignal cancellation = new CancellationSignal();
333 synchronized (mLock) {
334 mLayoutOrWriteCancellation = cancellation;
Svetoslav Ganova0027152013-06-25 14:59:53 -0700335 }
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700336
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700337 mDocumentAdapter.onLayout(oldAttributes, newAttributes, cancellation,
338 new MyLayoutResultCallback(callback, sequence), metadata);
Svetoslav Ganova0027152013-06-25 14:59:53 -0700339 } break;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700340
Svetoslav Ganova0027152013-06-25 14:59:53 -0700341 case MSG_WRITE: {
342 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700343 PageRange[] pages = (PageRange[]) args.arg1;
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700344 ParcelFileDescriptor fd = (ParcelFileDescriptor) args.arg2;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700345 IWriteResultCallback callback = (IWriteResultCallback) args.arg3;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700346 final int sequence = args.argi1;
Svetoslav Ganova0027152013-06-25 14:59:53 -0700347 args.recycle();
348
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700349 CancellationSignal cancellation = new CancellationSignal();
350 synchronized (mLock) {
351 mLayoutOrWriteCancellation = cancellation;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700352 }
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700353
354 mDocumentAdapter.onWrite(pages, fd, cancellation,
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700355 new MyWriteResultCallback(callback, fd, sequence));
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700356 } break;
357
Svetoslav Ganova0027152013-06-25 14:59:53 -0700358 case MSG_FINISH: {
359 mDocumentAdapter.onFinish();
360 doFinish();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700361 } break;
362
363 default: {
364 throw new IllegalArgumentException("Unknown message: "
365 + message.what);
366 }
367 }
368 }
369 }
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700370
371 private final class MyLayoutResultCallback extends LayoutResultCallback {
372 private ILayoutResultCallback mCallback;
373 private final int mSequence;
374
375 public MyLayoutResultCallback(ILayoutResultCallback callback,
376 int sequence) {
377 mCallback = callback;
378 mSequence = sequence;
379 }
380
381 @Override
382 public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
Svetoslav269403b2013-08-14 17:31:04 -0700383 if (info == null) {
384 throw new NullPointerException("document info cannot be null");
385 }
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700386 final ILayoutResultCallback callback;
387 synchronized (mLock) {
388 callback = mCallback;
389 clearLocked();
390 }
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700391 if (callback != null) {
392 try {
393 callback.onLayoutFinished(info, changed, mSequence);
394 } catch (RemoteException re) {
395 Log.e(LOG_TAG, "Error calling onLayoutFinished", re);
396 }
397 }
398 }
399
400 @Override
401 public void onLayoutFailed(CharSequence error) {
402 final ILayoutResultCallback callback;
403 synchronized (mLock) {
404 callback = mCallback;
405 clearLocked();
406 }
407 if (callback != null) {
408 try {
409 callback.onLayoutFailed(error, mSequence);
410 } catch (RemoteException re) {
411 Log.e(LOG_TAG, "Error calling onLayoutFailed", re);
412 }
413 }
414 }
415
416 @Override
417 public void onLayoutCancelled() {
418 synchronized (mLock) {
419 clearLocked();
420 }
421 }
422
423 private void clearLocked() {
424 mLayoutOrWriteCancellation = null;
425 mCallback = null;
426 }
427 }
428
429 private final class MyWriteResultCallback extends WriteResultCallback {
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700430 private ParcelFileDescriptor mFd;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700431 private int mSequence;
432 private IWriteResultCallback mCallback;
433
434 public MyWriteResultCallback(IWriteResultCallback callback,
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700435 ParcelFileDescriptor fd, int sequence) {
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700436 mFd = fd;
437 mSequence = sequence;
438 mCallback = callback;
439 }
440
441 @Override
442 public void onWriteFinished(PageRange[] pages) {
443 final IWriteResultCallback callback;
444 synchronized (mLock) {
445 callback = mCallback;
446 clearLocked();
447 }
448 if (pages == null) {
449 throw new IllegalArgumentException("pages cannot be null");
450 }
451 if (pages.length == 0) {
452 throw new IllegalArgumentException("pages cannot be empty");
453 }
454 if (callback != null) {
455 try {
456 callback.onWriteFinished(pages, mSequence);
457 } catch (RemoteException re) {
458 Log.e(LOG_TAG, "Error calling onWriteFinished", re);
459 }
460 }
461 }
462
463 @Override
464 public void onWriteFailed(CharSequence error) {
465 final IWriteResultCallback callback;
466 synchronized (mLock) {
467 callback = mCallback;
468 clearLocked();
469 }
470 if (callback != null) {
471 try {
472 callback.onWriteFailed(error, mSequence);
473 } catch (RemoteException re) {
474 Log.e(LOG_TAG, "Error calling onWriteFailed", re);
475 }
476 }
477 }
478
479 @Override
480 public void onWriteCancelled() {
481 synchronized (mLock) {
482 clearLocked();
483 }
484 }
485
486 private void clearLocked() {
487 mLayoutOrWriteCancellation = null;
488 IoUtils.closeQuietly(mFd);
489 mCallback = null;
490 mFd = null;
491 }
492 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700493 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700494}