Home | History | Annotate | Download | only in print
      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 {
     95         public void onPrintersAdded(List<PrinterInfo> printers);
     96         public void onPrintersRemoved(List<PrinterId> printerIds);
     97         public void onServiceDied(RemotePrintService service);
     98     }
     99 
    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 
    112     public ComponentName getComponentName() {
    113         return mComponentName;
    114     }
    115 
    116     public void destroy() {
    117         mHandler.sendEmptyMessage(MyHandler.MSG_DESTROY);
    118     }
    119 
    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
    152     public void binderDied() {
    153         mHandler.sendEmptyMessage(MyHandler.MSG_BINDER_DIED);
    154     }
    155 
    156     private void handleBinderDied() {
    157         mPrintService.asBinder().unlinkToDeath(this, 0);
    158         mPrintService = null;
    159         mServiceDied = true;
    160         mCallbacks.onServiceDied(this);
    161     }
    162 
    163     public void onAllPrintJobsHandled() {
    164         mHandler.sendEmptyMessage(MyHandler.MSG_ON_ALL_PRINT_JOBS_HANDLED);
    165     }
    166 
    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 
    196     public void onRequestCancelPrintJob(PrintJobInfo printJob) {
    197         mHandler.obtainMessage(MyHandler.MSG_ON_REQUEST_CANCEL_PRINT_JOB,
    198                 printJob).sendToTarget();
    199     }
    200 
    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 
    223     public void onPrintJobQueued(PrintJobInfo printJob) {
    224         mHandler.obtainMessage(MyHandler.MSG_ON_PRINT_JOB_QUEUED,
    225                 printJob).sendToTarget();
    226     }
    227 
    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 
    251     public void createPrinterDiscoverySession() {
    252         mHandler.sendEmptyMessage(MyHandler.MSG_CREATE_PRINTER_DISCOVERY_SESSION);
    253     }
    254 
    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 
    278     public void destroyPrinterDiscoverySession() {
    279         mHandler.sendEmptyMessage(MyHandler.MSG_DESTROY_PRINTER_DISCOVERY_SESSION);
    280     }
    281 
    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 
    316     public void startPrinterDiscovery(List<PrinterId> priorityList) {
    317         mHandler.obtainMessage(MyHandler.MSG_START_PRINTER_DISCOVERY,
    318                 priorityList).sendToTarget();
    319     }
    320 
    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 
    348     public void stopPrinterDiscovery() {
    349         mHandler.sendEmptyMessage(MyHandler.MSG_STOP_PRINTER_DISCOVERY);
    350     }
    351 
    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 
    376     public void validatePrinters(List<PrinterId> printerIds) {
    377         mHandler.obtainMessage(MyHandler.MSG_VALIDATE_PRINTERS,
    378                 printerIds).sendToTarget();
    379     }
    380 
    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 
    403     public void startPrinterStateTracking(PrinterId printerId) {
    404         mHandler.obtainMessage(MyHandler.MSG_START_PRINTER_STATE_TRACKING,
    405                 printerId).sendToTarget();
    406     }
    407 
    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 
    435     public void stopPrinterStateTracking(PrinterId printerId) {
    436         mHandler.obtainMessage(MyHandler.MSG_STOP_PRINTER_STATE_TRACKING,
    437                 printerId).sendToTarget();
    438     }
    439 
    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 
    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 
    488     private boolean isBound() {
    489         return mPrintService != null;
    490     }
    491 
    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 
    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 
    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
    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
    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 
    606         public MyHandler(Looper looper) {
    607             super(looper, null, false);
    608         }
    609 
    610         @Override
    611         @SuppressWarnings("unchecked")
    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 
    674         public RemotePrintServiceClient(RemotePrintService service) {
    675             mWeakService = new WeakReference<RemotePrintService>(service);
    676         }
    677 
    678         @Override
    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
    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
    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
    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
    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"})
    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"})
    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 
    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 
    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 
    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