PrintManager.java revision d26d4898fcc9b78f4b66118895c375384098205e
1/* 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; 22import android.os.Bundle; 23import android.os.CancellationSignal; 24import android.os.Handler; 25import android.os.Looper; 26import android.os.Message; 27import android.os.ParcelFileDescriptor; 28import android.os.RemoteException; 29import android.print.PrintDocumentAdapter.LayoutResultCallback; 30import android.print.PrintDocumentAdapter.WriteResultCallback; 31import android.text.TextUtils; 32import android.util.Log; 33 34import com.android.internal.os.SomeArgs; 35 36import libcore.io.IoUtils; 37 38import java.io.File; 39import 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 /** 74 * 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. 107 * @return An instance if the caller has the permission to access 108 * 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 116 PrintJobInfo getPrintJobInfo(int printJobId) { 117 try { 118 return mService.getPrintJobInfo(printJobId, mAppId, mUserId); 119 } catch (RemoteException re) { 120 Log.e(LOG_TAG, "Error getting a print job info:" + printJobId, re); 121 } 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 { 134 List<PrintJobInfo> printJobInfos = mService.getPrintJobInfos(mAppId, mUserId); 135 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) { 145 Log.e(LOG_TAG, "Error getting print jobs", re); 146 } 147 return Collections.emptyList(); 148 } 149 150 void cancelPrintJob(int printJobId) { 151 try { 152 mService.cancelPrintJob(printJobId, mAppId, mUserId); 153 } catch (RemoteException re) { 154 Log.e(LOG_TAG, "Error cancleing a print job: " + printJobId, re); 155 } 156 } 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. 163 * @param documentInfo Information about the printed document. 164 * @param attributes The default print job attributes. 165 * @return The created print job on success or null on failure. 166 * 167 * @see PrintJob 168 */ 169 public PrintJob print(String printJobName, File pdfFile, PrintDocumentInfo documentInfo, 170 PrintAttributes attributes) { 171 PrintFileDocumentAdapter documentAdapter = new PrintFileDocumentAdapter( 172 mContext, pdfFile, documentInfo); 173 return print(printJobName, documentAdapter, attributes); 174 } 175 176 /** 177 * Creates a print job for printing a {@link PrintDocumentAdapter} with default print 178 * attributes. 179 * 180 * @param printJobName A name for the new print job. 181 * @param documentAdapter An adapter that emits the document to print. 182 * @param attributes The default print job attributes. 183 * @return The created print job on success or null on failure. 184 * 185 * @see PrintJob 186 */ 187 public PrintJob print(String printJobName, PrintDocumentAdapter documentAdapter, 188 PrintAttributes attributes) { 189 if (TextUtils.isEmpty(printJobName)) { 190 throw new IllegalArgumentException("priintJobName cannot be empty"); 191 } 192 PrintDocumentAdapterDelegate delegate = new PrintDocumentAdapterDelegate(documentAdapter, 193 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 206 /** 207 * @hide 208 */ 209 public PrinterDiscoverySession createPrinterDiscoverySession() { 210 return new PrinterDiscoverySession(mService, mContext, mUserId); 211 } 212 213 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 233 private static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub { 234 235 private final Object mLock = new Object(); 236 237 private CancellationSignal mLayoutOrWriteCancellation; 238 239 private PrintDocumentAdapter mDocumentAdapter; // Strong reference OK - cleared in finish() 240 241 private Handler mHandler; // Strong reference OK - cleared in finish() 242 243 public PrintDocumentAdapterDelegate(PrintDocumentAdapter documentAdapter, Looper looper) { 244 mDocumentAdapter = documentAdapter; 245 mHandler = new MyHandler(looper); 246 } 247 248 @Override 249 public void start() { 250 mHandler.sendEmptyMessage(MyHandler.MSG_START); 251 } 252 253 @Override 254 public void layout(PrintAttributes oldAttributes, PrintAttributes newAttributes, 255 ILayoutResultCallback callback, Bundle metadata, int sequence) { 256 synchronized (mLock) { 257 if (mLayoutOrWriteCancellation != null) { 258 mLayoutOrWriteCancellation.cancel(); 259 } 260 } 261 SomeArgs args = SomeArgs.obtain(); 262 args.arg1 = oldAttributes; 263 args.arg2 = newAttributes; 264 args.arg3 = callback; 265 args.arg4 = metadata; 266 args.argi1 = sequence; 267 mHandler.removeMessages(MyHandler.MSG_LAYOUT); 268 mHandler.obtainMessage(MyHandler.MSG_LAYOUT, args).sendToTarget(); 269 } 270 271 @Override 272 public void write(PageRange[] pages, ParcelFileDescriptor fd, 273 IWriteResultCallback callback, int sequence) { 274 synchronized (mLock) { 275 if (mLayoutOrWriteCancellation != null) { 276 mLayoutOrWriteCancellation.cancel(); 277 } 278 } 279 SomeArgs args = SomeArgs.obtain(); 280 args.arg1 = pages; 281 args.arg2 = fd; 282 args.arg3 = callback; 283 args.argi1 = sequence; 284 mHandler.removeMessages(MyHandler.MSG_WRITE); 285 mHandler.obtainMessage(MyHandler.MSG_WRITE, args).sendToTarget(); 286 } 287 288 @Override 289 public void finish() { 290 mHandler.sendEmptyMessage(MyHandler.MSG_FINISH); 291 } 292 293 private boolean isFinished() { 294 return mDocumentAdapter == null; 295 } 296 297 private void doFinish() { 298 mDocumentAdapter = null; 299 mHandler = null; 300 mLayoutOrWriteCancellation = null; 301 } 302 303 private final class MyHandler extends Handler { 304 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; 308 309 public MyHandler(Looper looper) { 310 super(looper, null, true); 311 } 312 313 @Override 314 public void handleMessage(Message message) { 315 if (isFinished()) { 316 return; 317 } 318 switch (message.what) { 319 case MSG_START: { 320 mDocumentAdapter.onStart(); 321 } break; 322 323 case MSG_LAYOUT: { 324 SomeArgs args = (SomeArgs) message.obj; 325 PrintAttributes oldAttributes = (PrintAttributes) args.arg1; 326 PrintAttributes newAttributes = (PrintAttributes) args.arg2; 327 ILayoutResultCallback callback = (ILayoutResultCallback) args.arg3; 328 Bundle metadata = (Bundle) args.arg4; 329 final int sequence = args.argi1; 330 args.recycle(); 331 332 CancellationSignal cancellation = new CancellationSignal(); 333 synchronized (mLock) { 334 mLayoutOrWriteCancellation = cancellation; 335 } 336 337 mDocumentAdapter.onLayout(oldAttributes, newAttributes, cancellation, 338 new MyLayoutResultCallback(callback, sequence), metadata); 339 } break; 340 341 case MSG_WRITE: { 342 SomeArgs args = (SomeArgs) message.obj; 343 PageRange[] pages = (PageRange[]) args.arg1; 344 ParcelFileDescriptor fd = (ParcelFileDescriptor) args.arg2; 345 IWriteResultCallback callback = (IWriteResultCallback) args.arg3; 346 final int sequence = args.argi1; 347 args.recycle(); 348 349 CancellationSignal cancellation = new CancellationSignal(); 350 synchronized (mLock) { 351 mLayoutOrWriteCancellation = cancellation; 352 } 353 354 mDocumentAdapter.onWrite(pages, fd, cancellation, 355 new MyWriteResultCallback(callback, fd, sequence)); 356 } break; 357 358 case MSG_FINISH: { 359 mDocumentAdapter.onFinish(); 360 doFinish(); 361 } break; 362 363 default: { 364 throw new IllegalArgumentException("Unknown message: " 365 + message.what); 366 } 367 } 368 } 369 } 370 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) { 383 if (info == null) { 384 throw new NullPointerException("document info cannot be null"); 385 } 386 final ILayoutResultCallback callback; 387 synchronized (mLock) { 388 callback = mCallback; 389 clearLocked(); 390 } 391 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 { 430 private ParcelFileDescriptor mFd; 431 private int mSequence; 432 private IWriteResultCallback mCallback; 433 434 public MyWriteResultCallback(IWriteResultCallback callback, 435 ParcelFileDescriptor fd, int sequence) { 436 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 } 493 } 494} 495