blob: 4ebce23809b2de4c1980d51426fd79e002cd3579 [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;
Santos Cordona1610702014-06-04 20:22:56 -0700221 if (conferenceCallId == null) {
222 childCall.setParentCall(null);
223 } else {
224 Call conferenceCall = mCallIdMapper.getCall(conferenceCallId);
225 if (conferenceCall != null &&
226 !mPendingConferenceCalls.contains(conferenceCall)) {
227 childCall.setParentCall(conferenceCall);
228 } else {
229 Log.w(this, "setIsConferenced, unknown conference id %s",
230 conferenceCallId);
231 }
232 }
233 } else {
234 Log.w(this, "setIsConferenced, unknown call id: %s", args.arg1);
235 }
236 } finally {
237 args.recycle();
238 }
239 break;
240 }
241 case MSG_ADD_CONFERENCE_CALL: {
242 SomeArgs args = (SomeArgs) msg.obj;
243 try {
244 String callId = (String) args.arg1;
Santos Cordona1610702014-06-04 20:22:56 -0700245 Call conferenceCall = mCallIdMapper.getCall(callId);
246 if (mPendingConferenceCalls.remove(conferenceCall)) {
247 Log.v(this, "confirming conf call %s", conferenceCall);
248 conferenceCall.confirmConference();
249 } else {
250 Log.w(this, "addConference, unknown call id: %s", callId);
251 }
252 } finally {
253 args.recycle();
254 }
255 break;
256 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700257 }
258 }
259 };
260
261 /** {@inheritDoc} */
262 @Override
Santos Cordon3d3b4052014-05-05 12:05:36 -0700263 public void notifyIncomingCall(CallInfo callInfo) {
Ihab Awad55a34282014-06-18 10:31:09 -0700264 logIncoming("notifyIncomingCall %s", callInfo);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700265 mCallIdMapper.checkValidCallId(callInfo.getId());
266 mHandler.obtainMessage(MSG_NOTIFY_INCOMING_CALL, callInfo).sendToTarget();
267 }
268
269 /** {@inheritDoc} */
270 @Override
271 public void handleSuccessfulOutgoingCall(String callId) {
Ihab Awad55a34282014-06-18 10:31:09 -0700272 logIncoming("handleSuccessfulOutgoingCall %s", callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700273 mCallIdMapper.checkValidCallId(callId);
274 mHandler.obtainMessage(MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL, callId).sendToTarget();
275 }
276
277 /** {@inheritDoc} */
278 @Override
Ihab Awada3cb9e32014-06-03 18:45:05 -0700279 public void handleFailedOutgoingCall(
280 ConnectionRequest request,
281 int errorCode,
282 String errorMsg) {
Ihab Awad55a34282014-06-18 10:31:09 -0700283 logIncoming("handleFailedOutgoingCall %s %d %s", request, errorCode, errorMsg);
Ihab Awada3cb9e32014-06-03 18:45:05 -0700284 mCallIdMapper.checkValidCallId(request.getCallId());
Santos Cordon3d3b4052014-05-05 12:05:36 -0700285 SomeArgs args = SomeArgs.obtain();
Ihab Awada3cb9e32014-06-03 18:45:05 -0700286 args.arg1 = request.getCallId();
287 args.argi1 = errorCode;
288 args.arg2 = errorMsg;
Santos Cordon3d3b4052014-05-05 12:05:36 -0700289 mHandler.obtainMessage(MSG_HANDLE_FAILED_OUTGOING_CALL, args).sendToTarget();
290 }
291
292 /** {@inheritDoc} */
293 @Override
294 public void setActive(String callId) {
Ihab Awad55a34282014-06-18 10:31:09 -0700295 logIncoming("setActive %s", callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700296 mCallIdMapper.checkValidCallId(callId);
297 mHandler.obtainMessage(MSG_SET_ACTIVE, callId).sendToTarget();
298 }
299
300 /** {@inheritDoc} */
301 @Override
302 public void setRinging(String callId) {
Ihab Awad55a34282014-06-18 10:31:09 -0700303 logIncoming("setRinging %s", callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700304 mCallIdMapper.checkValidCallId(callId);
305 mHandler.obtainMessage(MSG_SET_RINGING, callId).sendToTarget();
306 }
307
308 /** {@inheritDoc} */
309 @Override
310 public void setDialing(String callId) {
Ihab Awad55a34282014-06-18 10:31:09 -0700311 logIncoming("setDialing %s", callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700312 mCallIdMapper.checkValidCallId(callId);
313 mHandler.obtainMessage(MSG_SET_DIALING, callId).sendToTarget();
314 }
315
316 /** {@inheritDoc} */
317 @Override
318 public void setDisconnected(
319 String callId, int disconnectCause, String disconnectMessage) {
Ihab Awad55a34282014-06-18 10:31:09 -0700320 logIncoming("setDisconnected %s %d %s", callId, disconnectCause, disconnectMessage);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700321 mCallIdMapper.checkValidCallId(callId);
322 SomeArgs args = SomeArgs.obtain();
323 args.arg1 = callId;
324 args.arg2 = disconnectMessage;
325 args.argi1 = disconnectCause;
326 mHandler.obtainMessage(MSG_SET_DISCONNECTED, args).sendToTarget();
327 }
328
329 /** {@inheritDoc} */
330 @Override
331 public void setOnHold(String callId) {
Ihab Awad55a34282014-06-18 10:31:09 -0700332 logIncoming("setOnHold %s", callId);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700333 mCallIdMapper.checkValidCallId(callId);
334 mHandler.obtainMessage(MSG_SET_ON_HOLD, callId).sendToTarget();
335 }
Ihab Awad50a57132014-05-28 16:49:38 -0700336
337 /** {@inheritDoc} */
338 @Override
339 public void setRequestingRingback(String callId, boolean ringback) {
Ihab Awad55a34282014-06-18 10:31:09 -0700340 logIncoming("setRequestingRingback %s %b", callId, ringback);
Ihab Awad50a57132014-05-28 16:49:38 -0700341 mCallIdMapper.checkValidCallId(callId);
342 SomeArgs args = SomeArgs.obtain();
343 args.arg1 = callId;
344 args.arg2 = ringback;
345 mHandler.obtainMessage(MSG_SET_REQUESTING_RINGBACK, args).sendToTarget();
346 }
Santos Cordon8f3282c2014-06-01 13:56:02 -0700347
348 /** ${inheritDoc} */
349 @Override
350 public void removeCall(String callId) {
Ihab Awad55a34282014-06-18 10:31:09 -0700351 logIncoming("removeCall %s", callId);
Santos Cordon8f3282c2014-06-01 13:56:02 -0700352 }
353
354 /** ${inheritDoc} */
355 @Override
Santos Cordona1610702014-06-04 20:22:56 -0700356 public void setCanConference(String callId, boolean canConference) {
Ihab Awad55a34282014-06-18 10:31:09 -0700357 logIncoming("setCanConference %s %b", callId, canConference);
Santos Cordona1610702014-06-04 20:22:56 -0700358 mHandler.obtainMessage(MSG_CAN_CONFERENCE, canConference ? 1 : 0, 0, callId)
359 .sendToTarget();
Santos Cordon8f3282c2014-06-01 13:56:02 -0700360 }
361
362 /** ${inheritDoc} */
363 @Override
Santos Cordona1610702014-06-04 20:22:56 -0700364 public void setIsConferenced(String callId, String conferenceCallId) {
Ihab Awad55a34282014-06-18 10:31:09 -0700365 logIncoming("setIsConferenced %s %s", callId, conferenceCallId);
Santos Cordona1610702014-06-04 20:22:56 -0700366 SomeArgs args = SomeArgs.obtain();
367 args.arg1 = callId;
368 args.arg2 = conferenceCallId;
369 mHandler.obtainMessage(MSG_SET_IS_CONFERENCED, args).sendToTarget();
370 }
371
372 /** ${InheritDoc} */
373 @Override
374 public void addConferenceCall(String callId, CallInfo callInfo) {
Ihab Awad55a34282014-06-18 10:31:09 -0700375 logIncoming("addConferenceCall %s %s", callId, callInfo);
Santos Cordona1610702014-06-04 20:22:56 -0700376 mCallIdMapper.checkValidCallId(callId);
377 SomeArgs args = SomeArgs.obtain();
378 args.arg1 = callId;
379 args.arg2 = callInfo;
380 mHandler.obtainMessage(MSG_ADD_CONFERENCE_CALL, args).sendToTarget();
Santos Cordon8f3282c2014-06-01 13:56:02 -0700381 }
Evan Charlton352105c2014-06-03 14:10:54 -0700382
383 @Override
384 public void onPostDialWait(String callId, String remaining) throws RemoteException {
Ihab Awad55a34282014-06-18 10:31:09 -0700385 logIncoming("onPostDialWait %s %s", callId, remaining);
Evan Charlton352105c2014-06-03 14:10:54 -0700386 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) {
Ihab Awad55a34282014-06-18 10:31:09 -0700396 logIncoming("handoffCall %s", callId);
Sailesh Nepal6098d2c2014-06-06 10:56:53 -0700397 mCallIdMapper.checkValidCallId(callId);
398 mHandler.obtainMessage(MSG_HANDOFF_CALL, callId).sendToTarget();
399 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700400 }
401
402 private final Adapter mAdapter = new Adapter();
403 private final CallsManager mCallsManager = CallsManager.getInstance();
Santos Cordona1610702014-06-04 20:22:56 -0700404 private final Set<Call> mPendingIncomingCalls = new HashSet<>();
405 private final Set<Call> mPendingConferenceCalls = new HashSet<>();
Ben Giladc5b22692014-02-18 20:03:22 -0800406 private final CallServiceDescriptor mDescriptor;
Santos Cordon3d3b4052014-05-05 12:05:36 -0700407 private final CallIdMapper mCallIdMapper = new CallIdMapper("CallService");
Santos Cordon3d3b4052014-05-05 12:05:36 -0700408 private final IncomingCallsManager mIncomingCallsManager;
Santos Cordon682fe6b2014-05-20 08:56:39 -0700409 private final Map<String, AsyncResultCallback<Boolean>> mPendingOutgoingCalls = new HashMap<>();
Santos Cordona1610702014-06-04 20:22:56 -0700410 private final Handler mHandler = new Handler();
Santos Cordonc195e362014-02-11 17:05:31 -0800411
Ben Gilad61925612014-03-11 19:06:36 -0700412 private Binder mBinder = new Binder();
Santos Cordon3d3b4052014-05-05 12:05:36 -0700413 private ICallService mServiceInterface;
Ben Gilad61925612014-03-11 19:06:36 -0700414
Santos Cordon63aeb162014-02-10 09:20:40 -0800415 /**
Sailesh Nepale59bb192014-04-01 18:33:59 -0700416 * Creates a call-service for the specified descriptor.
Santos Cordonc195e362014-02-11 17:05:31 -0800417 *
Santos Cordon61d0f702014-02-19 02:52:23 -0800418 * @param descriptor The call-service descriptor from
Santos Cordon3d3b4052014-05-05 12:05:36 -0700419 * {@link ICallServiceProvider#lookupCallServices}.
Sailesh Nepale59bb192014-04-01 18:33:59 -0700420 * @param incomingCallsManager Manages the incoming call initialization flow.
Santos Cordon63aeb162014-02-10 09:20:40 -0800421 */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700422 CallServiceWrapper(
423 CallServiceDescriptor descriptor,
Sailesh Nepale59bb192014-04-01 18:33:59 -0700424 IncomingCallsManager incomingCallsManager) {
Sailesh Nepala439e1b2014-03-11 18:19:58 -0700425 super(TelecommConstants.ACTION_CALL_SERVICE, descriptor.getServiceComponent());
Ben Giladc5b22692014-02-18 20:03:22 -0800426 mDescriptor = descriptor;
Santos Cordon3d3b4052014-05-05 12:05:36 -0700427 mIncomingCallsManager = incomingCallsManager;
Santos Cordon63aeb162014-02-10 09:20:40 -0800428 }
429
Ben Gilad61925612014-03-11 19:06:36 -0700430 CallServiceDescriptor getDescriptor() {
Ben Giladc5b22692014-02-18 20:03:22 -0800431 return mDescriptor;
Santos Cordonc195e362014-02-11 17:05:31 -0800432 }
433
Santos Cordon63aeb162014-02-10 09:20:40 -0800434 /** See {@link ICallService#setCallServiceAdapter}. */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700435 private void setCallServiceAdapter(ICallServiceAdapter callServiceAdapter) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800436 if (isServiceValid("setCallServiceAdapter")) {
437 try {
Ihab Awad55a34282014-06-18 10:31:09 -0700438 logOutgoing("setCallServiceAdapter %s", callServiceAdapter);
Santos Cordon63aeb162014-02-10 09:20:40 -0800439 mServiceInterface.setCallServiceAdapter(callServiceAdapter);
Santos Cordon61d0f702014-02-19 02:52:23 -0800440 } catch (RemoteException e) {
Santos Cordon63aeb162014-02-10 09:20:40 -0800441 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800442 }
443 }
444
Ben Gilad61925612014-03-11 19:06:36 -0700445 /**
Santos Cordon682fe6b2014-05-20 08:56:39 -0700446 * Attempts to place the specified call, see {@link ICallService#call}. Returns the result
447 * asynchronously through the specified callback.
Ben Gilad61925612014-03-11 19:06:36 -0700448 */
Santos Cordon682fe6b2014-05-20 08:56:39 -0700449 void call(final Call call, final AsyncResultCallback<Boolean> resultCallback) {
450 Log.d(this, "call(%s) via %s.", call, getComponentName());
Ben Gilad61925612014-03-11 19:06:36 -0700451 BindCallback callback = new BindCallback() {
Santos Cordon3d3b4052014-05-05 12:05:36 -0700452 @Override
453 public void onSuccess() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700454 String callId = mCallIdMapper.getCallId(call);
455 mPendingOutgoingCalls.put(callId, resultCallback);
456
457 try {
458 CallInfo callInfo = call.toCallInfo(callId);
Ihab Awad55a34282014-06-18 10:31:09 -0700459 logOutgoing("call %s", callInfo);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700460 mServiceInterface.call(callInfo);
461 } catch (RemoteException e) {
Ihab Awada3cb9e32014-06-03 18:45:05 -0700462 mPendingOutgoingCalls.remove(callId).onResult(
463 false, DisconnectCause.ERROR_UNSPECIFIED, e.toString());
Ben Gilad61925612014-03-11 19:06:36 -0700464 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800465 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700466
467 @Override
468 public void onFailure() {
Ihab Awada3cb9e32014-06-03 18:45:05 -0700469 resultCallback.onResult(false, DisconnectCause.ERROR_UNSPECIFIED, null);
Ben Gilad61925612014-03-11 19:06:36 -0700470 }
471 };
472
473 mBinder.bind(callback);
Santos Cordon63aeb162014-02-10 09:20:40 -0800474 }
475
Ihab Awad74549ec2014-03-10 15:33:25 -0700476 /** @see CallService#abort(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700477 void abort(Call call) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700478 // Clear out any pending outgoing call data
479 String callId = mCallIdMapper.getCallId(call);
480
481 // If still bound, tell the call service to abort.
Ben Gilad28e8ad62014-03-06 17:01:54 -0800482 if (isServiceValid("abort")) {
483 try {
Ihab Awad55a34282014-06-18 10:31:09 -0700484 logOutgoing("abort %s", callId);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700485 mServiceInterface.abort(callId);
Ben Gilad28e8ad62014-03-06 17:01:54 -0800486 } catch (RemoteException e) {
Santos Cordon63aeb162014-02-10 09:20:40 -0800487 }
Santos Cordon61d0f702014-02-19 02:52:23 -0800488 }
Santos Cordon682fe6b2014-05-20 08:56:39 -0700489
490 removeCall(call);
Santos Cordon61d0f702014-02-19 02:52:23 -0800491 }
492
Ihab Awad74549ec2014-03-10 15:33:25 -0700493 /** @see CallService#hold(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700494 void hold(Call call) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700495 if (isServiceValid("hold")) {
496 try {
Ihab Awad55a34282014-06-18 10:31:09 -0700497 logOutgoing("hold %s", mCallIdMapper.getCallId(call));
Sailesh Nepale59bb192014-04-01 18:33:59 -0700498 mServiceInterface.hold(mCallIdMapper.getCallId(call));
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700499 } catch (RemoteException e) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700500 }
501 }
502 }
503
Ihab Awad74549ec2014-03-10 15:33:25 -0700504 /** @see CallService#unhold(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700505 void unhold(Call call) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700506 if (isServiceValid("unhold")) {
507 try {
Ihab Awad55a34282014-06-18 10:31:09 -0700508 logOutgoing("unhold %s", mCallIdMapper.getCallId(call));
Sailesh Nepale59bb192014-04-01 18:33:59 -0700509 mServiceInterface.unhold(mCallIdMapper.getCallId(call));
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700510 } catch (RemoteException e) {
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700511 }
512 }
513 }
514
Ihab Awad74549ec2014-03-10 15:33:25 -0700515 /** @see CallService#onAudioStateChanged(String,CallAudioState) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700516 void onAudioStateChanged(Call activeCall, CallAudioState audioState) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700517 if (isServiceValid("onAudioStateChanged")) {
518 try {
Ihab Awad55a34282014-06-18 10:31:09 -0700519 logOutgoing("onAudioStateChanged %s %s",
520 mCallIdMapper.getCallId(activeCall), audioState);
Sailesh Nepale59bb192014-04-01 18:33:59 -0700521 mServiceInterface.onAudioStateChanged(mCallIdMapper.getCallId(activeCall),
522 audioState);
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700523 } catch (RemoteException e) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700524 }
525 }
526 }
527
Ben Gilad61925612014-03-11 19:06:36 -0700528 /**
529 * Starts retrieval of details for an incoming call. Details are returned through the
530 * call-service adapter using the specified call ID. Upon failure, the specified error callback
Santos Cordon3d3b4052014-05-05 12:05:36 -0700531 * is invoked. Can be invoked even when the call service is unbound. See
532 * {@link ICallService#setIncomingCallId}.
Ben Gilad61925612014-03-11 19:06:36 -0700533 *
Sailesh Nepale59bb192014-04-01 18:33:59 -0700534 * @param call The call used for the incoming call.
Ben Gilad61925612014-03-11 19:06:36 -0700535 * @param extras The {@link CallService}-provided extras which need to be sent back.
536 * @param errorCallback The callback to invoke upon failure.
537 */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700538 void setIncomingCallId(final Call call, final Bundle extras, final Runnable errorCallback) {
539 Log.d(this, "setIncomingCall(%s) via %s.", call, getComponentName());
Ben Gilad61925612014-03-11 19:06:36 -0700540 BindCallback callback = new BindCallback() {
Santos Cordon3d3b4052014-05-05 12:05:36 -0700541 @Override
542 public void onSuccess() {
Ben Gilad61925612014-03-11 19:06:36 -0700543 if (isServiceValid("setIncomingCallId")) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700544 mPendingIncomingCalls.add(call);
Ben Gilad61925612014-03-11 19:06:36 -0700545 try {
Ihab Awad55a34282014-06-18 10:31:09 -0700546 logOutgoing("setIncomingCallId %s %s",
547 mCallIdMapper.getCallId(call), extras);
Sailesh Nepale59bb192014-04-01 18:33:59 -0700548 mServiceInterface.setIncomingCallId(mCallIdMapper.getCallId(call),
549 extras);
Ben Gilad61925612014-03-11 19:06:36 -0700550 } catch (RemoteException e) {
Ben Gilad61925612014-03-11 19:06:36 -0700551 }
552 }
Santos Cordon61d0f702014-02-19 02:52:23 -0800553 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700554
555 @Override
556 public void onFailure() {
Ben Gilad61925612014-03-11 19:06:36 -0700557 errorCallback.run();
558 }
559 };
560
561 mBinder.bind(callback);
Santos Cordon63aeb162014-02-10 09:20:40 -0800562 }
563
Ihab Awad74549ec2014-03-10 15:33:25 -0700564 /** @see CallService#disconnect(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700565 void disconnect(Call call) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800566 if (isServiceValid("disconnect")) {
567 try {
Ihab Awad55a34282014-06-18 10:31:09 -0700568 logOutgoing("disconnect %s", mCallIdMapper.getCallId(call));
Sailesh Nepale59bb192014-04-01 18:33:59 -0700569 mServiceInterface.disconnect(mCallIdMapper.getCallId(call));
Santos Cordon61d0f702014-02-19 02:52:23 -0800570 } catch (RemoteException e) {
Santos Cordon63aeb162014-02-10 09:20:40 -0800571 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800572 }
573 }
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800574
Ihab Awad74549ec2014-03-10 15:33:25 -0700575 /** @see CallService#answer(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700576 void answer(Call call) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800577 if (isServiceValid("answer")) {
578 try {
Ihab Awad55a34282014-06-18 10:31:09 -0700579 logOutgoing("answer %s", mCallIdMapper.getCallId(call));
Sailesh Nepale59bb192014-04-01 18:33:59 -0700580 mServiceInterface.answer(mCallIdMapper.getCallId(call));
Santos Cordon61d0f702014-02-19 02:52:23 -0800581 } catch (RemoteException e) {
Santos Cordon7917d382014-02-14 02:31:18 -0800582 }
Santos Cordon61d0f702014-02-19 02:52:23 -0800583 }
584 }
585
Ihab Awad74549ec2014-03-10 15:33:25 -0700586 /** @see CallService#reject(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700587 void reject(Call call) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800588 if (isServiceValid("reject")) {
589 try {
Ihab Awad55a34282014-06-18 10:31:09 -0700590 logOutgoing("reject %s", mCallIdMapper.getCallId(call));
Sailesh Nepale59bb192014-04-01 18:33:59 -0700591 mServiceInterface.reject(mCallIdMapper.getCallId(call));
Santos Cordon61d0f702014-02-19 02:52:23 -0800592 } catch (RemoteException e) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700593 }
594 }
595 }
596
597 /** @see CallService#playDtmfTone(String,char) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700598 void playDtmfTone(Call call, char digit) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700599 if (isServiceValid("playDtmfTone")) {
600 try {
Ihab Awad55a34282014-06-18 10:31:09 -0700601 logOutgoing("playDtmfTone %s %c", mCallIdMapper.getCallId(call), digit);
Sailesh Nepale59bb192014-04-01 18:33:59 -0700602 mServiceInterface.playDtmfTone(mCallIdMapper.getCallId(call), digit);
Ihab Awad74549ec2014-03-10 15:33:25 -0700603 } catch (RemoteException e) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700604 }
605 }
606 }
607
608 /** @see CallService#stopDtmfTone(String) */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700609 void stopDtmfTone(Call call) {
Ihab Awad74549ec2014-03-10 15:33:25 -0700610 if (isServiceValid("stopDtmfTone")) {
611 try {
Ihab Awad55a34282014-06-18 10:31:09 -0700612 logOutgoing("stopDtmfTone %s", mCallIdMapper.getCallId(call));
Sailesh Nepale59bb192014-04-01 18:33:59 -0700613 mServiceInterface.stopDtmfTone(mCallIdMapper.getCallId(call));
Ihab Awad74549ec2014-03-10 15:33:25 -0700614 } catch (RemoteException e) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800615 }
Santos Cordon7917d382014-02-14 02:31:18 -0800616 }
617 }
618
Sailesh Nepale59bb192014-04-01 18:33:59 -0700619 void addCall(Call call) {
Santos Cordona1610702014-06-04 20:22:56 -0700620 if (mCallIdMapper.getCallId(call) == null) {
621 mCallIdMapper.addCall(call);
622 }
Santos Cordon7917d382014-02-14 02:31:18 -0800623 }
624
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700625 /**
626 * Associates newCall with this call service by replacing callToReplace.
627 */
628 void replaceCall(Call newCall, Call callToReplace) {
629 Preconditions.checkState(callToReplace.getCallService() == this);
630 mCallIdMapper.replaceCall(newCall, callToReplace);
631 }
632
Sailesh Nepale59bb192014-04-01 18:33:59 -0700633 void removeCall(Call call) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700634 mPendingIncomingCalls.remove(call);
635
636 AsyncResultCallback<Boolean> outgoingResultCallback =
637 mPendingOutgoingCalls.remove(mCallIdMapper.getCallId(call));
638 if (outgoingResultCallback != null) {
Ihab Awada3cb9e32014-06-03 18:45:05 -0700639 outgoingResultCallback.onResult(false, DisconnectCause.ERROR_UNSPECIFIED, null);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700640 }
641
Sailesh Nepale59bb192014-04-01 18:33:59 -0700642 mCallIdMapper.removeCall(call);
Yorke Leeadee12d2014-03-13 12:08:30 -0700643 }
644
Evan Charlton352105c2014-06-03 14:10:54 -0700645 void onPostDialContinue(Call call, boolean proceed) {
646 if (isServiceValid("onPostDialContinue")) {
647 try {
Ihab Awad55a34282014-06-18 10:31:09 -0700648 logOutgoing("onPostDialContinue %s %b", mCallIdMapper.getCallId(call), proceed);
Evan Charlton352105c2014-06-03 14:10:54 -0700649 mServiceInterface.onPostDialContinue(mCallIdMapper.getCallId(call), proceed);
650 } catch (RemoteException ignored) {
651 }
652 }
653 }
654
Santos Cordona1610702014-06-04 20:22:56 -0700655 void conference(final Call conferenceCall, Call call) {
656 if (isServiceValid("conference")) {
657 try {
658 conferenceCall.setCallService(this);
659 mPendingConferenceCalls.add(conferenceCall);
660 mHandler.postDelayed(new Runnable() {
661 @Override public void run() {
662 if (mPendingConferenceCalls.remove(conferenceCall)) {
663 conferenceCall.expireConference();
664 Log.i(this, "Conference call expired: %s", conferenceCall);
665 }
666 }
667 }, Timeouts.getConferenceCallExpireMillis());
668
Ihab Awad55a34282014-06-18 10:31:09 -0700669 logOutgoing("conference %s %s",
670 mCallIdMapper.getCallId(conferenceCall),
671 mCallIdMapper.getCallId(call));
Santos Cordona1610702014-06-04 20:22:56 -0700672 mServiceInterface.conference(
673 mCallIdMapper.getCallId(conferenceCall),
674 mCallIdMapper.getCallId(call));
675 } catch (RemoteException ignored) {
676 }
677 }
678 }
679
680 void splitFromConference(Call call) {
681 if (isServiceValid("splitFromConference")) {
682 try {
Ihab Awad55a34282014-06-18 10:31:09 -0700683 logOutgoing("splitFromConference %s", mCallIdMapper.getCallId(call));
Santos Cordona1610702014-06-04 20:22:56 -0700684 mServiceInterface.splitFromConference(mCallIdMapper.getCallId(call));
685 } catch (RemoteException ignored) {
686 }
687 }
688 }
689
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800690 /** {@inheritDoc} */
Santos Cordon3d3b4052014-05-05 12:05:36 -0700691 @Override
692 protected void setServiceInterface(IBinder binder) {
Santos Cordon4b2c1192014-03-19 18:15:38 -0700693 if (binder == null) {
694 // We have lost our service connection. Notify the world that this call service is done.
695 // We must notify the adapter before CallsManager. The adapter will force any pending
696 // outgoing calls to try the next call service. This needs to happen before CallsManager
697 // tries to clean up any calls still associated with this call service.
Santos Cordon3d3b4052014-05-05 12:05:36 -0700698 handleCallServiceDeath();
Santos Cordon4b2c1192014-03-19 18:15:38 -0700699 CallsManager.getInstance().handleCallServiceDeath(this);
700 mServiceInterface = null;
701 } else {
702 mServiceInterface = ICallService.Stub.asInterface(binder);
703 setCallServiceAdapter(mAdapter);
704 }
Santos Cordon5c12c6e2014-02-13 14:35:31 -0800705 }
Santos Cordon3d3b4052014-05-05 12:05:36 -0700706
707 /**
708 * Called when the associated call service dies.
709 */
710 private void handleCallServiceDeath() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700711 if (!mPendingOutgoingCalls.isEmpty()) {
712 for (AsyncResultCallback<Boolean> callback : mPendingOutgoingCalls.values()) {
Ihab Awada3cb9e32014-06-03 18:45:05 -0700713 callback.onResult(false, DisconnectCause.ERROR_UNSPECIFIED, null);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700714 }
715 mPendingOutgoingCalls.clear();
716 }
717
718 if (!mPendingIncomingCalls.isEmpty()) {
Santos Cordon3d3b4052014-05-05 12:05:36 -0700719 // Iterate through a copy because the code inside the loop will modify the original
720 // list.
Santos Cordon682fe6b2014-05-20 08:56:39 -0700721 for (Call call : ImmutableList.copyOf(mPendingIncomingCalls)) {
722 Preconditions.checkState(call.isIncoming());
723 mIncomingCallsManager.handleFailedIncomingCall(call);
Santos Cordon3d3b4052014-05-05 12:05:36 -0700724 }
725
Santos Cordona1610702014-06-04 20:22:56 -0700726 if (!mPendingIncomingCalls.isEmpty()) {
727 Log.wtf(this, "Pending calls did not get cleared.");
728 mPendingIncomingCalls.clear();
729 }
730 }
Santos Cordon8f3282c2014-06-01 13:56:02 -0700731
Santos Cordona1610702014-06-04 20:22:56 -0700732 mCallIdMapper.clear();
733 }
Ihab Awad55a34282014-06-18 10:31:09 -0700734
735 private void logIncoming(String msg, Object... params) {
736 Log.d(this, "CallService -> Telecomm: " + msg, params);
737 }
738
739 private void logOutgoing(String msg, Object... params) {
740 Log.d(this, "Telecomm -> CallService: " + msg, params);
741 }
Santos Cordon63aeb162014-02-10 09:20:40 -0800742}