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