• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.server.nearby.common.servicemonitor;
18 
19 import android.annotation.Nullable;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.os.Handler;
23 import android.os.IBinder;
24 import android.os.RemoteException;
25 
26 import java.io.PrintWriter;
27 import java.util.Objects;
28 import java.util.concurrent.Executor;
29 
30 /**
31  * This is exported from frameworks ServiceWatcher.
32  * A ServiceMonitor is responsible for continuously maintaining an active binding to a service
33  * selected by it's {@link ServiceProvider}. The {@link ServiceProvider} may change the service it
34  * selects over time, and the currently bound service may crash, restart, have a user change, have
35  * changes made to its package, and so on and so forth. The ServiceMonitor is responsible for
36  * maintaining the binding across all these changes.
37  *
38  * <p>Clients may invoke {@link BinderOperation}s on the ServiceMonitor, and it will make a best
39  * effort to run these on the currently bound service, but individual operations may fail (if there
40  * is no service currently bound for instance). In order to help clients maintain the correct state,
41  * clients may supply a {@link ServiceListener}, which is informed when the ServiceMonitor connects
42  * and disconnects from a service. This allows clients to bring a bound service back into a known
43  * state on connection, and then run binder operations from there. In order to help clients
44  * accomplish this, ServiceMonitor guarantees that {@link BinderOperation}s and the
45  * {@link ServiceListener} will always be run on the same thread, so that strong ordering guarantees
46  * can be established between them.
47  *
48  * There is never any guarantee of whether a ServiceMonitor is currently connected to a service, and
49  * whether any particular {@link BinderOperation} will succeed. Clients must ensure they do not rely
50  * on this, and instead use {@link ServiceListener} notifications as necessary to recover from
51  * failures.
52  */
53 public interface ServiceMonitor {
54 
55     /**
56      * Operation to run on a binder interface. All operations will be run on the thread used by the
57      * ServiceMonitor this is run with.
58      */
59     interface BinderOperation {
60         /** Invoked to run the operation. Run on the ServiceMonitor thread. */
run(IBinder binder)61         void run(IBinder binder) throws RemoteException;
62 
63         /**
64          * Invoked if {@link #run(IBinder)} could not be invoked because there was no current
65          * binding, or if {@link #run(IBinder)} threw an exception ({@link RemoteException} or
66          * {@link RuntimeException}). This callback is only intended for resource deallocation and
67          * cleanup in response to a single binder operation, it should not be used to propagate
68          * errors further. Run on the ServiceMonitor thread.
69          */
onError()70         default void onError() {}
71     }
72 
73     /**
74      * Listener for bind and unbind events. All operations will be run on the thread used by the
75      * ServiceMonitor this is run with.
76      *
77      * @param <TBoundServiceInfo> type of bound service
78      */
79     interface ServiceListener<TBoundServiceInfo extends BoundServiceInfo> {
80         /** Invoked when a service is bound. Run on the ServiceMonitor thread. */
onBind(IBinder binder, TBoundServiceInfo service)81         void onBind(IBinder binder, TBoundServiceInfo service) throws RemoteException;
82 
83         /** Invoked when a service is unbound. Run on the ServiceMonitor thread. */
onUnbind()84         void onUnbind();
85     }
86 
87     /**
88      * A listener for when a {@link ServiceProvider} decides that the current service has changed.
89      */
90     interface ServiceChangedListener {
91         /**
92          * Should be invoked when the current service may have changed.
93          */
onServiceChanged()94         void onServiceChanged();
95     }
96 
97     /**
98      * This provider encapsulates the logic of deciding what service a {@link ServiceMonitor} should
99      * be bound to at any given moment.
100      *
101      * @param <TBoundServiceInfo> type of bound service
102      */
103     interface ServiceProvider<TBoundServiceInfo extends BoundServiceInfo> {
104         /**
105          * Should return true if there exists at least one service capable of meeting the criteria
106          * of this provider. This does not imply that {@link #getServiceInfo()} will always return a
107          * non-null result, as any service may be disqualified for various reasons at any point in
108          * time. May be invoked at any time from any thread and thus should generally not have any
109          * dependency on the other methods in this interface.
110          */
hasMatchingService()111         boolean hasMatchingService();
112 
113         /**
114          * Invoked when the provider should start monitoring for any changes that could result in a
115          * different service selection, and should invoke
116          * {@link ServiceChangedListener#onServiceChanged()} in that case. {@link #getServiceInfo()}
117          * may be invoked after this method is called.
118          */
register(ServiceChangedListener listener)119         void register(ServiceChangedListener listener);
120 
121         /**
122          * Invoked when the provider should stop monitoring for any changes that could result in a
123          * different service selection, should no longer invoke
124          * {@link ServiceChangedListener#onServiceChanged()}. {@link #getServiceInfo()} will not be
125          * invoked after this method is called.
126          */
unregister()127         void unregister();
128 
129         /**
130          * Must be implemented to return the current service selected by this provider. May return
131          * null if no service currently meets the criteria. Only invoked while registered.
132          */
getServiceInfo()133         @Nullable TBoundServiceInfo getServiceInfo();
134     }
135 
136     /**
137      * Information on the service selected as the best option for binding.
138      */
139     class BoundServiceInfo {
140 
141         protected final @Nullable String mAction;
142         protected final int mUid;
143         protected final ComponentName mComponentName;
144 
BoundServiceInfo(String action, int uid, ComponentName componentName)145         protected BoundServiceInfo(String action, int uid, ComponentName componentName) {
146             mAction = action;
147             mUid = uid;
148             mComponentName = Objects.requireNonNull(componentName);
149         }
150 
151         /** Returns the action associated with this bound service. */
getAction()152         public @Nullable String getAction() {
153             return mAction;
154         }
155 
156         /** Returns the component of this bound service. */
getComponentName()157         public ComponentName getComponentName() {
158             return mComponentName;
159         }
160 
161         @Override
equals(Object o)162         public final boolean equals(Object o) {
163             if (this == o) {
164                 return true;
165             }
166             if (!(o instanceof BoundServiceInfo)) {
167                 return false;
168             }
169 
170             BoundServiceInfo that = (BoundServiceInfo) o;
171             return mUid == that.mUid
172                     && Objects.equals(mAction, that.mAction)
173                     && mComponentName.equals(that.mComponentName);
174         }
175 
176         @Override
hashCode()177         public final int hashCode() {
178             return Objects.hash(mAction, mUid, mComponentName);
179         }
180 
181         @Override
toString()182         public String toString() {
183             if (mComponentName == null) {
184                 return "none";
185             } else {
186                 return mUid + "/" + mComponentName.flattenToShortString();
187             }
188         }
189     }
190 
191     /**
192      * Creates a new ServiceMonitor instance.
193      */
create( Context context, String tag, ServiceProvider<TBoundServiceInfo> serviceProvider, @Nullable ServiceListener<? super TBoundServiceInfo> serviceListener)194     static <TBoundServiceInfo extends BoundServiceInfo> ServiceMonitor create(
195             Context context,
196             String tag,
197             ServiceProvider<TBoundServiceInfo> serviceProvider,
198             @Nullable ServiceListener<? super TBoundServiceInfo> serviceListener) {
199         return create(context, ForegroundThread.getHandler(), ForegroundThread.getExecutor(), tag,
200                 serviceProvider, serviceListener);
201     }
202 
203     /**
204      * Creates a new ServiceMonitor instance that runs on the given handler.
205      */
create( Context context, Handler handler, Executor executor, String tag, ServiceProvider<TBoundServiceInfo> serviceProvider, @Nullable ServiceListener<? super TBoundServiceInfo> serviceListener)206     static <TBoundServiceInfo extends BoundServiceInfo> ServiceMonitor create(
207             Context context,
208             Handler handler,
209             Executor executor,
210             String tag,
211             ServiceProvider<TBoundServiceInfo> serviceProvider,
212             @Nullable ServiceListener<? super TBoundServiceInfo> serviceListener) {
213         return new ServiceMonitorImpl<>(context, handler, executor, tag, serviceProvider,
214                 serviceListener);
215     }
216 
217     /**
218      * Returns true if there is at least one service that the ServiceMonitor could hypothetically
219      * bind to, as selected by the {@link ServiceProvider}.
220      */
checkServiceResolves()221     boolean checkServiceResolves();
222 
223     /**
224      * Registers the ServiceMonitor, so that it will begin maintaining an active binding to the
225      * service selected by {@link ServiceProvider}, until {@link #unregister()} is called.
226      */
register()227     void register();
228 
229     /**
230      * Unregisters the ServiceMonitor, so that it will release any active bindings. If the
231      * ServiceMonitor is currently bound, this will result in one final
232      * {@link ServiceListener#onUnbind()} invocation, which may happen after this method completes
233      * (but which is guaranteed to occur before any further
234      * {@link ServiceListener#onBind(IBinder, BoundServiceInfo)} invocation in response to a later
235      * call to {@link #register()}).
236      */
unregister()237     void unregister();
238 
239     /**
240      * Runs the given binder operation on the currently bound service (if available). The operation
241      * will always fail if the ServiceMonitor is not currently registered.
242      */
runOnBinder(BinderOperation operation)243     void runOnBinder(BinderOperation operation);
244 
245     /**
246      * Dumps ServiceMonitor information.
247      */
dump(PrintWriter pw)248     void dump(PrintWriter pw);
249 }
250