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