PrintManager.java revision 860f8a6b663ca96d30d17da09eca8caf065aae62
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.printservice.PrintServiceInfo; 32import android.text.TextUtils; 33import android.util.Log; 34 35import com.android.internal.os.SomeArgs; 36 37import libcore.io.IoUtils; 38 39import java.io.File; 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 /** 75 * 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. 108 * @return An instance if the caller has the permission to access 109 * 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 117 PrintJobInfo getPrintJobInfo(int printJobId) { 118 try { 119 return mService.getPrintJobInfo(printJobId, mAppId, mUserId); 120 } catch (RemoteException re) { 121 Log.e(LOG_TAG, "Error getting a print job info:" + printJobId, re); 122 } 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 { 135 List<PrintJobInfo> printJobInfos = mService.getPrintJobInfos(mAppId, mUserId); 136 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) { 146 Log.e(LOG_TAG, "Error getting print jobs", re); 147 } 148 return Collections.emptyList(); 149 } 150 151 void cancelPrintJob(int printJobId) { 152 try { 153 mService.cancelPrintJob(printJobId, mAppId, mUserId); 154 } catch (RemoteException re) { 155 Log.e(LOG_TAG, "Error cancleing a print job: " + printJobId, re); 156 } 157 } 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 documentInfo Information about the printed document. 165 * @param attributes The default print job attributes. 166 * @return The created print job on success or null on failure. 167 * 168 * @see PrintJob 169 */ 170 public PrintJob print(String printJobName, File pdfFile, PrintDocumentInfo documentInfo, 171 PrintAttributes attributes) { 172 PrintFileDocumentAdapter documentAdapter = new PrintFileDocumentAdapter( 173 mContext, pdfFile, documentInfo); 174 return print(printJobName, documentAdapter, attributes); 175 } 176 177 /** 178 * Creates a print job for printing a {@link PrintDocumentAdapter} with default print 179 * attributes. 180 * 181 * @param printJobName A name for the new print job. 182 * @param documentAdapter An adapter that emits the document to print. 183 * @param attributes The default print job attributes. 184 * @return The created print job on success or null on failure. 185 * 186 * @see PrintJob 187 */ 188 public PrintJob print(String printJobName, PrintDocumentAdapter documentAdapter, 189 PrintAttributes attributes) { 190 if (TextUtils.isEmpty(printJobName)) { 191 throw new IllegalArgumentException("priintJobName cannot be empty"); 192 } 193 PrintDocumentAdapterDelegate delegate = new PrintDocumentAdapterDelegate(documentAdapter, 194 mContext.getMainLooper()); 195 try { 196 PrintJobInfo printJob = mService.print(printJobName, mPrintClient, delegate, 197 attributes, mAppId, mUserId); 198 if (printJob != null) { 199 return new PrintJob(printJob, this); 200 } 201 } catch (RemoteException re) { 202 Log.e(LOG_TAG, "Error creating a print job", re); 203 } 204 return null; 205 } 206 207 /** 208 * Gets the list of enabled print services. 209 * 210 * @return The enabled service list or an empty list. 211 * 212 * @hide 213 */ 214 public List<PrintServiceInfo> getEnabledPrintServices() { 215 try { 216 List<PrintServiceInfo> enabledServices = mService.getEnabledPrintServices(mUserId); 217 if (enabledServices != null) { 218 return enabledServices; 219 } 220 } catch (RemoteException re) { 221 Log.e(LOG_TAG, "Error getting the enalbed print services", re); 222 } 223 return Collections.emptyList(); 224 } 225 226 /** 227 * @hide 228 */ 229 public PrinterDiscoverySession createPrinterDiscoverySession() { 230 return new PrinterDiscoverySession(mService, mContext, mUserId); 231 } 232 233 private static final class PrintClient extends IPrintClient.Stub { 234 235 private final WeakReference<PrintManager> mWeakPrintManager; 236 237 public PrintClient(PrintManager manager) { 238 mWeakPrintManager = new WeakReference<PrintManager>(manager); 239 } 240 241 @Override 242 public void startPrintJobConfigActivity(IntentSender intent) { 243 PrintManager manager = mWeakPrintManager.get(); 244 if (manager != null) { 245 SomeArgs args = SomeArgs.obtain(); 246 args.arg1 = manager.mContext; 247 args.arg2 = intent; 248 manager.mHandler.obtainMessage(0, args).sendToTarget(); 249 } 250 } 251 } 252 253 private static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub { 254 255 private final Object mLock = new Object(); 256 257 private CancellationSignal mLayoutOrWriteCancellation; 258 259 private PrintDocumentAdapter mDocumentAdapter; // Strong reference OK - cleared in finish() 260 261 private Handler mHandler; // Strong reference OK - cleared in finish() 262 263 public PrintDocumentAdapterDelegate(PrintDocumentAdapter documentAdapter, Looper looper) { 264 mDocumentAdapter = documentAdapter; 265 mHandler = new MyHandler(looper); 266 } 267 268 @Override 269 public void start() { 270 mHandler.sendEmptyMessage(MyHandler.MSG_START); 271 } 272 273 @Override 274 public void layout(PrintAttributes oldAttributes, PrintAttributes newAttributes, 275 ILayoutResultCallback callback, Bundle metadata, int sequence) { 276 synchronized (mLock) { 277 if (mLayoutOrWriteCancellation != null) { 278 mLayoutOrWriteCancellation.cancel(); 279 } 280 } 281 SomeArgs args = SomeArgs.obtain(); 282 args.arg1 = oldAttributes; 283 args.arg2 = newAttributes; 284 args.arg3 = callback; 285 args.arg4 = metadata; 286 args.argi1 = sequence; 287 mHandler.removeMessages(MyHandler.MSG_LAYOUT); 288 mHandler.obtainMessage(MyHandler.MSG_LAYOUT, args).sendToTarget(); 289 } 290 291 @Override 292 public void write(PageRange[] pages, ParcelFileDescriptor fd, 293 IWriteResultCallback callback, int sequence) { 294 synchronized (mLock) { 295 if (mLayoutOrWriteCancellation != null) { 296 mLayoutOrWriteCancellation.cancel(); 297 } 298 } 299 SomeArgs args = SomeArgs.obtain(); 300 args.arg1 = pages; 301 args.arg2 = fd; 302 args.arg3 = callback; 303 args.argi1 = sequence; 304 mHandler.removeMessages(MyHandler.MSG_WRITE); 305 mHandler.obtainMessage(MyHandler.MSG_WRITE, args).sendToTarget(); 306 } 307 308 @Override 309 public void finish() { 310 mHandler.sendEmptyMessage(MyHandler.MSG_FINISH); 311 } 312 313 private boolean isFinished() { 314 return mDocumentAdapter == null; 315 } 316 317 private void doFinish() { 318 mDocumentAdapter = null; 319 mHandler = null; 320 mLayoutOrWriteCancellation = null; 321 } 322 323 private final class MyHandler extends Handler { 324 public static final int MSG_START = 1; 325 public static final int MSG_LAYOUT = 2; 326 public static final int MSG_WRITE = 3; 327 public static final int MSG_FINISH = 4; 328 329 public MyHandler(Looper looper) { 330 super(looper, null, true); 331 } 332 333 @Override 334 public void handleMessage(Message message) { 335 if (isFinished()) { 336 return; 337 } 338 switch (message.what) { 339 case MSG_START: { 340 mDocumentAdapter.onStart(); 341 } break; 342 343 case MSG_LAYOUT: { 344 SomeArgs args = (SomeArgs) message.obj; 345 PrintAttributes oldAttributes = (PrintAttributes) args.arg1; 346 PrintAttributes newAttributes = (PrintAttributes) args.arg2; 347 ILayoutResultCallback callback = (ILayoutResultCallback) args.arg3; 348 Bundle metadata = (Bundle) args.arg4; 349 final int sequence = args.argi1; 350 args.recycle(); 351 352 CancellationSignal cancellation = new CancellationSignal(); 353 synchronized (mLock) { 354 mLayoutOrWriteCancellation = cancellation; 355 } 356 357 mDocumentAdapter.onLayout(oldAttributes, newAttributes, cancellation, 358 new MyLayoutResultCallback(callback, sequence), metadata); 359 } break; 360 361 case MSG_WRITE: { 362 SomeArgs args = (SomeArgs) message.obj; 363 PageRange[] pages = (PageRange[]) args.arg1; 364 ParcelFileDescriptor fd = (ParcelFileDescriptor) args.arg2; 365 IWriteResultCallback callback = (IWriteResultCallback) args.arg3; 366 final int sequence = args.argi1; 367 args.recycle(); 368 369 CancellationSignal cancellation = new CancellationSignal(); 370 synchronized (mLock) { 371 mLayoutOrWriteCancellation = cancellation; 372 } 373 374 mDocumentAdapter.onWrite(pages, fd, cancellation, 375 new MyWriteResultCallback(callback, fd, sequence)); 376 } break; 377 378 case MSG_FINISH: { 379 mDocumentAdapter.onFinish(); 380 doFinish(); 381 } break; 382 383 default: { 384 throw new IllegalArgumentException("Unknown message: " 385 + message.what); 386 } 387 } 388 } 389 } 390 391 private final class MyLayoutResultCallback extends LayoutResultCallback { 392 private ILayoutResultCallback mCallback; 393 private final int mSequence; 394 395 public MyLayoutResultCallback(ILayoutResultCallback callback, 396 int sequence) { 397 mCallback = callback; 398 mSequence = sequence; 399 } 400 401 @Override 402 public void onLayoutFinished(PrintDocumentInfo info, boolean changed) { 403 if (info == null) { 404 throw new NullPointerException("document info cannot be null"); 405 } 406 final ILayoutResultCallback callback; 407 synchronized (mLock) { 408 callback = mCallback; 409 clearLocked(); 410 } 411 if (callback != null) { 412 try { 413 callback.onLayoutFinished(info, changed, mSequence); 414 } catch (RemoteException re) { 415 Log.e(LOG_TAG, "Error calling onLayoutFinished", re); 416 } 417 } 418 } 419 420 @Override 421 public void onLayoutFailed(CharSequence error) { 422 final ILayoutResultCallback callback; 423 synchronized (mLock) { 424 callback = mCallback; 425 clearLocked(); 426 } 427 if (callback != null) { 428 try { 429 callback.onLayoutFailed(error, mSequence); 430 } catch (RemoteException re) { 431 Log.e(LOG_TAG, "Error calling onLayoutFailed", re); 432 } 433 } 434 } 435 436 @Override 437 public void onLayoutCancelled() { 438 synchronized (mLock) { 439 clearLocked(); 440 } 441 } 442 443 private void clearLocked() { 444 mLayoutOrWriteCancellation = null; 445 mCallback = null; 446 } 447 } 448 449 private final class MyWriteResultCallback extends WriteResultCallback { 450 private ParcelFileDescriptor mFd; 451 private int mSequence; 452 private IWriteResultCallback mCallback; 453 454 public MyWriteResultCallback(IWriteResultCallback callback, 455 ParcelFileDescriptor fd, int sequence) { 456 mFd = fd; 457 mSequence = sequence; 458 mCallback = callback; 459 } 460 461 @Override 462 public void onWriteFinished(PageRange[] pages) { 463 final IWriteResultCallback callback; 464 synchronized (mLock) { 465 callback = mCallback; 466 clearLocked(); 467 } 468 if (pages == null) { 469 throw new IllegalArgumentException("pages cannot be null"); 470 } 471 if (pages.length == 0) { 472 throw new IllegalArgumentException("pages cannot be empty"); 473 } 474 if (callback != null) { 475 try { 476 callback.onWriteFinished(pages, mSequence); 477 } catch (RemoteException re) { 478 Log.e(LOG_TAG, "Error calling onWriteFinished", re); 479 } 480 } 481 } 482 483 @Override 484 public void onWriteFailed(CharSequence error) { 485 final IWriteResultCallback callback; 486 synchronized (mLock) { 487 callback = mCallback; 488 clearLocked(); 489 } 490 if (callback != null) { 491 try { 492 callback.onWriteFailed(error, mSequence); 493 } catch (RemoteException re) { 494 Log.e(LOG_TAG, "Error calling onWriteFailed", re); 495 } 496 } 497 } 498 499 @Override 500 public void onWriteCancelled() { 501 synchronized (mLock) { 502 clearLocked(); 503 } 504 } 505 506 private void clearLocked() { 507 mLayoutOrWriteCancellation = null; 508 IoUtils.closeQuietly(mFd); 509 mCallback = null; 510 mFd = null; 511 } 512 } 513 } 514} 515