PrintManager.java revision 88d199130d44c6bacb383a7757e782cf97483c68
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.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.io.FileDescriptor; 41import java.lang.ref.WeakReference; 42import java.util.ArrayList; 43import java.util.Collections; 44import java.util.List; 45 46/** 47 * System level service for accessing the printing capabilities of the platform. 48 * <p> 49 * To obtain a handle to the print manager do the following: 50 * </p> 51 * <pre> 52 * PrintManager printManager = 53 * (PrintManager) context.getSystemService(Context.PRINT_SERVICE); 54 * </pre> 55 */ 56public final class PrintManager { 57 58 private static final String LOG_TAG = "PrintManager"; 59 60 /** @hide */ 61 public static final int APP_ID_ANY = -2; 62 63 private final Context mContext; 64 65 private final IPrintManager mService; 66 67 private final int mUserId; 68 69 private final int mAppId; 70 71 private final PrintClient mPrintClient; 72 73 private final Handler mHandler; 74 75 /** 76 * Creates a new instance. 77 * 78 * @param context The current context in which to operate. 79 * @param service The backing system service. 80 * 81 * @hide 82 */ 83 public PrintManager(Context context, IPrintManager service, int userId, int appId) { 84 mContext = context; 85 mService = service; 86 mUserId = userId; 87 mAppId = appId; 88 mPrintClient = new PrintClient(this); 89 mHandler = new Handler(context.getMainLooper(), null, false) { 90 @Override 91 public void handleMessage(Message message) { 92 SomeArgs args = (SomeArgs) message.obj; 93 Context context = (Context) args.arg1; 94 IntentSender intent = (IntentSender) args.arg2; 95 args.recycle(); 96 try { 97 context.startIntentSender(intent, null, 0, 0, 0); 98 } catch (SendIntentException sie) { 99 Log.e(LOG_TAG, "Couldn't start print job config activity.", sie); 100 } 101 } 102 }; 103 } 104 105 /** 106 * Creates an instance that can access all print jobs. 107 * 108 * @param userId The user id for which to get all print jobs. 109 * @return An instance if the caller has the permission to access 110 * all print jobs, null otherwise. 111 * 112 * @hide 113 */ 114 public PrintManager getGlobalPrintManagerForUser(int userId) { 115 return new PrintManager(mContext, mService, userId, APP_ID_ANY); 116 } 117 118 PrintJobInfo getPrintJobInfo(int printJobId) { 119 try { 120 return mService.getPrintJobInfo(printJobId, mAppId, mUserId); 121 } catch (RemoteException re) { 122 Log.e(LOG_TAG, "Error getting a print job info:" + printJobId, re); 123 } 124 return null; 125 } 126 127 /** 128 * Gets the print jobs for this application. 129 * 130 * @return The print job list. 131 * 132 * @see PrintJob 133 */ 134 public List<PrintJob> getPrintJobs() { 135 try { 136 List<PrintJobInfo> printJobInfos = mService.getPrintJobInfos(mAppId, mUserId); 137 if (printJobInfos == null) { 138 return Collections.emptyList(); 139 } 140 final int printJobCount = printJobInfos.size(); 141 List<PrintJob> printJobs = new ArrayList<PrintJob>(printJobCount); 142 for (int i = 0; i < printJobCount; i++) { 143 printJobs.add(new PrintJob(printJobInfos.get(i), this)); 144 } 145 return printJobs; 146 } catch (RemoteException re) { 147 Log.e(LOG_TAG, "Error getting print jobs", re); 148 } 149 return Collections.emptyList(); 150 } 151 152 void cancelPrintJob(int printJobId) { 153 try { 154 mService.cancelPrintJob(printJobId, mAppId, mUserId); 155 } catch (RemoteException re) { 156 Log.e(LOG_TAG, "Error cancleing a print job: " + printJobId, re); 157 } 158 } 159 160 /** 161 * Creates a print job for printing a file with default print attributes. 162 * 163 * @param printJobName A name for the new print job. 164 * @param pdfFile The PDF file to print. 165 * @param attributes The default print job attributes. 166 * @return The created print job. 167 * 168 * @see PrintJob 169 */ 170 public PrintJob print(String printJobName, File pdfFile, PrintAttributes attributes) { 171 FileDocumentAdapter documentAdapter = new FileDocumentAdapter(mContext, pdfFile); 172 return print(printJobName, documentAdapter, attributes); 173 } 174 175 /** 176 * Creates a print job for printing a {@link PrintDocumentAdapter} with default print 177 * attributes. 178 * 179 * @param printJobName A name for the new print job. 180 * @param documentAdapter An adapter that emits the document to print. 181 * @param attributes The default print job attributes. 182 * @return The created print job. 183 * 184 * @see PrintJob 185 */ 186 public PrintJob print(String printJobName, PrintDocumentAdapter documentAdapter, 187 PrintAttributes attributes) { 188 if (TextUtils.isEmpty(printJobName)) { 189 throw new IllegalArgumentException("priintJobName cannot be empty"); 190 } 191 PrintDocumentAdapterDelegate delegate = new PrintDocumentAdapterDelegate(documentAdapter, 192 mContext.getMainLooper()); 193 try { 194 PrintJobInfo printJob = mService.print(printJobName, mPrintClient, delegate, 195 attributes, mAppId, mUserId); 196 if (printJob != null) { 197 return new PrintJob(printJob, this); 198 } 199 } catch (RemoteException re) { 200 Log.e(LOG_TAG, "Error creating a print job", re); 201 } 202 return null; 203 } 204 205 private static final class PrintClient extends IPrintClient.Stub { 206 207 private final WeakReference<PrintManager> mWeakPrintManager; 208 209 public PrintClient(PrintManager manager) { 210 mWeakPrintManager = new WeakReference<PrintManager>(manager); 211 } 212 213 @Override 214 public void startPrintJobConfigActivity(IntentSender intent) { 215 PrintManager manager = mWeakPrintManager.get(); 216 if (manager != null) { 217 SomeArgs args = SomeArgs.obtain(); 218 args.arg1 = manager.mContext; 219 args.arg2 = intent; 220 manager.mHandler.obtainMessage(0, args).sendToTarget(); 221 } 222 } 223 } 224 225 private static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub { 226 private PrintDocumentAdapter mDocumentAdapter; // Strong reference OK - cleared in finish() 227 228 private Handler mHandler; // Strong reference OK - cleared in finish() 229 230 public PrintDocumentAdapterDelegate(PrintDocumentAdapter documentAdapter, Looper looper) { 231 mDocumentAdapter = documentAdapter; 232 mHandler = new MyHandler(looper); 233 } 234 235 @Override 236 public void start() { 237 mHandler.sendEmptyMessage(MyHandler.MSG_START); 238 } 239 240 @Override 241 public void layout(PrintAttributes oldAttributes, PrintAttributes newAttributes, 242 ILayoutResultCallback callback, Bundle metadata) { 243 SomeArgs args = SomeArgs.obtain(); 244 args.arg1 = oldAttributes; 245 args.arg2 = newAttributes; 246 args.arg3 = callback; 247 args.arg4 = metadata; 248 mHandler.obtainMessage(MyHandler.MSG_LAYOUT, args).sendToTarget(); 249 } 250 251 @Override 252 public void write(List<PageRange> pages, ParcelFileDescriptor fd, 253 IWriteResultCallback callback) { 254 SomeArgs args = SomeArgs.obtain(); 255 args.arg1 = pages; 256 args.arg2 = fd.getFileDescriptor(); 257 args.arg3 = callback; 258 mHandler.obtainMessage(MyHandler.MSG_WRITE, args).sendToTarget(); 259 } 260 261 @Override 262 public void finish() { 263 mHandler.sendEmptyMessage(MyHandler.MSG_FINISH); 264 } 265 266 private boolean isFinished() { 267 return mDocumentAdapter == null; 268 } 269 270 private void doFinish() { 271 mDocumentAdapter = null; 272 mHandler = null; 273 } 274 275 private final class MyHandler extends Handler { 276 public static final int MSG_START = 1; 277 public static final int MSG_LAYOUT = 2; 278 public static final int MSG_WRITE = 3; 279 public static final int MSG_FINISH = 4; 280 281 public MyHandler(Looper looper) { 282 super(looper, null, true); 283 } 284 285 @Override 286 @SuppressWarnings("unchecked") 287 public void handleMessage(Message message) { 288 if (isFinished()) { 289 return; 290 } 291 switch (message.what) { 292 case MSG_START: { 293 mDocumentAdapter.onStart(); 294 } break; 295 296 case MSG_LAYOUT: { 297 SomeArgs args = (SomeArgs) message.obj; 298 PrintAttributes oldAttributes = (PrintAttributes) args.arg1; 299 PrintAttributes newAttributes = (PrintAttributes) args.arg2; 300 ILayoutResultCallback callback = (ILayoutResultCallback) args.arg3; 301 Bundle metadata = (Bundle) args.arg4; 302 args.recycle(); 303 304 try { 305 ICancellationSignal remoteSignal = CancellationSignal.createTransport(); 306 callback.onLayoutStarted(remoteSignal); 307 308 mDocumentAdapter.onLayout(oldAttributes, newAttributes, 309 CancellationSignal.fromTransport(remoteSignal), 310 new LayoutResultCallbackWrapper(callback), metadata); 311 } catch (RemoteException re) { 312 Log.e(LOG_TAG, "Error printing", re); 313 } 314 } break; 315 316 case MSG_WRITE: { 317 SomeArgs args = (SomeArgs) message.obj; 318 List<PageRange> pages = (List<PageRange>) args.arg1; 319 FileDescriptor fd = (FileDescriptor) args.arg2; 320 IWriteResultCallback callback = (IWriteResultCallback) args.arg3; 321 args.recycle(); 322 323 try { 324 ICancellationSignal remoteSignal = CancellationSignal.createTransport(); 325 callback.onWriteStarted(remoteSignal); 326 327 mDocumentAdapter.onWrite(pages, fd, 328 CancellationSignal.fromTransport(remoteSignal), 329 new WriteResultCallbackWrapper(callback, fd)); 330 } catch (RemoteException re) { 331 Log.e(LOG_TAG, "Error printing", re); 332 IoUtils.closeQuietly(fd); 333 } 334 } break; 335 336 case MSG_FINISH: { 337 mDocumentAdapter.onFinish(); 338 doFinish(); 339 } break; 340 341 default: { 342 throw new IllegalArgumentException("Unknown message: " 343 + message.what); 344 } 345 } 346 } 347 } 348 } 349 350 private static final class WriteResultCallbackWrapper extends WriteResultCallback { 351 352 private final IWriteResultCallback mWrappedCallback; 353 private final FileDescriptor mFd; 354 355 public WriteResultCallbackWrapper(IWriteResultCallback callback, 356 FileDescriptor fd) { 357 mWrappedCallback = callback; 358 mFd = fd; 359 } 360 361 @Override 362 public void onWriteFinished(List<PageRange> pages) { 363 try { 364 // Close before notifying the other end. We want 365 // to be ready by the time we announce it. 366 IoUtils.closeQuietly(mFd); 367 mWrappedCallback.onWriteFinished(pages); 368 } catch (RemoteException re) { 369 Log.e(LOG_TAG, "Error calling onWriteFinished", re); 370 } 371 } 372 373 @Override 374 public void onWriteFailed(CharSequence error) { 375 try { 376 // Close before notifying the other end. We want 377 // to be ready by the time we announce it. 378 IoUtils.closeQuietly(mFd); 379 mWrappedCallback.onWriteFailed(error); 380 } catch (RemoteException re) { 381 Log.e(LOG_TAG, "Error calling onWriteFailed", re); 382 } 383 } 384 } 385 386 private static final class LayoutResultCallbackWrapper extends LayoutResultCallback { 387 388 private final ILayoutResultCallback mWrappedCallback; 389 390 public LayoutResultCallbackWrapper(ILayoutResultCallback callback) { 391 mWrappedCallback = callback; 392 } 393 394 @Override 395 public void onLayoutFinished(PrintDocumentInfo info, boolean changed) { 396 try { 397 mWrappedCallback.onLayoutFinished(info, changed); 398 } catch (RemoteException re) { 399 Log.e(LOG_TAG, "Error calling onLayoutFinished", re); 400 } 401 } 402 403 @Override 404 public void onLayoutFailed(CharSequence error) { 405 try { 406 mWrappedCallback.onLayoutFailed(error); 407 } catch (RemoteException re) { 408 Log.e(LOG_TAG, "Error calling onLayoutFailed", re); 409 } 410 } 411 } 412} 413