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