blob: 54291558324283be95b847d11f3fa634f1d06b94 [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 Ganov860f8a62013-09-14 00:59:03 -070031import android.printservice.PrintServiceInfo;
Svetoslav Ganov88d19912013-07-22 12:32:03 -070032import android.text.TextUtils;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070033import android.util.Log;
34
35import com.android.internal.os.SomeArgs;
36
37import libcore.io.IoUtils;
38
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
Svetoslav2fbd2a72013-09-16 17:53:51 -0700116 PrintJobInfo getPrintJobInfo(PrintJobId 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
Svetoslav2fbd2a72013-09-16 17:53:51 -0700150 void cancelPrintJob(PrintJobId 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 /**
Svetoslav Ganova0027152013-06-25 14:59:53 -0700159 * Creates a print job for printing a {@link PrintDocumentAdapter} with default print
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700160 * attributes.
161 *
162 * @param printJobName A name for the new print job.
Svetoslav Ganova0027152013-06-25 14:59:53 -0700163 * @param documentAdapter An adapter that emits the document to print.
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 Ganova0027152013-06-25 14:59:53 -0700169 public PrintJob print(String printJobName, PrintDocumentAdapter documentAdapter,
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700170 PrintAttributes attributes) {
Svetoslav Ganov88d19912013-07-22 12:32:03 -0700171 if (TextUtils.isEmpty(printJobName)) {
172 throw new IllegalArgumentException("priintJobName cannot be empty");
173 }
Svetoslav Ganova0027152013-06-25 14:59:53 -0700174 PrintDocumentAdapterDelegate delegate = new PrintDocumentAdapterDelegate(documentAdapter,
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700175 mContext.getMainLooper());
176 try {
177 PrintJobInfo printJob = mService.print(printJobName, mPrintClient, delegate,
178 attributes, mAppId, mUserId);
179 if (printJob != null) {
180 return new PrintJob(printJob, this);
181 }
182 } catch (RemoteException re) {
183 Log.e(LOG_TAG, "Error creating a print job", re);
184 }
185 return null;
186 }
187
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700188 /**
Svetoslav Ganov860f8a62013-09-14 00:59:03 -0700189 * Gets the list of enabled print services.
190 *
191 * @return The enabled service list or an empty list.
192 *
193 * @hide
194 */
195 public List<PrintServiceInfo> getEnabledPrintServices() {
196 try {
197 List<PrintServiceInfo> enabledServices = mService.getEnabledPrintServices(mUserId);
198 if (enabledServices != null) {
199 return enabledServices;
200 }
201 } catch (RemoteException re) {
202 Log.e(LOG_TAG, "Error getting the enalbed print services", re);
203 }
204 return Collections.emptyList();
205 }
206
207 /**
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700208 * @hide
209 */
210 public PrinterDiscoverySession createPrinterDiscoverySession() {
211 return new PrinterDiscoverySession(mService, mContext, mUserId);
212 }
213
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700214 private static final class PrintClient extends IPrintClient.Stub {
215
216 private final WeakReference<PrintManager> mWeakPrintManager;
217
218 public PrintClient(PrintManager manager) {
219 mWeakPrintManager = new WeakReference<PrintManager>(manager);
220 }
221
222 @Override
223 public void startPrintJobConfigActivity(IntentSender intent) {
224 PrintManager manager = mWeakPrintManager.get();
225 if (manager != null) {
226 SomeArgs args = SomeArgs.obtain();
227 args.arg1 = manager.mContext;
228 args.arg2 = intent;
229 manager.mHandler.obtainMessage(0, args).sendToTarget();
230 }
231 }
232 }
233
Svetoslav Ganova0027152013-06-25 14:59:53 -0700234 private static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub {
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700235
236 private final Object mLock = new Object();
237
238 private CancellationSignal mLayoutOrWriteCancellation;
239
Svetoslav Ganova0027152013-06-25 14:59:53 -0700240 private PrintDocumentAdapter mDocumentAdapter; // Strong reference OK - cleared in finish()
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700241
Svetoslav Ganova0027152013-06-25 14:59:53 -0700242 private Handler mHandler; // Strong reference OK - cleared in finish()
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700243
Svetoslav Ganova0027152013-06-25 14:59:53 -0700244 public PrintDocumentAdapterDelegate(PrintDocumentAdapter documentAdapter, Looper looper) {
245 mDocumentAdapter = documentAdapter;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700246 mHandler = new MyHandler(looper);
247 }
248
249 @Override
250 public void start() {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700251 mHandler.sendEmptyMessage(MyHandler.MSG_START);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700252 }
253
254 @Override
Svetoslav62836082013-07-17 14:52:35 -0700255 public void layout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700256 ILayoutResultCallback callback, Bundle metadata, int sequence) {
257 synchronized (mLock) {
258 if (mLayoutOrWriteCancellation != null) {
259 mLayoutOrWriteCancellation.cancel();
260 }
261 }
Svetoslav Ganova0027152013-06-25 14:59:53 -0700262 SomeArgs args = SomeArgs.obtain();
263 args.arg1 = oldAttributes;
264 args.arg2 = newAttributes;
265 args.arg3 = callback;
Svetoslav62836082013-07-17 14:52:35 -0700266 args.arg4 = metadata;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700267 args.argi1 = sequence;
268 mHandler.removeMessages(MyHandler.MSG_LAYOUT);
Svetoslav Ganova0027152013-06-25 14:59:53 -0700269 mHandler.obtainMessage(MyHandler.MSG_LAYOUT, args).sendToTarget();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700270 }
271
272 @Override
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700273 public void write(PageRange[] pages, ParcelFileDescriptor fd,
274 IWriteResultCallback callback, int sequence) {
275 synchronized (mLock) {
276 if (mLayoutOrWriteCancellation != null) {
277 mLayoutOrWriteCancellation.cancel();
278 }
279 }
Svetoslav Ganova0027152013-06-25 14:59:53 -0700280 SomeArgs args = SomeArgs.obtain();
281 args.arg1 = pages;
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700282 args.arg2 = fd;
Svetoslav Ganova0027152013-06-25 14:59:53 -0700283 args.arg3 = callback;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700284 args.argi1 = sequence;
285 mHandler.removeMessages(MyHandler.MSG_WRITE);
Svetoslav Ganova0027152013-06-25 14:59:53 -0700286 mHandler.obtainMessage(MyHandler.MSG_WRITE, args).sendToTarget();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700287 }
288
289 @Override
290 public void finish() {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700291 mHandler.sendEmptyMessage(MyHandler.MSG_FINISH);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700292 }
293
Svetoslav Ganova0027152013-06-25 14:59:53 -0700294 private boolean isFinished() {
295 return mDocumentAdapter == null;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700296 }
297
Svetoslav Ganova0027152013-06-25 14:59:53 -0700298 private void doFinish() {
299 mDocumentAdapter = null;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700300 mHandler = null;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700301 mLayoutOrWriteCancellation = null;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700302 }
303
304 private final class MyHandler extends Handler {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700305 public static final int MSG_START = 1;
306 public static final int MSG_LAYOUT = 2;
307 public static final int MSG_WRITE = 3;
308 public static final int MSG_FINISH = 4;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700309
310 public MyHandler(Looper looper) {
311 super(looper, null, true);
312 }
313
314 @Override
315 public void handleMessage(Message message) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700316 if (isFinished()) {
317 return;
318 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700319 switch (message.what) {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700320 case MSG_START: {
321 mDocumentAdapter.onStart();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700322 } break;
323
Svetoslav Ganova0027152013-06-25 14:59:53 -0700324 case MSG_LAYOUT: {
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700325 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700326 PrintAttributes oldAttributes = (PrintAttributes) args.arg1;
327 PrintAttributes newAttributes = (PrintAttributes) args.arg2;
328 ILayoutResultCallback callback = (ILayoutResultCallback) args.arg3;
329 Bundle metadata = (Bundle) args.arg4;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700330 final int sequence = args.argi1;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700331 args.recycle();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700332
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700333 CancellationSignal cancellation = new CancellationSignal();
334 synchronized (mLock) {
335 mLayoutOrWriteCancellation = cancellation;
Svetoslav Ganova0027152013-06-25 14:59:53 -0700336 }
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700337
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700338 mDocumentAdapter.onLayout(oldAttributes, newAttributes, cancellation,
339 new MyLayoutResultCallback(callback, sequence), metadata);
Svetoslav Ganova0027152013-06-25 14:59:53 -0700340 } break;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700341
Svetoslav Ganova0027152013-06-25 14:59:53 -0700342 case MSG_WRITE: {
343 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700344 PageRange[] pages = (PageRange[]) args.arg1;
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700345 ParcelFileDescriptor fd = (ParcelFileDescriptor) args.arg2;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700346 IWriteResultCallback callback = (IWriteResultCallback) args.arg3;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700347 final int sequence = args.argi1;
Svetoslav Ganova0027152013-06-25 14:59:53 -0700348 args.recycle();
349
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700350 CancellationSignal cancellation = new CancellationSignal();
351 synchronized (mLock) {
352 mLayoutOrWriteCancellation = cancellation;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700353 }
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700354
355 mDocumentAdapter.onWrite(pages, fd, cancellation,
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700356 new MyWriteResultCallback(callback, fd, sequence));
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700357 } break;
358
Svetoslav Ganova0027152013-06-25 14:59:53 -0700359 case MSG_FINISH: {
360 mDocumentAdapter.onFinish();
361 doFinish();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700362 } break;
363
364 default: {
365 throw new IllegalArgumentException("Unknown message: "
366 + message.what);
367 }
368 }
369 }
370 }
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700371
372 private final class MyLayoutResultCallback extends LayoutResultCallback {
373 private ILayoutResultCallback mCallback;
374 private final int mSequence;
375
376 public MyLayoutResultCallback(ILayoutResultCallback callback,
377 int sequence) {
378 mCallback = callback;
379 mSequence = sequence;
380 }
381
382 @Override
383 public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
Svetoslav269403b2013-08-14 17:31:04 -0700384 if (info == null) {
385 throw new NullPointerException("document info cannot be null");
386 }
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700387 final ILayoutResultCallback callback;
388 synchronized (mLock) {
389 callback = mCallback;
390 clearLocked();
391 }
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700392 if (callback != null) {
393 try {
394 callback.onLayoutFinished(info, changed, mSequence);
395 } catch (RemoteException re) {
396 Log.e(LOG_TAG, "Error calling onLayoutFinished", re);
397 }
398 }
399 }
400
401 @Override
402 public void onLayoutFailed(CharSequence error) {
403 final ILayoutResultCallback callback;
404 synchronized (mLock) {
405 callback = mCallback;
406 clearLocked();
407 }
408 if (callback != null) {
409 try {
410 callback.onLayoutFailed(error, mSequence);
411 } catch (RemoteException re) {
412 Log.e(LOG_TAG, "Error calling onLayoutFailed", re);
413 }
414 }
415 }
416
417 @Override
418 public void onLayoutCancelled() {
419 synchronized (mLock) {
420 clearLocked();
421 }
422 }
423
424 private void clearLocked() {
425 mLayoutOrWriteCancellation = null;
426 mCallback = null;
427 }
428 }
429
430 private final class MyWriteResultCallback extends WriteResultCallback {
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700431 private ParcelFileDescriptor mFd;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700432 private int mSequence;
433 private IWriteResultCallback mCallback;
434
435 public MyWriteResultCallback(IWriteResultCallback callback,
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700436 ParcelFileDescriptor fd, int sequence) {
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700437 mFd = fd;
438 mSequence = sequence;
439 mCallback = callback;
440 }
441
442 @Override
443 public void onWriteFinished(PageRange[] pages) {
444 final IWriteResultCallback callback;
445 synchronized (mLock) {
446 callback = mCallback;
447 clearLocked();
448 }
449 if (pages == null) {
450 throw new IllegalArgumentException("pages cannot be null");
451 }
452 if (pages.length == 0) {
453 throw new IllegalArgumentException("pages cannot be empty");
454 }
455 if (callback != null) {
456 try {
457 callback.onWriteFinished(pages, mSequence);
458 } catch (RemoteException re) {
459 Log.e(LOG_TAG, "Error calling onWriteFinished", re);
460 }
461 }
462 }
463
464 @Override
465 public void onWriteFailed(CharSequence error) {
466 final IWriteResultCallback callback;
467 synchronized (mLock) {
468 callback = mCallback;
469 clearLocked();
470 }
471 if (callback != null) {
472 try {
473 callback.onWriteFailed(error, mSequence);
474 } catch (RemoteException re) {
475 Log.e(LOG_TAG, "Error calling onWriteFailed", re);
476 }
477 }
478 }
479
480 @Override
481 public void onWriteCancelled() {
482 synchronized (mLock) {
483 clearLocked();
484 }
485 }
486
487 private void clearLocked() {
488 mLayoutOrWriteCancellation = null;
489 IoUtils.closeQuietly(mFd);
490 mCallback = null;
491 mFd = null;
492 }
493 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700494 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700495}