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