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.graphics.drawable.Icon;
     28 import android.os.Binder;
     29 import android.os.Build;
     30 import android.os.IBinder;
     31 import android.os.ParcelFileDescriptor;
     32 import android.os.RemoteException;
     33 import android.os.SystemClock;
     34 import android.os.UserHandle;
     35 import android.print.IPrintSpooler;
     36 import android.print.IPrintSpoolerCallbacks;
     37 import android.print.IPrintSpoolerClient;
     38 import android.print.PrintJobId;
     39 import android.print.PrintJobInfo;
     40 import android.print.PrintManager;
     41 import android.print.PrinterId;
     42 import android.printservice.PrintService;
     43 import android.util.Slog;
     44 import android.util.TimedRemoteCaller;
     45 
     46 import libcore.io.IoUtils;
     47 
     48 import java.io.FileDescriptor;
     49 import java.io.PrintWriter;
     50 import java.lang.ref.WeakReference;
     51 import java.util.List;
     52 import java.util.concurrent.TimeoutException;
     53 
     54 /**
     55  * This represents the remote print spooler as a local object to the
     56  * PrintManagerService. It is responsible to connecting to the remote
     57  * spooler if needed, to make the timed remote calls, to handle
     58  * remote exceptions, and to bind/unbind to the remote instance as
     59  * needed.
     60  */
     61 final class RemotePrintSpooler {
     62 
     63     private static final String LOG_TAG = "RemotePrintSpooler";
     64 
     65     private static final boolean DEBUG = false;
     66 
     67     private static final long BIND_SPOOLER_SERVICE_TIMEOUT =
     68             ("eng".equals(Build.TYPE)) ? 120000 : 10000;
     69 
     70     private final Object mLock = new Object();
     71 
     72     private final GetPrintJobInfosCaller mGetPrintJobInfosCaller = new GetPrintJobInfosCaller();
     73 
     74     private final GetPrintJobInfoCaller mGetPrintJobInfoCaller = new GetPrintJobInfoCaller();
     75 
     76     private final SetPrintJobStateCaller mSetPrintJobStatusCaller = new SetPrintJobStateCaller();
     77 
     78     private final SetPrintJobTagCaller mSetPrintJobTagCaller = new SetPrintJobTagCaller();
     79 
     80     private final OnCustomPrinterIconLoadedCaller mCustomPrinterIconLoadedCaller =
     81             new OnCustomPrinterIconLoadedCaller();
     82 
     83     private final ClearCustomPrinterIconCacheCaller mClearCustomPrinterIconCache =
     84             new ClearCustomPrinterIconCacheCaller();
     85 
     86     private final GetCustomPrinterIconCaller mGetCustomPrinterIconCaller =
     87             new GetCustomPrinterIconCaller();
     88 
     89     private final ServiceConnection mServiceConnection = new MyServiceConnection();
     90 
     91     private final Context mContext;
     92 
     93     private final UserHandle mUserHandle;
     94 
     95     private final PrintSpoolerClient mClient;
     96 
     97     private final Intent mIntent;
     98 
     99     private final PrintSpoolerCallbacks mCallbacks;
    100 
    101     private boolean mIsLowPriority;
    102 
    103     private IPrintSpooler mRemoteInstance;
    104 
    105     private boolean mDestroyed;
    106 
    107     private boolean mCanUnbind;
    108 
    109     public static interface PrintSpoolerCallbacks {
    110         public void onPrintJobQueued(PrintJobInfo printJob);
    111         public void onAllPrintJobsForServiceHandled(ComponentName printService);
    112         public void onPrintJobStateChanged(PrintJobInfo printJob);
    113     }
    114 
    115     public RemotePrintSpooler(Context context, int userId, boolean lowPriority,
    116             PrintSpoolerCallbacks callbacks) {
    117         mContext = context;
    118         mUserHandle = new UserHandle(userId);
    119         mCallbacks = callbacks;
    120         mIsLowPriority = lowPriority;
    121         mClient = new PrintSpoolerClient(this);
    122         mIntent = new Intent();
    123         mIntent.setComponent(new ComponentName(PrintManager.PRINT_SPOOLER_PACKAGE_NAME,
    124                 PrintManager.PRINT_SPOOLER_PACKAGE_NAME + ".model.PrintSpoolerService"));
    125     }
    126 
    127     public void increasePriority() {
    128         if (mIsLowPriority) {
    129             mIsLowPriority = false;
    130 
    131             synchronized (mLock) {
    132                 throwIfDestroyedLocked();
    133 
    134                 while (!mCanUnbind) {
    135                     try {
    136                         mLock.wait();
    137                     } catch (InterruptedException e) {
    138                         Slog.e(LOG_TAG, "Interrupted while waiting for operation to complete");
    139                     }
    140                 }
    141 
    142                 if (DEBUG) {
    143                     Slog.i(LOG_TAG, "Unbinding as previous binding was low priority");
    144                 }
    145 
    146                 unbindLocked();
    147             }
    148         }
    149     }
    150 
    151     public final List<PrintJobInfo> getPrintJobInfos(ComponentName componentName, int state,
    152             int appId) {
    153         throwIfCalledOnMainThread();
    154         synchronized (mLock) {
    155             throwIfDestroyedLocked();
    156             mCanUnbind = false;
    157         }
    158         try {
    159             return mGetPrintJobInfosCaller.getPrintJobInfos(getRemoteInstanceLazy(),
    160                     componentName, state, appId);
    161         } catch (RemoteException re) {
    162             Slog.e(LOG_TAG, "Error getting print jobs.", re);
    163         } catch (TimeoutException te) {
    164             Slog.e(LOG_TAG, "Error getting print jobs.", te);
    165         } finally {
    166             if (DEBUG) {
    167                 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] getPrintJobInfos()");
    168             }
    169             synchronized (mLock) {
    170                 mCanUnbind = true;
    171                 mLock.notifyAll();
    172             }
    173         }
    174         return null;
    175     }
    176 
    177     public final void createPrintJob(PrintJobInfo printJob) {
    178         throwIfCalledOnMainThread();
    179         synchronized (mLock) {
    180             throwIfDestroyedLocked();
    181             mCanUnbind = false;
    182         }
    183         try {
    184             getRemoteInstanceLazy().createPrintJob(printJob);
    185         } catch (RemoteException re) {
    186             Slog.e(LOG_TAG, "Error creating print job.", re);
    187         } catch (TimeoutException te) {
    188             Slog.e(LOG_TAG, "Error creating print job.", te);
    189         } finally {
    190             if (DEBUG) {
    191                 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] createPrintJob()");
    192             }
    193             synchronized (mLock) {
    194                 mCanUnbind = true;
    195                 mLock.notifyAll();
    196             }
    197         }
    198     }
    199 
    200     public final void writePrintJobData(ParcelFileDescriptor fd, PrintJobId printJobId) {
    201         throwIfCalledOnMainThread();
    202         synchronized (mLock) {
    203             throwIfDestroyedLocked();
    204             mCanUnbind = false;
    205         }
    206         try {
    207             getRemoteInstanceLazy().writePrintJobData(fd, printJobId);
    208         } catch (RemoteException re) {
    209             Slog.e(LOG_TAG, "Error writing print job data.", re);
    210         } catch (TimeoutException te) {
    211             Slog.e(LOG_TAG, "Error writing print job data.", te);
    212         } finally {
    213             if (DEBUG) {
    214                 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] writePrintJobData()");
    215             }
    216             // We passed the file descriptor across and now the other
    217             // side is responsible to close it, so close the local copy.
    218             IoUtils.closeQuietly(fd);
    219             synchronized (mLock) {
    220                 mCanUnbind = true;
    221                 mLock.notifyAll();
    222             }
    223         }
    224     }
    225 
    226     public final PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId) {
    227         throwIfCalledOnMainThread();
    228         synchronized (mLock) {
    229             throwIfDestroyedLocked();
    230             mCanUnbind = false;
    231         }
    232         try {
    233             return mGetPrintJobInfoCaller.getPrintJobInfo(getRemoteInstanceLazy(),
    234                     printJobId, appId);
    235         } catch (RemoteException re) {
    236             Slog.e(LOG_TAG, "Error getting print job info.", re);
    237         } catch (TimeoutException te) {
    238             Slog.e(LOG_TAG, "Error getting print job info.", te);
    239         } finally {
    240             if (DEBUG) {
    241                 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] getPrintJobInfo()");
    242             }
    243             synchronized (mLock) {
    244                 mCanUnbind = true;
    245                 mLock.notifyAll();
    246             }
    247         }
    248         return null;
    249     }
    250 
    251     public final boolean setPrintJobState(PrintJobId printJobId, int state, String error) {
    252         throwIfCalledOnMainThread();
    253         synchronized (mLock) {
    254             throwIfDestroyedLocked();
    255             mCanUnbind = false;
    256         }
    257         try {
    258             return mSetPrintJobStatusCaller.setPrintJobState(getRemoteInstanceLazy(),
    259                     printJobId, state, error);
    260         } catch (RemoteException re) {
    261             Slog.e(LOG_TAG, "Error setting print job state.", re);
    262         } catch (TimeoutException te) {
    263             Slog.e(LOG_TAG, "Error setting print job state.", te);
    264         } finally {
    265             if (DEBUG) {
    266                 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setPrintJobState()");
    267             }
    268             synchronized (mLock) {
    269                 mCanUnbind = true;
    270                 mLock.notifyAll();
    271             }
    272         }
    273         return false;
    274     }
    275 
    276     /**
    277      * Set progress of a print job.
    278      *
    279      * @param printJobId The print job to update
    280      * @param progress The new progress
    281      */
    282     public final void setProgress(@NonNull PrintJobId printJobId,
    283             @FloatRange(from=0.0, to=1.0) float progress) {
    284         throwIfCalledOnMainThread();
    285         synchronized (mLock) {
    286             throwIfDestroyedLocked();
    287             mCanUnbind = false;
    288         }
    289         try {
    290             getRemoteInstanceLazy().setProgress(printJobId, progress);
    291         } catch (RemoteException|TimeoutException re) {
    292             Slog.e(LOG_TAG, "Error setting progress.", re);
    293         } finally {
    294             if (DEBUG) {
    295                 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setProgress()");
    296             }
    297             synchronized (mLock) {
    298                 mCanUnbind = true;
    299                 mLock.notifyAll();
    300             }
    301         }
    302     }
    303 
    304     /**
    305      * Set status of a print job.
    306      *
    307      * @param printJobId The print job to update
    308      * @param status The new status
    309      */
    310     public final void setStatus(@NonNull PrintJobId printJobId, @Nullable CharSequence status) {
    311         throwIfCalledOnMainThread();
    312         synchronized (mLock) {
    313             throwIfDestroyedLocked();
    314             mCanUnbind = false;
    315         }
    316         try {
    317             getRemoteInstanceLazy().setStatus(printJobId, status);
    318         } catch (RemoteException|TimeoutException re) {
    319             Slog.e(LOG_TAG, "Error setting status.", re);
    320         } finally {
    321             if (DEBUG) {
    322                 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setStatus()");
    323             }
    324             synchronized (mLock) {
    325                 mCanUnbind = true;
    326                 mLock.notifyAll();
    327             }
    328         }
    329     }
    330 
    331     /**
    332      * Set status of a print job.
    333      *
    334      * @param printJobId The print job to update
    335      * @param status The new status as a string resource
    336      * @param appPackageName The app package name the string res belongs to
    337      */
    338     public final void setStatus(@NonNull PrintJobId printJobId, @StringRes int status,
    339             @NonNull CharSequence appPackageName) {
    340         throwIfCalledOnMainThread();
    341         synchronized (mLock) {
    342             throwIfDestroyedLocked();
    343             mCanUnbind = false;
    344         }
    345         try {
    346             getRemoteInstanceLazy().setStatusRes(printJobId, status, appPackageName);
    347         } catch (RemoteException|TimeoutException re) {
    348             Slog.e(LOG_TAG, "Error setting status.", re);
    349         } finally {
    350             if (DEBUG) {
    351                 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setStatus()");
    352             }
    353             synchronized (mLock) {
    354                 mCanUnbind = true;
    355                 mLock.notifyAll();
    356             }
    357         }
    358     }
    359 
    360     /**
    361      * Handle that a custom icon for a printer was loaded.
    362      *
    363      * @param printerId the id of the printer the icon belongs to
    364      * @param icon the icon that was loaded
    365      * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon()
    366      */
    367     public final void onCustomPrinterIconLoaded(@NonNull PrinterId printerId,
    368             @Nullable Icon icon) {
    369         throwIfCalledOnMainThread();
    370         synchronized (mLock) {
    371             throwIfDestroyedLocked();
    372             mCanUnbind = false;
    373         }
    374         try {
    375             mCustomPrinterIconLoadedCaller.onCustomPrinterIconLoaded(getRemoteInstanceLazy(),
    376                     printerId, icon);
    377         } catch (RemoteException|TimeoutException re) {
    378             Slog.e(LOG_TAG, "Error loading new custom printer icon.", re);
    379         } finally {
    380             if (DEBUG) {
    381                 Slog.i(LOG_TAG,
    382                         "[user: " + mUserHandle.getIdentifier() + "] onCustomPrinterIconLoaded()");
    383             }
    384             synchronized (mLock) {
    385                 mCanUnbind = true;
    386                 mLock.notifyAll();
    387             }
    388         }
    389     }
    390 
    391     /**
    392      * Get the custom icon for a printer. If the icon is not cached, the icon is
    393      * requested asynchronously. Once it is available the printer is updated.
    394      *
    395      * @param printerId the id of the printer the icon should be loaded for
    396      * @return the custom icon to be used for the printer or null if the icon is
    397      *         not yet available
    398      * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon()
    399      */
    400     public final @Nullable Icon getCustomPrinterIcon(@NonNull PrinterId printerId) {
    401         throwIfCalledOnMainThread();
    402         synchronized (mLock) {
    403             throwIfDestroyedLocked();
    404             mCanUnbind = false;
    405         }
    406         try {
    407             return mGetCustomPrinterIconCaller.getCustomPrinterIcon(getRemoteInstanceLazy(),
    408                     printerId);
    409         } catch (RemoteException|TimeoutException re) {
    410             Slog.e(LOG_TAG, "Error getting custom printer icon.", re);
    411             return null;
    412         } finally {
    413             if (DEBUG) {
    414                 Slog.i(LOG_TAG,
    415                         "[user: " + mUserHandle.getIdentifier() + "] getCustomPrinterIcon()");
    416             }
    417             synchronized (mLock) {
    418                 mCanUnbind = true;
    419                 mLock.notifyAll();
    420             }
    421         }
    422     }
    423 
    424     /**
    425      * Clear the custom printer icon cache
    426      */
    427     public void clearCustomPrinterIconCache() {
    428         throwIfCalledOnMainThread();
    429         synchronized (mLock) {
    430             throwIfDestroyedLocked();
    431             mCanUnbind = false;
    432         }
    433         try {
    434             mClearCustomPrinterIconCache.clearCustomPrinterIconCache(getRemoteInstanceLazy());
    435         } catch (RemoteException|TimeoutException re) {
    436             Slog.e(LOG_TAG, "Error clearing custom printer icon cache.", re);
    437         } finally {
    438             if (DEBUG) {
    439                 Slog.i(LOG_TAG,
    440                         "[user: " + mUserHandle.getIdentifier()
    441                                 + "] clearCustomPrinterIconCache()");
    442             }
    443             synchronized (mLock) {
    444                 mCanUnbind = true;
    445                 mLock.notifyAll();
    446             }
    447         }
    448     }
    449 
    450     public final boolean setPrintJobTag(PrintJobId printJobId, String tag) {
    451         throwIfCalledOnMainThread();
    452         synchronized (mLock) {
    453             throwIfDestroyedLocked();
    454             mCanUnbind = false;
    455         }
    456         try {
    457             return mSetPrintJobTagCaller.setPrintJobTag(getRemoteInstanceLazy(),
    458                     printJobId, tag);
    459         } catch (RemoteException re) {
    460             Slog.e(LOG_TAG, "Error setting print job tag.", re);
    461         } catch (TimeoutException te) {
    462             Slog.e(LOG_TAG, "Error setting print job tag.", te);
    463         } finally {
    464             if (DEBUG) {
    465                 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setPrintJobTag()");
    466             }
    467             synchronized (mLock) {
    468                 mCanUnbind = true;
    469                 mLock.notifyAll();
    470             }
    471         }
    472         return false;
    473     }
    474 
    475     public final void setPrintJobCancelling(PrintJobId printJobId, boolean cancelling) {
    476         throwIfCalledOnMainThread();
    477         synchronized (mLock) {
    478             throwIfDestroyedLocked();
    479             mCanUnbind = false;
    480         }
    481         try {
    482             getRemoteInstanceLazy().setPrintJobCancelling(printJobId,
    483                     cancelling);
    484         } catch (RemoteException re) {
    485             Slog.e(LOG_TAG, "Error setting print job cancelling.", re);
    486         } catch (TimeoutException te) {
    487             Slog.e(LOG_TAG, "Error setting print job cancelling.", te);
    488         } finally {
    489             if (DEBUG) {
    490                 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier()
    491                         + "] setPrintJobCancelling()");
    492             }
    493             synchronized (mLock) {
    494                 mCanUnbind = true;
    495                 mLock.notifyAll();
    496             }
    497         }
    498     }
    499 
    500     /**
    501      * Remove all approved {@link PrintService print services} that are not in the given set.
    502      *
    503      * @param servicesToKeep The {@link ComponentName names } of the services to keep
    504      */
    505     public final void pruneApprovedPrintServices(List<ComponentName> servicesToKeep) {
    506         throwIfCalledOnMainThread();
    507         synchronized (mLock) {
    508             throwIfDestroyedLocked();
    509             mCanUnbind = false;
    510         }
    511         try {
    512             getRemoteInstanceLazy().pruneApprovedPrintServices(servicesToKeep);
    513         } catch (RemoteException|TimeoutException re) {
    514             Slog.e(LOG_TAG, "Error pruning approved print services.", re);
    515         } finally {
    516             if (DEBUG) {
    517                 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier()
    518                         + "] pruneApprovedPrintServices()");
    519             }
    520             synchronized (mLock) {
    521                 mCanUnbind = true;
    522                 mLock.notifyAll();
    523             }
    524         }
    525     }
    526 
    527     public final void removeObsoletePrintJobs() {
    528         throwIfCalledOnMainThread();
    529         synchronized (mLock) {
    530             throwIfDestroyedLocked();
    531             mCanUnbind = false;
    532         }
    533         try {
    534             getRemoteInstanceLazy().removeObsoletePrintJobs();
    535         } catch (RemoteException re) {
    536             Slog.e(LOG_TAG, "Error removing obsolete print jobs .", re);
    537         } catch (TimeoutException te) {
    538             Slog.e(LOG_TAG, "Error removing obsolete print jobs .", te);
    539         } finally {
    540             if (DEBUG) {
    541                 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier()
    542                         + "] removeObsoletePrintJobs()");
    543             }
    544             synchronized (mLock) {
    545                 mCanUnbind = true;
    546                 mLock.notifyAll();
    547             }
    548         }
    549     }
    550 
    551     public final void destroy() {
    552         throwIfCalledOnMainThread();
    553         if (DEBUG) {
    554             Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] destroy()");
    555         }
    556         synchronized (mLock) {
    557             throwIfDestroyedLocked();
    558             unbindLocked();
    559             mDestroyed = true;
    560             mCanUnbind = false;
    561         }
    562     }
    563 
    564     public void dump(FileDescriptor fd, PrintWriter pw, String prefix) {
    565         synchronized (mLock) {
    566             pw.append(prefix).append("destroyed=")
    567                     .append(String.valueOf(mDestroyed)).println();
    568             pw.append(prefix).append("bound=")
    569                     .append((mRemoteInstance != null) ? "true" : "false").println();
    570 
    571             pw.flush();
    572 
    573             try {
    574                 getRemoteInstanceLazy().asBinder().dump(fd, new String[]{prefix});
    575             } catch (TimeoutException te) {
    576                 /* ignore */
    577             } catch (RemoteException re) {
    578                 /* ignore */
    579             }
    580         }
    581     }
    582 
    583     private void onAllPrintJobsHandled() {
    584         synchronized (mLock) {
    585             throwIfDestroyedLocked();
    586             unbindLocked();
    587         }
    588     }
    589 
    590     private void onPrintJobStateChanged(PrintJobInfo printJob) {
    591         mCallbacks.onPrintJobStateChanged(printJob);
    592     }
    593 
    594     private IPrintSpooler getRemoteInstanceLazy() throws TimeoutException {
    595         synchronized (mLock) {
    596             if (mRemoteInstance != null) {
    597                 return mRemoteInstance;
    598             }
    599             bindLocked();
    600             return mRemoteInstance;
    601         }
    602     }
    603 
    604     private void bindLocked() throws TimeoutException {
    605         if (mRemoteInstance != null) {
    606             return;
    607         }
    608         if (DEBUG) {
    609             Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] bindLocked() " +
    610                     (mIsLowPriority ? "low priority" : ""));
    611         }
    612 
    613         int flags;
    614         if (mIsLowPriority) {
    615             flags = Context.BIND_AUTO_CREATE;
    616         } else {
    617             flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE;
    618         }
    619 
    620         mContext.bindServiceAsUser(mIntent, mServiceConnection, flags, mUserHandle);
    621 
    622         final long startMillis = SystemClock.uptimeMillis();
    623         while (true) {
    624             if (mRemoteInstance != null) {
    625                 break;
    626             }
    627             final long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
    628             final long remainingMillis = BIND_SPOOLER_SERVICE_TIMEOUT - elapsedMillis;
    629             if (remainingMillis <= 0) {
    630                 throw new TimeoutException("Cannot get spooler!");
    631             }
    632             try {
    633                 mLock.wait(remainingMillis);
    634             } catch (InterruptedException ie) {
    635                 /* ignore */
    636             }
    637         }
    638 
    639         mCanUnbind = true;
    640         mLock.notifyAll();
    641     }
    642 
    643     private void unbindLocked() {
    644         if (mRemoteInstance == null) {
    645             return;
    646         }
    647         while (true) {
    648             if (mCanUnbind) {
    649                 if (DEBUG) {
    650                     Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] unbindLocked()");
    651                 }
    652                 clearClientLocked();
    653                 mRemoteInstance = null;
    654                 mContext.unbindService(mServiceConnection);
    655                 return;
    656             }
    657             try {
    658                 mLock.wait();
    659             } catch (InterruptedException ie) {
    660                 /* ignore */
    661             }
    662         }
    663 
    664     }
    665 
    666     private void setClientLocked() {
    667         try {
    668             mRemoteInstance.setClient(mClient);
    669         } catch (RemoteException re) {
    670             Slog.d(LOG_TAG, "Error setting print spooler client", re);
    671         }
    672     }
    673 
    674     private void clearClientLocked() {
    675         try {
    676             mRemoteInstance.setClient(null);
    677         } catch (RemoteException re) {
    678             Slog.d(LOG_TAG, "Error clearing print spooler client", re);
    679         }
    680 
    681     }
    682 
    683     private void throwIfDestroyedLocked() {
    684         if (mDestroyed) {
    685             throw new IllegalStateException("Cannot interact with a destroyed instance.");
    686         }
    687     }
    688 
    689     private void throwIfCalledOnMainThread() {
    690         if (Thread.currentThread() == mContext.getMainLooper().getThread()) {
    691             throw new RuntimeException("Cannot invoke on the main thread");
    692         }
    693     }
    694 
    695     private final class MyServiceConnection implements ServiceConnection {
    696         @Override
    697         public void onServiceConnected(ComponentName name, IBinder service) {
    698             synchronized (mLock) {
    699                 mRemoteInstance = IPrintSpooler.Stub.asInterface(service);
    700                 setClientLocked();
    701                 mLock.notifyAll();
    702             }
    703         }
    704 
    705         @Override
    706         public void onServiceDisconnected(ComponentName name) {
    707             synchronized (mLock) {
    708                 clearClientLocked();
    709                 mRemoteInstance = null;
    710             }
    711         }
    712     }
    713 
    714     private static final class GetPrintJobInfosCaller
    715             extends TimedRemoteCaller<List<PrintJobInfo>> {
    716         private final IPrintSpoolerCallbacks mCallback;
    717 
    718         public GetPrintJobInfosCaller() {
    719             super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
    720             mCallback = new BasePrintSpoolerServiceCallbacks() {
    721                 @Override
    722                 public void onGetPrintJobInfosResult(List<PrintJobInfo> printJobs, int sequence) {
    723                     onRemoteMethodResult(printJobs, sequence);
    724                 }
    725             };
    726         }
    727 
    728         public List<PrintJobInfo> getPrintJobInfos(IPrintSpooler target,
    729                 ComponentName componentName, int state, int appId)
    730                         throws RemoteException, TimeoutException {
    731             final int sequence = onBeforeRemoteCall();
    732             target.getPrintJobInfos(mCallback, componentName, state, appId, sequence);
    733             return getResultTimed(sequence);
    734         }
    735     }
    736 
    737     private static final class GetPrintJobInfoCaller extends TimedRemoteCaller<PrintJobInfo> {
    738         private final IPrintSpoolerCallbacks mCallback;
    739 
    740         public GetPrintJobInfoCaller() {
    741             super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
    742             mCallback = new BasePrintSpoolerServiceCallbacks() {
    743                 @Override
    744                 public void onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence) {
    745                     onRemoteMethodResult(printJob, sequence);
    746                 }
    747             };
    748         }
    749 
    750         public PrintJobInfo getPrintJobInfo(IPrintSpooler target, PrintJobId printJobId,
    751                 int appId) throws RemoteException, TimeoutException {
    752             final int sequence = onBeforeRemoteCall();
    753             target.getPrintJobInfo(printJobId, mCallback, appId, sequence);
    754             return getResultTimed(sequence);
    755         }
    756     }
    757 
    758     private static final class SetPrintJobStateCaller extends TimedRemoteCaller<Boolean> {
    759         private final IPrintSpoolerCallbacks mCallback;
    760 
    761         public SetPrintJobStateCaller() {
    762             super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
    763             mCallback = new BasePrintSpoolerServiceCallbacks() {
    764                 @Override
    765                 public void onSetPrintJobStateResult(boolean success, int sequence) {
    766                     onRemoteMethodResult(success, sequence);
    767                 }
    768             };
    769         }
    770 
    771         public boolean setPrintJobState(IPrintSpooler target, PrintJobId printJobId,
    772                 int status, String error) throws RemoteException, TimeoutException {
    773             final int sequence = onBeforeRemoteCall();
    774             target.setPrintJobState(printJobId, status, error, mCallback, sequence);
    775             return getResultTimed(sequence);
    776         }
    777     }
    778 
    779     private static final class SetPrintJobTagCaller extends TimedRemoteCaller<Boolean> {
    780         private final IPrintSpoolerCallbacks mCallback;
    781 
    782         public SetPrintJobTagCaller() {
    783             super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
    784             mCallback = new BasePrintSpoolerServiceCallbacks() {
    785                 @Override
    786                 public void onSetPrintJobTagResult(boolean success, int sequence) {
    787                     onRemoteMethodResult(success, sequence);
    788                 }
    789             };
    790         }
    791 
    792         public boolean setPrintJobTag(IPrintSpooler target, PrintJobId printJobId,
    793                 String tag) throws RemoteException, TimeoutException {
    794             final int sequence = onBeforeRemoteCall();
    795             target.setPrintJobTag(printJobId, tag, mCallback, sequence);
    796             return getResultTimed(sequence);
    797         }
    798     }
    799 
    800     private static final class OnCustomPrinterIconLoadedCaller extends TimedRemoteCaller<Void> {
    801         private final IPrintSpoolerCallbacks mCallback;
    802 
    803         public OnCustomPrinterIconLoadedCaller() {
    804             super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
    805             mCallback = new BasePrintSpoolerServiceCallbacks() {
    806                 @Override
    807                 public void onCustomPrinterIconCached(int sequence) {
    808                     onRemoteMethodResult(null, sequence);
    809                 }
    810             };
    811         }
    812 
    813         public Void onCustomPrinterIconLoaded(IPrintSpooler target, PrinterId printerId,
    814                 Icon icon) throws RemoteException, TimeoutException {
    815             final int sequence = onBeforeRemoteCall();
    816             target.onCustomPrinterIconLoaded(printerId, icon, mCallback, sequence);
    817             return getResultTimed(sequence);
    818         }
    819     }
    820 
    821     private static final class ClearCustomPrinterIconCacheCaller extends TimedRemoteCaller<Void> {
    822         private final IPrintSpoolerCallbacks mCallback;
    823 
    824         public ClearCustomPrinterIconCacheCaller() {
    825             super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
    826             mCallback = new BasePrintSpoolerServiceCallbacks() {
    827                 @Override
    828                 public void customPrinterIconCacheCleared(int sequence) {
    829                     onRemoteMethodResult(null, sequence);
    830                 }
    831             };
    832         }
    833 
    834         public Void clearCustomPrinterIconCache(IPrintSpooler target)
    835                 throws RemoteException, TimeoutException {
    836             final int sequence = onBeforeRemoteCall();
    837             target.clearCustomPrinterIconCache(mCallback, sequence);
    838             return getResultTimed(sequence);
    839         }
    840     }
    841 
    842     private static final class GetCustomPrinterIconCaller extends TimedRemoteCaller<Icon> {
    843         private final IPrintSpoolerCallbacks mCallback;
    844 
    845         public GetCustomPrinterIconCaller() {
    846             super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
    847             mCallback = new BasePrintSpoolerServiceCallbacks() {
    848                 @Override
    849                 public void onGetCustomPrinterIconResult(Icon icon, int sequence) {
    850                     onRemoteMethodResult(icon, sequence);
    851                 }
    852             };
    853         }
    854 
    855         public Icon getCustomPrinterIcon(IPrintSpooler target, PrinterId printerId)
    856                 throws RemoteException, TimeoutException {
    857             final int sequence = onBeforeRemoteCall();
    858             target.getCustomPrinterIcon(printerId, mCallback, sequence);
    859             return getResultTimed(sequence);
    860         }
    861     }
    862 
    863     private static abstract class BasePrintSpoolerServiceCallbacks
    864             extends IPrintSpoolerCallbacks.Stub {
    865         @Override
    866         public void onGetPrintJobInfosResult(List<PrintJobInfo> printJobIds, int sequence) {
    867             /* do nothing */
    868         }
    869 
    870         @Override
    871         public void onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence) {
    872             /* do nothing */
    873         }
    874 
    875         @Override
    876         public void onCancelPrintJobResult(boolean canceled, int sequence) {
    877             /* do nothing */
    878         }
    879 
    880         @Override
    881         public void onSetPrintJobStateResult(boolean success, int sequece) {
    882             /* do nothing */
    883         }
    884 
    885         @Override
    886         public void onSetPrintJobTagResult(boolean success, int sequence) {
    887             /* do nothing */
    888         }
    889 
    890         @Override
    891         public void onCustomPrinterIconCached(int sequence) {
    892             /* do nothing */
    893         }
    894 
    895         @Override
    896         public void onGetCustomPrinterIconResult(@Nullable Icon icon, int sequence) {
    897             /* do nothing */
    898         }
    899 
    900         @Override
    901         public void customPrinterIconCacheCleared(int sequence) {
    902             /* do nothing */
    903         }
    904     }
    905 
    906     private static final class PrintSpoolerClient extends IPrintSpoolerClient.Stub {
    907 
    908         private final WeakReference<RemotePrintSpooler> mWeakSpooler;
    909 
    910         public PrintSpoolerClient(RemotePrintSpooler spooler) {
    911             mWeakSpooler = new WeakReference<RemotePrintSpooler>(spooler);
    912         }
    913 
    914         @Override
    915         public void onPrintJobQueued(PrintJobInfo printJob) {
    916             RemotePrintSpooler spooler = mWeakSpooler.get();
    917             if (spooler != null) {
    918                 final long identity = Binder.clearCallingIdentity();
    919                 try {
    920                     spooler.mCallbacks.onPrintJobQueued(printJob);
    921                 } finally {
    922                     Binder.restoreCallingIdentity(identity);
    923                 }
    924             }
    925         }
    926 
    927         @Override
    928         public void onAllPrintJobsForServiceHandled(ComponentName printService) {
    929             RemotePrintSpooler spooler = mWeakSpooler.get();
    930             if (spooler != null) {
    931                 final long identity = Binder.clearCallingIdentity();
    932                 try {
    933                     spooler.mCallbacks.onAllPrintJobsForServiceHandled(printService);
    934                 } finally {
    935                     Binder.restoreCallingIdentity(identity);
    936                 }
    937             }
    938         }
    939 
    940         @Override
    941         public void onAllPrintJobsHandled() {
    942             RemotePrintSpooler spooler = mWeakSpooler.get();
    943             if (spooler != null) {
    944                 final long identity = Binder.clearCallingIdentity();
    945                 try {
    946                     spooler.onAllPrintJobsHandled();
    947                 } finally {
    948                     Binder.restoreCallingIdentity(identity);
    949                 }
    950             }
    951         }
    952 
    953         @Override
    954         public void onPrintJobStateChanged(PrintJobInfo printJob) {
    955             RemotePrintSpooler spooler = mWeakSpooler.get();
    956             if (spooler != null) {
    957                 final long identity = Binder.clearCallingIdentity();
    958                 try {
    959                     spooler.onPrintJobStateChanged(printJob);
    960                 } finally {
    961                     Binder.restoreCallingIdentity(identity);
    962                 }
    963             }
    964         }
    965     }
    966 }
    967