blob: 1b0dcdf7e6e3f81752efdfe7510fb891115072ff [file] [log] [blame]
Sailesh Nepalc92c4362014-07-04 18:33:21 -07001/*
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
19import android.content.ComponentName;
Sailesh Nepale8ecb982014-07-11 17:19:42 -070020import android.net.Uri;
Sailesh Nepalc92c4362014-07-04 18:33:21 -070021import android.os.Bundle;
22import android.os.Handler;
23import android.os.IBinder;
24import android.os.Message;
25import android.os.RemoteException;
26import android.telecomm.CallAudioState;
27import android.telecomm.ConnectionService;
Sailesh Nepalc92c4362014-07-04 18:33:21 -070028import android.telecomm.ConnectionRequest;
29import android.telecomm.GatewayInfo;
Sailesh Nepal35faf8c2014-07-08 22:02:34 -070030import android.telecomm.StatusHints;
Sailesh Nepalc92c4362014-07-04 18:33:21 -070031import android.telecomm.TelecommConstants;
32import android.telephony.DisconnectCause;
33
34import com.android.internal.os.SomeArgs;
Sailesh Nepalc92c4362014-07-04 18:33:21 -070035import com.android.internal.telecomm.IConnectionService;
36import com.android.internal.telecomm.IConnectionServiceAdapter;
Sailesh Nepalc92c4362014-07-04 18:33:21 -070037import com.android.internal.telecomm.ICallVideoProvider;
38import com.android.internal.telecomm.RemoteServiceCallback;
Sailesh Nepalc92c4362014-07-04 18:33:21 -070039import com.google.common.base.Preconditions;
40import com.google.common.collect.ImmutableList;
41
42import org.apache.http.conn.ClientConnectionRequest;
43
44import java.util.ArrayList;
45import java.util.Collection;
46import java.util.HashMap;
47import java.util.HashSet;
48import java.util.List;
49import java.util.Map;
50import java.util.Set;
51
52/**
53 * Wrapper for {@link IConnectionService}s, handles binding to {@link IConnectionService} and keeps
54 * track of when the object can safely be unbound. Other classes should not use
55 * {@link IConnectionService} directly and instead should use this class to invoke methods of
56 * {@link IConnectionService}.
57 */
58final class ConnectionServiceWrapper extends ServiceBinder<IConnectionService> {
59 private static final int MSG_NOTIFY_INCOMING_CALL = 1;
60 private static final int MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL = 2;
61 private static final int MSG_HANDLE_FAILED_OUTGOING_CALL = 3;
62 private static final int MSG_CANCEL_OUTGOING_CALL = 4;
63 private static final int MSG_SET_ACTIVE = 5;
64 private static final int MSG_SET_RINGING = 6;
65 private static final int MSG_SET_DIALING = 7;
66 private static final int MSG_SET_DISCONNECTED = 8;
67 private static final int MSG_SET_ON_HOLD = 9;
68 private static final int MSG_SET_REQUESTING_RINGBACK = 10;
Sailesh Nepale20bc972014-07-09 21:22:36 -070069 private static final int MSG_SET_CALL_CAPABILITIES = 11;
Sailesh Nepalc92c4362014-07-04 18:33:21 -070070 private static final int MSG_SET_IS_CONFERENCED = 12;
71 private static final int MSG_ADD_CONFERENCE_CALL = 13;
72 private static final int MSG_REMOVE_CALL = 14;
73 private static final int MSG_ON_POST_DIAL_WAIT = 15;
74 private static final int MSG_QUERY_REMOTE_CALL_SERVICES = 16;
75 private static final int MSG_SET_CALL_VIDEO_PROVIDER = 17;
Sailesh Nepale8ecb982014-07-11 17:19:42 -070076 private static final int MSG_SET_AUDIO_MODE_IS_VOIP = 18;
77 private static final int MSG_SET_STATUS_HINTS = 19;
78 private static final int MSG_SET_HANDLE = 20;
79 private static final int MSG_SET_CALLER_DISPLAY_NAME = 21;
Sailesh Nepalc92c4362014-07-04 18:33:21 -070080
81 private final Handler mHandler = new Handler() {
82 @Override
83 public void handleMessage(Message msg) {
84 Call call;
85 switch (msg.what) {
86 case MSG_NOTIFY_INCOMING_CALL: {
87 ConnectionRequest request = (ConnectionRequest) msg.obj;
88 call = mCallIdMapper.getCall(request.getCallId());
89 if (call != null && mPendingIncomingCalls.remove(call) &&
90 call.isIncoming()) {
91 mIncomingCallsManager.handleSuccessfulIncomingCall(call, request);
92 } else {
93 // TODO(santoscordon): For this an the other commented logging, we need
94 // to reenable it. At the moment all ConnectionServiceAdapters receive
95 // notification of changes to all calls, even calls which it may not own
96 // (ala remote connections). We need to fix that and then uncomment the
97 // logging calls here.
98 //Log.w(this, "notifyIncomingCall, unknown incoming call: %s, id: %s",
99 // call, request.getId());
100 }
101 break;
102 }
103 case MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL: {
104 ConnectionRequest request = (ConnectionRequest) msg.obj;
105 if (mPendingOutgoingCalls.containsKey(request.getCallId())) {
106 mPendingOutgoingCalls.remove(
107 request.getCallId()).onOutgoingCallSuccess();
108 } else {
109 //Log.w(this, "handleSuccessfulOutgoingCall, unknown call: %s", callId);
110 }
111 break;
112 }
113 case MSG_HANDLE_FAILED_OUTGOING_CALL: {
114 SomeArgs args = (SomeArgs) msg.obj;
115 try {
Sailesh Nepala49c6432014-07-07 22:47:11 -0700116 ConnectionRequest request = (ConnectionRequest) args.arg1;
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700117 int statusCode = args.argi1;
118 String statusMsg = (String) args.arg2;
119 // TODO(santoscordon): Do something with 'reason' or get rid of it.
120
121 if (mPendingOutgoingCalls.containsKey(request.getCallId())) {
122 mPendingOutgoingCalls.remove(request.getCallId())
123 .onOutgoingCallFailure(statusCode, statusMsg);
124 mCallIdMapper.removeCall(request.getCallId());
125 } else {
126 //Log.w(this, "handleFailedOutgoingCall, unknown call: %s", callId);
127 }
128 } finally {
129 args.recycle();
130 }
131 break;
132 }
133 case MSG_CANCEL_OUTGOING_CALL: {
134 ConnectionRequest request = (ConnectionRequest) msg.obj;
135 if (mPendingOutgoingCalls.containsKey(request.getCallId())) {
136 mPendingOutgoingCalls.remove(
137 request.getCallId()).onOutgoingCallCancel();
138 } else {
139 //Log.w(this, "cancelOutgoingCall, unknown call: %s", callId);
140 }
141 break;
142 }
143 case MSG_SET_ACTIVE:
144 call = mCallIdMapper.getCall(msg.obj);
145 if (call != null) {
146 mCallsManager.markCallAsActive(call);
147 } else {
148 //Log.w(this, "setActive, unknown call id: %s", msg.obj);
149 }
150 break;
151 case MSG_SET_RINGING:
152 call = mCallIdMapper.getCall(msg.obj);
153 if (call != null) {
154 mCallsManager.markCallAsRinging(call);
155 } else {
156 //Log.w(this, "setRinging, unknown call id: %s", msg.obj);
157 }
158 break;
159 case MSG_SET_DIALING:
160 call = mCallIdMapper.getCall(msg.obj);
161 if (call != null) {
162 mCallsManager.markCallAsDialing(call);
163 } else {
164 //Log.w(this, "setDialing, unknown call id: %s", msg.obj);
165 }
166 break;
167 case MSG_SET_DISCONNECTED: {
168 SomeArgs args = (SomeArgs) msg.obj;
169 try {
170 call = mCallIdMapper.getCall(args.arg1);
171 String disconnectMessage = (String) args.arg2;
172 int disconnectCause = args.argi1;
173 if (call != null) {
174 mCallsManager.markCallAsDisconnected(call, disconnectCause,
175 disconnectMessage);
176 } else {
177 //Log.w(this, "setDisconnected, unknown call id: %s", args.arg1);
178 }
179 } finally {
180 args.recycle();
181 }
182 break;
183 }
184 case MSG_SET_ON_HOLD:
185 call = mCallIdMapper.getCall(msg.obj);
186 if (call != null) {
187 mCallsManager.markCallAsOnHold(call);
188 } else {
189 //Log.w(this, "setOnHold, unknown call id: %s", msg.obj);
190 }
191 break;
192 case MSG_SET_REQUESTING_RINGBACK: {
193 SomeArgs args = (SomeArgs) msg.obj;
194 try {
195 call = mCallIdMapper.getCall(args.arg1);
196 boolean ringback = (boolean) args.arg2;
197 if (call != null) {
198 call.setRequestingRingback(ringback);
199 } else {
200 //Log.w(this, "setRingback, unknown call id: %s", args.arg1);
201 }
202 } finally {
203 args.recycle();
204 }
205 break;
206 }
Sailesh Nepale20bc972014-07-09 21:22:36 -0700207 case MSG_SET_CALL_CAPABILITIES: {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700208 call = mCallIdMapper.getCall(msg.obj);
209 if (call != null) {
Sailesh Nepale20bc972014-07-09 21:22:36 -0700210 call.setCallCapabilities(msg.arg1);
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700211 } else {
212 //Log.w(ConnectionServiceWrapper.this,
Sailesh Nepale20bc972014-07-09 21:22:36 -0700213 // "setCallCapabilities, unknown call id: %s", msg.obj);
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700214 }
215 break;
216 }
217 case MSG_SET_IS_CONFERENCED: {
218 SomeArgs args = (SomeArgs) msg.obj;
219 try {
220 Call childCall = mCallIdMapper.getCall(args.arg1);
221 if (childCall != null) {
222 String conferenceCallId = (String) args.arg2;
223 if (conferenceCallId == null) {
224 childCall.setParentCall(null);
225 } else {
226 Call conferenceCall = mCallIdMapper.getCall(conferenceCallId);
227 if (conferenceCall != null &&
228 !mPendingConferenceCalls.contains(conferenceCall)) {
229 childCall.setParentCall(conferenceCall);
230 } else {
231 //Log.w(this, "setIsConferenced, unknown conference id %s",
232 // conferenceCallId);
233 }
234 }
235 } else {
236 //Log.w(this, "setIsConferenced, unknown call id: %s", args.arg1);
237 }
238 } finally {
239 args.recycle();
240 }
241 break;
242 }
243 case MSG_ADD_CONFERENCE_CALL: {
244 Call conferenceCall = mCallIdMapper.getCall(msg.obj);
245 if (mPendingConferenceCalls.remove(conferenceCall)) {
246 Log.v(this, "confirming conf call %s", conferenceCall);
247 conferenceCall.confirmConference();
248 } else {
249 //Log.w(this, "addConference, unknown call id: %s", callId);
250 }
251 break;
252 }
253 case MSG_REMOVE_CALL:
254 break;
255 case MSG_ON_POST_DIAL_WAIT: {
256 SomeArgs args = (SomeArgs) msg.obj;
257 try {
258 call = mCallIdMapper.getCall(args.arg1);
259 if (call != null) {
260 String remaining = (String) args.arg2;
261 call.onPostDialWait(remaining);
262 } else {
263 //Log.w(this, "onPostDialWait, unknown call id: %s", args.arg1);
264 }
265 } finally {
266 args.recycle();
267 }
268 break;
269 }
270 case MSG_QUERY_REMOTE_CALL_SERVICES: {
271 ConnectionServiceWrapper.this.queryRemoteConnectionServices(
272 (RemoteServiceCallback) msg.obj);
273 break;
274 }
275 case MSG_SET_CALL_VIDEO_PROVIDER: {
276 SomeArgs args = (SomeArgs) msg.obj;
277 try {
278 call = mCallIdMapper.getCall(args.arg1);
279 ICallVideoProvider callVideoProvider = (ICallVideoProvider) args.arg2;
280 if (call != null) {
281 call.setCallVideoProvider(callVideoProvider);
282 }
283 } finally {
284 args.recycle();
285 }
286 break;
287 }
Sailesh Nepal7e669572014-07-08 21:29:12 -0700288 case MSG_SET_AUDIO_MODE_IS_VOIP: {
Sailesh Nepale20bc972014-07-09 21:22:36 -0700289 call = mCallIdMapper.getCall(msg.obj);
290 if (call != null) {
291 call.setAudioModeIsVoip(msg.arg1 == 1);
Sailesh Nepal7e669572014-07-08 21:29:12 -0700292 }
293 break;
294 }
Sailesh Nepal35faf8c2014-07-08 22:02:34 -0700295 case MSG_SET_STATUS_HINTS: {
296 SomeArgs args = (SomeArgs) msg.obj;
297 try {
298 call = mCallIdMapper.getCall(args.arg1);
299 StatusHints statusHints = (StatusHints) args.arg2;
300 if (call != null) {
301 call.setStatusHints(statusHints);
302 }
303 } finally {
304 args.recycle();
305 }
306 break;
307 }
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700308 case MSG_SET_HANDLE: {
309 SomeArgs args = (SomeArgs) msg.obj;
310 try {
311 call = mCallIdMapper.getCall(args.arg1);
312 if (call != null) {
313 call.setHandle((Uri) args.arg2, args.argi1);
314 }
315 } finally {
316 args.recycle();
317 }
318 break;
319 }
320 case MSG_SET_CALLER_DISPLAY_NAME: {
321 SomeArgs args = (SomeArgs) msg.obj;
322 try {
323 call = mCallIdMapper.getCall(args.arg1);
324 if (call != null) {
325 call.setCallerDisplayName((String) args.arg2, args.argi1);
326 }
327 } finally {
328 args.recycle();
329 }
330 break;
331 }
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700332 }
333 }
334 };
335
336 private final class Adapter extends IConnectionServiceAdapter.Stub {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700337 @Override
338 public void notifyIncomingCall(ConnectionRequest request) {
339 logIncoming("notifyIncomingCall %s", request);
340 mCallIdMapper.checkValidCallId(request.getCallId());
341 mHandler.obtainMessage(MSG_NOTIFY_INCOMING_CALL, request).sendToTarget();
342 }
343
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700344 @Override
345 public void handleSuccessfulOutgoingCall(ConnectionRequest request) {
346 logIncoming("handleSuccessfulOutgoingCall %s", request);
347 mCallIdMapper.checkValidCallId(request.getCallId());
348 mHandler.obtainMessage(MSG_HANDLE_SUCCESSFUL_OUTGOING_CALL, request).sendToTarget();
349 }
350
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700351 @Override
352 public void handleFailedOutgoingCall(
353 ConnectionRequest request,
354 int errorCode,
355 String errorMsg) {
356 logIncoming("handleFailedOutgoingCall %s %d %s", request, errorCode, errorMsg);
357 mCallIdMapper.checkValidCallId(request.getCallId());
358 SomeArgs args = SomeArgs.obtain();
359 args.arg1 = request;
360 args.argi1 = errorCode;
361 args.arg2 = errorMsg;
362 mHandler.obtainMessage(MSG_HANDLE_FAILED_OUTGOING_CALL, args).sendToTarget();
363 }
364
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700365 @Override
366 public void cancelOutgoingCall(ConnectionRequest request) {
367 logIncoming("cancelOutgoingCall %s", request);
368 mCallIdMapper.checkValidCallId(request.getCallId());
369 mHandler.obtainMessage(MSG_CANCEL_OUTGOING_CALL, request).sendToTarget();
370 }
371
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700372 @Override
373 public void setActive(String callId) {
374 logIncoming("setActive %s", callId);
375 mCallIdMapper.checkValidCallId(callId);
376 mHandler.obtainMessage(MSG_SET_ACTIVE, callId).sendToTarget();
377 }
378
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700379 @Override
380 public void setRinging(String callId) {
381 logIncoming("setRinging %s", callId);
382 mCallIdMapper.checkValidCallId(callId);
383 mHandler.obtainMessage(MSG_SET_RINGING, callId).sendToTarget();
384 }
385
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700386 @Override
387 public void setCallVideoProvider(String callId, ICallVideoProvider callVideoProvider) {
388 logIncoming("setCallVideoProvider %s", callId);
389 mCallIdMapper.checkValidCallId(callId);
390 SomeArgs args = SomeArgs.obtain();
391 args.arg1 = callId;
392 args.arg2 = callVideoProvider;
393 mHandler.obtainMessage(MSG_SET_CALL_VIDEO_PROVIDER, args).sendToTarget();
394 }
395
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700396 @Override
397 public void setDialing(String callId) {
398 logIncoming("setDialing %s", callId);
399 mCallIdMapper.checkValidCallId(callId);
400 mHandler.obtainMessage(MSG_SET_DIALING, callId).sendToTarget();
401 }
402
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700403 @Override
404 public void setDisconnected(
405 String callId, int disconnectCause, String disconnectMessage) {
406 logIncoming("setDisconnected %s %d %s", callId, disconnectCause, disconnectMessage);
407 mCallIdMapper.checkValidCallId(callId);
408 SomeArgs args = SomeArgs.obtain();
409 args.arg1 = callId;
410 args.arg2 = disconnectMessage;
411 args.argi1 = disconnectCause;
412 mHandler.obtainMessage(MSG_SET_DISCONNECTED, args).sendToTarget();
413 }
414
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700415 @Override
416 public void setOnHold(String callId) {
417 logIncoming("setOnHold %s", callId);
418 mCallIdMapper.checkValidCallId(callId);
419 mHandler.obtainMessage(MSG_SET_ON_HOLD, callId).sendToTarget();
420 }
421
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700422 @Override
423 public void setRequestingRingback(String callId, boolean ringback) {
424 logIncoming("setRequestingRingback %s %b", callId, ringback);
425 mCallIdMapper.checkValidCallId(callId);
426 SomeArgs args = SomeArgs.obtain();
427 args.arg1 = callId;
428 args.arg2 = ringback;
429 mHandler.obtainMessage(MSG_SET_REQUESTING_RINGBACK, args).sendToTarget();
430 }
431
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700432 @Override
433 public void removeCall(String callId) {
434 logIncoming("removeCall %s", callId);
435 }
436
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700437 @Override
Sailesh Nepale20bc972014-07-09 21:22:36 -0700438 public void setCallCapabilities(String callId, int callCapabilities) {
439 logIncoming("setCallCapabilities %s %d", callId, callCapabilities);
440 mHandler.obtainMessage(MSG_SET_CALL_CAPABILITIES, callCapabilities, 0, callId)
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700441 .sendToTarget();
442 }
443
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700444 @Override
445 public void setIsConferenced(String callId, String conferenceCallId) {
446 logIncoming("setIsConferenced %s %s", callId, conferenceCallId);
447 SomeArgs args = SomeArgs.obtain();
448 args.arg1 = callId;
449 args.arg2 = conferenceCallId;
450 mHandler.obtainMessage(MSG_SET_IS_CONFERENCED, args).sendToTarget();
451 }
452
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700453 @Override
454 public void addConferenceCall(String callId) {
455 logIncoming("addConferenceCall %s", callId);
456 mCallIdMapper.checkValidCallId(callId);
457 mHandler.obtainMessage(MSG_ADD_CONFERENCE_CALL, callId).sendToTarget();
458 }
459
460 @Override
461 public void onPostDialWait(String callId, String remaining) throws RemoteException {
462 logIncoming("onPostDialWait %s %s", callId, remaining);
463 mCallIdMapper.checkValidCallId(callId);
464 SomeArgs args = SomeArgs.obtain();
465 args.arg1 = callId;
466 args.arg2 = remaining;
467 mHandler.obtainMessage(MSG_ON_POST_DIAL_WAIT, args).sendToTarget();
468 }
469
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700470 @Override
471 public void queryRemoteConnectionServices(RemoteServiceCallback callback) {
472 logIncoming("queryRemoteCSs");
473 mHandler.obtainMessage(MSG_QUERY_REMOTE_CALL_SERVICES, callback).sendToTarget();
474 }
475
476 @Override
Sailesh Nepal7e669572014-07-08 21:29:12 -0700477 public void setAudioModeIsVoip(String callId, boolean isVoip) {
Sailesh Nepal7fa33ca2014-07-10 15:28:21 -0700478 logIncoming("setAudioModeIsVoip %s %b", callId, isVoip);
Sailesh Nepal7e669572014-07-08 21:29:12 -0700479 mCallIdMapper.checkValidCallId(callId);
Sailesh Nepale20bc972014-07-09 21:22:36 -0700480 mHandler.obtainMessage(MSG_SET_AUDIO_MODE_IS_VOIP, isVoip ? 1 : 0, 0,
481 callId).sendToTarget();
Sailesh Nepal7e669572014-07-08 21:29:12 -0700482 }
Sailesh Nepal35faf8c2014-07-08 22:02:34 -0700483
484 @Override
485 public void setStatusHints(String callId, StatusHints statusHints) {
486 logIncoming("setStatusHints %s %s", callId, statusHints);
487 mCallIdMapper.checkValidCallId(callId);
488 SomeArgs args = SomeArgs.obtain();
489 args.arg1 = callId;
490 args.arg2 = statusHints;
491 mHandler.obtainMessage(MSG_SET_STATUS_HINTS, args).sendToTarget();
492 }
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700493
494 @Override
495 public void setHandle(String callId, Uri handle, int presentation) {
496 logIncoming("setHandle %s %s %d", callId, handle, presentation);
497 mCallIdMapper.checkValidCallId(callId);
498 SomeArgs args = SomeArgs.obtain();
499 args.arg1 = callId;
500 args.arg2 = handle;
501 args.argi1 = presentation;
502 mHandler.obtainMessage(MSG_SET_HANDLE, args).sendToTarget();
503 }
504
505 @Override
506 public void setCallerDisplayName(
507 String callId, String callerDisplayName, int presentation) {
508 logIncoming("setCallerDisplayName %s %s %d", callId, callerDisplayName, presentation);
509 mCallIdMapper.checkValidCallId(callId);
510 SomeArgs args = SomeArgs.obtain();
511 args.arg1 = callId;
512 args.arg2 = callerDisplayName;
513 args.argi1 = presentation;
514 mHandler.obtainMessage(MSG_SET_CALLER_DISPLAY_NAME, args).sendToTarget();
515 }
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700516 }
517
518 private final Adapter mAdapter = new Adapter();
519 private final CallsManager mCallsManager = CallsManager.getInstance();
520 private final Set<Call> mPendingIncomingCalls = new HashSet<>();
521 private final Set<Call> mPendingConferenceCalls = new HashSet<>();
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700522 private final CallIdMapper mCallIdMapper = new CallIdMapper("ConnectionService");
523 private final IncomingCallsManager mIncomingCallsManager;
524 private final Map<String, OutgoingCallResponse> mPendingOutgoingCalls = new HashMap<>();
525
526 private Binder mBinder = new Binder();
527 private IConnectionService mServiceInterface;
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700528 private final ConnectionServiceRepository mConnectionServiceRepository;
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700529
530 /**
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700531 * Creates a connection service.
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700532 *
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700533 * @param componentName The component name of the service with which to bind.
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700534 * @param incomingCallsManager Manages the incoming call initialization flow.
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700535 * @param connectionServiceRepository Connection service repository.
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700536 */
537 ConnectionServiceWrapper(
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700538 ComponentName componentName,
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700539 IncomingCallsManager incomingCallsManager,
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700540 ConnectionServiceRepository connectionServiceRepository) {
541 super(TelecommConstants.ACTION_CONNECTION_SERVICE, componentName);
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700542 mIncomingCallsManager = incomingCallsManager;
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700543 mConnectionServiceRepository = connectionServiceRepository;
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700544 }
545
546 /** See {@link IConnectionService#addConnectionServiceAdapter}. */
547 private void addConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
548 if (isServiceValid("addConnectionServiceAdapter")) {
549 try {
Sailesh Nepal3fe8b722014-07-08 10:07:26 -0700550 logOutgoing("addConnectionServiceAdapter %s", adapter);
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700551 mServiceInterface.addConnectionServiceAdapter(adapter);
552 } catch (RemoteException e) {
553 }
554 }
555 }
556
557 /**
558 * Attempts to place the specified call, see {@link IConnectionService#call}. Returns the result
559 * asynchronously through the specified callback.
560 */
561 void call(final Call call, final OutgoingCallResponse callResponse) {
562 Log.d(this, "call(%s) via %s.", call, getComponentName());
563 BindCallback callback = new BindCallback() {
564 @Override
565 public void onSuccess() {
566 String callId = mCallIdMapper.getCallId(call);
567 mPendingOutgoingCalls.put(callId, callResponse);
568
569 GatewayInfo gatewayInfo = call.getGatewayInfo();
570 Bundle extras = call.getExtras();
571 if (gatewayInfo != null && gatewayInfo.getGatewayProviderPackageName() != null &&
572 gatewayInfo.getOriginalHandle() != null) {
573 extras = (Bundle) extras.clone();
574 extras.putString(
575 NewOutgoingCallIntentBroadcaster.EXTRA_GATEWAY_PROVIDER_PACKAGE,
576 gatewayInfo.getGatewayProviderPackageName());
577 extras.putParcelable(
578 NewOutgoingCallIntentBroadcaster.EXTRA_GATEWAY_ORIGINAL_URI,
579 gatewayInfo.getOriginalHandle());
580 }
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700581 ConnectionRequest request = new ConnectionRequest(
582 call.getPhoneAccount(),
583 callId,
584 call.getHandle(),
585 call.getHandlePresentation(),
586 extras,
Tyler Gunnc4abd912014-07-08 14:22:10 -0700587 call.getVideoState());
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700588
589 try {
590 mServiceInterface.call(request);
591 } catch (RemoteException e) {
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700592 Log.e(this, e, "Failure to call -- %s", getComponentName());
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700593 mPendingOutgoingCalls.remove(callId).onOutgoingCallFailure(
594 DisconnectCause.ERROR_UNSPECIFIED, e.toString());
595 }
596 }
597
598 @Override
599 public void onFailure() {
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700600 Log.e(this, new Exception(), "Failure to call %s", getComponentName());
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700601 callResponse.onOutgoingCallFailure(DisconnectCause.ERROR_UNSPECIFIED, null);
602 }
603 };
604
605 mBinder.bind(callback);
606 }
607
608 /** @see ConnectionService#abort(String) */
609 void abort(Call call) {
610 // Clear out any pending outgoing call data
611 String callId = mCallIdMapper.getCallId(call);
612
613 // If still bound, tell the connection service to abort.
614 if (isServiceValid("abort")) {
615 try {
616 logOutgoing("abort %s", callId);
617 mServiceInterface.abort(callId);
618 } catch (RemoteException e) {
619 }
620 }
621
622 removeCall(call);
623 }
624
625 /** @see ConnectionService#hold(String) */
626 void hold(Call call) {
627 if (isServiceValid("hold")) {
628 try {
629 logOutgoing("hold %s", mCallIdMapper.getCallId(call));
630 mServiceInterface.hold(mCallIdMapper.getCallId(call));
631 } catch (RemoteException e) {
632 }
633 }
634 }
635
636 /** @see ConnectionService#unhold(String) */
637 void unhold(Call call) {
638 if (isServiceValid("unhold")) {
639 try {
640 logOutgoing("unhold %s", mCallIdMapper.getCallId(call));
641 mServiceInterface.unhold(mCallIdMapper.getCallId(call));
642 } catch (RemoteException e) {
643 }
644 }
645 }
646
647 /** @see ConnectionService#onAudioStateChanged(String,CallAudioState) */
648 void onAudioStateChanged(Call activeCall, CallAudioState audioState) {
649 if (isServiceValid("onAudioStateChanged")) {
650 try {
651 logOutgoing("onAudioStateChanged %s %s",
652 mCallIdMapper.getCallId(activeCall), audioState);
653 mServiceInterface.onAudioStateChanged(mCallIdMapper.getCallId(activeCall),
654 audioState);
655 } catch (RemoteException e) {
656 }
657 }
658 }
659
660 /**
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700661 * Starts retrieval of details for an incoming call. Details are returned through the connection
662 * service adapter using the specified call ID. Upon failure, the specified error callback is
663 * invoked. Can be invoked even when the connection service is unbound. See
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700664 * {@link IConnectionService#createIncomingCall}.
665 *
666 * @param call The call used for the incoming call.
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700667 * @param errorCallback The callback to invoke upon failure.
668 */
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700669 void createIncomingCall(final Call call, final Runnable errorCallback) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700670 Log.d(this, "createIncomingCall(%s) via %s.", call, getComponentName());
671 BindCallback callback = new BindCallback() {
672 @Override
673 public void onSuccess() {
674 if (isServiceValid("createIncomingCall")) {
675 mPendingIncomingCalls.add(call);
676 String callId = mCallIdMapper.getCallId(call);
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700677 logOutgoing("createIncomingCall %s", callId);
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700678 ConnectionRequest request = new ConnectionRequest(
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700679 call.getPhoneAccount(),
680 callId,
681 call.getHandle(),
682 call.getHandlePresentation(),
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700683 call.getExtras(),
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700684 call.getVideoState());
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700685 try {
686 mServiceInterface.createIncomingCall(request);
687 } catch (RemoteException e) {
688 }
689 }
690 }
691
692 @Override
693 public void onFailure() {
694 errorCallback.run();
695 }
696 };
697
698 mBinder.bind(callback);
699 }
700
701 /** @see ConnectionService#disconnect(String) */
702 void disconnect(Call call) {
703 if (isServiceValid("disconnect")) {
704 try {
705 logOutgoing("disconnect %s", mCallIdMapper.getCallId(call));
706 mServiceInterface.disconnect(mCallIdMapper.getCallId(call));
707 } catch (RemoteException e) {
708 }
709 }
710 }
711
712 /** @see ConnectionService#answer(String) */
713 void answer(Call call) {
714 if (isServiceValid("answer")) {
715 try {
716 logOutgoing("answer %s", mCallIdMapper.getCallId(call));
717 mServiceInterface.answer(mCallIdMapper.getCallId(call));
718 } catch (RemoteException e) {
719 }
720 }
721 }
722
723 /** @see ConnectionService#reject(String) */
724 void reject(Call call) {
725 if (isServiceValid("reject")) {
726 try {
727 logOutgoing("reject %s", mCallIdMapper.getCallId(call));
728 mServiceInterface.reject(mCallIdMapper.getCallId(call));
729 } catch (RemoteException e) {
730 }
731 }
732 }
733
734 /** @see ConnectionService#playDtmfTone(String,char) */
735 void playDtmfTone(Call call, char digit) {
736 if (isServiceValid("playDtmfTone")) {
737 try {
738 logOutgoing("playDtmfTone %s %c", mCallIdMapper.getCallId(call), digit);
739 mServiceInterface.playDtmfTone(mCallIdMapper.getCallId(call), digit);
740 } catch (RemoteException e) {
741 }
742 }
743 }
744
745 /** @see ConnectionService#stopDtmfTone(String) */
746 void stopDtmfTone(Call call) {
747 if (isServiceValid("stopDtmfTone")) {
748 try {
749 logOutgoing("stopDtmfTone %s", mCallIdMapper.getCallId(call));
750 mServiceInterface.stopDtmfTone(mCallIdMapper.getCallId(call));
751 } catch (RemoteException e) {
752 }
753 }
754 }
755
756 void addCall(Call call) {
757 if (mCallIdMapper.getCallId(call) == null) {
758 mCallIdMapper.addCall(call);
759 }
760 }
761
762 /**
763 * Associates newCall with this connection service by replacing callToReplace.
764 */
765 void replaceCall(Call newCall, Call callToReplace) {
766 Preconditions.checkState(callToReplace.getConnectionService() == this);
767 mCallIdMapper.replaceCall(newCall, callToReplace);
768 }
769
770 void removeCall(Call call) {
771 mPendingIncomingCalls.remove(call);
772
773 OutgoingCallResponse outgoingResultCallback =
774 mPendingOutgoingCalls.remove(mCallIdMapper.getCallId(call));
775 if (outgoingResultCallback != null) {
776 outgoingResultCallback.onOutgoingCallFailure(DisconnectCause.ERROR_UNSPECIFIED, null);
777 }
778
779 mCallIdMapper.removeCall(call);
780 }
781
782 void onPostDialContinue(Call call, boolean proceed) {
783 if (isServiceValid("onPostDialContinue")) {
784 try {
785 logOutgoing("onPostDialContinue %s %b", mCallIdMapper.getCallId(call), proceed);
786 mServiceInterface.onPostDialContinue(mCallIdMapper.getCallId(call), proceed);
787 } catch (RemoteException ignored) {
788 }
789 }
790 }
791
792 void onPhoneAccountClicked(Call call) {
793 if (isServiceValid("onPhoneAccountClicked")) {
794 try {
795 logOutgoing("onPhoneAccountClicked %s", mCallIdMapper.getCallId(call));
796 mServiceInterface.onPhoneAccountClicked(mCallIdMapper.getCallId(call));
797 } catch (RemoteException ignored) {
798 }
799 }
800 }
801
802 void conference(final Call conferenceCall, Call call) {
803 if (isServiceValid("conference")) {
804 try {
805 conferenceCall.setConnectionService(this);
806 mPendingConferenceCalls.add(conferenceCall);
807 mHandler.postDelayed(new Runnable() {
808 @Override public void run() {
809 if (mPendingConferenceCalls.remove(conferenceCall)) {
810 conferenceCall.expireConference();
811 Log.i(this, "Conference call expired: %s", conferenceCall);
812 }
813 }
814 }, Timeouts.getConferenceCallExpireMillis());
815
816 logOutgoing("conference %s %s",
817 mCallIdMapper.getCallId(conferenceCall),
818 mCallIdMapper.getCallId(call));
819 mServiceInterface.conference(
820 mCallIdMapper.getCallId(conferenceCall),
821 mCallIdMapper.getCallId(call));
822 } catch (RemoteException ignored) {
823 }
824 }
825 }
826
827 void splitFromConference(Call call) {
828 if (isServiceValid("splitFromConference")) {
829 try {
830 logOutgoing("splitFromConference %s", mCallIdMapper.getCallId(call));
831 mServiceInterface.splitFromConference(mCallIdMapper.getCallId(call));
832 } catch (RemoteException ignored) {
833 }
834 }
835 }
836
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700837 void swapWithBackgroundCall(Call call) {
838 if (isServiceValid("swapWithBackgroundCall")) {
839 try {
840 logOutgoing("swapWithBackgroundCall %s", mCallIdMapper.getCallId(call));
841 mServiceInterface.swapWithBackgroundCall(mCallIdMapper.getCallId(call));
842 } catch (RemoteException ignored) {
843 }
844 }
845 }
846
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700847 /** {@inheritDoc} */
848 @Override
849 protected void setServiceInterface(IBinder binder) {
850 if (binder == null) {
851 // We have lost our service connection. Notify the world that this service is done.
852 // We must notify the adapter before CallsManager. The adapter will force any pending
853 // outgoing calls to try the next service. This needs to happen before CallsManager
854 // tries to clean up any calls still associated with this service.
855 handleConnectionServiceDeath();
856 CallsManager.getInstance().handleConnectionServiceDeath(this);
857 mServiceInterface = null;
858 } else {
859 mServiceInterface = IConnectionService.Stub.asInterface(binder);
860 addConnectionServiceAdapter(mAdapter);
861 }
862 }
863
864 /**
865 * Called when the associated connection service dies.
866 */
867 private void handleConnectionServiceDeath() {
868 if (!mPendingOutgoingCalls.isEmpty()) {
869 for (OutgoingCallResponse callback : mPendingOutgoingCalls.values()) {
870 callback.onOutgoingCallFailure(DisconnectCause.ERROR_UNSPECIFIED, null);
871 }
872 mPendingOutgoingCalls.clear();
873 }
874
875 if (!mPendingIncomingCalls.isEmpty()) {
876 // Iterate through a copy because the code inside the loop will modify the original
877 // list.
878 for (Call call : ImmutableList.copyOf(mPendingIncomingCalls)) {
879 Preconditions.checkState(call.isIncoming());
880 mIncomingCallsManager.handleFailedIncomingCall(call);
881 }
882
883 if (!mPendingIncomingCalls.isEmpty()) {
884 Log.wtf(this, "Pending calls did not get cleared.");
885 mPendingIncomingCalls.clear();
886 }
887 }
888
889 mCallIdMapper.clear();
890 }
891
892 private void logIncoming(String msg, Object... params) {
893 Log.d(this, "ConnectionService -> Telecomm: " + msg, params);
894 }
895
896 private void logOutgoing(String msg, Object... params) {
897 Log.d(this, "Telecomm -> ConnectionService: " + msg, params);
898 }
899
900 private void queryRemoteConnectionServices(final RemoteServiceCallback callback) {
901 final List<IBinder> connectionServices = new ArrayList<>();
902 final List<ComponentName> components = new ArrayList<>();
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700903 final Collection<ConnectionServiceWrapper> services =
904 mConnectionServiceRepository.lookupServices();
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700905
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700906 for (ConnectionServiceWrapper cs : services) {
907 if (cs != this) {
908 final ConnectionServiceWrapper currentConnectionService = cs;
909 cs.mBinder.bind(new BindCallback() {
910 private int mRemainingResponses = services.size() - 1;
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700911
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700912 @Override
913 public void onSuccess() {
914 Log.d(this, "Adding ***** %s", currentConnectionService.getComponentName());
915 connectionServices.add(
916 currentConnectionService.mServiceInterface.asBinder());
917 components.add(currentConnectionService.getComponentName());
918 maybeComplete();
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700919 }
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700920
921 @Override
922 public void onFailure() {
923 // add null so that we always add up to totalExpected even if
924 // some of the connection services fail to bind.
925 maybeComplete();
926 }
927
928 private void maybeComplete() {
929 if (--mRemainingResponses == 0) {
930 try {
931 callback.onResult(components, connectionServices);
932 } catch (RemoteException ignored) {
933 }
934 }
935 }
936 });
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700937 }
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700938 }
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700939 }
940}