1 /* 2 * Copyright (C) 2018 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.internal.infra; 18 19 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.content.ComponentName; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.ServiceConnection; 27 import android.os.Handler; 28 import android.os.IBinder; 29 import android.os.IBinder.DeathRecipient; 30 import android.os.IInterface; 31 import android.os.RemoteException; 32 import android.os.SystemClock; 33 import android.os.UserHandle; 34 import android.util.Slog; 35 import android.util.TimeUtils; 36 37 import com.android.internal.annotations.GuardedBy; 38 39 import java.io.PrintWriter; 40 import java.lang.ref.WeakReference; 41 import java.util.ArrayList; 42 43 /** 44 * Base class representing a remote service. 45 * 46 * <p>It abstracts away the binding and unbinding from the remote implementation, so clients can 47 * call its methods without worrying about when and how to bind/unbind/timeout. 48 * 49 * <p>All state of this class is modified on a handler thread. 50 * 51 * <p><b>NOTE: </b>this class should not be extended directly, you should extend either 52 * {@link AbstractSinglePendingRequestRemoteService} or 53 * {@link AbstractMultiplePendingRequestsRemoteService}. 54 * 55 * <p>See {@code com.android.server.autofill.RemoteFillService} for a concrete 56 * (no pun intended) example of how to use it. 57 * 58 * @param <S> the concrete remote service class 59 * @param <I> the interface of the binder service 60 * 61 * @deprecated Use {@link ServiceConnector} to manage remote service connections 62 * 63 * @hide 64 */ 65 //TODO(b/117779333): improve javadoc above instead of using Autofill as an example 66 @Deprecated 67 public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I>, 68 I extends IInterface> implements DeathRecipient { 69 private static final int MSG_BIND = 1; 70 private static final int MSG_UNBIND = 2; 71 72 public static final long PERMANENT_BOUND_TIMEOUT_MS = 0; 73 74 protected static final int LAST_PRIVATE_MSG = MSG_UNBIND; 75 76 // TODO(b/117779333): convert all booleans into an integer / flags 77 public final boolean mVerbose; 78 79 protected final String mTag = getClass().getSimpleName(); 80 protected final Handler mHandler; 81 protected final ComponentName mComponentName; 82 83 private final Context mContext; 84 private final Intent mIntent; 85 private final VultureCallback<S> mVultureCallback; 86 private final int mUserId; 87 private final ServiceConnection mServiceConnection = new RemoteServiceConnection(); 88 private final int mBindingFlags; 89 protected I mService; 90 91 private boolean mConnecting; 92 private boolean mDestroyed; 93 private boolean mServiceDied; 94 private boolean mCompleted; 95 96 // Used just for debugging purposes (on dump) 97 private long mNextUnbind; 98 99 /** Requests that have been scheduled, but that are not finished yet */ 100 private final ArrayList<BasePendingRequest<S, I>> mUnfinishedRequests = new ArrayList<>(); 101 102 /** 103 * Callback called when the service dies. 104 * 105 * @param <T> service class 106 */ 107 public interface VultureCallback<T> { 108 /** 109 * Called when the service dies. 110 * 111 * @param service service that died! 112 */ onServiceDied(T service)113 void onServiceDied(T service); 114 } 115 116 // NOTE: must be package-protected so this class is not extended outside AbstractRemoteService(@onNull Context context, @NonNull String serviceInterface, @NonNull ComponentName componentName, int userId, @NonNull VultureCallback<S> callback, @NonNull Handler handler, int bindingFlags, boolean verbose)117 AbstractRemoteService(@NonNull Context context, @NonNull String serviceInterface, 118 @NonNull ComponentName componentName, int userId, @NonNull VultureCallback<S> callback, 119 @NonNull Handler handler, int bindingFlags, boolean verbose) { 120 mContext = context; 121 mVultureCallback = callback; 122 mVerbose = verbose; 123 mComponentName = componentName; 124 mIntent = new Intent(serviceInterface).setComponent(mComponentName); 125 mUserId = userId; 126 mHandler = new Handler(handler.getLooper()); 127 mBindingFlags = bindingFlags; 128 } 129 130 /** 131 * Destroys this service. 132 */ destroy()133 public final void destroy() { 134 mHandler.sendMessage(obtainMessage(AbstractRemoteService::handleDestroy, this)); 135 } 136 137 /** 138 * Checks whether this service is destroyed. 139 */ isDestroyed()140 public final boolean isDestroyed() { 141 return mDestroyed; 142 } 143 144 /** 145 * Gets the name of the service. 146 */ 147 @NonNull getComponentName()148 public final ComponentName getComponentName() { 149 return mComponentName; 150 } 151 handleOnConnectedStateChangedInternal(boolean connected)152 private void handleOnConnectedStateChangedInternal(boolean connected) { 153 handleOnConnectedStateChanged(connected); 154 if (connected) { 155 handlePendingRequests(); 156 } 157 } 158 159 /** 160 * Handles the pending requests when the connection it bounds to the remote service. 161 */ handlePendingRequests()162 abstract void handlePendingRequests(); 163 164 /** 165 * Callback called when the system connected / disconnected to the service and the pending 166 * requests have been handled. 167 * 168 * @param state {@code true} when connected, {@code false} when disconnected. 169 */ handleOnConnectedStateChanged(boolean state)170 protected void handleOnConnectedStateChanged(boolean state) { 171 } 172 173 /** 174 * Gets the base Binder interface from the service. 175 */ 176 @NonNull getServiceInterface(@onNull IBinder service)177 protected abstract I getServiceInterface(@NonNull IBinder service); 178 179 /** 180 * Defines how long after the last interaction with the service we would unbind. 181 * 182 * @return time to unbind (in millis), or {@link #PERMANENT_BOUND_TIMEOUT_MS} to not unbind. 183 */ getTimeoutIdleBindMillis()184 protected abstract long getTimeoutIdleBindMillis(); 185 186 /** 187 * Defines how long after we make a remote request to a fill service we timeout. 188 * 189 * <p>Just need to be overridden by subclasses that uses sync {@link PendingRequest}s. 190 * 191 * @throws UnsupportedOperationException if called when not overridden. 192 * 193 */ getRemoteRequestMillis()194 protected long getRemoteRequestMillis() { 195 throw new UnsupportedOperationException("not implemented by " + getClass()); 196 } 197 198 /** 199 * Gets the currently registered service interface or {@code null} if the service is not 200 * connected. 201 */ 202 @Nullable getServiceInterface()203 public final I getServiceInterface() { 204 return mService; 205 } 206 handleDestroy()207 private void handleDestroy() { 208 if (checkIfDestroyed()) return; 209 handleOnDestroy(); 210 handleEnsureUnbound(); 211 mDestroyed = true; 212 } 213 214 /** 215 * Clears the state when this object is destroyed. 216 * 217 * <p>Typically used to cancel the pending requests. 218 */ handleOnDestroy()219 protected abstract void handleOnDestroy(); 220 221 @Override // from DeathRecipient binderDied()222 public void binderDied() { 223 mHandler.sendMessage(obtainMessage(AbstractRemoteService::handleBinderDied, this)); 224 } 225 handleBinderDied()226 private void handleBinderDied() { 227 if (checkIfDestroyed()) return; 228 if (mService != null) { 229 mService.asBinder().unlinkToDeath(this, 0); 230 } 231 mConnecting = true; 232 mService = null; 233 mServiceDied = true; 234 cancelScheduledUnbind(); 235 @SuppressWarnings("unchecked") // TODO(b/117779333): fix this warning 236 final S castService = (S) this; 237 mVultureCallback.onServiceDied(castService); 238 handleBindFailure(); 239 } 240 241 // Note: we are dumping without a lock held so this is a bit racy but 242 // adding a lock to a class that offloads to a handler thread would 243 // mean adding a lock adding overhead to normal runtime operation. 244 /** 245 * Dump it! 246 */ dump(@onNull String prefix, @NonNull PrintWriter pw)247 public void dump(@NonNull String prefix, @NonNull PrintWriter pw) { 248 String tab = " "; 249 pw.append(prefix).append("service:").println(); 250 pw.append(prefix).append(tab).append("userId=") 251 .append(String.valueOf(mUserId)).println(); 252 pw.append(prefix).append(tab).append("componentName=") 253 .append(mComponentName.flattenToString()).println(); 254 pw.append(prefix).append(tab).append("destroyed=") 255 .append(String.valueOf(mDestroyed)).println(); 256 pw.append(prefix).append(tab).append("numUnfinishedRequests=") 257 .append(String.valueOf(mUnfinishedRequests.size())).println(); 258 final boolean bound = handleIsBound(); 259 pw.append(prefix).append(tab).append("bound=") 260 .append(String.valueOf(bound)); 261 final long idleTimeout = getTimeoutIdleBindMillis(); 262 if (bound) { 263 if (idleTimeout > 0) { 264 pw.append(" (unbind in : "); 265 TimeUtils.formatDuration(mNextUnbind - SystemClock.elapsedRealtime(), pw); 266 pw.append(")"); 267 } else { 268 pw.append(" (permanently bound)"); 269 } 270 } 271 pw.println(); 272 pw.append(prefix).append("mBindingFlags=").println(mBindingFlags); 273 pw.append(prefix).append("idleTimeout=") 274 .append(Long.toString(idleTimeout / 1000)).append("s\n"); 275 pw.append(prefix).append("requestTimeout="); 276 try { 277 pw.append(Long.toString(getRemoteRequestMillis() / 1000)).append("s\n"); 278 } catch (UnsupportedOperationException e) { 279 pw.append("not supported\n"); 280 } 281 pw.println(); 282 } 283 284 /** 285 * Schedules a "sync" request. 286 * 287 * <p>This request must be responded by the service somehow (typically using a callback), 288 * othewise it will trigger a {@link PendingRequest#onTimeout(AbstractRemoteService)} if the 289 * service doesn't respond. 290 */ scheduleRequest(@onNull BasePendingRequest<S, I> pendingRequest)291 protected void scheduleRequest(@NonNull BasePendingRequest<S, I> pendingRequest) { 292 mHandler.sendMessage(obtainMessage( 293 AbstractRemoteService::handlePendingRequest, this, pendingRequest)); 294 } 295 296 /** 297 * Marks a pendingRequest as finished. 298 * 299 * @param finshedRequest The request that is finished 300 */ finishRequest(@onNull BasePendingRequest<S, I> finshedRequest)301 void finishRequest(@NonNull BasePendingRequest<S, I> finshedRequest) { 302 mHandler.sendMessage( 303 obtainMessage(AbstractRemoteService::handleFinishRequest, this, finshedRequest)); 304 } 305 handleFinishRequest(@onNull BasePendingRequest<S, I> finshedRequest)306 private void handleFinishRequest(@NonNull BasePendingRequest<S, I> finshedRequest) { 307 mUnfinishedRequests.remove(finshedRequest); 308 309 if (mUnfinishedRequests.isEmpty()) { 310 scheduleUnbind(); 311 } 312 } 313 314 /** 315 * Schedules an async request. 316 * 317 * <p>This request is not expecting a callback from the service, hence it's represented by 318 * a simple {@link Runnable}. 319 */ scheduleAsyncRequest(@onNull AsyncRequest<I> request)320 protected void scheduleAsyncRequest(@NonNull AsyncRequest<I> request) { 321 // TODO(b/117779333): fix generics below 322 @SuppressWarnings({"unchecked", "rawtypes"}) 323 final MyAsyncPendingRequest<S, I> asyncRequest = new MyAsyncPendingRequest(this, request); 324 mHandler.sendMessage( 325 obtainMessage(AbstractRemoteService::handlePendingRequest, this, asyncRequest)); 326 } 327 328 /** 329 * Executes an async request immediately instead of sending it to Handler queue as what 330 * {@link scheduleAsyncRequest} does. 331 * 332 * <p>This request is not expecting a callback from the service, hence it's represented by 333 * a simple {@link Runnable}. 334 */ executeAsyncRequest(@onNull AsyncRequest<I> request)335 protected void executeAsyncRequest(@NonNull AsyncRequest<I> request) { 336 // TODO(b/117779333): fix generics below 337 @SuppressWarnings({"unchecked", "rawtypes"}) 338 final MyAsyncPendingRequest<S, I> asyncRequest = new MyAsyncPendingRequest(this, request); 339 handlePendingRequest(asyncRequest); 340 } 341 cancelScheduledUnbind()342 private void cancelScheduledUnbind() { 343 mHandler.removeMessages(MSG_UNBIND); 344 } 345 346 /** 347 * Schedules a request to bind to the remote service. 348 * 349 * <p>Typically used on constructor for implementations that need a permanent connection to 350 * the remote service. 351 */ scheduleBind()352 protected void scheduleBind() { 353 if (mHandler.hasMessages(MSG_BIND)) { 354 if (mVerbose) Slog.v(mTag, "scheduleBind(): already scheduled"); 355 return; 356 } 357 mHandler.sendMessage(obtainMessage(AbstractRemoteService::handleEnsureBound, this) 358 .setWhat(MSG_BIND)); 359 } 360 361 /** 362 * Schedules a request to automatically unbind from the service after the 363 * {@link #getTimeoutIdleBindMillis() idle timeout} expires. 364 */ scheduleUnbind()365 protected void scheduleUnbind() { 366 scheduleUnbind(true); 367 } 368 scheduleUnbind(boolean delay)369 private void scheduleUnbind(boolean delay) { 370 long unbindDelay = getTimeoutIdleBindMillis(); 371 372 if (unbindDelay <= PERMANENT_BOUND_TIMEOUT_MS) { 373 if (mVerbose) Slog.v(mTag, "not scheduling unbind when value is " + unbindDelay); 374 return; 375 } 376 377 if (!delay) { 378 unbindDelay = 0; 379 } 380 381 cancelScheduledUnbind(); 382 // TODO(b/117779333): make sure it's unbound if the service settings changing (right now 383 // it's not) 384 385 mNextUnbind = SystemClock.elapsedRealtime() + unbindDelay; 386 if (mVerbose) Slog.v(mTag, "unbinding in " + unbindDelay + "ms: " + mNextUnbind); 387 mHandler.sendMessageDelayed(obtainMessage(AbstractRemoteService::handleUnbind, this) 388 .setWhat(MSG_UNBIND), unbindDelay); 389 } 390 handleUnbind()391 private void handleUnbind() { 392 if (checkIfDestroyed()) return; 393 394 handleEnsureUnbound(); 395 } 396 397 /** 398 * Handles a request, either processing it right now when bound, or saving it to be handled when 399 * bound. 400 */ handlePendingRequest(@onNull BasePendingRequest<S, I> pendingRequest)401 protected final void handlePendingRequest(@NonNull BasePendingRequest<S, I> pendingRequest) { 402 if (checkIfDestroyed() || mCompleted) return; 403 404 if (!handleIsBound()) { 405 if (mVerbose) Slog.v(mTag, "handlePendingRequest(): queuing " + pendingRequest); 406 handlePendingRequestWhileUnBound(pendingRequest); 407 handleEnsureBound(); 408 } else { 409 if (mVerbose) Slog.v(mTag, "handlePendingRequest(): " + pendingRequest); 410 411 mUnfinishedRequests.add(pendingRequest); 412 cancelScheduledUnbind(); 413 414 pendingRequest.run(); 415 if (pendingRequest.isFinal()) { 416 mCompleted = true; 417 } 418 } 419 } 420 421 /** 422 * Defines what to do with a request that arrives while not bound to the service. 423 */ handlePendingRequestWhileUnBound( @onNull BasePendingRequest<S, I> pendingRequest)424 abstract void handlePendingRequestWhileUnBound( 425 @NonNull BasePendingRequest<S, I> pendingRequest); 426 427 /** 428 * Called if {@link Context#bindServiceAsUser} returns {@code false}, or 429 * if {@link DeathRecipient#binderDied()} is called. 430 */ handleBindFailure()431 abstract void handleBindFailure(); 432 handleIsBound()433 private boolean handleIsBound() { 434 return mService != null; 435 } 436 handleEnsureBound()437 private void handleEnsureBound() { 438 if (handleIsBound() || mConnecting) return; 439 440 if (mVerbose) Slog.v(mTag, "ensureBound()"); 441 mConnecting = true; 442 443 final int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE 444 | Context.BIND_INCLUDE_CAPABILITIES | mBindingFlags; 445 446 final boolean willBind = mContext.bindServiceAsUser(mIntent, mServiceConnection, flags, 447 mHandler, new UserHandle(mUserId)); 448 449 if (!willBind) { 450 Slog.w(mTag, "could not bind to " + mIntent + " using flags " + flags); 451 mConnecting = false; 452 453 if (!mServiceDied) { 454 handleBinderDied(); 455 } 456 } 457 } 458 handleEnsureUnbound()459 private void handleEnsureUnbound() { 460 if (!handleIsBound() && !mConnecting) return; 461 462 if (mVerbose) Slog.v(mTag, "ensureUnbound()"); 463 mConnecting = false; 464 if (handleIsBound()) { 465 handleOnConnectedStateChangedInternal(false); 466 if (mService != null) { 467 mService.asBinder().unlinkToDeath(this, 0); 468 mService = null; 469 } 470 } 471 mNextUnbind = 0; 472 mContext.unbindService(mServiceConnection); 473 } 474 475 private class RemoteServiceConnection implements ServiceConnection { 476 @Override onServiceConnected(ComponentName name, IBinder service)477 public void onServiceConnected(ComponentName name, IBinder service) { 478 if (mVerbose) Slog.v(mTag, "onServiceConnected()"); 479 if (mDestroyed || !mConnecting) { 480 // This is abnormal. Unbinding the connection has been requested already. 481 Slog.wtf(mTag, "onServiceConnected() was dispatched after unbindService."); 482 return; 483 } 484 mConnecting = false; 485 try { 486 service.linkToDeath(AbstractRemoteService.this, 0); 487 } catch (RemoteException re) { 488 handleBinderDied(); 489 return; 490 } 491 mService = getServiceInterface(service); 492 handleOnConnectedStateChangedInternal(true); 493 mServiceDied = false; 494 } 495 496 @Override onServiceDisconnected(ComponentName name)497 public void onServiceDisconnected(ComponentName name) { 498 if (mVerbose) Slog.v(mTag, "onServiceDisconnected()"); 499 mConnecting = true; 500 mService = null; 501 } 502 503 @Override onBindingDied(ComponentName name)504 public void onBindingDied(ComponentName name) { 505 if (mVerbose) Slog.v(mTag, "onBindingDied()"); 506 scheduleUnbind(false); 507 } 508 } 509 checkIfDestroyed()510 private boolean checkIfDestroyed() { 511 if (mDestroyed) { 512 if (mVerbose) { 513 Slog.v(mTag, "Not handling operation as service for " + mComponentName 514 + " is already destroyed"); 515 } 516 } 517 return mDestroyed; 518 } 519 520 @Override toString()521 public String toString() { 522 return getClass().getSimpleName() + "[" + mComponentName 523 + " " + System.identityHashCode(this) 524 + (mService != null ? " (bound)" : " (unbound)") 525 + (mDestroyed ? " (destroyed)" : "") 526 + "]"; 527 } 528 529 /** 530 * Base class for the requests serviced by the remote service. 531 * 532 * <p><b>NOTE: </b> this class is not used directly, you should either override 533 * {@link com.android.internal.infra.AbstractRemoteService.PendingRequest} for sync requests, or 534 * use {@link AbstractRemoteService#scheduleAsyncRequest(AsyncRequest)} for async requests. 535 * 536 * @param <S> the remote service class 537 * @param <I> the interface of the binder service 538 */ 539 public abstract static class BasePendingRequest<S extends AbstractRemoteService<S, I>, 540 I extends IInterface> implements Runnable { 541 protected final String mTag = getClass().getSimpleName(); 542 protected final Object mLock = new Object(); 543 544 final WeakReference<S> mWeakService; 545 546 @GuardedBy("mLock") 547 boolean mCancelled; 548 549 @GuardedBy("mLock") 550 boolean mCompleted; 551 BasePendingRequest(@onNull S service)552 BasePendingRequest(@NonNull S service) { 553 mWeakService = new WeakReference<>(service); 554 } 555 556 /** 557 * Gets a reference to the remote service. 558 */ getService()559 protected final S getService() { 560 return mWeakService.get(); 561 } 562 563 /** 564 * Subclasses must call this method when the remote service finishes, i.e., when the service 565 * finishes processing a request. 566 * 567 * @return {@code false} in the service is already finished, {@code true} otherwise. 568 */ finish()569 protected final boolean finish() { 570 synchronized (mLock) { 571 if (mCompleted || mCancelled) { 572 return false; 573 } 574 mCompleted = true; 575 } 576 577 S service = mWeakService.get(); 578 if (service != null) { 579 service.finishRequest(this); 580 } 581 582 onFinished(); 583 584 return true; 585 } 586 onFinished()587 void onFinished() { } 588 589 /** 590 * Called when request fails due to reasons internal to {@link AbstractRemoteService}, 591 * e.g. failure to bind to service. 592 */ onFailed()593 protected void onFailed() { } 594 595 /** 596 * Checks whether this request was cancelled. 597 */ 598 @GuardedBy("mLock") isCancelledLocked()599 protected final boolean isCancelledLocked() { 600 return mCancelled; 601 } 602 603 /** 604 * Cancels the service. 605 * 606 * @return {@code false} if service is already canceled, {@code true} otherwise. 607 */ cancel()608 public boolean cancel() { 609 synchronized (mLock) { 610 if (mCancelled || mCompleted) { 611 return false; 612 } 613 mCancelled = true; 614 } 615 616 onCancel(); 617 return true; 618 } 619 onCancel()620 void onCancel() {} 621 622 /** 623 * Checks whether this request leads to a final state where no other requests can be made. 624 */ isFinal()625 protected boolean isFinal() { 626 return false; 627 } 628 isRequestCompleted()629 protected boolean isRequestCompleted() { 630 synchronized (mLock) { 631 return mCompleted; 632 } 633 } 634 } 635 636 /** 637 * Base class for the requests serviced by the remote service. 638 * 639 * <p><b>NOTE: </b> this class is typically used when the service needs to use a callback to 640 * communicate back with the system server. For cases where that's not needed, you should use 641 * {@link AbstractRemoteService#scheduleAsyncRequest(AsyncRequest)} instead. 642 * 643 * <p><b>NOTE: </b> you must override {@link AbstractRemoteService#getRemoteRequestMillis()}, 644 * otherwise the constructor will throw an {@link UnsupportedOperationException}. 645 * 646 * @param <S> the remote service class 647 * @param <I> the interface of the binder service 648 */ 649 public abstract static class PendingRequest<S extends AbstractRemoteService<S, I>, 650 I extends IInterface> extends BasePendingRequest<S, I> { 651 652 private final Runnable mTimeoutTrigger; 653 private final Handler mServiceHandler; 654 PendingRequest(S service)655 protected PendingRequest(S service) { 656 super(service); 657 mServiceHandler = service.mHandler; 658 659 mTimeoutTrigger = () -> { 660 synchronized (mLock) { 661 if (mCancelled) { 662 return; 663 } 664 mCompleted = true; 665 } 666 667 final S remoteService = mWeakService.get(); 668 if (remoteService != null) { 669 // TODO(b/117779333): we should probably ignore it if service is destroyed. 670 Slog.w(mTag, "timed out after " + service.getRemoteRequestMillis() + " ms"); 671 remoteService.finishRequest(this); 672 onTimeout(remoteService); 673 } else { 674 Slog.w(mTag, "timed out (no service)"); 675 } 676 }; 677 mServiceHandler.postAtTime(mTimeoutTrigger, 678 SystemClock.uptimeMillis() + service.getRemoteRequestMillis()); 679 } 680 681 @Override onFinished()682 final void onFinished() { 683 mServiceHandler.removeCallbacks(mTimeoutTrigger); 684 } 685 686 @Override onCancel()687 final void onCancel() { 688 mServiceHandler.removeCallbacks(mTimeoutTrigger); 689 } 690 691 /** 692 * Called by the self-destruct timeout when the remote service didn't reply to the 693 * request on time. 694 */ onTimeout(S remoteService)695 protected abstract void onTimeout(S remoteService); 696 } 697 698 /** 699 * Represents a request that does not expect a callback from the remote service. 700 * 701 * @param <I> the interface of the binder service 702 */ 703 public interface AsyncRequest<I extends IInterface> { 704 705 /** 706 * Run Forrest, run! 707 */ run(@onNull I binder)708 void run(@NonNull I binder) throws RemoteException; 709 } 710 711 private static final class MyAsyncPendingRequest<S extends AbstractRemoteService<S, I>, 712 I extends IInterface> extends BasePendingRequest<S, I> { 713 private static final String TAG = MyAsyncPendingRequest.class.getSimpleName(); 714 715 private final AsyncRequest<I> mRequest; 716 MyAsyncPendingRequest(@onNull S service, @NonNull AsyncRequest<I> request)717 protected MyAsyncPendingRequest(@NonNull S service, @NonNull AsyncRequest<I> request) { 718 super(service); 719 720 mRequest = request; 721 } 722 723 @Override run()724 public void run() { 725 final S remoteService = getService(); 726 if (remoteService == null) return; 727 try { 728 mRequest.run(remoteService.mService); 729 } catch (RemoteException e) { 730 Slog.w(TAG, "exception handling async request (" + this + "): " + e); 731 } finally { 732 finish(); 733 } 734 } 735 } 736 } 737