blob: 9b5e2c304982f7eda67760f92db29e99e38dd34e [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 -070020
21import android.os.Handler;
Santos Cordon63aeb162014-02-10 09:20:40 -080022import android.os.IBinder;
Santos Cordon3d3b4052014-05-05 12:05:36 -070023import android.os.Message;
Santos Cordon63aeb162014-02-10 09:20:40 -080024import android.os.RemoteException;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070025import android.telecomm.CallAudioState;
Santos Cordon63aeb162014-02-10 09:20:40 -080026import android.telecomm.CallInfo;
Evan Charltona05805b2014-03-05 08:21:46 -080027import android.telecomm.CallService;
Ben Giladc5b22692014-02-18 20:03:22 -080028import android.telecomm.CallServiceDescriptor;
Sailesh Nepal83cfe7c2014-03-11 19:54:22 -070029import android.telecomm.TelecommConstants;
Sailesh Nepala439e1b2014-03-11 18:19:58 -070030
Santos Cordon3d3b4052014-05-05 12:05:36 -070031import com.android.internal.os.SomeArgs;
Sailesh Nepala439e1b2014-03-11 18:19:58 -070032import com.android.internal.telecomm.ICallService;
33import com.android.internal.telecomm.ICallServiceAdapter;
34import com.android.internal.telecomm.ICallServiceProvider;
Sailesh Nepal0e5410a2014-04-04 01:20:58 -070035import com.google.common.base.Preconditions;
Santos Cordon3d3b4052014-05-05 12:05:36 -070036import com.google.common.collect.ImmutableList;
37import com.google.common.collect.Sets;
38
Santos Cordon682fe6b2014-05-20 08:56:39 -070039import java.util.HashMap;
40import 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;
Santos Cordon3d3b4052014-05-05 12:05:36 -070060
61 private final Handler mHandler = new Handler() {
62 @Override
63 public void handleMessage(Message msg) {
64 Call call;
65 switch (msg.what) {
Santos Cordon3d3b4052014-05-05 12:05:36 -070066 case MSG_NOTIFY_INCOMING_CALL:
67 CallInfo clientCallInfo = (CallInfo) msg.obj;
68 call = mCallIdMapper.getCall(clientCallInfo.getId());
Santos Cordon682fe6b2014-05-20 08:56:39 -070069 if (call != null && mPendingIncomingCalls.remove(call) &&
70 call.isIncoming()) {
Santos Cordon3d3b4052014-05-05 12:05:36 -070071 CallInfo callInfo = new CallInfo(null, clientCallInfo.getState(),
72 clientCallInfo.getHandle());
73 mIncomingCallsManager.handleSuccessfulIncomingCall(call, callInfo);
74 } else {
75 Log.w(this, "notifyIncomingCall, unknown incoming call: %s, id: %s",
76 call,
77 clientCallInfo.getId());
78 }
79 break;
Santos Cordon682fe6b2014-05-20 08:56:39 -070080 case MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL: {
81 String callId = (String) msg.obj;
82 if (mPendingOutgoingCalls.containsKey(callId)) {
83 mPendingOutgoingCalls.remove(callId).onResult(true);
Santos Cordon3d3b4052014-05-05 12:05:36 -070084 } else {
Santos Cordon682fe6b2014-05-20 08:56:39 -070085 Log.w(this, "handleSuccessfulOutgoingCall, unknown call: %s", callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -070086 }
87 break;
Santos Cordon682fe6b2014-05-20 08:56:39 -070088 }
Santos Cordon3d3b4052014-05-05 12:05:36 -070089 case MSG_HANDLE_FAILED_OUTGOING_CALL: {
90 SomeArgs args = (SomeArgs) msg.obj;
91 try {
Santos Cordon682fe6b2014-05-20 08:56:39 -070092 String callId = (String) args.arg1;
Santos Cordon3d3b4052014-05-05 12:05:36 -070093 String reason = (String) args.arg2;
Santos Cordon682fe6b2014-05-20 08:56:39 -070094 // TODO(santoscordon): Do something with 'reason' or get rid of it.
95
96 if (mPendingOutgoingCalls.containsKey(callId)) {
97 mPendingOutgoingCalls.remove(callId).onResult(false);
98 mCallIdMapper.removeCall(callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -070099 } else {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700100 Log.w(this, "handleFailedOutgoingCall, unknown call: %s", callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700101 }
102 } finally {
103 args.recycle();
104 }
105 break;
106 }
107 case MSG_SET_ACTIVE:
108 call = mCallIdMapper.getCall(msg.obj);
109 if (call != null) {
110 mCallsManager.markCallAsActive(call);
111 } else {
112 Log.w(this, "setActive, unknown call id: %s", msg.obj);
113 }
114 break;
115 case MSG_SET_RINGING:
116 call = mCallIdMapper.getCall(msg.obj);
117 if (call != null) {
118 mCallsManager.markCallAsRinging(call);
119 } else {
120 Log.w(this, "setRinging, unknown call id: %s", msg.obj);
121 }
122 break;
123 case MSG_SET_DIALING:
124 call = mCallIdMapper.getCall(msg.obj);
125 if (call != null) {
126 mCallsManager.markCallAsDialing(call);
127 } else {
128 Log.w(this, "setDialing, unknown call id: %s", msg.obj);
129 }
130 break;
131 case MSG_SET_DISCONNECTED: {
132 SomeArgs args = (SomeArgs) msg.obj;
133 try {
134 call = mCallIdMapper.getCall(args.arg1);
135 String disconnectMessage = (String) args.arg2;
136 int disconnectCause = args.argi1;
137 if (call != null) {
138 mCallsManager.markCallAsDisconnected(call, disconnectCause,
139 disconnectMessage);
140 } else {
141 Log.w(this, "setDisconnected, unknown call id: %s", args.arg1);
142 }
143 } finally {
144 args.recycle();
145 }
146 break;
147 }
148 case MSG_SET_ON_HOLD:
149 call = mCallIdMapper.getCall(msg.obj);
150 if (call != null) {
151 mCallsManager.markCallAsOnHold(call);
152 } else {
153 Log.w(this, "setOnHold, unknown call id: %s", msg.obj);
154 }
155 break;
Ihab Awad50a57132014-05-28 16:49:38 -0700156 case MSG_SET_REQUESTING_RINGBACK:
157 SomeArgs args = (SomeArgs) msg.obj;
158 try {
159 call = mCallIdMapper.getCall(args.arg1);
160 boolean ringback = (boolean) args.arg2;
161 if (call != null) {
162 call.setRequestingRingback(ringback);
163 } else {
164 Log.w(this, "setRingback, unknown call id: %s", args.arg1);
165 }
166 } finally {
167 args.recycle();
168 }
169 break;
Santos Cordon3d3b4052014-05-05 12:05:36 -0700170 }
171 }
172 };
173
174 /** {@inheritDoc} */
175 @Override
176 public void setIsCompatibleWith(String callId, boolean isCompatible) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700177 Log.wtf(this, "Not expected.");
Santos Cordon3d3b4052014-05-05 12:05:36 -0700178 }
179
180 /** {@inheritDoc} */
181 @Override
182 public void notifyIncomingCall(CallInfo callInfo) {
183 mCallIdMapper.checkValidCallId(callInfo.getId());
184 mHandler.obtainMessage(MSG_NOTIFY_INCOMING_CALL, callInfo).sendToTarget();
185 }
186
187 /** {@inheritDoc} */
188 @Override
189 public void handleSuccessfulOutgoingCall(String callId) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700190 Log.d(this, "handleSuccessfulOutgoingCall %s", callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700191 mCallIdMapper.checkValidCallId(callId);
192 mHandler.obtainMessage(MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL, callId).sendToTarget();
193 }
194
195 /** {@inheritDoc} */
196 @Override
197 public void handleFailedOutgoingCall(String callId, String reason) {
198 mCallIdMapper.checkValidCallId(callId);
Santos Cordon4f1b7b82014-05-25 21:03:04 -0700199 Log.d(this, "handleFailedOutgoingCall %s", callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700200 SomeArgs args = SomeArgs.obtain();
201 args.arg1 = callId;
202 args.arg2 = reason;
203 mHandler.obtainMessage(MSG_HANDLE_FAILED_OUTGOING_CALL, args).sendToTarget();
204 }
205
206 /** {@inheritDoc} */
207 @Override
208 public void setActive(String callId) {
209 mCallIdMapper.checkValidCallId(callId);
210 mHandler.obtainMessage(MSG_SET_ACTIVE, callId).sendToTarget();
211 }
212
213 /** {@inheritDoc} */
214 @Override
215 public void setRinging(String callId) {
216 mCallIdMapper.checkValidCallId(callId);
217 mHandler.obtainMessage(MSG_SET_RINGING, callId).sendToTarget();
218 }
219
220 /** {@inheritDoc} */
221 @Override
222 public void setDialing(String callId) {
223 mCallIdMapper.checkValidCallId(callId);
224 mHandler.obtainMessage(MSG_SET_DIALING, callId).sendToTarget();
225 }
226
227 /** {@inheritDoc} */
228 @Override
229 public void setDisconnected(
230 String callId, int disconnectCause, String disconnectMessage) {
231 mCallIdMapper.checkValidCallId(callId);
232 SomeArgs args = SomeArgs.obtain();
233 args.arg1 = callId;
234 args.arg2 = disconnectMessage;
235 args.argi1 = disconnectCause;
236 mHandler.obtainMessage(MSG_SET_DISCONNECTED, args).sendToTarget();
237 }
238
239 /** {@inheritDoc} */
240 @Override
241 public void setOnHold(String callId) {
242 mCallIdMapper.checkValidCallId(callId);
243 mHandler.obtainMessage(MSG_SET_ON_HOLD, callId).sendToTarget();
244 }
Ihab Awad50a57132014-05-28 16:49:38 -0700245
246 /** {@inheritDoc} */
247 @Override
248 public void setRequestingRingback(String callId, boolean ringback) {
249 mCallIdMapper.checkValidCallId(callId);
250 SomeArgs args = SomeArgs.obtain();
251 args.arg1 = callId;
252 args.arg2 = ringback;
253 mHandler.obtainMessage(MSG_SET_REQUESTING_RINGBACK, args).sendToTarget();
254 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700255 }
256
257 private final Adapter mAdapter = new Adapter();
258 private final CallsManager mCallsManager = CallsManager.getInstance();
Santos Cordon682fe6b2014-05-20 08:56:39 -0700259 private final Set<Call> mPendingIncomingCalls = Sets.newHashSet();
Ben Giladc5b22692014-02-18 20:03:22 -0800260 private final CallServiceDescriptor mDescriptor;
Santos Cordon3d3b4052014-05-05 12:05:36 -0700261 private final CallIdMapper mCallIdMapper = new CallIdMapper("CallService");
Santos Cordon3d3b4052014-05-05 12:05:36 -0700262 private final IncomingCallsManager mIncomingCallsManager;
Santos Cordon682fe6b2014-05-20 08:56:39 -0700263 private final Map<String, AsyncResultCallback<Boolean>> mPendingOutgoingCalls = new HashMap<>();
Santos Cordonc195e362014-02-11 17:05:31 -0800264
Ben Gilad61925612014-03-11 19:06:36 -0700265 private Binder mBinder = new Binder();
Santos Cordon3d3b4052014-05-05 12:05:36 -0700266 private ICallService mServiceInterface;
Ben Gilad61925612014-03-11 19:06:36 -0700267
Santos Cordon63aeb162014-02-10 09:20:40 -0800268 /**
Sailesh Nepale59bb192014-04-01 18:33:59 -0700269 * Creates a call-service for the specified descriptor.
Santos Cordonc195e362014-02-11 17:05:31 -0800270 *
Santos Cordon61d0f702014-02-19 02:52:23 -0800271 * @param descriptor The call-service descriptor from
Santos Cordon3d3b4052014-05-05 12:05:36 -0700272 * {@link ICallServiceProvider#lookupCallServices}.
Sailesh Nepale59bb192014-04-01 18:33:59 -0700273 * @param incomingCallsManager Manages the incoming call initialization flow.
Santos Cordon63aeb162014-02-10 09:20:40 -0800274 */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700275 CallServiceWrapper(
276 CallServiceDescriptor descriptor,
Sailesh Nepale59bb192014-04-01 18:33:59 -0700277 IncomingCallsManager incomingCallsManager) {
Sailesh Nepala439e1b2014-03-11 18:19:58 -0700278 super(TelecommConstants.ACTION_CALL_SERVICE, descriptor.getServiceComponent());
Ben Giladc5b22692014-02-18 20:03:22 -0800279 mDescriptor = descriptor;
Santos Cordon3d3b4052014-05-05 12:05:36 -0700280 mIncomingCallsManager = incomingCallsManager;
Santos Cordon63aeb162014-02-10 09:20:40 -0800281 }
282
Ben Gilad61925612014-03-11 19:06:36 -0700283 CallServiceDescriptor getDescriptor() {
Ben Giladc5b22692014-02-18 20:03:22 -0800284 return mDescriptor;
Santos Cordonc195e362014-02-11 17:05:31 -0800285 }
286
Santos Cordon63aeb162014-02-10 09:20:40 -0800287 /** See {@link ICallService#setCallServiceAdapter}. */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700288 private void setCallServiceAdapter(ICallServiceAdapter callServiceAdapter) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800289 if (isServiceValid("setCallServiceAdapter")) {
290 try {
Santos Cordon63aeb162014-02-10 09:20:40 -0800291 mServiceInterface.setCallServiceAdapter(callServiceAdapter);
Santos Cordon61d0f702014-02-19 02:52:23 -0800292 } catch (RemoteException e) {
Santos Cordon63aeb162014-02-10 09:20:40 -0800293 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800294 }
295 }
296
Ben Gilad61925612014-03-11 19:06:36 -0700297 /**
Santos Cordon682fe6b2014-05-20 08:56:39 -0700298 * Attempts to place the specified call, see {@link ICallService#call}. Returns the result
299 * asynchronously through the specified callback.
Ben Gilad61925612014-03-11 19:06:36 -0700300 */
Santos Cordon682fe6b2014-05-20 08:56:39 -0700301 void call(final Call call, final AsyncResultCallback<Boolean> resultCallback) {
302 Log.d(this, "call(%s) via %s.", call, getComponentName());
Ben Gilad61925612014-03-11 19:06:36 -0700303 BindCallback callback = new BindCallback() {
Santos Cordon3d3b4052014-05-05 12:05:36 -0700304 @Override
305 public void onSuccess() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700306 String callId = mCallIdMapper.getCallId(call);
307 mPendingOutgoingCalls.put(callId, resultCallback);
308
309 try {
310 CallInfo callInfo = call.toCallInfo(callId);
311 mServiceInterface.call(callInfo);
312 } catch (RemoteException e) {
313 mPendingOutgoingCalls.remove(callId).onResult(false);
Ben Gilad61925612014-03-11 19:06:36 -0700314 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800315 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700316
317 @Override
318 public void onFailure() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700319 resultCallback.onResult(false);
Ben Gilad61925612014-03-11 19:06:36 -0700320 }
321 };
322
323 mBinder.bind(callback);
Santos Cordon63aeb162014-02-10 09:20:40 -0800324 }
325
Ihab Awad74549ec2014-03-10 15:33:25 -0700326 /** @see CallService#abort(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700327 void abort(Call call) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700328 // Clear out any pending outgoing call data
329 String callId = mCallIdMapper.getCallId(call);
330
331 // If still bound, tell the call service to abort.
Ben Gilad28e8ad62014-03-06 17:01:54 -0800332 if (isServiceValid("abort")) {
333 try {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700334 mServiceInterface.abort(callId);
Ben Gilad28e8ad62014-03-06 17:01:54 -0800335 } catch (RemoteException e) {
Santos Cordon63aeb162014-02-10 09:20:40 -0800336 }
Santos Cordon61d0f702014-02-19 02:52:23 -0800337 }
Santos Cordon682fe6b2014-05-20 08:56:39 -0700338
339 removeCall(call);
Santos Cordon61d0f702014-02-19 02:52:23 -0800340 }
341
Ihab Awad74549ec2014-03-10 15:33:25 -0700342 /** @see CallService#hold(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700343 void hold(Call call) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700344 if (isServiceValid("hold")) {
345 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700346 mServiceInterface.hold(mCallIdMapper.getCallId(call));
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700347 } catch (RemoteException e) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700348 }
349 }
350 }
351
Ihab Awad74549ec2014-03-10 15:33:25 -0700352 /** @see CallService#unhold(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700353 void unhold(Call call) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700354 if (isServiceValid("unhold")) {
355 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700356 mServiceInterface.unhold(mCallIdMapper.getCallId(call));
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700357 } catch (RemoteException e) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700358 }
359 }
360 }
361
Ihab Awad74549ec2014-03-10 15:33:25 -0700362 /** @see CallService#onAudioStateChanged(String,CallAudioState) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700363 void onAudioStateChanged(Call activeCall, CallAudioState audioState) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700364 if (isServiceValid("onAudioStateChanged")) {
365 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700366 mServiceInterface.onAudioStateChanged(mCallIdMapper.getCallId(activeCall),
367 audioState);
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700368 } catch (RemoteException e) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700369 }
370 }
371 }
372
Ben Gilad61925612014-03-11 19:06:36 -0700373 /**
374 * Starts retrieval of details for an incoming call. Details are returned through the
375 * call-service adapter using the specified call ID. Upon failure, the specified error callback
Santos Cordon3d3b4052014-05-05 12:05:36 -0700376 * is invoked. Can be invoked even when the call service is unbound. See
377 * {@link ICallService#setIncomingCallId}.
Ben Gilad61925612014-03-11 19:06:36 -0700378 *
Sailesh Nepale59bb192014-04-01 18:33:59 -0700379 * @param call The call used for the incoming call.
Ben Gilad61925612014-03-11 19:06:36 -0700380 * @param extras The {@link CallService}-provided extras which need to be sent back.
381 * @param errorCallback The callback to invoke upon failure.
382 */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700383 void setIncomingCallId(final Call call, final Bundle extras, final Runnable errorCallback) {
384 Log.d(this, "setIncomingCall(%s) via %s.", call, getComponentName());
Ben Gilad61925612014-03-11 19:06:36 -0700385 BindCallback callback = new BindCallback() {
Santos Cordon3d3b4052014-05-05 12:05:36 -0700386 @Override
387 public void onSuccess() {
Ben Gilad61925612014-03-11 19:06:36 -0700388 if (isServiceValid("setIncomingCallId")) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700389 mPendingIncomingCalls.add(call);
Ben Gilad61925612014-03-11 19:06:36 -0700390 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700391 mServiceInterface.setIncomingCallId(mCallIdMapper.getCallId(call),
392 extras);
Ben Gilad61925612014-03-11 19:06:36 -0700393 } catch (RemoteException e) {
Ben Gilad61925612014-03-11 19:06:36 -0700394 }
395 }
Santos Cordon61d0f702014-02-19 02:52:23 -0800396 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700397
398 @Override
399 public void onFailure() {
Ben Gilad61925612014-03-11 19:06:36 -0700400 errorCallback.run();
401 }
402 };
403
404 mBinder.bind(callback);
Santos Cordon63aeb162014-02-10 09:20:40 -0800405 }
406
Ihab Awad74549ec2014-03-10 15:33:25 -0700407 /** @see CallService#disconnect(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700408 void disconnect(Call call) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800409 if (isServiceValid("disconnect")) {
410 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700411 mServiceInterface.disconnect(mCallIdMapper.getCallId(call));
Santos Cordon61d0f702014-02-19 02:52:23 -0800412 } catch (RemoteException e) {
Santos Cordon63aeb162014-02-10 09:20:40 -0800413 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800414 }
415 }
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800416
Ihab Awad74549ec2014-03-10 15:33:25 -0700417 /** @see CallService#answer(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700418 void answer(Call call) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800419 if (isServiceValid("answer")) {
420 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700421 mServiceInterface.answer(mCallIdMapper.getCallId(call));
Santos Cordon61d0f702014-02-19 02:52:23 -0800422 } catch (RemoteException e) {
Santos Cordon7917d382014-02-14 02:31:18 -0800423 }
Santos Cordon61d0f702014-02-19 02:52:23 -0800424 }
425 }
426
Ihab Awad74549ec2014-03-10 15:33:25 -0700427 /** @see CallService#reject(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700428 void reject(Call call) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800429 if (isServiceValid("reject")) {
430 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700431 mServiceInterface.reject(mCallIdMapper.getCallId(call));
Santos Cordon61d0f702014-02-19 02:52:23 -0800432 } catch (RemoteException e) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700433 }
434 }
435 }
436
437 /** @see CallService#playDtmfTone(String,char) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700438 void playDtmfTone(Call call, char digit) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700439 if (isServiceValid("playDtmfTone")) {
440 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700441 mServiceInterface.playDtmfTone(mCallIdMapper.getCallId(call), digit);
Ihab Awad74549ec2014-03-10 15:33:25 -0700442 } catch (RemoteException e) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700443 }
444 }
445 }
446
447 /** @see CallService#stopDtmfTone(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700448 void stopDtmfTone(Call call) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700449 if (isServiceValid("stopDtmfTone")) {
450 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700451 mServiceInterface.stopDtmfTone(mCallIdMapper.getCallId(call));
Ihab Awad74549ec2014-03-10 15:33:25 -0700452 } catch (RemoteException e) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800453 }
Santos Cordon7917d382014-02-14 02:31:18 -0800454 }
455 }
456
Sailesh Nepale59bb192014-04-01 18:33:59 -0700457 void addCall(Call call) {
458 mCallIdMapper.addCall(call);
Santos Cordon7917d382014-02-14 02:31:18 -0800459 }
460
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700461 /**
462 * Associates newCall with this call service by replacing callToReplace.
463 */
464 void replaceCall(Call newCall, Call callToReplace) {
465 Preconditions.checkState(callToReplace.getCallService() == this);
466 mCallIdMapper.replaceCall(newCall, callToReplace);
467 }
468
Sailesh Nepale59bb192014-04-01 18:33:59 -0700469 void removeCall(Call call) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700470 mPendingIncomingCalls.remove(call);
471
472 AsyncResultCallback<Boolean> outgoingResultCallback =
473 mPendingOutgoingCalls.remove(mCallIdMapper.getCallId(call));
474 if (outgoingResultCallback != null) {
475 outgoingResultCallback.onResult(false);
476 }
477
Sailesh Nepale59bb192014-04-01 18:33:59 -0700478 mCallIdMapper.removeCall(call);
Yorke Leeadee12d2014-03-13 12:08:30 -0700479 }
480
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800481 /** {@inheritDoc} */
Santos Cordon3d3b4052014-05-05 12:05:36 -0700482 @Override
483 protected void setServiceInterface(IBinder binder) {
Santos Cordon4b2c1192014-03-19 18:15:38 -0700484 if (binder == null) {
485 // We have lost our service connection. Notify the world that this call service is done.
486 // We must notify the adapter before CallsManager. The adapter will force any pending
487 // outgoing calls to try the next call service. This needs to happen before CallsManager
488 // tries to clean up any calls still associated with this call service.
Santos Cordon3d3b4052014-05-05 12:05:36 -0700489 handleCallServiceDeath();
Santos Cordon4b2c1192014-03-19 18:15:38 -0700490 CallsManager.getInstance().handleCallServiceDeath(this);
491 mServiceInterface = null;
492 } else {
493 mServiceInterface = ICallService.Stub.asInterface(binder);
494 setCallServiceAdapter(mAdapter);
495 }
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800496 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700497
498 /**
499 * Called when the associated call service dies.
500 */
501 private void handleCallServiceDeath() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700502 if (!mPendingOutgoingCalls.isEmpty()) {
503 for (AsyncResultCallback<Boolean> callback : mPendingOutgoingCalls.values()) {
504 callback.onResult(false);
505 }
506 mPendingOutgoingCalls.clear();
507 }
508
509 if (!mPendingIncomingCalls.isEmpty()) {
Santos Cordon3d3b4052014-05-05 12:05:36 -0700510 // Iterate through a copy because the code inside the loop will modify the original
511 // list.
Santos Cordon682fe6b2014-05-20 08:56:39 -0700512 for (Call call : ImmutableList.copyOf(mPendingIncomingCalls)) {
513 Preconditions.checkState(call.isIncoming());
514 mIncomingCallsManager.handleFailedIncomingCall(call);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700515 }
516
Santos Cordon682fe6b2014-05-20 08:56:39 -0700517 if (!mPendingIncomingCalls.isEmpty()) {
Santos Cordon3d3b4052014-05-05 12:05:36 -0700518 Log.wtf(this, "Pending calls did not get cleared.");
Santos Cordon682fe6b2014-05-20 08:56:39 -0700519 mPendingIncomingCalls.clear();
Santos Cordon3d3b4052014-05-05 12:05:36 -0700520 }
521 }
Santos Cordon682fe6b2014-05-20 08:56:39 -0700522
523 mCallIdMapper.clear();
Santos Cordon3d3b4052014-05-05 12:05:36 -0700524 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800525}