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