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