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