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