blob: d09c52ae72544c7a739159aa930636e1407a7c14 [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;
38import com.google.common.collect.Sets;
39
Ihab Awada3cb9e32014-06-03 18:45:05 -070040import org.apache.http.conn.ClientConnectionRequest;
41
Santos Cordon682fe6b2014-05-20 08:56:39 -070042import java.util.HashMap;
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 Cordon63aeb162014-02-10 09:20:40 -080053
Santos Cordon3d3b4052014-05-05 12:05:36 -070054 private final class Adapter extends ICallServiceAdapter.Stub {
Santos Cordon3d3b4052014-05-05 12:05:36 -070055 private static final int MSG_NOTIFY_INCOMING_CALL = 1;
56 private static final int MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL = 2;
57 private static final int MSG_HANDLE_FAILED_OUTGOING_CALL = 3;
58 private static final int MSG_SET_ACTIVE = 4;
59 private static final int MSG_SET_RINGING = 5;
60 private static final int MSG_SET_DIALING = 6;
61 private static final int MSG_SET_DISCONNECTED = 7;
62 private static final int MSG_SET_ON_HOLD = 8;
Ihab Awad50a57132014-05-28 16:49:38 -070063 private static final int MSG_SET_REQUESTING_RINGBACK = 9;
Santos Cordon3d3b4052014-05-05 12:05:36 -070064
65 private final Handler mHandler = new Handler() {
66 @Override
67 public void handleMessage(Message msg) {
68 Call call;
69 switch (msg.what) {
Santos Cordon3d3b4052014-05-05 12:05:36 -070070 case MSG_NOTIFY_INCOMING_CALL:
71 CallInfo clientCallInfo = (CallInfo) msg.obj;
72 call = mCallIdMapper.getCall(clientCallInfo.getId());
Santos Cordon682fe6b2014-05-20 08:56:39 -070073 if (call != null && mPendingIncomingCalls.remove(call) &&
74 call.isIncoming()) {
Santos Cordon3d3b4052014-05-05 12:05:36 -070075 CallInfo callInfo = new CallInfo(null, clientCallInfo.getState(),
76 clientCallInfo.getHandle());
77 mIncomingCallsManager.handleSuccessfulIncomingCall(call, callInfo);
78 } else {
79 Log.w(this, "notifyIncomingCall, unknown incoming call: %s, id: %s",
80 call,
81 clientCallInfo.getId());
82 }
83 break;
Santos Cordon682fe6b2014-05-20 08:56:39 -070084 case MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL: {
85 String callId = (String) msg.obj;
86 if (mPendingOutgoingCalls.containsKey(callId)) {
Ihab Awada3cb9e32014-06-03 18:45:05 -070087 mPendingOutgoingCalls.remove(callId).onResult(true, 0, null);
Santos Cordon3d3b4052014-05-05 12:05:36 -070088 } else {
Santos Cordon682fe6b2014-05-20 08:56:39 -070089 Log.w(this, "handleSuccessfulOutgoingCall, unknown call: %s", callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -070090 }
91 break;
Santos Cordon682fe6b2014-05-20 08:56:39 -070092 }
Santos Cordon3d3b4052014-05-05 12:05:36 -070093 case MSG_HANDLE_FAILED_OUTGOING_CALL: {
94 SomeArgs args = (SomeArgs) msg.obj;
95 try {
Santos Cordon682fe6b2014-05-20 08:56:39 -070096 String callId = (String) args.arg1;
Ihab Awada3cb9e32014-06-03 18:45:05 -070097 int statusCode = args.argi1;
98 String statusMsg = (String) args.arg2;
Santos Cordon682fe6b2014-05-20 08:56:39 -070099 // TODO(santoscordon): Do something with 'reason' or get rid of it.
100
101 if (mPendingOutgoingCalls.containsKey(callId)) {
Ihab Awada3cb9e32014-06-03 18:45:05 -0700102 mPendingOutgoingCalls.remove(callId).onResult(
103 false, statusCode, statusMsg);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700104 mCallIdMapper.removeCall(callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700105 } else {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700106 Log.w(this, "handleFailedOutgoingCall, unknown call: %s", callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700107 }
108 } finally {
109 args.recycle();
110 }
111 break;
112 }
113 case MSG_SET_ACTIVE:
114 call = mCallIdMapper.getCall(msg.obj);
115 if (call != null) {
116 mCallsManager.markCallAsActive(call);
117 } else {
118 Log.w(this, "setActive, unknown call id: %s", msg.obj);
119 }
120 break;
121 case MSG_SET_RINGING:
122 call = mCallIdMapper.getCall(msg.obj);
123 if (call != null) {
124 mCallsManager.markCallAsRinging(call);
125 } else {
126 Log.w(this, "setRinging, unknown call id: %s", msg.obj);
127 }
128 break;
129 case MSG_SET_DIALING:
130 call = mCallIdMapper.getCall(msg.obj);
131 if (call != null) {
132 mCallsManager.markCallAsDialing(call);
133 } else {
134 Log.w(this, "setDialing, unknown call id: %s", msg.obj);
135 }
136 break;
137 case MSG_SET_DISCONNECTED: {
138 SomeArgs args = (SomeArgs) msg.obj;
139 try {
140 call = mCallIdMapper.getCall(args.arg1);
141 String disconnectMessage = (String) args.arg2;
142 int disconnectCause = args.argi1;
143 if (call != null) {
144 mCallsManager.markCallAsDisconnected(call, disconnectCause,
145 disconnectMessage);
146 } else {
147 Log.w(this, "setDisconnected, unknown call id: %s", args.arg1);
148 }
149 } finally {
150 args.recycle();
151 }
152 break;
153 }
154 case MSG_SET_ON_HOLD:
155 call = mCallIdMapper.getCall(msg.obj);
156 if (call != null) {
157 mCallsManager.markCallAsOnHold(call);
158 } else {
159 Log.w(this, "setOnHold, unknown call id: %s", msg.obj);
160 }
161 break;
Ihab Awad50a57132014-05-28 16:49:38 -0700162 case MSG_SET_REQUESTING_RINGBACK:
163 SomeArgs args = (SomeArgs) msg.obj;
164 try {
165 call = mCallIdMapper.getCall(args.arg1);
166 boolean ringback = (boolean) args.arg2;
167 if (call != null) {
168 call.setRequestingRingback(ringback);
169 } else {
170 Log.w(this, "setRingback, unknown call id: %s", args.arg1);
171 }
172 } finally {
173 args.recycle();
174 }
175 break;
Santos Cordon3d3b4052014-05-05 12:05:36 -0700176 }
177 }
178 };
179
180 /** {@inheritDoc} */
181 @Override
182 public void setIsCompatibleWith(String callId, boolean isCompatible) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700183 Log.wtf(this, "Not expected.");
Santos Cordon3d3b4052014-05-05 12:05:36 -0700184 }
185
186 /** {@inheritDoc} */
187 @Override
188 public void notifyIncomingCall(CallInfo callInfo) {
189 mCallIdMapper.checkValidCallId(callInfo.getId());
190 mHandler.obtainMessage(MSG_NOTIFY_INCOMING_CALL, callInfo).sendToTarget();
191 }
192
193 /** {@inheritDoc} */
194 @Override
195 public void handleSuccessfulOutgoingCall(String callId) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700196 Log.d(this, "handleSuccessfulOutgoingCall %s", callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700197 mCallIdMapper.checkValidCallId(callId);
198 mHandler.obtainMessage(MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL, callId).sendToTarget();
199 }
200
201 /** {@inheritDoc} */
202 @Override
Ihab Awada3cb9e32014-06-03 18:45:05 -0700203 public void handleFailedOutgoingCall(
204 ConnectionRequest request,
205 int errorCode,
206 String errorMsg) {
207 mCallIdMapper.checkValidCallId(request.getCallId());
208 Log.d(this, "handleFailedOutgoingCall %s", request.getCallId());
Santos Cordon3d3b4052014-05-05 12:05:36 -0700209 SomeArgs args = SomeArgs.obtain();
Ihab Awada3cb9e32014-06-03 18:45:05 -0700210 args.arg1 = request.getCallId();
211 args.argi1 = errorCode;
212 args.arg2 = errorMsg;
Santos Cordon3d3b4052014-05-05 12:05:36 -0700213 mHandler.obtainMessage(MSG_HANDLE_FAILED_OUTGOING_CALL, args).sendToTarget();
214 }
215
216 /** {@inheritDoc} */
217 @Override
218 public void setActive(String callId) {
219 mCallIdMapper.checkValidCallId(callId);
220 mHandler.obtainMessage(MSG_SET_ACTIVE, callId).sendToTarget();
221 }
222
223 /** {@inheritDoc} */
224 @Override
225 public void setRinging(String callId) {
226 mCallIdMapper.checkValidCallId(callId);
227 mHandler.obtainMessage(MSG_SET_RINGING, callId).sendToTarget();
228 }
229
230 /** {@inheritDoc} */
231 @Override
232 public void setDialing(String callId) {
233 mCallIdMapper.checkValidCallId(callId);
234 mHandler.obtainMessage(MSG_SET_DIALING, callId).sendToTarget();
235 }
236
237 /** {@inheritDoc} */
238 @Override
239 public void setDisconnected(
240 String callId, int disconnectCause, String disconnectMessage) {
241 mCallIdMapper.checkValidCallId(callId);
242 SomeArgs args = SomeArgs.obtain();
243 args.arg1 = callId;
244 args.arg2 = disconnectMessage;
245 args.argi1 = disconnectCause;
246 mHandler.obtainMessage(MSG_SET_DISCONNECTED, args).sendToTarget();
247 }
248
249 /** {@inheritDoc} */
250 @Override
251 public void setOnHold(String callId) {
252 mCallIdMapper.checkValidCallId(callId);
253 mHandler.obtainMessage(MSG_SET_ON_HOLD, callId).sendToTarget();
254 }
Ihab Awad50a57132014-05-28 16:49:38 -0700255
256 /** {@inheritDoc} */
257 @Override
258 public void setRequestingRingback(String callId, boolean ringback) {
259 mCallIdMapper.checkValidCallId(callId);
260 SomeArgs args = SomeArgs.obtain();
261 args.arg1 = callId;
262 args.arg2 = ringback;
263 mHandler.obtainMessage(MSG_SET_REQUESTING_RINGBACK, args).sendToTarget();
264 }
Santos Cordon8f3282c2014-06-01 13:56:02 -0700265
266 /** ${inheritDoc} */
267 @Override
268 public void removeCall(String callId) {
269 }
270
271 /** ${inheritDoc} */
272 @Override
273 public void setCanConferenceWith(String callId, List<String> conferenceCapableCallIds) {
274 }
275
276 /** ${inheritDoc} */
277 @Override
278 public void setIsConferenced(String conferenceCallId, String callId, boolean isConferenced) {
279 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700280 }
281
282 private final Adapter mAdapter = new Adapter();
283 private final CallsManager mCallsManager = CallsManager.getInstance();
Santos Cordon682fe6b2014-05-20 08:56:39 -0700284 private final Set<Call> mPendingIncomingCalls = Sets.newHashSet();
Ben Giladc5b22692014-02-18 20:03:22 -0800285 private final CallServiceDescriptor mDescriptor;
Santos Cordon3d3b4052014-05-05 12:05:36 -0700286 private final CallIdMapper mCallIdMapper = new CallIdMapper("CallService");
Santos Cordon3d3b4052014-05-05 12:05:36 -0700287 private final IncomingCallsManager mIncomingCallsManager;
Santos Cordon682fe6b2014-05-20 08:56:39 -0700288 private final Map<String, AsyncResultCallback<Boolean>> mPendingOutgoingCalls = new HashMap<>();
Santos Cordonc195e362014-02-11 17:05:31 -0800289
Ben Gilad61925612014-03-11 19:06:36 -0700290 private Binder mBinder = new Binder();
Santos Cordon3d3b4052014-05-05 12:05:36 -0700291 private ICallService mServiceInterface;
Ben Gilad61925612014-03-11 19:06:36 -0700292
Santos Cordon63aeb162014-02-10 09:20:40 -0800293 /**
Sailesh Nepale59bb192014-04-01 18:33:59 -0700294 * Creates a call-service for the specified descriptor.
Santos Cordonc195e362014-02-11 17:05:31 -0800295 *
Santos Cordon61d0f702014-02-19 02:52:23 -0800296 * @param descriptor The call-service descriptor from
Santos Cordon3d3b4052014-05-05 12:05:36 -0700297 * {@link ICallServiceProvider#lookupCallServices}.
Sailesh Nepale59bb192014-04-01 18:33:59 -0700298 * @param incomingCallsManager Manages the incoming call initialization flow.
Santos Cordon63aeb162014-02-10 09:20:40 -0800299 */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700300 CallServiceWrapper(
301 CallServiceDescriptor descriptor,
Sailesh Nepale59bb192014-04-01 18:33:59 -0700302 IncomingCallsManager incomingCallsManager) {
Sailesh Nepala439e1b2014-03-11 18:19:58 -0700303 super(TelecommConstants.ACTION_CALL_SERVICE, descriptor.getServiceComponent());
Ben Giladc5b22692014-02-18 20:03:22 -0800304 mDescriptor = descriptor;
Santos Cordon3d3b4052014-05-05 12:05:36 -0700305 mIncomingCallsManager = incomingCallsManager;
Santos Cordon63aeb162014-02-10 09:20:40 -0800306 }
307
Ben Gilad61925612014-03-11 19:06:36 -0700308 CallServiceDescriptor getDescriptor() {
Ben Giladc5b22692014-02-18 20:03:22 -0800309 return mDescriptor;
Santos Cordonc195e362014-02-11 17:05:31 -0800310 }
311
Santos Cordon63aeb162014-02-10 09:20:40 -0800312 /** See {@link ICallService#setCallServiceAdapter}. */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700313 private void setCallServiceAdapter(ICallServiceAdapter callServiceAdapter) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800314 if (isServiceValid("setCallServiceAdapter")) {
315 try {
Santos Cordon63aeb162014-02-10 09:20:40 -0800316 mServiceInterface.setCallServiceAdapter(callServiceAdapter);
Santos Cordon61d0f702014-02-19 02:52:23 -0800317 } catch (RemoteException e) {
Santos Cordon63aeb162014-02-10 09:20:40 -0800318 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800319 }
320 }
321
Ben Gilad61925612014-03-11 19:06:36 -0700322 /**
Santos Cordon682fe6b2014-05-20 08:56:39 -0700323 * Attempts to place the specified call, see {@link ICallService#call}. Returns the result
324 * asynchronously through the specified callback.
Ben Gilad61925612014-03-11 19:06:36 -0700325 */
Santos Cordon682fe6b2014-05-20 08:56:39 -0700326 void call(final Call call, final AsyncResultCallback<Boolean> resultCallback) {
327 Log.d(this, "call(%s) via %s.", call, getComponentName());
Ben Gilad61925612014-03-11 19:06:36 -0700328 BindCallback callback = new BindCallback() {
Santos Cordon3d3b4052014-05-05 12:05:36 -0700329 @Override
330 public void onSuccess() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700331 String callId = mCallIdMapper.getCallId(call);
332 mPendingOutgoingCalls.put(callId, resultCallback);
333
334 try {
335 CallInfo callInfo = call.toCallInfo(callId);
336 mServiceInterface.call(callInfo);
337 } catch (RemoteException e) {
Ihab Awada3cb9e32014-06-03 18:45:05 -0700338 mPendingOutgoingCalls.remove(callId).onResult(
339 false, DisconnectCause.ERROR_UNSPECIFIED, e.toString());
Ben Gilad61925612014-03-11 19:06:36 -0700340 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800341 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700342
343 @Override
344 public void onFailure() {
Ihab Awada3cb9e32014-06-03 18:45:05 -0700345 resultCallback.onResult(false, DisconnectCause.ERROR_UNSPECIFIED, null);
Ben Gilad61925612014-03-11 19:06:36 -0700346 }
347 };
348
349 mBinder.bind(callback);
Santos Cordon63aeb162014-02-10 09:20:40 -0800350 }
351
Ihab Awad74549ec2014-03-10 15:33:25 -0700352 /** @see CallService#abort(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700353 void abort(Call call) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700354 // Clear out any pending outgoing call data
355 String callId = mCallIdMapper.getCallId(call);
356
357 // If still bound, tell the call service to abort.
Ben Gilad28e8ad62014-03-06 17:01:54 -0800358 if (isServiceValid("abort")) {
359 try {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700360 mServiceInterface.abort(callId);
Ben Gilad28e8ad62014-03-06 17:01:54 -0800361 } catch (RemoteException e) {
Santos Cordon63aeb162014-02-10 09:20:40 -0800362 }
Santos Cordon61d0f702014-02-19 02:52:23 -0800363 }
Santos Cordon682fe6b2014-05-20 08:56:39 -0700364
365 removeCall(call);
Santos Cordon61d0f702014-02-19 02:52:23 -0800366 }
367
Ihab Awad74549ec2014-03-10 15:33:25 -0700368 /** @see CallService#hold(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700369 void hold(Call call) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700370 if (isServiceValid("hold")) {
371 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700372 mServiceInterface.hold(mCallIdMapper.getCallId(call));
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700373 } catch (RemoteException e) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700374 }
375 }
376 }
377
Ihab Awad74549ec2014-03-10 15:33:25 -0700378 /** @see CallService#unhold(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700379 void unhold(Call call) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700380 if (isServiceValid("unhold")) {
381 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700382 mServiceInterface.unhold(mCallIdMapper.getCallId(call));
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700383 } catch (RemoteException e) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700384 }
385 }
386 }
387
Ihab Awad74549ec2014-03-10 15:33:25 -0700388 /** @see CallService#onAudioStateChanged(String,CallAudioState) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700389 void onAudioStateChanged(Call activeCall, CallAudioState audioState) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700390 if (isServiceValid("onAudioStateChanged")) {
391 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700392 mServiceInterface.onAudioStateChanged(mCallIdMapper.getCallId(activeCall),
393 audioState);
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700394 } catch (RemoteException e) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700395 }
396 }
397 }
398
Ben Gilad61925612014-03-11 19:06:36 -0700399 /**
400 * Starts retrieval of details for an incoming call. Details are returned through the
401 * call-service adapter using the specified call ID. Upon failure, the specified error callback
Santos Cordon3d3b4052014-05-05 12:05:36 -0700402 * is invoked. Can be invoked even when the call service is unbound. See
403 * {@link ICallService#setIncomingCallId}.
Ben Gilad61925612014-03-11 19:06:36 -0700404 *
Sailesh Nepale59bb192014-04-01 18:33:59 -0700405 * @param call The call used for the incoming call.
Ben Gilad61925612014-03-11 19:06:36 -0700406 * @param extras The {@link CallService}-provided extras which need to be sent back.
407 * @param errorCallback The callback to invoke upon failure.
408 */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700409 void setIncomingCallId(final Call call, final Bundle extras, final Runnable errorCallback) {
410 Log.d(this, "setIncomingCall(%s) via %s.", call, getComponentName());
Ben Gilad61925612014-03-11 19:06:36 -0700411 BindCallback callback = new BindCallback() {
Santos Cordon3d3b4052014-05-05 12:05:36 -0700412 @Override
413 public void onSuccess() {
Ben Gilad61925612014-03-11 19:06:36 -0700414 if (isServiceValid("setIncomingCallId")) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700415 mPendingIncomingCalls.add(call);
Ben Gilad61925612014-03-11 19:06:36 -0700416 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700417 mServiceInterface.setIncomingCallId(mCallIdMapper.getCallId(call),
418 extras);
Ben Gilad61925612014-03-11 19:06:36 -0700419 } catch (RemoteException e) {
Ben Gilad61925612014-03-11 19:06:36 -0700420 }
421 }
Santos Cordon61d0f702014-02-19 02:52:23 -0800422 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700423
424 @Override
425 public void onFailure() {
Ben Gilad61925612014-03-11 19:06:36 -0700426 errorCallback.run();
427 }
428 };
429
430 mBinder.bind(callback);
Santos Cordon63aeb162014-02-10 09:20:40 -0800431 }
432
Ihab Awad74549ec2014-03-10 15:33:25 -0700433 /** @see CallService#disconnect(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700434 void disconnect(Call call) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800435 if (isServiceValid("disconnect")) {
436 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700437 mServiceInterface.disconnect(mCallIdMapper.getCallId(call));
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 }
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800442
Ihab Awad74549ec2014-03-10 15:33:25 -0700443 /** @see CallService#answer(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700444 void answer(Call call) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800445 if (isServiceValid("answer")) {
446 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700447 mServiceInterface.answer(mCallIdMapper.getCallId(call));
Santos Cordon61d0f702014-02-19 02:52:23 -0800448 } catch (RemoteException e) {
Santos Cordon7917d382014-02-14 02:31:18 -0800449 }
Santos Cordon61d0f702014-02-19 02:52:23 -0800450 }
451 }
452
Ihab Awad74549ec2014-03-10 15:33:25 -0700453 /** @see CallService#reject(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700454 void reject(Call call) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800455 if (isServiceValid("reject")) {
456 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700457 mServiceInterface.reject(mCallIdMapper.getCallId(call));
Santos Cordon61d0f702014-02-19 02:52:23 -0800458 } catch (RemoteException e) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700459 }
460 }
461 }
462
463 /** @see CallService#playDtmfTone(String,char) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700464 void playDtmfTone(Call call, char digit) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700465 if (isServiceValid("playDtmfTone")) {
466 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700467 mServiceInterface.playDtmfTone(mCallIdMapper.getCallId(call), digit);
Ihab Awad74549ec2014-03-10 15:33:25 -0700468 } catch (RemoteException e) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700469 }
470 }
471 }
472
473 /** @see CallService#stopDtmfTone(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700474 void stopDtmfTone(Call call) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700475 if (isServiceValid("stopDtmfTone")) {
476 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700477 mServiceInterface.stopDtmfTone(mCallIdMapper.getCallId(call));
Ihab Awad74549ec2014-03-10 15:33:25 -0700478 } catch (RemoteException e) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800479 }
Santos Cordon7917d382014-02-14 02:31:18 -0800480 }
481 }
482
Sailesh Nepale59bb192014-04-01 18:33:59 -0700483 void addCall(Call call) {
484 mCallIdMapper.addCall(call);
Santos Cordon7917d382014-02-14 02:31:18 -0800485 }
486
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700487 /**
488 * Associates newCall with this call service by replacing callToReplace.
489 */
490 void replaceCall(Call newCall, Call callToReplace) {
491 Preconditions.checkState(callToReplace.getCallService() == this);
492 mCallIdMapper.replaceCall(newCall, callToReplace);
493 }
494
Sailesh Nepale59bb192014-04-01 18:33:59 -0700495 void removeCall(Call call) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700496 mPendingIncomingCalls.remove(call);
497
498 AsyncResultCallback<Boolean> outgoingResultCallback =
499 mPendingOutgoingCalls.remove(mCallIdMapper.getCallId(call));
500 if (outgoingResultCallback != null) {
Ihab Awada3cb9e32014-06-03 18:45:05 -0700501 outgoingResultCallback.onResult(false, DisconnectCause.ERROR_UNSPECIFIED, null);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700502 }
503
Sailesh Nepale59bb192014-04-01 18:33:59 -0700504 mCallIdMapper.removeCall(call);
Yorke Leeadee12d2014-03-13 12:08:30 -0700505 }
506
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800507 /** {@inheritDoc} */
Santos Cordon3d3b4052014-05-05 12:05:36 -0700508 @Override
509 protected void setServiceInterface(IBinder binder) {
Santos Cordon4b2c1192014-03-19 18:15:38 -0700510 if (binder == null) {
511 // We have lost our service connection. Notify the world that this call service is done.
512 // We must notify the adapter before CallsManager. The adapter will force any pending
513 // outgoing calls to try the next call service. This needs to happen before CallsManager
514 // tries to clean up any calls still associated with this call service.
Santos Cordon3d3b4052014-05-05 12:05:36 -0700515 handleCallServiceDeath();
Santos Cordon4b2c1192014-03-19 18:15:38 -0700516 CallsManager.getInstance().handleCallServiceDeath(this);
517 mServiceInterface = null;
518 } else {
519 mServiceInterface = ICallService.Stub.asInterface(binder);
520 setCallServiceAdapter(mAdapter);
521 }
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800522 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700523
524 /**
525 * Called when the associated call service dies.
526 */
527 private void handleCallServiceDeath() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700528 if (!mPendingOutgoingCalls.isEmpty()) {
529 for (AsyncResultCallback<Boolean> callback : mPendingOutgoingCalls.values()) {
Ihab Awada3cb9e32014-06-03 18:45:05 -0700530 callback.onResult(false, DisconnectCause.ERROR_UNSPECIFIED, null);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700531 }
532 mPendingOutgoingCalls.clear();
533 }
534
535 if (!mPendingIncomingCalls.isEmpty()) {
Santos Cordon3d3b4052014-05-05 12:05:36 -0700536 // Iterate through a copy because the code inside the loop will modify the original
537 // list.
Santos Cordon682fe6b2014-05-20 08:56:39 -0700538 for (Call call : ImmutableList.copyOf(mPendingIncomingCalls)) {
539 Preconditions.checkState(call.isIncoming());
540 mIncomingCallsManager.handleFailedIncomingCall(call);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700541 }
542
Santos Cordon8f3282c2014-06-01 13:56:02 -0700543 if (!mPendingIncomingCalls.isEmpty()) {
544 Log.wtf(this, "Pending calls did not get cleared.");
545 mPendingIncomingCalls.clear();
546 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700547 }
Santos Cordon8f3282c2014-06-01 13:56:02 -0700548
549 mCallIdMapper.clear();
550 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800551}