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.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 {
    100         public void onPrintersAdded(List<PrinterInfo> printers);
    101         public void onPrintersRemoved(List<PrinterId> printerIds);
    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          */
    111         public void onCustomPrinterIconLoaded(PrinterId printerId, Icon icon);
    112     }
    113 
    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 
    126     public ComponentName getComponentName() {
    127         return mComponentName;
    128     }
    129 
    130     public void destroy() {
    131         mHandler.sendEmptyMessage(MyHandler.MSG_DESTROY);
    132     }
    133 
    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
    156     public void binderDied() {
    157         mHandler.sendEmptyMessage(MyHandler.MSG_BINDER_DIED);
    158     }
    159 
    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 
    170     public void onAllPrintJobsHandled() {
    171         mHandler.sendEmptyMessage(MyHandler.MSG_ON_ALL_PRINT_JOBS_HANDLED);
    172     }
    173 
    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 
    202     public void onRequestCancelPrintJob(PrintJobInfo printJob) {
    203         mHandler.obtainMessage(MyHandler.MSG_ON_REQUEST_CANCEL_PRINT_JOB,
    204                 printJob).sendToTarget();
    205     }
    206 
    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 
    228     public void onPrintJobQueued(PrintJobInfo printJob) {
    229         mHandler.obtainMessage(MyHandler.MSG_ON_PRINT_JOB_QUEUED,
    230                 printJob).sendToTarget();
    231     }
    232 
    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 
    255     public void createPrinterDiscoverySession() {
    256         mHandler.sendEmptyMessage(MyHandler.MSG_CREATE_PRINTER_DISCOVERY_SESSION);
    257     }
    258 
    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 
    281     public void destroyPrinterDiscoverySession() {
    282         mHandler.sendEmptyMessage(MyHandler.MSG_DESTROY_PRINTER_DISCOVERY_SESSION);
    283     }
    284 
    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 
    318     public void startPrinterDiscovery(List<PrinterId> priorityList) {
    319         mHandler.obtainMessage(MyHandler.MSG_START_PRINTER_DISCOVERY,
    320                 priorityList).sendToTarget();
    321     }
    322 
    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 
    349     public void stopPrinterDiscovery() {
    350         mHandler.sendEmptyMessage(MyHandler.MSG_STOP_PRINTER_DISCOVERY);
    351     }
    352 
    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 
    380     public void validatePrinters(List<PrinterId> printerIds) {
    381         mHandler.obtainMessage(MyHandler.MSG_VALIDATE_PRINTERS,
    382                 printerIds).sendToTarget();
    383     }
    384 
    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 
    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      */
    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      */
    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 
    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 
    471     public void stopPrinterStateTracking(PrinterId printerId) {
    472         mHandler.obtainMessage(MyHandler.MSG_STOP_PRINTER_STATE_TRACKING,
    473                 printerId).sendToTarget();
    474     }
    475 
    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 
    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 
    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 
    536     private boolean isBound() {
    537         return mPrintService != null;
    538     }
    539 
    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 
    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
    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
    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 
    662         public MyHandler(Looper looper) {
    663             super(looper, null, false);
    664         }
    665 
    666         @Override
    667         @SuppressWarnings("unchecked")
    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 
    740         public RemotePrintServiceClient(RemotePrintService service) {
    741             mWeakService = new WeakReference<RemotePrintService>(service);
    742         }
    743 
    744         @Override
    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
    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
    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
    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
    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
    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
    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
    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"})
    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"})
    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 
    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 
    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 
    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
    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