blob: bf2c8e73af6cab946c534c915f2cf2721f2ccdf4 [file] [log] [blame]
Svetoslav Ganova0027152013-06-25 14:59:53 -07001/*
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 com.android.server.print;
18
19import android.content.ComponentName;
20import android.content.Context;
21import android.content.Intent;
22import android.content.ServiceConnection;
23import android.os.Binder;
24import android.os.IBinder;
25import android.os.ParcelFileDescriptor;
26import android.os.RemoteException;
27import android.os.SystemClock;
28import android.os.UserHandle;
29import android.print.IPrintClient;
30import android.print.IPrintDocumentAdapter;
31import android.print.IPrintSpooler;
32import android.print.IPrintSpoolerCallbacks;
33import android.print.IPrintSpoolerClient;
34import android.print.IPrinterDiscoveryObserver;
35import android.print.PrintAttributes;
36import android.print.PrintJobInfo;
37import android.util.Slog;
38import android.util.TimedRemoteCaller;
39
40import libcore.io.IoUtils;
41
42import java.lang.ref.WeakReference;
43import java.util.List;
44import java.util.concurrent.TimeoutException;
45
46/**
47 * This represents the remote print spooler as a local object to the
48 * PrintManagerSerivce. It is responsible to connecting to the remote
49 * spooler if needed, to make the timed remote calls, to handle
50 * remote exceptions, and to bind/unbind to the remote instance as
51 * needed.
52 */
53final class RemotePrintSpooler {
54
55 private static final String LOG_TAG = "RemotePrintSpooler";
56
57 private static final boolean DEBUG = true;
58
59 private static final long BIND_SPOOLER_SERVICE_TIMEOUT = 10000;
60
61 private final Object mLock = new Object();
62
63 private final GetPrintJobInfosCaller mGetPrintJobInfosCaller = new GetPrintJobInfosCaller();
64
65 private final CreatePrintJobCaller mCreatePrintJobCaller = new CreatePrintJobCaller();
66
67 private final CancelPrintJobCaller mCancelPrintJobCaller = new CancelPrintJobCaller();
68
69 private final GetPrintJobInfoCaller mGetPrintJobInfoCaller = new GetPrintJobInfoCaller();
70
71 private final SetPrintJobStateCaller mSetPrintJobStatusCaller = new SetPrintJobStateCaller();
72
73 private final SetPrintJobTagCaller mSetPrintJobTagCaller = new SetPrintJobTagCaller();
74
75 private final ServiceConnection mServiceConnection = new MyServiceConnection();
76
77 private final Context mContext;
78
79 private final UserHandle mUserHandle;
80
81 private final PrintSpoolerClient mClient;
82
83 private final Intent mIntent;
84
85 private final PrintSpoolerCallbacks mCallbacks;
86
87 private IPrintSpooler mRemoteInstance;
88
89 private boolean mDestroyed;
90
91 public static interface PrintSpoolerCallbacks {
92 public void onPrintJobQueued(PrintJobInfo printJob);
93 public void onStartPrinterDiscovery(IPrinterDiscoveryObserver observer);
94 public void onStopPrinterDiscovery();
95 public void onAllPrintJobsForServiceHandled(ComponentName printService);
96 }
97
98 public RemotePrintSpooler(Context context, int userId,
99 PrintSpoolerCallbacks callbacks) {
100 mContext = context;
101 mUserHandle = new UserHandle(userId);
102 mCallbacks = callbacks;
103 mClient = new PrintSpoolerClient(this);
104 mIntent = new Intent();
105 mIntent.setComponent(new ComponentName("com.android.printspooler",
106 "com.android.printspooler.PrintSpoolerService"));
107 }
108
109 public final List<PrintJobInfo> getPrintJobInfos(ComponentName componentName, int state,
110 int appId) {
111 throwIfCalledOnMainThread();
112 synchronized (mLock) {
113 throwIfDestroyedLocked();
114 }
115 if (DEBUG) {
116 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] getPrintJobInfos()");
117 }
118 try {
119 return mGetPrintJobInfosCaller.getPrintJobInfos(getRemoteInstanceLazy(),
120 componentName, state, appId);
121 } catch (RemoteException re) {
122 Slog.e(LOG_TAG, "Error getting print jobs.", re);
123 } catch (TimeoutException te) {
124 Slog.e(LOG_TAG, "Error getting print jobs.", te);
125 }
126 return null;
127 }
128
129 public final PrintJobInfo createPrintJob(String printJobName, IPrintClient client,
130 IPrintDocumentAdapter documentAdapter, PrintAttributes attributes, int appId) {
131 throwIfCalledOnMainThread();
132 synchronized (mLock) {
133 throwIfDestroyedLocked();
134 }
135 if (DEBUG) {
136 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] createPrintJob()");
137 }
138 try {
139 return mCreatePrintJobCaller.createPrintJob(getRemoteInstanceLazy(),
140 printJobName, client, documentAdapter, attributes, appId);
141 } catch (RemoteException re) {
142 Slog.e(LOG_TAG, "Error creating print job.", re);
143 } catch (TimeoutException te) {
144 Slog.e(LOG_TAG, "Error creating print job.", te);
145 }
146 return null;
147 }
148
149 public final boolean cancelPrintJob(int printJobId, int appId) {
150 throwIfCalledOnMainThread();
151 synchronized (mLock) {
152 throwIfDestroyedLocked();
153 }
154 if (DEBUG) {
155 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] cancelPrintJob()");
156 }
157 try {
158 return mCancelPrintJobCaller.cancelPrintJob(getRemoteInstanceLazy(),
159 printJobId, appId);
160 } catch (RemoteException re) {
161 Slog.e(LOG_TAG, "Error canceling print job.", re);
162 } catch (TimeoutException te) {
163 Slog.e(LOG_TAG, "Error canceling print job.", te);
164 }
165 return false;
166 }
167
168 public final void writePrintJobData(ParcelFileDescriptor fd, int printJobId) {
169 throwIfCalledOnMainThread();
170 synchronized (mLock) {
171 throwIfDestroyedLocked();
172 }
173 if (DEBUG) {
174 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] writePrintJobData()");
175 }
176 try {
177 getRemoteInstanceLazy().writePrintJobData(fd, printJobId);
178 } catch (RemoteException re) {
179 Slog.e(LOG_TAG, "Error writing print job data.", re);
180 } catch (TimeoutException te) {
181 Slog.e(LOG_TAG, "Error writing print job data.", te);
182 } finally {
183 // We passed the file descriptor across and now the other
184 // side is responsible to close it, so close the local copy.
185 IoUtils.closeQuietly(fd);
186 }
187 }
188
189 public final PrintJobInfo getPrintJobInfo(int printJobId, int appId) {
190 throwIfCalledOnMainThread();
191 synchronized (mLock) {
192 throwIfDestroyedLocked();
193 }
194 if (DEBUG) {
195 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] getPrintJobInfo()");
196 }
197 try {
198 return mGetPrintJobInfoCaller.getPrintJobInfo(getRemoteInstanceLazy(),
199 printJobId, appId);
200 } catch (RemoteException re) {
201 Slog.e(LOG_TAG, "Error getting print job info.", re);
202 } catch (TimeoutException te) {
203 Slog.e(LOG_TAG, "Error getting print job info.", te);
204 }
205 return null;
206 }
207
208 public final boolean setPrintJobState(int printJobId, int state) {
209 throwIfCalledOnMainThread();
210 synchronized (mLock) {
211 throwIfDestroyedLocked();
212 }
213 if (DEBUG) {
214 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setPrintJobState()");
215 }
216 try {
217 return mSetPrintJobStatusCaller.setPrintJobState(getRemoteInstanceLazy(),
218 printJobId, state);
219 } catch (RemoteException re) {
220 Slog.e(LOG_TAG, "Error setting print job state.", re);
221 } catch (TimeoutException te) {
222 Slog.e(LOG_TAG, "Error setting print job state.", te);
223 }
224 return false;
225 }
226
227 public final boolean setPrintJobTag(int printJobId, String tag) {
228 throwIfCalledOnMainThread();
229 synchronized (mLock) {
230 throwIfDestroyedLocked();
231 }
232 if (DEBUG) {
233 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setPrintJobTag()");
234 }
235 try {
236 return mSetPrintJobTagCaller.setPrintJobTag(getRemoteInstanceLazy(),
237 printJobId, tag);
238 } catch (RemoteException re) {
239 Slog.e(LOG_TAG, "Error setting print job tag.", re);
240 } catch (TimeoutException te) {
241 Slog.e(LOG_TAG, "Error setting print job tag.", te);
242 }
243 return false;
244 }
245
246 public final void notifyClientForActivteJobs() {
247 throwIfCalledOnMainThread();
248 synchronized (mLock) {
249 throwIfDestroyedLocked();
250 }
251 if (DEBUG) {
252 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier()
253 + "] notifyClientForActivteJobs()");
254 }
255 try {
256 getRemoteInstanceLazy().notifyClientForActivteJobs();
257 } catch (RemoteException re) {
258 Slog.e(LOG_TAG, "Error asking for active print job notification.", re);
259 } catch (TimeoutException te) {
260 Slog.e(LOG_TAG, "Error asking for active print job notification.", te);
261 }
262 }
263
264 public final void destroy() {
265 throwIfCalledOnMainThread();
266 if (DEBUG) {
267 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] destroy()");
268 }
269 synchronized (mLock) {
270 throwIfDestroyedLocked();
271 unbindLocked();
272 mDestroyed = true;
273 }
274 }
275
276 private void onAllPrintJobsHandled() {
277 synchronized (mLock) {
278 throwIfDestroyedLocked();
279 unbindLocked();
280 }
281 }
282
283 private IPrintSpooler getRemoteInstanceLazy() throws TimeoutException {
284 synchronized (mLock) {
285 if (mRemoteInstance != null) {
286 return mRemoteInstance;
287 }
288 bindLocked();
289 return mRemoteInstance;
290 }
291 }
292
293 private void bindLocked() throws TimeoutException {
294 if (DEBUG) {
295 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] bindLocked()");
296 }
297
298 mContext.bindServiceAsUser(mIntent, mServiceConnection,
299 Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_OOM_MANAGEMENT,
300 mUserHandle);
301
302 final long startMillis = SystemClock.uptimeMillis();
303 while (true) {
304 if (mRemoteInstance != null) {
305 break;
306 }
307 final long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
308 final long remainingMillis = BIND_SPOOLER_SERVICE_TIMEOUT - elapsedMillis;
309 if (remainingMillis <= 0) {
310 throw new TimeoutException("Cannot get spooler!");
311 }
312 try {
313 mLock.wait(remainingMillis);
314 } catch (InterruptedException ie) {
315 /* ignore */
316 }
317 }
318 }
319
320 private void unbindLocked() {
321 if (DEBUG) {
322 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] unbindLocked()");
323 }
324 clearClientLocked();
325 mRemoteInstance = null;
326 mContext.unbindService(mServiceConnection);
327 }
328
329 private void setClientLocked() {
330 try {
331 mRemoteInstance.setClient(mClient);
332 } catch (RemoteException re) {
333 Slog.d(LOG_TAG, "Error setting print spooler client", re);
334 }
335 }
336
337 private void clearClientLocked() {
338 try {
339 mRemoteInstance.setClient(null);
340 } catch (RemoteException re) {
341 Slog.d(LOG_TAG, "Error clearing print spooler client", re);
342 }
343
344 }
345
346 private void throwIfDestroyedLocked() {
347 if (mDestroyed) {
348 throw new IllegalStateException("Cannot interact with a destroyed instance.");
349 }
350 }
351
352 private void throwIfCalledOnMainThread() {
353 if (Thread.currentThread() == mContext.getMainLooper().getThread()) {
354 throw new RuntimeException("Cannot invoke on the main thread");
355 }
356 }
357
358 private final class MyServiceConnection implements ServiceConnection {
359 @Override
360 public void onServiceConnected(ComponentName name, IBinder service) {
361 synchronized (mLock) {
362 mRemoteInstance = IPrintSpooler.Stub.asInterface(service);
363 setClientLocked();
364 mLock.notifyAll();
365 }
366 }
367
368 @Override
369 public void onServiceDisconnected(ComponentName name) {
370 synchronized (mLock) {
371 clearClientLocked();
372 mRemoteInstance = null;
373 }
374 }
375 }
376
377 private static final class GetPrintJobInfosCaller
378 extends TimedRemoteCaller<List<PrintJobInfo>> {
379 private final IPrintSpoolerCallbacks mCallback;
380
381 public GetPrintJobInfosCaller() {
382 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
383 mCallback = new BasePrintSpoolerServiceCallbacks() {
384 @Override
385 public void onGetPrintJobInfosResult(List<PrintJobInfo> printJobs, int sequence) {
386 onRemoteMethodResult(printJobs, sequence);
387 }
388 };
389 }
390
391 public List<PrintJobInfo> getPrintJobInfos(IPrintSpooler target,
392 ComponentName componentName, int state, int appId)
393 throws RemoteException, TimeoutException {
394 final int sequence = onBeforeRemoteCall();
395 target.getPrintJobInfos(mCallback, componentName, state, appId, sequence);
396 return getResultTimed(sequence);
397 }
398 }
399
400 private static final class CreatePrintJobCaller extends TimedRemoteCaller<PrintJobInfo> {
401 private final IPrintSpoolerCallbacks mCallback;
402
403 public CreatePrintJobCaller() {
404 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
405 mCallback = new BasePrintSpoolerServiceCallbacks() {
406 @Override
407 public void onCreatePrintJobResult(PrintJobInfo printJob, int sequence) {
408 onRemoteMethodResult(printJob, sequence);
409 }
410 };
411 }
412
413 public PrintJobInfo createPrintJob(IPrintSpooler target, String printJobName,
414 IPrintClient client, IPrintDocumentAdapter documentAdapter,
415 PrintAttributes attributes, int appId) throws RemoteException, TimeoutException {
416 final int sequence = onBeforeRemoteCall();
417 target.createPrintJob(printJobName, client, documentAdapter, attributes,
418 mCallback, appId, sequence);
419 return getResultTimed(sequence);
420 }
421 }
422
423 private static final class CancelPrintJobCaller extends TimedRemoteCaller<Boolean> {
424 private final IPrintSpoolerCallbacks mCallback;
425
426 public CancelPrintJobCaller() {
427 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
428 mCallback = new BasePrintSpoolerServiceCallbacks() {
429 @Override
430 public void onCancelPrintJobResult(boolean canceled, int sequence) {
431 onRemoteMethodResult(canceled, sequence);
432 }
433 };
434 }
435
436 public boolean cancelPrintJob(IPrintSpooler target, int printJobId,
437 int appId) throws RemoteException, TimeoutException {
438 final int sequence = onBeforeRemoteCall();
439 target.cancelPrintJob(printJobId, mCallback, appId, sequence);
440 return getResultTimed(sequence);
441 }
442 }
443
444 private static final class GetPrintJobInfoCaller extends TimedRemoteCaller<PrintJobInfo> {
445 private final IPrintSpoolerCallbacks mCallback;
446
447 public GetPrintJobInfoCaller() {
448 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
449 mCallback = new BasePrintSpoolerServiceCallbacks() {
450 @Override
451 public void onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence) {
452 onRemoteMethodResult(printJob, sequence);
453 }
454 };
455 }
456
457 public PrintJobInfo getPrintJobInfo(IPrintSpooler target, int printJobId,
458 int appId) throws RemoteException, TimeoutException {
459 final int sequence = onBeforeRemoteCall();
460 target.getPrintJobInfo(printJobId, mCallback, appId, sequence);
461 return getResultTimed(sequence);
462 }
463 }
464
465 private static final class SetPrintJobStateCaller extends TimedRemoteCaller<Boolean> {
466 private final IPrintSpoolerCallbacks mCallback;
467
468 public SetPrintJobStateCaller() {
469 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
470 mCallback = new BasePrintSpoolerServiceCallbacks() {
471 @Override
472 public void onSetPrintJobStateResult(boolean success, int sequence) {
473 onRemoteMethodResult(success, sequence);
474 }
475 };
476 }
477
478 public boolean setPrintJobState(IPrintSpooler target, int printJobId,
479 int status) throws RemoteException, TimeoutException {
480 final int sequence = onBeforeRemoteCall();
481 target.setPrintJobState(printJobId, status, mCallback, sequence);
482 return getResultTimed(sequence);
483 }
484 }
485
486 private static final class SetPrintJobTagCaller extends TimedRemoteCaller<Boolean> {
487 private final IPrintSpoolerCallbacks mCallback;
488
489 public SetPrintJobTagCaller() {
490 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
491 mCallback = new BasePrintSpoolerServiceCallbacks() {
492 @Override
493 public void onSetPrintJobTagResult(boolean success, int sequence) {
494 onRemoteMethodResult(success, sequence);
495 }
496 };
497 }
498
499 public boolean setPrintJobTag(IPrintSpooler target, int printJobId,
500 String tag) throws RemoteException, TimeoutException {
501 final int sequence = onBeforeRemoteCall();
502 target.setPrintJobTag(printJobId, tag, mCallback, sequence);
503 return getResultTimed(sequence);
504 }
505 }
506
507 private static abstract class BasePrintSpoolerServiceCallbacks
508 extends IPrintSpoolerCallbacks.Stub {
509 @Override
510 public void onGetPrintJobInfosResult(List<PrintJobInfo> printJobIds, int sequence) {
511 /* do nothing */
512 }
513
514 @Override
515 public void onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence) {
516 /* do nothing */
517 }
518
519 @Override
520 public void onCreatePrintJobResult(PrintJobInfo printJob, int sequence) {
521 /* do nothing */
522 }
523
524 @Override
525 public void onCancelPrintJobResult(boolean canceled, int sequence) {
526 /* do nothing */
527 }
528
529 @Override
530 public void onSetPrintJobStateResult(boolean success, int sequece) {
531 /* do nothing */
532 }
533
534 @Override
535 public void onSetPrintJobTagResult(boolean success, int sequence) {
536 /* do nothing */
537 }
538 }
539
540 private static final class PrintSpoolerClient extends IPrintSpoolerClient.Stub {
541
542 private final WeakReference<RemotePrintSpooler> mWeakSpooler;
543
544 public PrintSpoolerClient(RemotePrintSpooler spooler) {
545 mWeakSpooler = new WeakReference<RemotePrintSpooler>(spooler);
546 }
547
548 @Override
549 public void onPrintJobQueued(PrintJobInfo printJob) {
550 RemotePrintSpooler spooler = mWeakSpooler.get();
551 if (spooler != null) {
552 final long identity = Binder.clearCallingIdentity();
553 try {
554 spooler.mCallbacks.onPrintJobQueued(printJob);
555 } finally {
556 Binder.restoreCallingIdentity(identity);
557 }
558 }
559 }
560
561 @Override
562 public void onAllPrintJobsForServiceHandled(ComponentName printService) {
563 RemotePrintSpooler spooler = mWeakSpooler.get();
564 if (spooler != null) {
565 final long identity = Binder.clearCallingIdentity();
566 try {
567 spooler.mCallbacks.onAllPrintJobsForServiceHandled(printService);
568 } finally {
569 Binder.restoreCallingIdentity(identity);
570 }
571 }
572 }
573
574 @Override
575 public void onAllPrintJobsHandled() {
576 RemotePrintSpooler spooler = mWeakSpooler.get();
577 if (spooler != null) {
578 final long identity = Binder.clearCallingIdentity();
579 try {
580 spooler.onAllPrintJobsHandled();
581 } finally {
582 Binder.restoreCallingIdentity(identity);
583 }
584 }
585 }
586
587 @Override
588 public void onStartPrinterDiscovery(IPrinterDiscoveryObserver observer) {
589 RemotePrintSpooler spooler = mWeakSpooler.get();
590 if (spooler != null) {
591 final long identity = Binder.clearCallingIdentity();
592 try {
593 spooler.mCallbacks.onStartPrinterDiscovery(observer);
594 } finally {
595 Binder.restoreCallingIdentity(identity);
596 }
597 }
598 }
599
600 @Override
601 public void onStopPrinterDiscovery() throws RemoteException {
602 RemotePrintSpooler spooler = mWeakSpooler.get();
603 if (spooler != null) {
604 final long identity = Binder.clearCallingIdentity();
605 try {
606 spooler.mCallbacks.onStopPrinterDiscovery();
607 } finally {
608 Binder.restoreCallingIdentity(identity);
609 }
610 }
611 }
612 }
613}