PrintManager.java revision 6283608e0bd40548742839f5a8b02f7e5c9c5c7c
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.ICancellationSignal; 26import android.os.Looper; 27import android.os.Message; 28import android.os.ParcelFileDescriptor; 29import android.os.RemoteException; 30import android.print.PrintDocumentAdapter.LayoutResultCallback; 31import android.print.PrintDocumentAdapter.WriteResultCallback; 32import 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 /** 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 attributes The default print job attributes. 165 * @return The created print job. 166 * 167 * @see PrintJob 168 */ 169 public PrintJob print(String printJobName, File pdfFile, PrintAttributes attributes) { 170 FileDocumentAdapter documentAdapter = new FileDocumentAdapter(mContext, pdfFile); 171 return print(printJobName, documentAdapter, attributes); 172 } 173 174 /** 175 * Creates a print job for printing a {@link PrintDocumentAdapter} with default print 176 * attributes. 177 * 178 * @param printJobName A name for the new print job. 179 * @param documentAdapter An adapter that emits the document to print. 180 * @param attributes The default print job attributes. 181 * @return The created print job. 182 * 183 * @see PrintJob 184 */ 185 public PrintJob print(String printJobName, PrintDocumentAdapter documentAdapter, 186 PrintAttributes attributes) { 187 PrintDocumentAdapterDelegate delegate = new PrintDocumentAdapterDelegate(documentAdapter, 188 mContext.getMainLooper()); 189 try { 190 PrintJobInfo printJob = mService.print(printJobName, mPrintClient, delegate, 191 attributes, mAppId, mUserId); 192 if (printJob != null) { 193 return new PrintJob(printJob, this); 194 } 195 } catch (RemoteException re) { 196 Log.e(LOG_TAG, "Error creating a print job", re); 197 } 198 return null; 199 } 200 201 private static final class PrintClient extends IPrintClient.Stub { 202 203 private final WeakReference<PrintManager> mWeakPrintManager; 204 205 public PrintClient(PrintManager manager) { 206 mWeakPrintManager = new WeakReference<PrintManager>(manager); 207 } 208 209 @Override 210 public void startPrintJobConfigActivity(IntentSender intent) { 211 PrintManager manager = mWeakPrintManager.get(); 212 if (manager != null) { 213 SomeArgs args = SomeArgs.obtain(); 214 args.arg1 = manager.mContext; 215 args.arg2 = intent; 216 manager.mHandler.obtainMessage(0, args).sendToTarget(); 217 } 218 } 219 } 220 221 private static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub { 222 private PrintDocumentAdapter mDocumentAdapter; // Strong reference OK - cleared in finish() 223 224 private Handler mHandler; // Strong reference OK - cleared in finish() 225 226 public PrintDocumentAdapterDelegate(PrintDocumentAdapter documentAdapter, Looper looper) { 227 mDocumentAdapter = documentAdapter; 228 mHandler = new MyHandler(looper); 229 } 230 231 @Override 232 public void start() { 233 mHandler.sendEmptyMessage(MyHandler.MSG_START); 234 } 235 236 @Override 237 public void layout(PrintAttributes oldAttributes, PrintAttributes newAttributes, 238 ILayoutResultCallback callback, Bundle metadata) { 239 SomeArgs args = SomeArgs.obtain(); 240 args.arg1 = oldAttributes; 241 args.arg2 = newAttributes; 242 args.arg3 = callback; 243 args.arg4 = metadata; 244 mHandler.obtainMessage(MyHandler.MSG_LAYOUT, args).sendToTarget(); 245 } 246 247 @Override 248 public void write(List<PageRange> pages, ParcelFileDescriptor fd, 249 IWriteResultCallback callback) { 250 SomeArgs args = SomeArgs.obtain(); 251 args.arg1 = pages; 252 args.arg2 = fd.getFileDescriptor(); 253 args.arg3 = callback; 254 mHandler.obtainMessage(MyHandler.MSG_WRITE, args).sendToTarget(); 255 } 256 257 @Override 258 public void finish() { 259 mHandler.sendEmptyMessage(MyHandler.MSG_FINISH); 260 } 261 262 private boolean isFinished() { 263 return mDocumentAdapter == null; 264 } 265 266 private void doFinish() { 267 mDocumentAdapter = null; 268 mHandler = null; 269 } 270 271 private final class MyHandler extends Handler { 272 public static final int MSG_START = 1; 273 public static final int MSG_LAYOUT = 2; 274 public static final int MSG_WRITE = 3; 275 public static final int MSG_FINISH = 4; 276 277 public MyHandler(Looper looper) { 278 super(looper, null, true); 279 } 280 281 @Override 282 @SuppressWarnings("unchecked") 283 public void handleMessage(Message message) { 284 if (isFinished()) { 285 return; 286 } 287 switch (message.what) { 288 case MSG_START: { 289 mDocumentAdapter.onStart(); 290 } break; 291 292 case MSG_LAYOUT: { 293 SomeArgs args = (SomeArgs) message.obj; 294 PrintAttributes oldAttributes = (PrintAttributes) args.arg1; 295 PrintAttributes newAttributes = (PrintAttributes) args.arg2; 296 ILayoutResultCallback callback = (ILayoutResultCallback) args.arg3; 297 Bundle metadata = (Bundle) args.arg4; 298 args.recycle(); 299 300 try { 301 ICancellationSignal remoteSignal = CancellationSignal.createTransport(); 302 callback.onLayoutStarted(remoteSignal); 303 304 mDocumentAdapter.onLayout(oldAttributes, newAttributes, 305 CancellationSignal.fromTransport(remoteSignal), 306 new LayoutResultCallbackWrapper(callback), metadata); 307 } catch (RemoteException re) { 308 Log.e(LOG_TAG, "Error printing", re); 309 } 310 } break; 311 312 case MSG_WRITE: { 313 SomeArgs args = (SomeArgs) message.obj; 314 List<PageRange> pages = (List<PageRange>) args.arg1; 315 FileDescriptor fd = (FileDescriptor) args.arg2; 316 IWriteResultCallback callback = (IWriteResultCallback) args.arg3; 317 args.recycle(); 318 319 try { 320 ICancellationSignal remoteSignal = CancellationSignal.createTransport(); 321 callback.onWriteStarted(remoteSignal); 322 323 mDocumentAdapter.onWrite(pages, fd, 324 CancellationSignal.fromTransport(remoteSignal), 325 new WriteResultCallbackWrapper(callback, fd)); 326 } catch (RemoteException re) { 327 Log.e(LOG_TAG, "Error printing", re); 328 IoUtils.closeQuietly(fd); 329 } 330 } break; 331 332 case MSG_FINISH: { 333 mDocumentAdapter.onFinish(); 334 doFinish(); 335 } break; 336 337 default: { 338 throw new IllegalArgumentException("Unknown message: " 339 + message.what); 340 } 341 } 342 } 343 } 344 } 345 346 private static final class WriteResultCallbackWrapper extends WriteResultCallback { 347 348 private final IWriteResultCallback mWrappedCallback; 349 private final FileDescriptor mFd; 350 351 public WriteResultCallbackWrapper(IWriteResultCallback callback, 352 FileDescriptor fd) { 353 mWrappedCallback = callback; 354 mFd = fd; 355 } 356 357 @Override 358 public void onWriteFinished(List<PageRange> pages) { 359 try { 360 // Close before notifying the other end. We want 361 // to be ready by the time we announce it. 362 IoUtils.closeQuietly(mFd); 363 mWrappedCallback.onWriteFinished(pages); 364 } catch (RemoteException re) { 365 Log.e(LOG_TAG, "Error calling onWriteFinished", re); 366 } 367 } 368 369 @Override 370 public void onWriteFailed(CharSequence error) { 371 try { 372 // Close before notifying the other end. We want 373 // to be ready by the time we announce it. 374 IoUtils.closeQuietly(mFd); 375 mWrappedCallback.onWriteFailed(error); 376 } catch (RemoteException re) { 377 Log.e(LOG_TAG, "Error calling onWriteFailed", re); 378 } 379 } 380 } 381 382 private static final class LayoutResultCallbackWrapper extends LayoutResultCallback { 383 384 private final ILayoutResultCallback mWrappedCallback; 385 386 public LayoutResultCallbackWrapper(ILayoutResultCallback callback) { 387 mWrappedCallback = callback; 388 } 389 390 @Override 391 public void onLayoutFinished(PrintDocumentInfo info, boolean changed) { 392 try { 393 mWrappedCallback.onLayoutFinished(info, changed); 394 } catch (RemoteException re) { 395 Log.e(LOG_TAG, "Error calling onLayoutFinished", re); 396 } 397 } 398 399 @Override 400 public void onLayoutFailed(CharSequence error) { 401 try { 402 mWrappedCallback.onLayoutFailed(error); 403 } catch (RemoteException re) { 404 Log.e(LOG_TAG, "Error calling onLayoutFailed", re); 405 } 406 } 407 } 408} 409