blob: 41a80e54d41d23daa4d967ba0eba9ec270ea4407 [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;
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 Cordon8f3282c2014-06-01 13:56:02 -0700255
256 /** ${inheritDoc} */
257 @Override
258 public void removeCall(String callId) {
259 }
260
261 /** ${inheritDoc} */
262 @Override
263 public void setCanConferenceWith(String callId, List<String> conferenceCapableCallIds) {
264 }
265
266 /** ${inheritDoc} */
267 @Override
268 public void setIsConferenced(String conferenceCallId, String callId, boolean isConferenced) {
269 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700270 }
271
272 private final Adapter mAdapter = new Adapter();
273 private final CallsManager mCallsManager = CallsManager.getInstance();
Santos Cordon682fe6b2014-05-20 08:56:39 -0700274 private final Set<Call> mPendingIncomingCalls = Sets.newHashSet();
Ben Giladc5b22692014-02-18 20:03:22 -0800275 private final CallServiceDescriptor mDescriptor;
Santos Cordon3d3b4052014-05-05 12:05:36 -0700276 private final CallIdMapper mCallIdMapper = new CallIdMapper("CallService");
Santos Cordon3d3b4052014-05-05 12:05:36 -0700277 private final IncomingCallsManager mIncomingCallsManager;
Santos Cordon682fe6b2014-05-20 08:56:39 -0700278 private final Map<String, AsyncResultCallback<Boolean>> mPendingOutgoingCalls = new HashMap<>();
Santos Cordonc195e362014-02-11 17:05:31 -0800279
Ben Gilad61925612014-03-11 19:06:36 -0700280 private Binder mBinder = new Binder();
Santos Cordon3d3b4052014-05-05 12:05:36 -0700281 private ICallService mServiceInterface;
Ben Gilad61925612014-03-11 19:06:36 -0700282
Santos Cordon63aeb162014-02-10 09:20:40 -0800283 /**
Sailesh Nepale59bb192014-04-01 18:33:59 -0700284 * Creates a call-service for the specified descriptor.
Santos Cordonc195e362014-02-11 17:05:31 -0800285 *
Santos Cordon61d0f702014-02-19 02:52:23 -0800286 * @param descriptor The call-service descriptor from
Santos Cordon3d3b4052014-05-05 12:05:36 -0700287 * {@link ICallServiceProvider#lookupCallServices}.
Sailesh Nepale59bb192014-04-01 18:33:59 -0700288 * @param incomingCallsManager Manages the incoming call initialization flow.
Santos Cordon63aeb162014-02-10 09:20:40 -0800289 */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700290 CallServiceWrapper(
291 CallServiceDescriptor descriptor,
Sailesh Nepale59bb192014-04-01 18:33:59 -0700292 IncomingCallsManager incomingCallsManager) {
Sailesh Nepala439e1b2014-03-11 18:19:58 -0700293 super(TelecommConstants.ACTION_CALL_SERVICE, descriptor.getServiceComponent());
Ben Giladc5b22692014-02-18 20:03:22 -0800294 mDescriptor = descriptor;
Santos Cordon3d3b4052014-05-05 12:05:36 -0700295 mIncomingCallsManager = incomingCallsManager;
Santos Cordon63aeb162014-02-10 09:20:40 -0800296 }
297
Ben Gilad61925612014-03-11 19:06:36 -0700298 CallServiceDescriptor getDescriptor() {
Ben Giladc5b22692014-02-18 20:03:22 -0800299 return mDescriptor;
Santos Cordonc195e362014-02-11 17:05:31 -0800300 }
301
Santos Cordon63aeb162014-02-10 09:20:40 -0800302 /** See {@link ICallService#setCallServiceAdapter}. */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700303 private void setCallServiceAdapter(ICallServiceAdapter callServiceAdapter) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800304 if (isServiceValid("setCallServiceAdapter")) {
305 try {
Santos Cordon63aeb162014-02-10 09:20:40 -0800306 mServiceInterface.setCallServiceAdapter(callServiceAdapter);
Santos Cordon61d0f702014-02-19 02:52:23 -0800307 } catch (RemoteException e) {
Santos Cordon63aeb162014-02-10 09:20:40 -0800308 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800309 }
310 }
311
Ben Gilad61925612014-03-11 19:06:36 -0700312 /**
Santos Cordon682fe6b2014-05-20 08:56:39 -0700313 * Attempts to place the specified call, see {@link ICallService#call}. Returns the result
314 * asynchronously through the specified callback.
Ben Gilad61925612014-03-11 19:06:36 -0700315 */
Santos Cordon682fe6b2014-05-20 08:56:39 -0700316 void call(final Call call, final AsyncResultCallback<Boolean> resultCallback) {
317 Log.d(this, "call(%s) via %s.", call, getComponentName());
Ben Gilad61925612014-03-11 19:06:36 -0700318 BindCallback callback = new BindCallback() {
Santos Cordon3d3b4052014-05-05 12:05:36 -0700319 @Override
320 public void onSuccess() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700321 String callId = mCallIdMapper.getCallId(call);
322 mPendingOutgoingCalls.put(callId, resultCallback);
323
324 try {
325 CallInfo callInfo = call.toCallInfo(callId);
326 mServiceInterface.call(callInfo);
327 } catch (RemoteException e) {
328 mPendingOutgoingCalls.remove(callId).onResult(false);
Ben Gilad61925612014-03-11 19:06:36 -0700329 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800330 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700331
332 @Override
333 public void onFailure() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700334 resultCallback.onResult(false);
Ben Gilad61925612014-03-11 19:06:36 -0700335 }
336 };
337
338 mBinder.bind(callback);
Santos Cordon63aeb162014-02-10 09:20:40 -0800339 }
340
Ihab Awad74549ec2014-03-10 15:33:25 -0700341 /** @see CallService#abort(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700342 void abort(Call call) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700343 // Clear out any pending outgoing call data
344 String callId = mCallIdMapper.getCallId(call);
345
346 // If still bound, tell the call service to abort.
Ben Gilad28e8ad62014-03-06 17:01:54 -0800347 if (isServiceValid("abort")) {
348 try {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700349 mServiceInterface.abort(callId);
Ben Gilad28e8ad62014-03-06 17:01:54 -0800350 } catch (RemoteException e) {
Santos Cordon63aeb162014-02-10 09:20:40 -0800351 }
Santos Cordon61d0f702014-02-19 02:52:23 -0800352 }
Santos Cordon682fe6b2014-05-20 08:56:39 -0700353
354 removeCall(call);
Santos Cordon61d0f702014-02-19 02:52:23 -0800355 }
356
Ihab Awad74549ec2014-03-10 15:33:25 -0700357 /** @see CallService#hold(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700358 void hold(Call call) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700359 if (isServiceValid("hold")) {
360 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700361 mServiceInterface.hold(mCallIdMapper.getCallId(call));
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700362 } catch (RemoteException e) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700363 }
364 }
365 }
366
Ihab Awad74549ec2014-03-10 15:33:25 -0700367 /** @see CallService#unhold(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700368 void unhold(Call call) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700369 if (isServiceValid("unhold")) {
370 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700371 mServiceInterface.unhold(mCallIdMapper.getCallId(call));
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700372 } catch (RemoteException e) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700373 }
374 }
375 }
376
Ihab Awad74549ec2014-03-10 15:33:25 -0700377 /** @see CallService#onAudioStateChanged(String,CallAudioState) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700378 void onAudioStateChanged(Call activeCall, CallAudioState audioState) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700379 if (isServiceValid("onAudioStateChanged")) {
380 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700381 mServiceInterface.onAudioStateChanged(mCallIdMapper.getCallId(activeCall),
382 audioState);
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700383 } catch (RemoteException e) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700384 }
385 }
386 }
387
Ben Gilad61925612014-03-11 19:06:36 -0700388 /**
389 * Starts retrieval of details for an incoming call. Details are returned through the
390 * call-service adapter using the specified call ID. Upon failure, the specified error callback
Santos Cordon3d3b4052014-05-05 12:05:36 -0700391 * is invoked. Can be invoked even when the call service is unbound. See
392 * {@link ICallService#setIncomingCallId}.
Ben Gilad61925612014-03-11 19:06:36 -0700393 *
Sailesh Nepale59bb192014-04-01 18:33:59 -0700394 * @param call The call used for the incoming call.
Ben Gilad61925612014-03-11 19:06:36 -0700395 * @param extras The {@link CallService}-provided extras which need to be sent back.
396 * @param errorCallback The callback to invoke upon failure.
397 */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700398 void setIncomingCallId(final Call call, final Bundle extras, final Runnable errorCallback) {
399 Log.d(this, "setIncomingCall(%s) via %s.", call, getComponentName());
Ben Gilad61925612014-03-11 19:06:36 -0700400 BindCallback callback = new BindCallback() {
Santos Cordon3d3b4052014-05-05 12:05:36 -0700401 @Override
402 public void onSuccess() {
Ben Gilad61925612014-03-11 19:06:36 -0700403 if (isServiceValid("setIncomingCallId")) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700404 mPendingIncomingCalls.add(call);
Ben Gilad61925612014-03-11 19:06:36 -0700405 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700406 mServiceInterface.setIncomingCallId(mCallIdMapper.getCallId(call),
407 extras);
Ben Gilad61925612014-03-11 19:06:36 -0700408 } catch (RemoteException e) {
Ben Gilad61925612014-03-11 19:06:36 -0700409 }
410 }
Santos Cordon61d0f702014-02-19 02:52:23 -0800411 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700412
413 @Override
414 public void onFailure() {
Ben Gilad61925612014-03-11 19:06:36 -0700415 errorCallback.run();
416 }
417 };
418
419 mBinder.bind(callback);
Santos Cordon63aeb162014-02-10 09:20:40 -0800420 }
421
Ihab Awad74549ec2014-03-10 15:33:25 -0700422 /** @see CallService#disconnect(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700423 void disconnect(Call call) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800424 if (isServiceValid("disconnect")) {
425 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700426 mServiceInterface.disconnect(mCallIdMapper.getCallId(call));
Santos Cordon61d0f702014-02-19 02:52:23 -0800427 } catch (RemoteException e) {
Santos Cordon63aeb162014-02-10 09:20:40 -0800428 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800429 }
430 }
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800431
Ihab Awad74549ec2014-03-10 15:33:25 -0700432 /** @see CallService#answer(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700433 void answer(Call call) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800434 if (isServiceValid("answer")) {
435 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700436 mServiceInterface.answer(mCallIdMapper.getCallId(call));
Santos Cordon61d0f702014-02-19 02:52:23 -0800437 } catch (RemoteException e) {
Santos Cordon7917d382014-02-14 02:31:18 -0800438 }
Santos Cordon61d0f702014-02-19 02:52:23 -0800439 }
440 }
441
Ihab Awad74549ec2014-03-10 15:33:25 -0700442 /** @see CallService#reject(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700443 void reject(Call call) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800444 if (isServiceValid("reject")) {
445 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700446 mServiceInterface.reject(mCallIdMapper.getCallId(call));
Santos Cordon61d0f702014-02-19 02:52:23 -0800447 } catch (RemoteException e) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700448 }
449 }
450 }
451
452 /** @see CallService#playDtmfTone(String,char) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700453 void playDtmfTone(Call call, char digit) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700454 if (isServiceValid("playDtmfTone")) {
455 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700456 mServiceInterface.playDtmfTone(mCallIdMapper.getCallId(call), digit);
Ihab Awad74549ec2014-03-10 15:33:25 -0700457 } catch (RemoteException e) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700458 }
459 }
460 }
461
462 /** @see CallService#stopDtmfTone(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700463 void stopDtmfTone(Call call) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700464 if (isServiceValid("stopDtmfTone")) {
465 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700466 mServiceInterface.stopDtmfTone(mCallIdMapper.getCallId(call));
Ihab Awad74549ec2014-03-10 15:33:25 -0700467 } catch (RemoteException e) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800468 }
Santos Cordon7917d382014-02-14 02:31:18 -0800469 }
470 }
471
Sailesh Nepale59bb192014-04-01 18:33:59 -0700472 void addCall(Call call) {
473 mCallIdMapper.addCall(call);
Santos Cordon7917d382014-02-14 02:31:18 -0800474 }
475
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700476 /**
477 * Associates newCall with this call service by replacing callToReplace.
478 */
479 void replaceCall(Call newCall, Call callToReplace) {
480 Preconditions.checkState(callToReplace.getCallService() == this);
481 mCallIdMapper.replaceCall(newCall, callToReplace);
482 }
483
Sailesh Nepale59bb192014-04-01 18:33:59 -0700484 void removeCall(Call call) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700485 mPendingIncomingCalls.remove(call);
486
487 AsyncResultCallback<Boolean> outgoingResultCallback =
488 mPendingOutgoingCalls.remove(mCallIdMapper.getCallId(call));
489 if (outgoingResultCallback != null) {
490 outgoingResultCallback.onResult(false);
491 }
492
Sailesh Nepale59bb192014-04-01 18:33:59 -0700493 mCallIdMapper.removeCall(call);
Yorke Leeadee12d2014-03-13 12:08:30 -0700494 }
495
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800496 /** {@inheritDoc} */
Santos Cordon3d3b4052014-05-05 12:05:36 -0700497 @Override
498 protected void setServiceInterface(IBinder binder) {
Santos Cordon4b2c1192014-03-19 18:15:38 -0700499 if (binder == null) {
500 // We have lost our service connection. Notify the world that this call service is done.
501 // We must notify the adapter before CallsManager. The adapter will force any pending
502 // outgoing calls to try the next call service. This needs to happen before CallsManager
503 // tries to clean up any calls still associated with this call service.
Santos Cordon3d3b4052014-05-05 12:05:36 -0700504 handleCallServiceDeath();
Santos Cordon4b2c1192014-03-19 18:15:38 -0700505 CallsManager.getInstance().handleCallServiceDeath(this);
506 mServiceInterface = null;
507 } else {
508 mServiceInterface = ICallService.Stub.asInterface(binder);
509 setCallServiceAdapter(mAdapter);
510 }
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800511 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700512
513 /**
514 * Called when the associated call service dies.
515 */
516 private void handleCallServiceDeath() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700517 if (!mPendingOutgoingCalls.isEmpty()) {
518 for (AsyncResultCallback<Boolean> callback : mPendingOutgoingCalls.values()) {
519 callback.onResult(false);
520 }
521 mPendingOutgoingCalls.clear();
522 }
523
524 if (!mPendingIncomingCalls.isEmpty()) {
Santos Cordon3d3b4052014-05-05 12:05:36 -0700525 // Iterate through a copy because the code inside the loop will modify the original
526 // list.
Santos Cordon682fe6b2014-05-20 08:56:39 -0700527 for (Call call : ImmutableList.copyOf(mPendingIncomingCalls)) {
528 Preconditions.checkState(call.isIncoming());
529 mIncomingCallsManager.handleFailedIncomingCall(call);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700530 }
531
Santos Cordon8f3282c2014-06-01 13:56:02 -0700532 if (!mPendingIncomingCalls.isEmpty()) {
533 Log.wtf(this, "Pending calls did not get cleared.");
534 mPendingIncomingCalls.clear();
535 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700536 }
Santos Cordon8f3282c2014-06-01 13:56:02 -0700537
538 mCallIdMapper.clear();
539 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800540}