blob: 4462cff0c6b044c693ddcc6dc64fb02d57644601 [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;
Ihab Awada3cb9e32014-06-03 18:45:05 -070028import android.telecomm.ConnectionRequest;
Sailesh Nepal83cfe7c2014-03-11 19:54:22 -070029import android.telecomm.TelecommConstants;
Ihab Awada3cb9e32014-06-03 18:45:05 -070030import android.telephony.DisconnectCause;
Sailesh Nepala439e1b2014-03-11 18:19:58 -070031
Santos Cordon3d3b4052014-05-05 12:05:36 -070032import com.android.internal.os.SomeArgs;
Sailesh Nepala439e1b2014-03-11 18:19:58 -070033import com.android.internal.telecomm.ICallService;
34import com.android.internal.telecomm.ICallServiceAdapter;
35import com.android.internal.telecomm.ICallServiceProvider;
Sailesh Nepal0e5410a2014-04-04 01:20:58 -070036import com.google.common.base.Preconditions;
Santos Cordon3d3b4052014-05-05 12:05:36 -070037import com.google.common.collect.ImmutableList;
Santos Cordon3d3b4052014-05-05 12:05:36 -070038
Ihab Awada3cb9e32014-06-03 18:45:05 -070039import org.apache.http.conn.ClientConnectionRequest;
40
Santos Cordon682fe6b2014-05-20 08:56:39 -070041import java.util.HashMap;
Santos Cordona1610702014-06-04 20:22:56 -070042import java.util.HashSet;
Santos Cordon8f3282c2014-06-01 13:56:02 -070043import java.util.List;
Santos Cordon682fe6b2014-05-20 08:56:39 -070044import java.util.Map;
Santos Cordon3d3b4052014-05-05 12:05:36 -070045import java.util.Set;
Santos Cordon63aeb162014-02-10 09:20:40 -080046
47/**
48 * Wrapper for {@link ICallService}s, handles binding to {@link ICallService} and keeps track of
49 * when the object can safely be unbound. Other classes should not use {@link ICallService} directly
50 * and instead should use this class to invoke methods of {@link ICallService}.
Santos Cordon63aeb162014-02-10 09:20:40 -080051 */
Ben Gilad61925612014-03-11 19:06:36 -070052final class CallServiceWrapper extends ServiceBinder<ICallService> {
Santos Cordona1610702014-06-04 20:22:56 -070053 private static final String TAG = CallServiceWrapper.class.getSimpleName();
Santos Cordon63aeb162014-02-10 09:20:40 -080054
Santos Cordon3d3b4052014-05-05 12:05:36 -070055 private final class Adapter extends ICallServiceAdapter.Stub {
Santos Cordon3d3b4052014-05-05 12:05:36 -070056 private static final int MSG_NOTIFY_INCOMING_CALL = 1;
57 private static final int MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL = 2;
58 private static final int MSG_HANDLE_FAILED_OUTGOING_CALL = 3;
59 private static final int MSG_SET_ACTIVE = 4;
60 private static final int MSG_SET_RINGING = 5;
61 private static final int MSG_SET_DIALING = 6;
62 private static final int MSG_SET_DISCONNECTED = 7;
63 private static final int MSG_SET_ON_HOLD = 8;
Ihab Awad50a57132014-05-28 16:49:38 -070064 private static final int MSG_SET_REQUESTING_RINGBACK = 9;
Evan Charlton352105c2014-06-03 14:10:54 -070065 private static final int MSG_ON_POST_DIAL_WAIT = 10;
Santos Cordona1610702014-06-04 20:22:56 -070066 private static final int MSG_CAN_CONFERENCE = 11;
67 private static final int MSG_SET_IS_CONFERENCED = 12;
68 private static final int MSG_ADD_CONFERENCE_CALL = 13;
69 private static final int MSG_HANDOFF_CALL = 14;
Santos Cordon3d3b4052014-05-05 12:05:36 -070070
71 private final Handler mHandler = new Handler() {
72 @Override
73 public void handleMessage(Message msg) {
74 Call call;
75 switch (msg.what) {
Santos Cordon3d3b4052014-05-05 12:05:36 -070076 case MSG_NOTIFY_INCOMING_CALL:
77 CallInfo clientCallInfo = (CallInfo) msg.obj;
78 call = mCallIdMapper.getCall(clientCallInfo.getId());
Santos Cordon682fe6b2014-05-20 08:56:39 -070079 if (call != null && mPendingIncomingCalls.remove(call) &&
80 call.isIncoming()) {
Santos Cordon3d3b4052014-05-05 12:05:36 -070081 CallInfo callInfo = new CallInfo(null, clientCallInfo.getState(),
82 clientCallInfo.getHandle());
83 mIncomingCallsManager.handleSuccessfulIncomingCall(call, callInfo);
84 } else {
85 Log.w(this, "notifyIncomingCall, unknown incoming call: %s, id: %s",
Santos Cordona1610702014-06-04 20:22:56 -070086 call, clientCallInfo.getId());
Santos Cordon3d3b4052014-05-05 12:05:36 -070087 }
88 break;
Santos Cordon682fe6b2014-05-20 08:56:39 -070089 case MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL: {
90 String callId = (String) msg.obj;
91 if (mPendingOutgoingCalls.containsKey(callId)) {
Ihab Awada3cb9e32014-06-03 18:45:05 -070092 mPendingOutgoingCalls.remove(callId).onResult(true, 0, null);
Santos Cordon3d3b4052014-05-05 12:05:36 -070093 } else {
Santos Cordon682fe6b2014-05-20 08:56:39 -070094 Log.w(this, "handleSuccessfulOutgoingCall, unknown call: %s", callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -070095 }
96 break;
Santos Cordon682fe6b2014-05-20 08:56:39 -070097 }
Santos Cordon3d3b4052014-05-05 12:05:36 -070098 case MSG_HANDLE_FAILED_OUTGOING_CALL: {
99 SomeArgs args = (SomeArgs) msg.obj;
100 try {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700101 String callId = (String) args.arg1;
Ihab Awada3cb9e32014-06-03 18:45:05 -0700102 int statusCode = args.argi1;
103 String statusMsg = (String) args.arg2;
Santos Cordon682fe6b2014-05-20 08:56:39 -0700104 // TODO(santoscordon): Do something with 'reason' or get rid of it.
105
106 if (mPendingOutgoingCalls.containsKey(callId)) {
Ihab Awada3cb9e32014-06-03 18:45:05 -0700107 mPendingOutgoingCalls.remove(callId).onResult(
108 false, statusCode, statusMsg);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700109 mCallIdMapper.removeCall(callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700110 } else {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700111 Log.w(this, "handleFailedOutgoingCall, unknown call: %s", callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700112 }
113 } finally {
114 args.recycle();
115 }
116 break;
117 }
118 case MSG_SET_ACTIVE:
119 call = mCallIdMapper.getCall(msg.obj);
120 if (call != null) {
121 mCallsManager.markCallAsActive(call);
122 } else {
123 Log.w(this, "setActive, unknown call id: %s", msg.obj);
124 }
125 break;
126 case MSG_SET_RINGING:
127 call = mCallIdMapper.getCall(msg.obj);
128 if (call != null) {
129 mCallsManager.markCallAsRinging(call);
130 } else {
131 Log.w(this, "setRinging, unknown call id: %s", msg.obj);
132 }
133 break;
134 case MSG_SET_DIALING:
135 call = mCallIdMapper.getCall(msg.obj);
136 if (call != null) {
137 mCallsManager.markCallAsDialing(call);
138 } else {
139 Log.w(this, "setDialing, unknown call id: %s", msg.obj);
140 }
141 break;
142 case MSG_SET_DISCONNECTED: {
143 SomeArgs args = (SomeArgs) msg.obj;
144 try {
145 call = mCallIdMapper.getCall(args.arg1);
146 String disconnectMessage = (String) args.arg2;
147 int disconnectCause = args.argi1;
148 if (call != null) {
149 mCallsManager.markCallAsDisconnected(call, disconnectCause,
150 disconnectMessage);
151 } else {
152 Log.w(this, "setDisconnected, unknown call id: %s", args.arg1);
153 }
154 } finally {
155 args.recycle();
156 }
157 break;
158 }
159 case MSG_SET_ON_HOLD:
160 call = mCallIdMapper.getCall(msg.obj);
161 if (call != null) {
162 mCallsManager.markCallAsOnHold(call);
163 } else {
164 Log.w(this, "setOnHold, unknown call id: %s", msg.obj);
165 }
166 break;
Evan Charlton352105c2014-06-03 14:10:54 -0700167 case MSG_SET_REQUESTING_RINGBACK: {
Ihab Awad50a57132014-05-28 16:49:38 -0700168 SomeArgs args = (SomeArgs) msg.obj;
169 try {
170 call = mCallIdMapper.getCall(args.arg1);
171 boolean ringback = (boolean) args.arg2;
172 if (call != null) {
173 call.setRequestingRingback(ringback);
174 } else {
175 Log.w(this, "setRingback, unknown call id: %s", args.arg1);
176 }
177 } finally {
178 args.recycle();
179 }
180 break;
Evan Charlton352105c2014-06-03 14:10:54 -0700181 }
Santos Cordona1610702014-06-04 20:22:56 -0700182 case MSG_ON_POST_DIAL_WAIT: {
Evan Charlton352105c2014-06-03 14:10:54 -0700183 SomeArgs args = (SomeArgs) msg.obj;
184 try {
185 call = mCallIdMapper.getCall(args.arg1);
186 if (call != null) {
187 String remaining = (String) args.arg2;
188 call.onPostDialWait(remaining);
189 } else {
190 Log.w(this, "onPostDialWait, unknown call id: %s", args.arg1);
191 }
192 } finally {
193 args.recycle();
194 }
Santos Cordona1610702014-06-04 20:22:56 -0700195 break;
196 }
Sailesh Nepal6098d2c2014-06-06 10:56:53 -0700197 case MSG_HANDOFF_CALL:
198 call = mCallIdMapper.getCall(msg.obj);
199 if (call != null) {
200 mCallsManager.startHandoffForCall(call);
201 } else {
202 Log.w(this, "handoffCall, unknown call id: %s", msg.obj);
203 }
204 break;
Santos Cordona1610702014-06-04 20:22:56 -0700205 case MSG_CAN_CONFERENCE: {
206 call = mCallIdMapper.getCall(msg.obj);
207 if (call != null) {
208 call.setIsConferenceCapable(msg.arg1 == 1);
209 } else {
210 Log.w(CallServiceWrapper.this, "canConference, unknown call id: %s",
211 msg.obj);
212 }
213 break;
214 }
215 case MSG_SET_IS_CONFERENCED: {
216 SomeArgs args = (SomeArgs) msg.obj;
217 try {
218 Call childCall = mCallIdMapper.getCall(args.arg1);
219 if (childCall != null) {
220 String conferenceCallId = (String) args.arg2;
221 Log.d(this, "setIsConferenced %s, %s", childCall, conferenceCallId);
222
223 if (conferenceCallId == null) {
224 childCall.setParentCall(null);
225 } else {
226 Call conferenceCall = mCallIdMapper.getCall(conferenceCallId);
227 if (conferenceCall != null &&
228 !mPendingConferenceCalls.contains(conferenceCall)) {
229 childCall.setParentCall(conferenceCall);
230 } else {
231 Log.w(this, "setIsConferenced, unknown conference id %s",
232 conferenceCallId);
233 }
234 }
235 } else {
236 Log.w(this, "setIsConferenced, unknown call id: %s", args.arg1);
237 }
238 } finally {
239 args.recycle();
240 }
241 break;
242 }
243 case MSG_ADD_CONFERENCE_CALL: {
244 SomeArgs args = (SomeArgs) msg.obj;
245 try {
246 String callId = (String) args.arg1;
247 Log.d(this, "addConferenceCall attempt %s, %s",
248 callId, mPendingConferenceCalls);
249
250 Call conferenceCall = mCallIdMapper.getCall(callId);
251 if (mPendingConferenceCalls.remove(conferenceCall)) {
252 Log.v(this, "confirming conf call %s", conferenceCall);
253 conferenceCall.confirmConference();
254 } else {
255 Log.w(this, "addConference, unknown call id: %s", callId);
256 }
257 } finally {
258 args.recycle();
259 }
260 break;
261 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700262 }
263 }
264 };
265
266 /** {@inheritDoc} */
267 @Override
268 public void setIsCompatibleWith(String callId, boolean isCompatible) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700269 Log.wtf(this, "Not expected.");
Santos Cordon3d3b4052014-05-05 12:05:36 -0700270 }
271
272 /** {@inheritDoc} */
273 @Override
274 public void notifyIncomingCall(CallInfo callInfo) {
275 mCallIdMapper.checkValidCallId(callInfo.getId());
276 mHandler.obtainMessage(MSG_NOTIFY_INCOMING_CALL, callInfo).sendToTarget();
277 }
278
279 /** {@inheritDoc} */
280 @Override
281 public void handleSuccessfulOutgoingCall(String callId) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700282 Log.d(this, "handleSuccessfulOutgoingCall %s", callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700283 mCallIdMapper.checkValidCallId(callId);
284 mHandler.obtainMessage(MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL, callId).sendToTarget();
285 }
286
287 /** {@inheritDoc} */
288 @Override
Ihab Awada3cb9e32014-06-03 18:45:05 -0700289 public void handleFailedOutgoingCall(
290 ConnectionRequest request,
291 int errorCode,
292 String errorMsg) {
293 mCallIdMapper.checkValidCallId(request.getCallId());
294 Log.d(this, "handleFailedOutgoingCall %s", request.getCallId());
Santos Cordon3d3b4052014-05-05 12:05:36 -0700295 SomeArgs args = SomeArgs.obtain();
Ihab Awada3cb9e32014-06-03 18:45:05 -0700296 args.arg1 = request.getCallId();
297 args.argi1 = errorCode;
298 args.arg2 = errorMsg;
Santos Cordon3d3b4052014-05-05 12:05:36 -0700299 mHandler.obtainMessage(MSG_HANDLE_FAILED_OUTGOING_CALL, args).sendToTarget();
300 }
301
302 /** {@inheritDoc} */
303 @Override
304 public void setActive(String callId) {
305 mCallIdMapper.checkValidCallId(callId);
306 mHandler.obtainMessage(MSG_SET_ACTIVE, callId).sendToTarget();
307 }
308
309 /** {@inheritDoc} */
310 @Override
311 public void setRinging(String callId) {
312 mCallIdMapper.checkValidCallId(callId);
313 mHandler.obtainMessage(MSG_SET_RINGING, callId).sendToTarget();
314 }
315
316 /** {@inheritDoc} */
317 @Override
318 public void setDialing(String callId) {
319 mCallIdMapper.checkValidCallId(callId);
320 mHandler.obtainMessage(MSG_SET_DIALING, callId).sendToTarget();
321 }
322
323 /** {@inheritDoc} */
324 @Override
325 public void setDisconnected(
326 String callId, int disconnectCause, String disconnectMessage) {
327 mCallIdMapper.checkValidCallId(callId);
328 SomeArgs args = SomeArgs.obtain();
329 args.arg1 = callId;
330 args.arg2 = disconnectMessage;
331 args.argi1 = disconnectCause;
332 mHandler.obtainMessage(MSG_SET_DISCONNECTED, args).sendToTarget();
333 }
334
335 /** {@inheritDoc} */
336 @Override
337 public void setOnHold(String callId) {
338 mCallIdMapper.checkValidCallId(callId);
339 mHandler.obtainMessage(MSG_SET_ON_HOLD, callId).sendToTarget();
340 }
Ihab Awad50a57132014-05-28 16:49:38 -0700341
342 /** {@inheritDoc} */
343 @Override
344 public void setRequestingRingback(String callId, boolean ringback) {
345 mCallIdMapper.checkValidCallId(callId);
346 SomeArgs args = SomeArgs.obtain();
347 args.arg1 = callId;
348 args.arg2 = ringback;
349 mHandler.obtainMessage(MSG_SET_REQUESTING_RINGBACK, args).sendToTarget();
350 }
Santos Cordon8f3282c2014-06-01 13:56:02 -0700351
352 /** ${inheritDoc} */
353 @Override
354 public void removeCall(String callId) {
355 }
356
357 /** ${inheritDoc} */
358 @Override
Santos Cordona1610702014-06-04 20:22:56 -0700359 public void setCanConference(String callId, boolean canConference) {
360 Log.d(this, "setCanConference(%s, %b)", callId, canConference);
361 mHandler.obtainMessage(MSG_CAN_CONFERENCE, canConference ? 1 : 0, 0, callId)
362 .sendToTarget();
Santos Cordon8f3282c2014-06-01 13:56:02 -0700363 }
364
365 /** ${inheritDoc} */
366 @Override
Santos Cordona1610702014-06-04 20:22:56 -0700367 public void setIsConferenced(String callId, String conferenceCallId) {
368 SomeArgs args = SomeArgs.obtain();
369 args.arg1 = callId;
370 args.arg2 = conferenceCallId;
371 mHandler.obtainMessage(MSG_SET_IS_CONFERENCED, args).sendToTarget();
372 }
373
374 /** ${InheritDoc} */
375 @Override
376 public void addConferenceCall(String callId, CallInfo callInfo) {
377 mCallIdMapper.checkValidCallId(callId);
378 SomeArgs args = SomeArgs.obtain();
379 args.arg1 = callId;
380 args.arg2 = callInfo;
381 mHandler.obtainMessage(MSG_ADD_CONFERENCE_CALL, args).sendToTarget();
Santos Cordon8f3282c2014-06-01 13:56:02 -0700382 }
Evan Charlton352105c2014-06-03 14:10:54 -0700383
384 @Override
385 public void onPostDialWait(String callId, String remaining) throws RemoteException {
386 mCallIdMapper.checkValidCallId(callId);
387 SomeArgs args = SomeArgs.obtain();
388 args.arg1 = callId;
389 args.arg2 = remaining;
390 mHandler.obtainMessage(MSG_ON_POST_DIAL_WAIT, args).sendToTarget();
391 }
Sailesh Nepal6098d2c2014-06-06 10:56:53 -0700392
393 /** {@inheritDoc} */
394 @Override
395 public void handoffCall(String callId) {
396 mCallIdMapper.checkValidCallId(callId);
397 mHandler.obtainMessage(MSG_HANDOFF_CALL, callId).sendToTarget();
398 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700399 }
400
401 private final Adapter mAdapter = new Adapter();
402 private final CallsManager mCallsManager = CallsManager.getInstance();
Santos Cordona1610702014-06-04 20:22:56 -0700403 private final Set<Call> mPendingIncomingCalls = new HashSet<>();
404 private final Set<Call> mPendingConferenceCalls = new HashSet<>();
Ben Giladc5b22692014-02-18 20:03:22 -0800405 private final CallServiceDescriptor mDescriptor;
Santos Cordon3d3b4052014-05-05 12:05:36 -0700406 private final CallIdMapper mCallIdMapper = new CallIdMapper("CallService");
Santos Cordon3d3b4052014-05-05 12:05:36 -0700407 private final IncomingCallsManager mIncomingCallsManager;
Santos Cordon682fe6b2014-05-20 08:56:39 -0700408 private final Map<String, AsyncResultCallback<Boolean>> mPendingOutgoingCalls = new HashMap<>();
Santos Cordona1610702014-06-04 20:22:56 -0700409 private final Handler mHandler = new Handler();
Santos Cordonc195e362014-02-11 17:05:31 -0800410
Ben Gilad61925612014-03-11 19:06:36 -0700411 private Binder mBinder = new Binder();
Santos Cordon3d3b4052014-05-05 12:05:36 -0700412 private ICallService mServiceInterface;
Ben Gilad61925612014-03-11 19:06:36 -0700413
Santos Cordon63aeb162014-02-10 09:20:40 -0800414 /**
Sailesh Nepale59bb192014-04-01 18:33:59 -0700415 * Creates a call-service for the specified descriptor.
Santos Cordonc195e362014-02-11 17:05:31 -0800416 *
Santos Cordon61d0f702014-02-19 02:52:23 -0800417 * @param descriptor The call-service descriptor from
Santos Cordon3d3b4052014-05-05 12:05:36 -0700418 * {@link ICallServiceProvider#lookupCallServices}.
Sailesh Nepale59bb192014-04-01 18:33:59 -0700419 * @param incomingCallsManager Manages the incoming call initialization flow.
Santos Cordon63aeb162014-02-10 09:20:40 -0800420 */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700421 CallServiceWrapper(
422 CallServiceDescriptor descriptor,
Sailesh Nepale59bb192014-04-01 18:33:59 -0700423 IncomingCallsManager incomingCallsManager) {
Sailesh Nepala439e1b2014-03-11 18:19:58 -0700424 super(TelecommConstants.ACTION_CALL_SERVICE, descriptor.getServiceComponent());
Ben Giladc5b22692014-02-18 20:03:22 -0800425 mDescriptor = descriptor;
Santos Cordon3d3b4052014-05-05 12:05:36 -0700426 mIncomingCallsManager = incomingCallsManager;
Santos Cordon63aeb162014-02-10 09:20:40 -0800427 }
428
Ben Gilad61925612014-03-11 19:06:36 -0700429 CallServiceDescriptor getDescriptor() {
Ben Giladc5b22692014-02-18 20:03:22 -0800430 return mDescriptor;
Santos Cordonc195e362014-02-11 17:05:31 -0800431 }
432
Santos Cordon63aeb162014-02-10 09:20:40 -0800433 /** See {@link ICallService#setCallServiceAdapter}. */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700434 private void setCallServiceAdapter(ICallServiceAdapter callServiceAdapter) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800435 if (isServiceValid("setCallServiceAdapter")) {
436 try {
Santos Cordon63aeb162014-02-10 09:20:40 -0800437 mServiceInterface.setCallServiceAdapter(callServiceAdapter);
Santos Cordon61d0f702014-02-19 02:52:23 -0800438 } catch (RemoteException e) {
Santos Cordon63aeb162014-02-10 09:20:40 -0800439 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800440 }
441 }
442
Ben Gilad61925612014-03-11 19:06:36 -0700443 /**
Santos Cordon682fe6b2014-05-20 08:56:39 -0700444 * Attempts to place the specified call, see {@link ICallService#call}. Returns the result
445 * asynchronously through the specified callback.
Ben Gilad61925612014-03-11 19:06:36 -0700446 */
Santos Cordon682fe6b2014-05-20 08:56:39 -0700447 void call(final Call call, final AsyncResultCallback<Boolean> resultCallback) {
448 Log.d(this, "call(%s) via %s.", call, getComponentName());
Ben Gilad61925612014-03-11 19:06:36 -0700449 BindCallback callback = new BindCallback() {
Santos Cordon3d3b4052014-05-05 12:05:36 -0700450 @Override
451 public void onSuccess() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700452 String callId = mCallIdMapper.getCallId(call);
453 mPendingOutgoingCalls.put(callId, resultCallback);
454
455 try {
456 CallInfo callInfo = call.toCallInfo(callId);
457 mServiceInterface.call(callInfo);
458 } catch (RemoteException e) {
Ihab Awada3cb9e32014-06-03 18:45:05 -0700459 mPendingOutgoingCalls.remove(callId).onResult(
460 false, DisconnectCause.ERROR_UNSPECIFIED, e.toString());
Ben Gilad61925612014-03-11 19:06:36 -0700461 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800462 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700463
464 @Override
465 public void onFailure() {
Ihab Awada3cb9e32014-06-03 18:45:05 -0700466 resultCallback.onResult(false, DisconnectCause.ERROR_UNSPECIFIED, null);
Ben Gilad61925612014-03-11 19:06:36 -0700467 }
468 };
469
470 mBinder.bind(callback);
Santos Cordon63aeb162014-02-10 09:20:40 -0800471 }
472
Ihab Awad74549ec2014-03-10 15:33:25 -0700473 /** @see CallService#abort(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700474 void abort(Call call) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700475 // Clear out any pending outgoing call data
476 String callId = mCallIdMapper.getCallId(call);
477
478 // If still bound, tell the call service to abort.
Ben Gilad28e8ad62014-03-06 17:01:54 -0800479 if (isServiceValid("abort")) {
480 try {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700481 mServiceInterface.abort(callId);
Ben Gilad28e8ad62014-03-06 17:01:54 -0800482 } catch (RemoteException e) {
Santos Cordon63aeb162014-02-10 09:20:40 -0800483 }
Santos Cordon61d0f702014-02-19 02:52:23 -0800484 }
Santos Cordon682fe6b2014-05-20 08:56:39 -0700485
486 removeCall(call);
Santos Cordon61d0f702014-02-19 02:52:23 -0800487 }
488
Ihab Awad74549ec2014-03-10 15:33:25 -0700489 /** @see CallService#hold(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700490 void hold(Call call) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700491 if (isServiceValid("hold")) {
492 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700493 mServiceInterface.hold(mCallIdMapper.getCallId(call));
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700494 } catch (RemoteException e) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700495 }
496 }
497 }
498
Ihab Awad74549ec2014-03-10 15:33:25 -0700499 /** @see CallService#unhold(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700500 void unhold(Call call) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700501 if (isServiceValid("unhold")) {
502 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700503 mServiceInterface.unhold(mCallIdMapper.getCallId(call));
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700504 } catch (RemoteException e) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700505 }
506 }
507 }
508
Ihab Awad74549ec2014-03-10 15:33:25 -0700509 /** @see CallService#onAudioStateChanged(String,CallAudioState) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700510 void onAudioStateChanged(Call activeCall, CallAudioState audioState) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700511 if (isServiceValid("onAudioStateChanged")) {
512 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700513 mServiceInterface.onAudioStateChanged(mCallIdMapper.getCallId(activeCall),
514 audioState);
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700515 } catch (RemoteException e) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700516 }
517 }
518 }
519
Ben Gilad61925612014-03-11 19:06:36 -0700520 /**
521 * Starts retrieval of details for an incoming call. Details are returned through the
522 * call-service adapter using the specified call ID. Upon failure, the specified error callback
Santos Cordon3d3b4052014-05-05 12:05:36 -0700523 * is invoked. Can be invoked even when the call service is unbound. See
524 * {@link ICallService#setIncomingCallId}.
Ben Gilad61925612014-03-11 19:06:36 -0700525 *
Sailesh Nepale59bb192014-04-01 18:33:59 -0700526 * @param call The call used for the incoming call.
Ben Gilad61925612014-03-11 19:06:36 -0700527 * @param extras The {@link CallService}-provided extras which need to be sent back.
528 * @param errorCallback The callback to invoke upon failure.
529 */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700530 void setIncomingCallId(final Call call, final Bundle extras, final Runnable errorCallback) {
531 Log.d(this, "setIncomingCall(%s) via %s.", call, getComponentName());
Ben Gilad61925612014-03-11 19:06:36 -0700532 BindCallback callback = new BindCallback() {
Santos Cordon3d3b4052014-05-05 12:05:36 -0700533 @Override
534 public void onSuccess() {
Ben Gilad61925612014-03-11 19:06:36 -0700535 if (isServiceValid("setIncomingCallId")) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700536 mPendingIncomingCalls.add(call);
Ben Gilad61925612014-03-11 19:06:36 -0700537 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700538 mServiceInterface.setIncomingCallId(mCallIdMapper.getCallId(call),
539 extras);
Ben Gilad61925612014-03-11 19:06:36 -0700540 } catch (RemoteException e) {
Ben Gilad61925612014-03-11 19:06:36 -0700541 }
542 }
Santos Cordon61d0f702014-02-19 02:52:23 -0800543 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700544
545 @Override
546 public void onFailure() {
Ben Gilad61925612014-03-11 19:06:36 -0700547 errorCallback.run();
548 }
549 };
550
551 mBinder.bind(callback);
Santos Cordon63aeb162014-02-10 09:20:40 -0800552 }
553
Ihab Awad74549ec2014-03-10 15:33:25 -0700554 /** @see CallService#disconnect(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700555 void disconnect(Call call) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800556 if (isServiceValid("disconnect")) {
557 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700558 mServiceInterface.disconnect(mCallIdMapper.getCallId(call));
Santos Cordon61d0f702014-02-19 02:52:23 -0800559 } catch (RemoteException e) {
Santos Cordon63aeb162014-02-10 09:20:40 -0800560 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800561 }
562 }
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800563
Ihab Awad74549ec2014-03-10 15:33:25 -0700564 /** @see CallService#answer(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700565 void answer(Call call) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800566 if (isServiceValid("answer")) {
567 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700568 mServiceInterface.answer(mCallIdMapper.getCallId(call));
Santos Cordon61d0f702014-02-19 02:52:23 -0800569 } catch (RemoteException e) {
Santos Cordon7917d382014-02-14 02:31:18 -0800570 }
Santos Cordon61d0f702014-02-19 02:52:23 -0800571 }
572 }
573
Ihab Awad74549ec2014-03-10 15:33:25 -0700574 /** @see CallService#reject(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700575 void reject(Call call) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800576 if (isServiceValid("reject")) {
577 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700578 mServiceInterface.reject(mCallIdMapper.getCallId(call));
Santos Cordon61d0f702014-02-19 02:52:23 -0800579 } catch (RemoteException e) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700580 }
581 }
582 }
583
584 /** @see CallService#playDtmfTone(String,char) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700585 void playDtmfTone(Call call, char digit) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700586 if (isServiceValid("playDtmfTone")) {
587 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700588 mServiceInterface.playDtmfTone(mCallIdMapper.getCallId(call), digit);
Ihab Awad74549ec2014-03-10 15:33:25 -0700589 } catch (RemoteException e) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700590 }
591 }
592 }
593
594 /** @see CallService#stopDtmfTone(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700595 void stopDtmfTone(Call call) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700596 if (isServiceValid("stopDtmfTone")) {
597 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700598 mServiceInterface.stopDtmfTone(mCallIdMapper.getCallId(call));
Ihab Awad74549ec2014-03-10 15:33:25 -0700599 } catch (RemoteException e) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800600 }
Santos Cordon7917d382014-02-14 02:31:18 -0800601 }
602 }
603
Sailesh Nepale59bb192014-04-01 18:33:59 -0700604 void addCall(Call call) {
Santos Cordona1610702014-06-04 20:22:56 -0700605 if (mCallIdMapper.getCallId(call) == null) {
606 mCallIdMapper.addCall(call);
607 }
Santos Cordon7917d382014-02-14 02:31:18 -0800608 }
609
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700610 /**
611 * Associates newCall with this call service by replacing callToReplace.
612 */
613 void replaceCall(Call newCall, Call callToReplace) {
614 Preconditions.checkState(callToReplace.getCallService() == this);
615 mCallIdMapper.replaceCall(newCall, callToReplace);
616 }
617
Sailesh Nepale59bb192014-04-01 18:33:59 -0700618 void removeCall(Call call) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700619 mPendingIncomingCalls.remove(call);
620
621 AsyncResultCallback<Boolean> outgoingResultCallback =
622 mPendingOutgoingCalls.remove(mCallIdMapper.getCallId(call));
623 if (outgoingResultCallback != null) {
Ihab Awada3cb9e32014-06-03 18:45:05 -0700624 outgoingResultCallback.onResult(false, DisconnectCause.ERROR_UNSPECIFIED, null);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700625 }
626
Sailesh Nepale59bb192014-04-01 18:33:59 -0700627 mCallIdMapper.removeCall(call);
Yorke Leeadee12d2014-03-13 12:08:30 -0700628 }
629
Evan Charlton352105c2014-06-03 14:10:54 -0700630 void onPostDialContinue(Call call, boolean proceed) {
631 if (isServiceValid("onPostDialContinue")) {
632 try {
633 mServiceInterface.onPostDialContinue(mCallIdMapper.getCallId(call), proceed);
634 } catch (RemoteException ignored) {
635 }
636 }
637 }
638
Santos Cordona1610702014-06-04 20:22:56 -0700639 void conference(final Call conferenceCall, Call call) {
640 if (isServiceValid("conference")) {
641 try {
642 conferenceCall.setCallService(this);
643 mPendingConferenceCalls.add(conferenceCall);
644 mHandler.postDelayed(new Runnable() {
645 @Override public void run() {
646 if (mPendingConferenceCalls.remove(conferenceCall)) {
647 conferenceCall.expireConference();
648 Log.i(this, "Conference call expired: %s", conferenceCall);
649 }
650 }
651 }, Timeouts.getConferenceCallExpireMillis());
652
653 mServiceInterface.conference(
654 mCallIdMapper.getCallId(conferenceCall),
655 mCallIdMapper.getCallId(call));
656 } catch (RemoteException ignored) {
657 }
658 }
659 }
660
661 void splitFromConference(Call call) {
662 if (isServiceValid("splitFromConference")) {
663 try {
664 mServiceInterface.splitFromConference(mCallIdMapper.getCallId(call));
665 } catch (RemoteException ignored) {
666 }
667 }
668 }
669
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800670 /** {@inheritDoc} */
Santos Cordon3d3b4052014-05-05 12:05:36 -0700671 @Override
672 protected void setServiceInterface(IBinder binder) {
Santos Cordon4b2c1192014-03-19 18:15:38 -0700673 if (binder == null) {
674 // We have lost our service connection. Notify the world that this call service is done.
675 // We must notify the adapter before CallsManager. The adapter will force any pending
676 // outgoing calls to try the next call service. This needs to happen before CallsManager
677 // tries to clean up any calls still associated with this call service.
Santos Cordon3d3b4052014-05-05 12:05:36 -0700678 handleCallServiceDeath();
Santos Cordon4b2c1192014-03-19 18:15:38 -0700679 CallsManager.getInstance().handleCallServiceDeath(this);
680 mServiceInterface = null;
681 } else {
682 mServiceInterface = ICallService.Stub.asInterface(binder);
683 setCallServiceAdapter(mAdapter);
684 }
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800685 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700686
687 /**
688 * Called when the associated call service dies.
689 */
690 private void handleCallServiceDeath() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700691 if (!mPendingOutgoingCalls.isEmpty()) {
692 for (AsyncResultCallback<Boolean> callback : mPendingOutgoingCalls.values()) {
Ihab Awada3cb9e32014-06-03 18:45:05 -0700693 callback.onResult(false, DisconnectCause.ERROR_UNSPECIFIED, null);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700694 }
695 mPendingOutgoingCalls.clear();
696 }
697
698 if (!mPendingIncomingCalls.isEmpty()) {
Santos Cordon3d3b4052014-05-05 12:05:36 -0700699 // Iterate through a copy because the code inside the loop will modify the original
700 // list.
Santos Cordon682fe6b2014-05-20 08:56:39 -0700701 for (Call call : ImmutableList.copyOf(mPendingIncomingCalls)) {
702 Preconditions.checkState(call.isIncoming());
703 mIncomingCallsManager.handleFailedIncomingCall(call);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700704 }
705
Santos Cordona1610702014-06-04 20:22:56 -0700706 if (!mPendingIncomingCalls.isEmpty()) {
707 Log.wtf(this, "Pending calls did not get cleared.");
708 mPendingIncomingCalls.clear();
709 }
710 }
Santos Cordon8f3282c2014-06-01 13:56:02 -0700711
Santos Cordona1610702014-06-04 20:22:56 -0700712 mCallIdMapper.clear();
713 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800714}