blob: 160b1e91ef7fcc79f0c4bc92b2796066f4fb3a20 [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;
Evan Charlton352105c2014-06-03 14:10:54 -070064 private static final int MSG_ON_POST_DIAL_WAIT = 10;
Santos Cordon3d3b4052014-05-05 12:05:36 -070065
66 private final Handler mHandler = new Handler() {
67 @Override
68 public void handleMessage(Message msg) {
69 Call call;
70 switch (msg.what) {
Santos Cordon3d3b4052014-05-05 12:05:36 -070071 case MSG_NOTIFY_INCOMING_CALL:
72 CallInfo clientCallInfo = (CallInfo) msg.obj;
73 call = mCallIdMapper.getCall(clientCallInfo.getId());
Santos Cordon682fe6b2014-05-20 08:56:39 -070074 if (call != null && mPendingIncomingCalls.remove(call) &&
75 call.isIncoming()) {
Santos Cordon3d3b4052014-05-05 12:05:36 -070076 CallInfo callInfo = new CallInfo(null, clientCallInfo.getState(),
77 clientCallInfo.getHandle());
78 mIncomingCallsManager.handleSuccessfulIncomingCall(call, callInfo);
79 } else {
80 Log.w(this, "notifyIncomingCall, unknown incoming call: %s, id: %s",
81 call,
82 clientCallInfo.getId());
83 }
84 break;
Santos Cordon682fe6b2014-05-20 08:56:39 -070085 case MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL: {
86 String callId = (String) msg.obj;
87 if (mPendingOutgoingCalls.containsKey(callId)) {
Ihab Awada3cb9e32014-06-03 18:45:05 -070088 mPendingOutgoingCalls.remove(callId).onResult(true, 0, null);
Santos Cordon3d3b4052014-05-05 12:05:36 -070089 } else {
Santos Cordon682fe6b2014-05-20 08:56:39 -070090 Log.w(this, "handleSuccessfulOutgoingCall, unknown call: %s", callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -070091 }
92 break;
Santos Cordon682fe6b2014-05-20 08:56:39 -070093 }
Santos Cordon3d3b4052014-05-05 12:05:36 -070094 case MSG_HANDLE_FAILED_OUTGOING_CALL: {
95 SomeArgs args = (SomeArgs) msg.obj;
96 try {
Santos Cordon682fe6b2014-05-20 08:56:39 -070097 String callId = (String) args.arg1;
Ihab Awada3cb9e32014-06-03 18:45:05 -070098 int statusCode = args.argi1;
99 String statusMsg = (String) args.arg2;
Santos Cordon682fe6b2014-05-20 08:56:39 -0700100 // TODO(santoscordon): Do something with 'reason' or get rid of it.
101
102 if (mPendingOutgoingCalls.containsKey(callId)) {
Ihab Awada3cb9e32014-06-03 18:45:05 -0700103 mPendingOutgoingCalls.remove(callId).onResult(
104 false, statusCode, statusMsg);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700105 mCallIdMapper.removeCall(callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700106 } else {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700107 Log.w(this, "handleFailedOutgoingCall, unknown call: %s", callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700108 }
109 } finally {
110 args.recycle();
111 }
112 break;
113 }
114 case MSG_SET_ACTIVE:
115 call = mCallIdMapper.getCall(msg.obj);
116 if (call != null) {
117 mCallsManager.markCallAsActive(call);
118 } else {
119 Log.w(this, "setActive, unknown call id: %s", msg.obj);
120 }
121 break;
122 case MSG_SET_RINGING:
123 call = mCallIdMapper.getCall(msg.obj);
124 if (call != null) {
125 mCallsManager.markCallAsRinging(call);
126 } else {
127 Log.w(this, "setRinging, unknown call id: %s", msg.obj);
128 }
129 break;
130 case MSG_SET_DIALING:
131 call = mCallIdMapper.getCall(msg.obj);
132 if (call != null) {
133 mCallsManager.markCallAsDialing(call);
134 } else {
135 Log.w(this, "setDialing, unknown call id: %s", msg.obj);
136 }
137 break;
138 case MSG_SET_DISCONNECTED: {
139 SomeArgs args = (SomeArgs) msg.obj;
140 try {
141 call = mCallIdMapper.getCall(args.arg1);
142 String disconnectMessage = (String) args.arg2;
143 int disconnectCause = args.argi1;
144 if (call != null) {
145 mCallsManager.markCallAsDisconnected(call, disconnectCause,
146 disconnectMessage);
147 } else {
148 Log.w(this, "setDisconnected, unknown call id: %s", args.arg1);
149 }
150 } finally {
151 args.recycle();
152 }
153 break;
154 }
155 case MSG_SET_ON_HOLD:
156 call = mCallIdMapper.getCall(msg.obj);
157 if (call != null) {
158 mCallsManager.markCallAsOnHold(call);
159 } else {
160 Log.w(this, "setOnHold, unknown call id: %s", msg.obj);
161 }
162 break;
Evan Charlton352105c2014-06-03 14:10:54 -0700163 case MSG_SET_REQUESTING_RINGBACK: {
Ihab Awad50a57132014-05-28 16:49:38 -0700164 SomeArgs args = (SomeArgs) msg.obj;
165 try {
166 call = mCallIdMapper.getCall(args.arg1);
167 boolean ringback = (boolean) args.arg2;
168 if (call != null) {
169 call.setRequestingRingback(ringback);
170 } else {
171 Log.w(this, "setRingback, unknown call id: %s", args.arg1);
172 }
173 } finally {
174 args.recycle();
175 }
176 break;
Evan Charlton352105c2014-06-03 14:10:54 -0700177 }
178 case MSG_ON_POST_DIAL_WAIT:
179 SomeArgs args = (SomeArgs) msg.obj;
180 try {
181 call = mCallIdMapper.getCall(args.arg1);
182 if (call != null) {
183 String remaining = (String) args.arg2;
184 call.onPostDialWait(remaining);
185 } else {
186 Log.w(this, "onPostDialWait, unknown call id: %s", args.arg1);
187 }
188 } finally {
189 args.recycle();
190 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700191 }
192 }
193 };
194
195 /** {@inheritDoc} */
196 @Override
197 public void setIsCompatibleWith(String callId, boolean isCompatible) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700198 Log.wtf(this, "Not expected.");
Santos Cordon3d3b4052014-05-05 12:05:36 -0700199 }
200
201 /** {@inheritDoc} */
202 @Override
203 public void notifyIncomingCall(CallInfo callInfo) {
204 mCallIdMapper.checkValidCallId(callInfo.getId());
205 mHandler.obtainMessage(MSG_NOTIFY_INCOMING_CALL, callInfo).sendToTarget();
206 }
207
208 /** {@inheritDoc} */
209 @Override
210 public void handleSuccessfulOutgoingCall(String callId) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700211 Log.d(this, "handleSuccessfulOutgoingCall %s", callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700212 mCallIdMapper.checkValidCallId(callId);
213 mHandler.obtainMessage(MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL, callId).sendToTarget();
214 }
215
216 /** {@inheritDoc} */
217 @Override
Ihab Awada3cb9e32014-06-03 18:45:05 -0700218 public void handleFailedOutgoingCall(
219 ConnectionRequest request,
220 int errorCode,
221 String errorMsg) {
222 mCallIdMapper.checkValidCallId(request.getCallId());
223 Log.d(this, "handleFailedOutgoingCall %s", request.getCallId());
Santos Cordon3d3b4052014-05-05 12:05:36 -0700224 SomeArgs args = SomeArgs.obtain();
Ihab Awada3cb9e32014-06-03 18:45:05 -0700225 args.arg1 = request.getCallId();
226 args.argi1 = errorCode;
227 args.arg2 = errorMsg;
Santos Cordon3d3b4052014-05-05 12:05:36 -0700228 mHandler.obtainMessage(MSG_HANDLE_FAILED_OUTGOING_CALL, args).sendToTarget();
229 }
230
231 /** {@inheritDoc} */
232 @Override
233 public void setActive(String callId) {
234 mCallIdMapper.checkValidCallId(callId);
235 mHandler.obtainMessage(MSG_SET_ACTIVE, callId).sendToTarget();
236 }
237
238 /** {@inheritDoc} */
239 @Override
240 public void setRinging(String callId) {
241 mCallIdMapper.checkValidCallId(callId);
242 mHandler.obtainMessage(MSG_SET_RINGING, callId).sendToTarget();
243 }
244
245 /** {@inheritDoc} */
246 @Override
247 public void setDialing(String callId) {
248 mCallIdMapper.checkValidCallId(callId);
249 mHandler.obtainMessage(MSG_SET_DIALING, callId).sendToTarget();
250 }
251
252 /** {@inheritDoc} */
253 @Override
254 public void setDisconnected(
255 String callId, int disconnectCause, String disconnectMessage) {
256 mCallIdMapper.checkValidCallId(callId);
257 SomeArgs args = SomeArgs.obtain();
258 args.arg1 = callId;
259 args.arg2 = disconnectMessage;
260 args.argi1 = disconnectCause;
261 mHandler.obtainMessage(MSG_SET_DISCONNECTED, args).sendToTarget();
262 }
263
264 /** {@inheritDoc} */
265 @Override
266 public void setOnHold(String callId) {
267 mCallIdMapper.checkValidCallId(callId);
268 mHandler.obtainMessage(MSG_SET_ON_HOLD, callId).sendToTarget();
269 }
Ihab Awad50a57132014-05-28 16:49:38 -0700270
271 /** {@inheritDoc} */
272 @Override
273 public void setRequestingRingback(String callId, boolean ringback) {
274 mCallIdMapper.checkValidCallId(callId);
275 SomeArgs args = SomeArgs.obtain();
276 args.arg1 = callId;
277 args.arg2 = ringback;
278 mHandler.obtainMessage(MSG_SET_REQUESTING_RINGBACK, args).sendToTarget();
279 }
Santos Cordon8f3282c2014-06-01 13:56:02 -0700280
281 /** ${inheritDoc} */
282 @Override
283 public void removeCall(String callId) {
284 }
285
286 /** ${inheritDoc} */
287 @Override
288 public void setCanConferenceWith(String callId, List<String> conferenceCapableCallIds) {
289 }
290
291 /** ${inheritDoc} */
292 @Override
293 public void setIsConferenced(String conferenceCallId, String callId, boolean isConferenced) {
294 }
Evan Charlton352105c2014-06-03 14:10:54 -0700295
296 @Override
297 public void onPostDialWait(String callId, String remaining) throws RemoteException {
298 mCallIdMapper.checkValidCallId(callId);
299 SomeArgs args = SomeArgs.obtain();
300 args.arg1 = callId;
301 args.arg2 = remaining;
302 mHandler.obtainMessage(MSG_ON_POST_DIAL_WAIT, args).sendToTarget();
303 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700304 }
305
306 private final Adapter mAdapter = new Adapter();
307 private final CallsManager mCallsManager = CallsManager.getInstance();
Santos Cordon682fe6b2014-05-20 08:56:39 -0700308 private final Set<Call> mPendingIncomingCalls = Sets.newHashSet();
Ben Giladc5b22692014-02-18 20:03:22 -0800309 private final CallServiceDescriptor mDescriptor;
Santos Cordon3d3b4052014-05-05 12:05:36 -0700310 private final CallIdMapper mCallIdMapper = new CallIdMapper("CallService");
Santos Cordon3d3b4052014-05-05 12:05:36 -0700311 private final IncomingCallsManager mIncomingCallsManager;
Santos Cordon682fe6b2014-05-20 08:56:39 -0700312 private final Map<String, AsyncResultCallback<Boolean>> mPendingOutgoingCalls = new HashMap<>();
Santos Cordonc195e362014-02-11 17:05:31 -0800313
Ben Gilad61925612014-03-11 19:06:36 -0700314 private Binder mBinder = new Binder();
Santos Cordon3d3b4052014-05-05 12:05:36 -0700315 private ICallService mServiceInterface;
Ben Gilad61925612014-03-11 19:06:36 -0700316
Santos Cordon63aeb162014-02-10 09:20:40 -0800317 /**
Sailesh Nepale59bb192014-04-01 18:33:59 -0700318 * Creates a call-service for the specified descriptor.
Santos Cordonc195e362014-02-11 17:05:31 -0800319 *
Santos Cordon61d0f702014-02-19 02:52:23 -0800320 * @param descriptor The call-service descriptor from
Santos Cordon3d3b4052014-05-05 12:05:36 -0700321 * {@link ICallServiceProvider#lookupCallServices}.
Sailesh Nepale59bb192014-04-01 18:33:59 -0700322 * @param incomingCallsManager Manages the incoming call initialization flow.
Santos Cordon63aeb162014-02-10 09:20:40 -0800323 */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700324 CallServiceWrapper(
325 CallServiceDescriptor descriptor,
Sailesh Nepale59bb192014-04-01 18:33:59 -0700326 IncomingCallsManager incomingCallsManager) {
Sailesh Nepala439e1b2014-03-11 18:19:58 -0700327 super(TelecommConstants.ACTION_CALL_SERVICE, descriptor.getServiceComponent());
Ben Giladc5b22692014-02-18 20:03:22 -0800328 mDescriptor = descriptor;
Santos Cordon3d3b4052014-05-05 12:05:36 -0700329 mIncomingCallsManager = incomingCallsManager;
Santos Cordon63aeb162014-02-10 09:20:40 -0800330 }
331
Ben Gilad61925612014-03-11 19:06:36 -0700332 CallServiceDescriptor getDescriptor() {
Ben Giladc5b22692014-02-18 20:03:22 -0800333 return mDescriptor;
Santos Cordonc195e362014-02-11 17:05:31 -0800334 }
335
Santos Cordon63aeb162014-02-10 09:20:40 -0800336 /** See {@link ICallService#setCallServiceAdapter}. */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700337 private void setCallServiceAdapter(ICallServiceAdapter callServiceAdapter) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800338 if (isServiceValid("setCallServiceAdapter")) {
339 try {
Santos Cordon63aeb162014-02-10 09:20:40 -0800340 mServiceInterface.setCallServiceAdapter(callServiceAdapter);
Santos Cordon61d0f702014-02-19 02:52:23 -0800341 } catch (RemoteException e) {
Santos Cordon63aeb162014-02-10 09:20:40 -0800342 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800343 }
344 }
345
Ben Gilad61925612014-03-11 19:06:36 -0700346 /**
Santos Cordon682fe6b2014-05-20 08:56:39 -0700347 * Attempts to place the specified call, see {@link ICallService#call}. Returns the result
348 * asynchronously through the specified callback.
Ben Gilad61925612014-03-11 19:06:36 -0700349 */
Santos Cordon682fe6b2014-05-20 08:56:39 -0700350 void call(final Call call, final AsyncResultCallback<Boolean> resultCallback) {
351 Log.d(this, "call(%s) via %s.", call, getComponentName());
Ben Gilad61925612014-03-11 19:06:36 -0700352 BindCallback callback = new BindCallback() {
Santos Cordon3d3b4052014-05-05 12:05:36 -0700353 @Override
354 public void onSuccess() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700355 String callId = mCallIdMapper.getCallId(call);
356 mPendingOutgoingCalls.put(callId, resultCallback);
357
358 try {
359 CallInfo callInfo = call.toCallInfo(callId);
360 mServiceInterface.call(callInfo);
361 } catch (RemoteException e) {
Ihab Awada3cb9e32014-06-03 18:45:05 -0700362 mPendingOutgoingCalls.remove(callId).onResult(
363 false, DisconnectCause.ERROR_UNSPECIFIED, e.toString());
Ben Gilad61925612014-03-11 19:06:36 -0700364 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800365 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700366
367 @Override
368 public void onFailure() {
Ihab Awada3cb9e32014-06-03 18:45:05 -0700369 resultCallback.onResult(false, DisconnectCause.ERROR_UNSPECIFIED, null);
Ben Gilad61925612014-03-11 19:06:36 -0700370 }
371 };
372
373 mBinder.bind(callback);
Santos Cordon63aeb162014-02-10 09:20:40 -0800374 }
375
Ihab Awad74549ec2014-03-10 15:33:25 -0700376 /** @see CallService#abort(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700377 void abort(Call call) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700378 // Clear out any pending outgoing call data
379 String callId = mCallIdMapper.getCallId(call);
380
381 // If still bound, tell the call service to abort.
Ben Gilad28e8ad62014-03-06 17:01:54 -0800382 if (isServiceValid("abort")) {
383 try {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700384 mServiceInterface.abort(callId);
Ben Gilad28e8ad62014-03-06 17:01:54 -0800385 } catch (RemoteException e) {
Santos Cordon63aeb162014-02-10 09:20:40 -0800386 }
Santos Cordon61d0f702014-02-19 02:52:23 -0800387 }
Santos Cordon682fe6b2014-05-20 08:56:39 -0700388
389 removeCall(call);
Santos Cordon61d0f702014-02-19 02:52:23 -0800390 }
391
Ihab Awad74549ec2014-03-10 15:33:25 -0700392 /** @see CallService#hold(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700393 void hold(Call call) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700394 if (isServiceValid("hold")) {
395 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700396 mServiceInterface.hold(mCallIdMapper.getCallId(call));
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700397 } catch (RemoteException e) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700398 }
399 }
400 }
401
Ihab Awad74549ec2014-03-10 15:33:25 -0700402 /** @see CallService#unhold(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700403 void unhold(Call call) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700404 if (isServiceValid("unhold")) {
405 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700406 mServiceInterface.unhold(mCallIdMapper.getCallId(call));
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700407 } catch (RemoteException e) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700408 }
409 }
410 }
411
Ihab Awad74549ec2014-03-10 15:33:25 -0700412 /** @see CallService#onAudioStateChanged(String,CallAudioState) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700413 void onAudioStateChanged(Call activeCall, CallAudioState audioState) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700414 if (isServiceValid("onAudioStateChanged")) {
415 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700416 mServiceInterface.onAudioStateChanged(mCallIdMapper.getCallId(activeCall),
417 audioState);
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700418 } catch (RemoteException e) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700419 }
420 }
421 }
422
Ben Gilad61925612014-03-11 19:06:36 -0700423 /**
424 * Starts retrieval of details for an incoming call. Details are returned through the
425 * call-service adapter using the specified call ID. Upon failure, the specified error callback
Santos Cordon3d3b4052014-05-05 12:05:36 -0700426 * is invoked. Can be invoked even when the call service is unbound. See
427 * {@link ICallService#setIncomingCallId}.
Ben Gilad61925612014-03-11 19:06:36 -0700428 *
Sailesh Nepale59bb192014-04-01 18:33:59 -0700429 * @param call The call used for the incoming call.
Ben Gilad61925612014-03-11 19:06:36 -0700430 * @param extras The {@link CallService}-provided extras which need to be sent back.
431 * @param errorCallback The callback to invoke upon failure.
432 */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700433 void setIncomingCallId(final Call call, final Bundle extras, final Runnable errorCallback) {
434 Log.d(this, "setIncomingCall(%s) via %s.", call, getComponentName());
Ben Gilad61925612014-03-11 19:06:36 -0700435 BindCallback callback = new BindCallback() {
Santos Cordon3d3b4052014-05-05 12:05:36 -0700436 @Override
437 public void onSuccess() {
Ben Gilad61925612014-03-11 19:06:36 -0700438 if (isServiceValid("setIncomingCallId")) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700439 mPendingIncomingCalls.add(call);
Ben Gilad61925612014-03-11 19:06:36 -0700440 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700441 mServiceInterface.setIncomingCallId(mCallIdMapper.getCallId(call),
442 extras);
Ben Gilad61925612014-03-11 19:06:36 -0700443 } catch (RemoteException e) {
Ben Gilad61925612014-03-11 19:06:36 -0700444 }
445 }
Santos Cordon61d0f702014-02-19 02:52:23 -0800446 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700447
448 @Override
449 public void onFailure() {
Ben Gilad61925612014-03-11 19:06:36 -0700450 errorCallback.run();
451 }
452 };
453
454 mBinder.bind(callback);
Santos Cordon63aeb162014-02-10 09:20:40 -0800455 }
456
Ihab Awad74549ec2014-03-10 15:33:25 -0700457 /** @see CallService#disconnect(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700458 void disconnect(Call call) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800459 if (isServiceValid("disconnect")) {
460 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700461 mServiceInterface.disconnect(mCallIdMapper.getCallId(call));
Santos Cordon61d0f702014-02-19 02:52:23 -0800462 } catch (RemoteException e) {
Santos Cordon63aeb162014-02-10 09:20:40 -0800463 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800464 }
465 }
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800466
Ihab Awad74549ec2014-03-10 15:33:25 -0700467 /** @see CallService#answer(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700468 void answer(Call call) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800469 if (isServiceValid("answer")) {
470 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700471 mServiceInterface.answer(mCallIdMapper.getCallId(call));
Santos Cordon61d0f702014-02-19 02:52:23 -0800472 } catch (RemoteException e) {
Santos Cordon7917d382014-02-14 02:31:18 -0800473 }
Santos Cordon61d0f702014-02-19 02:52:23 -0800474 }
475 }
476
Ihab Awad74549ec2014-03-10 15:33:25 -0700477 /** @see CallService#reject(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700478 void reject(Call call) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800479 if (isServiceValid("reject")) {
480 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700481 mServiceInterface.reject(mCallIdMapper.getCallId(call));
Santos Cordon61d0f702014-02-19 02:52:23 -0800482 } catch (RemoteException e) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700483 }
484 }
485 }
486
487 /** @see CallService#playDtmfTone(String,char) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700488 void playDtmfTone(Call call, char digit) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700489 if (isServiceValid("playDtmfTone")) {
490 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700491 mServiceInterface.playDtmfTone(mCallIdMapper.getCallId(call), digit);
Ihab Awad74549ec2014-03-10 15:33:25 -0700492 } catch (RemoteException e) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700493 }
494 }
495 }
496
497 /** @see CallService#stopDtmfTone(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700498 void stopDtmfTone(Call call) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700499 if (isServiceValid("stopDtmfTone")) {
500 try {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700501 mServiceInterface.stopDtmfTone(mCallIdMapper.getCallId(call));
Ihab Awad74549ec2014-03-10 15:33:25 -0700502 } catch (RemoteException e) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800503 }
Santos Cordon7917d382014-02-14 02:31:18 -0800504 }
505 }
506
Sailesh Nepale59bb192014-04-01 18:33:59 -0700507 void addCall(Call call) {
508 mCallIdMapper.addCall(call);
Santos Cordon7917d382014-02-14 02:31:18 -0800509 }
510
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700511 /**
512 * Associates newCall with this call service by replacing callToReplace.
513 */
514 void replaceCall(Call newCall, Call callToReplace) {
515 Preconditions.checkState(callToReplace.getCallService() == this);
516 mCallIdMapper.replaceCall(newCall, callToReplace);
517 }
518
Sailesh Nepale59bb192014-04-01 18:33:59 -0700519 void removeCall(Call call) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700520 mPendingIncomingCalls.remove(call);
521
522 AsyncResultCallback<Boolean> outgoingResultCallback =
523 mPendingOutgoingCalls.remove(mCallIdMapper.getCallId(call));
524 if (outgoingResultCallback != null) {
Ihab Awada3cb9e32014-06-03 18:45:05 -0700525 outgoingResultCallback.onResult(false, DisconnectCause.ERROR_UNSPECIFIED, null);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700526 }
527
Sailesh Nepale59bb192014-04-01 18:33:59 -0700528 mCallIdMapper.removeCall(call);
Yorke Leeadee12d2014-03-13 12:08:30 -0700529 }
530
Evan Charlton352105c2014-06-03 14:10:54 -0700531 void onPostDialContinue(Call call, boolean proceed) {
532 if (isServiceValid("onPostDialContinue")) {
533 try {
534 mServiceInterface.onPostDialContinue(mCallIdMapper.getCallId(call), proceed);
535 } catch (RemoteException ignored) {
536 }
537 }
538 }
539
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800540 /** {@inheritDoc} */
Santos Cordon3d3b4052014-05-05 12:05:36 -0700541 @Override
542 protected void setServiceInterface(IBinder binder) {
Santos Cordon4b2c1192014-03-19 18:15:38 -0700543 if (binder == null) {
544 // We have lost our service connection. Notify the world that this call service is done.
545 // We must notify the adapter before CallsManager. The adapter will force any pending
546 // outgoing calls to try the next call service. This needs to happen before CallsManager
547 // tries to clean up any calls still associated with this call service.
Santos Cordon3d3b4052014-05-05 12:05:36 -0700548 handleCallServiceDeath();
Santos Cordon4b2c1192014-03-19 18:15:38 -0700549 CallsManager.getInstance().handleCallServiceDeath(this);
550 mServiceInterface = null;
551 } else {
552 mServiceInterface = ICallService.Stub.asInterface(binder);
553 setCallServiceAdapter(mAdapter);
554 }
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800555 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700556
557 /**
558 * Called when the associated call service dies.
559 */
560 private void handleCallServiceDeath() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700561 if (!mPendingOutgoingCalls.isEmpty()) {
562 for (AsyncResultCallback<Boolean> callback : mPendingOutgoingCalls.values()) {
Ihab Awada3cb9e32014-06-03 18:45:05 -0700563 callback.onResult(false, DisconnectCause.ERROR_UNSPECIFIED, null);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700564 }
565 mPendingOutgoingCalls.clear();
566 }
567
568 if (!mPendingIncomingCalls.isEmpty()) {
Santos Cordon3d3b4052014-05-05 12:05:36 -0700569 // Iterate through a copy because the code inside the loop will modify the original
570 // list.
Santos Cordon682fe6b2014-05-20 08:56:39 -0700571 for (Call call : ImmutableList.copyOf(mPendingIncomingCalls)) {
572 Preconditions.checkState(call.isIncoming());
573 mIncomingCallsManager.handleFailedIncomingCall(call);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700574 }
575
Santos Cordon8f3282c2014-06-01 13:56:02 -0700576 if (!mPendingIncomingCalls.isEmpty()) {
577 Log.wtf(this, "Pending calls did not get cleared.");
578 mPendingIncomingCalls.clear();
579 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700580 }
Santos Cordon8f3282c2014-06-01 13:56:02 -0700581
582 mCallIdMapper.clear();
583 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800584}