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