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.os.Binder; 24 import android.os.IBinder; 25 import android.os.ParcelFileDescriptor; 26 import android.os.RemoteException; 27 import android.os.SystemClock; 28 import android.os.UserHandle; 29 import android.print.IPrintSpooler; 30 import android.print.IPrintSpoolerCallbacks; 31 import android.print.IPrintSpoolerClient; 32 import android.print.PrintJobId; 33 import android.print.PrintJobInfo; 34 import android.util.Slog; 35 import android.util.TimedRemoteCaller; 36 37 import java.io.FileDescriptor; 38 import java.io.PrintWriter; 39 import java.lang.ref.WeakReference; 40 import java.util.List; 41 import java.util.concurrent.TimeoutException; 42 43 import libcore.io.IoUtils; 44 45 /** 46 * This represents the remote print spooler as a local object to the 47 * PrintManagerSerivce. It is responsible to connecting to the remote 48 * spooler if needed, to make the timed remote calls, to handle 49 * remote exceptions, and to bind/unbind to the remote instance as 50 * needed. 51 */ 52 final class RemotePrintSpooler { 53 54 private static final String LOG_TAG = "RemotePrintSpooler"; 55 56 private static final boolean DEBUG = false; 57 58 private static final long BIND_SPOOLER_SERVICE_TIMEOUT = 10000; 59 60 private final Object mLock = new Object(); 61 62 private final GetPrintJobInfosCaller mGetPrintJobInfosCaller = new GetPrintJobInfosCaller(); 63 64 private final GetPrintJobInfoCaller mGetPrintJobInfoCaller = new GetPrintJobInfoCaller(); 65 66 private final SetPrintJobStateCaller mSetPrintJobStatusCaller = new SetPrintJobStateCaller(); 67 68 private final SetPrintJobTagCaller mSetPrintJobTagCaller = new SetPrintJobTagCaller(); 69 70 private final ServiceConnection mServiceConnection = new MyServiceConnection(); 71 72 private final Context mContext; 73 74 private final UserHandle mUserHandle; 75 76 private final PrintSpoolerClient mClient; 77 78 private final Intent mIntent; 79 80 private final PrintSpoolerCallbacks mCallbacks; 81 82 private IPrintSpooler mRemoteInstance; 83 84 private boolean mDestroyed; 85 86 private boolean mCanUnbind; 87 88 public static interface PrintSpoolerCallbacks { onPrintJobQueued(PrintJobInfo printJob)89 public void onPrintJobQueued(PrintJobInfo printJob); onAllPrintJobsForServiceHandled(ComponentName printService)90 public void onAllPrintJobsForServiceHandled(ComponentName printService); onPrintJobStateChanged(PrintJobInfo printJob)91 public void onPrintJobStateChanged(PrintJobInfo printJob); 92 } 93 RemotePrintSpooler(Context context, int userId, PrintSpoolerCallbacks callbacks)94 public RemotePrintSpooler(Context context, int userId, 95 PrintSpoolerCallbacks callbacks) { 96 mContext = context; 97 mUserHandle = new UserHandle(userId); 98 mCallbacks = callbacks; 99 mClient = new PrintSpoolerClient(this); 100 mIntent = new Intent(); 101 mIntent.setComponent(new ComponentName("com.android.printspooler", 102 "com.android.printspooler.PrintSpoolerService")); 103 } 104 getPrintJobInfos(ComponentName componentName, int state, int appId)105 public final List<PrintJobInfo> getPrintJobInfos(ComponentName componentName, int state, 106 int appId) { 107 throwIfCalledOnMainThread(); 108 synchronized (mLock) { 109 throwIfDestroyedLocked(); 110 mCanUnbind = false; 111 } 112 try { 113 return mGetPrintJobInfosCaller.getPrintJobInfos(getRemoteInstanceLazy(), 114 componentName, state, appId); 115 } catch (RemoteException re) { 116 Slog.e(LOG_TAG, "Error getting print jobs.", re); 117 } catch (TimeoutException te) { 118 Slog.e(LOG_TAG, "Error getting print jobs.", te); 119 } finally { 120 if (DEBUG) { 121 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] getPrintJobInfos()"); 122 } 123 synchronized (mLock) { 124 mCanUnbind = true; 125 mLock.notifyAll(); 126 } 127 } 128 return null; 129 } 130 createPrintJob(PrintJobInfo printJob)131 public final void createPrintJob(PrintJobInfo printJob) { 132 throwIfCalledOnMainThread(); 133 synchronized (mLock) { 134 throwIfDestroyedLocked(); 135 mCanUnbind = false; 136 } 137 try { 138 getRemoteInstanceLazy().createPrintJob(printJob); 139 } catch (RemoteException re) { 140 Slog.e(LOG_TAG, "Error creating print job.", re); 141 } catch (TimeoutException te) { 142 Slog.e(LOG_TAG, "Error creating print job.", te); 143 } finally { 144 if (DEBUG) { 145 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] createPrintJob()"); 146 } 147 synchronized (mLock) { 148 mCanUnbind = true; 149 mLock.notifyAll(); 150 } 151 } 152 } 153 writePrintJobData(ParcelFileDescriptor fd, PrintJobId printJobId)154 public final void writePrintJobData(ParcelFileDescriptor fd, PrintJobId printJobId) { 155 throwIfCalledOnMainThread(); 156 synchronized (mLock) { 157 throwIfDestroyedLocked(); 158 mCanUnbind = false; 159 } 160 try { 161 getRemoteInstanceLazy().writePrintJobData(fd, printJobId); 162 } catch (RemoteException re) { 163 Slog.e(LOG_TAG, "Error writing print job data.", re); 164 } catch (TimeoutException te) { 165 Slog.e(LOG_TAG, "Error writing print job data.", te); 166 } finally { 167 if (DEBUG) { 168 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] writePrintJobData()"); 169 } 170 // We passed the file descriptor across and now the other 171 // side is responsible to close it, so close the local copy. 172 IoUtils.closeQuietly(fd); 173 synchronized (mLock) { 174 mCanUnbind = true; 175 mLock.notifyAll(); 176 } 177 } 178 } 179 getPrintJobInfo(PrintJobId printJobId, int appId)180 public final PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId) { 181 throwIfCalledOnMainThread(); 182 synchronized (mLock) { 183 throwIfDestroyedLocked(); 184 mCanUnbind = false; 185 } 186 try { 187 return mGetPrintJobInfoCaller.getPrintJobInfo(getRemoteInstanceLazy(), 188 printJobId, appId); 189 } catch (RemoteException re) { 190 Slog.e(LOG_TAG, "Error getting print job info.", re); 191 } catch (TimeoutException te) { 192 Slog.e(LOG_TAG, "Error getting print job info.", te); 193 } finally { 194 if (DEBUG) { 195 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] getPrintJobInfo()"); 196 } 197 synchronized (mLock) { 198 mCanUnbind = true; 199 mLock.notifyAll(); 200 } 201 } 202 return null; 203 } 204 setPrintJobState(PrintJobId printJobId, int state, String error)205 public final boolean setPrintJobState(PrintJobId printJobId, int state, String error) { 206 throwIfCalledOnMainThread(); 207 synchronized (mLock) { 208 throwIfDestroyedLocked(); 209 mCanUnbind = false; 210 } 211 try { 212 return mSetPrintJobStatusCaller.setPrintJobState(getRemoteInstanceLazy(), 213 printJobId, state, error); 214 } catch (RemoteException re) { 215 Slog.e(LOG_TAG, "Error setting print job state.", re); 216 } catch (TimeoutException te) { 217 Slog.e(LOG_TAG, "Error setting print job state.", te); 218 } finally { 219 if (DEBUG) { 220 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setPrintJobState()"); 221 } 222 synchronized (mLock) { 223 mCanUnbind = true; 224 mLock.notifyAll(); 225 } 226 } 227 return false; 228 } 229 setPrintJobTag(PrintJobId printJobId, String tag)230 public final boolean setPrintJobTag(PrintJobId printJobId, String tag) { 231 throwIfCalledOnMainThread(); 232 synchronized (mLock) { 233 throwIfDestroyedLocked(); 234 mCanUnbind = false; 235 } 236 try { 237 return mSetPrintJobTagCaller.setPrintJobTag(getRemoteInstanceLazy(), 238 printJobId, tag); 239 } catch (RemoteException re) { 240 Slog.e(LOG_TAG, "Error setting print job tag.", re); 241 } catch (TimeoutException te) { 242 Slog.e(LOG_TAG, "Error setting print job tag.", te); 243 } finally { 244 if (DEBUG) { 245 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setPrintJobTag()"); 246 } 247 synchronized (mLock) { 248 mCanUnbind = true; 249 mLock.notifyAll(); 250 } 251 } 252 return false; 253 } 254 setPrintJobCancelling(PrintJobId printJobId, boolean cancelling)255 public final void setPrintJobCancelling(PrintJobId printJobId, boolean cancelling) { 256 throwIfCalledOnMainThread(); 257 synchronized (mLock) { 258 throwIfDestroyedLocked(); 259 mCanUnbind = false; 260 } 261 try { 262 getRemoteInstanceLazy().setPrintJobCancelling(printJobId, 263 cancelling); 264 } catch (RemoteException re) { 265 Slog.e(LOG_TAG, "Error setting print job cancelling.", re); 266 } catch (TimeoutException te) { 267 Slog.e(LOG_TAG, "Error setting print job cancelling.", te); 268 } finally { 269 if (DEBUG) { 270 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() 271 + "] setPrintJobCancelling()"); 272 } 273 synchronized (mLock) { 274 mCanUnbind = true; 275 mLock.notifyAll(); 276 } 277 } 278 } 279 removeObsoletePrintJobs()280 public final void removeObsoletePrintJobs() { 281 throwIfCalledOnMainThread(); 282 synchronized (mLock) { 283 throwIfDestroyedLocked(); 284 mCanUnbind = false; 285 } 286 try { 287 getRemoteInstanceLazy().removeObsoletePrintJobs(); 288 } catch (RemoteException re) { 289 Slog.e(LOG_TAG, "Error removing obsolete print jobs .", re); 290 } catch (TimeoutException te) { 291 Slog.e(LOG_TAG, "Error removing obsolete print jobs .", te); 292 } finally { 293 if (DEBUG) { 294 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() 295 + "] removeObsoletePrintJobs()"); 296 } 297 synchronized (mLock) { 298 mCanUnbind = true; 299 mLock.notifyAll(); 300 } 301 } 302 } 303 destroy()304 public final void destroy() { 305 throwIfCalledOnMainThread(); 306 if (DEBUG) { 307 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] destroy()"); 308 } 309 synchronized (mLock) { 310 throwIfDestroyedLocked(); 311 unbindLocked(); 312 mDestroyed = true; 313 mCanUnbind = false; 314 } 315 } 316 dump(FileDescriptor fd, PrintWriter pw, String prefix)317 public void dump(FileDescriptor fd, PrintWriter pw, String prefix) { 318 synchronized (mLock) { 319 pw.append(prefix).append("destroyed=") 320 .append(String.valueOf(mDestroyed)).println(); 321 pw.append(prefix).append("bound=") 322 .append((mRemoteInstance != null) ? "true" : "false").println(); 323 324 pw.flush(); 325 326 try { 327 getRemoteInstanceLazy().asBinder().dump(fd, new String[]{prefix}); 328 } catch (TimeoutException te) { 329 /* ignore */ 330 } catch (RemoteException re) { 331 /* ignore */ 332 } 333 } 334 } 335 onAllPrintJobsHandled()336 private void onAllPrintJobsHandled() { 337 synchronized (mLock) { 338 throwIfDestroyedLocked(); 339 unbindLocked(); 340 } 341 } 342 onPrintJobStateChanged(PrintJobInfo printJob)343 private void onPrintJobStateChanged(PrintJobInfo printJob) { 344 mCallbacks.onPrintJobStateChanged(printJob); 345 } 346 getRemoteInstanceLazy()347 private IPrintSpooler getRemoteInstanceLazy() throws TimeoutException { 348 synchronized (mLock) { 349 if (mRemoteInstance != null) { 350 return mRemoteInstance; 351 } 352 bindLocked(); 353 return mRemoteInstance; 354 } 355 } 356 bindLocked()357 private void bindLocked() throws TimeoutException { 358 if (mRemoteInstance != null) { 359 return; 360 } 361 if (DEBUG) { 362 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] bindLocked()"); 363 } 364 365 mContext.bindServiceAsUser(mIntent, mServiceConnection, 366 Context.BIND_AUTO_CREATE, mUserHandle); 367 368 final long startMillis = SystemClock.uptimeMillis(); 369 while (true) { 370 if (mRemoteInstance != null) { 371 break; 372 } 373 final long elapsedMillis = SystemClock.uptimeMillis() - startMillis; 374 final long remainingMillis = BIND_SPOOLER_SERVICE_TIMEOUT - elapsedMillis; 375 if (remainingMillis <= 0) { 376 throw new TimeoutException("Cannot get spooler!"); 377 } 378 try { 379 mLock.wait(remainingMillis); 380 } catch (InterruptedException ie) { 381 /* ignore */ 382 } 383 } 384 385 mCanUnbind = true; 386 mLock.notifyAll(); 387 } 388 unbindLocked()389 private void unbindLocked() { 390 if (mRemoteInstance == null) { 391 return; 392 } 393 while (true) { 394 if (mCanUnbind) { 395 if (DEBUG) { 396 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] unbindLocked()"); 397 } 398 clearClientLocked(); 399 mRemoteInstance = null; 400 mContext.unbindService(mServiceConnection); 401 return; 402 } 403 try { 404 mLock.wait(); 405 } catch (InterruptedException ie) { 406 /* ignore */ 407 } 408 } 409 410 } 411 setClientLocked()412 private void setClientLocked() { 413 try { 414 mRemoteInstance.setClient(mClient); 415 } catch (RemoteException re) { 416 Slog.d(LOG_TAG, "Error setting print spooler client", re); 417 } 418 } 419 clearClientLocked()420 private void clearClientLocked() { 421 try { 422 mRemoteInstance.setClient(null); 423 } catch (RemoteException re) { 424 Slog.d(LOG_TAG, "Error clearing print spooler client", re); 425 } 426 427 } 428 throwIfDestroyedLocked()429 private void throwIfDestroyedLocked() { 430 if (mDestroyed) { 431 throw new IllegalStateException("Cannot interact with a destroyed instance."); 432 } 433 } 434 throwIfCalledOnMainThread()435 private void throwIfCalledOnMainThread() { 436 if (Thread.currentThread() == mContext.getMainLooper().getThread()) { 437 throw new RuntimeException("Cannot invoke on the main thread"); 438 } 439 } 440 441 private final class MyServiceConnection implements ServiceConnection { 442 @Override onServiceConnected(ComponentName name, IBinder service)443 public void onServiceConnected(ComponentName name, IBinder service) { 444 synchronized (mLock) { 445 mRemoteInstance = IPrintSpooler.Stub.asInterface(service); 446 setClientLocked(); 447 mLock.notifyAll(); 448 } 449 } 450 451 @Override onServiceDisconnected(ComponentName name)452 public void onServiceDisconnected(ComponentName name) { 453 synchronized (mLock) { 454 clearClientLocked(); 455 mRemoteInstance = null; 456 } 457 } 458 } 459 460 private static final class GetPrintJobInfosCaller 461 extends TimedRemoteCaller<List<PrintJobInfo>> { 462 private final IPrintSpoolerCallbacks mCallback; 463 GetPrintJobInfosCaller()464 public GetPrintJobInfosCaller() { 465 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); 466 mCallback = new BasePrintSpoolerServiceCallbacks() { 467 @Override 468 public void onGetPrintJobInfosResult(List<PrintJobInfo> printJobs, int sequence) { 469 onRemoteMethodResult(printJobs, sequence); 470 } 471 }; 472 } 473 getPrintJobInfos(IPrintSpooler target, ComponentName componentName, int state, int appId)474 public List<PrintJobInfo> getPrintJobInfos(IPrintSpooler target, 475 ComponentName componentName, int state, int appId) 476 throws RemoteException, TimeoutException { 477 final int sequence = onBeforeRemoteCall(); 478 target.getPrintJobInfos(mCallback, componentName, state, appId, sequence); 479 return getResultTimed(sequence); 480 } 481 } 482 483 private static final class GetPrintJobInfoCaller extends TimedRemoteCaller<PrintJobInfo> { 484 private final IPrintSpoolerCallbacks mCallback; 485 GetPrintJobInfoCaller()486 public GetPrintJobInfoCaller() { 487 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); 488 mCallback = new BasePrintSpoolerServiceCallbacks() { 489 @Override 490 public void onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence) { 491 onRemoteMethodResult(printJob, sequence); 492 } 493 }; 494 } 495 getPrintJobInfo(IPrintSpooler target, PrintJobId printJobId, int appId)496 public PrintJobInfo getPrintJobInfo(IPrintSpooler target, PrintJobId printJobId, 497 int appId) throws RemoteException, TimeoutException { 498 final int sequence = onBeforeRemoteCall(); 499 target.getPrintJobInfo(printJobId, mCallback, appId, sequence); 500 return getResultTimed(sequence); 501 } 502 } 503 504 private static final class SetPrintJobStateCaller extends TimedRemoteCaller<Boolean> { 505 private final IPrintSpoolerCallbacks mCallback; 506 SetPrintJobStateCaller()507 public SetPrintJobStateCaller() { 508 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); 509 mCallback = new BasePrintSpoolerServiceCallbacks() { 510 @Override 511 public void onSetPrintJobStateResult(boolean success, int sequence) { 512 onRemoteMethodResult(success, sequence); 513 } 514 }; 515 } 516 setPrintJobState(IPrintSpooler target, PrintJobId printJobId, int status, String error)517 public boolean setPrintJobState(IPrintSpooler target, PrintJobId printJobId, 518 int status, String error) throws RemoteException, TimeoutException { 519 final int sequence = onBeforeRemoteCall(); 520 target.setPrintJobState(printJobId, status, error, mCallback, sequence); 521 return getResultTimed(sequence); 522 } 523 } 524 525 private static final class SetPrintJobTagCaller extends TimedRemoteCaller<Boolean> { 526 private final IPrintSpoolerCallbacks mCallback; 527 SetPrintJobTagCaller()528 public SetPrintJobTagCaller() { 529 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); 530 mCallback = new BasePrintSpoolerServiceCallbacks() { 531 @Override 532 public void onSetPrintJobTagResult(boolean success, int sequence) { 533 onRemoteMethodResult(success, sequence); 534 } 535 }; 536 } 537 setPrintJobTag(IPrintSpooler target, PrintJobId printJobId, String tag)538 public boolean setPrintJobTag(IPrintSpooler target, PrintJobId printJobId, 539 String tag) throws RemoteException, TimeoutException { 540 final int sequence = onBeforeRemoteCall(); 541 target.setPrintJobTag(printJobId, tag, mCallback, sequence); 542 return getResultTimed(sequence); 543 } 544 } 545 546 private static abstract class BasePrintSpoolerServiceCallbacks 547 extends IPrintSpoolerCallbacks.Stub { 548 @Override onGetPrintJobInfosResult(List<PrintJobInfo> printJobIds, int sequence)549 public void onGetPrintJobInfosResult(List<PrintJobInfo> printJobIds, int sequence) { 550 /* do nothing */ 551 } 552 553 @Override onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence)554 public void onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence) { 555 /* do nothing */ 556 } 557 558 @Override onCancelPrintJobResult(boolean canceled, int sequence)559 public void onCancelPrintJobResult(boolean canceled, int sequence) { 560 /* do nothing */ 561 } 562 563 @Override onSetPrintJobStateResult(boolean success, int sequece)564 public void onSetPrintJobStateResult(boolean success, int sequece) { 565 /* do nothing */ 566 } 567 568 @Override onSetPrintJobTagResult(boolean success, int sequence)569 public void onSetPrintJobTagResult(boolean success, int sequence) { 570 /* do nothing */ 571 } 572 } 573 574 private static final class PrintSpoolerClient extends IPrintSpoolerClient.Stub { 575 576 private final WeakReference<RemotePrintSpooler> mWeakSpooler; 577 PrintSpoolerClient(RemotePrintSpooler spooler)578 public PrintSpoolerClient(RemotePrintSpooler spooler) { 579 mWeakSpooler = new WeakReference<RemotePrintSpooler>(spooler); 580 } 581 582 @Override onPrintJobQueued(PrintJobInfo printJob)583 public void onPrintJobQueued(PrintJobInfo printJob) { 584 RemotePrintSpooler spooler = mWeakSpooler.get(); 585 if (spooler != null) { 586 final long identity = Binder.clearCallingIdentity(); 587 try { 588 spooler.mCallbacks.onPrintJobQueued(printJob); 589 } finally { 590 Binder.restoreCallingIdentity(identity); 591 } 592 } 593 } 594 595 @Override onAllPrintJobsForServiceHandled(ComponentName printService)596 public void onAllPrintJobsForServiceHandled(ComponentName printService) { 597 RemotePrintSpooler spooler = mWeakSpooler.get(); 598 if (spooler != null) { 599 final long identity = Binder.clearCallingIdentity(); 600 try { 601 spooler.mCallbacks.onAllPrintJobsForServiceHandled(printService); 602 } finally { 603 Binder.restoreCallingIdentity(identity); 604 } 605 } 606 } 607 608 @Override onAllPrintJobsHandled()609 public void onAllPrintJobsHandled() { 610 RemotePrintSpooler spooler = mWeakSpooler.get(); 611 if (spooler != null) { 612 final long identity = Binder.clearCallingIdentity(); 613 try { 614 spooler.onAllPrintJobsHandled(); 615 } finally { 616 Binder.restoreCallingIdentity(identity); 617 } 618 } 619 } 620 621 @Override onPrintJobStateChanged(PrintJobInfo printJob)622 public void onPrintJobStateChanged(PrintJobInfo printJob) { 623 RemotePrintSpooler spooler = mWeakSpooler.get(); 624 if (spooler != null) { 625 final long identity = Binder.clearCallingIdentity(); 626 try { 627 spooler.onPrintJobStateChanged(printJob); 628 } finally { 629 Binder.restoreCallingIdentity(identity); 630 } 631 } 632 } 633 } 634 } 635