blob: 945f3545d7dabe6e0d23e4bab7abc1ae2bca5315 [file] [log] [blame]
Santos Cordon63aeb162014-02-10 09:20:40 -08001/*
2 * Copyright 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
17package com.android.telecomm;
18
Evan Charltona05805b2014-03-05 08:21:46 -080019import android.os.Bundle;
Santos Cordon3d3b4052014-05-05 12:05:36 -070020import android.os.Handler;
Santos Cordon63aeb162014-02-10 09:20:40 -080021import android.os.IBinder;
Santos Cordon3d3b4052014-05-05 12:05:36 -070022import android.os.Message;
Santos Cordon63aeb162014-02-10 09:20:40 -080023import android.os.RemoteException;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070024import android.telecomm.CallAudioState;
Santos Cordon63aeb162014-02-10 09:20:40 -080025import android.telecomm.CallInfo;
Evan Charltona05805b2014-03-05 08:21:46 -080026import android.telecomm.CallService;
Ben Giladc5b22692014-02-18 20:03:22 -080027import android.telecomm.CallServiceDescriptor;
Sailesh Nepal83cfe7c2014-03-11 19:54:22 -070028import android.telecomm.TelecommConstants;
Sailesh Nepala439e1b2014-03-11 18:19:58 -070029
Santos Cordon3d3b4052014-05-05 12:05:36 -070030import com.android.internal.os.SomeArgs;
Sailesh Nepala439e1b2014-03-11 18:19:58 -070031import com.android.internal.telecomm.ICallService;
32import com.android.internal.telecomm.ICallServiceAdapter;
33import com.android.internal.telecomm.ICallServiceProvider;
Sailesh Nepal0e5410a2014-04-04 01:20:58 -070034import com.google.common.base.Preconditions;
Santos Cordon3d3b4052014-05-05 12:05:36 -070035import com.google.common.collect.ImmutableList;
36import com.google.common.collect.Sets;
37
Santos Cordon682fe6b2014-05-20 08:56:39 -070038import java.util.HashMap;
Santos Cordon8f3282c2014-06-01 13:56:02 -070039import java.util.List;
Santos Cordon682fe6b2014-05-20 08:56:39 -070040import java.util.Map;
Santos Cordon3d3b4052014-05-05 12:05:36 -070041import java.util.Set;
Santos Cordon63aeb162014-02-10 09:20:40 -080042
43/**
44 * Wrapper for {@link ICallService}s, handles binding to {@link ICallService} and keeps track of
45 * when the object can safely be unbound. Other classes should not use {@link ICallService} directly
46 * and instead should use this class to invoke methods of {@link ICallService}.
Santos Cordon63aeb162014-02-10 09:20:40 -080047 */
Ben Gilad61925612014-03-11 19:06:36 -070048final class CallServiceWrapper extends ServiceBinder<ICallService> {
Santos Cordon63aeb162014-02-10 09:20:40 -080049
Santos Cordon3d3b4052014-05-05 12:05:36 -070050 private final class Adapter extends ICallServiceAdapter.Stub {
Santos Cordon3d3b4052014-05-05 12:05:36 -070051 private static final int MSG_NOTIFY_INCOMING_CALL = 1;
52 private static final int MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL = 2;
53 private static final int MSG_HANDLE_FAILED_OUTGOING_CALL = 3;
54 private static final int MSG_SET_ACTIVE = 4;
55 private static final int MSG_SET_RINGING = 5;
56 private static final int MSG_SET_DIALING = 6;
57 private static final int MSG_SET_DISCONNECTED = 7;
58 private static final int MSG_SET_ON_HOLD = 8;
Ihab Awad50a57132014-05-28 16:49:38 -070059 private static final int MSG_SET_REQUESTING_RINGBACK = 9;
Evan Charlton352105c2014-06-03 14:10:54 -070060 private static final int MSG_ON_POST_DIAL_WAIT = 10;
Santos Cordon3d3b4052014-05-05 12:05:36 -070061
62 private final Handler mHandler = new Handler() {
63 @Override
64 public void handleMessage(Message msg) {
65 Call call;
66 switch (msg.what) {
Santos Cordon3d3b4052014-05-05 12:05:36 -070067 case MSG_NOTIFY_INCOMING_CALL:
68 CallInfo clientCallInfo = (CallInfo) msg.obj;
69 call = mCallIdMapper.getCall(clientCallInfo.getId());
Santos Cordon682fe6b2014-05-20 08:56:39 -070070 if (call != null && mPendingIncomingCalls.remove(call) &&
71 call.isIncoming()) {
Santos Cordon3d3b4052014-05-05 12:05:36 -070072 CallInfo callInfo = new CallInfo(null, clientCallInfo.getState(),
73 clientCallInfo.getHandle());
74 mIncomingCallsManager.handleSuccessfulIncomingCall(call, callInfo);
75 } else {
76 Log.w(this, "notifyIncomingCall, unknown incoming call: %s, id: %s",
77 call,
78 clientCallInfo.getId());
79 }
80 break;
Santos Cordon682fe6b2014-05-20 08:56:39 -070081 case MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL: {
82 String callId = (String) msg.obj;
83 if (mPendingOutgoingCalls.containsKey(callId)) {
84 mPendingOutgoingCalls.remove(callId).onResult(true);
Santos Cordon3d3b4052014-05-05 12:05:36 -070085 } else {
Santos Cordon682fe6b2014-05-20 08:56:39 -070086 Log.w(this, "handleSuccessfulOutgoingCall, unknown call: %s", callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -070087 }
88 break;
Santos Cordon682fe6b2014-05-20 08:56:39 -070089 }
Santos Cordon3d3b4052014-05-05 12:05:36 -070090 case MSG_HANDLE_FAILED_OUTGOING_CALL: {
91 SomeArgs args = (SomeArgs) msg.obj;
92 try {
Santos Cordon682fe6b2014-05-20 08:56:39 -070093 String callId = (String) args.arg1;
Santos Cordon3d3b4052014-05-05 12:05:36 -070094 String reason = (String) args.arg2;
Santos Cordon682fe6b2014-05-20 08:56:39 -070095 // TODO(santoscordon): Do something with 'reason' or get rid of it.
96
97 if (mPendingOutgoingCalls.containsKey(callId)) {
98 mPendingOutgoingCalls.remove(callId).onResult(false);
99 mCallIdMapper.removeCall(callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700100 } else {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700101 Log.w(this, "handleFailedOutgoingCall, unknown call: %s", callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700102 }
103 } finally {
104 args.recycle();
105 }
106 break;
107 }
108 case MSG_SET_ACTIVE:
109 call = mCallIdMapper.getCall(msg.obj);
110 if (call != null) {
111 mCallsManager.markCallAsActive(call);
112 } else {
113 Log.w(this, "setActive, unknown call id: %s", msg.obj);
114 }
115 break;
116 case MSG_SET_RINGING:
117 call = mCallIdMapper.getCall(msg.obj);
118 if (call != null) {
119 mCallsManager.markCallAsRinging(call);
120 } else {
121 Log.w(this, "setRinging, unknown call id: %s", msg.obj);
122 }
123 break;
124 case MSG_SET_DIALING:
125 call = mCallIdMapper.getCall(msg.obj);
126 if (call != null) {
127 mCallsManager.markCallAsDialing(call);
128 } else {
129 Log.w(this, "setDialing, unknown call id: %s", msg.obj);
130 }
131 break;
132 case MSG_SET_DISCONNECTED: {
133 SomeArgs args = (SomeArgs) msg.obj;
134 try {
135 call = mCallIdMapper.getCall(args.arg1);
136 String disconnectMessage = (String) args.arg2;
137 int disconnectCause = args.argi1;
138 if (call != null) {
139 mCallsManager.markCallAsDisconnected(call, disconnectCause,
140 disconnectMessage);
141 } else {
142 Log.w(this, "setDisconnected, unknown call id: %s", args.arg1);
143 }
144 } finally {
145 args.recycle();
146 }
147 break;
148 }
149 case MSG_SET_ON_HOLD:
150 call = mCallIdMapper.getCall(msg.obj);
151 if (call != null) {
152 mCallsManager.markCallAsOnHold(call);
153 } else {
154 Log.w(this, "setOnHold, unknown call id: %s", msg.obj);
155 }
156 break;
Evan Charlton352105c2014-06-03 14:10:54 -0700157 case MSG_SET_REQUESTING_RINGBACK: {
Ihab Awad50a57132014-05-28 16:49:38 -0700158 SomeArgs args = (SomeArgs) msg.obj;
159 try {
160 call = mCallIdMapper.getCall(args.arg1);
161 boolean ringback = (boolean) args.arg2;
162 if (call != null) {
163 call.setRequestingRingback(ringback);
164 } else {
165 Log.w(this, "setRingback, unknown call id: %s", args.arg1);
166 }
167 } finally {
168 args.recycle();
169 }
170 break;
Evan Charlton352105c2014-06-03 14:10:54 -0700171 }
172 case MSG_ON_POST_DIAL_WAIT:
173 SomeArgs args = (SomeArgs) msg.obj;
174 try {
175 call = mCallIdMapper.getCall(args.arg1);
176 if (call != null) {
177 String remaining = (String) args.arg2;
178 call.onPostDialWait(remaining);
179 } else {
180 Log.w(this, "onPostDialWait, unknown call id: %s", args.arg1);
181 }
182 } finally {
183 args.recycle();
184 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700185 }
186 }
187 };
188
189 /** {@inheritDoc} */
190 @Override
191 public void setIsCompatibleWith(String callId, boolean isCompatible) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700192 Log.wtf(this, "Not expected.");
Santos Cordon3d3b4052014-05-05 12:05:36 -0700193 }
194
195 /** {@inheritDoc} */
196 @Override
197 public void notifyIncomingCall(CallInfo callInfo) {
198 mCallIdMapper.checkValidCallId(callInfo.getId());
199 mHandler.obtainMessage(MSG_NOTIFY_INCOMING_CALL, callInfo).sendToTarget();
200 }
201
202 /** {@inheritDoc} */
203 @Override
204 public void handleSuccessfulOutgoingCall(String callId) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700205 Log.d(this, "handleSuccessfulOutgoingCall %s", callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700206 mCallIdMapper.checkValidCallId(callId);
207 mHandler.obtainMessage(MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL, callId).sendToTarget();
208 }
209
210 /** {@inheritDoc} */
211 @Override
212 public void handleFailedOutgoingCall(String callId, String reason) {
213 mCallIdMapper.checkValidCallId(callId);
Santos Cordon4f1b7b82014-05-25 21:03:04 -0700214 Log.d(this, "handleFailedOutgoingCall %s", callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700215 SomeArgs args = SomeArgs.obtain();
216 args.arg1 = callId;
217 args.arg2 = reason;
218 mHandler.obtainMessage(MSG_HANDLE_FAILED_OUTGOING_CALL, args).sendToTarget();
219 }
220
221 /** {@inheritDoc} */
222 @Override
223 public void setActive(String callId) {
224 mCallIdMapper.checkValidCallId(callId);
225 mHandler.obtainMessage(MSG_SET_ACTIVE, callId).sendToTarget();
226 }
227
228 /** {@inheritDoc} */
229 @Override
230 public void setRinging(String callId) {
231 mCallIdMapper.checkValidCallId(callId);
232 mHandler.obtainMessage(MSG_SET_RINGING, callId).sendToTarget();
233 }
234
235 /** {@inheritDoc} */
236 @Override
237 public void setDialing(String callId) {
238 mCallIdMapper.checkValidCallId(callId);
239 mHandler.obtainMessage(MSG_SET_DIALING, callId).sendToTarget();
240 }
241
242 /** {@inheritDoc} */
243 @Override
244 public void setDisconnected(
245 String callId, int disconnectCause, String disconnectMessage) {
246 mCallIdMapper.checkValidCallId(callId);
247 SomeArgs args = SomeArgs.obtain();
248 args.arg1 = callId;
249 args.arg2 = disconnectMessage;
250 args.argi1 = disconnectCause;
251 mHandler.obtainMessage(MSG_SET_DISCONNECTED, args).sendToTarget();
252 }
253
254 /** {@inheritDoc} */
255 @Override
256 public void setOnHold(String callId) {
257 mCallIdMapper.checkValidCallId(callId);
258 mHandler.obtainMessage(MSG_SET_ON_HOLD, callId).sendToTarget();
259 }
Ihab Awad50a57132014-05-28 16:49:38 -0700260
261 /** {@inheritDoc} */
262 @Override
263 public void setRequestingRingback(String callId, boolean ringback) {
264 mCallIdMapper.checkValidCallId(callId);
265 SomeArgs args = SomeArgs.obtain();
266 args.arg1 = callId;
267 args.arg2 = ringback;
268 mHandler.obtainMessage(MSG_SET_REQUESTING_RINGBACK, args).sendToTarget();
269 }
Santos Cordon8f3282c2014-06-01 13:56:02 -0700270
271 /** ${inheritDoc} */
272 @Override
273 public void removeCall(String callId) {
274 }
275
276 /** ${inheritDoc} */
277 @Override
278 public void setCanConferenceWith(String callId, List<String> conferenceCapableCallIds) {
279 }
280
281 /** ${inheritDoc} */
282 @Override
283 public void setIsConferenced(String conferenceCallId, String callId, boolean isConferenced) {
284 }
Evan Charlton352105c2014-06-03 14:10:54 -0700285
286 @Override
287 public void onPostDialWait(String callId, String remaining) throws RemoteException {
288 mCallIdMapper.checkValidCallId(callId);
289 SomeArgs args = SomeArgs.obtain();
290 args.arg1 = callId;
291 args.arg2 = remaining;
292 mHandler.obtainMessage(MSG_ON_POST_DIAL_WAIT, args).sendToTarget();
293 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700294 }
295
296 private final Adapter mAdapter = new Adapter();
297 private final CallsManager mCallsManager = CallsManager.getInstance();
Santos Cordon682fe6b2014-05-20 08:56:39 -0700298 private final Set<Call> mPendingIncomingCalls = Sets.newHashSet();
Ben Giladc5b22692014-02-18 20:03:22 -0800299 private final CallServiceDescriptor mDescriptor;
Santos Cordon3d3b4052014-05-05 12:05:36 -0700300 private final CallIdMapper mCallIdMapper = new CallIdMapper("CallService");
Santos Cordon3d3b4052014-05-05 12:05:36 -0700301 private final IncomingCallsManager mIncomingCallsManager;
Santos Cordon682fe6b2014-05-20 08:56:39 -0700302 private final Map<String, AsyncResultCallback<Boolean>> mPendingOutgoingCalls = new HashMap<>();
Santos Cordonc195e362014-02-11 17:05:31 -0800303
Ben Gilad61925612014-03-11 19:06:36 -0700304 private Binder mBinder = new Binder();
Santos Cordon3d3b4052014-05-05 12:05:36 -0700305 private ICallService mServiceInterface;
Ben Gilad61925612014-03-11 19:06:36 -0700306
Santos Cordon63aeb162014-02-10 09:20:40 -0800307 /**
Sailesh Nepale59bb192014-04-01 18:33:59 -0700308 * Creates a call-service for the specified descriptor.
Santos Cordonc195e362014-02-11 17:05:31 -0800309 *
Santos Cordon61d0f702014-02-19 02:52:23 -0800310 * @param descriptor The call-service descriptor from
Santos Cordon3d3b4052014-05-05 12:05:36 -0700311 * {@link ICallServiceProvider#lookupCallServices}.
Sailesh Nepale59bb192014-04-01 18:33:59 -0700312 * @param incomingCallsManager Manages the incoming call initialization flow.
Santos Cordon63aeb162014-02-10 09:20:40 -0800313 */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700314 CallServiceWrapper(
315 CallServiceDescriptor descriptor,
Sailesh Nepale59bb192014-04-01 18:33:59 -0700316 IncomingCallsManager incomingCallsManager) {
Sailesh Nepala439e1b2014-03-11 18:19:58 -0700317 super(TelecommConstants.ACTION_CALL_SERVICE, descriptor.getServiceComponent());
Ben Giladc5b22692014-02-18 20:03:22 -0800318 mDescriptor = descriptor;
Santos Cordon3d3b4052014-05-05 12:05:36 -0700319 mIncomingCallsManager = incomingCallsManager;
Santos Cordon63aeb162014-02-10 09:20:40 -0800320 }
321
Ben Gilad61925612014-03-11 19:06:36 -0700322 CallServiceDescriptor getDescriptor() {
Ben Giladc5b22692014-02-18 20:03:22 -0800323 return mDescriptor;
Santos Cordonc195e362014-02-11 17:05:31 -0800324 }
325
Santos Cordon63aeb162014-02-10 09:20:40 -0800326 /** See {@link ICallService#setCallServiceAdapter}. */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700327 private void setCallServiceAdapter(ICallServiceAdapter callServiceAdapter) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800328 if (isServiceValid("setCallServiceAdapter")) {
329 try {
Santos Cordon63aeb162014-02-10 09:20:40 -0800330 mServiceInterface.setCallServiceAdapter(callServiceAdapter);
Santos Cordon61d0f702014-02-19 02:52:23 -0800331 } catch (RemoteException e) {
Santos Cordon63aeb162014-02-10 09:20:40 -0800332 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800333 }
334 }
335
Ben Gilad61925612014-03-11 19:06:36 -0700336 /**
Santos Cordon682fe6b2014-05-20 08:56:39 -0700337 * Attempts to place the specified call, see {@link ICallService#call}. Returns the result
338 * asynchronously through the specified callback.
Ben Gilad61925612014-03-11 19:06:36 -0700339 */
Santos Cordon682fe6b2014-05-20 08:56:39 -0700340 void call(final Call call, final AsyncResultCallback<Boolean> resultCallback) {
341 Log.d(this, "call(%s) via %s.", call, getComponentName());
Ben Gilad61925612014-03-11 19:06:36 -0700342 BindCallback callback = new BindCallback() {
Santos Cordon3d3b4052014-05-05 12:05:36 -0700343 @Override
344 public void onSuccess() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700345 String callId = mCallIdMapper.getCallId(call);
346 mPendingOutgoingCalls.put(callId, resultCallback);
347
348 try {
349 CallInfo callInfo = call.toCallInfo(callId);
350 mServiceInterface.call(callInfo);
351 } catch (RemoteException e) {
352 mPendingOutgoingCalls.remove(callId).onResult(false);
Ben Gilad61925612014-03-11 19:06:36 -0700353 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800354 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700355
356 @Override
357 public void onFailure() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700358 resultCallback.onResult(false);
Ben Gilad61925612014-03-11 19:06:36 -0700359 }
360 };
361
362 mBinder.bind(callback);
Santos Cordon63aeb162014-02-10 09:20:40 -0800363 }
364
Ihab Awad74549ec2014-03-10 15:33:25 -0700365 /** @see CallService#abort(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700366 void abort(Call call) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700367 // Clear out any pending outgoing call data
368 String callId = mCallIdMapper.getCallId(call);
369
370 // If still bound, tell the call service to abort.
Ben Gilad28e8ad62014-03-06 17:01:54 -0800371 if (isServiceValid("abort")) {
372 try {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700373 mServiceInterface.abort(callId);
Ben Gilad28e8ad62014-03-06 17:01:54 -0800374 } catch (RemoteException e) {
Santos Cordon63aeb162014-02-10 09:20:40 -0800375 }
Santos Cordon61d0f702014-02-19 02:52:23 -0800376 }
Santos Cordon682fe6b2014-05-20 08:56:39 -0700377
378 removeCall(call);
Santos Cordon61d0f702014-02-19 02:52:23 -0800379 }
380
Ihab Awad74549ec2014-03-10 15:33:25 -0700381 /** @see CallService#hold(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700382 void hold(Call call) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700383 if (isServiceValid("hold")) {
384 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700385 mServiceInterface.hold(mCallIdMapper.getCallId(call));
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700386 } catch (RemoteException e) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700387 }
388 }
389 }
390
Ihab Awad74549ec2014-03-10 15:33:25 -0700391 /** @see CallService#unhold(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700392 void unhold(Call call) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700393 if (isServiceValid("unhold")) {
394 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700395 mServiceInterface.unhold(mCallIdMapper.getCallId(call));
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700396 } catch (RemoteException e) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700397 }
398 }
399 }
400
Ihab Awad74549ec2014-03-10 15:33:25 -0700401 /** @see CallService#onAudioStateChanged(String,CallAudioState) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700402 void onAudioStateChanged(Call activeCall, CallAudioState audioState) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700403 if (isServiceValid("onAudioStateChanged")) {
404 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700405 mServiceInterface.onAudioStateChanged(mCallIdMapper.getCallId(activeCall),
406 audioState);
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700407 } catch (RemoteException e) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700408 }
409 }
410 }
411
Ben Gilad61925612014-03-11 19:06:36 -0700412 /**
413 * Starts retrieval of details for an incoming call. Details are returned through the
414 * call-service adapter using the specified call ID. Upon failure, the specified error callback
Santos Cordon3d3b4052014-05-05 12:05:36 -0700415 * is invoked. Can be invoked even when the call service is unbound. See
416 * {@link ICallService#setIncomingCallId}.
Ben Gilad61925612014-03-11 19:06:36 -0700417 *
Sailesh Nepale59bb192014-04-01 18:33:59 -0700418 * @param call The call used for the incoming call.
Ben Gilad61925612014-03-11 19:06:36 -0700419 * @param extras The {@link CallService}-provided extras which need to be sent back.
420 * @param errorCallback The callback to invoke upon failure.
421 */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700422 void setIncomingCallId(final Call call, final Bundle extras, final Runnable errorCallback) {
423 Log.d(this, "setIncomingCall(%s) via %s.", call, getComponentName());
Ben Gilad61925612014-03-11 19:06:36 -0700424 BindCallback callback = new BindCallback() {
Santos Cordon3d3b4052014-05-05 12:05:36 -0700425 @Override
426 public void onSuccess() {
Ben Gilad61925612014-03-11 19:06:36 -0700427 if (isServiceValid("setIncomingCallId")) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700428 mPendingIncomingCalls.add(call);
Ben Gilad61925612014-03-11 19:06:36 -0700429 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700430 mServiceInterface.setIncomingCallId(mCallIdMapper.getCallId(call),
431 extras);
Ben Gilad61925612014-03-11 19:06:36 -0700432 } catch (RemoteException e) {
Ben Gilad61925612014-03-11 19:06:36 -0700433 }
434 }
Santos Cordon61d0f702014-02-19 02:52:23 -0800435 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700436
437 @Override
438 public void onFailure() {
Ben Gilad61925612014-03-11 19:06:36 -0700439 errorCallback.run();
440 }
441 };
442
443 mBinder.bind(callback);
Santos Cordon63aeb162014-02-10 09:20:40 -0800444 }
445
Ihab Awad74549ec2014-03-10 15:33:25 -0700446 /** @see CallService#disconnect(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700447 void disconnect(Call call) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800448 if (isServiceValid("disconnect")) {
449 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700450 mServiceInterface.disconnect(mCallIdMapper.getCallId(call));
Santos Cordon61d0f702014-02-19 02:52:23 -0800451 } catch (RemoteException e) {
Santos Cordon63aeb162014-02-10 09:20:40 -0800452 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800453 }
454 }
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800455
Ihab Awad74549ec2014-03-10 15:33:25 -0700456 /** @see CallService#answer(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700457 void answer(Call call) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800458 if (isServiceValid("answer")) {
459 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700460 mServiceInterface.answer(mCallIdMapper.getCallId(call));
Santos Cordon61d0f702014-02-19 02:52:23 -0800461 } catch (RemoteException e) {
Santos Cordon7917d382014-02-14 02:31:18 -0800462 }
Santos Cordon61d0f702014-02-19 02:52:23 -0800463 }
464 }
465
Ihab Awad74549ec2014-03-10 15:33:25 -0700466 /** @see CallService#reject(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700467 void reject(Call call) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800468 if (isServiceValid("reject")) {
469 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700470 mServiceInterface.reject(mCallIdMapper.getCallId(call));
Santos Cordon61d0f702014-02-19 02:52:23 -0800471 } catch (RemoteException e) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700472 }
473 }
474 }
475
476 /** @see CallService#playDtmfTone(String,char) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700477 void playDtmfTone(Call call, char digit) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700478 if (isServiceValid("playDtmfTone")) {
479 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700480 mServiceInterface.playDtmfTone(mCallIdMapper.getCallId(call), digit);
Ihab Awad74549ec2014-03-10 15:33:25 -0700481 } catch (RemoteException e) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700482 }
483 }
484 }
485
486 /** @see CallService#stopDtmfTone(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700487 void stopDtmfTone(Call call) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700488 if (isServiceValid("stopDtmfTone")) {
489 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700490 mServiceInterface.stopDtmfTone(mCallIdMapper.getCallId(call));
Ihab Awad74549ec2014-03-10 15:33:25 -0700491 } catch (RemoteException e) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800492 }
Santos Cordon7917d382014-02-14 02:31:18 -0800493 }
494 }
495
Sailesh Nepale59bb192014-04-01 18:33:59 -0700496 void addCall(Call call) {
497 mCallIdMapper.addCall(call);
Santos Cordon7917d382014-02-14 02:31:18 -0800498 }
499
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700500 /**
501 * Associates newCall with this call service by replacing callToReplace.
502 */
503 void replaceCall(Call newCall, Call callToReplace) {
504 Preconditions.checkState(callToReplace.getCallService() == this);
505 mCallIdMapper.replaceCall(newCall, callToReplace);
506 }
507
Sailesh Nepale59bb192014-04-01 18:33:59 -0700508 void removeCall(Call call) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700509 mPendingIncomingCalls.remove(call);
510
511 AsyncResultCallback<Boolean> outgoingResultCallback =
512 mPendingOutgoingCalls.remove(mCallIdMapper.getCallId(call));
513 if (outgoingResultCallback != null) {
514 outgoingResultCallback.onResult(false);
515 }
516
Sailesh Nepale59bb192014-04-01 18:33:59 -0700517 mCallIdMapper.removeCall(call);
Yorke Leeadee12d2014-03-13 12:08:30 -0700518 }
519
Evan Charlton352105c2014-06-03 14:10:54 -0700520 void onPostDialContinue(Call call, boolean proceed) {
521 if (isServiceValid("onPostDialContinue")) {
522 try {
523 mServiceInterface.onPostDialContinue(mCallIdMapper.getCallId(call), proceed);
524 } catch (RemoteException ignored) {
525 }
526 }
527 }
528
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800529 /** {@inheritDoc} */
Santos Cordon3d3b4052014-05-05 12:05:36 -0700530 @Override
531 protected void setServiceInterface(IBinder binder) {
Santos Cordon4b2c1192014-03-19 18:15:38 -0700532 if (binder == null) {
533 // We have lost our service connection. Notify the world that this call service is done.
534 // We must notify the adapter before CallsManager. The adapter will force any pending
535 // outgoing calls to try the next call service. This needs to happen before CallsManager
536 // tries to clean up any calls still associated with this call service.
Santos Cordon3d3b4052014-05-05 12:05:36 -0700537 handleCallServiceDeath();
Santos Cordon4b2c1192014-03-19 18:15:38 -0700538 CallsManager.getInstance().handleCallServiceDeath(this);
539 mServiceInterface = null;
540 } else {
541 mServiceInterface = ICallService.Stub.asInterface(binder);
542 setCallServiceAdapter(mAdapter);
543 }
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800544 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700545
546 /**
547 * Called when the associated call service dies.
548 */
549 private void handleCallServiceDeath() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700550 if (!mPendingOutgoingCalls.isEmpty()) {
551 for (AsyncResultCallback<Boolean> callback : mPendingOutgoingCalls.values()) {
552 callback.onResult(false);
553 }
554 mPendingOutgoingCalls.clear();
555 }
556
557 if (!mPendingIncomingCalls.isEmpty()) {
Santos Cordon3d3b4052014-05-05 12:05:36 -0700558 // Iterate through a copy because the code inside the loop will modify the original
559 // list.
Santos Cordon682fe6b2014-05-20 08:56:39 -0700560 for (Call call : ImmutableList.copyOf(mPendingIncomingCalls)) {
561 Preconditions.checkState(call.isIncoming());
562 mIncomingCallsManager.handleFailedIncomingCall(call);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700563 }
564
Santos Cordon8f3282c2014-06-01 13:56:02 -0700565 if (!mPendingIncomingCalls.isEmpty()) {
566 Log.wtf(this, "Pending calls did not get cleared.");
567 mPendingIncomingCalls.clear();
568 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700569 }
Santos Cordon8f3282c2014-06-01 13:56:02 -0700570
571 mCallIdMapper.clear();
572 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800573}