blob: f15570b4446bed853b484390d170b5b746786aa1 [file] [log] [blame]
Santos Cordon63aeb162014-02-10 09:20:40 -08001/*
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
Tyler Gunn7cc70b42014-09-12 22:17:27 -070017package com.android.server.telecom;
Santos Cordon63aeb162014-02-10 09:20:40 -080018
19import android.content.ComponentName;
20import android.content.Context;
21import android.content.Intent;
22import android.content.ServiceConnection;
23import android.os.IBinder;
Tyler Gunnf7d32b52017-06-21 19:55:54 -070024import android.os.RemoteException;
Evan Charlton105d9772014-11-25 14:08:53 -080025import android.os.UserHandle;
Brad Ebingera3eccfe2016-10-05 15:45:22 -070026import android.telecom.Log;
Santos Cordon4bc02452014-11-19 15:51:12 -080027import android.text.TextUtils;
28import android.util.ArraySet;
Santos Cordon63aeb162014-02-10 09:20:40 -080029
Brad Ebingerf1900072015-11-12 17:25:06 -080030import com.android.internal.annotations.VisibleForTesting;
Tyler Gunn91d43cf2014-09-17 12:19:39 -070031import com.android.internal.util.Preconditions;
Santos Cordon5c12c6e2014-02-13 14:35:31 -080032
Jay Shraunera82c8f72014-08-14 15:49:16 -070033import java.util.Collections;
Santos Cordon5c12c6e2014-02-13 14:35:31 -080034import java.util.Set;
Jay Shraunera82c8f72014-08-14 15:49:16 -070035import java.util.concurrent.ConcurrentHashMap;
Santos Cordon63aeb162014-02-10 09:20:40 -080036
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 */
Ihab Awad78a5e6b2015-02-06 10:13:05 -080042abstract class ServiceBinder {
Santos Cordon63aeb162014-02-10 09:20:40 -080043
Santos Cordon5c12c6e2014-02-13 14:35:31 -080044 /**
45 * Callback to notify after a binding succeeds or fails.
46 */
47 interface BindCallback {
Santos Cordonc499c1c2014-04-14 17:13:14 -070048 void onSuccess();
49 void onFailure();
50 }
51
52 /**
53 * Listener for bind events on ServiceBinder.
54 */
Ihab Awad78a5e6b2015-02-06 10:13:05 -080055 interface Listener<ServiceBinderClass extends ServiceBinder> {
Santos Cordon74d420b2014-05-07 14:38:47 -070056 void onUnbind(ServiceBinderClass serviceBinder);
Santos Cordon5c12c6e2014-02-13 14:35:31 -080057 }
58
Ben Gilad61925612014-03-11 19:06:36 -070059 /**
60 * Helper class to perform on-demand binding.
61 */
Ihab Awad8731faf2015-03-26 13:43:22 -070062 final class Binder2 {
Ben Gilad61925612014-03-11 19:06:36 -070063 /**
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.
Santos Cordon165c1ce2015-07-10 16:07:59 -070068 * @param call The call for which we are being bound.
Ben Gilad61925612014-03-11 19:06:36 -070069 */
Santos Cordon165c1ce2015-07-10 16:07:59 -070070 void bind(BindCallback callback, Call call) {
Ben Gilad61925612014-03-11 19:06:36 -070071 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);
Santos Cordon165c1ce2015-07-10 16:07:59 -070085 ServiceConnection connection = new ServiceBinderConnection(call);
Ben Gilad61925612014-03-11 19:06:36 -070086
Brad Ebingera3eccfe2016-10-05 15:45:22 -070087 Log.addEvent(call, LogUtils.Events.BIND_CS, mComponentName);
Santos Cordon8e6edbf2015-05-29 12:25:37 -070088 final int bindingFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE;
89 final boolean isBound;
Evan Charlton105d9772014-11-25 14:08:53 -080090 if (mUserHandle != null) {
Santos Cordon8e6edbf2015-05-29 12:25:37 -070091 isBound = mContext.bindServiceAsUser(serviceIntent, connection, bindingFlags,
92 mUserHandle);
Evan Charlton105d9772014-11-25 14:08:53 -080093 } else {
Santos Cordon8e6edbf2015-05-29 12:25:37 -070094 isBound = mContext.bindService(serviceIntent, connection, bindingFlags);
Evan Charlton105d9772014-11-25 14:08:53 -080095 }
Santos Cordon8e6edbf2015-05-29 12:25:37 -070096 if (!isBound) {
Ben Gilad61925612014-03-11 19:06:36 -070097 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
Tyler Gunnf7d32b52017-06-21 19:55:54 -0700108 private class ServiceDeathRecipient implements IBinder.DeathRecipient {
109
110 private ComponentName mComponentName;
111
112 ServiceDeathRecipient(ComponentName name) {
113 mComponentName = name;
114 }
115
116 @Override
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
Santos Cordon63aeb162014-02-10 09:20:40 -0800131 private final class ServiceBinderConnection implements ServiceConnection {
Santos Cordon165c1ce2015-07-10 16:07:59 -0700132 /**
133 * The initial call for which the service was bound.
134 */
135 private Call mCall;
136
137 ServiceBinderConnection(Call call) {
138 mCall = call;
139 }
140
Santos Cordon63aeb162014-02-10 09:20:40 -0800141 @Override
142 public void onServiceConnected(ComponentName componentName, IBinder binder) {
Brad Ebinger11623a32015-11-25 13:52:02 -0800143 try {
144 Log.startSession("SBC.oSC");
145 synchronized (mLock) {
146 Log.i(this, "Service bound %s", componentName);
Santos Cordon63aeb162014-02-10 09:20:40 -0800147
Brad Ebingera3eccfe2016-10-05 15:45:22 -0700148 Log.addEvent(mCall, LogUtils.Events.CS_BOUND, componentName);
Brad Ebinger11623a32015-11-25 13:52:02 -0800149 mCall = null;
Santos Cordon165c1ce2015-07-10 16:07:59 -0700150
Brad Ebinger11623a32015-11-25 13:52:02 -0800151 // Unbind request was queued so unbind immediately.
152 if (mIsBindingAborted) {
153 clearAbort();
154 logServiceDisconnected("onServiceConnected");
155 mContext.unbindService(this);
156 handleFailedConnection();
157 return;
158 }
Tyler Gunnf7d32b52017-06-21 19:55:54 -0700159 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 }
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700173 }
Brad Ebinger11623a32015-11-25 13:52:02 -0800174 } finally {
175 Log.endSession();
Santos Cordon63aeb162014-02-10 09:20:40 -0800176 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800177 }
178
179 @Override
180 public void onServiceDisconnected(ComponentName componentName) {
Brad Ebinger11623a32015-11-25 13:52:02 -0800181 try {
182 Log.startSession("SBC.oSD");
183 synchronized (mLock) {
184 logServiceDisconnected("onServiceDisconnected");
Tyler Gunnf7d32b52017-06-21 19:55:54 -0700185 handleDisconnect();
Brad Ebinger11623a32015-11-25 13:52:02 -0800186 }
187 } finally {
188 Log.endSession();
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700189 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800190 }
191 }
192
Tyler Gunnf7d32b52017-06-21 19:55:54 -0700193 private void handleDisconnect() {
194 mServiceConnection = null;
195 clearAbort();
196
197 handleServiceDisconnected();
198 }
199
Santos Cordon63aeb162014-02-10 09:20:40 -0800200 /** The application context. */
201 private final Context mContext;
202
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700203 /** The Telecom lock object. */
204 protected final TelecomSystem.SyncRoot mLock;
205
Santos Cordon63aeb162014-02-10 09:20:40 -0800206 /** 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. */
Tyler Gunn9b618b82016-10-17 15:54:35 -0700210 protected final ComponentName mComponentName;
Santos Cordon63aeb162014-02-10 09:20:40 -0800211
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800212 /** The set of callbacks waiting for notification of the binding's success or failure. */
Santos Cordon4bc02452014-11-19 15:51:12 -0800213 private final Set<BindCallback> mCallbacks = new ArraySet<>();
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800214
Santos Cordon63aeb162014-02-10 09:20:40 -0800215 /** Used to bind and unbind from the service. */
216 private ServiceConnection mServiceConnection;
217
Tyler Gunnf7d32b52017-06-21 19:55:54 -0700218 /** Used to handle death of the service. */
219 private ServiceDeathRecipient mServiceDeathRecipient;
220
Evan Charlton105d9772014-11-25 14:08:53 -0800221 /** {@link UserHandle} to use for binding, to support work profiles and multi-user. */
222 private UserHandle mUserHandle;
223
Santos Cordon63aeb162014-02-10 09:20:40 -0800224 /** The binder provided by {@link ServiceConnection#onServiceConnected} */
225 private IBinder mBinder;
226
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800227 private int mAssociatedCallCount = 0;
228
Santos Cordon63aeb162014-02-10 09:20:40 -0800229 /**
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 /**
Santos Cordonc499c1c2014-04-14 17:13:14 -0700236 * Set of currently registered listeners.
Jay Shraunera82c8f72014-08-14 15:49:16 -0700237 * 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
Santos Cordonc499c1c2014-04-14 17:13:14 -0700240 */
Jay Shraunera82c8f72014-08-14 15:49:16 -0700241 private final Set<Listener> mListeners = Collections.newSetFromMap(
242 new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
Santos Cordonc499c1c2014-04-14 17:13:14 -0700243
244 /**
Santos Cordon63aeb162014-02-10 09:20:40 -0800245 * 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.
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700249 * @param context The context.
Evan Charlton105d9772014-11-25 14:08:53 -0800250 * @param userHandle The {@link UserHandle} to use for binding.
Santos Cordon63aeb162014-02-10 09:20:40 -0800251 */
Evan Charlton105d9772014-11-25 14:08:53 -0800252 protected ServiceBinder(String serviceAction, ComponentName componentName, Context context,
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700253 TelecomSystem.SyncRoot lock, UserHandle userHandle) {
Santos Cordon4bc02452014-11-19 15:51:12 -0800254 Preconditions.checkState(!TextUtils.isEmpty(serviceAction));
Santos Cordon63aeb162014-02-10 09:20:40 -0800255 Preconditions.checkNotNull(componentName);
256
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700257 mContext = context;
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700258 mLock = lock;
Santos Cordon63aeb162014-02-10 09:20:40 -0800259 mServiceAction = serviceAction;
260 mComponentName = componentName;
Evan Charlton105d9772014-11-25 14:08:53 -0800261 mUserHandle = userHandle;
Santos Cordon63aeb162014-02-10 09:20:40 -0800262 }
263
Andreas Gampe98c15772018-03-01 13:58:57 -0800264 final UserHandle getUserHandle() {
265 return mUserHandle;
266 }
267
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800268 final void incrementAssociatedCallCount() {
269 mAssociatedCallCount++;
Santos Cordonc499c1c2014-04-14 17:13:14 -0700270 Log.v(this, "Call count increment %d, %s", mAssociatedCallCount,
271 mComponentName.flattenToShortString());
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800272 }
273
274 final void decrementAssociatedCallCount() {
Tyler Gunn9b618b82016-10-17 15:54:35 -0700275 decrementAssociatedCallCount(false /*isSuppressingUnbind*/);
276 }
277
278 final void decrementAssociatedCallCount(boolean isSuppressingUnbind) {
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800279 if (mAssociatedCallCount > 0) {
280 mAssociatedCallCount--;
Santos Cordonc499c1c2014-04-14 17:13:14 -0700281 Log.v(this, "Call count decrement %d, %s", mAssociatedCallCount,
282 mComponentName.flattenToShortString());
283
Tyler Gunn9b618b82016-10-17 15:54:35 -0700284 if (!isSuppressingUnbind && mAssociatedCallCount == 0) {
Santos Cordonc499c1c2014-04-14 17:13:14 -0700285 unbind();
286 }
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800287 } else {
Sailesh Nepalf1c191d2014-03-07 18:17:39 -0800288 Log.wtf(this, "%s: ignoring a request to decrement mAssociatedCallCount below zero",
289 mComponentName.getClassName());
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800290 }
291 }
292
293 final int getAssociatedCallCount() {
294 return mAssociatedCallCount;
295 }
296
Santos Cordon63aeb162014-02-10 09:20:40 -0800297 /**
298 * Unbinds from the service if already bound, no-op otherwise.
299 */
300 final void unbind() {
Santos Cordon63aeb162014-02-10 09:20:40 -0800301 if (mServiceConnection == null) {
302 // We're not yet bound, so queue up an abort request.
303 mIsBindingAborted = true;
304 } else {
Santos Cordonc499c1c2014-04-14 17:13:14 -0700305 logServiceDisconnected("unbind");
Tyler Gunn110dd5d2018-04-16 16:20:23 -0700306 unlinkDeathRecipient();
Santos Cordon63aeb162014-02-10 09:20:40 -0800307 mContext.unbindService(mServiceConnection);
308 mServiceConnection = null;
Ben Gilad61925612014-03-11 19:06:36 -0700309 setBinder(null);
Santos Cordon63aeb162014-02-10 09:20:40 -0800310 }
311 }
312
Tyler Gunnf4f05392018-03-26 18:57:59 +0000313 public final ComponentName getComponentName() {
Santos Cordon63aeb162014-02-10 09:20:40 -0800314 return mComponentName;
315 }
316
Brad Ebingerf1900072015-11-12 17:25:06 -0800317 @VisibleForTesting
318 public boolean isServiceValid(String actionName) {
Ben Gilad61925612014-03-11 19:06:36 -0700319 if (mBinder == null) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700320 Log.w(this, "%s invoked while service is unbound", actionName);
Ben Gilad61925612014-03-11 19:06:36 -0700321 return false;
322 }
323
324 return true;
325 }
326
Santos Cordonc499c1c2014-04-14 17:13:14 -0700327 final void addListener(Listener listener) {
328 mListeners.add(listener);
329 }
330
331 final void removeListener(Listener listener) {
Jay Shraunera82c8f72014-08-14 15:49:16 -0700332 if (listener != null) {
333 mListeners.remove(listener);
334 }
Santos Cordonc499c1c2014-04-14 17:13:14 -0700335 }
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 */
345 private void logServiceDisconnected(String sourceTag) {
346 Log.i(this, "Service unbound %s, from %s.", mComponentName, sourceTag);
347 }
348
Santos Cordon63aeb162014-02-10 09:20:40 -0800349 /**
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800350 * Notifies all the outstanding callbacks that the service is successfully bound. The list of
351 * outstanding callbacks is cleared afterwards.
Santos Cordon63aeb162014-02-10 09:20:40 -0800352 */
Sailesh Nepalb6141ae2014-02-18 08:45:26 -0800353 private void handleSuccessfulConnection() {
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800354 for (BindCallback callback : mCallbacks) {
355 callback.onSuccess();
356 }
357 mCallbacks.clear();
358 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800359
360 /**
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800361 * Notifies all the outstanding callbacks that the service failed to bind. The list of
362 * outstanding callbacks is cleared afterwards.
Santos Cordon63aeb162014-02-10 09:20:40 -0800363 */
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800364 private void handleFailedConnection() {
365 for (BindCallback callback : mCallbacks) {
366 callback.onFailure();
367 }
368 mCallbacks.clear();
369 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800370
371 /**
372 * Handles a service disconnection.
373 */
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800374 private void handleServiceDisconnected() {
Tyler Gunn110dd5d2018-04-16 16:20:23 -0700375 unlinkDeathRecipient();
Ben Gilad61925612014-03-11 19:06:36 -0700376 setBinder(null);
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800377 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800378
Tyler Gunn110dd5d2018-04-16 16:20:23 -0700379 /**
380 * Handles un-linking the death recipient from the service's binder.
381 */
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
Santos Cordon63aeb162014-02-10 09:20:40 -0800394 private void clearAbort() {
395 mIsBindingAborted = false;
396 }
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800397
398 /**
Ben Gilad61925612014-03-11 19:06:36 -0700399 * Sets the (private) binder and updates the child class.
400 *
401 * @param binder The new binder value.
402 */
403 private void setBinder(IBinder binder) {
Santos Cordonc499c1c2014-04-14 17:13:14 -0700404 if (mBinder != binder) {
Santos Cordonc499c1c2014-04-14 17:13:14 -0700405 if (binder == null) {
Brad Ebinger6e8f3d72016-06-20 11:35:42 -0700406 removeServiceInterface();
407 mBinder = null;
Jay Shraunera82c8f72014-08-14 15:49:16 -0700408 for (Listener l : mListeners) {
Santos Cordonc499c1c2014-04-14 17:13:14 -0700409 l.onUnbind(this);
410 }
Brad Ebinger6e8f3d72016-06-20 11:35:42 -0700411 } else {
412 mBinder = binder;
413 setServiceInterface(binder);
Santos Cordonc499c1c2014-04-14 17:13:14 -0700414 }
415 }
Ben Gilad61925612014-03-11 19:06:36 -0700416 }
417
418 /**
Brad Ebinger6e8f3d72016-06-20 11:35:42 -0700419 * Sets the service interface after the service is bound.
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800420 *
Brad Ebinger6e8f3d72016-06-20 11:35:42 -0700421 * @param binder The new binder interface that is being set.
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800422 */
423 protected abstract void setServiceInterface(IBinder binder);
Brad Ebinger6e8f3d72016-06-20 11:35:42 -0700424
425 /**
426 * Removes the service interface before the service is unbound.
427 */
428 protected abstract void removeServiceInterface();
Santos Cordon63aeb162014-02-10 09:20:40 -0800429}