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