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> finishedRequest)358     private void handleFinishRequest(@NonNull BasePendingRequest<S, I> finishedRequest) {
359         synchronized (mUnfinishedRequests) {
360             mUnfinishedRequests.remove(finishedRequest);
361         }
362         if (mUnfinishedRequests.isEmpty()) {
363             scheduleUnbind();
364         }
365     }
366 
367     /**
368      * Schedules an async request.
369      *
370      * <p>This request is not expecting a callback from the service, hence it's represented by
371      * a simple {@link Runnable}.
372      */
scheduleAsyncRequest(@onNull AsyncRequest<I> request)373     protected void scheduleAsyncRequest(@NonNull AsyncRequest<I> request) {
374         // TODO(b/117779333): fix generics below
375         @SuppressWarnings({"unchecked", "rawtypes"})
376         final MyAsyncPendingRequest<S, I> asyncRequest = new MyAsyncPendingRequest(this, request);
377         mHandler.sendMessage(
378                 obtainMessage(AbstractRemoteService::handlePendingRequest, this, asyncRequest));
379     }
380 
381     /**
382      * Executes an async request immediately instead of sending it to Handler queue as what
383      * {@link scheduleAsyncRequest} does.
384      *
385      * <p>This request is not expecting a callback from the service, hence it's represented by
386      * a simple {@link Runnable}.
387      */
executeAsyncRequest(@onNull AsyncRequest<I> request)388     protected void executeAsyncRequest(@NonNull AsyncRequest<I> request) {
389         // TODO(b/117779333): fix generics below
390         @SuppressWarnings({"unchecked", "rawtypes"})
391         final MyAsyncPendingRequest<S, I> asyncRequest = new MyAsyncPendingRequest(this, request);
392         handlePendingRequest(asyncRequest);
393     }
394 
cancelScheduledUnbind()395     private void cancelScheduledUnbind() {
396         mHandler.removeMessages(MSG_UNBIND);
397     }
398 
399     /**
400      * Schedules a request to bind to the remote service.
401      *
402      * <p>Typically used on constructor for implementations that need a permanent connection to
403      * the remote service.
404      */
scheduleBind()405     protected void scheduleBind() {
406         if (mHandler.hasMessages(MSG_BIND)) {
407             if (mVerbose) Slog.v(mTag, "scheduleBind(): already scheduled");
408             return;
409         }
410         mHandler.sendMessage(obtainMessage(AbstractRemoteService::handleEnsureBound, this)
411                 .setWhat(MSG_BIND));
412     }
413 
414     /**
415      * Schedules a request to automatically unbind from the service after the
416      * {@link #getTimeoutIdleBindMillis() idle timeout} expires.
417      */
scheduleUnbind()418     protected void scheduleUnbind() {
419         scheduleUnbind(true);
420     }
421 
scheduleUnbind(boolean delay)422     private void scheduleUnbind(boolean delay) {
423         long unbindDelay = getTimeoutIdleBindMillis();
424 
425         if (unbindDelay <= PERMANENT_BOUND_TIMEOUT_MS) {
426             if (mVerbose) Slog.v(mTag, "not scheduling unbind when value is " + unbindDelay);
427             return;
428         }
429 
430         if (!delay) {
431             unbindDelay = 0;
432         }
433 
434         cancelScheduledUnbind();
435         // TODO(b/117779333): make sure it's unbound if the service settings changing (right now
436         // it's not)
437 
438         mNextUnbind = SystemClock.elapsedRealtime() + unbindDelay;
439         if (mVerbose) Slog.v(mTag, "unbinding in " + unbindDelay + "ms: " + mNextUnbind);
440         mHandler.sendMessageDelayed(obtainMessage(AbstractRemoteService::handleUnbind, this)
441                 .setWhat(MSG_UNBIND), unbindDelay);
442     }
443 
handleUnbind()444     private void handleUnbind() {
445         if (checkIfDestroyed()) return;
446 
447         handleEnsureUnbound();
448     }
449 
450     /**
451      * Handles a request, either processing it right now when bound, or saving it to be handled when
452      * bound.
453      */
handlePendingRequest(@onNull BasePendingRequest<S, I> pendingRequest)454     protected final void handlePendingRequest(@NonNull BasePendingRequest<S, I> pendingRequest) {
455         if (checkIfDestroyed() || mCompleted) return;
456 
457         if (!handleIsBound()) {
458             if (mVerbose) Slog.v(mTag, "handlePendingRequest(): queuing " + pendingRequest);
459             handlePendingRequestWhileUnBound(pendingRequest);
460             handleEnsureBound();
461         } else {
462             if (mVerbose) Slog.v(mTag, "handlePendingRequest(): " + pendingRequest);
463 
464             synchronized (mUnfinishedRequests) {
465                 mUnfinishedRequests.add(pendingRequest);
466             }
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             S service = mWeakService.get();
680             if (service != null) {
681                 service.finishRequest(this);
682             }
683 
684             onCancel();
685             return true;
686         }
687 
onCancel()688         void onCancel() {}
689 
690         /**
691          * Checks whether this request leads to a final state where no other requests can be made.
692          */
isFinal()693         protected boolean isFinal() {
694             return false;
695         }
696 
isRequestCompleted()697         protected boolean isRequestCompleted() {
698             synchronized (mLock) {
699                 return mCompleted;
700             }
701         }
702     }
703 
704     /**
705      * Base class for the requests serviced by the remote service.
706      *
707      * <p><b>NOTE: </b> this class is typically used when the service needs to use a callback to
708      * communicate back with the system server. For cases where that's not needed, you should use
709      * {@link AbstractRemoteService#scheduleAsyncRequest(AsyncRequest)} instead.
710      *
711      * <p><b>NOTE: </b> you must override {@link AbstractRemoteService#getRemoteRequestMillis()},
712      * otherwise the constructor will throw an {@link UnsupportedOperationException}.
713      *
714      * @param <S> the remote service class
715      * @param <I> the interface of the binder service
716      */
717     public abstract static class PendingRequest<S extends AbstractRemoteService<S, I>,
718             I extends IInterface> extends BasePendingRequest<S, I> {
719 
720         private final Runnable mTimeoutTrigger;
721         private final Handler mServiceHandler;
722 
PendingRequest(S service)723         protected PendingRequest(S service) {
724             super(service);
725             mServiceHandler = service.mHandler;
726 
727             mTimeoutTrigger = () -> {
728                 synchronized (mLock) {
729                     if (mCancelled) {
730                         return;
731                     }
732                     mCompleted = true;
733                 }
734 
735                 final S remoteService = mWeakService.get();
736                 if (remoteService != null) {
737                     // TODO(b/117779333): we should probably ignore it if service is destroyed.
738                     Slog.w(mTag, "timed out after " + service.getRemoteRequestMillis() + " ms");
739                     remoteService.finishRequest(this);
740                     onTimeout(remoteService);
741                 } else {
742                     Slog.w(mTag, "timed out (no service)");
743                 }
744             };
745             mServiceHandler.postAtTime(mTimeoutTrigger,
746                     SystemClock.uptimeMillis() + service.getRemoteRequestMillis());
747         }
748 
749         @Override
onFinished()750         final void onFinished() {
751             mServiceHandler.removeCallbacks(mTimeoutTrigger);
752         }
753 
754         @Override
onCancel()755         final void onCancel() {
756             mServiceHandler.removeCallbacks(mTimeoutTrigger);
757         }
758 
759         /**
760          * Called by the self-destruct timeout when the remote service didn't reply to the
761          * request on time.
762          */
onTimeout(S remoteService)763         protected abstract void onTimeout(S remoteService);
764     }
765 
766     /**
767      * Represents a request that does not expect a callback from the remote service.
768      *
769      * @param <I> the interface of the binder service
770      */
771     public interface AsyncRequest<I extends IInterface> {
772 
773         /**
774          * Run Forrest, run!
775          */
run(@onNull I binder)776         void run(@NonNull I binder) throws RemoteException;
777     }
778 
779     private static final class MyAsyncPendingRequest<S extends AbstractRemoteService<S, I>,
780             I extends IInterface> extends BasePendingRequest<S, I> {
781         private static final String TAG = MyAsyncPendingRequest.class.getSimpleName();
782 
783         private final AsyncRequest<I> mRequest;
784 
MyAsyncPendingRequest(@onNull S service, @NonNull AsyncRequest<I> request)785         protected MyAsyncPendingRequest(@NonNull S service, @NonNull AsyncRequest<I> request) {
786             super(service);
787 
788             mRequest = request;
789         }
790 
791         @Override
run()792         public void run() {
793             final S remoteService = getService();
794             if (remoteService == null) return;
795             try {
796                 mRequest.run(remoteService.mService);
797             } catch (RemoteException e) {
798                 Slog.w(TAG, "exception handling async request (" + this + "): " + e);
799             } finally {
800                 finish();
801             }
802         }
803     }
804 }
805