• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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.telecom;
18 
19 import android.content.ComponentName;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.ServiceConnection;
23 import android.os.IBinder;
24 import android.os.UserHandle;
25 import android.text.TextUtils;
26 import android.util.ArraySet;
27 
28 import com.android.internal.annotations.VisibleForTesting;
29 import com.android.internal.util.Preconditions;
30 
31 import java.util.Collections;
32 import java.util.Set;
33 import java.util.concurrent.ConcurrentHashMap;
34 
35 /**
36  * Abstract class to perform the work of binding and unbinding to the specified service interface.
37  * Subclasses supply the service intent and component name and this class will invoke protected
38  * methods when the class is bound, unbound, or upon failure.
39  */
40 abstract class ServiceBinder {
41 
42     /**
43      * Callback to notify after a binding succeeds or fails.
44      */
45     interface BindCallback {
onSuccess()46         void onSuccess();
onFailure()47         void onFailure();
48     }
49 
50     /**
51      * Listener for bind events on ServiceBinder.
52      */
53     interface Listener<ServiceBinderClass extends ServiceBinder> {
onUnbind(ServiceBinderClass serviceBinder)54         void onUnbind(ServiceBinderClass serviceBinder);
55     }
56 
57     /**
58      * Helper class to perform on-demand binding.
59      */
60     final class Binder2 {
61         /**
62          * Performs an asynchronous bind to the service (only if not already bound) and executes the
63          * specified callback.
64          *
65          * @param callback The callback to notify of the binding's success or failure.
66          * @param call The call for which we are being bound.
67          */
bind(BindCallback callback, Call call)68         void bind(BindCallback callback, Call call) {
69             Log.d(ServiceBinder.this, "bind()");
70 
71             // Reset any abort request if we're asked to bind again.
72             clearAbort();
73 
74             if (!mCallbacks.isEmpty()) {
75                 // Binding already in progress, append to the list of callbacks and bail out.
76                 mCallbacks.add(callback);
77                 return;
78             }
79 
80             mCallbacks.add(callback);
81             if (mServiceConnection == null) {
82                 Intent serviceIntent = new Intent(mServiceAction).setComponent(mComponentName);
83                 ServiceConnection connection = new ServiceBinderConnection(call);
84 
85                 Log.event(call, Log.Events.BIND_CS, mComponentName);
86                 final int bindingFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE;
87                 final boolean isBound;
88                 if (mUserHandle != null) {
89                     isBound = mContext.bindServiceAsUser(serviceIntent, connection, bindingFlags,
90                             mUserHandle);
91                 } else {
92                     isBound = mContext.bindService(serviceIntent, connection, bindingFlags);
93                 }
94                 if (!isBound) {
95                     handleFailedConnection();
96                     return;
97                 }
98             } else {
99                 Log.d(ServiceBinder.this, "Service is already bound.");
100                 Preconditions.checkNotNull(mBinder);
101                 handleSuccessfulConnection();
102             }
103         }
104     }
105 
106     private final class ServiceBinderConnection implements ServiceConnection {
107         /**
108          * The initial call for which the service was bound.
109          */
110         private Call mCall;
111 
ServiceBinderConnection(Call call)112         ServiceBinderConnection(Call call) {
113             mCall = call;
114         }
115 
116         @Override
onServiceConnected(ComponentName componentName, IBinder binder)117         public void onServiceConnected(ComponentName componentName, IBinder binder) {
118             try {
119                 Log.startSession("SBC.oSC");
120                 synchronized (mLock) {
121                     Log.i(this, "Service bound %s", componentName);
122 
123                     Log.event(mCall, Log.Events.CS_BOUND, componentName);
124                     mCall = null;
125 
126                     // Unbind request was queued so unbind immediately.
127                     if (mIsBindingAborted) {
128                         clearAbort();
129                         logServiceDisconnected("onServiceConnected");
130                         mContext.unbindService(this);
131                         handleFailedConnection();
132                         return;
133                     }
134 
135                     mServiceConnection = this;
136                     setBinder(binder);
137                     handleSuccessfulConnection();
138                 }
139             } finally {
140                 Log.endSession();
141             }
142         }
143 
144         @Override
onServiceDisconnected(ComponentName componentName)145         public void onServiceDisconnected(ComponentName componentName) {
146             try {
147                 Log.startSession("SBC.oSD");
148                 synchronized (mLock) {
149                     logServiceDisconnected("onServiceDisconnected");
150 
151                     mServiceConnection = null;
152                     clearAbort();
153 
154                     handleServiceDisconnected();
155                 }
156             } finally {
157                 Log.endSession();
158             }
159         }
160     }
161 
162     /** The application context. */
163     private final Context mContext;
164 
165     /** The Telecom lock object. */
166     protected final TelecomSystem.SyncRoot mLock;
167 
168     /** The intent action to use when binding through {@link Context#bindService}. */
169     private final String mServiceAction;
170 
171     /** The component name of the service to bind to. */
172     protected final ComponentName mComponentName;
173 
174     /** The set of callbacks waiting for notification of the binding's success or failure. */
175     private final Set<BindCallback> mCallbacks = new ArraySet<>();
176 
177     /** Used to bind and unbind from the service. */
178     private ServiceConnection mServiceConnection;
179 
180     /** {@link UserHandle} to use for binding, to support work profiles and multi-user. */
181     private UserHandle mUserHandle;
182 
183     /** The binder provided by {@link ServiceConnection#onServiceConnected} */
184     private IBinder mBinder;
185 
186     private int mAssociatedCallCount = 0;
187 
188     /**
189      * Indicates that an unbind request was made when the service was not yet bound. If the service
190      * successfully connects when this is true, it should be unbound immediately.
191      */
192     private boolean mIsBindingAborted;
193 
194     /**
195      * Set of currently registered listeners.
196      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
197      * load factor before resizing, 1 means we only expect a single thread to
198      * access the map so make only a single shard
199      */
200     private final Set<Listener> mListeners = Collections.newSetFromMap(
201             new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
202 
203     /**
204      * Persists the specified parameters and initializes the new instance.
205      *
206      * @param serviceAction The intent-action used with {@link Context#bindService}.
207      * @param componentName The component name of the service with which to bind.
208      * @param context The context.
209      * @param userHandle The {@link UserHandle} to use for binding.
210      */
ServiceBinder(String serviceAction, ComponentName componentName, Context context, TelecomSystem.SyncRoot lock, UserHandle userHandle)211     protected ServiceBinder(String serviceAction, ComponentName componentName, Context context,
212             TelecomSystem.SyncRoot lock, UserHandle userHandle) {
213         Preconditions.checkState(!TextUtils.isEmpty(serviceAction));
214         Preconditions.checkNotNull(componentName);
215 
216         mContext = context;
217         mLock = lock;
218         mServiceAction = serviceAction;
219         mComponentName = componentName;
220         mUserHandle = userHandle;
221     }
222 
incrementAssociatedCallCount()223     final void incrementAssociatedCallCount() {
224         mAssociatedCallCount++;
225         Log.v(this, "Call count increment %d, %s", mAssociatedCallCount,
226                 mComponentName.flattenToShortString());
227     }
228 
decrementAssociatedCallCount()229     final void decrementAssociatedCallCount() {
230         decrementAssociatedCallCount(false /*isSuppressingUnbind*/);
231     }
232 
decrementAssociatedCallCount(boolean isSuppressingUnbind)233     final void decrementAssociatedCallCount(boolean isSuppressingUnbind) {
234         if (mAssociatedCallCount > 0) {
235             mAssociatedCallCount--;
236             Log.v(this, "Call count decrement %d, %s", mAssociatedCallCount,
237                     mComponentName.flattenToShortString());
238 
239             if (!isSuppressingUnbind && mAssociatedCallCount == 0) {
240                 unbind();
241             }
242         } else {
243             Log.wtf(this, "%s: ignoring a request to decrement mAssociatedCallCount below zero",
244                     mComponentName.getClassName());
245         }
246     }
247 
getAssociatedCallCount()248     final int getAssociatedCallCount() {
249         return mAssociatedCallCount;
250     }
251 
252     /**
253      * Unbinds from the service if already bound, no-op otherwise.
254      */
unbind()255     final void unbind() {
256         if (mServiceConnection == null) {
257             // We're not yet bound, so queue up an abort request.
258             mIsBindingAborted = true;
259         } else {
260             logServiceDisconnected("unbind");
261             mContext.unbindService(mServiceConnection);
262             mServiceConnection = null;
263             setBinder(null);
264         }
265     }
266 
getComponentName()267     final ComponentName getComponentName() {
268         return mComponentName;
269     }
270 
271     @VisibleForTesting
isServiceValid(String actionName)272     public boolean isServiceValid(String actionName) {
273         if (mBinder == null) {
274             Log.w(this, "%s invoked while service is unbound", actionName);
275             return false;
276         }
277 
278         return true;
279     }
280 
addListener(Listener listener)281     final void addListener(Listener listener) {
282         mListeners.add(listener);
283     }
284 
removeListener(Listener listener)285     final void removeListener(Listener listener) {
286         if (listener != null) {
287             mListeners.remove(listener);
288         }
289     }
290 
291     /**
292      * Logs a standard message upon service disconnection. This method exists because there is no
293      * single method called whenever the service unbinds and we want to log the same string in all
294      * instances where that occurs.  (Context.unbindService() does not cause onServiceDisconnected
295      * to execute).
296      *
297      * @param sourceTag Tag to disambiguate
298      */
logServiceDisconnected(String sourceTag)299     private void logServiceDisconnected(String sourceTag) {
300         Log.i(this, "Service unbound %s, from %s.", mComponentName, sourceTag);
301     }
302 
303     /**
304      * Notifies all the outstanding callbacks that the service is successfully bound. The list of
305      * outstanding callbacks is cleared afterwards.
306      */
handleSuccessfulConnection()307     private void handleSuccessfulConnection() {
308         for (BindCallback callback : mCallbacks) {
309             callback.onSuccess();
310         }
311         mCallbacks.clear();
312     }
313 
314     /**
315      * Notifies all the outstanding callbacks that the service failed to bind. The list of
316      * outstanding callbacks is cleared afterwards.
317      */
handleFailedConnection()318     private void handleFailedConnection() {
319         for (BindCallback callback : mCallbacks) {
320             callback.onFailure();
321         }
322         mCallbacks.clear();
323     }
324 
325     /**
326      * Handles a service disconnection.
327      */
handleServiceDisconnected()328     private void handleServiceDisconnected() {
329         setBinder(null);
330     }
331 
clearAbort()332     private void clearAbort() {
333         mIsBindingAborted = false;
334     }
335 
336     /**
337      * Sets the (private) binder and updates the child class.
338      *
339      * @param binder The new binder value.
340      */
setBinder(IBinder binder)341     private void setBinder(IBinder binder) {
342         if (mBinder != binder) {
343             if (binder == null) {
344                 removeServiceInterface();
345                 mBinder = null;
346                 for (Listener l : mListeners) {
347                     l.onUnbind(this);
348                 }
349             } else {
350                 mBinder = binder;
351                 setServiceInterface(binder);
352             }
353         }
354     }
355 
356     /**
357      * Sets the service interface after the service is bound.
358      *
359      * @param binder The new binder interface that is being set.
360      */
setServiceInterface(IBinder binder)361     protected abstract void setServiceInterface(IBinder binder);
362 
363     /**
364      * Removes the service interface before the service is unbound.
365      */
removeServiceInterface()366     protected abstract void removeServiceInterface();
367 }
368