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