• 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 static com.android.internal.print.DumpUtils.writePrinterId;
20 import static com.android.internal.util.dump.DumpUtils.writeComponentName;
21 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
22 
23 import android.annotation.FloatRange;
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.annotation.StringRes;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.ServiceConnection;
31 import android.content.pm.ParceledListSlice;
32 import android.graphics.drawable.Icon;
33 import android.os.Binder;
34 import android.os.Handler;
35 import android.os.IBinder;
36 import android.os.IBinder.DeathRecipient;
37 import android.os.ParcelFileDescriptor;
38 import android.os.RemoteException;
39 import android.os.UserHandle;
40 import android.print.PrintJobId;
41 import android.print.PrintJobInfo;
42 import android.print.PrintManager;
43 import android.print.PrinterId;
44 import android.print.PrinterInfo;
45 import android.printservice.IPrintService;
46 import android.printservice.IPrintServiceClient;
47 import android.service.print.ActivePrintServiceProto;
48 import android.util.Slog;
49 
50 import com.android.internal.annotations.GuardedBy;
51 import com.android.internal.util.dump.DualDumpOutputStream;
52 
53 import java.lang.ref.WeakReference;
54 import java.util.ArrayList;
55 import java.util.List;
56 
57 /**
58  * This class represents a remote print service. It abstracts away the binding
59  * and unbinding from the remote implementation. Clients can call methods of
60  * this class without worrying about when and how to bind/unbind.
61  */
62 final class RemotePrintService implements DeathRecipient {
63 
64     private static final String LOG_TAG = "RemotePrintService";
65 
66     private static final boolean DEBUG = false;
67 
68     private final Object mLock = new Object();
69 
70     private final Context mContext;
71 
72     private final ComponentName mComponentName;
73 
74     private final Intent mIntent;
75 
76     private final RemotePrintSpooler mSpooler;
77 
78     private final PrintServiceCallbacks mCallbacks;
79 
80     private final int mUserId;
81 
82     private final List<Runnable> mPendingCommands = new ArrayList<Runnable>();
83 
84     private final ServiceConnection mServiceConnection = new RemoteServiceConneciton();
85 
86     private final RemotePrintServiceClient mPrintServiceClient;
87 
88     private IPrintService mPrintService;
89 
90     private boolean mBinding;
91 
92     private boolean mDestroyed;
93 
94     private boolean mHasActivePrintJobs;
95 
96     private boolean mHasPrinterDiscoverySession;
97 
98     private boolean mServiceDied;
99 
100     private List<PrinterId> mDiscoveryPriorityList;
101 
102     @GuardedBy("mLock")
103     private List<PrinterId> mTrackedPrinterList;
104 
105     public static interface PrintServiceCallbacks {
onPrintersAdded(List<PrinterInfo> printers)106         public void onPrintersAdded(List<PrinterInfo> printers);
onPrintersRemoved(List<PrinterId> printerIds)107         public void onPrintersRemoved(List<PrinterId> printerIds);
onServiceDied(RemotePrintService service)108         public void onServiceDied(RemotePrintService service);
109 
110         /**
111          * Handle that a custom icon for a printer was loaded.
112          *
113          * @param printerId the id of the printer the icon belongs to
114          * @param icon the icon that was loaded
115          * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon()
116          */
onCustomPrinterIconLoaded(PrinterId printerId, Icon icon)117         public void onCustomPrinterIconLoaded(PrinterId printerId, Icon icon);
118     }
119 
RemotePrintService(Context context, ComponentName componentName, int userId, RemotePrintSpooler spooler, PrintServiceCallbacks callbacks)120     public RemotePrintService(Context context, ComponentName componentName, int userId,
121             RemotePrintSpooler spooler, PrintServiceCallbacks callbacks) {
122         mContext = context;
123         mCallbacks = callbacks;
124         mComponentName = componentName;
125         mIntent = new Intent().setComponent(mComponentName);
126         mUserId = userId;
127         mSpooler = spooler;
128         mPrintServiceClient = new RemotePrintServiceClient(this);
129     }
130 
getComponentName()131     public ComponentName getComponentName() {
132         return mComponentName;
133     }
134 
destroy()135     public void destroy() {
136         Handler.getMain().sendMessage(obtainMessage(
137                 RemotePrintService::handleDestroy, this));
138     }
139 
handleDestroy()140     private void handleDestroy() {
141         // Stop tracking printers.
142         stopTrackingAllPrinters();
143 
144         // Stop printer discovery.
145         if (mDiscoveryPriorityList != null) {
146             handleStopPrinterDiscovery();
147         }
148 
149         // Destroy the discovery session.
150         if (mHasPrinterDiscoverySession) {
151             handleDestroyPrinterDiscoverySession();
152         }
153 
154         // Unbind.
155         ensureUnbound();
156 
157         // Done
158         mDestroyed = true;
159     }
160 
161     @Override
binderDied()162     public void binderDied() {
163         Handler.getMain().sendMessage(obtainMessage(
164                 RemotePrintService::handleBinderDied, this));
165     }
166 
handleBinderDied()167     private void handleBinderDied() {
168         if (mPrintService != null) {
169             mPrintService.asBinder().unlinkToDeath(this, 0);
170         }
171 
172         mPrintService = null;
173         mServiceDied = true;
174         mCallbacks.onServiceDied(this);
175     }
176 
onAllPrintJobsHandled()177     public void onAllPrintJobsHandled() {
178         Handler.getMain().sendMessage(obtainMessage(
179                 RemotePrintService::handleOnAllPrintJobsHandled, this));
180     }
181 
handleOnAllPrintJobsHandled()182     private void handleOnAllPrintJobsHandled() {
183         mHasActivePrintJobs = false;
184         if (!isBound()) {
185             // The service is dead and neither has active jobs nor discovery
186             // session, so ensure we are unbound since the service has no work.
187             if (mServiceDied && !mHasPrinterDiscoverySession) {
188                 ensureUnbound();
189                 return;
190             }
191             ensureBound();
192             mPendingCommands.add(new Runnable() {
193                 @Override
194                 public void run() {
195                     handleOnAllPrintJobsHandled();
196                 }
197             });
198         } else {
199             if (DEBUG) {
200                 Slog.i(LOG_TAG, "[user: " + mUserId + "] onAllPrintJobsHandled()");
201             }
202             // If the service has a printer discovery session
203             // created we should not disconnect from it just yet.
204             if (!mHasPrinterDiscoverySession) {
205                 ensureUnbound();
206             }
207         }
208     }
209 
onRequestCancelPrintJob(PrintJobInfo printJob)210     public void onRequestCancelPrintJob(PrintJobInfo printJob) {
211         Handler.getMain().sendMessage(obtainMessage(
212                 RemotePrintService::handleRequestCancelPrintJob, this, printJob));
213     }
214 
handleRequestCancelPrintJob(final PrintJobInfo printJob)215     private void handleRequestCancelPrintJob(final PrintJobInfo printJob) {
216         if (!isBound()) {
217             ensureBound();
218             mPendingCommands.add(new Runnable() {
219                 @Override
220                 public void run() {
221                     handleRequestCancelPrintJob(printJob);
222                 }
223             });
224         } else {
225             if (DEBUG) {
226                 Slog.i(LOG_TAG, "[user: " + mUserId + "] requestCancelPrintJob()");
227             }
228             try {
229                 mPrintService.requestCancelPrintJob(printJob);
230             } catch (RemoteException re) {
231                 Slog.e(LOG_TAG, "Error canceling a pring job.", re);
232             }
233         }
234     }
235 
onPrintJobQueued(PrintJobInfo printJob)236     public void onPrintJobQueued(PrintJobInfo printJob) {
237         Handler.getMain().sendMessage(obtainMessage(
238                 RemotePrintService::handleOnPrintJobQueued, this, printJob));
239     }
240 
handleOnPrintJobQueued(final PrintJobInfo printJob)241     private void handleOnPrintJobQueued(final PrintJobInfo printJob) {
242         mHasActivePrintJobs = true;
243         if (!isBound()) {
244             ensureBound();
245             mPendingCommands.add(new Runnable() {
246                 @Override
247                 public void run() {
248                     handleOnPrintJobQueued(printJob);
249                 }
250             });
251         } else {
252             if (DEBUG) {
253                 Slog.i(LOG_TAG, "[user: " + mUserId + "] onPrintJobQueued()");
254             }
255             try {
256                 mPrintService.onPrintJobQueued(printJob);
257             } catch (RemoteException re) {
258                 Slog.e(LOG_TAG, "Error announcing queued pring job.", re);
259             }
260         }
261     }
262 
createPrinterDiscoverySession()263     public void createPrinterDiscoverySession() {
264         Handler.getMain().sendMessage(obtainMessage(
265                 RemotePrintService::handleCreatePrinterDiscoverySession, this));
266     }
267 
handleCreatePrinterDiscoverySession()268     private void handleCreatePrinterDiscoverySession() {
269         mHasPrinterDiscoverySession = true;
270         if (!isBound()) {
271             ensureBound();
272             mPendingCommands.add(new Runnable() {
273                 @Override
274                 public void run() {
275                     handleCreatePrinterDiscoverySession();
276                 }
277             });
278         } else {
279             if (DEBUG) {
280                 Slog.i(LOG_TAG, "[user: " + mUserId + "] createPrinterDiscoverySession()");
281             }
282             try {
283                 mPrintService.createPrinterDiscoverySession();
284             } catch (RemoteException re) {
285                 Slog.e(LOG_TAG, "Error creating printer discovery session.", re);
286             }
287         }
288     }
289 
destroyPrinterDiscoverySession()290     public void destroyPrinterDiscoverySession() {
291         Handler.getMain().sendMessage(obtainMessage(
292                 RemotePrintService::handleDestroyPrinterDiscoverySession, this));
293     }
294 
handleDestroyPrinterDiscoverySession()295     private void handleDestroyPrinterDiscoverySession() {
296         mHasPrinterDiscoverySession = false;
297         if (!isBound()) {
298             // The service is dead and neither has active jobs nor discovery
299             // session, so ensure we are unbound since the service has no work.
300             if (mServiceDied && !mHasActivePrintJobs) {
301                 ensureUnbound();
302                 return;
303             }
304             ensureBound();
305             mPendingCommands.add(new Runnable() {
306                 @Override
307                 public void run() {
308                     handleDestroyPrinterDiscoverySession();
309                 }
310             });
311         } else {
312             if (DEBUG) {
313                 Slog.i(LOG_TAG, "[user: " + mUserId + "] destroyPrinterDiscoverySession()");
314             }
315             try {
316                 mPrintService.destroyPrinterDiscoverySession();
317             } catch (RemoteException re) {
318                 Slog.e(LOG_TAG, "Error destroying printer dicovery session.", re);
319             }
320             // If the service has no print jobs and no active discovery
321             // session anymore we should disconnect from it.
322             if (!mHasActivePrintJobs) {
323                 ensureUnbound();
324             }
325         }
326     }
327 
startPrinterDiscovery(List<PrinterId> priorityList)328     public void startPrinterDiscovery(List<PrinterId> priorityList) {
329         Handler.getMain().sendMessage(obtainMessage(
330                 RemotePrintService::handleStartPrinterDiscovery, this, priorityList));
331     }
332 
handleStartPrinterDiscovery(final List<PrinterId> priorityList)333     private void handleStartPrinterDiscovery(final List<PrinterId> priorityList) {
334         // Take a note that we are doing discovery.
335         mDiscoveryPriorityList = new ArrayList<PrinterId>();
336         if (priorityList != null) {
337             mDiscoveryPriorityList.addAll(priorityList);
338         }
339         if (!isBound()) {
340             ensureBound();
341             mPendingCommands.add(new Runnable() {
342                 @Override
343                 public void run() {
344                     handleStartPrinterDiscovery(priorityList);
345                 }
346             });
347         } else {
348             if (DEBUG) {
349                 Slog.i(LOG_TAG, "[user: " + mUserId + "] startPrinterDiscovery()");
350             }
351             try {
352                 mPrintService.startPrinterDiscovery(priorityList);
353             } catch (RemoteException re) {
354                 Slog.e(LOG_TAG, "Error starting printer dicovery.", re);
355             }
356         }
357     }
358 
stopPrinterDiscovery()359     public void stopPrinterDiscovery() {
360         Handler.getMain().sendMessage(obtainMessage(
361                 RemotePrintService::handleStopPrinterDiscovery, this));
362     }
363 
handleStopPrinterDiscovery()364     private void handleStopPrinterDiscovery() {
365         // We are not doing discovery anymore.
366         mDiscoveryPriorityList = null;
367         if (!isBound()) {
368             ensureBound();
369             mPendingCommands.add(new Runnable() {
370                 @Override
371                 public void run() {
372                     handleStopPrinterDiscovery();
373                 }
374             });
375         } else {
376             if (DEBUG) {
377                 Slog.i(LOG_TAG, "[user: " + mUserId + "] stopPrinterDiscovery()");
378             }
379 
380             // Stop tracking printers.
381             stopTrackingAllPrinters();
382 
383             try {
384                 mPrintService.stopPrinterDiscovery();
385             } catch (RemoteException re) {
386                 Slog.e(LOG_TAG, "Error stopping printer discovery.", re);
387             }
388         }
389     }
390 
validatePrinters(List<PrinterId> printerIds)391     public void validatePrinters(List<PrinterId> printerIds) {
392         Handler.getMain().sendMessage(obtainMessage(
393                 RemotePrintService::handleValidatePrinters, this, printerIds));
394     }
395 
handleValidatePrinters(final List<PrinterId> printerIds)396     private void handleValidatePrinters(final List<PrinterId> printerIds) {
397         if (!isBound()) {
398             ensureBound();
399             mPendingCommands.add(new Runnable() {
400                 @Override
401                 public void run() {
402                     handleValidatePrinters(printerIds);
403                 }
404             });
405         } else {
406             if (DEBUG) {
407                 Slog.i(LOG_TAG, "[user: " + mUserId + "] validatePrinters()");
408             }
409             try {
410                 mPrintService.validatePrinters(printerIds);
411             } catch (RemoteException re) {
412                 Slog.e(LOG_TAG, "Error requesting printers validation.", re);
413             }
414         }
415     }
416 
startPrinterStateTracking(@onNull PrinterId printerId)417     public void startPrinterStateTracking(@NonNull PrinterId printerId) {
418         Handler.getMain().sendMessage(obtainMessage(
419                 RemotePrintService::handleStartPrinterStateTracking, this, printerId));
420     }
421 
422     /**
423      * Queue a request for a custom printer icon for a printer.
424      *
425      * @param printerId the id of the printer the icon should be loaded for
426      * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon
427      */
requestCustomPrinterIcon(@onNull PrinterId printerId)428     public void requestCustomPrinterIcon(@NonNull PrinterId printerId) {
429         Handler.getMain().sendMessage(obtainMessage(
430                 RemotePrintService::handleRequestCustomPrinterIcon, this, printerId));
431     }
432 
433     /**
434      * Request a custom printer icon for a printer.
435      *
436      * @param printerId the id of the printer the icon should be loaded for
437      * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon
438      */
handleRequestCustomPrinterIcon(@onNull PrinterId printerId)439     private void handleRequestCustomPrinterIcon(@NonNull PrinterId printerId) {
440         if (!isBound()) {
441             ensureBound();
442             mPendingCommands.add(() -> handleRequestCustomPrinterIcon(printerId));
443         } else {
444             if (DEBUG) {
445                 Slog.i(LOG_TAG, "[user: " + mUserId + "] requestCustomPrinterIcon()");
446             }
447 
448             try {
449                 mPrintService.requestCustomPrinterIcon(printerId);
450             } catch (RemoteException re) {
451                 Slog.e(LOG_TAG, "Error requesting icon for " + printerId, re);
452             }
453         }
454     }
455 
handleStartPrinterStateTracking(final @NonNull PrinterId printerId)456     private void handleStartPrinterStateTracking(final @NonNull PrinterId printerId) {
457         synchronized (mLock) {
458             // Take a note we are tracking the printer.
459             if (mTrackedPrinterList == null) {
460                 mTrackedPrinterList = new ArrayList<PrinterId>();
461             }
462             mTrackedPrinterList.add(printerId);
463         }
464 
465         if (!isBound()) {
466             ensureBound();
467             mPendingCommands.add(new Runnable() {
468                 @Override
469                 public void run() {
470                     handleStartPrinterStateTracking(printerId);
471                 }
472             });
473         } else {
474             if (DEBUG) {
475                 Slog.i(LOG_TAG, "[user: " + mUserId + "] startPrinterTracking()");
476             }
477             try {
478                 mPrintService.startPrinterStateTracking(printerId);
479             } catch (RemoteException re) {
480                 Slog.e(LOG_TAG, "Error requesting start printer tracking.", re);
481             }
482         }
483     }
484 
stopPrinterStateTracking(PrinterId printerId)485     public void stopPrinterStateTracking(PrinterId printerId) {
486         Handler.getMain().sendMessage(obtainMessage(
487                 RemotePrintService::handleStopPrinterStateTracking, this, printerId));
488     }
489 
handleStopPrinterStateTracking(final PrinterId printerId)490     private void handleStopPrinterStateTracking(final PrinterId printerId) {
491         synchronized (mLock) {
492             // We are no longer tracking the printer.
493             if (mTrackedPrinterList == null || !mTrackedPrinterList.remove(printerId)) {
494                 return;
495             }
496             if (mTrackedPrinterList.isEmpty()) {
497                 mTrackedPrinterList = null;
498             }
499         }
500 
501         if (!isBound()) {
502             ensureBound();
503             mPendingCommands.add(new Runnable() {
504                 @Override
505                 public void run() {
506                     handleStopPrinterStateTracking(printerId);
507                 }
508             });
509         } else {
510             if (DEBUG) {
511                 Slog.i(LOG_TAG, "[user: " + mUserId + "] stopPrinterTracking()");
512             }
513             try {
514                 mPrintService.stopPrinterStateTracking(printerId);
515             } catch (RemoteException re) {
516                 Slog.e(LOG_TAG, "Error requesting stop printer tracking.", re);
517             }
518         }
519     }
520 
stopTrackingAllPrinters()521     private void stopTrackingAllPrinters() {
522         synchronized (mLock) {
523             if (mTrackedPrinterList == null) {
524                 return;
525             }
526             final int trackedPrinterCount = mTrackedPrinterList.size();
527             for (int i = trackedPrinterCount - 1; i >= 0; i--) {
528                 PrinterId printerId = mTrackedPrinterList.get(i);
529                 if (printerId.getServiceName().equals(mComponentName)) {
530                     handleStopPrinterStateTracking(printerId);
531                 }
532             }
533         }
534     }
535 
dump(@onNull DualDumpOutputStream proto)536     public void dump(@NonNull DualDumpOutputStream proto) {
537         writeComponentName(proto, "component_name", ActivePrintServiceProto.COMPONENT_NAME,
538                 mComponentName);
539 
540         proto.write("is_destroyed", ActivePrintServiceProto.IS_DESTROYED, mDestroyed);
541         proto.write("is_bound", ActivePrintServiceProto.IS_BOUND, isBound());
542         proto.write("has_discovery_session", ActivePrintServiceProto.HAS_DISCOVERY_SESSION,
543                 mHasPrinterDiscoverySession);
544         proto.write("has_active_print_jobs", ActivePrintServiceProto.HAS_ACTIVE_PRINT_JOBS,
545                 mHasActivePrintJobs);
546         proto.write("is_discovering_printers", ActivePrintServiceProto.IS_DISCOVERING_PRINTERS,
547                 mDiscoveryPriorityList != null);
548 
549         synchronized (mLock) {
550             if (mTrackedPrinterList != null) {
551                 int numTrackedPrinters = mTrackedPrinterList.size();
552                 for (int i = 0; i < numTrackedPrinters; i++) {
553                     writePrinterId(proto, "tracked_printers",
554                             ActivePrintServiceProto.TRACKED_PRINTERS, mTrackedPrinterList.get(i));
555                 }
556             }
557         }
558     }
559 
isBound()560     private boolean isBound() {
561         return mPrintService != null;
562     }
563 
ensureBound()564     private void ensureBound() {
565         if (isBound() || mBinding) {
566             return;
567         }
568         if (DEBUG) {
569             Slog.i(LOG_TAG, "[user: " + mUserId + "] ensureBound()");
570         }
571         mBinding = true;
572 
573         boolean wasBound = mContext.bindServiceAsUser(mIntent, mServiceConnection,
574                 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
575                         | (Flags.doNotIncludeCapabilities() ? 0 : Context.BIND_INCLUDE_CAPABILITIES)
576                         | Context.BIND_ALLOW_INSTANT,
577                 new UserHandle(mUserId));
578 
579         if (!wasBound) {
580             if (DEBUG) {
581                 Slog.i(LOG_TAG, "[user: " + mUserId + "] could not bind to " + mIntent);
582             }
583             mBinding = false;
584 
585             if (!mServiceDied) {
586                 handleBinderDied();
587             }
588         }
589     }
590 
ensureUnbound()591     private void ensureUnbound() {
592         if (!isBound() && !mBinding) {
593             return;
594         }
595         if (DEBUG) {
596             Slog.i(LOG_TAG, "[user: " + mUserId + "] ensureUnbound()");
597         }
598         mBinding = false;
599         mPendingCommands.clear();
600         mHasActivePrintJobs = false;
601         mHasPrinterDiscoverySession = false;
602         mDiscoveryPriorityList = null;
603 
604         synchronized (mLock) {
605             mTrackedPrinterList = null;
606         }
607 
608         if (isBound()) {
609             try {
610                 mPrintService.setClient(null);
611             } catch (RemoteException re) {
612                 /* ignore */
613             }
614             mPrintService.asBinder().unlinkToDeath(this, 0);
615             mPrintService = null;
616             mContext.unbindService(mServiceConnection);
617         }
618     }
619 
620     private class RemoteServiceConneciton implements ServiceConnection {
621         @Override
onServiceConnected(ComponentName name, IBinder service)622         public void onServiceConnected(ComponentName name, IBinder service) {
623             if (mDestroyed || !mBinding) {
624                 mContext.unbindService(mServiceConnection);
625                 return;
626             }
627             mBinding = false;
628             mPrintService = IPrintService.Stub.asInterface(service);
629             try {
630                 service.linkToDeath(RemotePrintService.this, 0);
631             } catch (RemoteException re) {
632                 handleBinderDied();
633                 return;
634             }
635             try {
636                 mPrintService.setClient(mPrintServiceClient);
637             } catch (RemoteException re) {
638                 Slog.e(LOG_TAG, "Error setting client for: " + service, re);
639                 handleBinderDied();
640                 return;
641             }
642             // If the service died and there is a discovery session, recreate it.
643             if (mServiceDied && mHasPrinterDiscoverySession) {
644                 handleCreatePrinterDiscoverySession();
645             }
646             // If the service died and there is discovery started, restart it.
647             if (mServiceDied && mDiscoveryPriorityList != null) {
648                 handleStartPrinterDiscovery(mDiscoveryPriorityList);
649             }
650             synchronized (mLock) {
651                 // If the service died and printers were tracked, start tracking.
652                 if (mServiceDied && mTrackedPrinterList != null) {
653                     final int trackedPrinterCount = mTrackedPrinterList.size();
654                     for (int i = 0; i < trackedPrinterCount; i++) {
655                         handleStartPrinterStateTracking(mTrackedPrinterList.get(i));
656                     }
657                 }
658             }
659             // Finally, do all the pending work.
660             while (!mPendingCommands.isEmpty()) {
661                 Runnable pendingCommand = mPendingCommands.remove(0);
662                 pendingCommand.run();
663             }
664             // We did a best effort to get to the last state if we crashed.
665             // If we do not have print jobs and no discovery is in progress,
666             // then no need to be bound.
667             if (!mHasPrinterDiscoverySession && !mHasActivePrintJobs) {
668                 ensureUnbound();
669             }
670             mServiceDied = false;
671         }
672 
673         @Override
onServiceDisconnected(ComponentName name)674         public void onServiceDisconnected(ComponentName name) {
675             mBinding = true;
676         }
677     }
678 
679     private static final class RemotePrintServiceClient extends IPrintServiceClient.Stub {
680         private final WeakReference<RemotePrintService> mWeakService;
681 
RemotePrintServiceClient(RemotePrintService service)682         public RemotePrintServiceClient(RemotePrintService service) {
683             mWeakService = new WeakReference<RemotePrintService>(service);
684         }
685 
686         @Override
getPrintJobInfos()687         public List<PrintJobInfo> getPrintJobInfos() {
688             RemotePrintService service = mWeakService.get();
689             if (service != null) {
690                 final long identity = Binder.clearCallingIdentity();
691                 try {
692                     return service.mSpooler.getPrintJobInfos(service.mComponentName,
693                             PrintJobInfo.STATE_ANY_SCHEDULED, PrintManager.APP_ID_ANY);
694                 } finally {
695                     Binder.restoreCallingIdentity(identity);
696                 }
697             }
698             return null;
699         }
700 
701         @Override
getPrintJobInfo(PrintJobId printJobId)702         public PrintJobInfo getPrintJobInfo(PrintJobId printJobId) {
703             RemotePrintService service = mWeakService.get();
704             if (service != null) {
705                 final long identity = Binder.clearCallingIdentity();
706                 try {
707                     return service.mSpooler.getPrintJobInfo(printJobId,
708                             PrintManager.APP_ID_ANY);
709                 } finally {
710                     Binder.restoreCallingIdentity(identity);
711                 }
712             }
713             return null;
714         }
715 
716         @Override
setPrintJobState(PrintJobId printJobId, int state, String error)717         public boolean setPrintJobState(PrintJobId printJobId, int state, String error) {
718             RemotePrintService service = mWeakService.get();
719             if (service != null) {
720                 final long identity = Binder.clearCallingIdentity();
721                 try {
722                     return service.mSpooler.setPrintJobState(printJobId, state, error);
723                 } finally {
724                     Binder.restoreCallingIdentity(identity);
725                 }
726             }
727             return false;
728         }
729 
730         @Override
setPrintJobTag(PrintJobId printJobId, String tag)731         public boolean setPrintJobTag(PrintJobId printJobId, String tag) {
732             RemotePrintService service = mWeakService.get();
733             if (service != null) {
734                 final long identity = Binder.clearCallingIdentity();
735                 try {
736                     return service.mSpooler.setPrintJobTag(printJobId, tag);
737                 } finally {
738                     Binder.restoreCallingIdentity(identity);
739                 }
740             }
741             return false;
742         }
743 
744         @Override
writePrintJobData(ParcelFileDescriptor fd, PrintJobId printJobId)745         public void writePrintJobData(ParcelFileDescriptor fd, PrintJobId printJobId) {
746             RemotePrintService service = mWeakService.get();
747             if (service != null) {
748                 final long identity = Binder.clearCallingIdentity();
749                 try {
750                     service.mSpooler.writePrintJobData(fd, printJobId);
751                 } finally {
752                     Binder.restoreCallingIdentity(identity);
753                 }
754             }
755         }
756 
757         @Override
setProgress(@onNull PrintJobId printJobId, @FloatRange(from=0.0, to=1.0) float progress)758         public void setProgress(@NonNull PrintJobId printJobId,
759                 @FloatRange(from=0.0, to=1.0) float progress) {
760             RemotePrintService service = mWeakService.get();
761             if (service != null) {
762                 final long identity = Binder.clearCallingIdentity();
763                 try {
764                     service.mSpooler.setProgress(printJobId, progress);
765                 } finally {
766                     Binder.restoreCallingIdentity(identity);
767                 }
768             }
769         }
770 
771         @Override
setStatus(@onNull PrintJobId printJobId, @Nullable CharSequence status)772         public void setStatus(@NonNull PrintJobId printJobId, @Nullable CharSequence status) {
773             RemotePrintService service = mWeakService.get();
774             if (service != null) {
775                 final long identity = Binder.clearCallingIdentity();
776                 try {
777                     service.mSpooler.setStatus(printJobId, status);
778                 } finally {
779                     Binder.restoreCallingIdentity(identity);
780                 }
781             }
782         }
783 
784         @Override
setStatusRes(@onNull PrintJobId printJobId, @StringRes int status, @NonNull CharSequence appPackageName)785         public void setStatusRes(@NonNull PrintJobId printJobId, @StringRes int status,
786                 @NonNull CharSequence appPackageName) {
787             RemotePrintService service = mWeakService.get();
788             if (service != null) {
789                 final long identity = Binder.clearCallingIdentity();
790                 try {
791                     service.mSpooler.setStatus(printJobId, status, appPackageName);
792                 } finally {
793                     Binder.restoreCallingIdentity(identity);
794                 }
795             }
796         }
797 
798         @Override
799         @SuppressWarnings({"rawtypes", "unchecked"})
onPrintersAdded(ParceledListSlice printers)800         public void onPrintersAdded(ParceledListSlice printers) {
801             RemotePrintService service = mWeakService.get();
802             if (service != null) {
803                 List<PrinterInfo> addedPrinters = (List<PrinterInfo>) printers.getList();
804                 throwIfPrinterIdsForPrinterInfoTampered(service.mComponentName, addedPrinters);
805                 final long identity = Binder.clearCallingIdentity();
806                 try {
807                     service.mCallbacks.onPrintersAdded(addedPrinters);
808                 } finally {
809                     Binder.restoreCallingIdentity(identity);
810                 }
811             }
812         }
813 
814         @Override
815         @SuppressWarnings({"rawtypes", "unchecked"})
onPrintersRemoved(ParceledListSlice printerIds)816         public void onPrintersRemoved(ParceledListSlice printerIds) {
817             RemotePrintService service = mWeakService.get();
818             if (service != null) {
819                 List<PrinterId> removedPrinterIds = (List<PrinterId>) printerIds.getList();
820                 throwIfPrinterIdsTampered(service.mComponentName, removedPrinterIds);
821                 final long identity = Binder.clearCallingIdentity();
822                 try {
823                     service.mCallbacks.onPrintersRemoved(removedPrinterIds);
824                 } finally {
825                     Binder.restoreCallingIdentity(identity);
826                 }
827             }
828         }
829 
throwIfPrinterIdsForPrinterInfoTampered(ComponentName serviceName, List<PrinterInfo> printerInfos)830         private void throwIfPrinterIdsForPrinterInfoTampered(ComponentName serviceName,
831                 List<PrinterInfo> printerInfos) {
832             final int printerInfoCount = printerInfos.size();
833             for (int i = 0; i < printerInfoCount; i++) {
834                 PrinterId printerId = printerInfos.get(i).getId();
835                 throwIfPrinterIdTampered(serviceName, printerId);
836             }
837         }
838 
throwIfPrinterIdsTampered(ComponentName serviceName, List<PrinterId> printerIds)839         private void throwIfPrinterIdsTampered(ComponentName serviceName,
840                 List<PrinterId> printerIds) {
841             final int printerIdCount = printerIds.size();
842             for (int i = 0; i < printerIdCount; i++) {
843                 PrinterId printerId = printerIds.get(i);
844                 throwIfPrinterIdTampered(serviceName, printerId);
845             }
846         }
847 
throwIfPrinterIdTampered(ComponentName serviceName, PrinterId printerId)848         private void throwIfPrinterIdTampered(ComponentName serviceName, PrinterId printerId) {
849             if (printerId == null || !printerId.getServiceName().equals(serviceName)) {
850                 throw new IllegalArgumentException("Invalid printer id: " + printerId);
851             }
852         }
853 
854         @Override
onCustomPrinterIconLoaded(PrinterId printerId, Icon icon)855         public void onCustomPrinterIconLoaded(PrinterId printerId, Icon icon)
856                 throws RemoteException {
857             RemotePrintService service = mWeakService.get();
858             if (service != null) {
859                 final long identity = Binder.clearCallingIdentity();
860                 try {
861                     service.mCallbacks.onCustomPrinterIconLoaded(printerId, icon);
862                 } finally {
863                     Binder.restoreCallingIdentity(identity);
864                 }
865             }
866         }
867     }
868 }
869