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