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