PrintManager.java revision 17b7f6e6d4ec9f5e9597bfd283f1c017b6c66275
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(mContext, 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