PrintManager.java revision fd90651cfcc7e2b75254666fd6861038b72fb4ac
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.PrintAdapter.PrintResultCallback;
30import android.util.Log;
31
32import com.android.internal.os.SomeArgs;
33
34import libcore.io.IoUtils;
35
36import java.io.File;
37import java.io.FileDescriptor;
38import java.lang.ref.WeakReference;
39import java.util.ArrayList;
40import java.util.Collections;
41import java.util.List;
42
43/**
44 * System level service for accessing the printing capabilities of the platform.
45 * <p>
46 * To obtain a handle to the print manager do the following:
47 * </p>
48 * <pre>
49 * PrintManager printManager =
50 *         (PrintManager) context.getSystemService(Context.PRINT_SERVICE);
51 * </pre>
52 */
53public final class PrintManager {
54
55    private static final String LOG_TAG = "PrintManager";
56
57    /** @hide */
58    public static final int APP_ID_ANY = -2;
59
60    private final Context mContext;
61
62    private final IPrintManager mService;
63
64    private final int mUserId;
65
66    private final int mAppId;
67
68    private final PrintClient mPrintClient;
69
70    private final Handler mHandler;
71
72    /**
73     * Creates a new instance.
74     *
75     * @param context The current context in which to operate.
76     * @param service The backing system service.
77     *
78     * @hide
79     */
80    public PrintManager(Context context, IPrintManager service, int userId, int appId) {
81        mContext = context;
82        mService = service;
83        mUserId = userId;
84        mAppId = appId;
85        mPrintClient = new PrintClient(this);
86        mHandler = new Handler(context.getMainLooper(), null, false) {
87            @Override
88            public void handleMessage(Message message) {
89                SomeArgs args = (SomeArgs) message.obj;
90                Context context = (Context) args.arg1;
91                IntentSender intent = (IntentSender) args.arg2;
92                args.recycle();
93                try {
94                    context.startIntentSender(intent, null, 0, 0, 0);
95                } catch (SendIntentException sie) {
96                    Log.e(LOG_TAG, "Couldn't start print job config activity.", sie);
97                }
98            }
99        };
100    }
101
102    /**
103     * Creates an instance that can access all print jobs.
104     *
105     * @param userId The user id for which to get all print jobs.
106     * @return An instance of the caller has the permission to access
107     * all print jobs, null otherwise.
108     *
109     * @hide
110     */
111    public PrintManager getGlobalPrintManagerForUser(int userId) {
112        return new PrintManager(mContext, mService, userId, APP_ID_ANY);
113    }
114
115    PrintJobInfo getPrintJob(int printJobId) {
116        try {
117            return mService.getPrintJob(printJobId, mAppId, mUserId);
118        } catch (RemoteException re) {
119            Log.e(LOG_TAG, "Error getting print job:" + printJobId, re);
120        }
121        return null;
122    }
123
124    /**
125     * Gets the print jobs for this application.
126     *
127     * @return The print job list.
128     *
129     * @see PrintJob
130     */
131    public List<PrintJob> getPrintJobs() {
132        try {
133            List<PrintJobInfo> printJobInfos = mService.getPrintJobs(mAppId, mUserId);
134            if (printJobInfos == null) {
135                return Collections.emptyList();
136            }
137            final int printJobCount = printJobInfos.size();
138            List<PrintJob> printJobs = new ArrayList<PrintJob>(printJobCount);
139            for (int i = 0; i < printJobCount; i++) {
140                printJobs.add(new PrintJob(printJobInfos.get(i), this));
141            }
142            return printJobs;
143        } catch (RemoteException re) {
144            Log.e(LOG_TAG, "Error getting print jobs!", re);
145        }
146        return Collections.emptyList();
147    }
148
149    ICancellationSignal cancelPrintJob(int printJobId) {
150        try {
151            mService.cancelPrintJob(printJobId, mAppId, mUserId);
152        } catch (RemoteException re) {
153            Log.e(LOG_TAG, "Error cancleing a print job:" + printJobId, re);
154        }
155        return null;
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        PrintFileAdapter printable = new PrintFileAdapter(pdfFile);
170        return print(printJobName, printable, attributes);
171    }
172
173    /**
174     * Creates a print job for printing a {@link PrintAdapter} with default print
175     * attributes.
176     *
177     * @param printJobName A name for the new print job.
178     * @param printAdapter The printable adapter 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, PrintAdapter printAdapter,
185            PrintAttributes attributes) {
186        PrintAdapterDelegate delegate = new PrintAdapterDelegate(printAdapter,
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 PrintAdapterDelegate extends IPrintAdapter.Stub {
221        private final Object mLock = new Object();
222
223        private PrintAdapter mPrintAdapter;
224
225        private Handler mHandler;
226
227        public PrintAdapterDelegate(PrintAdapter printAdapter, Looper looper) {
228            mPrintAdapter = printAdapter;
229            mHandler = new MyHandler(looper);
230        }
231
232        @Override
233        public void start() {
234            synchronized (mLock) {
235                if (isFinishedLocked()) {
236                    return;
237                }
238                mHandler.obtainMessage(MyHandler.MESSAGE_START,
239                        mPrintAdapter).sendToTarget();
240            }
241        }
242
243        @Override
244        public void printAttributesChanged(PrintAttributes attributes) {
245            synchronized (mLock) {
246                if (isFinishedLocked()) {
247                    return;
248                }
249                SomeArgs args = SomeArgs.obtain();
250                args.arg1 = mPrintAdapter;
251                args.arg2 = attributes;
252                mHandler.obtainMessage(MyHandler.MESSAGE_PRINT_ATTRIBUTES_CHANGED,
253                        args).sendToTarget();
254            }
255        }
256
257        @Override
258        public void print(List<PageRange> pages, ParcelFileDescriptor fd,
259                IPrintResultCallback callback) {
260            synchronized (mLock) {
261                if (isFinishedLocked()) {
262                    return;
263                }
264                SomeArgs args = SomeArgs.obtain();
265                args.arg1 = mPrintAdapter;
266                args.arg2 = pages;
267                args.arg3 = fd.getFileDescriptor();
268                args.arg4 = callback;
269                mHandler.obtainMessage(MyHandler.MESSAGE_PRINT, args).sendToTarget();
270            }
271        }
272
273        @Override
274        public void finish() {
275            synchronized (mLock) {
276                if (isFinishedLocked()) {
277                    return;
278                }
279                mHandler.obtainMessage(MyHandler.MESSAGE_FINIS,
280                        mPrintAdapter).sendToTarget();
281            }
282        }
283
284        private boolean isFinishedLocked() {
285            return mPrintAdapter == null;
286        }
287
288        private void finishLocked() {
289            mPrintAdapter = null;
290            mHandler = null;
291        }
292
293        private final class MyHandler extends Handler {
294            public static final int MESSAGE_START = 1;
295            public static final int MESSAGE_PRINT_ATTRIBUTES_CHANGED = 2;
296            public static final int MESSAGE_PRINT = 3;
297            public static final int MESSAGE_FINIS = 4;
298
299            public MyHandler(Looper looper) {
300                super(looper, null, true);
301            }
302
303            @Override
304            public void handleMessage(Message message) {
305                switch (message.what) {
306                    case MESSAGE_START: {
307                        PrintAdapter adapter = (PrintAdapter) message.obj;
308                        adapter.onStart();
309                    } break;
310
311                    case MESSAGE_PRINT_ATTRIBUTES_CHANGED: {
312                        SomeArgs args = (SomeArgs) message.obj;
313                        PrintAdapter adapter = (PrintAdapter) args.arg1;
314                        PrintAttributes attributes = (PrintAttributes) args.arg2;
315                        args.recycle();
316                        adapter.onPrintAttributesChanged(attributes);
317                    } break;
318
319                    case MESSAGE_PRINT: {
320                        SomeArgs args = (SomeArgs) message.obj;
321                        PrintAdapter adapter = (PrintAdapter) args.arg1;
322                        @SuppressWarnings("unchecked")
323                        List<PageRange> pages = (List<PageRange>) args.arg2;
324                        final FileDescriptor fd = (FileDescriptor) args.arg3;
325                        IPrintResultCallback callback = (IPrintResultCallback) args.arg4;
326                        args.recycle();
327                        try {
328                            ICancellationSignal remoteSignal = CancellationSignal.createTransport();
329                            callback.onPrintStarted(adapter.getInfo(), remoteSignal);
330
331                            CancellationSignal localSignal = CancellationSignal.fromTransport(
332                                    remoteSignal);
333                            adapter.onPrint(pages, fd, localSignal,
334                                    new PrintResultCallbackWrapper(callback) {
335                                        @Override
336                                        public void onPrintFinished(List<PageRange> pages) {
337                                            IoUtils.closeQuietly(fd);
338                                            super.onPrintFinished(pages);
339                                        }
340
341                                        @Override
342                                        public void onPrintFailed(CharSequence error) {
343                                            IoUtils.closeQuietly(fd);
344                                            super.onPrintFailed(error);
345                                        }
346                                    });
347                        } catch (RemoteException re) {
348                            Log.e(LOG_TAG, "Error printing", re);
349                            IoUtils.closeQuietly(fd);
350                        }
351                    } break;
352
353                    case MESSAGE_FINIS: {
354                        PrintAdapter adapter = (PrintAdapter) message.obj;
355                        adapter.onFinish();
356                        synchronized (mLock) {
357                            finishLocked();
358                        }
359                    } break;
360
361                    default: {
362                        throw new IllegalArgumentException("Unknown message: "
363                                + message.what);
364                    }
365                }
366            }
367        }
368    }
369
370    private static abstract class PrintResultCallbackWrapper extends PrintResultCallback {
371
372        private final IPrintResultCallback mWrappedCallback;
373
374        public PrintResultCallbackWrapper(IPrintResultCallback callback) {
375            mWrappedCallback = callback;
376        }
377
378        @Override
379        public void onPrintFinished(List<PageRange> pages) {
380            try {
381                mWrappedCallback.onPrintFinished(pages);
382            } catch (RemoteException re) {
383                Log.e(LOG_TAG, "Error calling onPrintFinished", re);
384            }
385        }
386
387        @Override
388        public void onPrintFailed(CharSequence error) {
389            try {
390                mWrappedCallback.onPrintFailed(error);
391            } catch (RemoteException re) {
392                Log.e(LOG_TAG, "Error calling onPrintFailed", re);
393            }
394        }
395    }
396}
397